diff --git a/apps/site/gate-report.md b/apps/site/gate-report.md index b8d4bc0..b98fd6b 100644 --- a/apps/site/gate-report.md +++ b/apps/site/gate-report.md @@ -1,204 +1,25 @@ -# 게이트 리포트 — 2026-09-24 03:17 +# 게이트 리포트 — 2026-10-06 07:51 **전 항목 통과 — 배포 가능** +집계: 충족 13 / 미검증 0 / 실패 0 (꺼짐 4) + ``` -PASS L0 정적 — SKIP — checks.static=false -PASS gaon-lms 죽은 선택자 — 2개 CSS × 2개 HTML·JS 문서(무시 2접두사) -PASS dure-enrollment 죽은 선택자 — 2개 CSS × 2개 HTML·JS 문서(무시 2접두사) -PASS twentyoz-corp 죽은 선택자 — 2개 CSS × 2개 HTML·JS 문서(무시 2접두사) -PASS hannam-jari 죽은 선택자 — 1개 CSS × 2개 HTML·JS 문서(무시 2접두사) -PASS hannam-jari-admin 죽은 선택자 — 1개 CSS × 2개 HTML·JS 문서(무시 2접두사) -PASS griddle-club 죽은 선택자 — 1개 CSS × 4개 HTML·JS 문서(무시 2접두사) -PASS griddle-club-franchise 죽은 선택자 — 1개 CSS × 2개 HTML·JS 문서(무시 2접두사) -PASS gaon-lms 토큰 위생 — 색 0/0건(0종)·radius 1종·토큰 1파일/76개 -PASS dure-enrollment 토큰 위생 — 색 0/0건(0종)·radius 2종·토큰 1파일/73개 -PASS twentyoz-corp 토큰 위생 — 색 0/0건(0종)·radius 1종·토큰 1파일/73개 -PASS hannam-jari 토큰 위생 — 색 64/64건(49종)·radius 2/2종·토큰 2파일/64개 -PASS hannam-jari-admin 토큰 위생 — 색 254/256건(114종)·radius 14/15종·토큰 1파일/56개 -PASS griddle-club 토큰 위생 — 색 35/41건(28종)·radius 3/6종·토큰 1파일/39개 -PASS griddle-club-franchise 토큰 위생 — 색 21/26건(17종)·radius 0/0종·토큰 1파일/39개 -PASS gaon-lms SEO/meta — title 18자·desc 65자·OG✓·alt 누락 0 · 장식(alt="") 0 -PASS gaon-lms/dashboard 오버플로 — 320/375/390/768/1440 -PASS gaon-lms/dashboard h1 보임 — 1줄(줄수 계약 없음) -PASS gaon-lms/dashboard 수축 없음 -PASS gaon-lms/dashboard 리듬·트랙 -PASS gaon-lms/dashboard 대비 AA -PASS gaon-lms/students 오버플로 — 320/375/390/768/1440 -PASS gaon-lms/students h1 보임 — 1줄(줄수 계약 없음) -PASS gaon-lms/students 수축 없음 -PASS gaon-lms/students 리듬·트랙 -PASS gaon-lms/students 대비 AA -PASS gaon-lms/attendance 오버플로 — 320/375/390/768/1440 -PASS gaon-lms/attendance h1 보임 — 1줄(줄수 계약 없음) -PASS gaon-lms/attendance 수축 없음 -PASS gaon-lms/attendance 리듬·트랙 -PASS gaon-lms/attendance 대비 AA -PASS gaon-lms/grades 오버플로 — 320/375/390/768/1440 -PASS gaon-lms/grades h1 보임 — 1줄(줄수 계약 없음) -PASS gaon-lms/grades 수축 없음 -PASS gaon-lms/grades 리듬·트랙 -PASS gaon-lms/grades 대비 AA -PASS gaon-lms/files 오버플로 — 320/375/390/768/1440 -PASS gaon-lms/files h1 보임 — 1줄(줄수 계약 없음) -PASS gaon-lms/files 수축 없음 -PASS gaon-lms/files 리듬·트랙 -PASS gaon-lms/files 대비 AA -PASS gaon-lms/calendar 오버플로 — 320/375/390/768/1440 -PASS gaon-lms/calendar h1 보임 — 1줄(줄수 계약 없음) -PASS gaon-lms/calendar 수축 없음 -PASS gaon-lms/calendar 리듬·트랙 -PASS gaon-lms/calendar 대비 AA -PASS gaon-lms/counsel 오버플로 — 320/375/390/768/1440 -PASS gaon-lms/counsel h1 보임 — 1줄(줄수 계약 없음) -PASS gaon-lms/counsel 수축 없음 -PASS gaon-lms/counsel 리듬·트랙 -PASS gaon-lms/counsel 대비 AA -PASS gaon-lms/report 오버플로 — 320/375/390/768/1440 -PASS gaon-lms/report h1 보임 — 1줄(줄수 계약 없음) -PASS gaon-lms/report 수축 없음 -PASS gaon-lms/report 리듬·트랙 -PASS gaon-lms/report 대비 AA -PASS gaon-lms/notice 오버플로 — 320/375/390/768/1440 -PASS gaon-lms/notice h1 보임 — 1줄(줄수 계약 없음) -PASS gaon-lms/notice 수축 없음 -PASS gaon-lms/notice 리듬·트랙 -PASS gaon-lms/notice 대비 AA -PASS gaon-lms 스케일×폭 h1 — 줄수 정보 375@1x:1줄, 375@1.3x:1줄, 390@1x:1줄, 390@1.3x:1줄 -PASS gaon-lms 브랜드 줄바꿈 없음 — 텍스트 블록 1개, 각 1줄 -PASS gaon-lms/dashboard 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS gaon-lms/students 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS gaon-lms/attendance 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS gaon-lms/grades 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS gaon-lms/files 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS gaon-lms/calendar 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS gaon-lms/counsel 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS gaon-lms/report 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS gaon-lms/notice 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS gaon-lms/dashboard axe — WCAG 2.x AA 위반 0건 -PASS gaon-lms/students axe — WCAG 2.x AA 위반 0건 -PASS gaon-lms/attendance axe — WCAG 2.x AA 위반 0건 -PASS gaon-lms/grades axe — WCAG 2.x AA 위반 0건 -PASS gaon-lms/files axe — WCAG 2.x AA 위반 0건 -PASS gaon-lms/calendar axe — WCAG 2.x AA 위반 0건 -PASS gaon-lms/counsel axe — WCAG 2.x AA 위반 0건 -PASS gaon-lms/report axe — WCAG 2.x AA 위반 0건 -PASS gaon-lms/notice axe — WCAG 2.x AA 위반 0건 -PASS gaon-lms/dashboard 왼쪽 인셋 — 최소 텍스트 시작 16px -PASS gaon-lms/students 왼쪽 인셋 — 최소 텍스트 시작 15px -PASS gaon-lms/attendance 왼쪽 인셋 — 최소 텍스트 시작 16px -PASS gaon-lms/grades 왼쪽 인셋 — 최소 텍스트 시작 16px -PASS gaon-lms/files 왼쪽 인셋 — 최소 텍스트 시작 16px -PASS gaon-lms/calendar 왼쪽 인셋 — 최소 텍스트 시작 16px -PASS gaon-lms/counsel 왼쪽 인셋 — 최소 텍스트 시작 16px -PASS gaon-lms/report 왼쪽 인셋 — 최소 텍스트 시작 16px -PASS gaon-lms/notice 왼쪽 인셋 — 최소 텍스트 시작 16px -PASS dure-enrollment SEO/meta — title 15자·desc 71자·OG✓·alt 누락 0 · 장식(alt="") 0 -PASS dure-enrollment/catalog 오버플로 — 320/375/390/768/1440 -PASS dure-enrollment/catalog h1 보임 — 1줄(줄수 계약 없음) -PASS dure-enrollment/catalog 수축 없음 -PASS dure-enrollment/catalog 리듬·트랙 -PASS dure-enrollment/catalog 대비 AA -PASS dure-enrollment/starred 오버플로 — 320/375/390/768/1440 -PASS dure-enrollment/starred h1 보임 — 1줄(줄수 계약 없음) -PASS dure-enrollment/starred 수축 없음 -PASS dure-enrollment/starred 리듬·트랙 -PASS dure-enrollment/starred 대비 AA -PASS dure-enrollment/enrollments 오버플로 — 320/375/390/768/1440 -PASS dure-enrollment/enrollments h1 보임 — 1줄(줄수 계약 없음) -PASS dure-enrollment/enrollments 수축 없음 -PASS dure-enrollment/enrollments 리듬·트랙 -PASS dure-enrollment/enrollments 대비 AA -PASS dure-enrollment/credits 오버플로 — 320/375/390/768/1440 -PASS dure-enrollment/credits h1 보임 — 1줄(줄수 계약 없음) -PASS dure-enrollment/credits 수축 없음 -PASS dure-enrollment/credits 리듬·트랙 -PASS dure-enrollment/credits 대비 AA -PASS dure-enrollment/archive 오버플로 — 320/375/390/768/1440 -PASS dure-enrollment/archive h1 보임 — 1줄(줄수 계약 없음) -PASS dure-enrollment/archive 수축 없음 -PASS dure-enrollment/archive 리듬·트랙 -PASS dure-enrollment/archive 대비 AA -PASS dure-enrollment/notices 오버플로 — 320/375/390/768/1440 -PASS dure-enrollment/notices h1 보임 — 1줄(줄수 계약 없음) -PASS dure-enrollment/notices 수축 없음 -PASS dure-enrollment/notices 리듬·트랙 -PASS dure-enrollment/notices 대비 AA -PASS dure-enrollment 스케일×폭 h1 — 줄수 정보 375@1x:1줄, 375@1.3x:1줄, 390@1x:1줄, 390@1.3x:1줄 -PASS dure-enrollment 브랜드 줄바꿈 없음 — 텍스트 블록 1개, 각 1줄 -PASS dure-enrollment/catalog 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS dure-enrollment/starred 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS dure-enrollment/enrollments 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS dure-enrollment/credits 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS dure-enrollment/archive 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS dure-enrollment/notices 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS dure-enrollment/catalog axe — WCAG 2.x AA 위반 0건 -PASS dure-enrollment/starred axe — WCAG 2.x AA 위반 0건 -PASS dure-enrollment/enrollments axe — WCAG 2.x AA 위반 0건 -PASS dure-enrollment/credits axe — WCAG 2.x AA 위반 0건 -PASS dure-enrollment/archive axe — WCAG 2.x AA 위반 0건 -PASS dure-enrollment/notices axe — WCAG 2.x AA 위반 0건 -PASS dure-enrollment/catalog 왼쪽 인셋 — 최소 텍스트 시작 24px -PASS dure-enrollment/starred 왼쪽 인셋 — 최소 텍스트 시작 24px -PASS dure-enrollment/enrollments 왼쪽 인셋 — 최소 텍스트 시작 24px -PASS dure-enrollment/credits 왼쪽 인셋 — 최소 텍스트 시작 24px -PASS dure-enrollment/archive 왼쪽 인셋 — 최소 텍스트 시작 24px -PASS dure-enrollment/notices 왼쪽 인셋 — 최소 텍스트 시작 24px -PASS twentyoz-corp SEO/meta — title 17자·desc 80자·OG✓·alt 누락 0 · 장식(alt="") 0 -PASS twentyoz-corp/single 오버플로 — 320/375/390/768/1440 -PASS twentyoz-corp/single h1 보임 — 1줄(줄수 계약 없음) -PASS twentyoz-corp/single 수축 없음 -PASS twentyoz-corp/single 리듬·트랙 -PASS twentyoz-corp/single 대비 AA -PASS twentyoz-corp 스케일×폭 h1 — 줄수 정보 375@1x:1줄, 375@1.3x:1줄, 390@1x:1줄, 390@1.3x:1줄 -PASS twentyoz-corp 브랜드 줄바꿈 없음 — 텍스트 블록 1개, 각 1줄 -PASS twentyoz-corp/single 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS twentyoz-corp/single axe — WCAG 2.x AA 위반 0건 -PASS twentyoz-corp/single 왼쪽 인셋 — 최소 텍스트 시작 20px -PASS hannam-jari SEO/meta — title 19자·desc 75자·OG✓·alt 누락 0 · 장식(alt="") 0 -PASS hannam-jari/single 오버플로 — 320/375/390/768/1440 -PASS hannam-jari/single h1 최대 2줄 — 2줄 -PASS hannam-jari/single 수축 없음 -PASS hannam-jari/single 리듬·트랙 -PASS hannam-jari/single 대비 AA -PASS hannam-jari 스케일×폭 h1 — 줄수 정보 375@1x:2줄, 375@1.3x:2줄, 390@1x:2줄, 390@1.3x:2줄 -PASS hannam-jari 브랜드 줄바꿈 없음 — 텍스트 블록 1개, 각 1줄 -PASS hannam-jari/single 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS hannam-jari/single axe — WCAG 2.x AA 위반 0건 -PASS hannam-jari/single 왼쪽 인셋 — 최소 텍스트 시작 16px -PASS hannam-jari-admin SEO/meta — title 18자·desc 67자·OG✓·alt 누락 0 · 장식(alt="") 0 -PASS hannam-jari-admin/single 오버플로 — 320/375/390/768/1440 -PASS hannam-jari-admin/single h1 보임 — 1줄(줄수 계약 없음) -PASS hannam-jari-admin/single 수축 없음 -PASS hannam-jari-admin/single 리듬·트랙 -PASS hannam-jari-admin/single 대비 AA -PASS hannam-jari-admin 스케일×폭 h1 — 줄수 정보 375@1x:1줄, 375@1.3x:1줄, 390@1x:1줄, 390@1.3x:1줄 -PASS hannam-jari-admin 브랜드 줄바꿈 없음 — 텍스트 블록 1개, 각 1줄 -PASS hannam-jari-admin/single 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS hannam-jari-admin/single axe — WCAG 2.x AA 위반 0건 -PASS hannam-jari-admin/single 왼쪽 인셋 — 최소 텍스트 시작 12px -PASS griddle-club SEO/meta — title 20자·desc 85자·OG✓·alt 누락 0 · 장식(alt="") 0 -PASS griddle-club/single 오버플로 — 320/375/390/768/1440 -PASS griddle-club/single h1 최대 2줄 — 2줄 -PASS griddle-club/single 수축 없음 -PASS griddle-club/single 리듬·트랙 -PASS griddle-club/single 대비 AA -PASS griddle-club 스케일×폭 h1 — 줄수 정보 375@1x:2줄, 375@1.3x:2줄, 390@1x:2줄, 390@1.3x:2줄 -PASS griddle-club 브랜드 줄바꿈 없음 — 텍스트 블록 1개, 각 1줄 -PASS griddle-club/single 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS griddle-club/single axe — WCAG 2.x AA 위반 0건 -PASS griddle-club/single 왼쪽 인셋 — 최소 텍스트 시작 18px -PASS griddle-club-franchise SEO/meta — title 25자·desc 95자·OG✓·alt 누락 0 · 장식(alt="") 0 -PASS griddle-club-franchise/single 오버플로 — 320/375/390/768/1440 -PASS griddle-club-franchise/single h1 최대 2줄 — 2줄 -PASS griddle-club-franchise/single 수축 없음 -PASS griddle-club-franchise/single 리듬·트랙 -PASS griddle-club-franchise/single 대비 AA -PASS griddle-club-franchise 스케일×폭 h1 — 줄수 정보 375@1x:2줄, 375@1.3x:2줄, 390@1x:2줄, 390@1.3x:2줄 -PASS griddle-club-franchise 브랜드 줄바꿈 없음 — 텍스트 블록 1개, 각 1줄 -PASS griddle-club-franchise/single 터치타깃 — 컨트롤 ≥44px·인라인 ≥24px -PASS griddle-club-franchise/single axe — WCAG 2.x AA 위반 0건 -PASS griddle-club-franchise/single 왼쪽 인셋 — 최소 텍스트 시작 17px -PASS L3 시각 회귀 — SKIP — checks.visual=false -PASS L4 WebKit — SKIP — checks.webkit=false +OFF L0 정적 — checks.static=false +OFF 죽은 선택자 — checks.deadCss=false +PASS twentyoz-products 토큰 위생 — 색 0/0건(0종)·radius 0종·토큰 1파일/73개 +PASS twentyoz-products SEO/meta — title 17자·desc 85자·OG✓·alt 누락 0 · 장식(alt="") 0 +PASS twentyoz-products/single 오버플로 — 320/375/390/768/1440 +PASS twentyoz-products/single h1 보임 — 1줄(줄수 계약 없음) +PASS twentyoz-products/single 수축 없음 +PASS twentyoz-products/single 리듬·트랙 +PASS twentyoz-products/single 대비 AA — 검사 요소 215개 +PASS twentyoz-products 스케일×폭 h1 — 줄수 정보 375@1x:1줄, 375@1.3x:1줄, 390@1x:1줄, 390@1.3x:1줄 +PASS twentyoz-products 브랜드 줄바꿈 없음 — 텍스트 블록 1개, 각 1줄 +PASS twentyoz-products/single 터치타깃 — 컨트롤 ≥44px·링크/인라인 ≥24px · 대상 15개 +PASS twentyoz-products/single axe — WCAG 2.x AA 위반 0건 +PASS twentyoz-products/single 왼쪽 인셋 — 최소 텍스트 시작 20px +PASS twentyoz-products 런타임 — 스크립트 예외 0·로컬 요청 실패 0·깨진 이미지 0 +OFF L3 시각 회귀 — checks.visual=false +OFF L4 WebKit — checks.webkit=false ``` diff --git a/apps/site/gate.config.json b/apps/site/gate.config.json index 2bec3c5..45f346d 100644 --- a/apps/site/gate.config.json +++ b/apps/site/gate.config.json @@ -159,6 +159,36 @@ "radiusLiterals": 0 } } + }, + { + "name": "lumina-optics", + "path": "public/work/lumina-optics/index.html", + "views": [ + "single" + ], + "h1": "h1", + "h1MaxLines": 2, + "brand": ".wordmark" + }, + { + "name": "pulsegate", + "path": "public/work/pulsegate/index.html", + "views": [ + "single" + ], + "h1": "h1", + "h1MaxLines": 2, + "brand": ".wordmark" + }, + { + "name": "synapse-bci", + "path": "public/work/synapse-bci/index.html", + "views": [ + "single" + ], + "h1": "h1", + "h1MaxLines": 2, + "brand": ".wordmark" } ], "widths": [ diff --git a/apps/site/package.json b/apps/site/package.json index 1f10bd5..ec7cae5 100644 --- a/apps/site/package.json +++ b/apps/site/package.json @@ -19,6 +19,7 @@ "postbuild": "node tools/no-hanja.mjs --root dist", "preview": "astro preview", "test:no-hanja": "node tools/no-hanja.mjs", + "test:similarity": "node tools/similarity.mjs", "typecheck": "echo \"(사이트는 build 로 검증한다)\"", "verify": "node tools/verify.mjs" }, diff --git a/apps/site/public/work/baekje-celadon/assets/fonts/LICENSE.html b/apps/site/public/work/baekje-celadon/assets/fonts/LICENSE.html deleted file mode 100644 index 571387d..0000000 --- a/apps/site/public/work/baekje-celadon/assets/fonts/LICENSE.html +++ /dev/null @@ -1,169 +0,0 @@ -네이버 나눔글꼴 라이선스안내 : 고객센터
빈줄

      - - - -
      -
      -
      -
      -
      -

      네이버 나눔글꼴의 지적 재산권은 네이버와 네이버 문화재단에 있습니다.

      -

      ​

      -

      네이버 나눔글꼴은 개인 및 기업 사용자를 포함한 모든 사용자에게 무료로 제공되며 글꼴 자체를 유료로 판매하는 것을 제외한 상업적인 사용이 가능합니다.

      -

      ​

      -

      네이버 나눔글꼴은 본 저작권 안내와 라이선스 전문을 포함해서 다른 소프트웨어와 번들하거나 재배포 또는 판매가 가능하고 자유롭게 수정, 재배포하실 수 있습니다.

      -

      ​

      -

      네이버 나눔글꼴 라이선스 전문을 포함하기 어려울 경우, 나눔글꼴의 출처 표기를 권장합니다.

      -

      예) 이 페이지에는 네이버에서 제공한 나눔글꼴이 적용되어 있습니다.

      -

      ​

      -

      네이버 나눔글꼴을 사용한 인쇄물, 광고물(온라인 포함)의 이미지는 나눔글꼴 프로모션을 위해 활용될 수 있습니다.

      -

      ​

      -

      이를 원치 않는 사용자는 언제든지 당사에 요청하실 수 있습니다.

      -

      ​

      -

      정확한 사용 조건은 아래 네이버 나눔글꼴 라이선스 전문을 참고하시기 바랍니다.

      -

      ​

      -

      네이버 마루 부리 글꼴도 나눔 글꼴과 같은 오픈라이센스 폰트로 동일한 저작권 규정을 적용받습니다.

      -

      ​

      -

      ※ 참고해 주세요!

      -

      - 나눔글꼴 라이선스 전문의 한글은 이용자의 이해를 돕기 위해 영문 원본을 번역해 서비스하고 있으며, 법적 효력은 영문에 한합니다.

      -

      ​

      -

      1. 라이선스

      -

      Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/) with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri, NanumSquareNeo

      -

      ​

      -

      This Font Software is licensed under the SIL Open Font License, Version 1.1.

      -

      ​

      -

      This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL

      -

      ​

      -

      SIL OPEN FONT LICENSE

      -

      Version 1.1 - 26 February 2007

      -

      ​

      -

      ‘나눔, 네이버 나눔, 나눔고딕, 네이버 나눔고딕, 나눔명조, 네이버 나눔명조, 나눔손글씨, 네이버 나눔손글씨, 나눔펜, 네이버 나눔펜, 네이버 나눔고딕에코, 나눔고딕에코, 네이버 나눔명조에코, 나눔명조에코, 네이버 나눔고딕라이트, 나눔고딕라이트, 나눔바른고딕, 네이버나눔바른고딕, 나눔스퀘어라운드, 나눔바른펜, 마루 부리, 나눔스퀘어네오’ 폰트명에 대해 NAVER(https://www.navercorp.com/)가 저작권을 소유하고 있습니다.

      -

      ​

      -

      본 폰트 소프트웨어는 SIL 오픈 폰트 라이선스 버전 1.1에 따라 라이선스 취득을 하였습니다.

      -

      ​

      -

      본 라이선스는 하단에 복사되었고 http://scripts.sil.org/OFL의 FAQ 란에서도 열람 가능합니다.

      -

      ​

      -

      SIL 오픈 폰트 라이선스

      -

      버전 1.1 (2007년 2월 26일)

      -

      ​

      -

      [DEFINITIONS (정의)]

      -

      ""Font Software"" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.

      -

      ​

      -

      ""Reserved Font Name"" refers to any names specified as such after the copyright statement(s).

      -

      ​

      -

      ""Original Version"" refers to the collection of Font Software components as distributed by the Copyright Holder(s).

      -

      ​

      -

      ""Modified Version"" refers to any derivative made by adding to, deleting, or substituting in part or in whole any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.‘

      -

      ​

      -

      ""Author"" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.

      -

      ​

      -

      ‘폰트 소프트웨어’는 본 라이선스에 입거해 저작권자가 배포하고 명확하게 같은 표시가 된 파일들의 집합을 뜻하며, 여기에는 소스 파일, 빌드 스크립트와 문서가 이에 포함됩니다.

      -

      ​

      -

      ‘저작권이 있는 폰트명’은 저작권 정책에 따라서 지정된 이름을 말합니다.

      -

      ​

      -

      ‘원본’은 저작권자가 배포한 폰트 소프트웨어 구성요소를 의미합니다.

      -

      ​

      -

      ‘수정본’은 포맷의 변경이나 폰트 소프트웨어를 새로운 환경에 포팅 시켜, 원본의 일부 혹은 전체에 추가, 삭제 대체해 만든 파생 저작물을 의미합니다.

      -

      ​

      -

      ‘저자’는 폰트 소프트웨어에 기여한 디자이너, 엔지니어, 프로그래머, 기술 전문가 등을 의미합니다.

      -

      ​

      -

      2. PREAMBLE (전문)

      -

      The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.

      -

      ​

      -

      The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works.

      -

      ​

      -

      The fonts and derivatives, however, cannot be released under any other type of license.

      -

      ​

      -

      The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.

      -

      ​

      -

      본 폰트 라이선스를 오픈하는 것은(이하 OFL)는 전 세계 폰트 개발 프로젝트를 지원하고 학계와 언어 관련 학계의 폰트 개발을 위한 연구를 지지하기 위해서인 동시에, 폰트 제휴를 통해 폰트가 공유되고 개선될 수 있는 자유롭게 개방된 환경을 만들기 위해서입니다.

      -

      ​

      -

      OFL은 라이선스를 취득한 폰트가 그 자체로 판매되지 않는 한 자유롭게 사용, 연구, 수정, 재배포 하는 것을 허가합니다.

      -

      ​

      -

      수정된 폰트를 포함한 폰트는 저작권 명이 사용되지 않는 한 기타 소프트웨어와 함께 묶이거나 삽입, 재배포 할 수 있습니다.

      -

      ​

      -

      단 폰트와 수정된 폰트는 기타 다른 라이선스에 포함되어 배포될 수는 없습니다.

      -

      ​

      -

      이 라이선스 하에 있기 위한 폰트에 대한 요구사항은 본 폰트나 수정본을 사용하여 제작된 어떠한 문서에도 적용되지 않습니다.

      -

      ​

      -

      3. PERMISSION & CONDITIONS (허가 및 조건)

      -

      Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:

      -

      ​

      -

      본 폰트 소프트웨어를 사용하도록 허가받은 개인/기업/단체 누구라도 다음 명시된 조건에 따라 폰트 소프트웨어의 수정 혹은 수정되지 않은 복사본을 무료로 사용, 연구, 복사, 통합, 삽입, 수정, 재배포할 수 있도록 허가합니다.

      -

      ​

      -

      1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.

      -

      ​

      -

      원본이나 수정본의 폰트 소프트웨어 혹은 개별 구성요소인 폰트 자체가 판매되어서는 안됩니다.

      -

      ​

      -

      2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license.

      -

      ​

      -

      These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.

      -

      ​

      -

      본 폰트 소프트웨어의 원본 혹은 수정본은 상기 저작권 안내와 본 라이선스에 대한 내용을 포함하는 경우에는 다른 소프트웨어와 함께 묶이거나 재배포 혹은 판매가 가능합니다.

      -

      ​

      -

      이는 독립 텍스트 파일과 가독성이 있는 헤더 혹은 유저가 용이하게 열람 가능한 이상 텍스트 파일 혹은 이진 파일 내 기계가 읽을 수 있는 메타데이터 형태를 모두 의미합니다.

      -

      ​

      -

      3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder.

      -

      ​

      -

      This restriction only applies to the primary font name as presented to the users.

      -

      ​

      -

      본 폰트 소프트웨어의 어떠한 수정본도 동일한 저작권자가 명시적 허가서를 부여하지 않는 한 저작권이 있는 폰트명을 사용해서는 안 됩니다.

      -

      ​

      -

      본 제한 사항은 유저들에게 제공된 기존 폰트명을 뜻합니다.

      -

      ​

      -

      4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.

      -

      ​

      -

      본 폰트 소프트웨어의 저작권자 혹은 저자의 이름은 그들의 명시적 서면 허가가 있거나 또는 그들의 공헌을 인정하기 위한 경우를 제외하고는 수정본에 대한 사용을 유도, 추천 혹은 광고하기 위한 목적으로 사용할 수 없습니다.

      -

      ​

      -

      5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed any other license.

      -

      ​

      -

      The requirement for fonts to remain under this license does not apply to any document created using the Font Software.

      -

      ​

      -

      본 폰트 소프트웨어는 전체나 부분, 혹은 수정 여부에 상관없이 본 라이선스 하에 배포가 되어야 하며 기타 다른 라이선스 하에서는 배포를 할 수 없습니다.

      -

      ​

      -

      폰트에 대한 요구 조건은 이 라이선스 하에서만 유효하며 이 라이선스 하에 있기 위한 폰트에 대한 요구사항은 본 폰트 소프트웨어를 사용해 제작한 어떠한 문서에도 적용되지 않습니다.

      -

      ​

      -

      4. TERMINATION (계약의 종료)

      -

      This license becomes null and void if any of the above conditions are not met.

      -

      ​

      -

      본 라이선스는 상기 조건 중 일부라도 부합되지 않으면 무효가 될 수 있습니다.

      -

      ​

      -

      5. DISCLAIMER (면책조항)

      -

      THE FONT SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT.

      -

      ​

      -

      IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

      -

      ​

      -

      본 폰트 소프트웨어는 저작권, 특허권, 상표권 및 기타 권리의 비침해성과 특정 목적에의 적합성 포함한 명시적, 묵시적인 어떠한 종류의 보증 없이 “있는 그대로” 제공됩니다.

      -

      ​

      -

      어떠한 경우에도 저작권자는 본 폰트 소프트웨어의 사용 또는 이의 사용 불가, 그밖에 폰트 소프트웨어의 취급과 관련하여 발생하는 모든 계약, 불법행위 혹은 다른 일로 하여금 발생하는 일반적, 특수적, 간접적, 부차적 혹은 필연적 손해를 포함하는 소송, 손해, 혹은 기타 책임에 대한 의무를 가지지 않습니다.

      - -
      -
      -
      - -
      -
      만족도 평가
    \ No newline at end of file diff --git a/apps/site/public/work/baekje-celadon/assets/fonts/MaruBuri-Regular.woff2 b/apps/site/public/work/baekje-celadon/assets/fonts/MaruBuri-Regular.woff2 deleted file mode 100644 index 3bc205f..0000000 Binary files a/apps/site/public/work/baekje-celadon/assets/fonts/MaruBuri-Regular.woff2 and /dev/null differ diff --git a/apps/site/public/work/baekje-celadon/assets/fonts/MaruBuri-SemiBold.woff2 b/apps/site/public/work/baekje-celadon/assets/fonts/MaruBuri-SemiBold.woff2 deleted file mode 100644 index be593a5..0000000 Binary files a/apps/site/public/work/baekje-celadon/assets/fonts/MaruBuri-SemiBold.woff2 and /dev/null differ diff --git a/apps/site/public/work/baekje-celadon/assets/fonts/SOURCES.md b/apps/site/public/work/baekje-celadon/assets/fonts/SOURCES.md deleted file mode 100644 index 66d2c89..0000000 --- a/apps/site/public/work/baekje-celadon/assets/fonts/SOURCES.md +++ /dev/null @@ -1,13 +0,0 @@ -# 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. diff --git a/apps/site/public/work/baekje-celadon/design.md b/apps/site/public/work/baekje-celadon/design.md index 11b1f32..47f388c 100644 --- a/apps/site/public/work/baekje-celadon/design.md +++ b/apps/site/public/work/baekje-celadon/design.md @@ -1,17 +1,129 @@ -# 비색의 침묵 +# design.md — 비색, 1,280°C (고려청자 특별전) -암실의 청자 사진을 주연으로 유지하고 회색 카드나 격자는 추가하지 않는다. 제1·2실의 방 번호, 제목, 재질 설명, 매크로 캡션을 공통 레일로 정리했다. 첫 사진은 기존 4:5 비율을 보존하고 관람 구성 CTA와 가상 관람 안내를 인접하게 둔다. +## 진행 상태 -| 판단 축 | 적용 | 한계 | -| --- | --- | --- | -| 구도 | Hofmann의 레일과 비대칭을 참고해 방 정보와 캡션을 같은 열에 맞췄다. | 전시 사진의 감상 효과를 보편적으로 예측하지 않는다. | -| 타입 | Ruder를 참고해 방 번호와 제목·설명의 크기 차이를 명확히 했다. | 서체만으로 체류나 이해가 바뀐다고 단정하지 않는다. | -| 색 | Albers를 참고해 어두운 바탕, 비색 강조, 밝은 CTA의 역할을 나눴다. | 실제 명도 대비는 브라우저 검증에서 확인한다. | +- 0단계 완료 — 전체(리디자인) 경로. 현행 감사와 보존 자산은 `outputs/rework-2026-10-06/baekje-celadon.md` 0단계 절. +- 1단계 완료 — R1 호림박물관 실측, R2 아모레퍼시픽미술관 실측, R3 motion-parallax §9. +- 2단계 완료 — 카드 3장 중 A "세 개의 벽"(위임으로 선택). +- 3단계 완료 — `styles/tokens.css`. +- 4단계 완료 — 레이아웃 → 듀오톤·유약 결 → scroll-driven. +- 5단계 완료 — html-validate·stylelint·no-hanja·showcase-audit·design-gate 통과, 루브릭 17/20. +- 6단계 완료 — 이 문서. -토큰은 `--surface`, `--ink`, `--accent`, `--container`, `--space-*`를 유지한다. 로컬 MaruBuri 400/600은 `styles/fonts.css`, Pretendard는 기존 CDN을 유지한다. 가상 전시·관람 안내와 기존 앵커는 보존했다. +## 브리프 -## 2026-09-12 현재 검증 기록 +- 무엇을: 고려청자 특별전 "비색, 1,280°C" 안내 페이지 한 장. 전시실 두 곳(빙렬·상감)과 관람 안내. +- 누구에게: 전시를 보러 갈지 정하려는 관람객. 유약 접사를 보고 전시실 구성을 읽은 뒤 기간·시간을 확인한다. +- 제약: 정적 HTML/CSS, JS 없음. 기존 이미지 3장만 쓴다(새 이미지 생성 금지). 가상 전시라 장소·연락처·관람료가 없다. 기본 언어 ko-KR(대화 언어·기존 로컬라이제이션). 배정값(plan.md): 전면 이미지 히어로 · 한 줄 큰 낱말 · 표제부 차례 · 연락처·시간 카드 푸터 · Song Myung / Noto Sans KR · #0f1a18 · 167° · 4px · scroll-driven · feTurbulence · 듀오톤·풀블리드. +- 화면: `index.html` 하나. 2차 페이지 없음. -- `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이다. -- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다. +## 미확정 + +- `[관람료]` — 푸터 카드. 가상 전시라 값이 없다. +- `[전시 장소와 주소]` — 푸터 카드. +- `[문의 전화]` — 푸터 카드. +- 표식은 `--placeholder-line`(점선 밑줄) 하나로 통일했다. 실제 값이 오면 `index.html` 푸터 `dl` 세 자리만 바꾼다. +- 방향 선택은 위임이었다(아래 "방향"). + +## 레퍼런스 + +| 슬롯 | 출처 | 실측(2026-10-06, Playwright 1440×900) | 가져온 원리 | +|---|---|---|---| +| R1 구조 | 호림박물관 https://www.horimmuseum.org/ | 헤더 72px 아래 전시 포스터 1440×668 풀블리드. 푸터(높이 160)가 분관 2곳의 주소·개관 요일·시간·전화 카드 열, 14px | 전면 이미지 한 장 + 연락처·시간 카드 푸터가 실제 박물관 사이트의 조합이다. 카드 항목 순서(장소 → 시간 → 전화)를 따랐다 | +| R2 톤 | 아모레퍼시픽미술관 https://apma.amorepacific.com/ | body rgb(39,38,43) 위 1440×900 전면 이미지 한 장이 첫 화면 전부 | 어두운 바탕 + 전면 이미지. 예약 모달이 떠 표제 위치는 미검증 | +| R3 디테일 | `references/motion-parallax.md` §9, `motion.md` §3 [WEB-BASELINE] | 최상단 요소는 `scroll(root block)`, `@supports` 가드 안에 초기 상태, `transform`만 | 히어로 접사의 확대·이동 구현 계약 | + +못 연 곳: 리움(게이트 페이지), 국립중앙박물관(리다이렉트 루프), The Met(429). "표제가 사진 하단에 있는 전시 페이지"의 실제 사례는 미검증이고 layout.md §2-b("글은 사진 위 띠나 사진 아래로")를 근거로 썼다. 장르 필수 블록은 genre-conventions §9. + +## 방향 + +**지문**: 전면 이미지(유약 접사 풀블리드 100svh, 표제 하단) · Song Myung / Noto Sans KR · 청록빛 먹 #0f1a18 · 강조 #84c3a9(OKLCH 167°) · radius 4px · 흐름 전면 접사(차례) → 제1실(글 + 주자 세로 사진) → 제2실(상감 접사 풀블리드 띠 → 글) → 관람(동선·해설·예약) → 푸터 카드 · 내비 표제부에 녹인 차례(번호 + 이름 + 한 줄) · 푸터 연락처·시간 카드 · 이미지 듀오톤 3장, 풀블리드 2곳, 캡션 아래 · 모션 scroll-driven 확대·이동 2곳, 그 밖에 없음. + +- 한 문장 컨셉: 관람객이 유약 접사를 화면 가득 보고, 전시실 두 곳의 차례를 그 자리에서 읽은 뒤 기간·시간을 확인한다. 섹션 하나가 암실의 벽 하나다. +- 프리셋: editorial(콘텐츠가 주인공)을 지배로 두되 서체·배경·강조는 배정값. 보조 축 없음. +- 표현 리스크: 히어로 접사의 scroll-driven 확대(1 → 1.14)와 상감 띠의 세로 이동(±4%). 과업 적합 이유 — 유약 표면에 다가가는 동작이 "접사로 본다"는 컨셉 그 자체다. 검증 — 스크롤 50% 지점에서 computed transform `matrix(1.07, …)`, 상감 띠 `translateY -11px` 실측. 감소 모션·미지원 브라우저는 정지. +- 히어로 글 묶음: 라벨 없음 / 한 줄 큰 낱말(전시명) / 한 문장 리드 / 행동 없음(차례가 다음 정보로 이어진다). + +제시한 카드(위임): A 세 개의 벽 · B 접사 한 장, 아래는 도록(전시실 2열 격자) · C 가로 띠 차례(사진 아래 단색 띠에 전시명+차례, 분할 2회). **A를 골랐다** — 브리프의 "전시실마다 조명 하나와 청자 한 점"이 섹션 = 벽 구조와 맞고, R1 호림이 포스터를 풀블리드로 두고 글을 아래 둔 구조였으며, 배정 "듀오톤·풀블리드"가 A에서만 두 곳에 쓰인다. 기각 — B는 접사가 작아져 결이 안 보이고, C는 분할 반복으로 05 계측 수렴 군에 다시 붙는다. 카드 전문은 `outputs/rework-2026-10-06/baekje-celadon.md`. + +반사실 점검(5단계 렌더): 고유 요소 — 빙렬 접사 전면 사진, h1의 "1,280°C", 차례 "1 빙렬 / 2 상감"(이 전시의 전시실 구성). 같은 업종 치환(다른 도자 전시)에서 셋 다 바뀌어야 한다. 다른 업종(세무사무소)에서 전면 접사·차례·카드 푸터가 모두 쓸모없다 → 통과. B-1 5축(이전 baekje·다른 쇼케이스 대비): 구도 다름·표제 서체 다름·eyebrow 없음(다름)·CTA 없음(다름)·캡션 문장형 마침표(다름) → 0/5. B-2 6축: 섹션 흐름·글 묶음·내비·푸터·이미지 처리·모션 전부 다름 → 0/6. `tools/similarity.mjs`(읽기 전용 실행) 상위 30쌍에 baekje 없음. + +보존 자산: 전시명, 모든 사실 문구·수치(1,280°C, 철분, 백토·자토, 기간·시간·해설), 이미지 3장, 앵커 id `room-1`·`room-2`·`visit`·`main`, 파비콘 주자 실루엣, 가상 전시 고지 1회. + +## 토큰과 예산 + +값은 `styles/tokens.css`. 역할과 이유: + +- 타입: 본문 16/1.7(Noto Sans KR 400 하나), 리드 19, 섹션 제목 24→36, h1 44→120(한 줄 큰 낱말, 1440에서 본문의 7.5배), 전시실 번호 64→144. 캡션 13px이 한글 하한. +- 색: `--surface` #0f1a18(배정), `--ink` #e8ece9, `--ink-muted` #aab6b0(8.9:1), `--ink-faint` #8a9892(5.6:1, 캡션), `--accent` #84c3a9 — similarity.mjs의 OKLCH 기준으로 167°에 맞췄다(sRGB HSL 기준 165°). 강조는 차례 번호·전시실 번호·포커스·hover 밑줄에만. +- 듀오톤 램프: 그림자 #0f1a18 → 중간 #589a8a → 하이라이트 #cfe3da. `index.html`의 `#duotone` 필터 값과 같다. +- 간격: 8px 리듬, 벽 사이 `--space-8` 5→11rem이 의도된 극단. +- radius 4px(배정) — 주자 사진과 푸터 카드에만. +- `--measure` 34em. + +예산(tokens §5)과 실측(로컬 file://, Playwright Chromium, 2026-10-06): + +| 항목 | 예산 | 실측 | 판정 | +|---|---|---|---| +| 히어로까지 JS | 150KB | 0KB(스크립트 없음) | 통과 | +| 첫 화면 전송량 | 1MB | 이미지 115KB + 폰트 438KB + 폰트 CSS(압축 전 151KB) ≈ 0.7MB | 통과 | +| LCP(lab proxy) | 2.5s | 1440: 308ms(h1) · 390: 260ms(.lead). 크롬은 필터 걸린 히어로 이미지를 LCP 후보로 잡지 않았다 | 통과(lab) | +| CLS(lab proxy) | 0.1 | 1440: 0.0002 · 390: 0.024 | 통과 | +| 폰트 패밀리 | 2 | Song Myung 400 + Noto Sans KR 400 | 통과 | +| **폰트 전송량** | 100KB(기본) → **200KB(데모 등급으로 올림)** | **438KB** — Noto Sans KR 17슬라이스 176KB + Song Myung 12슬라이스 263KB | **초과** | + +폰트 초과를 숨기지 않는다. Google Fonts 한글 슬라이스는 Noto Sans KR 하나만으로도 176KB라 100KB 기본선은 이 서체 배정 자체로 닿을 수 없다. 줄이는 선택지: 섹션 제목(h2) 세 줄을 Noto Sans KR로 바꾸면 Song Myung이 4슬라이스 80KB로 줄어 총 255KB(실측). 표제 서체가 전시실 제목까지 이어져야 한 전시로 읽힌다고 판단해 유지했고, 결정은 호출자에게 넘긴다. Noto Sans KR 500은 지워 144KB를 줄였다(강조는 색으로). + +## 이펙트와 폴백 + +- 듀오톤(`#duotone`, feColorMatrix + feComponentTransfer, `color-interpolation-filters="sRGB"`): 사진 3장. 필터를 끄면 원본 사진 그대로 보인다(`final-1440-nofx-first.png`로 확인). +- 유약 결(`#glaze`, feTurbulence 2옥타브, 필터 영역 100%): `.hero-glaze` 정적 오버레이 한 장, soft-light 0.7. 움직이는 래퍼와 분리했다. 렌더 확인 — 오버레이만 찍어 G 채널 26~108 변동. +- scroll-driven: `@media (prefers-reduced-motion: no-preference)` 안의 `@supports (animation-timeline: scroll())`에만 있다. 초기 상태(transform 없음)가 가드 밖이므로 Firefox·감소 모션에서는 정지 화면이 완성본이다. 상감 띠는 scale 대신 높이 112%로 두고 세로로만 밀어 가로 오버플로를 만들지 않는다(감사가 clip 전 rect를 잰다 — 실측 실패 후 수정). +- `transform`·`opacity` 외 애니메이션 없음. `backdrop-filter` 없음(고정 바가 없다). + +## 하지 않기로 한 것 + +- 상단 바·sticky 헤더·backdrop-filter: 섹션이 셋이고 차례가 곧 내용이라 표제부에 녹였다(layout §2-c). +- 스크롤 리빌(`.reveal` 19곳): 배정표에 없다. 전부 지웠다. +- 라벨(eyebrow)·버튼: 표제만으로 무엇의 페이지인지 알 수 있고 목표 행동이 "읽고 확인"이라 없다. 전시실 번호 옆 "제1실" 낱말은 라벨이 아니라 차례의 반복이다. +- 가운뎃점 메타 문자열: 가시 텍스트에서 0곳. +- 은유 표제 "환원 불에서만 남는 색": h1은 전시명으로 바꾸고 환원 불·철분은 리드의 사실 문장으로 옮겼다. 페이지에 은유 0곳. +- 장소·관람료·문의 값: 지어내지 않고 placeholder. +- MaruBuri 로컬 폰트(900KB)와 `fonts.css`: 배정 서체로 바뀌어 죽은 자산이라 지웠다. +- 새 이미지·og.png 생성: 금지. og:image는 빙렬 접사 webp 절대 URL. + +## 검증 + +검증 줄: html-validate 0건 · stylelint 0건 · no-hanja PASS(3파일) · showcase-audit PASS 6/6(320·390·768·1440·1920·2560, 대비·오버플로·h1 1줄·깨진 이미지 0) · design-gate 충족 13 / 미검증 0 / 실패 0(죽은 선택자 0, 색 리터럴 0/0, radius 리터럴 0, 메타, 오버플로 5폭, h1 1줄(375·390 ×1·1.3), 수축 없음, 리듬, 대비 AA 46요소, 터치 타깃 4개, axe 0, 왼쪽 인셋 20px, 런타임 0) · 설정·리포트는 프로젝트 밖 스크래치. + +실측(자동 검사 밖): 히어로 글자 뒤 실제 픽셀 대비(글자를 투명하게 하고 상자 안 최대 휘도) — 1440: h1 9.6:1, 리드 8.2:1, 차례 11.6:1 / 390: h1 9.4:1, 리드 7.7:1(수정 전 3.98:1 → 스크림 강화), 차례 14.4:1 / 1920: h1 12.4:1 · 표제 마지막 줄 어절 ≥2(h1·h2 전부, 390·1440) · 키보드: 스킵 → 차례 3 → 끝, 포커스 outline solid · 768·1920·2560 레이아웃 확인(2560에서 상감 띠 폭이 줄던 결함 수정) · 200% 확대 근사(720×450) 가로 스크롤 없음, h1 1줄. + +시각 루브릭(critique §2-1, 약한 축부터, 캡처 `scratchpad/baekje/shots/final-*`): + +| 축 | 점수 | 근거 | 다음 행동 | +|---|---|---|---| +| R3 묶음·정렬 | 1 | 1440 시작선이 넷(표제 열 80px, 번호 열, 본문 열, 차례 열 1099px). 묶음 사이 간격 > 안 간격은 성립 | 차례를 표제 열에 붙이면 셋이 되지만 표제부 차례의 뜻이 약해진다. 유지 | +| R9 디테일 | 1 | 200% 크롭에서 닿는 글리프·여백 0 없음. 2560에서 1600px 원본이 업스케일돼 접사가 무르다 | 원본 교체 전까지 유지 | +| R2 위계 | 1 | 1440 회색조: 사진 → h1 → 차례(의도와 같음). 390 회색조에서는 h1과 리드 3줄이 무게를 다툰다 | 390 리드를 2줄로 줄이는 안 검토 | +| R1 첫인상 | 2 | 3초 문장: "고려청자 특별전 '비색, 1,280°C', 유약 접사를 보고 빙렬·상감 두 전시실로 간다" — 컨셉 명사·동사와 일치, 1440·390 모두. 반례: "왜 좋은가"는 사실(1,280°C)로만 답한다 | +| R4 타이포 | 2 | 합성 굵기·사체 없음(400 하나씩), 마지막 줄 어절 ≥2, measure 34em, 제목은 서체+크기+색으로 대비. 반례 못 찾음 | +| R5 여백 리듬 | 2 | 100svh 히어로 → 벽 사이 11rem → 카드 안 2.5rem로 전환이 정보 단위와 일치. 극단은 h1 120px/본문 16px와 전면 사진/먹. 반례: 제3실 오른쪽 열이 빈다 | +| R6 이미지 | 2 | 슬롯 역할 셋(접사=히어로, 주자=제1실, 상감=제2실 띠), 크롭에서 주자·꽃이 남음, 듀오톤으로 한 세계. 반례: 320×568 첫 화면은 사진이 거의 안 보인다 | +| R7 카피 | 2 | 합니다체 하나, 표제·제목이 사실 문장, 은유 0, 경쟁 전시로 바꾸면 h1·차례·리드가 깨진다. 반례: "비색"은 청자 일반 용어 | +| R8 구분성 | 2 | 고유 요소 셋, B-1 0/5, B-2 0/6, similarity 상위 쌍 없음. 반례 못 찾음 | +| R10 모바일 | 2 | 390 첫 화면에 사진·제목·차례(이동 수단·다음 정보) 모두, h1 top 392px. 스크림·차례 목록을 좁은 화면용으로 다시 짰다. 반례: 320×568 | + +합계 17/20, 0점 없음, R1·R8 2점 — 통과선 충족. + +미검증: 실기기 INP·field CWV(RUM 없음) · WebKit 렌더(gate webkit=false) · 스크린리더 실청취(읽기 순서는 소스가 시각 순서와 같음) · Windows 고대비(forced-colors) · 실제 200% OS 확대(720×450 근사만). + +## 피드백 기록 · 보존 목록 + +- 2026-10-06 재작업 전 피드백 없음. 보존: 전시명, 사실 문구, 이미지 3장, 앵커 id. + +## 변경 기록 + +- 2026-10-06 · 05 계측 수렴 축 재작업(plan.md 배정) · 분할 히어로 → 전면 접사, 상단 바 → 표제부 차례, 고지 푸터 → 연락처·시간 카드, MaruBuri/Pretendard → Song Myung/Noto Sans KR, `.reveal` 19곳 삭제, 가운뎃점 5곳 삭제, h1 "환원 불에서만 남는 색" → 전시명, 섹션 제목을 사실 문장으로. +- 2026-10-02 · 카피 교정 · 번역체·잠언형 문장을 사실 문장으로("유약이 남긴 지도", "빛으로 읽습니다" 등 제거). +- 2026-10-02 · 레드팀 06 · 전시명 "비색의 침묵" → "비색, 1,280°C", 900px 미만에서 사진을 표제 앞으로, "예시" 표기 6회 → 1회. +- 2026-08 · 최초 제작 · editorial-archival, 암실 사진 주연. diff --git a/apps/site/public/work/baekje-celadon/index.html b/apps/site/public/work/baekje-celadon/index.html index c2e2e80..3a0e28c 100644 --- a/apps/site/public/work/baekje-celadon/index.html +++ b/apps/site/public/work/baekje-celadon/index.html @@ -3,180 +3,238 @@ - 비색의 침묵 · 고려청자 전시 구성 + 비색, 1,280°C — 고려청자 특별전 - + - + + + + - + + - - + -
    -
    - 비색의 침묵 -
    - -
    -
    -
    세션 항목
    -

    - 도슨트 테이스팅 세션 선택 -

    -

    안내: 아래 예약 항목과 가격은 예시입니다. 입력 정보는 저장·전송되지 않으며 실제 예약으로 이어지지 않습니다.

    -
    -
    - - -
    + diff --git a/apps/site/public/work/cask-archive/index.html b/apps/site/public/work/cask-archive/index.html index 21605aa..1a4a139 100644 --- a/apps/site/public/work/cask-archive/index.html +++ b/apps/site/public/work/cask-archive/index.html @@ -3,150 +3,84 @@ - 캐스크 아카이브 (CASK ARCHIVE) · 독립병입 위스키 안내 + 캐스크 아카이브 — 독립병입 싱글 캐스크 5통 - - + + - + + + + - - - - + - -
    -
    -
    - -
    -
    -
    -
    - INDEPENDENT BOTTLER TASTING ARCHIVE -
    -

    - 시간과 배럴이 만든
    - 단 하나의 캐스크 원액 -

    -

    - 독립병입 위스키의 숙성 정보와 향미를 비교하며, 취향에 맞는 테이스팅 선택 방식을 살펴볼 수 있습니다. -

    - -

    안내: 이 페이지의 원장·인증 정보·가격·예약·구독은 예시입니다. 입력 정보는 저장·전송되지 않으며 실제 예약 또는 구독으로 이어지지 않습니다.

    -
    - -
    -
    - 차링된 오크 배럴 상판 위 싱글 캐스크 위스키 보틀과 테이스팅 글라스 -
    -
    CASK #8821 · 58.4% ABV
    -
    Signatory Vintage 1997 · Bottle 42 of 186
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    GORDON & MACPHAIL
    -
    병입자 표기를 원장에 연결하는 예시
    -
    -
    -
    SIGNATORY VINTAGE
    -
    숙성 조건과 도수 메타데이터 예시
    -
    -
    -
    THE SCOTCH MALT WHISKY SOCIETY
    -
    캐스크 번호와 향미 기준의 연결 예시
    -
    -
    -
    CADENHEAD'S
    -
    1842년 설립, 스코틀랜드에서 가장 오래된 독립병입자 라인업
    -
    -
    -

    아래 병입자·수입 정보는 예시입니다. 실제 인증이나 입고 현황을 뜻하지 않습니다.

    -
    - - -
    -
    -
    -

    독립병입 캐스크 목록

    -

    아래 병입 정보·수량·인증 표시는 예시입니다. 향미 기준으로 원하는 캐스크를 비교해 보세요.

    +
    + +
    +
    +

    독립병입 싱글 캐스크 5통, 도슨트와 1:1로 시음합니다

    +

    행 끝의 병입 정보 버튼을 누르면 통관 번호까지 보입니다.

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

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

    -

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

    +

    병입자 네 곳의 라벨 정보를 그대로 옮겨 적습니다

    +

    캐스크 번호, 숙성 배럴, 연수, 도수는 병 라벨에 적힌 값입니다. 향미는 도슨트가 시음한 메모입니다.

    -
    - -
    -
    +
    +
    + 그을린 오크 배럴 상판 위에 놓인 싱글 캐스크 위스키 한 병과 테이스팅 글라스 +
    + #8821, 58.4% + Signatory Vintage 1997 + 186병 가운데 42번째 병 + 사진은 896×1200 원본 비율 그대로입니다. +
    +
    + +
      +
    • + Gordon & MacPhail +

      라벨의 병입자 정보를 목록에 함께 적습니다.

      +
    • +
    • + Signatory Vintage +

      숙성 배럴, 연수, 도수를 한 줄에 적습니다.

      +
    • +
    • + The Scotch Malt Whisky Society +

      캐스크 번호별 향미 메모를 싣습니다.

      +
    • +
    • + Cadenhead's +

      1842년 설립, 스코틀랜드에서 가장 오래된 독립병입자입니다.

      +
    • +
    +
    +
    + + +
    +
    +

    도슨트 한 명이 한 팀만 맡아 3잔 또는 5잔을 설명합니다

    +

    좋아하는 향을 먼저 여쭙고, 그에 맞는 캐스크를 골라 잔마다 숙성 배럴과 도수의 차이를 짚습니다. 자세한 라인업은 도슨트 상세 페이지에 있습니다.

    +
    + +
    +
      +
    • -

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

      +

      코스 A. 독립병입자 입문 플라이트, 3잔

      65,000원
      -

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

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

    같은 증류소의 오피셜 보틀과 독립병입 싱글 캐스크를 나란히 마셔 보며 숙성 배럴에 따른 차이를 비교합니다.

    +
      +
    • 45분
    • +
    • 도슨트 1:1
    • +
    • 글렌캐런 글라스
    • +
    + +
  • -

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

    +

    코스 B. 캐스크 스트렝스와 빈티지 플라이트, 5잔

    120,000원
    -

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

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

    1990년대 빈티지와 도수 55% 이상 캐스크 스트렝스 5종을 테이스팅 휠로 하나씩 짚어 보는 심화 코스입니다.

    + + + - -
    -

    - 도슨트 프라이빗 세션 항목 -

    +
    +

    도슨트 세션 선택

    - - + +
    -
    -
    - - -
    -
    - - -
    -
    - -
    -
    - - -
    -
    - - -
    +
    + +
    - - + + +
    +
    + +
    - +
    + + +
    + +
    + + +
    + + +

    가상 브랜드 쇼케이스입니다. 입력 정보는 저장하거나 전송하지 않으며 실제 예약으로 이어지지 않습니다.

    -
    -
    + - -
    -
    + +
    -

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

    -

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

    +

    9월 12일과 26일 저녁에 시음회를 엽니다

    +

    매달 새로 들어오는 독립병입 캐스크로 시음회를 엽니다. 정원은 회마다 8명입니다.

    -
    -
    -
    - 09.12 - FRI 20:00 -
    +
      +
    • +
      9월 12일금요일 20:00
      -

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

      -

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

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

    1990년대 올드 셰리 버트 4종 버티컬 시음회

    +

    Signatory와 Gordon & MacPhail의 90년대 빈티지 셰리 캐스크 4종을 비교합니다.

    + +
  • +
    9월 26일토요일 21:00
    -

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

    -

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

    +

    옥토모어와 아일라 피트 위스키 심야 시음회

    +

    페놀 수치 80ppm 이상, 피트 향이 강한 독립병입 위스키를 블라인드로 마십니다.

    -
  • -
    - - -
    + -
    - - 📸 인스타그램 살펴보기 ↗ - -
    + +
    - +

    캐스크 아카이브는 가상 브랜드 쇼케이스입니다. 캐스크 정보, 통관 번호, 가격, 세션은 예시이며 입력 정보는 저장하거나 전송하지 않습니다.

    + + - + -
    -

    - 병입 정보 예시 -

    - +
    +

    병입 정보

    +
    -
    -
    증류소 및 보틀:
    -
    독립병입자:
    -
    숙성 정보:
    -
    알코올 도수:
    -
    통관 필증 번호:
    -
    -
    - +
    +
    증류소
    +
    +
    병입자
    +
    +
    숙성
    +
    +
    도수
    +
    +
    통관 번호
    +
    +
    +
    +
    - - - - diff --git a/apps/site/public/work/cask-archive/styles/fonts.css b/apps/site/public/work/cask-archive/styles/fonts.css deleted file mode 100644 index f5234d6..0000000 --- a/apps/site/public/work/cask-archive/styles/fonts.css +++ /dev/null @@ -1,15 +0,0 @@ -@font-face { - font-family: "MaruBuri"; - src: url("../assets/fonts/MaruBuri-Regular.woff2") format("woff2"); - font-style: normal; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "MaruBuri"; - src: url("../assets/fonts/MaruBuri-SemiBold.woff2") format("woff2"); - font-style: normal; - font-weight: 600; - font-display: swap; -} diff --git a/apps/site/public/work/cask-archive/styles/main.css b/apps/site/public/work/cask-archive/styles/main.css index a62dbff..6c06ffd 100644 --- a/apps/site/public/work/cask-archive/styles/main.css +++ b/apps/site/public/work/cask-archive/styles/main.css @@ -1,33 +1,64 @@ -@import "./tokens.css?v=20260912all"; +@import "./tokens.css?v=20261006"; /* ========================================================================== - 캐스크 아카이브 (CASK ARCHIVE) — 메인 스타일시트 SSOT - 프리셋: editorial-archival + dark-instrument + high-end-visual-design + 캐스크 아카이브 — 메인 스타일시트 + 구조: 왼쪽 측면 색인(sticky) + 오른쪽 본문. 첫 화면은 캐스크 번호 목록이다. + 모션은 hover·focus 상태 전환만 둔다. 등장 모션·재질 필터는 없다. ========================================================================== */ -*, *::before, *::after { +*, +*::before, +*::after { box-sizing: border-box; margin: 0; padding: 0; } html { - font-size: 16px; - scroll-behavior: smooth; - background-color: var(--surface); + background: var(--surface); color: var(--ink); font-family: var(--font-body); + font-size: 16px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - overflow-x: hidden; + font-synthesis-style: none; + font-synthesis-weight: none; } body { min-height: 100dvh; - line-height: var(--leading-normal); - overflow-x: hidden; background: var(--surface); color: var(--ink); + line-height: var(--leading-normal); + overflow-x: hidden; +} + +/* 한글 조판 정본(typography §6-5) */ +:lang(ko) { + word-break: keep-all; + overflow-wrap: anywhere; + line-break: strict; +} + +:lang(ko) :is(p, li, dd, figcaption) { + letter-spacing: 0; +} + +:lang(ko) :is(h1, h2, h3) { + line-height: var(--leading-tight); + text-wrap: balance; +} + +:lang(ko) :is(em, i, cite, dfn, address) { + font-style: normal; +} + +:lang(ko) em { + font-weight: var(--weight-strong); +} + +:lang(ko) a { + text-underline-position: under; } img { @@ -39,14 +70,17 @@ img { a { color: inherit; text-decoration: none; - transition: color var(--dur-quick); + transition: color var(--dur-quick) var(--ease-soft); } a:hover { color: var(--accent); } -button, input, select, textarea { +button, +input, +select, +textarea { font: inherit; color: inherit; } @@ -57,6 +91,30 @@ button { background: none; } +h1, +h2, +h3 { + font-family: var(--font-display); + font-weight: var(--weight-display); + color: var(--ink); +} + +strong { + font-weight: var(--weight-strong); +} + +.num { + font-variant-numeric: tabular-nums; +} + +.mt-5 { + margin-top: var(--space-5); +} + +.mt-6 { + margin-top: var(--space-6); +} + .sr-only { position: absolute; width: 1px; @@ -69,29 +127,12 @@ button { border-width: 0; } -.mono { - font-family: var(--font-mono); +:where(a, button, input, select, textarea, summary):focus-visible { + outline: 3px solid var(--accent); + outline-offset: 2px; } -.serif { - font-family: var(--font-serif); -} - -.italic { - font-family: var(--font-serif); - font-style: italic; -} - -.gold { - color: var(--accent); -} - -.wrap { - width: min(100% - 2 * var(--pad-inline), var(--container)); - margin-inline: auto; -} - -/* ── 접근성 스킵 링크 ── */ +/* ── 스킵 링크 ── */ .skip-link { position: fixed; top: 1rem; @@ -101,309 +142,172 @@ button { background: var(--accent); color: var(--accent-ink); border-radius: var(--radius); + font-weight: var(--weight-strong); transform: translateY(-200%); - transition: transform var(--dur-quick); - text-decoration: none; - font-weight: 700; } + .skip-link:focus { transform: translateY(0); } -/* ── 헤더 (GNB SSOT) ── */ -.site-head { - position: sticky; - top: 0; - z-index: 100; - background: rgba(11, 11, 13, 0.94); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border-bottom: 1px solid var(--line); - padding-block: var(--space-3); +/* ── 셸: 측면 색인 + 본문 ── */ +.shell { + display: grid; + grid-template-columns: var(--index-width) minmax(0, 1fr); + min-height: 100dvh; } -.head-inner { - display: flex; - align-items: center; - justify-content: space-between; - gap: var(--space-4); +.index { + grid-column: 1; + position: sticky; + top: 0; + align-self: start; + max-height: 100dvh; + overflow-y: auto; + padding: var(--space-5) var(--space-4) var(--space-5) var(--pad-inline); + border-right: 1px solid var(--line); + background: var(--surface); } .wordmark { - display: inline-flex; - align-items: baseline; - gap: var(--space-2); - font-family: var(--font-serif); - font-size: var(--step-1); - font-weight: 700; - color: var(--ink); - text-decoration: none; - letter-spacing: 0.04em; - white-space: nowrap; - flex-shrink: 0; -} - -.wordmark-han { - font-size: var(--step--1); - color: var(--accent); - font-weight: 500; - letter-spacing: 0.08em; -} - -.nav { - display: flex; - align-items: center; - gap: var(--space-5); - list-style: none; -} - -.nav a { - text-decoration: none; - color: var(--ink-muted); - font-size: var(--step--1); - font-weight: 500; - transition: color var(--dur-quick); - white-space: nowrap; -} - -.nav a:hover, -.nav a.active { - color: var(--ink); -} - -.head-actions { - display: flex; - align-items: center; - gap: var(--space-3); - flex-shrink: 0; -} - -.head-actions .btn { - display: inline-flex; - align-items: center; - justify-content: center; - background: var(--accent); - color: #0b0b0d; - font-weight: 700; - font-size: var(--step--1); - padding: 6px 14px; - border-radius: var(--radius); - transition: opacity var(--dur-quick), transform var(--dur-quick); -} - -.head-actions .btn:hover { - opacity: 0.9; - transform: translateY(-1px); -} - -/* ── 버튼 시스템 & 배지 ── */ -.btn-amber { - display: inline-flex; - align-items: center; - justify-content: center; - gap: var(--space-2); - background: var(--accent); - color: #0b0b0d; - padding: 10px 20px; - border-radius: var(--radius); - font-weight: 700; - font-size: var(--step--1); - text-decoration: none; - white-space: nowrap; - transition: transform var(--dur-quick), background var(--dur-quick), opacity var(--dur-quick); - border: 1px solid var(--accent); -} - -.btn-amber:hover { - background: #e6ab48; - color: #0b0b0d; - transform: translateY(-1px); -} - -.btn-outline { - display: inline-flex; - align-items: center; - justify-content: center; - gap: var(--space-2); - background: transparent; - color: var(--ink); - border: 1px solid var(--line); - padding: 10px 18px; - border-radius: var(--radius); - font-weight: 600; - font-size: var(--step--1); - text-decoration: none; - white-space: nowrap; - transition: border-color var(--dur-quick), color var(--dur-quick); -} - -.btn-outline:hover { - border-color: var(--accent); - color: var(--accent); -} - -.eyebrow-amber { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 4px 10px; - border-radius: var(--radius); - background: rgba(217, 155, 56, 0.12); - color: var(--accent); - font-size: 11px; - font-family: var(--font-mono); - font-weight: 600; - letter-spacing: 0.08em; - margin-bottom: var(--space-3); - border: 1px solid rgba(217, 155, 56, 0.25); -} - -/* ── 히어로 섹션 ── */ -.hero-section { - padding-block: var(--space-8) var(--space-7); - position: relative; -} - -.hero-grid { - display: grid; - grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); - gap: var(--space-7); - align-items: center; -} - -.hero-title { - font-family: var(--font-serif); - font-size: clamp(2.4rem, 1.8rem + 2.2vw, 3.8rem); - font-weight: 800; - line-height: 1.2; - letter-spacing: -0.025em; - color: var(--ink); - margin-bottom: var(--space-4); - text-wrap: balance; - word-break: keep-all; -} - -.hero-desc { - font-size: clamp(1.05rem, 0.98rem + 0.35vw, 1.2rem); - color: var(--ink-muted); - max-width: 48ch; - line-height: 1.75; - margin-bottom: var(--space-6); - text-wrap: pretty; - word-break: keep-all; -} - -.hero-ctas { - display: flex; - align-items: center; - gap: var(--space-3); - flex-wrap: wrap; -} - -.hero-visual { - position: relative; -} - -.hero-frame { - 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); - background: #141418; -} - -.hero-frame img { - width: 100%; - aspect-ratio: 4/5; - object-fit: cover; display: block; + font-family: var(--font-display); + font-size: var(--step-1); + font-weight: var(--weight-display); + color: var(--ink); + line-height: 1.3; + white-space: nowrap; } -.hero-cask-tag { - padding: var(--space-3) 0 0; - background: transparent; - border: 0; +.wordmark-sub { + display: block; + white-space: normal; + margin-top: var(--space-1); + font-size: var(--step--1); + color: var(--ink-muted); +} + +.toc { + margin-top: var(--space-5); +} + +.toc summary { + display: none; +} + +/* 닫힌 차례의 링크는 content-visibility 로만 숨기지 않고 상자도 없앤다 — Chromium 에서 닫힌 details 안 + 링크가 상자를 가진 채 UA 기본 색으로 계산되는 경우가 있었다(실렌더 감사 320 대비 1.93 오탐) */ +.toc:not([open]) nav { + display: none; +} + +.toc ol { + list-style: none; + counter-reset: toc; +} + +.toc li { + counter-increment: toc; +} + +.toc a { display: flex; - justify-content: space-between; + gap: var(--space-2); align-items: baseline; + min-height: var(--control-block); + padding: var(--space-2) 0 var(--space-2) var(--space-3); + border-left: 2px solid transparent; + color: var(--ink-muted); + font-size: var(--step--1); + line-height: 1.4; + transition: color var(--dur-quick) var(--ease-soft), border-color var(--dur-quick) var(--ease-soft); } -.cask-tag-no { - font-family: var(--font-mono); - font-size: 12px; - font-weight: 700; +.toc a::before { + content: counter(toc, decimal-leading-zero); + font-family: var(--font-display); + font-variant-numeric: tabular-nums; + color: var(--ink-faint); + transition: color var(--dur-quick) var(--ease-soft); +} + +.toc a:hover, +.toc a[aria-current] { + color: var(--ink); + border-left-color: var(--accent); +} + +.toc a:hover::before, +.toc a[aria-current]::before { color: var(--accent); } -.cask-tag-sub { - font-size: 11px; - color: var(--ink-muted); -} - -/* ── 공식 독립병입자 수입 인증 스트립 ── */ -.ib-cert-strip { - border-block: 1px solid var(--line); - background: var(--surface-raised); - padding-block: var(--space-5); -} - -.cert-grid { +.index-foot { + margin-top: var(--space-5); + padding-top: var(--space-4); + border-top: 1px solid var(--line); display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: var(--space-4); + gap: var(--space-3); } -.cert-item { - border-left: 2px solid var(--accent); - padding-left: var(--space-3); - display: flex; - flex-direction: column; - gap: 4px; -} - -.cert-name { - font-family: var(--font-mono); +.index-foot p { font-size: var(--step--1); - font-weight: 700; - color: var(--ink); - letter-spacing: 0.05em; -} - -.cert-desc { - font-size: 12px; color: var(--ink-muted); - line-height: 1.4; } -/* ── 캐스크 원장 섹션 (Interactive Cask Ledger) ── */ -.ledger-section { - padding-block: var(--space-8); +/* ── 본문 ── */ +.content { + grid-column: 2; + min-width: 0; + padding-inline: var(--pad-inline); +} + +.section { + max-width: var(--content-max); + padding-block: var(--space-6); + border-top: 1px solid var(--line); + scroll-margin-top: var(--space-4); +} + +.section:first-child { + border-top: 0; + padding-top: var(--space-5); } .section-head { - text-align: left; - max-width: 48rem; - margin-inline: 0; - margin-bottom: var(--space-6); + margin-bottom: var(--space-5); + max-width: var(--measure); } -.section-title { - font-family: var(--font-serif); - font-size: clamp(1.8rem, 1.4rem + 1.2vw, 2.25rem); - font-weight: 800; - letter-spacing: -0.02em; - color: var(--ink); +.section-head h2 { + font-size: var(--step-2); margin-bottom: var(--space-2); - text-wrap: balance; - word-break: keep-all; } -.section-sub { - font-size: var(--step-0); +.section-head p { color: var(--ink-muted); - line-height: 1.6; text-wrap: pretty; - word-break: keep-all; +} + +/* ── 첫 화면: 캐스크 목록 ── */ +.ledger-head { + display: flex; + flex-wrap: wrap; + align-items: baseline; + justify-content: space-between; + gap: var(--space-2) var(--space-5); + margin-bottom: var(--space-4); +} + +.ledger-head h1 { + font-size: var(--step-1); + font-weight: var(--weight-display); + max-width: var(--measure); +} + +.ledger-head p { + font-size: var(--step--1); + color: var(--ink-muted); } .ledger-filter-bar { @@ -412,7 +316,7 @@ button { align-items: center; flex-wrap: wrap; gap: var(--space-3); - margin-bottom: var(--space-4); + margin-bottom: var(--space-3); } .filter-btn-group { @@ -423,37 +327,41 @@ button { } .filter-btn { - padding: 6px 14px; - border-radius: var(--radius); - background: var(--surface-raised); + min-height: var(--control-block); + padding-inline: var(--control-pad-inline); border: 1px solid var(--line); + border-radius: var(--radius); + background: transparent; color: var(--ink-muted); - font-size: 12px; - font-weight: 600; - transition: background-color var(--dur-quick), border-color var(--dur-quick), color var(--dur-quick); + font-size: var(--step--1); + transition: border-color var(--dur-quick) var(--ease-soft), color var(--dur-quick) var(--ease-soft), background-color var(--dur-quick) var(--ease-soft); } .filter-btn:hover { - border-color: var(--accent); + border-color: var(--line-strong); color: var(--ink); } .filter-btn.is-active { - background: rgba(217, 155, 56, 0.15); border-color: var(--accent); + background: var(--accent-light); color: var(--accent); } .search-input { - background: var(--surface-raised); + min-height: var(--control-block); + min-width: 16rem; + padding-inline: var(--control-pad-inline); border: 1px solid var(--line); - color: var(--ink); - padding: 8px 14px; border-radius: var(--radius); - font-size: 13px; - min-width: 240px; - outline: none; - transition: border-color var(--dur-quick); + background: var(--surface-sunken); + color: var(--ink); + font-size: var(--step--1); + transition: border-color var(--dur-quick) var(--ease-soft); +} + +.search-input::placeholder { + color: var(--ink-muted); } .search-input:focus { @@ -461,155 +369,335 @@ button { } .ledger-table-wrap { + /* 표 안의 sr-only(absolute) 가 스크롤 상자 밖으로 새어 문서 폭을 늘리지 않게 상자를 기준점으로 둔다(200% 확대 실측) */ + position: relative; overflow-x: auto; - border: 1px solid var(--line); - border-radius: var(--radius-sm); - background: var(--surface-raised); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); - /* 앰버 골드 커스텀 스크롤바 (Firefox & 표준) */ - scrollbar-width: thin; - scrollbar-color: var(--accent) rgba(255, 255, 255, 0.04); -} - -/* 앰버 골드 커스텀 스크롤바 (WebKit / Chrome / Safari / Edge) */ -.ledger-table-wrap::-webkit-scrollbar { - height: 6px; - background: var(--surface-raised); -} - -.ledger-table-wrap::-webkit-scrollbar-track { - background: rgba(255, 255, 255, 0.02); - border-radius: 3px; -} - -.ledger-table-wrap::-webkit-scrollbar-thumb { - background: var(--accent); - border-radius: 3px; - border: 1px solid rgba(11, 11, 13, 0.5); -} - -.ledger-table-wrap::-webkit-scrollbar-thumb:hover { - background: #e6ab48; + border-top: 1px solid var(--line-strong); } .ledger-table { width: 100%; border-collapse: collapse; - font-size: 12.5px; text-align: left; + font-size: var(--step--1); line-height: 1.5; } .ledger-table th { - background: rgba(255, 255, 255, 0.03); - padding: 12px 10px; + padding: var(--space-2) var(--space-2); color: var(--ink-muted); - font-weight: 600; + font-weight: var(--weight-regular); + font-size: var(--step--1); border-bottom: 1px solid var(--line); - font-family: var(--font-mono); - font-size: 11.5px; - letter-spacing: 0.04em; white-space: nowrap; } .ledger-table td { - padding: 12px 10px; + padding: var(--space-4) var(--space-2); border-bottom: 1px solid var(--line); - vertical-align: middle; + vertical-align: top; } .ledger-table tbody tr { - transition: background var(--dur-quick); + scroll-margin-top: var(--space-4); + transition: background-color var(--dur-quick) var(--ease-soft); } .ledger-table tbody tr:hover { - background: rgba(217, 155, 56, 0.06); + background: var(--surface-raised); } -.ledger-table td:nth-child(1), -.ledger-table td:nth-child(3), -.ledger-table td:nth-child(4), -.ledger-table td:nth-child(5), -.ledger-table td:nth-child(6), -.ledger-table td:nth-child(8) { +.ledger-table .cask-no { + font-family: var(--font-display); + font-size: var(--step-3); + font-weight: var(--weight-display); + line-height: 1; + color: var(--accent); + white-space: nowrap; + width: 1%; + padding-top: calc(var(--space-4) + 0.07em); /* 숫자 윗선을 증류소 이름의 대문자 윗선에 맞춘다(2x 실측 8px 차) */ + padding-right: var(--space-4); +} + +.ledger-table .cask-name { + display: block; + font-size: var(--step-0); + font-weight: var(--weight-strong); + color: var(--ink); white-space: nowrap; } -.ledger-table td:nth-child(7) { - white-space: normal; - max-width: 240px; - font-size: 12px; - color: #d1d5db; +.ledger-table .distillery { + min-width: 12rem; +} + +.ledger-table .cask-sub { + display: block; + color: var(--ink-muted); +} + +.ledger-table .abv { + color: var(--ink); + font-weight: var(--weight-strong); + white-space: nowrap; +} + +.ledger-table .bottles { + white-space: nowrap; +} + +.ledger-table .bottler { + min-width: 7rem; +} + +.ledger-table .cask-type { + min-width: 9rem; +} + +.ledger-table .notes { + min-width: 11rem; + color: var(--ink-muted); text-wrap: pretty; } -.cask-badge { - font-weight: 700; +.ledger-table .act { + text-align: right; +} + +.ledger-table .act .btn-outline { + min-height: var(--control-block); +} + +/* ── 버튼 ── */ +.btn-fill, +.btn-outline { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: var(--control-block); + padding-inline: var(--space-4); + border-radius: var(--radius); + font-size: var(--step--1); + font-weight: var(--weight-strong); + white-space: nowrap; + transition: background-color var(--dur-quick) var(--ease-soft), border-color var(--dur-quick) var(--ease-soft), color var(--dur-quick) var(--ease-soft); +} + +.btn-fill { + background: var(--accent); + color: var(--accent-ink); + border: 1px solid var(--accent); +} + +.btn-fill:hover { + background: var(--accent-hover); + border-color: var(--accent-hover); + color: var(--accent-ink); +} + +.btn-outline { + background: transparent; + color: var(--ink); + border: 1px solid var(--line-strong); +} + +.btn-outline:hover { + border-color: var(--accent); color: var(--accent); } -/* ── 1:1 위스키 도슨트 테이스팅 코스 ── */ -.docent-section { - padding-block: var(--space-8); - border-top: 1px solid var(--line); - background: var(--surface-card); +/* ── 그림: 원본 비율, 캡션은 옆 ── */ +.figure-aside { + display: grid; + grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); + gap: var(--space-4) var(--space-5); + align-items: start; + margin: 0; } -.docent-grid { +.figure-aside img { + width: 100%; + border: 1px solid var(--line); +} + +.figure-aside figcaption { + font-size: var(--step--1); + color: var(--ink-muted); display: grid; - grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); + gap: var(--space-2); + align-content: start; +} + +.figure-aside figcaption strong { + display: block; + color: var(--ink); + font-family: var(--font-display); + font-size: var(--step-1); + font-weight: var(--weight-display); +} + +.figure-aside.is-portrait { + grid-template-columns: minmax(0, 22rem) minmax(0, 1fr); +} + +.figure-aside.is-reverse figcaption { + order: -1; +} + +/* ── 정의 목록(사양·병입자) ── */ +.spec-list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); + gap: var(--space-4) var(--space-5); +} + +.spec-list dt { + font-size: var(--step--1); + color: var(--ink-muted); + margin-bottom: var(--space-1); +} + +.spec-list dd { + color: var(--ink); +} + +.bottler-list { + list-style: none; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: var(--space-4) var(--space-5); +} + +.bottler-list li { + padding-top: var(--space-3); + border-top: 1px solid var(--line); +} + +.bottler-list strong { + display: block; + font-family: var(--font-display); + font-size: var(--step-1); + font-weight: var(--weight-display); + margin-bottom: var(--space-1); +} + +.bottler-list p { + font-size: var(--step--1); + color: var(--ink-muted); +} + +.two-col { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-6); align-items: start; } +/* ── 도슨트 코스 ── */ .course-list { - display: flex; - flex-direction: column; + list-style: none; + display: grid; gap: var(--space-4); } .course-card { - background: var(--surface-raised); - border: 1px solid var(--line); - border-radius: var(--radius-sm); - padding: var(--space-5); - transition: border-color var(--dur-quick); + padding: var(--space-4) 0; + border-top: 1px solid var(--line); + transition: border-color var(--dur-quick) var(--ease-soft); } .course-card:hover { - border-color: var(--accent); + border-top-color: var(--accent); } .course-header { display: flex; justify-content: space-between; align-items: baseline; - margin-bottom: var(--space-2); gap: var(--space-3); + margin-bottom: var(--space-2); } .course-title { - font-family: var(--font-serif); font-size: var(--step-1); - font-weight: 700; - color: var(--ink); - letter-spacing: -0.02em; } .course-price { - font-family: var(--font-mono); - font-size: var(--step-0); - font-weight: 700; + font-family: var(--font-display); + font-size: var(--step-1); + font-weight: var(--weight-display); color: var(--accent); white-space: nowrap; - flex-shrink: 0; + font-variant-numeric: tabular-nums; } -.docent-booking-form { - background: var(--surface-raised); +.course-card p { + color: var(--ink-muted); + font-size: var(--step--1); + margin-bottom: var(--space-2); +} + +.course-meta { + list-style: none; + display: flex; + flex-wrap: wrap; + gap: var(--space-1) var(--space-4); + font-size: var(--step--1); + color: var(--ink); +} + +.course-card .lineup-table { + margin-top: var(--space-3); +} + +.course-foot { + display: flex; + justify-content: space-between; + align-items: center; + gap: var(--space-3); + margin-top: var(--space-4); +} + +.lineup-table { + width: 100%; + border-collapse: collapse; + font-size: var(--step--1); +} + +.lineup-table th, +.lineup-table td { + padding: var(--space-2) var(--space-2) var(--space-2) 0; + text-align: left; + border-bottom: 1px solid var(--line); + vertical-align: top; +} + +.lineup-table th { + color: var(--ink-muted); + font-weight: var(--weight-regular); +} + +.lineup-table td:first-child { + font-family: var(--font-display); + color: var(--accent); + width: 2.5rem; +} + +.lineup-table td:last-child { + font-variant-numeric: tabular-nums; +} + +/* ── 폼 ── */ +.form-panel { + padding: var(--space-5); border: 1px solid var(--line); - border-radius: var(--radius-sm); - padding: var(--space-6); + border-radius: var(--radius); + background: var(--surface-raised); +} + +.form-panel h3 { + font-size: var(--step-1); + margin-bottom: var(--space-4); } .form-group { @@ -618,196 +706,38 @@ button { .form-label { display: block; - font-size: 12px; - font-family: var(--font-mono); - font-weight: 600; + font-size: var(--step--1); color: var(--ink-muted); - letter-spacing: 0.05em; - margin-bottom: 6px; + margin-bottom: var(--space-1); } -.form-select, .form-input { width: 100%; - padding: 10px 12px; - background: var(--surface); + min-height: var(--control-block); + padding-inline: var(--control-pad-inline); + background: var(--surface-sunken); border: 1px solid var(--line); border-radius: var(--radius); color: var(--ink); - font-size: 13px; - outline: none; - transition: border-color var(--dur-quick); + font-size: var(--step-0); + transition: border-color var(--dur-quick) var(--ease-soft); +} + +.form-input::placeholder { + color: var(--ink-muted); } -.form-select:focus, .form-input:focus { border-color: var(--accent); } -/* ── 이벤트 & 뉴스레터 ── */ -.events-section { - padding-block: var(--space-8); - border-top: 1px solid var(--line); -} - -.event-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: var(--space-4); - margin-bottom: var(--space-7); -} - -.event-item { - background: var(--surface-raised); - border: 1px solid var(--line); - border-radius: var(--radius-sm); - padding: var(--space-5); - display: flex; - gap: var(--space-4); - align-items: center; -} - -.event-date-box { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 10px 14px; - background: rgba(217, 155, 56, 0.1); - border: 1px solid rgba(217, 155, 56, 0.25); - border-radius: var(--radius); - min-width: 80px; - flex-shrink: 0; -} - -.event-day { - font-family: var(--font-mono); - font-size: var(--step-1); - font-weight: 800; - color: var(--accent); -} - -.event-month { - font-family: var(--font-mono); - font-size: 10px; - color: var(--ink-muted); - letter-spacing: 0.05em; -} - -.newsletter-card { - text-align: center; - background: var(--surface-card); - border: 1px solid var(--line); - border-radius: var(--radius-md); - padding: var(--space-7) var(--space-5); - max-width: 720px; - margin-inline: auto; -} - -.newsletter-form { - display: flex; - gap: var(--space-2); - max-width: 480px; - margin: var(--space-5) auto 0; -} - -/* ── 인증서 모달 ── */ -.cert-dialog { - position: fixed; - inset: 0; - margin: auto; - width: min(90vw, 480px); - 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; -} - -.cert-dialog::backdrop { - background: rgba(0, 0, 0, 0.75); - backdrop-filter: blur(8px); -} - -/* ── 푸터 (SSOT 정본) ── */ -.site-foot { - border-top: 1px solid var(--line); - padding-block: var(--space-6); - background: var(--surface); - font-size: var(--step--1); - color: var(--ink-muted); -} - -.foot-inner { - display: flex; - align-items: center; - justify-content: space-between; - flex-wrap: wrap; - gap: var(--space-4); -} - -/* ── 반응형 미디어 쿼리 ── */ -@media (max-width: 860px) { - .nav { - display: none; - } - .hero-grid, - .docent-grid, - .event-grid { - grid-template-columns: 1fr; - gap: var(--space-5); - } - .cert-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - .newsletter-form { - flex-direction: column; - } -} - -@media (max-width: 540px) { - .cert-grid { - grid-template-columns: 1fr; - } - .wordmark-han { - display: none; - } -} - -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); +.form-panel .btn-fill { + width: 100%; + margin-top: var(--space-2); } .demo-notice { - max-width: 58ch; + max-width: var(--measure); margin-top: var(--space-4); padding: var(--space-3) var(--space-4); border-left: 3px solid var(--accent); @@ -815,23 +745,417 @@ html:has(dialog[open]) { color: var(--ink-muted); font-size: var(--step--1); line-height: 1.55; - word-break: keep-all; +} + +/* ── 시음회 일정 ── */ +.event-list { + list-style: none; + display: grid; + gap: 0; + margin-bottom: var(--space-6); +} + +.event-item { + display: grid; + grid-template-columns: 9rem minmax(0, 1fr); + gap: var(--space-4); + padding: var(--space-4) 0; + border-top: 1px solid var(--line); +} + +.event-item:last-child { + border-bottom: 1px solid var(--line); +} + +.event-date { + font-family: var(--font-display); + font-size: var(--step-2); + font-weight: var(--weight-display); + color: var(--accent); + line-height: 1.1; + font-variant-numeric: tabular-nums; +} + +.event-date small { + display: block; + margin-top: var(--space-1); + font-family: var(--font-body); + font-size: var(--step--1); + font-weight: var(--weight-regular); + color: var(--ink-muted); +} + +.event-item h3 { + font-size: var(--step-1); + margin-bottom: var(--space-1); +} + +.event-item p { + font-size: var(--step--1); + color: var(--ink-muted); +} + +.newsletter { + max-width: var(--measure); +} + +.newsletter h3 { + font-size: var(--step-1); + margin-bottom: var(--space-2); +} + +.newsletter p { + color: var(--ink-muted); + font-size: var(--step--1); +} + +.newsletter-form { + display: flex; + gap: var(--space-2); + margin-top: var(--space-4); +} + +.newsletter-form .form-input { + flex: 1 1 auto; +} + +/* ── 병입 정보 모달 ── */ +.cert-dialog { + position: fixed; + inset: 0; + margin: auto; + width: min(90vw, 30rem); + max-height: calc(100dvh - var(--space-5) * 2); + padding: var(--space-5); + background: var(--surface-card); + color: var(--ink); + border: 1px solid var(--line-strong); + border-radius: var(--radius); + overflow: auto; + overscroll-behavior: contain; +} + +.cert-dialog::backdrop { + background: rgba(20, 15, 13, 0.8); +} + +.cert-head { + display: flex; + justify-content: space-between; + align-items: center; + gap: var(--space-3); + padding-bottom: var(--space-3); + margin-bottom: var(--space-4); + border-bottom: 1px solid var(--line); +} + +.cert-head h3 { + font-size: var(--step-1); +} + +.cert-close { + min-width: var(--control-block); + min-height: var(--control-block); + color: var(--ink-muted); + border-radius: var(--radius); + transition: color var(--dur-quick) var(--ease-soft); +} + +.cert-close:hover { + color: var(--ink); +} + +.cert-list { + display: grid; + grid-template-columns: 7rem minmax(0, 1fr); + gap: var(--space-2) var(--space-3); + font-size: var(--step--1); +} + +.cert-list dt { + color: var(--ink-muted); +} + +.cert-list dd { + color: var(--ink); + font-variant-numeric: tabular-nums; +} + +.cert-foot { + margin-top: var(--space-4); + display: flex; + justify-content: flex-end; +} + +html:has(dialog[open]) { + overflow: hidden; +} + +/* ── 푸터: 다열 색인 ── */ +.site-foot { + grid-column: 1 / -1; + border-top: 1px solid var(--line-strong); + padding: var(--space-6) var(--pad-inline); + background: var(--surface-sunken); + font-size: var(--step--1); + color: var(--ink-muted); +} + +.foot-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: var(--space-5); + max-width: calc(var(--index-width) + var(--content-max)); +} + +.foot-grid h2 { + font-size: var(--step-0); + margin-bottom: var(--space-3); +} + +.foot-grid ul { + list-style: none; + display: grid; + gap: var(--space-1); +} + +.foot-grid a { + display: inline-flex; + align-items: center; + min-height: 2rem; + color: var(--ink-muted); +} + +.foot-grid a:hover { + color: var(--accent); +} + +.foot-grid p + p { + margin-top: var(--space-2); +} + +.foot-note { + max-width: calc(var(--index-width) + var(--content-max)); + margin-top: var(--space-5); + padding-top: var(--space-4); + border-top: 1px solid var(--line); +} + +/* ── 반응형 ── */ +@media (max-width: 1023px) { + .shell { + grid-template-columns: minmax(0, 1fr); + } + + .index { + grid-column: 1; + position: static; + max-height: none; + overflow: visible; + padding: var(--space-3) var(--pad-inline); + border-right: 0; + border-bottom: 1px solid var(--line); + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + column-gap: var(--space-4); + align-items: center; + } + + .wordmark { + grid-column: 1; + } + + .toc { + grid-column: 1 / -1; + margin-top: 0; + } + + .toc summary { + display: inline-flex; + align-items: center; + min-height: var(--control-block); + padding-inline: var(--space-3); + margin-top: var(--space-2); + border: 1px solid var(--line); + border-radius: var(--radius); + font-size: var(--step--1); + color: var(--ink); + cursor: pointer; + list-style: none; + } + + .toc summary::-webkit-details-marker { + display: none; + } + + .toc summary::before { + content: "차례"; + } + + .toc[open] summary::before { + content: "차례 닫기"; + } + + .toc ol { + padding-block: var(--space-2); + } + + /* 좁은 화면에서는 차례가 접히므로 색인 아래 요약·버튼을 숨긴다. 같은 행동은 차례와 푸터에 있다 */ + .index-foot { + display: none; + } + + .content { + grid-column: 1; + } + + .foot-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 1399px) { + /* 본문 폭이 1074px을 밑돌면 8열 표가 가로 스크롤을 만든다(1280 실측 1051px > 960px, 1366 실측 1056px > 1042px). + 행을 flex 줄바꿈 목록으로 바꾼다. 행은 그대로 tr 이므로 검색·분류 스크립트는 바뀌지 않는다 */ + .ledger-table thead { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + } + + .ledger-table, + .ledger-table tbody, + .ledger-table td { + display: block; + } + + .ledger-table tr { + display: flex; + flex-wrap: wrap; + align-items: baseline; + gap: var(--space-2) var(--space-4); + padding-block: var(--space-4); + border-bottom: 1px solid var(--line); + } + + .ledger-table td, + .ledger-table .distillery, + .ledger-table .bottler, + .ledger-table .cask-type, + .ledger-table .notes { + padding: 0; + border: 0; + min-width: 0; + } + + .ledger-table .cask-name { + white-space: normal; + } + + .ledger-table .cask-no { + flex: 0 0 11rem; + width: auto; + padding: 0; + } + + .ledger-table .distillery { + flex: 1 1 11rem; + } + + .ledger-table .bottler, + .ledger-table .cask-type { + flex: 1 1 9rem; + white-space: normal; + } + + .ledger-table .abv, + .ledger-table .bottles { + flex: 0 1 5rem; + white-space: normal; + } + + .ledger-table .notes { + flex: 1 1 20rem; + margin-left: calc(11rem + var(--space-4)); + } + + .ledger-table .act { + flex: 0 0 auto; + margin-left: auto; + text-align: left; + } + + .ledger-table td[data-label]:not(.distillery)::before { + content: attr(data-label) " "; + color: var(--ink-muted); + } + + .two-col, + .figure-aside, + .figure-aside.is-portrait { + grid-template-columns: minmax(0, 1fr); + } + + .figure-aside.is-reverse figcaption { + order: 0; + } } @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); } + .ledger-table .cask-no { + flex-basis: auto; + } + + .ledger-table .notes { + margin-left: 0; + } + + .search-input { + min-width: 0; + width: 100%; + } + + .bottler-list { + grid-template-columns: minmax(0, 1fr); + } + + .spec-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .course-header, + .course-foot { + flex-direction: column; + align-items: flex-start; + } + + .event-item { + grid-template-columns: minmax(0, 1fr); + gap: var(--space-2); + } + + .newsletter-form { + flex-direction: column; + } + + .foot-grid { + grid-template-columns: minmax(0, 1fr); + } + + .cert-list { + grid-template-columns: minmax(0, 1fr); + } } @media (prefers-reduced-motion: reduce) { - *, *::before, *::after { - animation-duration: 1ms !important; - animation-iteration-count: 1 !important; - scroll-behavior: auto !important; + *, + *::before, + *::after { transition-duration: 1ms !important; + animation-duration: 1ms !important; + scroll-behavior: auto !important; } } diff --git a/apps/site/public/work/cask-archive/styles/tokens.css b/apps/site/public/work/cask-archive/styles/tokens.css index 13a3b8d..db7a498 100644 --- a/apps/site/public/work/cask-archive/styles/tokens.css +++ b/apps/site/public/work/cask-archive/styles/tokens.css @@ -1,59 +1,50 @@ /* ========================================================================== - 캐스크 아카이브 (CASK ARCHIVE) — 디자인 토큰 SSOT - 프리셋: dark-instrument + editorial-archival - 컨셉: 독립병입자 희귀 캐스크 원장과 1:1 위스키 도슨트 — 오크와 피트의 시간 - 단일 강조색: 앰버 골드 (#D99B38) + 캐스크 아카이브 — 디자인 토큰 (이 파일 한 곳에서만 정의한다) + 방향: 어두운 웜 바탕의 아카이브 문서. 캐스크 번호 목록이 첫 화면, 표제는 작게. + 표제 Nanum Myeongjo / 본문 IBM Plex Sans KR / 강조 25° 구리 / radius 2px ========================================================================== */ :root { - /* 타이포그래피 스케일 */ - --step--1: 0.8125rem; /* 13px — 캡션·일련번호 */ - --step-0: 1rem; /* 16px — 본문 */ - --step-1: 1.1875rem; /* 19px — 소제목 */ - --step-2: clamp(1.375rem, 1.2rem + 0.9vw, 1.875rem); /* 30px — 섹션 표제 */ - --step-3: clamp(1.8rem, 1.4rem + 1.2vw, 2.25rem); /* 36px — 서브 표제 */ - --step-4: clamp(2.4rem, 1.8rem + 2.2vw, 3.8rem); /* 60px — 메인 디스플레이 */ + /* 타입 스케일 — 한글 본문 16px, 캡션 하한 13px(typography §6-5) */ + --step--1: 0.875rem; /* 14px — 캡션·보조 라벨 */ + --step-0: 1rem; /* 16px — 본문 */ + --step-1: 1.125rem; /* 18px — 소제목·h1(작게) */ + --step-2: clamp(1.375rem, 1.2rem + 0.7vw, 1.75rem); /* 22~28px — 섹션 제목 */ + --step-3: clamp(2rem, 1.5rem + 1.6vw, 3rem); /* 32~48px — 캐스크 번호 */ - --leading-tight: 1.18; + --leading-tight: 1.3; --leading-normal: 1.7; - --measure: 54ch; + --measure: 36em; - --font-serif: "MaruBuri", "Noto Serif KR", serif; - --font-display: "MaruBuri", "Noto Serif KR", serif; - --font-body: "Pretendard Variable", Pretendard, -apple-system, sans-serif; - --font-mono: "JetBrains Mono", monospace; + --font-display: "Nanum Myeongjo", "Noto Serif KR", serif; + --font-body: "IBM Plex Sans KR", "Noto Sans KR", sans-serif; - /* 다크 럭셔리 색상 — 미드나이트 오닉스 + 앰버 골드 1강조색 */ - --surface: #0b0b0d; - --surface-raised: #141418; - --surface-card: #1c1c22; - --surface-subtle: #24242c; - --surface-sunken: #070708; - --surface-inverse: #f4f4f0; + --weight-regular: 400; + --weight-strong: 600; + --weight-display: 700; - --ink: #f4f4f0; - --ink-muted: #9ca3af; - --ink-faint: #6b7280; - --ink-inverse: #0b0b0d; - --ink-inverse-muted: #4b5563; + /* 어두운 웜 바탕 — 숙성고의 오크색 그늘(#1a1512 계열) */ + --surface: #1a1512; + --surface-raised: #211a16; + --surface-card: #28201b; + --surface-sunken: #140f0d; - --line: #2a2a34; - --line-strong: #3f3f4e; - --line-subtle: #1e1e26; - --line-inverse: #d1d5db; + --ink: #efe6da; + --ink-muted: #b3a595; /* #1a1512 위 7.5:1, #28201b 위 6.7:1 */ + --ink-faint: #8f8071; /* 차례 번호 — #1a1512 위 4.7:1 */ + --ink-inverse: #1a1512; - /* 단일 강조색 — 오크 숙성 싱글 몰트 앰버 골드 */ - --accent: #d99b38; - --accent-hover: #c28528; - --accent-light: rgba(217, 155, 56, 0.12); - --accent-surface: rgba(217, 155, 56, 0.2); - --accent-ink: #0b0b0d; + --line: #3a3029; + --line-strong: #5a4b3f; + + /* 단일 강조 — 25° 구리 */ + --accent: #d18147; /* hsl(25 60% 55%) — #1a1512 위 6.0:1 */ + --accent-hover: #dc9160; + --accent-light: rgba(209, 129, 71, 0.12); + --accent-ink: #1a1512; --radius: 2px; - --radius-sm: 4px; - --radius-md: 8px; - /* 간격 — SSOT 표준 */ --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 1rem; @@ -61,16 +52,15 @@ --space-5: 2.5rem; --space-6: 4rem; --space-7: 6rem; - --space-8: clamp(4.5rem, 3rem + 6vw, 9rem); - /* 컨테이너 및 패딩 — 전체 쇼케이스 절대 통일 SSOT */ - --container: clamp(70rem, 62vw, 85rem); - --pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem); + /* 측면 색인 + 본문의 두 열 */ + --index-width: 15rem; + --content-max: 68rem; + --pad-inline: clamp(1rem, 0.5rem + 2.5vw, 3rem); --control-block: 2.75rem; --control-pad-inline: 0.875rem; - /* 모션 */ - --dur-quick: 180ms; - --dur-normal: 420ms; + /* 모션 — hover·focus 상태 전환만 */ + --dur-quick: 160ms; --ease-soft: cubic-bezier(0.25, 1, 0.35, 1); } diff --git a/apps/site/public/work/cheongon-care/assets/fonts/LICENSE.html b/apps/site/public/work/cheongon-care/assets/fonts/LICENSE.html deleted file mode 100644 index 571387d..0000000 --- a/apps/site/public/work/cheongon-care/assets/fonts/LICENSE.html +++ /dev/null @@ -1,169 +0,0 @@ -네이버 나눔글꼴 라이선스안내 : 고객센터
    빈줄

        - - - -
        -
        -
        -
        -
        -

        네이버 나눔글꼴의 지적 재산권은 네이버와 네이버 문화재단에 있습니다.

        -

        ​

        -

        네이버 나눔글꼴은 개인 및 기업 사용자를 포함한 모든 사용자에게 무료로 제공되며 글꼴 자체를 유료로 판매하는 것을 제외한 상업적인 사용이 가능합니다.

        -

        ​

        -

        네이버 나눔글꼴은 본 저작권 안내와 라이선스 전문을 포함해서 다른 소프트웨어와 번들하거나 재배포 또는 판매가 가능하고 자유롭게 수정, 재배포하실 수 있습니다.

        -

        ​

        -

        네이버 나눔글꼴 라이선스 전문을 포함하기 어려울 경우, 나눔글꼴의 출처 표기를 권장합니다.

        -

        예) 이 페이지에는 네이버에서 제공한 나눔글꼴이 적용되어 있습니다.

        -

        ​

        -

        네이버 나눔글꼴을 사용한 인쇄물, 광고물(온라인 포함)의 이미지는 나눔글꼴 프로모션을 위해 활용될 수 있습니다.

        -

        ​

        -

        이를 원치 않는 사용자는 언제든지 당사에 요청하실 수 있습니다.

        -

        ​

        -

        정확한 사용 조건은 아래 네이버 나눔글꼴 라이선스 전문을 참고하시기 바랍니다.

        -

        ​

        -

        네이버 마루 부리 글꼴도 나눔 글꼴과 같은 오픈라이센스 폰트로 동일한 저작권 규정을 적용받습니다.

        -

        ​

        -

        ※ 참고해 주세요!

        -

        - 나눔글꼴 라이선스 전문의 한글은 이용자의 이해를 돕기 위해 영문 원본을 번역해 서비스하고 있으며, 법적 효력은 영문에 한합니다.

        -

        ​

        -

        1. 라이선스

        -

        Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/) with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri, NanumSquareNeo

        -

        ​

        -

        This Font Software is licensed under the SIL Open Font License, Version 1.1.

        -

        ​

        -

        This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL

        -

        ​

        -

        SIL OPEN FONT LICENSE

        -

        Version 1.1 - 26 February 2007

        -

        ​

        -

        ‘나눔, 네이버 나눔, 나눔고딕, 네이버 나눔고딕, 나눔명조, 네이버 나눔명조, 나눔손글씨, 네이버 나눔손글씨, 나눔펜, 네이버 나눔펜, 네이버 나눔고딕에코, 나눔고딕에코, 네이버 나눔명조에코, 나눔명조에코, 네이버 나눔고딕라이트, 나눔고딕라이트, 나눔바른고딕, 네이버나눔바른고딕, 나눔스퀘어라운드, 나눔바른펜, 마루 부리, 나눔스퀘어네오’ 폰트명에 대해 NAVER(https://www.navercorp.com/)가 저작권을 소유하고 있습니다.

        -

        ​

        -

        본 폰트 소프트웨어는 SIL 오픈 폰트 라이선스 버전 1.1에 따라 라이선스 취득을 하였습니다.

        -

        ​

        -

        본 라이선스는 하단에 복사되었고 http://scripts.sil.org/OFL의 FAQ 란에서도 열람 가능합니다.

        -

        ​

        -

        SIL 오픈 폰트 라이선스

        -

        버전 1.1 (2007년 2월 26일)

        -

        ​

        -

        [DEFINITIONS (정의)]

        -

        ""Font Software"" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.

        -

        ​

        -

        ""Reserved Font Name"" refers to any names specified as such after the copyright statement(s).

        -

        ​

        -

        ""Original Version"" refers to the collection of Font Software components as distributed by the Copyright Holder(s).

        -

        ​

        -

        ""Modified Version"" refers to any derivative made by adding to, deleting, or substituting in part or in whole any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.‘

        -

        ​

        -

        ""Author"" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.

        -

        ​

        -

        ‘폰트 소프트웨어’는 본 라이선스에 입거해 저작권자가 배포하고 명확하게 같은 표시가 된 파일들의 집합을 뜻하며, 여기에는 소스 파일, 빌드 스크립트와 문서가 이에 포함됩니다.

        -

        ​

        -

        ‘저작권이 있는 폰트명’은 저작권 정책에 따라서 지정된 이름을 말합니다.

        -

        ​

        -

        ‘원본’은 저작권자가 배포한 폰트 소프트웨어 구성요소를 의미합니다.

        -

        ​

        -

        ‘수정본’은 포맷의 변경이나 폰트 소프트웨어를 새로운 환경에 포팅 시켜, 원본의 일부 혹은 전체에 추가, 삭제 대체해 만든 파생 저작물을 의미합니다.

        -

        ​

        -

        ‘저자’는 폰트 소프트웨어에 기여한 디자이너, 엔지니어, 프로그래머, 기술 전문가 등을 의미합니다.

        -

        ​

        -

        2. PREAMBLE (전문)

        -

        The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.

        -

        ​

        -

        The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works.

        -

        ​

        -

        The fonts and derivatives, however, cannot be released under any other type of license.

        -

        ​

        -

        The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.

        -

        ​

        -

        본 폰트 라이선스를 오픈하는 것은(이하 OFL)는 전 세계 폰트 개발 프로젝트를 지원하고 학계와 언어 관련 학계의 폰트 개발을 위한 연구를 지지하기 위해서인 동시에, 폰트 제휴를 통해 폰트가 공유되고 개선될 수 있는 자유롭게 개방된 환경을 만들기 위해서입니다.

        -

        ​

        -

        OFL은 라이선스를 취득한 폰트가 그 자체로 판매되지 않는 한 자유롭게 사용, 연구, 수정, 재배포 하는 것을 허가합니다.

        -

        ​

        -

        수정된 폰트를 포함한 폰트는 저작권 명이 사용되지 않는 한 기타 소프트웨어와 함께 묶이거나 삽입, 재배포 할 수 있습니다.

        -

        ​

        -

        단 폰트와 수정된 폰트는 기타 다른 라이선스에 포함되어 배포될 수는 없습니다.

        -

        ​

        -

        이 라이선스 하에 있기 위한 폰트에 대한 요구사항은 본 폰트나 수정본을 사용하여 제작된 어떠한 문서에도 적용되지 않습니다.

        -

        ​

        -

        3. PERMISSION & CONDITIONS (허가 및 조건)

        -

        Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:

        -

        ​

        -

        본 폰트 소프트웨어를 사용하도록 허가받은 개인/기업/단체 누구라도 다음 명시된 조건에 따라 폰트 소프트웨어의 수정 혹은 수정되지 않은 복사본을 무료로 사용, 연구, 복사, 통합, 삽입, 수정, 재배포할 수 있도록 허가합니다.

        -

        ​

        -

        1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.

        -

        ​

        -

        원본이나 수정본의 폰트 소프트웨어 혹은 개별 구성요소인 폰트 자체가 판매되어서는 안됩니다.

        -

        ​

        -

        2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license.

        -

        ​

        -

        These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.

        -

        ​

        -

        본 폰트 소프트웨어의 원본 혹은 수정본은 상기 저작권 안내와 본 라이선스에 대한 내용을 포함하는 경우에는 다른 소프트웨어와 함께 묶이거나 재배포 혹은 판매가 가능합니다.

        -

        ​

        -

        이는 독립 텍스트 파일과 가독성이 있는 헤더 혹은 유저가 용이하게 열람 가능한 이상 텍스트 파일 혹은 이진 파일 내 기계가 읽을 수 있는 메타데이터 형태를 모두 의미합니다.

        -

        ​

        -

        3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder.

        -

        ​

        -

        This restriction only applies to the primary font name as presented to the users.

        -

        ​

        -

        본 폰트 소프트웨어의 어떠한 수정본도 동일한 저작권자가 명시적 허가서를 부여하지 않는 한 저작권이 있는 폰트명을 사용해서는 안 됩니다.

        -

        ​

        -

        본 제한 사항은 유저들에게 제공된 기존 폰트명을 뜻합니다.

        -

        ​

        -

        4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.

        -

        ​

        -

        본 폰트 소프트웨어의 저작권자 혹은 저자의 이름은 그들의 명시적 서면 허가가 있거나 또는 그들의 공헌을 인정하기 위한 경우를 제외하고는 수정본에 대한 사용을 유도, 추천 혹은 광고하기 위한 목적으로 사용할 수 없습니다.

        -

        ​

        -

        5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed any other license.

        -

        ​

        -

        The requirement for fonts to remain under this license does not apply to any document created using the Font Software.

        -

        ​

        -

        본 폰트 소프트웨어는 전체나 부분, 혹은 수정 여부에 상관없이 본 라이선스 하에 배포가 되어야 하며 기타 다른 라이선스 하에서는 배포를 할 수 없습니다.

        -

        ​

        -

        폰트에 대한 요구 조건은 이 라이선스 하에서만 유효하며 이 라이선스 하에 있기 위한 폰트에 대한 요구사항은 본 폰트 소프트웨어를 사용해 제작한 어떠한 문서에도 적용되지 않습니다.

        -

        ​

        -

        4. TERMINATION (계약의 종료)

        -

        This license becomes null and void if any of the above conditions are not met.

        -

        ​

        -

        본 라이선스는 상기 조건 중 일부라도 부합되지 않으면 무효가 될 수 있습니다.

        -

        ​

        -

        5. DISCLAIMER (면책조항)

        -

        THE FONT SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT.

        -

        ​

        -

        IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

        -

        ​

        -

        본 폰트 소프트웨어는 저작권, 특허권, 상표권 및 기타 권리의 비침해성과 특정 목적에의 적합성 포함한 명시적, 묵시적인 어떠한 종류의 보증 없이 “있는 그대로” 제공됩니다.

        -

        ​

        -

        어떠한 경우에도 저작권자는 본 폰트 소프트웨어의 사용 또는 이의 사용 불가, 그밖에 폰트 소프트웨어의 취급과 관련하여 발생하는 모든 계약, 불법행위 혹은 다른 일로 하여금 발생하는 일반적, 특수적, 간접적, 부차적 혹은 필연적 손해를 포함하는 소송, 손해, 혹은 기타 책임에 대한 의무를 가지지 않습니다.

        - -
        -
        -
        - -
        -
        만족도 평가
      \ No newline at end of file diff --git a/apps/site/public/work/cheongon-care/assets/fonts/MaruBuri-Regular.woff2 b/apps/site/public/work/cheongon-care/assets/fonts/MaruBuri-Regular.woff2 deleted file mode 100644 index 3bc205f..0000000 Binary files a/apps/site/public/work/cheongon-care/assets/fonts/MaruBuri-Regular.woff2 and /dev/null differ diff --git a/apps/site/public/work/cheongon-care/assets/fonts/MaruBuri-SemiBold.woff2 b/apps/site/public/work/cheongon-care/assets/fonts/MaruBuri-SemiBold.woff2 deleted file mode 100644 index be593a5..0000000 Binary files a/apps/site/public/work/cheongon-care/assets/fonts/MaruBuri-SemiBold.woff2 and /dev/null differ diff --git a/apps/site/public/work/cheongon-care/assets/fonts/SOURCES.md b/apps/site/public/work/cheongon-care/assets/fonts/SOURCES.md deleted file mode 100644 index 66d2c89..0000000 --- a/apps/site/public/work/cheongon-care/assets/fonts/SOURCES.md +++ /dev/null @@ -1,13 +0,0 @@ -# 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. diff --git a/apps/site/public/work/cheongon-care/design.md b/apps/site/public/work/cheongon-care/design.md index 7529aa7..1b778f1 100644 --- a/apps/site/public/work/cheongon-care/design.md +++ b/apps/site/public/work/cheongon-care/design.md @@ -1,17 +1,78 @@ -# 청온 케어 — 디자인 결정 기록 +# design.md — 청온 케어 -## 관찰과 방향 +## 진행 상태 -- 관찰: 기존 첫 화면은 카드 안의 사진과 떠 있는 수치 배지가 서비스 범위보다 먼저 읽혔다. -- 원리 적용 범위: 소개와 견적 행동은 왼쪽 읽기 폭에, 생활 공간 사진과 32개 검수 항목 예시는 오른쪽 한 장면에 둔다. 견적은 입력 작업표와 합계 명세를 분리한다. -- 결정: 이중 프레임·떠 있는 배지를 제거하고 사진 캡션으로 검수 항목의 맥락을 연결했다. 성능 수치·인증·의학적 표현은 근거가 없으므로 작업표에 기록할 확인 절차로 바꿨다. -- 기각 조건: 사진이 견적 행동을 밀거나 작은 폭에서 입력과 합계가 동시에 읽히지 않으면 1열 순서와 간격을 다시 조정한다. +- 2026-10-06 재작업(전체·리디자인 경로, 방향 선택은 사용자 위임). 0 현행 감사 → 1 레퍼런스 → 2 카드 3장 중 A → 3 토큰 → 4 구현(index + protocol 공통 셸) → 5 감사 통과 → 6 이 문서. 완료. -## 토큰과 검증 +## 브리프 -- 타입: `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이다. -- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다. +- 무엇을: 가상 입주·이사·사무실 청소 브랜드의 견적 페이지 한 장 + 작업 절차 2차 페이지. 정적 HTML/CSS + 인라인 JS, 쇼케이스 사이트(`apps/site/public/work/cheongon-care/`) 안. +- 누구에게: 입주·이사를 앞둔 사람이 평형과 옵션을 골라 예상 금액을 보고 견적을 요청한다. 보조로 운영 화면 예시(접수·배차)를 같은 페이지에서 전환해 본다. +- 제약: 기본 언어 ko-KR(기존 로컬라이제이션). 상호·단가·옵션 가격·32개 항목·6단계 문구는 기존 파일 값 유지. 새 이미지 금지. 서체는 OFL·공개 배포본. plan.md 배정값(아래 지문 블록)이 제약이다. +- 화면 목록: `index.html`(견적 계산기 → 절차 → 장비 → 요청 푸터, 운영 화면 예시 모드) · `protocol/index.html`(절차 상세, 같은 셸). + +## 미확정 + +- 대표 전화번호 02-000-4172 — 푸터 연락처 줄, 전화 다이얼로그. 예시 값이다(사용자 결정 2026-10-06, 다이얼로그와 푸터 고지에 명시). +- 응답 시간·서비스 지역 — 기존 파일에 없어 사실 목록에 넣지 않았다. 값이 오면 `.facts`에 행 하나 추가. +- og:image — 전용 1200×630 이미지가 없어 기존 사진의 절대 URL을 가리킨다. +- 방향 카드 선택(A)은 위임으로 정했다. + +## 레퍼런스 + +- R1 구조: Homeaglow https://www.homeaglow.com/ — 중간 크기 표제 아래 입력 한 칸, 옆에 가격 사실 3줄($19·$59/월·7일 예약). 가져온 원리: 표제 작게, 사실 목록, 입력이 첫 화면. +- R1′ 같은 시장: 청연 https://www.chyeon.com/, 미소 https://miso.kr/ — 앱 다운로드 히어로·상단 바·가격 없음. 가져온 원리: 가격을 첫 화면에 두는 것이 이 시장에서 차별점이다. +- R2 톤: 기존 design.md의 "입력 작업표와 합계 명세 분리" 결정 재사용. +- R3 디테일: designpaca `mobile-app-ux.md` safe area 절(`viewport-fit=cover` + `env()` + `max()`). +- 미검증: 숨고 검색 페이지(404), 당근 비즈프로필 하단 바(페처 불가). + +## 방향 + +지문: 히어로 데이터·도구(견적 패널이 첫 화면, 표제 작게, 사실 목록 4줄, 행동은 패널 안 링크) · 표제/본문 Wanted Sans / Wanted Sans · 바탕 밝음·쿨 #eef3f6 · 강조 187° #156a75 · radius 8px · 흐름 견적 계산기 → 6단계 번호 목록 → 장비 누끼 + 기록 4항목 → 견적 요청(푸터) · 내비 없음 + 모바일 하단 고정 바(전화·견적) · 푸터 = 견적 요청 폼 + 상호·전화 placeholder·고지 한 줄 · 이미지 multiply + 방사형 마스크 누끼 · 모션 없음(폼 상태 색·테두리 전환 160ms만) + +- 한 문장 컨셉: 입주를 앞둔 사람이 공간 유형·평형·옵션을 고르고 합계를 본 뒤 연락처를 남긴다. +- 프리셋: swiss-minimal 지배(흰 작업 패널, 선으로 나눈 사실 목록) + 보조 없음. 강조색은 행동·합계·활성 상태에만. +- 표현 리스크: 합계 숫자(44px)가 페이지에서 가장 큰 글자이고 표제(28px)보다 크다 — 도구가 주인이라는 구도를 글자 크기로 말한다. 검증: 회색조 캡처 첫 시선이 합계였다. +- 표면 다이얼: 고객 화면 표현성 1·밀도 2 / 운영 화면 예시 표현성 0·밀도 3·증거 노출(예시 고지) 3. +- 제시 카드: A 계산기가 첫 화면(채택) · B 영수증 한 장(기각 — 1440 폭 낭비, 390에서 합계가 멀다) · C 단가표가 먼저(기각 — 390 표 변환, 행동까지 읽을 것이 많다). A를 고른 이유: 배정 제약과 R1 구성이 같고, 기존 E2E가 슬라이더·유형·합계가 한 화면에서 바뀌는 것을 단언한다. +- 반사실: 고유 요소 — 단가 3종 버튼(11,000/9,500/14,500), 32개 항목 검수 사실, 옵션 4종 가격. 같은 업종 치환에서 값이 바뀌어야 하고, 세무사무소 치환에서 평형 슬라이더가 무의미 → 통과. 수렴: plan 분포에서 같은 내비 형태인 thursday-flowers와 흐름·푸터·이미지·모션이 다르다(B-2 2/6 같음). +- 보존 자산: 상호, 단가·옵션 가격, 32개 항목·6단계 문구, 운영 표 예시 행, `assets/*.webp` 3장, `#calculator`·`#pyeongSlider`·`#sumTotal`·`.type-btn[data-base]`·`#btnModeAdmin/#btnModeClient`·`#qcModal`·`.card a[href='../index.html#calculator']`(E2E 계약). + +## 토큰과 예산 + +- 값은 `styles/tokens.css`. 역할: `--surface`(바탕) / `--surface-raised`(패널·입력) / `--surface-card`(합계 면·표 헤더) / `--accent`(행동·활성·절차 번호) / `--accent-hover`(합계 숫자·호버) / `--ink-faint`(가격·눈금, 흰 위 5.36:1) / 상태 알약 3종은 브랜드 강조와 분리. +- 타입: step--1 13 / 0 16 / 1 19 / 2 22~28(h1·섹션 제목) / 3 32~44(합계 한 곳). measure 36em. 서체 하나(Wanted Sans Variable, jsdelivr split 서브셋, swap). 모노 없음 — 숫자는 `tabular-nums`. +- 간격 토큰 1~8, 컨테이너 78rem, 컨트롤 44px, 하단 바 높이 4.25rem + safe area. +- 예산: tokens §5 기본선. 실측 — 인라인 JS 약 5KB, 첫 화면 이미지 0장, 아래 사진 2장 lazy(84KB·72KB). 폰트 전송량은 split 서브셋이라 쓰인 글자 범위만 — 미측정. + +## 이펙트와 폴백 + +- 누끼 대체: `.gear-cutout img { mix-blend-mode: multiply; mask-image: radial-gradient(ellipse …) }`. 마스크 미지원이면 직사각 사진 그대로 보인다(완성된 상태). +- 폼 상태 전환 160ms 색·테두리. 감소 모션에서 1ms. +- 그 밖의 모션·재질 없음. + +## 하지 않기로 한 것 + +- 상단 바 내비·sticky·backdrop-filter — 한 장짜리에 행동 하나라 차례가 필요 없다. +- 히어로 사진 — 도구가 주인인 구도가 포기한 것. `clean-hero.webp`는 남기되 쓰지 않는다. +- eyebrow·영문 라벨·가운뎃점 메타 문자열 — 라벨은 `dt` 한 낱말. +- JetBrains Mono·MaruBuri — 한글 라벨에 라틴 모노를 쓰지 않고, 쓰지 않는 표제 서체를 싣지 않는다(`assets/fonts/` 삭제). +- 지어낸 전화번호·응답 시간·지역 — placeholder 또는 생략. +- 스크롤 리빌·등장 모션 — 배정표에 없다. + +## 검증 + +- 자동: html-validate 0 오류(2페이지) · stylelint 0 · no-hanja PASS · integrity cheongon-care 5/5 · design-gate 충족 28/미검증 0/실패 0(폭 320·390·768·1440·1920, axe AA 0건) · showcase-audit PASS 12/0 · archive E2E cheongon 블록 24/24. +- 수동: 키보드 순회 1440·390 전부 도달·포커스 링 보임 · 전화 다이얼로그 Esc 닫힘·포커스 복귀 · 감소 모션 1ms · 대비 실측(흰/강조 6.27, 합계/회색 면 7.14, 희미/흰 5.36, 알약 6.56~7.42; 어두운 면 위 텍스트 없음) · h1·h2 마지막 줄 어절 ≥2. +- 시각 루브릭(약한 축부터): R5 1 · R6 1 · R9 1 · R1 2 · R2 2 · R3 2 · R4 2 · R7 2 · R8 2 · R10 2 = 17/20. 대표 화면 index, 2차는 캡처만 열어 봄. 근거는 `outputs/rework-2026-10-06/cheongon-care.md`. +- 미검증: 하단 바 국내 실측 사례, 실기기 safe area, 200% 확대 캡처, WebKit, 폰트 전송량. + +## 피드백 기록 · 보존 목록 + +- 2026-10-02 카피 교정에서 정한 업종 어휘(입주청소, 평당 단가, 줄눈, 추가 옵션, 팀장)와 합니다체는 유지했다. + +## 변경 기록 + +- 2026-08 초기: ledger-console 프리셋, 5:7 소개/사진 히어로, 입력 작업표/합계 명세 분리, verify-final 18묶음 통과. +- 2026-10-02 카피 교정: "공간의 숨결"·"케어 솔루션"·영문 직함 제거, 합니다체 통일. +- 2026-10-06 재작업: 히어로를 견적 패널로, 내비 제거 + 하단 행동 바, 푸터를 요청 폼으로, 서체 Wanted Sans 하나, radius 8 한 값, eyebrow 0, 가운뎃점 메타 0, 2차 페이지 공통 셸·인라인 style 제거, 운영 표 행 추가를 DOM 생성으로 변경. diff --git a/apps/site/public/work/cheongon-care/index.html b/apps/site/public/work/cheongon-care/index.html index e6f8e79..bf8772d 100644 --- a/apps/site/public/work/cheongon-care/index.html +++ b/apps/site/public/work/cheongon-care/index.html @@ -3,372 +3,248 @@ - 청온 케어 · 프리미엄 주거·오피스 위생 케어 안내 + 청온 케어 입주청소 견적 - - + + - + + + + - - - - - + + - +
      -
      - -
      -
      -
      -
      - 현장 검수 절차 예시 -
      -

      - 보이지 않는 오염까지,
      - 공간을 새롭게 돌봅니다 -

      -

      - 공간 유형과 선택 항목을 바탕으로 케어 범위와 예상 금액을 살펴볼 수 있습니다. -

      -
      - - 항목별 예상 견적 보기 - - -
      - 선택 항목별 예상 금액을 확인하세요 + +
      +
      +
      +

      입주·이사 청소 견적, 평형과 옵션을 고르면 바로 나옵니다

      +
      +
      +
      기본 단가
      +
      평당 9,500원부터
      -
      -

      안내: 이 페이지의 서비스·가격·운영 현황은 예시입니다. 입력 정보는 저장·전송되지 않으며 실제 견적이나 신청으로 이어지지 않습니다.

      +
      +
      범위
      +
      주거 입주와 이사, 사무실 정기, 준공 청소
      +
      +
      +
      검수
      +
      32개 항목을 현장 팀장과 함께 확인하고 서명
      +
      +
      +
      추가 옵션
      +
      스팀, 피톤치드, 에어컨 분해, 유리창 발수 4종
      +
      +
      -
      -
      - 정돈된 주거 공간의 거실 전경 -
      주거 공간 케어 예시 · 현장에서는 32개 검수 항목을 확인하는 흐름을 표시합니다.
      -
      -
      -
      -
      - - -
      -
      -
      -

      견적 작업표

      -

      항목별 예상 견적

      -

      공간 면적과 선택 항목을 기준으로 예상 금액의 구성 방식을 확인합니다.

      -
      - -
      -
      -
      - -
      - -
      - - - -
      +
      + +
      + 공간 유형 +
      + + +
      +
      - -
      -
      - - 32 평 (약 105㎡) -
      - +
      +
      + + 32평 (약 105㎡)
      + + +
      - -
      - 정밀 특수 옵션 -
      - - - - -
      +
      + 추가 옵션 +
      + + + +
      +
      + - -
      - 신청자 정보 -
      - - - - -
      - - +
      +

      예상 견적

      +
      +
      +
      공간
      +
      주거 입주/이사
      - -
      - - -
      -

      예상 견적 명세

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

      - 선택 항목에 따른 예상 금액을 확인할 수 있습니다. 실제 견적·신청은 진행되지 않습니다. -

      + 이 금액으로 견적 요청 +

      예시 금액입니다. 실제 견적과 신청은 진행되지 않습니다.

      - -
      -
      -
      -
      -

      6단계 현장 검수 절차 예시

      -

      작업 순서와 확인 항목을 기록하는 방식의 예시입니다. 결과나 성능을 보증하지 않습니다.

      -
      - - 검수 절차 예시 보기 → - -
      - -
      -
      -
      STEP 01
      -

      건식 초미세 집진

      -

      벽면, 천장 몰딩, 바닥의 시공 잔여물을 확인하고 흡진 작업 여부를 기록합니다.

      -
      - -
      -
      STEP 02
      -

      스팀 장비 설정 확인

      -

      배수구, 욕실 타일 틈새, 주방 후드처럼 확인이 필요한 면과 장비 설정을 작업표에 기록합니다.

      -
      - -
      -
      STEP 03
      -

      표면별 세정 확인

      -

      표면 재질과 사용 제품 정보를 작업표에 기록하고, 적용 전후 상태를 함께 확인합니다.

      -
      - -
      -
      STEP 04
      -

      서랍·후드 전면 탈거 세척

      -

      필요한 분리 작업의 범위와 접근 가능한 안쪽 면을 확인 항목으로 남깁니다.

      -
      - -
      -
      STEP 05
      -

      환기와 마감 확인

      -

      환기와 마감 상태를 확인하고, 사용한 향·마감 제품이 있다면 작업표에 표시합니다.

      -
      - -
      -
      STEP 06
      -

      32개 항목 마스터 검수

      -

      현장 담당자와 의뢰자가 검수 항목을 함께 확인하는 흐름을 예시로 표시합니다.

      -
      -
      -
      -
      - - -
      + +
      -

      작업 장비와 검수 기록

      -

      현장에서 확인할 장비·필터·제품·마감 항목을 작업표에 표시하는 예시입니다.

      +

      6단계로 작업하고, 단계마다 쓴 장비와 제품을 작업표에 적습니다

      + 절차 상세 보기
      -
      -
      -
      - 장비 기록 - 사용 장비와 설정값을 작업표에 표시 -
      -
      - 필터 확인 - 필터 종류와 교체 상태를 현장에서 확인 -
      -
      - 사용 제품 - 제품명과 적용 표면을 작업표에 기록 -
      -
      - 마감 확인 - 환기·표면 상태를 검수 항목으로 확인 -
      -
      +
        +
      1. +

        공사 먼지 흡진

        +

        벽면, 천장 몰딩, 바닥의 시공 잔여물을 확인하고 흡진 작업 여부를 기록합니다.

        +
      2. +
      3. +

        스팀 장비 설정 확인

        +

        배수구, 욕실 타일 줄눈, 주방 후드를 스팀으로 청소하고 장비 온도 설정을 작업표에 적습니다.

        +
      4. +
      5. +

        재질에 맞는 세제 사용

        +

        대리석, 원목, 스테인리스 등 재질에 맞는 세제를 쓰고 사용한 제품을 작업표에 적습니다.

        +
      6. +
      7. +

        서랍과 후드 분리 세척

        +

        서랍과 후드 필터를 떼어 내 안쪽까지 닦고, 분리한 부위를 작업표에 표시합니다.

        +
      8. +
      9. +

        환기와 마감 확인

        +

        환기와 마감 상태를 확인하고, 사용한 향이나 마감 제품이 있으면 작업표에 표시합니다.

        +
      10. +
      11. +

        32개 항목 최종 검수

        +

        현장 팀장과 고객님이 32개 검수 항목을 작업표로 함께 확인하고 서명합니다.

        +
      12. +
      +
      +
      -
      - 청온 케어 마스터 위생 장비와 정밀 세척 노즐 - 작업 장비 예시 - 검수 항목 확인 -
      + +
      +
      +
      + 청온 케어 현장에서 쓰는 스팀 청소기와 세척 노즐, 극세사 천, 세제 샘플 +
      스팀 청소기, 극세사 천, 세제 샘플. 현장에서 쓰는 장비와 제품입니다.
      +
      +
      +

      장비 설정값과 사용 제품은 현장에서 작업표에 적습니다

      +
      +
      +
      장비
      +
      사용 장비와 설정값을 작업표에 표시합니다.
      +
      +
      +
      필터
      +
      필터 종류와 교체 상태를 현장에서 확인합니다.
      +
      +
      +
      제품
      +
      제품명과 적용 표면을 작업표에 기록합니다.
      +
      +
      +
      마감
      +
      환기와 표면 상태를 검수 항목으로 확인합니다.
      +
      +
      - +
      -
      운영 현황
      -

      오늘의 케어 현황

      -

      아래 접수·배차 정보는 운영 화면 예시입니다. 실제 고객 정보나 접수 내역이 아닙니다.

      +

      오늘 작업 현황

      +

      아래 접수와 배차 정보는 운영 화면 예시입니다. 실제 고객 정보나 접수 내역이 아닙니다.

      - +
      -
      오늘 신규 접수
      @@ -379,34 +255,33 @@
      4개 조
      -
      검수 대기 / 완료
      +
      검수 대기
      2건
      오늘 견적 총액
      -
      2,480,000원
      +
      2,480,000원
      -
      - 접수·배차 현황 -
      - - - - + 접수와 배차 현황 +
      + + + +
      -
      +
      - - + + @@ -416,108 +291,157 @@ - - + + - - + + - + - - - + + + - + - - + + - - - + + + - - + + - +
      접수번호고객명 / 연락처공간 / 면적고객명공간 현장 주소 견적액 배정 팀장
      #CO-2026-0819이서연
      010-4821-9930
      #CO-2026-0819이서연
      010-4821-9930
      주거 입주 (32평) 성동구 성수동 아크로포레스트432,000원김정온 마스터432,000원김정온 팀장 (3팀) 신규 접수
      #CO-2026-0818박도현
      010-9120-3341
      오피스 정기 (45평)#CO-2026-0818박도현
      010-9120-3341
      사무실 정기 (45평) 강남구 테헤란로 142 위워크520,000원520,000원 박준호 팀장 (2팀)스팀 케어 중작업 중
      #CO-2026-0817한지민
      010-7712-4091
      하이엔드 준공 (58평)#CO-2026-0817한지민
      010-7712-4091
      준공 청소 (58평) 용산구 한남동 나인원한남841,000원최수진 닥터 (1팀)841,000원최수진 팀장 (1팀) 검수 승인 완료
      + + +
      + 화면 +
      + + +
      +
      - +
      -

      32개 정밀 위생 검수 체크리스트

      - +

      32개 항목 검수표

      +
      -
      +

      32개 항목 가운데 8개를 예시로 보입니다.

      +
      - +
      - + + +

      전화 문의

      +

      02-000-4172

      +

      예시 번호라 실제로 연결되지 않습니다. 견적 요청 폼으로 흐름을 확인할 수 있습니다.

      +
      + +
      +
      + + - + +
      + + 견적 요청 +
      + diff --git a/apps/site/public/work/cheongon-care/protocol/index.html b/apps/site/public/work/cheongon-care/protocol/index.html index a23056c..4e984df 100644 --- a/apps/site/public/work/cheongon-care/protocol/index.html +++ b/apps/site/public/work/cheongon-care/protocol/index.html @@ -1,343 +1,185 @@ - + - 6단계 위생 케어 절차 · 예시 리포트 — 청온 케어 - - + 청온 케어 6단계 작업 절차 + + + + + + + + + - - - + + + - - - - + - - + +
      -
      - -
      +
      + +
      - + +

      6단계로 청소하고, 단계마다 작업표에 적습니다

      +
      +
      +
      단계
      +
      흡진, 스팀, 세제, 분리 세척, 환기와 마감, 최종 검수 6단계
      +
      +
      +
      기록
      +
      장비 설정값, 필터 상태, 사용 제품, 마감 상태 4가지
      +
      +
      +
      검수
      +
      32개 항목을 현장 팀장과 고객이 함께 확인하고 서명
      +
      +
      +

      아래 절차와 기록값은 예시입니다. 실제 시공 결과를 보장하거나 예약을 접수하지 않습니다.

      +
      +
      -
      -

      CARE CHECKLIST EXAMPLE

      -

      6단계 위생 케어 절차

      -

      - 현장 장비, 필터, 표면, 마감 상태를 작업표에 기록하는 절차와 확인 항목을 소개합니다. -

      -

      안내: 아래 절차와 측정 수치는 예시입니다. 실제 시공 결과를 보장하거나 예약을 접수하지 않습니다.

      - -
      -
      -
      Steam Temp
      -
      현장 장비 설정 기록
      + +
      +
      +
      + 장갑을 낀 손이 잡은 휴대용 측정기와 면봉 검체 키트 +
      표면 점검에 쓰는 휴대용 측정기. 읽은 값은 작업표에 적습니다.
      +
      +
      +

      점검값은 전과 후를 나란히 적습니다

      +

      아래는 점검 기록이 작업표에 표시되는 방식의 예시입니다. 실제 현장 측정값이나 시공 결과가 아닙니다.

      +
      +
      +
      점검 전
      +
      기록값 A
      +
      확인할 항목 표시
      -
      -
      Filtration
      -
      필터 종류·상태 확인
      -
      -
      -
      Detergent
      -
      사용 제품·표면 기록
      -
      -
      -
      QC Points
      -
      32개 정밀 항목
      +
      +
      점검 후
      +
      기록값 B
      +
      확인 완료 표시
      - -
      - 정돈된 주거 공간의 실내 전경 -
      - 주거 공간 · 6단계 절차 예시 -
      -
      - -
      -
      -
      - 검수 기록에 사용하는 계측 장비 화면 예시 -
      -
      -
      검수 기록 표시 예시
      -

      - 주거 공간 예시
      - 점검값 기록 방식 -

      -

      - 아래 값은 점검 기록이 표시되는 방식을 설명하기 위한 예시입니다. 실제 현장 측정값이나 시공 결과를 뜻하지 않습니다. -

      - -
      -
      -
      점검 전 기록
      -
      기록값 A
      -
      확인할 항목 표시 예시
      -
      -
      -
      점검 후 기록
      -
      기록값 B
      -
      확인 완료 표시 예시
      -
      -
      + +
      +
      +
      +

      단계마다 무엇을 하고 무엇을 적는지

      +
        +
      1. +

        공사 먼지 흡진

        +

        벽면, 천장 몰딩, 조명 레일의 작업 범위를 확인하고 흡진 작업 여부를 작업표에 기록합니다.

        +
      2. +
      3. +

        스팀 장비 설정 확인

        +

        배수구, 욕실 타일 줄눈, 주방 후드를 스팀으로 청소하고 장비 온도 설정을 적습니다.

        +
      4. +
      5. +

        재질에 맞는 세제 사용

        +

        바닥과 석재의 재질을 확인한 뒤 사용 제품과 적용 범위를 작업표에 표시합니다.

        +
      6. +
      7. +

        서랍장과 걸레받이 분리 세척

        +

        싱크대 하부 걸레받이와 수납장 서랍을 떼어 내 레일 안쪽까지 닦고, 분리한 부위를 기록합니다.

        +
      8. +
      9. +

        환기와 마감 확인

        +

        환기와 마감 상태를 확인하고, 향이나 마감 제품을 사용했다면 제품명과 적용 범위를 기록합니다.

        +
      10. +
      11. +

        32개 항목 최종 검수

        +

        현장 팀장과 고객님이 32개 항목 검수표를 함께 확인하고 서명합니다.

        +
      12. +
      - -
      -
      단계별 시공 절차
      -

      - 청온 케어 6단계 검수 절차 예시 -

      - -
      -
      -
      STEP 01
      -
      -

      필터 흡진 작업 확인

      -

      - 벽면, 천장 몰딩, 조명 레일의 작업 범위를 확인하고 흡진 작업 여부를 작업표에 기록합니다. -

      -
      -
      - -
      -
      STEP 02
      -
      -

      스팀 장비 설정 확인

      -

      - 배수구, 욕실 틈새, 주방 후드처럼 확인이 필요한 면을 구분하고 사용 장비 설정을 남깁니다. -

      -
      -
      - -
      -
      STEP 03
      -
      -

      표면별 세정 확인

      -

      - 바닥과 석재의 재질을 확인한 뒤 사용 제품과 적용 범위를 작업표에 표시합니다. -

      -
      -
      - -
      -
      STEP 04
      -
      -

      서랍장·걸레받이 전면 탈거 정밀 세척

      -

      - 싱크대 하부와 수납장 레일의 접근 가능 여부를 확인하고, 분리 작업 범위를 기록합니다. -

      -
      -
      - -
      -
      STEP 05
      -
      -

      환기와 마감 확인

      -

      - 환기와 마감 상태를 확인하고, 향·마감 제품을 사용했다면 제품명과 적용 범위를 기록합니다. -

      -
      -
      - -
      -
      STEP 06
      -
      -

      32개 항목 현장 검수 확인

      -

      - 현장 담당자와 의뢰자가 32개 체크리스트를 함께 확인하는 흐름을 예시로 표시합니다. -

      -
      -
      -
      -
      - - -
      -
      - 작업 장비와 검수 키트를 보여 주는 예시 이미지 -
      -
      - - -
      -
      -
      정찰제 가격표
      -

      - 우리 집 공간의 정찰제 견적을 확인하세요 -

      -

      - 선택 항목을 기준으로 예상 금액을 확인할 수 있습니다. 실제 견적이나 신청은 진행되지 않습니다. -

      - - 항목별 예상 견적 보기 → - + +
      +
      +

      평형과 옵션을 고르면 예상 금액이 바로 나옵니다

      +

      평당 9,500원부터, 추가 옵션 4종. 예시 금액이며 실제 견적이나 신청은 진행되지 않습니다.

      + 견적 계산하기
      - + +

      전화 문의

      +

      [대표 전화번호]

      +

      청온 케어는 가상 브랜드라 실제 전화번호가 없습니다. 견적 요청 폼으로 흐름을 확인할 수 있습니다.

      +
      + +
      +
      +
      -
      -

      청온 케어 · 위생 케어 안내

      -

      이 페이지의 절차와 수치는 예시이며 실제 예약은 접수되지 않습니다.

      +
      +
      +

      견적을 요청합니다

      +

      첫 페이지에서 평형과 옵션을 고른 뒤 연락처를 남기면 현장 팀장이 일정을 잡습니다.

      +
      +
      + 견적 요청으로 가기 +

      예시 화면입니다. 입력한 정보는 저장하거나 전송하지 않습니다.

      +
      +
      +
      +

      청온 케어

      +

      전화 [대표 전화번호]

      +

      이 페이지의 절차와 수치는 예시이며 실제 예약은 접수되지 않습니다.

      + +
      + + 견적 요청 +
      + + diff --git a/apps/site/public/work/cheongon-care/styles/fonts.css b/apps/site/public/work/cheongon-care/styles/fonts.css deleted file mode 100644 index f5234d6..0000000 --- a/apps/site/public/work/cheongon-care/styles/fonts.css +++ /dev/null @@ -1,15 +0,0 @@ -@font-face { - font-family: "MaruBuri"; - src: url("../assets/fonts/MaruBuri-Regular.woff2") format("woff2"); - font-style: normal; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "MaruBuri"; - src: url("../assets/fonts/MaruBuri-SemiBold.woff2") format("woff2"); - font-style: normal; - font-weight: 600; - font-display: swap; -} diff --git a/apps/site/public/work/cheongon-care/styles/main.css b/apps/site/public/work/cheongon-care/styles/main.css index 25f961f..5d3fb24 100644 --- a/apps/site/public/work/cheongon-care/styles/main.css +++ b/apps/site/public/work/cheongon-care/styles/main.css @@ -1,11 +1,12 @@ -@import "./tokens.css?v=20260912all"; - /* ========================================================================== - 청온 케어 (CHEONG-ON CARE) — 메인 스타일시트 SSOT - 프리셋: clinical-sanitation + transparent-calculator + high-end-visual-design + 청온 케어 — 메인 스타일시트 + 공통 셸(머리·하단 행동 바·푸터·컨트롤)과 첫 페이지 섹션. 토큰은 tokens.css 한 곳에서만 정의한다. + 2차 페이지(protocol/)는 이 파일 + protocol.css 를 쓴다. ========================================================================== */ -*, *::before, *::after { +*, +*::before, +*::after { box-sizing: border-box; margin: 0; padding: 0; @@ -13,21 +14,47 @@ html { font-size: 16px; - scroll-behavior: smooth; background-color: var(--surface); color: var(--ink); font-family: var(--font-body); + font-synthesis-style: none; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - overflow-x: hidden; + scroll-padding-top: var(--space-6); } body { min-height: 100dvh; line-height: var(--leading-normal); - overflow-x: hidden; + overflow-x: clip; background: var(--surface); color: var(--ink); + font-variant-numeric: tabular-nums; +} + +/* 한글 조판 정본(typography §6-5) */ +:lang(ko) { + word-break: keep-all; + overflow-wrap: anywhere; + line-break: strict; +} + +:lang(ko) :is(p, li, dd, dt, figcaption) { + line-height: var(--leading-normal); + letter-spacing: 0; +} + +:lang(ko) :is(h1, h2, h3) { + line-height: var(--leading-tight); + text-wrap: balance; +} + +:lang(ko) :is(em, i, cite, dfn, var, address) { + font-style: normal; +} + +:lang(ko) a { + text-underline-position: under; } img { @@ -39,14 +66,13 @@ img { a { color: inherit; text-decoration: none; - transition: color var(--dur-quick); } -a:hover { - color: var(--accent); -} - -button, input, select, textarea { +button, +input, +select, +textarea, +output { font: inherit; color: inherit; } @@ -57,20 +83,13 @@ button { background: none; } -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; +fieldset { + border: 0; + min-width: 0; } -.mono { - font-family: var(--font-mono); +legend { + padding: 0; } .wrap { @@ -78,7 +97,18 @@ button { margin-inline: auto; } -/* ── 접근성 스킵 링크 ── */ +.num { + font-variant-numeric: tabular-nums; +} + +/* 사용자가 줄 값의 자리 — 대괄호 표기 + 점선 밑줄 한 가지 표식 */ +.placeholder { + color: var(--placeholder-ink); + text-decoration: underline dotted; + text-underline-offset: 0.2em; +} + +/* ── 접근성 ── */ .skip-link { position: fixed; top: 1rem; @@ -87,24 +117,23 @@ button { padding: 0.5rem 1rem; background: var(--surface-inverse); color: var(--ink-inverse); - border-radius: var(--radius-sm); + border-radius: var(--radius); transform: translateY(-200%); - transition: transform var(--dur-quick); - text-decoration: none; - font-weight: 500; + font-weight: var(--weight-medium); } + .skip-link:focus { transform: translateY(0); } -/* ── 헤더 & 뷰 모드 스위처 (GNB SSOT) ── */ +:where(a, button, input, select, textarea, [tabindex]):focus-visible { + outline: 3px solid var(--accent); + outline-offset: 2px; +} + +/* ── 공통 셸: 머리 — 내비 없음. 상호와 행동 하나 ── */ .site-head { - position: sticky; - top: 0; - z-index: 100; - background: rgba(248, 250, 249, 0.94); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); + background: var(--head-bg); border-bottom: 1px solid var(--line); padding-block: var(--space-3); } @@ -114,602 +143,532 @@ button { align-items: center; justify-content: space-between; gap: var(--space-3); - min-width: 0; + min-height: var(--control-block); } .wordmark { - display: inline-flex; - align-items: center; - gap: var(--space-2); font-size: var(--step-1); - font-weight: 800; - color: var(--ink); - text-decoration: none; - letter-spacing: -0.02em; + font-weight: var(--weight-strong); + letter-spacing: -0.01em; white-space: nowrap; - flex-shrink: 0; -} - -.wordmark-badge { - background: var(--accent); - color: #fff; - font-size: 11px; - font-weight: 700; - padding: 2px 6px; - border-radius: var(--radius); -} - -.wordmark-han { - font-size: var(--step--1); - color: var(--accent); - font-weight: 600; - letter-spacing: 0.05em; -} - -.nav { - display: flex; - align-items: center; - gap: var(--space-5); - list-style: none; -} - -.nav a { - text-decoration: none; - color: var(--ink-muted); - font-size: var(--step--1); - font-weight: 500; - transition: color var(--dur-quick); - white-space: nowrap; -} - -.nav a:hover, -.nav a.active { color: var(--ink); -} - -.head-actions { - display: flex; - align-items: center; - gap: var(--space-2); - flex-shrink: 0; - min-width: 0; -} - -.mode-toggle { - display: inline-flex; - background: var(--surface-subtle); - border: 1px solid var(--line); - border-radius: var(--radius-sm); - padding: 2px; - gap: 2px; -} - -.mode-btn { - padding: 4px 10px; - border-radius: calc(var(--radius-sm) - 2px); - font-size: 12px; - font-weight: 600; - color: var(--ink-muted); display: inline-flex; align-items: center; - gap: 5px; - transition: background-color var(--dur-quick), color var(--dur-quick), box-shadow var(--dur-quick); - white-space: nowrap; + min-height: var(--control-block); } -.mode-btn.is-active { - background: var(--surface-raised); - color: var(--ink); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06); -} - -.mode-btn .badge-dot { - width: 6px; - height: 6px; - border-radius: 50%; - background: #10b981; -} - -.head-actions .btn { +.head-action { display: inline-flex; align-items: center; justify-content: center; - background: var(--accent); - color: #fff; - font-weight: 700; - font-size: var(--step--1); - padding: 6px 14px; + min-height: var(--control-block); + padding-inline: var(--space-4); border-radius: var(--radius); - transition: opacity var(--dur-quick); + background: var(--accent); + color: var(--accent-ink); + font-weight: var(--weight-strong); + font-size: var(--step--1); + white-space: nowrap; + transition: background-color var(--dur-quick) var(--ease-soft); } -.head-actions .btn:hover { - opacity: 0.9; +.head-action:hover { + background: var(--accent-hover); } -/* ── 버튼 시스템 & 뱃지 ── */ +/* ── 버튼 ── */ .btn-primary { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); + min-height: var(--control-block); + padding: 0 var(--space-4); background: var(--accent); - color: #ffffff; - padding: 10px 20px; - border-radius: var(--radius-sm); - font-weight: 700; - font-size: var(--step--1); - text-decoration: none; + color: var(--accent-ink); + border-radius: var(--radius); + font-weight: var(--weight-strong); + font-size: var(--step-0); white-space: nowrap; - transition: transform var(--dur-quick), background var(--dur-quick); - border: none; + transition: background-color var(--dur-quick) var(--ease-soft); } .btn-primary:hover { background: var(--accent-hover); - color: #ffffff; - transform: translateY(-1px); } -.btn-outline { +.btn-small { + min-height: 2.25rem; + font-size: var(--step--1); + padding-inline: var(--space-3); +} + +.text-link { + display: inline-flex; + align-items: center; + min-height: var(--control-block); + color: var(--accent); + font-weight: var(--weight-strong); + text-decoration: underline; + text-underline-offset: 0.25em; + white-space: nowrap; +} + +.text-link:hover { + color: var(--accent-hover); +} + +.action-btn { display: inline-flex; align-items: center; justify-content: center; - gap: var(--space-2); - background: transparent; - color: var(--ink); - border: 1px solid var(--line); - padding: 10px 18px; - border-radius: var(--radius-sm); - font-weight: 600; + min-height: 2.25rem; + padding-inline: var(--control-pad-inline); + border: 1px solid var(--line-strong); + border-radius: var(--radius); font-size: var(--step--1); - text-decoration: none; + font-weight: var(--weight-medium); + background: var(--surface-raised); + color: var(--ink); white-space: nowrap; - transition: border-color var(--dur-quick), color var(--dur-quick); + transition: border-color var(--dur-quick) var(--ease-soft), color var(--dur-quick) var(--ease-soft); } -.btn-outline:hover { +.action-btn:hover { border-color: var(--accent); color: var(--accent); } -.eyebrow { +.icon-btn { display: inline-flex; align-items: center; - gap: 6px; - padding: 3px 10px; + justify-content: center; + width: var(--control-block); + height: var(--control-block); border-radius: var(--radius); - background: var(--accent-light); - color: var(--accent); - font-size: 11px; - font-family: var(--font-body); - font-weight: 700; - letter-spacing: 0.02em; - margin-bottom: var(--space-3); + font-size: var(--step-1); + color: var(--ink-muted); } -/* ── 히어로 섹션 ── */ -.hero-section { - padding-block: var(--space-7); - position: relative; +.icon-btn:hover { + background: var(--surface-card); + color: var(--ink); } -.hero-grid { +/* ── 섹션 공통 ── */ +.section-title { + font-size: var(--step-2); + font-weight: var(--weight-strong); + letter-spacing: -0.015em; + color: var(--ink); + max-width: 24em; +} + +.section-head { + display: flex; + justify-content: space-between; + align-items: flex-end; + flex-wrap: wrap; + gap: var(--space-2) var(--space-4); + margin-bottom: var(--space-5); +} + +/* ── 1. 첫 화면: 견적 계산기 ── */ +.quote { + padding-block: var(--space-5) var(--space-7); +} + +.quote-grid { display: grid; - grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); - gap: var(--space-7); - align-items: center; + grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); + gap: var(--space-6); + align-items: start; } -.hero-title { - font-size: clamp(2.4rem, 1.8rem + 2.2vw, 3.8rem); - font-weight: 800; - line-height: 1.18; - letter-spacing: -0.035em; +.quote-title { + font-size: var(--step-2); + font-weight: var(--weight-strong); + letter-spacing: -0.015em; color: var(--ink); margin-bottom: var(--space-4); - text-wrap: balance; - word-break: keep-all; + max-width: 18em; } -.hero-title em { - font-style: normal; - color: var(--accent); -} - -.hero-desc { - font-size: clamp(1.05rem, 0.98rem + 0.35vw, 1.2rem); - color: var(--ink-muted); - max-width: 48ch; - line-height: 1.75; - margin-bottom: var(--space-5); - text-wrap: pretty; - word-break: keep-all; -} - -.hero-ctas { - display: flex; - align-items: center; - gap: var(--space-3); - flex-wrap: wrap; -} - -.hero-trust { - display: flex; - align-items: center; - gap: var(--space-2); - font-size: var(--step--1); - color: var(--ink-muted); -} - -.hero-trust-stars { - color: #f59e0b; - font-size: 13px; -} - -.hero-visual { - position: relative; -} - -.hero-frame { - margin: 0; - border: 1px solid var(--line); - background: #ffffff; -} - -.hero-frame img { - width: 100%; - aspect-ratio: 5/4; - object-fit: cover; - display: block; -} - -.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 { - padding-block: var(--space-8); - border-top: 1px solid var(--line); -} - -.calc-section .section-head { - text-align: left; - max-width: 48rem; - margin-inline: 0; - margin-bottom: var(--space-6); -} - -.section-title { - font-size: clamp(1.8rem, 1.4rem + 1.2vw, 2.25rem); - font-weight: 800; - letter-spacing: -0.025em; - color: var(--ink); - margin-bottom: var(--space-2); - text-wrap: balance; - word-break: keep-all; -} - -.section-sub { - font-size: var(--step-0); - color: var(--ink-muted); - line-height: 1.6; - text-wrap: pretty; - word-break: keep-all; -} - -.calc-grid { +.facts { display: grid; - grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr); - gap: var(--space-5); - align-items: start; - min-width: 0; + gap: 0; + border-top: 1px solid var(--line-strong); } -.calc-card { +.fact { + display: grid; + grid-template-columns: 6em minmax(0, 1fr); + gap: var(--space-3); + padding-block: var(--space-2); + border-bottom: 1px solid var(--line); + font-size: var(--step-0); +} + +.fact dt { + color: var(--ink-muted); + font-weight: var(--weight-medium); +} + +.fact dd { + color: var(--ink); +} + +/* 작업 패널: 입력과 합계가 한 상자 안에 있다 */ +.quote-tool { + display: grid; + grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); background: var(--surface-raised); border: 1px solid var(--line); - border-radius: var(--radius-md); + border-radius: var(--radius); + box-shadow: var(--shadow-panel); + overflow: hidden; +} + +.quote-form { padding: var(--space-5); min-width: 0; } -.form-group { - margin-bottom: var(--space-4); +.form-group + .form-group { + margin-top: var(--space-5); } .form-label { display: block; font-size: var(--step--1); - font-weight: 700; + font-weight: var(--weight-strong); color: var(--ink); margin-bottom: var(--space-2); - letter-spacing: -0.01em; } .type-selector { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); - min-width: 0; } .type-btn { - padding: 10px 8px; - border: 1px solid var(--line); - border-radius: var(--radius-sm); - background: var(--surface); + min-height: var(--control-block); + padding: var(--space-2); + border: 1px solid var(--line-strong); + border-radius: var(--radius); + background: var(--surface-raised); color: var(--ink-muted); - font-weight: 600; + font-weight: var(--weight-medium); font-size: var(--step--1); text-align: center; - transition: background-color var(--dur-quick), border-color var(--dur-quick), color var(--dur-quick); - white-space: normal; - word-break: keep-all; + transition: background-color var(--dur-quick) var(--ease-soft), border-color var(--dur-quick) var(--ease-soft), color var(--dur-quick) var(--ease-soft); +} + +.type-btn:hover { + border-color: var(--accent); + color: var(--ink); } .type-btn.is-active { border-color: var(--accent); background: var(--accent-light); - color: var(--accent); + color: var(--accent-hover); + font-weight: var(--weight-strong); } .slider-header { display: flex; justify-content: space-between; align-items: baseline; - margin-bottom: var(--space-2); + gap: var(--space-3); } -.slider-value { - font-family: var(--font-mono); +.slider-val { font-size: var(--step-1); - font-weight: 700; + font-weight: var(--weight-strong); color: var(--accent); } -.range-slider { +.range-input { + display: block; width: 100%; + min-height: var(--control-block); accent-color: var(--accent); - height: 6px; cursor: pointer; } +.slider-scale { + display: flex; + justify-content: space-between; + font-size: var(--step--1); + color: var(--ink-faint); +} + .addon-grid { display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-columns: minmax(0, 1fr); gap: var(--space-2); - min-width: 0; } .addon-label { - display: flex; + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--space-2); - padding: 10px 12px; - border: 1px solid var(--line); - border-radius: var(--radius-sm); - background: var(--surface); + min-height: var(--control-block); + padding: var(--space-2) var(--control-pad-inline); + border: 1px solid var(--line-strong); + border-radius: var(--radius); + background: var(--surface-raised); font-size: var(--step--1); color: var(--ink-muted); cursor: pointer; - transition: background-color var(--dur-quick), border-color var(--dur-quick), color var(--dur-quick); + transition: background-color var(--dur-quick) var(--ease-soft), border-color var(--dur-quick) var(--ease-soft), color var(--dur-quick) var(--ease-soft); +} + +.addon-label input { + accent-color: var(--accent); + width: 1.1rem; + height: 1.1rem; +} + +.addon-price { + color: var(--ink-faint); + white-space: nowrap; } .addon-label:has(input:checked) { border-color: var(--accent); background: var(--accent-light); color: var(--ink); - font-weight: 600; } -/* 실시간 견적 영수증 요약 패널 */ -.calc-summary { - background: var(--surface-raised); - border: 1px solid var(--line); - border-radius: var(--radius-md); +.addon-label:has(input:checked) .addon-price { + color: var(--accent-hover); +} + +/* 합계 패널 — 숫자가 페이지에서 가장 큰 글자다 */ +.quote-summary { padding: var(--space-5); + background: var(--surface-card); + border-left: 1px solid var(--line); min-width: 0; + display: flex; + flex-direction: column; } .summary-title { - font-size: var(--step-1); - font-weight: 700; - margin-bottom: var(--space-4); - padding-bottom: var(--space-2); - border-bottom: 1px solid var(--line); + font-size: var(--step--1); + font-weight: var(--weight-strong); + color: var(--ink-muted); + margin-bottom: var(--space-3); +} + +.summary-list { + display: grid; + gap: var(--space-2); } .summary-row { display: flex; justify-content: space-between; - margin-bottom: var(--space-2); + gap: var(--space-3); font-size: var(--step--1); color: var(--ink-muted); } -.summary-row .mono { +.summary-row dd { color: var(--ink); - font-weight: 600; + font-weight: var(--weight-medium); + text-align: right; } .summary-total { - display: flex; - justify-content: space-between; - align-items: baseline; + margin-top: var(--space-4); padding-top: var(--space-3); - border-top: 1px solid var(--line); - margin-block: var(--space-4) var(--space-5); + border-top: 1px solid var(--line-strong); } -.summary-total span:first-child { - font-size: var(--step-0); - font-weight: 700; +.summary-total-label { + display: block; + font-size: var(--step--1); + font-weight: var(--weight-strong); + color: var(--ink-muted); } -.summary-total .total-amount { - font-family: var(--font-mono); - font-size: var(--step-2); - font-weight: 800; - color: var(--accent); +.total-amount { + display: block; + font-size: var(--step-3); + font-weight: var(--weight-strong); + letter-spacing: -0.02em; + line-height: 1.1; + white-space: nowrap; + color: var(--accent-hover); + margin-top: var(--space-1); } -.calc-submit { +.summary-cta { + margin-top: var(--space-4); width: 100%; - padding: 14px; - background: var(--accent); - color: #fff; - font-weight: 700; - font-size: var(--step-0); - border-radius: var(--radius-sm); - transition: background var(--dur-quick); } -.calc-submit:hover { - background: var(--accent-hover); +.summary-note { + margin-top: var(--space-2); + font-size: var(--step--1); + color: var(--ink-muted); } -/* ── 6단계 위생 프로토콜 섹션 ── */ -.protocol-section { - padding-block: var(--space-8); +/* ── 2. 절차 ── */ +.steps { + padding-block: var(--space-7); border-top: 1px solid var(--line); } -.protocol-grid { +.step-list { + list-style: none; + counter-reset: step; display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: var(--space-4); + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0 var(--space-6); + border-top: 1px solid var(--line-strong); } -.protocol-card { - background: var(--surface-raised); - border: 1px solid var(--line); - border-radius: var(--radius-sm); - padding: var(--space-5); - display: flex; - flex-direction: column; - gap: var(--space-2); +.step { + counter-increment: step; + display: grid; + grid-template-columns: 3rem minmax(0, 1fr); + gap: var(--space-3); + padding-block: var(--space-4); + border-bottom: 1px solid var(--line); } -.protocol-num { - font-family: var(--font-mono); - font-size: 11px; - font-weight: 700; +.step::before { + grid-row: 1 / span 2; + content: counter(step, decimal-leading-zero); + font-size: var(--step-1); + font-weight: var(--weight-strong); color: var(--accent); - letter-spacing: 0.06em; + line-height: var(--leading-tight); + padding-top: 0.1em; } -.protocol-card h3 { +.step h3, +.step p { + grid-column: 2; +} + +.step h3 { font-size: var(--step-0); - font-weight: 700; + font-weight: var(--weight-strong); color: var(--ink); } -.protocol-card p { +.step p { + margin-top: var(--space-1); font-size: var(--step--1); color: var(--ink-muted); - line-height: 1.6; + max-width: var(--measure); } -/* ── 전문 장비 & Before/After 섹션 ── */ -.equipment-section { - padding-block: var(--space-8); +/* ── 3. 장비 — 사진은 누끼처럼. 배경을 곱하기 합성으로 바탕에 녹이고 가장자리는 마스크로 지운다 ── */ +.gear { + padding-block: var(--space-7); border-top: 1px solid var(--line); - background: var(--surface-card); } -.equip-grid { +.gear-grid { display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); gap: var(--space-6); align-items: center; } -.equip-specs { - display: flex; - flex-direction: column; - gap: var(--space-3); +.gear-cutout { + margin: 0; } -.spec-item { - background: var(--surface-raised); - border: 1px solid var(--line); - border-radius: var(--radius-sm); - padding: 12px 16px; - display: flex; - justify-content: space-between; - align-items: center; +.gear-cutout img { + width: 100%; + aspect-ratio: 4 / 3; + object-fit: cover; + object-position: 40% 60%; + mix-blend-mode: multiply; + mask-image: radial-gradient(ellipse 58% 54% at 46% 58%, var(--ink) 55%, transparent 100%); + -webkit-mask-image: radial-gradient(ellipse 58% 54% at 46% 58%, var(--ink) 55%, transparent 100%); } -.spec-key { +.gear-cutout figcaption { + margin-top: var(--space-2); font-size: var(--step--1); - font-weight: 600; color: var(--ink-muted); + text-align: center; } -.spec-val { - font-family: var(--font-mono); - font-size: var(--step--1); - font-weight: 700; +.gear-text .section-title { + margin-bottom: var(--space-4); +} + +.record-list { + display: grid; + border-top: 1px solid var(--line-strong); +} + +.record { + display: grid; + grid-template-columns: 4em minmax(0, 1fr); + gap: var(--space-3); + padding-block: var(--space-3); + border-bottom: 1px solid var(--line); +} + +.record dt { + font-weight: var(--weight-strong); color: var(--accent); } -.ba-container { - position: relative; - border-radius: var(--radius-md); - overflow: hidden; - border: 1px solid var(--line); - box-shadow: 0 12px 32px -8px rgba(0, 0, 0, 0.08); +.record dd { + color: var(--ink-muted); } -.ba-container img { - width: 100%; - aspect-ratio: 4/3; - object-fit: cover; +/* ── 운영 화면 예시 ── */ +.admin-view { + display: none; + padding-block: var(--space-5) var(--space-7); +} + +[data-view="admin"] .client-view { + display: none; +} + +[data-view="admin"] .admin-view { display: block; } -.ba-badge { - position: absolute; - bottom: 12px; - padding: 4px 10px; - border-radius: var(--radius); - font-size: 11px; - font-family: var(--font-mono); - font-weight: 700; -} - -.ba-badge.before { - left: 12px; - background: rgba(15, 23, 42, 0.85); - color: #fff; -} - -.ba-badge.after { - right: 12px; - background: var(--accent); - color: #fff; -} - -/* =================================================================== - 2. 관리자 관제 콘솔 뷰 (Admin Console View) - =================================================================== */ -.admin-view { +[data-view="admin"] .request { display: none; - padding-block: var(--space-6) var(--space-8); } -[data-view="admin"] .client-view { display: none; } -[data-view="admin"] .admin-view { display: block; } - .admin-header { display: flex; justify-content: space-between; - align-items: flex-end; + align-items: flex-start; margin-bottom: var(--space-5); flex-wrap: wrap; gap: var(--space-3); } +.demo-notice { + max-width: var(--measure); + margin-top: var(--space-3); + 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); + border-radius: 0 var(--radius) var(--radius) 0; +} + .admin-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); @@ -720,22 +679,21 @@ button { .kpi-card { background: var(--surface-raised); border: 1px solid var(--line); - border-radius: var(--radius-sm); + border-radius: var(--radius); padding: var(--space-4); } .kpi-label { - font-size: 12px; + font-size: var(--step--1); color: var(--ink-muted); - font-weight: 600; + font-weight: var(--weight-medium); } .kpi-val { - font-family: var(--font-mono); font-size: var(--step-2); - font-weight: 800; + font-weight: var(--weight-strong); color: var(--ink); - margin-top: 4px; + margin-top: var(--space-1); } .kpi-val.accent { @@ -745,7 +703,7 @@ button { .admin-table-card { background: var(--surface-raised); border: 1px solid var(--line); - border-radius: var(--radius-sm); + border-radius: var(--radius); overflow: hidden; } @@ -753,227 +711,481 @@ button { display: flex; justify-content: space-between; align-items: center; - padding: 12px 16px; + flex-wrap: wrap; + gap: var(--space-2); + padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--line); background: var(--surface-card); } .table-title { font-size: var(--step--1); - font-weight: 700; + font-weight: var(--weight-strong); color: var(--ink); } .table-filter-tabs { display: flex; - gap: 4px; + gap: var(--space-1); } .filter-tab { - padding: 3px 8px; + min-height: 2.25rem; + padding-inline: var(--control-pad-inline); border-radius: var(--radius); - font-size: 11px; - font-weight: 600; - background: var(--surface); - border: 1px solid var(--line); + font-size: var(--step--1); + font-weight: var(--weight-medium); + background: var(--surface-raised); + border: 1px solid var(--line-strong); color: var(--ink-muted); } .filter-tab.is-active { background: var(--accent); - color: #fff; + color: var(--accent-ink); border-color: var(--accent); } +.table-scroll { + overflow-x: auto; +} + .admin-table { width: 100%; border-collapse: collapse; - font-size: 13px; + font-size: var(--step--1); text-align: left; white-space: nowrap; } .admin-table th { background: var(--surface-card); - padding: 10px 14px; - font-weight: 600; + padding: var(--space-2) var(--space-3); + font-weight: var(--weight-medium); color: var(--ink-muted); border-bottom: 1px solid var(--line); - font-family: var(--font-mono); - font-size: 12px; } .admin-table td { - padding: 12px 14px; + padding: var(--space-3); border-bottom: 1px solid var(--line); + vertical-align: middle; +} + +.cell-sub { + color: var(--ink-muted); + font-size: var(--step--1); +} + +.cell-strong { + font-weight: var(--weight-strong); +} + +.cell-accent { + color: var(--accent); +} + +.row-fresh { + background: var(--accent-light); } .status-pill { display: inline-flex; align-items: center; - padding: 2px 8px; + padding: 0.125rem var(--space-2); border-radius: var(--radius); - font-size: 11px; - font-weight: 700; + font-size: var(--step--1); + font-weight: var(--weight-strong); } -.status-pill.new { background: #fef3c7; color: #92400e; } -.status-pill.progress { background: #ccfbf1; color: #0f766e; } -.status-pill.done { background: #dbeafe; color: #1e40af; } +.status-pill.new { + background: var(--status-new-bg); + color: var(--status-new-ink); +} -.action-btn { - padding: 4px 10px; +.status-pill.progress { + background: var(--status-progress-bg); + color: var(--status-progress-ink); +} + +.status-pill.done { + background: var(--status-done-bg); + color: var(--status-done-ink); +} + +/* 화면 전환 */ +.view-switch { + display: flex; + align-items: center; + gap: var(--space-3); + padding-block: var(--space-4); + border-top: 1px solid var(--line); +} + +.view-switch-label { + font-size: var(--step--1); + color: var(--ink-muted); + font-weight: var(--weight-medium); +} + +.mode-toggle { + display: inline-flex; + background: var(--surface-card); border: 1px solid var(--line); border-radius: var(--radius); - font-size: 12px; - font-weight: 600; - background: var(--surface); - cursor: pointer; - transition: border-color var(--dur-quick), color var(--dur-quick); + padding: 2px; + gap: 2px; } -.action-btn:hover { - border-color: var(--accent); - color: var(--accent); +.mode-btn { + min-height: var(--control-block); + padding-inline: var(--control-pad-inline); + border-radius: calc(var(--radius) - 2px); + font-size: var(--step--1); + font-weight: var(--weight-medium); + color: var(--ink-muted); + white-space: nowrap; + transition: background-color var(--dur-quick) var(--ease-soft), color var(--dur-quick) var(--ease-soft); } -/* ── 32개 QC 모달 다이얼로그 ── */ -.qc-dialog { +.mode-btn.is-active { + background: var(--surface-raised); + color: var(--ink); + font-weight: var(--weight-strong); +} + +/* ── 다이얼로그 ── */ +.qc-dialog, +.call-dialog { position: fixed; inset: 0; margin: auto; - width: min(90vw, 560px); - max-height: calc(100dvh - (var(--space-4) * 2)); - background: #ffffff; + width: min(90vw, 34rem); + max-height: calc(100dvh - 2 * var(--space-4)); + background: var(--surface-raised); border: 1px solid var(--line); - border-radius: var(--radius-md); + border-radius: var(--radius); padding: var(--space-5); overflow: auto; overscroll-behavior: contain; - box-shadow: 0 24px 64px rgba(0, 0, 0, 0.15); - outline: none; + box-shadow: var(--shadow-panel); + color: var(--ink); } -.qc-dialog::backdrop { - background: rgba(0, 0, 0, 0.5); - backdrop-filter: blur(4px); +.qc-dialog::backdrop, +.call-dialog::backdrop { + background: var(--scrim); } -/* ── 푸터 (SSOT 정본) ── */ -.site-foot { - border-top: 1px solid var(--line); - padding-block: var(--space-6); - background: var(--surface); +.qc-head { + display: flex; + justify-content: space-between; + align-items: center; + gap: var(--space-3); + margin-bottom: var(--space-3); +} + +.qc-title { + font-size: var(--step-1); + font-weight: var(--weight-strong); +} + +.qc-list { + list-style: none; + display: grid; + gap: var(--space-1); +} + +.qc-item label { + display: flex; + align-items: center; + gap: var(--space-2); + min-height: var(--control-block); + font-size: var(--step--1); + cursor: pointer; +} + +.qc-item input { + accent-color: var(--accent); + width: 1.1rem; + height: 1.1rem; +} + +.qc-note { + margin-top: var(--space-3); font-size: var(--step--1); color: var(--ink-muted); } -.foot-inner { +.qc-actions { display: flex; - align-items: center; - justify-content: space-between; - flex-wrap: wrap; - gap: var(--space-4); + justify-content: flex-end; + gap: var(--space-2); + margin-top: var(--space-4); } -/* ── 반응형 미디어 쿼리 ── */ -@media (max-width: 860px) { - .nav { - display: none; - } - .hero-grid, .calc-grid, .equip-grid, .protocol-grid { - grid-template-columns: 1fr; - gap: var(--space-5); - } - .admin-kpis { - grid-template-columns: 1fr 1fr; - } +.call-dialog { + width: min(90vw, 24rem); } -@media (max-width: 600px) { - .type-selector, .addon-grid { - grid-template-columns: 1fr; - } - .admin-kpis { - grid-template-columns: 1fr; - } - .wordmark-han { - display: none; - } - .mode-btn { - padding: 3px 6px; - font-size: 11px; - } +.call-number { + margin-top: var(--space-3); + font-size: var(--step-2); + font-weight: var(--weight-strong); +} + +.call-note { + margin-top: var(--space-2); + font-size: var(--step--1); + color: var(--ink-muted); } html:has(dialog[open]) { overflow: hidden; } -/* 접근성·초광폭 프리플라이트: 컨트롤의 독립성과 실제 표면 여백을 같은 토큰으로 유지한다. */ -:where(a, button, input, select, textarea):focus-visible { - outline: 3px solid var(--accent); - outline-offset: 2px; +/* ── 공통 셸: 푸터 — 다음 행동 하나(견적 요청)가 대부분이다 ── */ +.site-foot { + border-top: 1px solid var(--line-strong); + background: var(--surface-raised); } -.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); +.request { + padding-block: var(--space-7); + display: grid; + grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); + gap: var(--space-6); + align-items: start; } -.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); +.request-title { + font-size: var(--step-2); + font-weight: var(--weight-strong); + letter-spacing: -0.015em; } -.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); +.request-lead { + margin-top: var(--space-3); color: var(--ink-muted); - font-size: var(--step--1); - line-height: 1.55; - word-break: keep-all; + max-width: var(--measure); } -@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; } +.request-total { + font-weight: var(--weight-strong); + color: var(--accent-hover); +} + +.request-form { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: var(--space-3); +} + +.field-wide { + grid-column: 1 / -1; +} + +.text-input { + display: block; + width: 100%; + min-width: 0; + min-height: var(--control-block); + padding-inline: var(--control-pad-inline); + border: 1px solid var(--line-strong); + border-radius: var(--radius); + background: var(--surface-raised); + font-size: var(--step-0); + transition: border-color var(--dur-quick) var(--ease-soft); +} + +.text-input::placeholder { + color: var(--ink-faint); +} + +.text-input:hover { + border-color: var(--ink-faint); +} + +.calc-submit { + grid-column: 1 / -1; + width: 100%; + min-height: 3.5rem; + background: var(--accent); + color: var(--accent-ink); + font-weight: var(--weight-strong); + font-size: var(--step-1); + border-radius: var(--radius); + transition: background-color var(--dur-quick) var(--ease-soft); +} + +.calc-submit:hover { + background: var(--accent-hover); +} + +.request-note { + grid-column: 1 / -1; + font-size: var(--step--1); + color: var(--ink-muted); +} + +.foot-meta { + display: flex; + flex-wrap: wrap; + align-items: baseline; + gap: var(--space-2) var(--space-5); + padding-block: var(--space-4); + border-top: 1px solid var(--line); + font-size: var(--step--1); + color: var(--ink-muted); +} + +.foot-brand { + font-weight: var(--weight-strong); + color: var(--ink); +} + +.foot-notice { + flex-basis: 100%; +} + +/* ── 모바일 하단 고정 행동 바 — 넓은 화면에서는 없다 ── */ +.action-bar { + display: none; +} + +/* ── 반응형 ── */ +@media (max-width: 60em) { + .quote-grid, + .gear-grid, + .request { + grid-template-columns: minmax(0, 1fr); + gap: var(--space-5); + } + + .quote-tool { + grid-template-columns: minmax(0, 1fr); + } + + .quote-summary { + border-left: 0; + border-top: 1px solid var(--line); + } + + .step-list { + grid-template-columns: minmax(0, 1fr); + } + + /* 패널이 한 열이 되면 옵션은 두 열이 들어간다 */ + .addon-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .admin-kpis { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .gear-grid { + grid-template-columns: minmax(0, 1fr); + } + + .gear-cutout { + order: 2; + } + + /* 하단 행동 바: safe area 를 더하고 본문 끝에 그만큼 자리를 비운다 */ + body { + padding-bottom: calc(var(--action-bar-height) + var(--safe-bottom)); + } + + .head-action { + display: none; + } + + .action-bar { + position: fixed; + inset-inline: 0; + bottom: 0; + z-index: 90; + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); + gap: var(--space-2); + padding: var(--space-2) calc(var(--space-3) + var(--safe-right)) calc(var(--space-2) + var(--safe-bottom)) calc(var(--space-3) + var(--safe-left)); + background: var(--surface-raised); + border-top: 1px solid var(--line); + box-shadow: var(--shadow-bar); + } + + .action-bar-call, + .action-bar-quote { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 3.25rem; + border-radius: var(--radius); + font-weight: var(--weight-strong); + font-size: var(--step-0); + } + + .action-bar-call { + border: 1px solid var(--line-strong); + background: var(--surface-raised); + color: var(--ink); + } + + .action-bar-quote { + background: var(--accent); + color: var(--accent-ink); + } + + .qc-dialog, + .call-dialog { + max-height: calc(100dvh - 2 * var(--space-4) - var(--action-bar-height)); + } +} + +@media (max-width: 37.5em) { + .type-selector, + .addon-grid, + .request-form { + grid-template-columns: minmax(0, 1fr); + } + + .admin-kpis { + grid-template-columns: minmax(0, 1fr); + } + + .quote-form, + .quote-summary { + padding: var(--space-4); + } + + .fact { + grid-template-columns: 5em minmax(0, 1fr); + font-size: var(--step--1); + } + + /* 좁은 폭에서는 옵션 이름이 세 줄로 접히지 않게 가격을 아랫줄로 내린다 */ + .addon-label { + grid-template-columns: auto minmax(0, 1fr); + row-gap: 0; + } + + .addon-price { + grid-column: 2; + } + + .step { + grid-template-columns: 2.5rem minmax(0, 1fr); + } } @media (prefers-reduced-motion: reduce) { - *, *::before, *::after { + *, + *::before, + *::after { + transition-duration: 1ms !important; animation-duration: 1ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; - transition-duration: 1ms !important; } } diff --git a/apps/site/public/work/cheongon-care/styles/protocol.css b/apps/site/public/work/cheongon-care/styles/protocol.css new file mode 100644 index 0000000..63470f1 --- /dev/null +++ b/apps/site/public/work/cheongon-care/styles/protocol.css @@ -0,0 +1,162 @@ +/* ========================================================================== + 청온 케어 — 2차 페이지(작업 절차) 전용 스타일. 공통 셸과 토큰은 main.css·tokens.css 가 맡는다. + ========================================================================== */ + +.proto-intro { + padding-block: var(--space-5) var(--space-7); +} + +.crumb { + display: flex; + align-items: center; + gap: var(--space-2); + font-size: var(--step--1); + color: var(--ink-muted); + margin-bottom: var(--space-4); +} + +.crumb a { + display: inline-flex; + align-items: center; + min-height: var(--control-block); + text-decoration: underline; + text-underline-offset: 0.25em; +} + +.crumb a:hover { + color: var(--accent); +} + +.crumb-sep { + color: var(--ink-faint); +} + +.crumb [aria-current="page"] { + color: var(--ink); + font-weight: var(--weight-strong); +} + +.proto-facts { + max-width: 40em; +} + +.proto-intro .demo-notice { + margin-top: var(--space-5); +} + +.proto-record { + padding-block: var(--space-7); + border-top: 1px solid var(--line); +} + +.proto-record-grid { + display: grid; + grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); + gap: var(--space-6); + align-items: center; +} + +.proto-record-grid .gear-cutout img { + aspect-ratio: 16 / 10; + object-position: 55% 45%; + mask-image: radial-gradient(ellipse 58% 62% at 55% 50%, var(--ink) 55%, transparent 100%); + -webkit-mask-image: radial-gradient(ellipse 58% 62% at 55% 50%, var(--ink) 55%, transparent 100%); +} + +.proto-lead { + margin-top: var(--space-3); + color: var(--ink-muted); + max-width: var(--measure); +} + +.compare { + margin-top: var(--space-4); + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--surface-raised); + overflow: hidden; +} + +.compare-cell { + padding: var(--space-4); +} + +.compare-cell + .compare-cell { + border-left: 1px solid var(--line); +} + +.compare dt { + font-size: var(--step--1); + font-weight: var(--weight-medium); + color: var(--ink-muted); +} + +.compare-val { + margin-top: var(--space-1); + font-size: var(--step-2); + font-weight: var(--weight-strong); + letter-spacing: -0.01em; +} + +.compare-val.before { + color: var(--ink-muted); + text-decoration: line-through; + text-decoration-thickness: 2px; +} + +.compare-val.after { + color: var(--accent-hover); +} + +.compare-sub { + font-size: var(--step--1); + color: var(--ink-muted); +} + +.proto-cta { + padding-block: 0 var(--space-7); +} + +.card { + padding: var(--space-5); + background: var(--surface-raised); + border: 1px solid var(--line); + border-radius: var(--radius); + display: grid; + gap: var(--space-2); + justify-items: start; +} + +.card .btn-primary { + margin-top: var(--space-3); +} + +.calc-submit-link { + display: inline-flex; + align-items: center; + justify-content: center; +} + +@media (max-width: 60em) { + .proto-record-grid { + grid-template-columns: minmax(0, 1fr); + gap: var(--space-5); + } + + .proto-record-grid .gear-cutout { + order: 2; + } +} + +@media (max-width: 37.5em) { + .compare { + grid-template-columns: minmax(0, 1fr); + } + + .compare-cell + .compare-cell { + border-left: 0; + border-top: 1px solid var(--line); + } +} diff --git a/apps/site/public/work/cheongon-care/styles/tokens.css b/apps/site/public/work/cheongon-care/styles/tokens.css index 6f8fedc..599e729 100644 --- a/apps/site/public/work/cheongon-care/styles/tokens.css +++ b/apps/site/public/work/cheongon-care/styles/tokens.css @@ -1,58 +1,69 @@ /* ========================================================================== - 청온 케어 (CHEONG-ON CARE) — 디자인 토큰 SSOT - 프리셋: ledger-console + swiss-minimal - 컨셉: 현장 검수 절차와 정직한 정찰제 — 공간의 상태를 기록하는 케어 - 단일 강조색: 에메랄드 틸 (#0D9488) + 청온 케어 — 디자인 토큰 (유일한 정의 지점) + 방향: 도구가 주인인 견적 페이지. 밝은 쿨 바탕 위에 흰 작업 패널, 청록(187°) 하나만 상태·행동에 쓴다. + 서체는 Wanted Sans 하나로 표제·본문·숫자를 다 맡긴다(모노 없음, 숫자는 tabular-nums). + radius 8px 한 값. 모션은 폼 상태 전환(색·테두리)뿐이다. ========================================================================== */ :root { - /* 타이포그래피 스케일 */ - --step--1: 0.8125rem; /* 13px — 캡션·태그 */ - --step-0: 1rem; /* 16px — 본문 */ - --step-1: 1.1875rem; /* 19px — 소제목 */ - --step-2: clamp(1.375rem, 1.2rem + 0.9vw, 1.875rem); /* 30px — 섹션 헤딩 */ - --step-3: clamp(2.25rem, 1.5rem + 3vw, 3.8rem); /* 60px — 메인 디스플레이 */ + /* ── 타입 스케일 (16px 기준, 비율 1.2) ── */ + --step--1: 0.8125rem; /* 13px — 보조 기록·캡션 */ + --step-0: 1rem; /* 16px — 본문·입력 */ + --step-1: 1.1875rem; /* 19px — 소제목·요약 행 */ + --step-2: clamp(1.375rem, 1.2rem + 0.8vw, 1.75rem); /* 22~28px — h1(작게)·섹션 제목 */ + --step-3: clamp(2rem, 1.3rem + 1.8vw, 2.75rem); /* 32~44px — 합계 금액 한 곳(1,297,500원 10자가 합계 열에 한 줄로 들어가는 상한) */ - --leading-tight: 1.2; + --leading-tight: 1.3; --leading-normal: 1.7; - --measure: 58ch; + --measure: 36em; - --font-serif: "MaruBuri", "Noto Serif KR", serif; - --font-display: "Pretendard Variable", Pretendard, -apple-system, sans-serif; - --font-body: "Pretendard Variable", Pretendard, -apple-system, sans-serif; - --font-mono: "JetBrains Mono", monospace; + --weight-regular: 400; + --weight-medium: 500; + --weight-strong: 700; - /* 색상 — 클린 퓨어 화이트 & 딥 슬레이트 + 에메랄드 틸 1강조색 */ - --surface: #f8faf9; + --font-body: "Wanted Sans Variable", "Wanted Sans", "Apple SD Gothic Neo", "Malgun Gothic", sans-serif; + --font-display: var(--font-body); + + /* ── 색 — 밝은 쿨 바탕(#eef3f6) + 흰 패널 + 청록 187° ── */ + --surface: #eef3f6; --surface-raised: #ffffff; - --surface-card: #f0f4f2; - --surface-subtle: #e6ede9; - --surface-sunken: #e2e9e5; - --surface-inverse: #0f172a; + --surface-card: #e1eaef; + --surface-sunken: #d8e3e9; + --surface-inverse: #15252e; - --ink: #0f172a; - --ink-muted: #475569; - --ink-faint: #94a3b8; - --ink-inverse: #f8faf9; - --ink-inverse-muted: #cbd5e1; + --ink: #112027; + --ink-muted: #4b616c; + --ink-faint: #586e79; /* 흰 바탕 5.4:1, 쿨 바탕 4.8:1 — 보조 글자도 AA */ + --ink-inverse: #eef3f6; + --ink-inverse-muted: #b2c0c7; - --line: #e2e8e5; - --line-strong: #cbd5e1; - --line-subtle: #edf2ef; - --line-inverse: #334155; + --line: #ced9de; + --line-strong: #b2c0c7; - /* 단일 강조색 (에메랄드 틸) */ - --accent: #0f766e; - --accent-hover: #115e59; - --accent-light: #f0fdfa; - --accent-surface: #ccfbf1; + --accent: #156a75; /* 바탕 위 5.6:1, 흰 글자 6.3:1 */ + --accent-hover: #0f535c; + --accent-light: #e5f3f5; + --accent-surface: #cde6ea; --accent-ink: #ffffff; - --radius: 2px; - --radius-sm: 4px; - --radius-md: 8px; + --placeholder-ink: #4b616c; - /* 간격 — SSOT 표준 */ + /* 상태색 — 운영 화면 접수 상태 알약에만 쓴다(브랜드 강조와 분리) */ + --status-new-bg: #fdf0d5; + --status-new-ink: #7a4b08; + --status-progress-bg: #cde6ea; + --status-progress-ink: #0f535c; + --status-done-bg: #dde7f6; + --status-done-ink: #27467f; + + --scrim: rgba(21, 37, 46, 0.55); + --shadow-panel: 0 1px 2px rgba(21, 37, 46, 0.06), 0 12px 32px -16px rgba(21, 37, 46, 0.18); + --shadow-bar: 0 -8px 24px -12px rgba(21, 37, 46, 0.28); + --head-bg: rgba(238, 243, 246, 0.96); + + --radius: 8px; + + /* ── 간격 ── */ --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 1rem; @@ -62,14 +73,17 @@ --space-7: 6rem; --space-8: clamp(4.5rem, 3rem + 6vw, 9rem); - /* 컨테이너 및 패딩 — 전체 쇼케이스 절대 통일 SSOT */ - --container: clamp(70rem, 62vw, 85rem); - --pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem); + --container: 78rem; + --pad-inline: clamp(1rem, 0.5rem + 2.5vw, 3rem); --control-block: 2.75rem; --control-pad-inline: 0.875rem; + --action-bar-height: 4.25rem; - /* 모션 */ - --dur-quick: 180ms; - --dur-normal: 420ms; + --safe-bottom: env(safe-area-inset-bottom, 0px); + --safe-left: env(safe-area-inset-left, 0px); + --safe-right: env(safe-area-inset-right, 0px); + + /* ── 모션 — 폼 상태 전환만 ── */ + --dur-quick: 160ms; --ease-soft: cubic-bezier(0.25, 1, 0.35, 1); } diff --git a/apps/site/public/work/dure-enrollment/design.md b/apps/site/public/work/dure-enrollment/design.md index 682acbf..0255f3c 100644 --- a/apps/site/public/work/dure-enrollment/design.md +++ b/apps/site/public/work/dure-enrollment/design.md @@ -10,11 +10,9 @@ | 축 | 질문 | 적용 | | --- | --- | --- | -| Warde · 정보 전달 | 필터 뒤의 과목 목록과 시간표가 한 작업 흐름으로 읽히는가 | 공통 작업면과 한 단계 낮춘 중복 테두리 | -| Ruder · 비교 변수 | 과목명·시간·학점·여석을 같은 시선 흐름에서 비교하는가 | 행 메타와 우측 학점·여석의 정렬, tabular figures | -| Albers · 상태 맥락 | 관심·신청·여석 부족 상태가 텍스트와 색으로 함께 구분되는가 | 상태 텍스트를 남기고 색은 상태 보조로만 사용 | - -Warde, Ruder, Albers는 정보 전달·비교 변수·상태 맥락을 검토하는 참고 질문이며 인과나 성능 법칙이 아니다. +| 작업 흐름 | 필터 → 과목 목록 → 내 시간표가 한 작업면에서 이어지는가 | 공통 작업면과 한 단계 낮춘 중복 테두리. 신청하면 시간표 칸이 바로 채워진다 | +| 비교 변수 | 과목명·요일·학점(3학점/2학점)·여석(여석 3~31)을 같은 시선 흐름에서 비교하는가 | 행 메타와 우측 학점·여석의 정렬, 숫자는 tabular | +| 상태 | 관심·신청·여석 3 이하(`is-low`) 상태가 텍스트와 색으로 함께 구분되는가 | 상태 텍스트를 남기고 색은 상태 보조로만 사용 | ## 역할 토큰과 검증 @@ -22,3 +20,11 @@ Warde, Ruder, Albers는 정보 전달·비교 변수·상태 맥락을 검토하 - `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이다. - 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다. + +## 2026-10-02 레드팀 06 수정 기록 + +- `.mono` 유틸리티를 등폭 서체에서 본문 서체(IBM Plex Sans KR) + tabular-nums로 바꿨다. "여석 12", "월·수 13:00–14:30"처럼 한글이 섞인 줄에서 공백이 등폭 셀 너비로 벌어졌다. 시각 회귀 기준선은 이 의도적 변경으로 갱신했다. + +## 2026-10-02 카피 교정 + +- "시간표에 얹히는 수강신청", "과목의 원장", "주간 자리"처럼 대학 수강신청 화면에서 쓰지 않는 은유·어휘를 사실 문장으로 바꿨다. 다이얼로그의 영문 장식 라벨 COURSE는 "과목"으로 바꿨다. diff --git a/apps/site/public/work/dure-enrollment/index.html b/apps/site/public/work/dure-enrollment/index.html index 99671de..0d6c14b 100644 --- a/apps/site/public/work/dure-enrollment/index.html +++ b/apps/site/public/work/dure-enrollment/index.html @@ -20,7 +20,7 @@ content="과목 선택과 시간표를 함께 살펴보는 수강신청 화면입니다. 관심 과목, 신청 내역, 이수 현황을 한 흐름으로 확인할 수 있습니다." /> - + @@ -136,7 +136,7 @@ @@ -145,7 +145,7 @@

      신청 내역

      -

      이번 학기에 신청한 과목의 원장. 여기서도 취소할 수 있습니다.

      +

      이번 학기에 신청한 과목입니다. 여기서도 취소할 수 있습니다.

      합계 0학점 / 18

      @@ -164,7 +164,7 @@

      @@ -174,7 +174,7 @@

      이수 현황

      -

      네 학기를 마친 시점의 기록에 이번 학기 신청을 얹어 봅니다. 신청은 확정이 아닙니다.

      +

      4학기까지 이수한 학점에 이번 학기 신청 학점을 더해 보여 줍니다. 신청 학점은 아직 확정 전입니다.

      @@ -199,7 +199,7 @@

      과목 총람

      -

      개설 과목 전부를 계열별로 묶어 적습니다. 여석이 5 이하인 과목은 붉게 표시합니다.

      +

      개설 과목 전체를 전공·교양으로 나눠 보여 줍니다. 여석이 5석 이하인 과목은 빨간색으로 표시합니다.

      @@ -220,7 +220,7 @@

      공지 · 변경

      -

      수강신청 기간의 게시판. 강의실 변경은 이 원장에 남깁니다.

      +

      수강신청 기간 공지와 강의실 변경 안내입니다.

      @@ -231,7 +231,7 @@
      - +

      과목명

      CODE · 계열

      @@ -249,7 +249,7 @@

      교수 · 강의실

      -

      주간 자리

      +

      주간 시간표

      @@ -344,7 +344,7 @@ const NOTICES = [ { date: "03-08", kind: "변경", body: "게임이론(ECON315) 강의실이 경상관 308호에서 309호로 바뀝니다. 이 목록에는 이미 반영했습니다." }, - { date: "03-07", kind: "공지", body: "수강신청 기간은 3월 2일부터 3월 13일 자정까지입니다. 기간 끝에는 시간표가 확정됩니다." }, + { date: "03-07", kind: "공지", body: "수강신청 기간은 3월 2일부터 3월 13일 자정까지입니다. 기간이 끝나면 시간표가 확정됩니다." }, { date: "03-06", kind: "안내", body: "기준 학점(18)을 넘는 신청은 학부 사무실의 승인을 받아야 합니다. 이 화면에서는 기준을 넘는 신청을 막습니다." }, { date: "03-05", kind: "안내", body: "드로잉 입문(ART130)은 재료비 3만 원이 듭니다. 첫 수업에 안내합니다." }, { date: "03-03", kind: "공지", body: "정원이 마감된 과목은 대기자 신청을 받지 않습니다. 분반 개설은 학부 상황에 따라 결정됩니다." }, @@ -800,7 +800,7 @@ // 주간 자리 — 9:00–18:00 를 세로로 접은 미니 격자 const strip = $("cd-strip"); - strip.setAttribute("aria-label", `${timeLabel(c)} — 시간표에 놓이는 자리`); + strip.setAttribute("aria-label", `${timeLabel(c)} — 시간표 위치`); strip.innerHTML = [1, 2, 3, 4, 5] .map((d) => { const day = fmtDay(d); diff --git a/apps/site/public/work/dure-enrollment/og.png b/apps/site/public/work/dure-enrollment/og.png index a9c53e3..ffba0a0 100644 Binary files a/apps/site/public/work/dure-enrollment/og.png and b/apps/site/public/work/dure-enrollment/og.png differ diff --git a/apps/site/public/work/dure-enrollment/styles/main.css b/apps/site/public/work/dure-enrollment/styles/main.css index 038b3c6..72d9b96 100644 --- a/apps/site/public/work/dure-enrollment/styles/main.css +++ b/apps/site/public/work/dure-enrollment/styles/main.css @@ -44,7 +44,9 @@ h3 { font-size: var(--step-0); font-weight: 500; } :where(p, li, td, th, dd, figcaption) { word-break: keep-all; overflow-wrap: anywhere; } -.mono { font-family: var(--font-mono); font-size: var(--font-mono-size); font-variant-numeric: tabular-nums; } +/* .mono 는 "수학 심화 B · 3/11 마감"처럼 한글이 섞인 줄에도 쓰인다. 등폭 서체에 두면 한글 사이 + 공백이 등폭 셀 너비로 벌어졌다(레드팀 06 P1-3). 숫자 정렬은 본문 서체의 tabular-nums 로 지킨다 */ +.mono { font-family: var(--font-body); font-size: var(--font-mono-size); font-variant-numeric: tabular-nums; } .muted { color: var(--muted-foreground); } :focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: var(--radius-xs); } @@ -754,7 +756,7 @@ kbd { /* ── 터치 타깃 — M3 48dp·HIG 44pt 정신의 44px 하한(coarse 포인터에서만) ── */ @media (pointer: coarse) { .chip, .give-btn, .btn-line { min-height: 44px; } - .icon-btn, .bell, .modal-close { width: 44px; height: 44px; } + .icon-btn, .modal-close { width: 44px; height: 44px; } .btn-primary, .btn-star { min-height: 44px; } #q { min-height: 44px; } } diff --git a/apps/site/public/work/dure-enrollment/styles/tokens.css b/apps/site/public/work/dure-enrollment/styles/tokens.css index 18db851..9839c77 100644 --- a/apps/site/public/work/dure-enrollment/styles/tokens.css +++ b/apps/site/public/work/dure-enrollment/styles/tokens.css @@ -21,7 +21,9 @@ --font-body: "IBM Plex Sans KR", "Pretendard Variable", -apple-system, sans-serif; --font-display: "Noto Serif KR", "IBM Plex Sans KR", serif; - --font-mono: "JetBrains Mono", ui-monospace, monospace; + /* 한글 글리프가 없는 모노 다음에 본문 한글 서체를 둔다. 그래야 "시험 구간"처럼 한글이 섞인 라벨이 + 시스템 등폭 한글로 떨어져 글자 사이가 벌어지지 않는다(레드팀 06 P1-3). 숫자·라틴만 모노로 남는다 */ + --font-mono: "JetBrains Mono", "IBM Plex Sans KR", "Pretendard Variable", monospace; --font-mono-size: 0.8125rem; --background: #fafafa; diff --git a/apps/site/public/work/font-foundry/design.md b/apps/site/public/work/font-foundry/design.md index 5c8e249..ca0e11a 100644 --- a/apps/site/public/work/font-foundry/design.md +++ b/apps/site/public/work/font-foundry/design.md @@ -1,77 +1,75 @@ # design.md — 한성활자연구소 +## 진행 상태 + +- 2026-10-06 재작업(전체·리디자인 경로, 방향 위임): 0 감사 → 1 레퍼런스 → 2 카드 A 선택 → 3 토큰 → 4 구현 → 5 검증 통과 → 6 이 문서. 작업 노트 `outputs/rework-2026-10-06/font-foundry.md`. + ## 브리프 -- **무엇을**: 가상 서체 기획을 정직하게 밝히면서, 실제 마루 부리와 Pretendard로 문장 조판의 형태 차이를 즉시 체험하는 한국어 타입 스튜디오. -- **누구에게**: 활자를 고르거나 본문 조판 값을 결정해야 하는 편집자·디자이너·개발자. -- **제약**: ko-KR 한국어, 밝은 아이보리·검정 본문·버밀리언 정체성 유지. 가상 서체를 판매하거나 가짜 스펙을 제시하지 않으며, 본문은 최대 42rem. 320px부터 가로 넘침이 없어야 한다. +- **무엇을**: 한글 본문 서체 두 종(마루 부리·Pretendard)을 글자 하나의 굵기 축에서 보고, 같은 문장을 크기·자간·행간을 바꿔 조판해 보는 가상 서체 연구소 한 장. +- **누구에게**: 본문 서체와 조판 값을 정해야 하는 편집자·디자이너·개발자. 목표 행동은 굵기 슬라이더와 조판 시험대를 직접 움직이는 것(문의·구매 없음). +- **제약**: ko-KR. 정적 HTML/CSS/인라인 JS. 가상 기획이라 실제 주문으로 이어지지 않으며 가격은 예시. 마루 부리는 공식 배포본 그대로(서브셋 금지). 320px부터 가로 넘침 없음. 화면: `index.html` 한 장. +- 확정 슬롯: 업종 서체 연구소 / 톤 "글자 자체가 이미지, 설명은 작게" / 자산 마루 부리 WOFF2 2종·견본 문장·예시 가격 / 좁은 화면 내비 탭 2개 그대로. -## 리디자인 감사와 레퍼런스 +## 미확정 -이전 판은 한 글자 표본과 실제 렌더 서체의 관계가 멀고, 11,172자·1000 UPM 같은 가상 수치가 체험보다 앞섰다. 실제 사용자가 판단할 수 있는 것은 문장 안의 폭·획·간격이므로 그 역할을 첫 화면으로 올렸다. +- 가격 140,000·220,000원 — 예시(order 섹션에 "예시" 명시). +- 글리프 기본 글자 `활` — 브랜드 연상으로 정했고 사용자가 바꿀 수 있다. +- 방향 카드 선택 — 위임으로 A를 골랐다(아래). -| 출처 ID · 슬롯 | 출처 | 관찰 | 채택한 원리와 검증 | -| --- | --- | --- | --- | -| `KLIM-FONTS` · R1 구조 | https://klim.co.nz/fonts/ | Display, Text, Narrow, Condensed처럼 같은 계열의 역할과 폭을 나란히 제시한다. | 글꼴을 사양 표가 아니라 읽는 역할로 비교한다. 같은 문장을 두 역할로 렌더해 비교한다. | -| `CHIPPERFIELD-PROJECTS` · R2 톤 | https://davidchipperfield.com/projects | 프로젝트명·연도·필터가 정렬선과 짧은 메타 안에서 읽힌다. | 큰 표본 아래에 기준선, 짧은 메타, 한 가지 행동을 정렬한다. 1440px 첫 화면에서 표본·상태·CTA의 순서를 확인한다. | -| `SANDOLL-TYPELAB` · R3 디테일 | https://typelab.sandoll.co.kr/experience | 권장규격과 맞춤규격, 한글 범위를 선택하며 표본을 실제 작업으로 연결한다. | 버튼과 조판 슬라이더가 즉시 보이는 결과를 바꾸게 한다. 키보드·range·리셋으로 값을 바꾸어 검증한다. | -| `WCAG-READ` · 접근성 | 200% 텍스트 확대와 320 CSS px reflow를 핵심 화면에서 별도로 시험한다. | 큰 활자 장면은 접근성 제목을 대신하지 않는다. | 이전 판에서 320px·200% 확대와 `aria-hidden` 표본을 관찰했다. Round 4 현재 결과는 최종 proof를 참조한다. | +## 레퍼런스 + +| 슬롯 | 출처 | 가져온 원리 | +|---|---|---| +| R1 구조 | https://abcdinamo.com/typefaces/favorit (2026-10-06 텍스트 페처) | 첫 화면이 타입 테스터, 변종·보기를 탭으로 바꾼다 | +| R2 톤 | https://rsms.me/inter/ (2026-10-06 텍스트 페처) | 글자 표본이 곧 첫 화면, 설명은 아래 작게 | +| R3 디테일 | https://typelab.sandoll.co.kr/experience (이전 판 기록 재사용, 미재검증) | 조작이 즉시 표본을 바꾼다 | +| 푸터 | https://pangrampangram.com/ 확인 — 워드마크 한 줄 푸터 아님. 배정값으로 감(미검증) | — | ## 방향 -- **컨셉**: 문장이 보여 주는 활자의 차이. -- **프리셋**: `editorial` — 읽는 시간이 주제이므로 따뜻한 바탕, 제한된 색, 좁은 본문 폭, 세리프 표제를 쓴다. -- **감수한 리스크**: 이미지를 히어로에서 빼고 버밀리언 세 글자 표본 하나를 가장 큰 장면으로 사용한다. 제목과 설명은 별도 DOM으로 남겨 접근성 판단을 표본에 기대지 않는다. +**지문**: 히어로 타이포 단독(글자 하나 거대 + 굵기 슬라이더) · 표제 Noto Serif KR 900 / 본문 Noto Sans KR 400 · 바탕 밝은 쿨 중립 #f1f2f4 · 강조 240° 남색 잉크 #22235a · radius 0 · 흐름 글리프 → 같은 문장 비교 → 조판 시험대 → 옆면 여백과 자간 → 가격(예시) → 워드마크 · 내비 탭·세그먼트(서체별 보기, 상단 바 없음, sticky 없음) · 푸터 큰 워드마크 한 줄 · 이미지 없음(글자가 이미지) · 모션 탭 전환 View Transitions + 글리프 크기 scroll-driven. -## 토큰과 구현 +- **컨셉**: 본문 서체를 고르는 편집자가 한 글자를 굵기 축으로 밀어 보며 마루 부리(정적 2굵기)와 Pretendard(가변 45~920)의 성격을 판단하고, 같은 문장 조판으로 확인한다. +- **프리셋**: swiss-minimal 지배(격자·침묵·쿨 중립), 보조 없음. 서체·명도·hue·radius는 plan.md 배정값. +- **표현 리스크**: 글자 하나가 첫 화면 56svh를 차지해 "무엇을 하는 곳"이 h1·리드에만 있다. 검증: 1440·390 첫 화면에 h1·리드·슬라이더가 함께 들어오는지(둘 다 들어옴, 390 하단 821px). +- **카드**: A 글자 하나의 굵기(선택) / B 문장 폭포(문장 다섯 크기, 자간 슬라이더) / C 자모 분해(초·중·종성 세 칸). A를 고른 이유 — 브리프의 사실(정적 2굵기 vs 가변 축)이 굵기 축에서 가장 분명하고, R1·R2 둘 다 글자 표본이 첫 화면이었다. B는 비교 섹션과 역할이 겹치고 C는 글자 교체가 어렵다. +- **반사실**: 고유 요소 — 마루 부리 거대 글리프 + 서체별로 바뀌는 굵기 축(400↔600 / 100~900), 서체 이름 탭, 네이버·OFL·굵기 수 사실. 세무사무소로 치환하면 슬라이더·탭이 성립하지 않는다. 수렴(muwol-coffee 대비 6축): 히어로 패밀리·탭 내비 같음, 나머지 4축 다름. +- **보존 자산**: 상호, 견본 문장, 시험대 기본값 17px/0em/1.8과 `display/body` 값, 마루 부리 파일, 예시 가격, 가상 고지. -- **폰트**: 마루 부리는 공식 로컬 WOFF2 Regular 400·SemiBold 600으로 제목·조판 시험대·표본의 제목 상태를 맡고, Pretendard CDN은 본문·기본 표본 상태를 맡는다. `font-synthesis-weight: none`으로 합성 굵기를 막는다. -- **색**: `--surface #f5f1e8`, `--ink #26221d`, `--accent #a63d2f`. 버밀리언은 표본과 행동에만 쓴다. -- **크기**: h1 `clamp(40px, 1.6rem + 3vw, 60px)`, 표본 `clamp(96px, 11vw, 180px)`, 본문 폭 최대 `42rem`. -- **레이아웃**: 데스크톱 첫 화면은 5:7 두 칼럼으로, 종이색 소개면과 적갈색 표본면을 맞물린다. 900px 미만에서는 소개면 다음에 표본면을 한 칼럼으로 둔다. -- **상호작용**: native 버튼 두 개의 `aria-pressed`가 표본 폰트와 상태 문구를 바꾼다. 조판 시험대는 실제 마루 부리·Pretendard 선택과 크기·자간·행간 range를 제공하고, 리셋은 네 값을 함께 복원한다. -- **성능**: 새 JavaScript 라이브러리·이미지·애니메이션 없음. 초기 체험은 인라인 DOM 코드만 사용한다. 활자 케이스의 두 장면은 생성 이미지임을 캡션으로 밝힌다. +## 토큰과 예산 -## 의도적으로 하지 않은 것 +값은 `styles/tokens.css`. 역할: `--font-display` 표제·h3·워드마크, `--font-body` 본문·UI, `--face-buri`·`--face-godic` 표본·시험대 전용(UI에 쓰지 않는다). `--accent`는 거대 글리프·활성 탭·슬라이더·버튼에만. `--line-strong`은 컨트롤 테두리(3.98:1). Noto Sans KR은 400 한 굵기만 싣고 강조는 서체·색으로 낸다. -- 마루 부리·Pretendard를 가상의 한성 서체로 속여 판매하지 않는다. 가상 기획과 실제 구현 폰트를 표본 바로 위와 푸터에 밝힌다. -- 인장·붓글씨·세로쓰기·종이결처럼 한국 활자를 이국화하는 장식을 사용하지 않는다. 활자의 형태와 조판만으로 분위기를 만든다. -- 움직임으로 활자의 차이를 설명하지 않는다. reduced motion에서도 같은 표본과 모든 조작이 남는다. +예산(tokens §5 데모): JS 150KB — 실측 인라인 4KB. LCP 2.5s — 로컬 308ms(`#glyph`), CLS 0.000. 폰트 전송 200KB — **실측 ≈939KB 초과**(마루 부리 424KB + Pretendard 동적 서브셋 229KB + Noto 286KB). 표본 서체가 제품이고 마루 부리는 공식 배포본 그대로라 이번 작업에서 줄이지 않았다. 별도 작업 제안: OFL 범위의 한글 서브셋. -## 이전 판 검증 기록 · 2026-09-12 +## 이펙트와 폴백 -아래는 이전 판에서 남긴 역사적 관찰이며 Round 4의 현재 통과 결과가 아니다. Round 4 결과는 최종 proof를 참조한다. +- 탭 전환: `document.startViewTransition` 래퍼. 미지원·감소 모션은 즉시 교체. `::view-transition-*(glyph|face-status)`만 저자 키프레임. +- 글리프 축소: `animation-timeline: scroll(root block)` 0~80vh, scale 1→0.55. `@media (prefers-reduced-motion: no-preference)`와 `@supports` 안에 둬 Firefox·감소 모션은 고정 크기. +- 재질 없음. 이펙트 전부 끄고 JS를 꺼도 글리프·비교·시험대가 그대로 남는다(검증 참조). -- `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 +- 사진 2장(`metaltype.webp`·`inkstone.webp`)을 화면에서 뺐다 — 같은 생성 이미지를 두 번 싣던 자리였고 배정이 "글자가 이미지"다. 파일은 디스크에 남긴다. +- 상단 바·앵커 내비·sticky·backdrop-filter를 두지 않았다 — 한 장짜리 4화면 길이, 내비는 서체 탭 하나로 충분하다. +- 마루 부리·Pretendard를 UI 서체로 쓰지 않았다(6-6 하우스 룩). 표본으로만 남긴다. +- 가운뎃점 메타 문자열(`A · B`)을 가시 텍스트에서 없앴다. 시험대 캡션은 쉼표. +- 스크롤 리빌 없음. 눈썹 라벨 없음. -| 축 | 참고 범위와 현재 질문 | 채택과 기각 조건 | 구현 | -| --- | --- | --- | --- | -| 구도 | 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 +- 자동(2026-10-06, apps/site): html-validate 0오류 · stylelint 통과 · no-hanja PASS · showcase-audit PASS 6/FAIL 0 · design-gate 충족 13/미검증 0/실패 0(static·visual·webkit 꺼짐) · 상호작용 스크립트 34/34(탭 키보드 패턴, 슬라이더·지연 로드, 기존 E2E 3단언, 200%+1.4.12 간격 재정의 가로 넘침 0, 감소 모션, 이펙트·JS 끔, 표제 마지막 줄 어절 ≥2) · 기존 `archive-showcases-e2e.mjs` font-foundry 14/14. +- 시각 루브릭(약한 축부터): R7 1(h1이 범용) · R9 1(390 글자 입력이 한 줄 독점) · R1 2 · R2 2 · R3 2 · R4 2 · R5 2 · R8 2 · R10 2 · R6 N/A — 16/18, 통과. 근거와 캡처 경로는 작업 노트. +- 대비: 본문 15.96:1, 보조 6.21:1(띠 위 5.62:1), 남색 면 위 글자 12.88:1. +- 미검증: WebKit 실렌더(게이트 webkit=false), 실기기 INP, Firefox 폴백은 코드 가드만 확인. -- **방향과 원리**: 한성활자연구소의 종이색·적갈색·금속 활자 이미지는 유지하고, 첫 화면을 5:7 종이 소개면과 적갈색 표본면으로 재배치했다. 표본면의 큰 흰 글자, 기준선, 선택 상태는 같은 시선 안에서 글꼴 전환을 읽게 한다. -- **비교 조건**: Ruder의 구조·리듬·비례를 현재 화면의 검증 질문으로만 번역했다. 비교 두 칼럼은 같은 원문, 동일 폭, 기본 22px(`1.375rem`)와 `1.8` 행간을 공유하며 글꼴 가족만 다르다. 서체마다 글자가 보이는 크기는 다를 수 있다는 안내를 함께 둔다. -- **채택과 측정 한계**: 조판 시험대는 실제 웹폰트 선택을 문장에 적용하고 네 제어값을 리셋한다. 화면 비교는 형태·폭·줄바꿈의 관찰을 돕지만, 읽기 속도·이해도 향상은 사용자 과업 측정 전까지 주장하지 않는다. 역사처럼 보일 수 있는 견본 문단은 견본글임을 명시했다. -- **동일 브리프 보존**: 가상 기획·예시 가격·라이선스 안내, 네 개의 내비와 앵커, 기존 CTA, 금속 이미지와 생성 이미지 표기, 버튼의 `aria-pressed`·상태 문구, 기존 조판 range와 reset 기능을 보존한다. +## 피드백 기록 · 보존 목록 -### Round 4 최종 판정 +- 보존: 같은 문장 22px/1.8 동일 폭 비교, 시험대 네 컨트롤과 리셋, 가상 고지 위치(가격 섹션 + 푸터). -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이다. -- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다. +- 2026-09-12 Round 4: 5:7 종이 소개면 + 적갈색 표본면, 마루 부리·Pretendard 비교 계약, 320·200% 확대 검사(옛 기록). +- 2026-10-02 레드팀 06: 헤더 정렬·가격 "예시" 표기 정리. +- 2026-10-06 재작업: 타이포 단독 히어로(거대 글리프 + 굵기 축), 서체 탭 내비, 큰 워드마크 푸터, Noto Serif KR 900/Noto Sans KR, #f1f2f4·#22235a·radius 0, View Transitions·scroll-driven, 사진 제거, og.png 추가. 이유: 19종 구성 수렴 해소(plan.md 배정). diff --git a/apps/site/public/work/font-foundry/index.html b/apps/site/public/work/font-foundry/index.html index b402cd8..0f95018 100644 --- a/apps/site/public/work/font-foundry/index.html +++ b/apps/site/public/work/font-foundry/index.html @@ -3,169 +3,232 @@ - 한성활자연구소 · 읽는 리듬을 만드는 활자 - - - + 한성활자연구소 — 한글 본문 서체 시험대 + + + - + + + + + - + + - - + + + - +
      -
      -
      -
      -

      한글 타입 스튜디오

      -

      읽는 리듬을
      만드는 활자.

      -
      -
      -

      글자는 모양만으로 읽히지 않습니다. 문장 안에서 어떤 폭으로 서고, 얼마나 가까이 호흡하는지 직접 관찰해 보세요.

      - 조판해 보기 -
      +
      +
      +

      서체 시험대

      +

      한글 본문 서체 두 종을 글자 하나의 굵기 축에서 보고, 같은 문장을 크기·자간·행간을 바꿔 가며 조판합니다.

      -
      -

      가상 서체 기획 · 화면은 마루 부리와 Pretendard로 구현

      - - -
      +
      -

      두 가지 읽기 방식

      -

      같은 문장, 서로 다른 결.

      +

      같은 문장을 22px, 행간 1.8, 같은 폭으로 두 서체에 조판했습니다.

      +

      서체마다 글자가 보이는 크기는 다를 수 있습니다. 줄 길이와 줄바꿈 자리를 비교해 보세요.

      -

      크기·행간·폭을 맞췄습니다. 서체마다 글자가 보이는 크기는 다를 수 있습니다.

      -
      +
      +
      +

      마루 부리

      +

      부리 계열. 네이버 배포, SIL 오픈 폰트 라이선스. 이 화면에는 정적 굵기 400·600 두 종을 실었습니다.

      +
      +

      아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 마을 어귀 느티나무 아래에서는 할머니 두 분이 멍석에 고추를 널고 있었다.

      +
      +

      Pretendard

      -

      본문용 산세리프

      +

      민부리 계열. 가변 서체라 굵기 축을 45부터 920까지 연속으로 바꿀 수 있습니다. SIL 오픈 폰트 라이선스.

      -

      아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 글자는 종이 위에서 소리를 내지 않지만, 잘 짜인 활자는 소리 내어 읽게 만든다.

      -
      -
      -
      -

      MaruBuri

      -

      명조 계열 세리프

      -
      -

      아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 글자는 종이 위에서 소리를 내지 않지만, 잘 짜인 활자는 소리 내어 읽게 만든다.

      +

      아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 마을 어귀 느티나무 아래에서는 할머니 두 분이 멍석에 고추를 널고 있었다.

      +
      -
      -
      -

      조판 시험대

      -

      읽는 호흡을
      직접 맞춰 보세요.

      -
      -

      크기·자간·행간을 바꾸며 문장 폭, 줄바꿈, 글자 형태의 차이가 어떻게 보이는지 비교해 보세요.

      +
      +

      글자 크기 13~26px, 자간, 행간 1.4~2.4를 바꾸면 줄바꿈이 바로 달라집니다.

      +

      웹 본문은 보통 16~18px, 한글 행간은 1.6~1.8배에서 시작합니다. 기본값으로 되돌리면 17px, 자간 0em, 행간 1.8로 돌아갑니다.

      -
      +
      17px
      0em
      1.8
      -

      마루 부리 · 17px · 자간 0em · 행간 1.8

      -

      아래 문단은 역사 사실을 설명하는 글이 아닌 조판 견본글입니다.

      -
      활자의 역사는 글자를 찍는 기계의 역사가 아니라, 글자를 읽는 눈의 역사였다. 활자소의 식자공들은 하루에 만 글자를 짜면서 어느 글꼴이 눈을 덜 아프게 하는지 알고 있었다. 그 지식은 문서로 남지 않았고 손끝으로만 전해졌다. 이 미리보기는 그 판단을 눈으로 비교할 수 있게 옮긴 것입니다. 자간을 0에서 5로 바꾸면 문단의 호흡과 줄 길이가 어떻게 달라지는지 확인할 수 있습니다.
      +

      마루 부리, 17px, 자간 0em, 행간 1.8

      +

      아래 문단은 조판 견본글입니다.

      +
      웹 본문 글자는 보통 16~18px로 정하고, 한글 행간은 글자 크기의 1.6~1.8배 정도로 잡습니다. 자간은 0을 기준으로 큰 제목에서는 조금 좁히고, 작은 글자에서는 조금 넓히기도 합니다. 같은 문단이라도 서체에 따라 한 줄에 들어가는 글자 수가 달라집니다. 자간을 0em에서 0.05em으로 바꾸면 줄 길이와 줄바꿈 위치가 어떻게 달라지는지 확인해 보세요.
      -
      -
      -
      - 나무 활자 케이스에 골라 담긴 금속 활자들 — 활자면이 위에서 빛을 받아 결이 보인다 -
      생성 이미지 · 금속 활자 케이스의 관찰 장면
      -
      -
      -

      활자 케이스

      -

      금속에서 시작해
      화면에서 읽힌다.

      -

      이 가상 기획은 금속 활자의 옆면 여백을 관찰의 출발점으로 삼습니다. 납활자는 글자 바로 옆에 살이 붙어 있어, 찍히는 글자 사이가 일정했습니다. 이 화면에서는 그 일정함이 문장 안의 자간과 행간에 어떤 리듬을 만드는지 살펴봅니다.

      -
      - 빛을 받은 금속 활자와 정렬된 활자 케이스 -
      생성 이미지 · 활자 폭과 여백을 보는 편집 장면
      -
      + +
      +
      +
      +

      납활자 옆면의 여백이 화면에서는 자간 값이 됩니다.

      +

      납활자는 글자 몸통 양옆에 일정한 여백이 있어 찍힌 글자 사이 간격이 고르게 나왔습니다. 디지털 서체도 글자마다 옆면 여백을 갖고 있고, 자간은 그 여백에 더하거나 빼는 값입니다. 아래는 같은 네 글자를 자간 -0.05em과 0.1em으로 놓은 것입니다. 한글은 자간을 넓히면 낱글자로 흩어지기 쉬워 본문에서는 0 근처에서 시작합니다.

      +
      + + +
      위는 자간 -0.05em, 아래는 0.1em. 보는 서체의 굵기 400으로 그렸습니다.
      +
      +
      -
      -

      가상 서체 기획

      -

      라이선스 안내

      -

      아래 라이선스 종류와 가격은 서체 판매 정보를 구성하는 방식을 보여 주는 예시입니다. 실제 주문·결제·문의로 이어지지 않습니다.

      +
      +

      라이선스 가격은 웨이트 전종 기준의 예시입니다.

      +

      한성활자연구소는 가상 서체 기획입니다. 실제 주문·결제·문의로 이어지지 않으며, 화면의 서체는 네이버 마루 부리와 Pretendard로 구현했습니다.

      -

      웹

      도메인당 · 웨이트 전종 · 자체 호스팅 WOFF2

      예시 140,000원

      -

      출판

      도서 권수당 · 인쇄+전자 동시

      예시 220,000원

      +

      웹

      도메인당, 웨이트 전종, 자체 호스팅 WOFF2

      140,000원

      +

      출판

      도서 권수당, 인쇄와 전자책 동시

      220,000원

      조판해 보기
      -

      한성활자연구소 · 가상 서체 기획

      화면의 글꼴은 마루 부리와 Pretendard로 구현했습니다.

      +
      +
      + +

      한성활자연구소는 가상 서체 기획 쇼케이스입니다. 서체와 가격은 예시이며, 화면의 글꼴은 마루 부리와 Pretendard(둘 다 SIL 오픈 폰트 라이선스)로 구현했습니다.

      +
      +
      diff --git a/apps/site/public/work/font-foundry/og.png b/apps/site/public/work/font-foundry/og.png new file mode 100644 index 0000000..e369274 Binary files /dev/null and b/apps/site/public/work/font-foundry/og.png differ diff --git a/apps/site/public/work/font-foundry/styles/fonts.css b/apps/site/public/work/font-foundry/styles/fonts.css index f5234d6..cbe3a14 100644 --- a/apps/site/public/work/font-foundry/styles/fonts.css +++ b/apps/site/public/work/font-foundry/styles/fonts.css @@ -1,3 +1,4 @@ +/* 마루 부리 — 표본 서체 전용(자체 호스팅, 네이버 공식 배포본 그대로, SOURCES.md). UI 서체는 tokens.css의 Noto Serif KR·Noto Sans KR이다. */ @font-face { font-family: "MaruBuri"; src: url("../assets/fonts/MaruBuri-Regular.woff2") format("woff2"); diff --git a/apps/site/public/work/font-foundry/styles/main.css b/apps/site/public/work/font-foundry/styles/main.css index adffb99..d41b3a9 100644 --- a/apps/site/public/work/font-foundry/styles/main.css +++ b/apps/site/public/work/font-foundry/styles/main.css @@ -1,125 +1,144 @@ -@import url("./tokens.css?v=20260912all"); +@import url("./tokens.css?v=20261006"); +/* ── 기본 ── */ *, *::before, *::after { box-sizing: border-box; } * { margin: 0; } html { background: var(--surface); scroll-behavior: smooth; -webkit-text-size-adjust: 100%; } -body { background: var(--surface); color: var(--ink); font-family: var(--font-body); font-size: var(--step-0); line-height: var(--leading-normal); font-synthesis-weight: none; -webkit-font-smoothing: antialiased; } -:where(h1, h2, h3) { font-family: var(--font-display); font-weight: 600; line-height: var(--leading-tight); text-wrap: balance; word-break: keep-all; overflow-wrap: break-word; } -h2 { font-size: var(--step-3); } -h3 { font-family: var(--font-body); font-size: var(--step-1); font-weight: 600; } -:where(p, li, dd, dt, th, td, figcaption) { overflow-wrap: break-word; word-break: keep-all; text-wrap: pretty; } -a { color: inherit; } +body { background: var(--surface); color: var(--ink); font-family: var(--font-body); font-size: var(--step-0); line-height: var(--leading-normal); font-synthesis: none; -webkit-font-smoothing: antialiased; word-break: keep-all; overflow-wrap: anywhere; } +:where(h1, h2) { font-family: var(--font-display); font-weight: var(--weight-display); line-height: var(--leading-tight); letter-spacing: -0.02em; text-wrap: balance; } +h2 { font-size: var(--step-2); } +h3 { font-family: var(--font-display); font-size: var(--step-1); font-weight: var(--weight-display); line-height: 1.35; letter-spacing: -0.01em; } +:where(p, li, dd, dt, figcaption) { text-wrap: pretty; } +a { color: inherit; text-underline-position: under; } .muted { color: var(--ink-muted); } -.eyebrow, .stage-label, .bench-caption { color: var(--ink-muted); font-size: var(--step--1); letter-spacing: 0.04em; } :focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; } -.skip { position: absolute; z-index: 100; top: 0; left: -9999px; padding: var(--space-3) var(--space-4); background: var(--ink); color: var(--surface); } +.skip { position: absolute; z-index: 100; top: 0; left: -9999px; padding: var(--space-3) var(--space-4); background: var(--accent); color: var(--accent-ink); } .skip:focus { left: 0; } .wrap { width: min(100% - 2 * var(--pad-inline), var(--container)); margin-inline: auto; } -.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; 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); } +/* ── 머리: 워드마크 + 서체 탭(내비는 탭 하나뿐) ── */ +.site-head { border-bottom: 1px solid var(--line); } +.head-inner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); min-block-size: var(--head-height); } +.wordmark { font-family: var(--font-display); font-size: var(--step-1); font-weight: var(--weight-display); text-decoration: none; white-space: nowrap; } +.tabs { display: flex; } +.tab { min-block-size: 2.75rem; white-space: nowrap; padding-inline: var(--space-4); border: 1px solid var(--line-strong); border-inline-start-width: 0; border-radius: var(--radius); background: transparent; color: var(--ink); cursor: pointer; font: inherit; } +.tab:first-child { border-inline-start-width: 1px; } +.tab:hover { background: var(--accent-soft); } +.tab[aria-selected="true"] { border-color: var(--accent); background: var(--accent); color: var(--accent-ink); } +.tab[aria-selected="true"] + .tab { border-inline-start-color: var(--accent); } -.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 { 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: 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; 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 { 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-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 { 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; } -.bench { display: grid; grid-template-columns: minmax(13rem, 3fr) minmax(0, 7fr); gap: var(--space-6) var(--space-7); margin-top: var(--space-6); padding-top: var(--space-5); border-top: 1px solid var(--line); } -.bench-controls { display: grid; align-content: start; gap: var(--space-4); } +/* ── 첫 화면: 글자 하나가 이미지다 ── */ +.hero { padding-block: 0 var(--space-6); } +.hero-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); grid-template-rows: minmax(0, 1fr) auto; gap: var(--space-5) var(--space-6); min-block-size: calc(100svh - var(--head-height) - var(--space-5)); } +.hero-grid > * { min-inline-size: 0; } +.hero-title { grid-column: 1; grid-row: 2; align-self: end; } +.hero h1 { font-size: var(--step-3); white-space: nowrap; } +.hero-lead { max-inline-size: 28em; margin-top: var(--space-3); color: var(--ink-muted); } +.stage { grid-column: 1 / -1; grid-row: 1 / 3; display: grid; grid-template-columns: subgrid; grid-template-rows: subgrid; } +.glyph { grid-column: 1 / -1; grid-row: 1; justify-self: center; align-self: center; font-family: var(--face-buri); font-size: var(--glyph-size); font-weight: 400; line-height: 1; color: var(--accent); transform-origin: center bottom; view-transition-name: glyph; } +[data-face="godic"] .glyph { font-family: var(--face-godic); } +.axis { grid-column: 2; grid-row: 2; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-3) var(--space-5); align-items: end; align-self: end; } +.type-status { grid-column: 1 / -1; color: var(--ink-muted); font-size: var(--step--1); max-inline-size: 40em; view-transition-name: face-status; } .control { display: grid; gap: var(--space-2); } .control-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); } .control label, .control output { color: var(--ink-muted); font-size: var(--step--1); } .control output, .order-price { font-variant-numeric: tabular-nums; } +.control-glyph input { inline-size: 3.25rem; min-block-size: 2.75rem; border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--surface); color: var(--ink); font-family: inherit; font-size: var(--step-1); text-align: center; } 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 { 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; } +input[type="range"] { min-block-size: 2.75rem; margin: 0; } +select { min-block-size: 2.75rem; border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--surface); color: var(--ink); font: inherit; padding-inline: var(--space-3); } -.case-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--space-6) var(--space-7); align-items: start; } -.case-photo, .case-text { min-inline-size: 0; } -.case-photo img, .case-photo-b img { display: block; max-inline-size: 100%; inline-size: 100%; block-size: auto; border-radius: var(--radius); } -.case-photo figcaption, .case-photo-b figcaption { margin-top: var(--space-2); color: var(--ink-faint); font-size: var(--step--1); } -.case-text { max-inline-size: 42rem; } -.case-text h2 { margin-top: var(--space-3); margin-bottom: var(--space-4); } -.case-photo-b { max-inline-size: 34rem; margin-top: var(--space-5); } +/* ── 섹션 공통 ── */ +.section { padding-block: var(--space-8); } +.section[id] { scroll-margin-block-start: var(--space-4); } +.band { border-block: 1px solid var(--line); background: var(--surface-raised); } +.section-head { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 4fr); gap: var(--space-4) var(--space-6); align-items: end; } +.section-head h2 { max-inline-size: 22em; } +.section-note { color: var(--ink-muted); max-inline-size: 28em; } + +/* ── 비교: 같은 문장, 같은 폭 ── */ +.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-5) var(--space-6); border-top: 3px solid transparent; margin-top: -2px; } +[data-face="buri"] .typeface[data-face="buri"], [data-face="godic"] .typeface[data-face="godic"] { border-top-color: var(--accent); } +.typeface-head .muted { margin-top: var(--space-2); font-size: var(--step--1); max-inline-size: 30em; } +.specimen { margin-top: var(--space-5); inline-size: 100%; font-size: 1.375rem; line-height: 1.8; } +.specimen-buri { font-family: var(--face-buri); } +.specimen-godic { font-family: var(--face-godic); } + +/* ── 조판 시험대 ── */ +.bench { display: grid; grid-template-columns: minmax(13rem, 3fr) minmax(0, 7fr); gap: var(--space-6) var(--space-7); margin-top: var(--space-6); padding-top: var(--space-5); border-top: 1px solid var(--line); } +.bench-controls { display: grid; align-content: start; gap: var(--space-4); } +.btn-reset { justify-self: start; min-block-size: 2.75rem; margin-top: var(--space-2); padding-inline: 0.9rem; border: 1px solid var(--line-strong); border-radius: var(--radius); background: transparent; color: var(--ink); cursor: pointer; font: inherit; } +.btn-reset:hover { border-color: var(--accent); color: var(--accent); } +.bench-caption { margin-bottom: var(--space-2); color: var(--ink-muted); font-size: var(--step--1); } +.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(--face-buri); font-size: 1.0625rem; line-height: 1.8; } + +/* ── 옆면 여백과 자간: 글자가 그림이다 ── */ +.spacing-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--space-6) var(--space-7); align-items: center; } +.spacing-grid > * { min-inline-size: 0; } +.spacing-text p { margin-top: var(--space-4); max-inline-size: var(--measure); } +.track-demo { border-block: 1px solid var(--line); padding-block: var(--space-4); overflow: hidden; } +.track-line { font-family: var(--face-buri); font-size: clamp(2.5rem, 1rem + 6vw, 6rem); font-weight: 400; line-height: 1.2; color: var(--accent); white-space: nowrap; } +[data-face="godic"] .track-line { font-family: var(--face-godic); } +.track-tight { letter-spacing: -0.05em; } +.track-loose { letter-spacing: 0.1em; } +.track-demo figcaption { margin-top: var(--space-3); color: var(--ink-muted); font-size: var(--step--1); } + +/* ── 라이선스(예시) ── */ .order-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr) auto; gap: var(--space-5) var(--space-7); align-items: center; } .order-grid > * { min-inline-size: 0; } -.order-grid h2 { margin-top: var(--space-3); } -.order-grid .muted { max-inline-size: 28rem; margin-top: var(--space-3); } +.order-text .muted { max-inline-size: 28em; margin-top: var(--space-3); } .order-rows { border-top: 1px solid var(--line); } .order-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); padding-block: var(--space-3); border-bottom: 1px solid var(--line); } .order-row p { margin-top: var(--space-1); color: var(--ink-muted); font-size: var(--step--1); } -.order-price { flex: 0 0 auto; color: var(--ink) !important; font-family: var(--font-display); font-size: var(--step-1) !important; white-space: nowrap; } -.btn { display: inline-flex; align-items: center; justify-content: center; min-block-size: 2.75rem; border: 1px solid var(--accent); border-radius: var(--radius); background: var(--accent); color: var(--accent-ink); font-weight: 600; padding-inline: 1.25rem; text-decoration: none; } +.order-price { flex: 0 0 auto; color: var(--ink); font-family: var(--font-display); font-weight: var(--weight-display); font-size: var(--step-1); white-space: nowrap; } +.btn { display: inline-flex; align-items: center; justify-content: center; min-block-size: 2.75rem; border: 1px solid var(--accent); border-radius: var(--radius); background: var(--accent); color: var(--accent-ink); padding-inline: 1.25rem; text-decoration: none; } .btn:hover { background: var(--accent-hover); } -.site-foot { border-top: 1px solid var(--line); padding-block: var(--space-5); font-size: var(--step--1); } -.foot-inner { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-3) var(--space-5); } +/* ── 푸터: 큰 워드마크 한 줄 ── */ +.site-foot { border-top: 1px solid var(--line); padding-block: var(--space-6) var(--space-5); overflow: hidden; } +.foot-mark { font-family: var(--font-display); font-weight: var(--weight-display); font-size: var(--wordmark-size); line-height: 1; letter-spacing: -0.03em; color: var(--accent); white-space: nowrap; } +.foot-note { margin-top: var(--space-4); color: var(--ink-muted); font-size: var(--step--1); max-inline-size: 44em; } + +/* ── 모션: 탭 전환은 View Transitions, 표본 크기는 스크롤이 움직인다 ── */ +::view-transition-old(glyph), ::view-transition-old(face-status) { animation: calc(var(--dur-slow) * 0.65) var(--ease-in) both vt-out; } +::view-transition-new(glyph), ::view-transition-new(face-status) { animation: var(--dur-slow) var(--ease-soft) both vt-in; } +@keyframes vt-out { to { opacity: 0; translate: 0 -0.5rem; } } +@keyframes vt-in { from { opacity: 0; translate: 0 0.5rem; } } +@media (prefers-reduced-motion: no-preference) { + @supports (animation-timeline: scroll()) { + .glyph { animation: glyph-shrink 1ms linear both; animation-timeline: scroll(root block); animation-range: 0 80vh; } + @keyframes glyph-shrink { to { scale: 0.55; } } + } +} + +/* ── 반응형 ── */ @media (max-width: 900px) { - .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; } + .hero-grid { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) auto auto; gap: var(--space-4); } + .hero-title { grid-row: 2; } + .stage { grid-row: 1 / 4; } + .glyph { grid-column: 1; } + .axis { grid-column: 1; grid-row: 3; grid-template-columns: 1fr; } + .control-glyph { grid-template-columns: auto 1fr; align-items: center; } + .hero-lead { max-inline-size: 34em; } + .section-head, .comparison, .bench, .spacing-grid, .order-grid { grid-template-columns: 1fr; } .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); } - .nav a { flex: 0 0 auto; padding-block: var(--space-1); } - .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; } + :root { --glyph-size: min(72vw, 42svh); --head-height: 7.5rem; } + .head-inner { flex-wrap: wrap; gap: var(--space-2) var(--space-3); padding-block: var(--space-3); } + .face-nav { flex: 1 0 100%; } + .tabs { display: grid; grid-template-columns: 1fr 1fr; } + .tab { padding-inline: var(--space-3); white-space: normal; } + .hero { padding-block: 0 var(--space-5); } + .hero-grid { min-block-size: calc(100svh - var(--head-height) - var(--space-4)); } + .hero h1, .wordmark { white-space: normal; } .section { padding-block: var(--space-7); } - .section[id] { scroll-margin-block-start: calc(7rem + 1rem); } + .order-row { align-items: flex-start; flex-direction: column; } + .order-price { white-space: normal; } } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } - *, *::before, *::after { transition-duration: 0.01ms !important; } + ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; } } diff --git a/apps/site/public/work/font-foundry/styles/tokens.css b/apps/site/public/work/font-foundry/styles/tokens.css index c63a644..2c4646a 100644 --- a/apps/site/public/work/font-foundry/styles/tokens.css +++ b/apps/site/public/work/font-foundry/styles/tokens.css @@ -1,23 +1,39 @@ +/* 한성활자연구소 토큰 — 값은 여기서만 정의한다(05 Q-25). 다른 CSS는 var()로만 쓴다. */ :root { + /* 타입 스케일: 본문 17px, 보조 13px, 표제는 한 줄 큰 낱말 */ --step--1: 0.8125rem; --step-0: 1.0625rem; --step-1: 1.25rem; - --step-2: clamp(1.75rem, 1.3rem + 1.5vw, 2.5rem); - --step-3: clamp(2.5rem, 1.6rem + 4vw, 5rem); - --leading-tight: 1.08; - --leading-normal: 1.8; - --font-display: "MaruBuri", Georgia, serif; - --font-body: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, sans-serif; - --surface: #f5f1e8; - --surface-raised: #ede7d9; - --ink: #26221d; - --ink-muted: #625b52; - --ink-faint: #756c60; - --line: #d9d1bf; - --accent: #a63d2f; - --accent-ink: #f5f1e8; - --stage-ink: #fffaf1; - --accent-hover: #8a3225; + --step-2: clamp(1.5rem, 1.15rem + 1.3vw, 2.125rem); + --step-3: clamp(2.75rem, 1.4rem + 4.5vw, 5.75rem); + --glyph-size: min(56svh, 50vw); + --wordmark-size: clamp(2.5rem, 12vw, 11rem); + --leading-tight: 1.25; + --leading-normal: 1.7; + --measure: 36em; + + /* UI 서체: 표제 Noto Serif KR 900 / 본문 Noto Sans KR. 표본 서체는 UI에 쓰지 않는다 */ + --font-display: "Noto Serif KR", "Apple SD Gothic Neo", "Malgun Gothic", serif; + --font-body: "Noto Sans KR", "Apple SD Gothic Neo", "Malgun Gothic", sans-serif; + --weight-display: 900; + --weight-strong: 400; /* Noto Sans KR은 400 한 굵기만 싣는다(전송량). 강조는 색·서체로 낸다 */ + + /* 표본 서체: 마루 부리(자체 호스팅, 정적 400·600)와 Pretendard(가변 45~920). 표본·시험대에만 쓴다 */ + --face-buri: "MaruBuri", "Noto Serif KR", serif; + --face-godic: "Pretendard Variable", Pretendard, "Noto Sans KR", sans-serif; + + /* 색: 밝은 쿨 중립 바탕, 240° 남색 잉크 강조. 강조는 거대 글자·슬라이더·선택 상태에만 */ + --surface: #f1f2f4; + --surface-raised: #e5e7eb; + --ink: #16171d; + --ink-muted: #565966; + --line: #c3c6cf; + --line-strong: #737786; + --accent: #22235a; + --accent-hover: #15163f; + --accent-ink: #f1f2f4; + --accent-soft: #d8daea; + --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 1rem; @@ -26,11 +42,14 @@ --space-6: 4rem; --space-7: 6rem; --space-8: clamp(5rem, 3rem + 7vw, 10rem); - --radius: 2px; - --container: 76rem; - --container-wide: 106.25rem; + + --radius: 0; + --container: 80rem; --pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4.5rem); - --pad-wide: clamp(1.25rem, 1vw + 1rem, 4rem); + --head-height: 4.25rem; + --dur-quick: 160ms; + --dur-slow: 320ms; --ease-soft: cubic-bezier(0.25, 1, 0.35, 1); + --ease-in: cubic-bezier(0.5, 0, 0.9, 0.4); } diff --git a/apps/site/public/work/form-architecture/design.md b/apps/site/public/work/form-architecture/design.md index 8ce5b34..d6897ce 100644 --- a/apps/site/public/work/form-architecture/design.md +++ b/apps/site/public/work/form-architecture/design.md @@ -1,50 +1,87 @@ -# 스튜디오 폼 — 디자인 결정 기록 +# design.md — 스튜디오 폼 -## 0. 브리프와 범위 +## 진행 상태 -- 무엇을: 빛·비례·구조를 사진과 도면으로 함께 읽는 가상 공간건축 포트폴리오 예시. -- 누구에게: 건축 작업의 기록 방식을 살피고 프로젝트 인덱스로 이동하려는 방문자. -- 제약: 기존 흑백 사진·도면과 Pretendard를 보존한다. 실제 수행 이력·연락처로 오인될 수 없게 가상 사례와 생성 이미지라는 사실을 가까이에 표기한다. 기본 언어는 ko-KR 한국어이며, 영문은 고유 표기 FORM만 사용한다. +- 2026-10-06 전체(리디자인) 경로 0~6단계 완료. 재개할 일이 생기면 "검증"의 미검증 목록부터 본다. -경로: **연장**. 정적 HTML의 토큰·콘텐츠 구조를 재구성했으며 이 문서가 이후 작업의 국소 기준이 된다. +## 브리프 -## 1. 조사와 관찰 +- 무엇을: 주택·기념관·재생 건축 여섯 건을 사진과 평면도로 기록하는 가상 건축연구소 포트폴리오 한 장. 대표 기록은 폼 하우스(가평, 2025). +- 누구에게: 건축 작업의 기록 방식을 살피고 프로젝트 색인으로 이동하려는 건축주·협력사. 목표 행동 없음(상담·견적을 받지 않는 가상 연구소). +- 제약: 정적 HTML·CSS, 기존 흑백 사진 3장과 평면도만 사용(새 이미지 없음), 기본 언어 ko-KR(상호 "스튜디오 폼"만 표기, 영문 FORM 미사용), 한자 금지, 가운뎃점 메타 문자열 금지, 폰트는 OFL·공개 배포본. +- 배정(outputs/rework-2026-10-06/plan.md): 표제 + 전폭 무대 · 측면 색인 · 큰 워드마크 한 줄 푸터 · Gothic A1 300 / Gothic A1 · 밝음 중립 #fcfcfb · 강조 없음 · radius 0 · scroll-driven 진행선 · 종이 결 feTurbulence · 극단 비율 파노라마 + 흑백. +- 화면: `index.html` 한 장(2차 페이지 없음). -| 출처 | 관찰 | 적용 범위 | -| --- | --- | --- | -| [David Chipperfield Architects — Projects](https://davidchipperfield.com/projects) | 프로젝트 사진·모형과 그 아래 정렬된 메타데이터가 탐색의 위계를 소유하고, Grid/List/Filter는 보조 탐색으로 물러난다. | 사진을 첫 화면의 주 장면으로 두고, 인덱스는 번호·이름·위치·연도를 정렬한다. | -| [Klim Type Foundry](https://klim.co.nz/) | 글자와 작업물이 표면을 주도하고 인터페이스는 뒤로 물러난다. | 큰 헤드라인, 얇은 선, 흑백 사진의 우선순위. | -| [Carbon 2x Grid](https://carbondesignsystem.com/elements/2x-grid/usage/) | 넓은 표면과 읽기 표면은 분리된 레일을 가져야 한다. | 최대 1800px stage와 1280px 정보 레일, 720px 본문 measure. | +## 미확정 -표현을 복제하지 않았다. 이 페이지의 고유한 결합은 `제목 → 실제로 보이는 사진 → 도면의 이유`라는 건축 기록 순서다. +- 프로젝트 02~06의 평면도·사양·사진 — 없음. 색인 여섯 건 모두 같은 예시 평면도(`#drawing`)로 연결되며 레일 아래에 그 사실을 적었다. +- 연락처·주소 — 없음. 연구소 섹션과 `
      `에 "연락처와 사무실 주소가 없습니다"로 표기. +- 방향 카드는 위임으로 골랐다(아래 "방향"). -## 2. 수정 전 문제와 결정 +## 레퍼런스 -- 1440px 첫 화면에서 소개 문단이 약 80px 열에 갇혀 단어마다 줄바꿈됐고, 사진과 인덱스 행동이 첫 화면에 없었다. -- `설립 연도 예시`, `팀 구성 예시`처럼 작업 판독에 기여하지 않는 가상 수치를 제거했다. -- 기존 인덱스의 여섯 항목은 모두 같은 도면으로 연결됐지만 그 관계가 설명되지 않았다. 이제 목록 직전에 같은 가상 도면을 다른 관점에서 참조한다는 사실을 고지한다. -- 기존 생성 이미지는 유지하고 각 사진 캡션에 `생성 이미지 · 실제 프로젝트 사진 아님`을 남긴다. +| 슬롯 | 출처 | 가져온 원리 | +|---|---|---| +| R1 구조 | https://davidchipperfield.com/projects/neues-museum — 브라우저 실측 1440: h1 3줄(42px/700) 좌상단 → 바로 아래 전폭 무대 사진 1149×768 | 표제 블록 다음에 무대가 온다. 우리는 표제를 26px/300으로 낮추고 무대를 3.6:1로 늘렸다 | +| R1' 색인 | https://www.6a.co.uk/projects — "_Tate Liverpool_, Royal Albert Dock 2022 - 2027" | 색인 행 = 번호 / 이름 / 장소 연도 | +| R3 디테일 | https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timeline — sticky 우측 차례(폭 296, 링크 16/24, 행 32, 현재 항목 `aria-current` + 2px 막대 + 틴트) | 측면 색인의 현재 위치: `aria-current` + 왼쪽 2px 먹 막대. 틴트는 강조색 없음 규칙으로 뺐다 | +| R2 톤 | 비움 — 흑백 사진·먹 선이 톤을 이미 정했고, 배정이 서체·바탕·강조를 고정했다 | — | +| 미검증 | lacatonvassal.com(텍스트 페처 오판, 실측은 드롭다운 필터), sergisonbates.com 401, sanaa.co.jp 인증서 오류, pezo.cl 빈 응답 | 쓰지 않음 | -## 3. 토큰과 레이아웃 계약 +## 방향 -- 표면 `#fdfdfb`, 전경 `#171b19`, 중립 선 `#d8dbd7`, 보조 글자 `#5b615d`. -- 본문 16px 이상, 캡션 12px 이상, 본문 줄간 1.75, 읽기 폭 720px. -- `h1: clamp(2.5rem, 5.5vw, 5.5rem)`; Pretendard 단일 패밀리. -- 1440px 정보 레일 최대 1280px, stage 최대 1800px; 2560px에서는 사진만 stage에 머물고 산문은 720px 내에서 읽힌다. -- 모바일 gutter는 20px, 320px에서 16px. 이미지의 원본 비율을 유지하며 `object-fit: cover`는 슬롯에서 정한 비율 안의 편집에만 사용한다. +지문: 표제+전폭 무대 · Gothic A1 300 / Gothic A1 400 · 밝음 중립 #fcfcfb · 강조 없음(먹만) · radius 0 · 흐름 [긴 문장 표제 → 3.6:1 흑백 파노라마 → 평면도(스크롤로 선이 그어짐)+사양 → 사진 2장(3:4, 2.4:1) → 연구소 → 큰 워드마크 푸터] · 측면 색인(≤64rem 상단 접는 차례) · 큰 워드마크 한 줄 푸터 · 극단 비율 파노라마 + 흑백, 캡션은 번호와 함께 · scroll-driven(진행선 1px, 도면 stroke-dashoffset), 등장 리빌 없음 -성능 예산: 첫 화면 JS 0KB / 첫 인터랙션은 정적 링크 / 폰트 패밀리 1개. 이미지 생성·추가는 하지 않는다. +- 한 문장 컨셉: 건축주가 폼 하우스 한 건의 파노라마와 평면도를 사실 문장과 함께 읽고, 왼쪽 색인에서 다른 다섯 건으로 이동한다. +- 프리셋: swiss-minimal(평평한 종이, 재질 최소). 보조 축 없음. 종이 결은 재질 한 가지(종이)로 한정. +- 히어로 글 묶음: 라벨 없음 / 서너 줄 긴 문장 표제 / 리드 없음 / 행동 없음(다음 정보인 무대로 이어진다). +- 표현 리스크: 표제 26px vs 푸터 워드마크 176px(6.8배)의 크기 긴장. 1440·390 전체 캡처로 확인했다. +- 카드(위임, 구조가 다른 3장): A 도면집 한 장(좌측 색인 + 표제 3줄 → 파노라마) / B 긴 두루마리(사진 3장을 가로 스크롤 띠로) / C 평면도가 무대(전폭 평면도 먼저). **A 채택** — 설득 재료가 사진(재료·빛)과 평면도(배치·동선) 둘이고 R1이 사진을 표제 바로 아래 무대로 두며, 색인의 현재 위치 표시는 한 장을 읽는 동안에만 의미가 생긴다. B는 가로 스크롤이 세로 진행선과 충돌해 기각, C는 첫 화면에 사진이 없어 기각. +- 반사실: 첫 화면 고유 요소 3개 — 긴 수평창 파빌리온 파노라마(자산), 가평·백령도·성수 색인 6건, "사진과 평면도로 집을 왜 그렇게 앉혔는지" 방법 문장. 세무사무소로 치환하면 색인 레일·파노라마가 성립하지 않는다. 페이지 수렴: plan.md상 orbit-aerospace와 측면 색인·극단 비율·scroll-driven 도면 선 3축이 같다 — 글 묶음·푸터·바탕·서체로 갈라 둔 상태이고 섹션 흐름이 달라야 한다. +- 보존 자산: pavilion-bw·interior-bw·model-bw webp, 프로젝트 6건의 이름·위치·연도, 폼 하우스 사양 수치와 설계 이유 문장, `.index a` 6개 → `#drawing`. -## 4. 검증 추적 +## 토큰과 예산 -- 320, 390, 768, 1440, 1920, 2560px에서 문서 및 요소 가로 오버플로, h1/본문 가시성, 레일 상한, 이미지 로드, 탭 순서를 점검한다. -- `prefers-reduced-motion`과 효과 제거 상태에서도 콘텐츠가 같은 순서로 읽히는지 점검한다. -- 200% 텍스트 확대에서 본문·CTA가 사라지지 않는지 점검한다. -- 실행 결과와 화면 캡처는 `outputs/design-expertise-20260912/form/`에 남긴다. +값은 `styles/tokens.css`. 역할만 적는다. -## 2026-09-12 전체 쇼케이스 점검 +- 서체 한 패밀리 두 웨이트: 300은 표제·h2·푸터 워드마크(가늘고 넓게), 400은 본문·색인. 500은 폰트 예산 때문에 뺐고 색인 이름은 색(ink)과 행 위치로 구분한다. +- 색: 바탕 #fcfcfb(배정 유지), 먹 #161716, 보조 #585b58(6.7:1), 흐림 #6b6e6b(5.0:1), 선 두 단계. 강조색 없음. +- 크기: 캡션 13px(한글 하한), 본문 16px, 표제·h2 clamp(20~26px) — "작은 표제", 워드마크 clamp(52~176px). 표제 measure 30em(1440에서 3줄), 본문 36em. +- 무대 비율: 3.6:1(≥64rem) / 2.4:1 / 16:10(≤40rem). 사진 띠 2.4:1 / 2:1 / 16:10. +- 예산(tokens §5 데모/포트폴리오): JS 인라인 1.2KB / 폰트 **254KB 실측 — 200KB를 올렸다**(Gothic A1 한글 슬라이스 두 웨이트; 본문까지 300으로 줄이면 가독성이 떨어진다) / LCP 348ms(1440)·252ms(390) 로컬 / CLS ≤0.001 / 첫 화면 전송 약 430KB. -- 기존 흑백 사진·도면 구도, 앵커와 긴 모바일 라벨의 줄바꿈 규칙을 보존했다. 로컬 MaruBuri 파일을 함께 제공하되 현재 Pretendard 단일 패밀리 본문 계약은 바꾸지 않았다. -- `verify-final` 18개 묶음은 16개가 즉시 통과했다. 전체 묶음의 invariant·visual 검사는 Gaon·Dure를 대상으로 하며, 나머지 2개는 수정 뒤 release 독립 재실행에서 ALL PASS·ALL MATCH였다. -- root는 28개 모바일, 21개 데스크톱, 7개 하위 경로와 30개 앱 상태를 직접 검토해 수용했다. stress 112건은 hard failure 0이며, 가로 넘침 4건은 targeted fix 뒤 390px 재측정에서 0이다. -- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다. +## 이펙트와 폴백 + +- 도면 선 그리기: `.plan`에 `view-timeline: --plan`, 벽→내벽→창→문 순으로 `stroke-dashoffset 1 → 0`. `@supports (animation-timeline: view())` 안에만 초기 상태를 두어 미지원(Firefox)은 완성 상태. 감소 모션도 완성 상태. +- 진행선: 레일 오른쪽 1px 실선 위 먹 구간이 `scroll(root block)`로 자란다. 감소 모션은 가득 찬 상태(정보). +- 종이 결: feTurbulence 타일(220px, 2옥타브) SVG를 `body::before` 고정 레이어 배경으로, opacity 0.08. 런타임 필터 연산 없음. 끄면 바탕색만 남고 레이아웃은 그대로. +- 읽는 위치: IntersectionObserver가 "이 장" 링크에 `aria-current`를 준다. JS가 없으면 표시만 없고 색인은 항상 열린다. +- 좁은 화면 차례: `
      `를 JS가 폭(64rem)에 따라 열고 닫는다. 링크를 누르면 닫힌다. + +## 하지 않기로 한 것 + +- 상단 바·backdrop-filter·스크롤 리빌·eyebrow·리드·버튼 — 수렴 지문이라 뺐다. +- 영문 "FORM" 워드마크·영문 장식 라벨 — 기본 언어 규칙. +- 같은 사진을 두 번 싣기(옛 히어로·사진 섹션 중복) — R6 실패 신호. +- 프로젝트 02~06의 상세 — 사실이 없어 만들지 않았다(placeholder 대신 색인에서 같은 예시 평면도로 보낸다는 고지). +- 가운뎃점 메타 문자열, 캡션마다 붙던 "생성 이미지 · 실제 프로젝트 사진 아님" — 히어로 아래 고지 한 문단이 페이지 범위를 한 번 소유한다. 푸터는 브랜드 범위 한 줄만. +- MaruBuri 로컬 폰트 — 싣지 않는다(파일은 출처 기록과 함께 남겨 둠). `fonts.css`는 지웠다. + +## 검증 + +- 검증 줄: html-validate 0 오류 · stylelint 0 · no-hanja PASS · showcase-audit PASS 6/0(320~2560) · design-gate 충족 13 / 미검증 0 / 실패 0(꺼짐 3: static·visual·webkit; 대비 AA 66요소, axe 0, 터치타깃·오버플로·죽은 선택자·색 리터럴 0) · archive-showcases-e2e 107/108(실패 1 = 390 접힌 차례 선택자, 제안 수정은 outputs 노트) · similarity 경고 없음. +- 시각 루브릭(약한 축부터): R3 1(레일 시작선 4개, 역할 설명 가능) · R6 2 · R1 2 · R2 2 · R4 2 · R5 2 · R7 2 · R8 2 · R9 2 · R10 2 = 19/20. 근거와 2점 반례는 `outputs/rework-2026-10-06/form-architecture.md`. +- 미검증: WebKit 렌더, 실기기 INP, Firefox 실제 실행(폴백 경로는 `@supports`로 보장). +- 캡처: 스크래치 `form/final-*.png`(1440·390 첫 화면·전체·회색조·흐림·감소 모션·도면 중간 상태·2x 크롭). + +## 피드백 기록 · 보존 목록 + +- 보존: 흑백 사진 3장과 역할(남측 입면 파노라마 / 전시실 빛 / 석고 모형), 가로형 평면도, 색인 6건, 폼 하우스 사양. +- 피드백: 없음(2026-10-06 재작업 직후). + +## 변경 기록 + +- 2026-10-06 · 전체 리디자인 · plan.md 배정(측면 색인·표제+전폭 무대·워드마크 푸터·Gothic A1·진행선·종이 결·파노라마)에 따라 구조·토큰·카피를 다시 썼다. 평면도를 가로형(14,400×9,600)으로 다시 그려 치수·비례 불일치와 "기둥 없음" 문장과의 모순을 없앴다. 가운뎃점 메타 문자열·eyebrow·리드·버튼·고지 띠 푸터 제거. +- 2026-10-02 · 카피 교정 · "빛이 머무는" 같은 무생물 주어·시구형 표제를 사실 문장으로. +- 2026-09-12 · 전체 쇼케이스 점검 · verify-final 묶음 통과, MaruBuri 파일 동봉(미사용). +- 2026-08 · 최초 작성 · Pretendard 단일 패밀리, 상단 바 + 두 줄 표제 + 2.35:1 사진 + 인덱스 + 도면 + 사진 시퀀스 + 상담 구성. diff --git a/apps/site/public/work/form-architecture/index.html b/apps/site/public/work/form-architecture/index.html index 36bb38a..1e8587c 100644 --- a/apps/site/public/work/form-architecture/index.html +++ b/apps/site/public/work/form-architecture/index.html @@ -3,93 +3,162 @@ - 스튜디오 폼 · 공간건축연구소 - - - + 스튜디오 폼 — 주택, 기념관, 재생 건축 설계 + + + - - - - - + + + + + + + + + -
      - -
      -
      -
      -
      -
      -

      가상 공간건축 포트폴리오 · 설계 기록 형식

      -

      공간을 짓는
      명료한 질서.

      -
      -
      -

      빛이 머무는 방향과 구조가 건너는 거리를 함께 읽습니다. 사진은 결과를, 도면은 그 결과가 만들어진 이유를 남깁니다.

      - 프로젝트 인덱스 보기 -
      -
      -
      - 긴 수평창이 난 콘크리트 파빌리온에 벽 그림자가 지는 모습 -
      폼 하우스 · 가평 · 2025생성 이미지 · 실제 프로젝트 사진 아님
      -
      -

      이 페이지의 프로젝트명·연도·도면·이미지는 공간 설계 기록의 형식을 설명하기 위한 가상 사례입니다.

      -
      -
      - -
      +
      +
      + 스튜디오 폼 +
      + 차례닫기 + +
      + +
      -
      -
      -
      -

      도면으로 읽는 장면

      -

      폼 하우스, 가평

      -

      대지 남쪽의 소나무군을 지우지 않기 위해 집은 길게 눕고, 마당은 집 안쪽으로 들어왔습니다. 구조는 9.6m 축 한 번으로 건너뛰어 거실에 기둥이 없습니다.

      -

      벽체는 단열 성능의 요구치에서 두께가 정해졌습니다. 창은 남향 긴면 하나, 동쪽 마당 면 하나. 그 두 개로 이 집의 하루가 결정됩니다.

      -
      구조
      RC 라멘 · 9.6m 스팬
      면적
      132.4 ㎡ (40.0평)
      창개구
      남측 14.4m · 동측 4.2m
      공사 기간
      2025-04 – 2026-01
      -
      -
      - - - 14,4009,600 - 거실주방·식당서비스현관N +
      +
      +

      스튜디오 폼은 주택, 기념관, 오래된 건물의 재생을 설계하는 건축연구소입니다. 프로젝트마다 완공 사진과 평면도를 함께 실어, 집을 왜 그렇게 앉혔는지 설명합니다.

      +
      + 긴 수평창이 난 콘크리트 파빌리온의 남측 벽에 그림자가 비스듬히 지는 모습 +
      사진 01폼 하우스 남측 진입부, 가평
      +
      +

      이 페이지의 프로젝트명, 연도, 도면과 사진 세 장은 모두 예시입니다. 사진은 생성 이미지이고 실제 프로젝트 사진이 아닙니다.

      +
      + +
      +

      폼 하우스는 남쪽 소나무를 베지 않으려고 집을 동서로 길게 앉혔습니다.

      +
      + + + + + + + + + + + + + 14,4009,600 + 거실마당주방, 식당현관 + N -

      폼 하우스 · 1층 평면도 · 가상 설계 도면 예시 · mm

      +
      도면 01폼 하우스 1층 평면도, 단위 mm
      +
      +
      +
      +

      대지 남쪽에 소나무가 있어 집을 동서로 길게 두고, 마당은 거실과 주방 사이 집 안쪽에 넣었습니다. 거실은 남북 9.6m를 보 하나로 건너 기둥이 없습니다.

      +

      창은 남쪽 긴 면 전체와 거실 동쪽 마당 쪽에 냈습니다. 아침 해는 마당 쪽 창으로, 낮 햇빛은 남쪽 창으로 들어옵니다. 벽 두께는 단열 기준에 맞춰 정했습니다.

      +
      +
      +
      구조
      RC 라멘, 9.6m 스팬
      +
      면적
      132.4 m² (40.0평)
      +
      창개구
      남측 14.4m, 동측 4.2m
      +
      공사 기간
      2025-04 – 2026-01
      +
      -
      -
      + -
      -

      사진 시퀀스

      빛의 방향, 재료의 밀도, 비례의 검토를 순서대로 기록합니다.

      +
      +

      재료와 빛은 사진으로 봅니다. 백령도 해녀기념관 전시실과 석고 스터디 모형, 두 장을 실었습니다.

      -
      긴 수평창이 낸 콘크리트 파빌리온에 벽 그림자가 지는 모습
      폼 하우스 · 남측 · 준공 후 첫 여름생성 이미지 · 실제 프로젝트 사진 아님
      -
      천창으로 한 줄기 빛이 사선으로 들어오는 미장 마감 전시실
      백령도 해녀기념관 · 제1전시실생성 이미지 · 실제 프로젝트 사진 아님
      -

      비례의 검토

      빛과 재료의 표정은 사진으로, 공간의 관계는 도면으로 살펴봅니다.

      -
      직육면체를 쌓은 백색 석고 모형에 측광이 지는 모습
      스터디 모델 07 · 석고생성 이미지 · 실제 프로젝트 사진 아님
      +
      +
      사진 02백령도 해녀기념관 제1전시실
      + 천창으로 한 줄기 빛이 사선으로 들어오는 미장 마감 전시실 +
      +
      +
      사진 03스터디 모델 07, 석고
      + 직육면체를 쌓은 백색 석고 모형에 측광이 지는 모습 +
      -
      -
      + -

      구성 안내

      가상 공간건축 포트폴리오
      실제 연락처를 제공하지 않습니다.

      이 페이지는 도면·사진·사양을 한 흐름으로 읽는 포트폴리오 형식의 예시입니다. 신규 프로젝트 상담이나 견적을 접수하지 않습니다.

      - -

      스튜디오 폼 · 가상 공간건축 포트폴리오

      설계 기록 형식 예시

      +
      +

      가상 연구소라 상담과 견적은 받지 않습니다.

      +
      +
      +

      스튜디오 폼은 이 쇼케이스를 위해 만든 가상 건축연구소입니다. 프로젝트 기록은 완공 사진, 1층 평면도, 설계 이유를 적은 문단 세 가지로 구성하고, 사진으로는 재료와 빛을, 평면도로는 방 배치와 동선을 보여 줍니다.

      +
      +
      스튜디오 폼
      연락처와 사무실 주소가 없습니다.
      +
      +
      + + +
      + +

      스튜디오 폼은 가상 브랜드 쇼케이스입니다. 실제 사무소가 아닙니다.

      +
      + + + diff --git a/apps/site/public/work/form-architecture/styles/fonts.css b/apps/site/public/work/form-architecture/styles/fonts.css deleted file mode 100644 index f5234d6..0000000 --- a/apps/site/public/work/form-architecture/styles/fonts.css +++ /dev/null @@ -1,15 +0,0 @@ -@font-face { - font-family: "MaruBuri"; - src: url("../assets/fonts/MaruBuri-Regular.woff2") format("woff2"); - font-style: normal; - font-weight: 400; - font-display: swap; -} - -@font-face { - font-family: "MaruBuri"; - src: url("../assets/fonts/MaruBuri-SemiBold.woff2") format("woff2"); - font-style: normal; - font-weight: 600; - font-display: swap; -} diff --git a/apps/site/public/work/form-architecture/styles/main.css b/apps/site/public/work/form-architecture/styles/main.css index 49f49c4..bcb84b3 100644 --- a/apps/site/public/work/form-architecture/styles/main.css +++ b/apps/site/public/work/form-architecture/styles/main.css @@ -1,91 +1,147 @@ -@import url("./tokens.css?v=20260912all"); +/* 스튜디오 폼 — 레이아웃·재질·모션. 값은 tokens.css 토큰만 쓴다. */ *, *::before, *::after { box-sizing: border-box; } * { margin: 0; } html { background: var(--surface); scroll-behavior: smooth; -webkit-text-size-adjust: 100%; } -body { min-width: 0; background: var(--surface); color: var(--ink); font-family: var(--font-body); font-size: var(--step-0); line-height: var(--leading-normal); -webkit-font-smoothing: antialiased; } +body { min-width: 0; background: var(--surface); color: var(--ink); font-family: var(--font-body); font-weight: var(--weight-body); font-size: var(--step-0); line-height: var(--leading-body); -webkit-font-smoothing: antialiased; } img { display: block; max-width: 100%; height: auto; } -h1, h2 { font-weight: 600; line-height: var(--leading-tight); letter-spacing: -.025em; word-break: keep-all; overflow-wrap: break-word; text-wrap: balance; } -p, dd, dt, address, figcaption { word-break: keep-all; overflow-wrap: break-word; text-wrap: pretty; } a { color: inherit; } -a:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; } -.rail, .wide-rail, .stage { width: min(100% - 2 * var(--pad), var(--rail)); margin-inline: auto; } -.wide-rail, .stage { width: min(100% - 2 * var(--pad), var(--stage)); } +a:focus-visible, summary:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; } + +/* 한글 조판(typography §6-5) */ +:lang(ko) { word-break: keep-all; overflow-wrap: anywhere; line-break: strict; } +:lang(ko) :is(p, li, dd, dt, address, figcaption) { letter-spacing: 0; text-wrap: pretty; } +:lang(ko) :is(h1, h2) { text-wrap: pretty; } +:root { font-synthesis-style: none; } +:lang(ko) :is(em, i, cite, dfn, var, address) { font-style: normal; } +:lang(ko) a { text-underline-position: under; } + .skip { position: absolute; left: -999px; top: 0; z-index: 100; background: var(--ink); color: var(--surface); padding: var(--space-3); } .skip:focus { left: 0; } -.site-head { position: sticky; top: 0; z-index: 20; min-height: var(--header-h); background: color-mix(in srgb, var(--surface) 96%, transparent); border-bottom: 1px solid var(--line); backdrop-filter: blur(8px); } -.head-inner { min-height: var(--header-h); display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); } -.wordmark { font-weight: 600; text-decoration: none; letter-spacing: -.02em; } -.wordmark span { margin-left: var(--space-1); color: var(--ink-faint); font-weight: 400; } -.nav { display: flex; gap: clamp(1rem, 2vw, 2.5rem); font-size: var(--step--1); } -.nav a { display: inline-flex; align-items: center; min-height: 2.75rem; text-decoration: none; color: var(--ink-muted); border-bottom: 1px solid transparent; transition: color var(--dur-quick) var(--ease), border-color var(--dur-quick) var(--ease); } -.nav a:hover { color: var(--ink); border-color: var(--ink); } +/* 재질: 종이 결. 타일 한 장을 고정 레이어로 깐다. 필터 연산은 로드 때 한 번뿐이다 */ +body::before { content: ""; position: fixed; inset: 0; z-index: 50; pointer-events: none; background-image: var(--grain); opacity: var(--grain-opacity); } -.hero { padding: var(--space-6) 0 var(--space-5); border-bottom: 1px solid var(--line); } -.hero-top { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(18rem, .7fr); gap: var(--space-6); align-items: end; margin-bottom: var(--space-5); } -.eyebrow { font-size: var(--step--1); line-height: 1.5; color: var(--ink-muted); letter-spacing: .08em; } -.hero-title .eyebrow { margin-bottom: var(--space-3); } -h1 { font-size: var(--step-display); max-width: 10em; } -.hero-intro { max-width: 28rem; padding-bottom: var(--space-1); } -.hero-intro p { color: var(--ink-muted); } -.index-link { display: inline-flex; align-items: center; gap: var(--space-2); min-height: 2.75rem; margin-top: var(--space-3); font-weight: 500; text-underline-offset: .35em; text-decoration-thickness: 1px; } -.index-link span { font-size: 1.25em; } -.hero-image img { width: 100%; aspect-ratio: 2.35 / 1; object-fit: cover; object-position: center 58%; filter: grayscale(1); } -figcaption { display: flex; justify-content: space-between; gap: var(--space-3); margin-top: var(--space-2); color: var(--ink-faint); font-size: var(--step--1); line-height: 1.55; } -.hero-caption { margin-top: var(--space-3); color: var(--ink-muted); font-size: var(--step--1); } -.hero-caption p { max-width: var(--measure); } +/* ── 틀: 왼쪽 색인 레일 + 오른쪽 시트 ── */ +.frame { display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr); column-gap: var(--space-6); padding-inline-start: var(--pad); } +.rail { position: sticky; top: 0; align-self: start; min-height: 100dvh; max-height: 100dvh; overflow-y: auto; padding: var(--space-4) var(--space-4) var(--space-5) 0; } +.sheet { min-width: 0; padding-inline-end: var(--pad); } -.section { padding-block: var(--space-7); } -.section-head { display: grid; grid-template-columns: minmax(14rem, .7fr) minmax(0, 1fr); gap: var(--space-5); align-items: baseline; margin-bottom: var(--space-5); border-top: 1px solid var(--line-strong); padding-top: var(--space-3); } -.section-head h2 { font-size: var(--step-1); letter-spacing: .03em; } -.section-head p { max-width: 40rem; color: var(--ink-muted); font-size: var(--step--1); } -.index { list-style: none; padding: 0; border-top: 1px solid var(--line-strong); } +.wordmark { display: inline-flex; align-items: center; min-height: 2.75rem; letter-spacing: -0.01em; text-decoration: none; white-space: nowrap; } + +.toc-summary { display: none; } +.toc-label { margin-top: var(--space-5); font-size: var(--step--1); line-height: 1.5; color: var(--ink-faint); } +.toc-note { margin-top: var(--space-2); font-size: var(--step--1); line-height: 1.5; color: var(--ink-faint); } +.index, .parts { list-style: none; padding: 0; margin-top: var(--space-2); border-top: 1px solid var(--line-strong); } .index li { border-bottom: 1px solid var(--line); } -.index a { display: grid; grid-template-columns: 3.5rem minmax(0, 1fr) minmax(8rem, .35fr) 4rem; gap: var(--space-3); align-items: baseline; padding: 1.15rem var(--space-2); text-decoration: none; transition: background var(--dur-quick) var(--ease), color var(--dur-quick) var(--ease); } -.index a:hover { background: var(--ink); color: var(--surface); } -.idx-no, .idx-loc, .idx-year { font-size: var(--step--1); color: var(--ink-faint); } -.idx-name { font-size: var(--step-1); font-weight: 500; } -.idx-year { font-variant-numeric: tabular-nums; text-align: right; } -.index a:hover .idx-no, .index a:hover .idx-loc, .index a:hover .idx-year { color: inherit; } +.index a { display: grid; grid-template-columns: 1.75rem minmax(0, 1fr); column-gap: var(--space-2); min-height: 2.75rem; padding: 0.55rem 0 0.55rem 0.75rem; font-size: var(--step--1); line-height: 1.45; text-decoration: none; color: var(--ink-muted); transition: color var(--dur-quick) var(--ease); } +.index a:hover { color: var(--ink); } +.idx-no { color: var(--ink-faint); font-variant-numeric: tabular-nums; } +.idx-name { color: var(--ink); } +.idx-meta { grid-column: 2; display: flex; gap: var(--space-2); } +.idx-meta span:last-child { font-variant-numeric: tabular-nums; } +.index a[aria-current="true"] { border-inline-start: 2px solid var(--ink); padding-inline-start: 0.625rem; } +.index a[aria-current="true"] .idx-no { color: var(--ink); } +.parts a { display: block; min-height: 2.25rem; padding: 0.4rem 0 0.4rem 0.75rem; font-size: var(--step--1); line-height: 1.5; text-decoration: none; color: var(--ink-muted); transition: color var(--dur-quick) var(--ease); } +.parts a:hover { color: var(--ink); } +.parts a[aria-current="true"] { color: var(--ink); border-inline-start: 2px solid var(--ink); padding-inline-start: 0.625rem; } -.drawing { border-top: 1px solid var(--line); background: var(--surface-raised); } -.drawing-grid { display: grid; grid-template-columns: minmax(17rem, .82fr) minmax(20rem, 1fr); gap: clamp(2.5rem, 6vw, 8rem); align-items: start; } -.drawing-notes { max-width: var(--measure); } -.drawing-notes .eyebrow { margin-bottom: var(--space-3); } -.drawing-notes h2 { font-size: var(--step-2); margin-bottom: var(--space-4); } -.drawing-notes p + p { margin-top: var(--space-3); } -.drawing-spec { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); margin-top: var(--space-5); padding-top: var(--space-4); border-top: 1px solid var(--line); } -.drawing-spec dt { font-size: var(--step--1); color: var(--ink-faint); } -.drawing-spec dd { margin-top: var(--space-1); font-size: var(--step--1); font-weight: 500; font-variant-numeric: tabular-nums; } -.drawing-stage { position: sticky; top: calc(var(--header-h) + var(--space-3)); } -.drawing-stage svg { display: block; width: 100%; height: auto; border: 1px solid var(--line); background: var(--surface); } -.wall { fill: none; stroke: var(--ink); stroke-width: 2; } +/* 진행선: 레일과 시트 사이 세로 실선. 먹 구간이 스크롤에 따라 아래로 자란다 */ +.progress { position: absolute; inset-block: 0; right: 0; width: 1px; background: var(--line); } +.progress-fill { position: absolute; inset: 0; background: var(--ink); transform-origin: top; scale: 1 1; } + +/* ── 히어로: 긴 문장 표제 + 전폭 무대 ── */ +.hero { padding-top: var(--space-4); } +h1 { font-weight: var(--weight-light); font-size: var(--step-2); line-height: var(--leading-title); letter-spacing: 0; max-width: var(--measure-title); min-height: 2.75rem; padding-top: 0.45rem; } +.stage { margin-top: var(--space-6); margin-inline-end: calc(-1 * var(--pad)); } +.stage img { width: 100%; aspect-ratio: var(--stage-ratio); object-fit: cover; object-position: center 56%; filter: grayscale(1) contrast(1.04); } +figcaption { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); font-size: var(--step--1); line-height: 1.5; color: var(--ink-muted); } +.stage-caption { margin-top: var(--space-2); padding-inline-end: var(--pad); } +.cap-no { color: var(--ink-faint); font-variant-numeric: tabular-nums; } +.notice { margin-top: var(--space-4); max-width: var(--measure); font-size: var(--step--1); line-height: 1.6; color: var(--ink-muted); } + +/* ── 본문 섹션 ── */ +.section { padding-top: var(--space-7); max-width: 80rem; } +h2 { font-weight: var(--weight-light); font-size: var(--step-2); line-height: var(--leading-title); letter-spacing: 0; max-width: var(--measure); border-top: 1px solid var(--line-strong); padding-top: var(--space-3); } +.prose { max-width: var(--measure); } +.prose p + p { margin-top: var(--space-3); } + +.plan { margin-top: var(--space-5); max-width: var(--sheet-max); view-timeline: --plan block; } +.plan-svg { display: block; width: 100%; height: auto; background: var(--sheet); border: 1px solid var(--line); } +.plan-caption { margin-top: var(--space-2); } +.wall { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linejoin: miter; } .wall.inner { stroke-width: 1.25; } -.door, .dim path, .north path { fill: none; stroke: var(--ink-muted); stroke-width: 1; } -.column { fill: var(--ink); } -.dim text, .north text { fill: var(--ink-faint); font-size: 12px; } -.room-label text { fill: var(--ink-muted); font-size: 13px; } -.drawing-caption { display: block; } +.window, .door { fill: none; stroke: var(--ink-muted); stroke-width: 1; } +.hatch { fill: none; stroke: var(--line); stroke-width: 1; } +.dim path, .north path { fill: none; stroke: var(--ink-muted); stroke-width: 1; } +.dim text, .north text { fill: var(--ink-faint); font-family: inherit; font-size: 13px; } +.room-label text { fill: var(--ink); font-family: inherit; font-size: 14px; paint-order: stroke; stroke: var(--sheet); stroke-width: 6px; stroke-linejoin: round; } +.drawing-notes { display: grid; grid-template-columns: minmax(0, 1fr) minmax(14rem, 0.55fr); gap: var(--space-4) var(--space-6); margin-top: var(--space-5); max-width: var(--sheet-max); align-items: start; } +.spec { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3) var(--space-4); border-top: 1px solid var(--line-strong); padding-top: var(--space-3); font-size: var(--step--1); line-height: 1.5; } +.spec dt { color: var(--ink-faint); } +.spec dd { margin-top: var(--space-1); font-variant-numeric: tabular-nums; } -.photo { border-top: 1px solid var(--line); } -.photo-grid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(15rem, .55fr); gap: var(--space-5); } -.ph-wide { grid-column: 1 / -1; } -.ph-wide img { width: 100%; aspect-ratio: 2.35 / 1; object-fit: cover; filter: grayscale(1); } -.ph-narrow { align-self: start; } -.ph-narrow img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; filter: grayscale(1); } -.ph-model { align-self: end; } -.ph-model img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; filter: grayscale(1); } -.ph-note { align-self: end; border-top: 1px solid var(--line-strong); padding-top: var(--space-3); max-width: 30rem; } -.ph-note p:last-child { margin-top: var(--space-3); font-size: var(--step-1); line-height: 1.55; } +.photo-grid { display: grid; grid-template-columns: minmax(14rem, 0.5fr) minmax(0, 1fr); gap: var(--space-5); margin-top: var(--space-5); align-items: end; } +.ph figcaption { margin-bottom: var(--space-2); } +.ph img { width: 100%; filter: grayscale(1); } +.ph-tall img { aspect-ratio: 3 / 4; object-fit: cover; } +.ph-panel img { aspect-ratio: var(--panel-ratio); object-fit: cover; object-position: center 58%; } -.contact { border-top: 1px solid var(--line); } -.contact-grid { display: grid; grid-template-columns: .7fr 1fr 1.2fr; gap: var(--space-5); align-items: start; } -.contact h2 { font-size: var(--step-1); letter-spacing: .03em; } -.contact address, .contact p { color: var(--ink-muted); font-style: normal; } -.site-foot { border-top: 1px solid var(--line-strong); padding-block: var(--space-4); font-size: var(--step--1); color: var(--ink-muted); } -.foot-inner { display: flex; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; } +.studio-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(12rem, 0.45fr); gap: var(--space-4) var(--space-6); margin-top: var(--space-5); max-width: var(--sheet-max); align-items: start; } +.studio-address { font-style: normal; color: var(--ink-muted); border-top: 1px solid var(--line-strong); padding-top: var(--space-3); font-size: var(--step--1); line-height: 1.6; } -@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .nav a, .index a { transition: none; } } -@media (max-width: 48rem) { .hero-top, .drawing-grid { grid-template-columns: 1fr; } .hero-top { gap: var(--space-4); } .hero-intro { max-width: var(--measure); } .drawing-stage { position: static; grid-row: 1; } .drawing-notes { grid-row: 2; } .photo-grid { grid-template-columns: minmax(0, 1fr) minmax(11rem, .72fr); } .contact-grid { grid-template-columns: 1fr; gap: var(--space-3); } } -@media (max-width: 35rem) { .head-inner { padding-block: var(--space-2); align-items: flex-start; flex-direction: column; gap: 0; } .site-head { min-height: auto; } .nav { width: 100%; justify-content: space-between; gap: var(--space-2); } .nav a { min-height: 2.25rem; } .hero { padding-top: var(--space-5); } .hero-image img, .ph-wide img { aspect-ratio: 1.45 / 1; } .section-head { grid-template-columns: 1fr; gap: var(--space-2); } .index a { grid-template-columns: 2.25rem minmax(0, 1fr) auto; gap: var(--space-2); padding-inline: 0; } .idx-loc { grid-column: 2; } .idx-year { grid-column: 3; grid-row: 1; text-align: right; } .photo-grid { grid-template-columns: 1fr; } .ph-note { grid-row: 2; } .ph-narrow { grid-row: 3; max-width: 70%; } .ph-model { grid-row: 4; } .contact { padding-block: var(--space-6); } figcaption { flex-direction: column; gap: 0; } .drawing-spec { grid-template-columns: 1fr; } } +/* ── 푸터: 큰 워드마크 한 줄 ── */ +.foot { grid-column: 1 / -1; margin-top: var(--space-7); padding: var(--space-5) var(--pad) var(--space-4) 0; border-top: 1px solid var(--line-strong); } +.foot-mark { font-weight: var(--weight-light); font-size: var(--step-display); line-height: 1.1; letter-spacing: -0.02em; white-space: nowrap; } +.foot-line { margin-top: var(--space-3); max-width: var(--measure); font-size: var(--step--1); line-height: 1.6; color: var(--ink-muted); } + +/* ── 모션: 스크롤 축 ── */ +@supports (animation-timeline: view()) { + .plan-draw path { stroke-dasharray: 1; stroke-dashoffset: 1; animation: draw 1ms linear both; animation-timeline: --plan; animation-range: entry calc(10% + var(--i, 0) * 7%) cover calc(42% + var(--i, 0) * 7%); } + .plan-draw path:nth-child(2) { --i: 1; } + .plan-draw path:nth-child(3) { --i: 2; } + .plan-draw path:nth-child(4) { --i: 3; } + .plan-draw path:nth-child(5) { --i: 3; } + .plan-draw path:nth-child(6) { --i: 4; } + .plan-draw path:nth-child(7) { --i: 4; } + @keyframes draw { to { stroke-dashoffset: 0; } } +} +@supports (animation-timeline: scroll()) { + .progress-fill { scale: 1 0; animation: fill 1ms linear both; animation-timeline: scroll(root block); } + @keyframes fill { to { scale: 1 1; } } +} +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + .index a, .parts a { transition: none; } + /* 감소 모션은 완성 상태: 도면은 다 그어져 있고 진행선은 정보이므로 남긴다 */ + .plan-draw path { animation: none; stroke-dashoffset: 0; } + .progress-fill { animation: none; scale: 1 1; } +} + +/* ── 좁은 화면: 레일이 위쪽 접는 차례가 된다 ── */ +@media (max-width: 64rem) { + .frame { display: block; padding-inline: var(--pad); } + .rail { position: sticky; top: 0; z-index: 20; display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); min-height: 0; max-height: none; overflow: visible; margin-inline: calc(-1 * var(--pad)); padding: var(--space-1) var(--pad); background: var(--surface); border-bottom: 1px solid var(--line); } + .progress { display: none; } + .toc-summary { display: inline-flex; align-items: center; min-height: 2.75rem; padding-inline: var(--space-2); cursor: pointer; list-style: none; font-size: var(--step--1); } + .toc-summary::-webkit-details-marker { display: none; } + .toc-summary span { border-bottom: 1px solid var(--ink); } + .toc-close-label, .toc[open] .toc-open-label { display: none; } + .toc[open] .toc-close-label { display: inline; } + .toc:not([open]) .toc-body { display: none; } + .toc-body { position: absolute; left: 0; right: 0; top: 100%; max-height: calc(100dvh - 3.5rem); overflow-y: auto; padding: var(--space-2) var(--pad) var(--space-4); background: var(--surface); border-bottom: 1px solid var(--line-strong); } + .toc-label { margin-top: var(--space-3); } + .sheet { padding-inline-end: 0; } + .hero { padding-top: var(--space-5); } + .stage { margin-inline: calc(-1 * var(--pad)); } + .stage-caption { padding-inline: var(--pad); } + .drawing-notes, .photo-grid, .studio-grid { grid-template-columns: 1fr; } + .photo-grid { align-items: start; } + .ph-tall { max-width: 24rem; } + .foot { padding-inline: 0; } +} + +@media (max-width: 40rem) { + .spec { grid-template-columns: 1fr; } + .ph-tall { max-width: 100%; } +} diff --git a/apps/site/public/work/form-architecture/styles/tokens.css b/apps/site/public/work/form-architecture/styles/tokens.css index 1de8ebf..78ee516 100644 --- a/apps/site/public/work/form-architecture/styles/tokens.css +++ b/apps/site/public/work/form-architecture/styles/tokens.css @@ -1,34 +1,65 @@ +/* 스튜디오 폼 — 토큰. 색·크기·간격은 여기서만 정한다(05 Q-25). */ :root { - --font-body: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, sans-serif; - --surface: #fdfdfb; - --surface-raised: #ffffff; - --ink: #171b19; - --ink-muted: #5b615d; - --ink-faint: #686e6a; - --line: #d8dbd7; - --line-strong: #a7aca7; - --step--1: 0.75rem; + /* 서체: Gothic A1 한 패밀리, 두 웨이트. 표제·워드마크는 300(가늘고 넓게), 본문·색인은 400 */ + --font-body: "Gothic A1", "Apple SD Gothic Neo", "Malgun Gothic", sans-serif; + --weight-light: 300; + --weight-body: 400; + + /* 색: 밝은 중립 종이 위에 먹만 쓴다. 강조색 없음(배정). */ + --surface: #fcfcfb; + --sheet: #ffffff; + --ink: #161716; + --ink-muted: #585b58; + --ink-faint: #6b6e6b; + --line: #d9dad7; + --line-strong: #9a9c99; + --ink-rgb: 22 23 22; + + /* 타입 스케일. 한글 캡션 하한 13px, 본문 16px(typography §6-5) */ + --step--1: 0.8125rem; --step-0: 1rem; - --step-1: 1.25rem; - --step-2: clamp(1.75rem, 1.3rem + 1.7vw, 2.5rem); - --step-display: clamp(2.5rem, 5.5vw, 5.5rem); - --leading-tight: 1.12; - --leading-normal: 1.75; - --measure: 45rem; - --rail: 78rem; - --stage: 112.5rem; - --pad: clamp(1.25rem, 1rem + 2vw, 3rem); + --step-1: 1.125rem; + --step-2: clamp(1.25rem, 1.05rem + 0.7vw, 1.625rem); + --step-display: clamp(3.25rem, 1rem + 11vw, 11rem); + --leading-body: 1.7; + --leading-title: 1.55; + --leading-tight: 1.25; + + /* 폭: 긴 문장 표제·본문 measure는 em(한글 32~40자) */ + --measure: 36em; + --measure-title: 30em; + --rail-w: 15rem; + --sheet-max: 56rem; + --pad: clamp(1rem, 0.6rem + 1.6vw, 2.5rem); + --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 1rem; --space-4: 1.5rem; --space-5: 2.5rem; - --space-6: clamp(3.5rem, 2.2rem + 4vw, 6rem); - --space-7: clamp(5rem, 3rem + 6vw, 9rem); - --header-h: 4rem; + --space-6: clamp(3rem, 2rem + 3vw, 5rem); + --space-7: clamp(5rem, 3rem + 7vw, 10rem); + + /* 모서리: 도면·종이. 0 고정(배정) */ + --radius: 0; + + /* 무대 비율: 넓은 화면은 극단 파노라마, 세로 화면은 띠가 되지 않게 다시 정한다 */ + --stage-ratio: 3.6 / 1; + --panel-ratio: 2.4 / 1; + + /* 모션: 상태 전환만 시간 기반. 진행선·도면 선은 스크롤 축 */ --dur-quick: 140ms; - --ease: cubic-bezier(.25, 1, .35, 1); + --ease: cubic-bezier(0.25, 1, 0.35, 1); + + /* 재질: 종이 결. feTurbulence 한 장을 타일로 굽는다(런타임 필터 없음). 아주 약하게 */ + --grain-opacity: 0.08; + --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.09 0 0 0 0 0.09 0 0 0 0 0.09 0 0 0 0.55 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E"); } -@media (min-width: 120rem) { :root { --rail: 80rem; --stage: 112.5rem; } } -@media (max-width: 20rem) { :root { --pad: 1rem; } } +@media (max-width: 64rem) { + :root { --stage-ratio: 2.4 / 1; --panel-ratio: 2 / 1; } +} + +@media (max-width: 40rem) { + :root { --stage-ratio: 16 / 10; --panel-ratio: 16 / 10; } +} diff --git a/apps/site/public/work/gaon-lms/design.md b/apps/site/public/work/gaon-lms/design.md index 071f163..a0d7315 100644 --- a/apps/site/public/work/gaon-lms/design.md +++ b/apps/site/public/work/gaon-lms/design.md @@ -10,11 +10,9 @@ | 축 | 질문 | 적용 | | --- | --- | --- | -| Warde · 정보 전달 | 다음 확인 대상이 수치보다 먼저 읽히는가 | 공통 정렬선과 가는 구분선의 요약 기록 밴드, 15px 표 본문 | -| Ruder · 비교 변수 | 같은 종류의 값이 같은 열과 숫자 폭에서 비교되는가 | 표 숫자에 tabular figures, 수치 열 오른쪽 정렬 | -| Albers · 상태 맥락 | 상태색이 주변의 중립 기록면에서 구분되는가 | 상태는 텍스트와 함께 유지하고 배경색은 보조로 사용 | - -Warde, Ruder, Albers는 정보 전달·비교 변수·상태 맥락을 검토하는 참고 질문이며 인과나 성능 법칙이 아니다. +| 다음 할 일 | "이번 주 현황 · 재학생 10명"처럼 무엇을 보는 화면인지와 대상 인원이 먼저 읽히는가 | 공통 정렬선과 가는 구분선의 요약 기록 밴드, 15px 표 본문 | +| 비교 변수 | 요일별 출석(10·9·10·9·8)·지각·미채점 같은 값이 같은 열과 숫자 폭에서 비교되는가 | 표 숫자에 tabular figures, 수치 열 오른쪽 정렬 | +| 상태 | 진행 중·예정·미제출 상태가 중립 기록면에서 구분되는가 | 상태는 텍스트와 함께 유지하고 배경색은 보조로 사용 | ## 역할 토큰과 검증 @@ -22,3 +20,11 @@ Warde, Ruder, Albers는 정보 전달·비교 변수·상태 맥락을 검토하 - `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이다. - 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다. + +## 2026-10-02 레드팀 06 수정 기록 + +- `.mono` 유틸리티를 등폭 서체에서 본문 서체 + tabular-nums로 바꿨다. "수학 심화 B · 3/11 마감"이 "수 학 심 화 B"처럼 벌어져 보였다(레드팀 06 P1-3). 시각 회귀 기준선은 이 의도적 변경으로 갱신했다. + +## 2026-10-02 카피 교정 + +- 대시보드 표제 "이번 주, 열두 명 중 열 명"(명사로 끝나는 수수께끼 표제)을 "이번 주 현황 · 재학생 10명"으로, "출석부에서 출발한"(무생물 주어) 등 설명 문장을 학원 관리 화면 어휘로 바꿨다. diff --git a/apps/site/public/work/gaon-lms/index.html b/apps/site/public/work/gaon-lms/index.html index 2009c8b..20f57eb 100644 --- a/apps/site/public/work/gaon-lms/index.html +++ b/apps/site/public/work/gaon-lms/index.html @@ -20,7 +20,7 @@ content="학원 강사를 위한 학습 관리 화면입니다. 출석, 수강생 기록, 과제 채점, 학사 일정을 한곳에서 살펴볼 수 있습니다." /> - + @@ -139,7 +139,7 @@
      -

      이번 주, 열두 명 중 열 명

      +

      이번 주 현황 · 재학생 10명

      3월 9일 월요일 오전 기준. 명단 12명 중 재학 10명, 주간 출석은 지난 주(3/2–3/6) 결산입니다.

      표시된 수강생·수업·상담 기록은 예시입니다. 이 화면에서 바꾼 내용은 새로고침하면 초기화됩니다.

      @@ -378,7 +378,7 @@

      출석부

      -

      칸을 누르면 출석 · 지각 · 결석이 순환합니다. 반과 주를 바꿔가며 적습니다.

      +

      칸을 누를 때마다 출석 → 지각 → 결석 순으로 바뀝니다. 반과 주차를 바꿔 가며 기록하세요.

      @@ -530,7 +530,7 @@

      자료실

      -

      배부 자료의 목록. 배부 여부와 일시를 바로 확인할 수 있습니다.

      +

      수업 자료 배부 목록입니다. 배부 여부와 일시를 바로 확인할 수 있습니다.

      @@ -562,7 +562,7 @@

      학사 일정 · 2026년 3월

      -

      마감과 휴원을 먼저 보는 달력. 오른쪽은 3월 9일 이후의 일정입니다.

      +

      마감일과 휴원일을 표시한 달력입니다. 3월 9일 이후 일정은 ‘다가오는 일정’에 따로 모았습니다.

      diff --git a/apps/site/public/work/gaon-lms/og.png b/apps/site/public/work/gaon-lms/og.png index e260f0d..80a050c 100644 Binary files a/apps/site/public/work/gaon-lms/og.png and b/apps/site/public/work/gaon-lms/og.png differ diff --git a/apps/site/public/work/gaon-lms/styles/main.css b/apps/site/public/work/gaon-lms/styles/main.css index 6e5bf33..c7bf92f 100644 --- a/apps/site/public/work/gaon-lms/styles/main.css +++ b/apps/site/public/work/gaon-lms/styles/main.css @@ -44,7 +44,9 @@ h3 { font-size: var(--step-0); font-weight: 500; } :where(p, li, td, th, dd, figcaption) { word-break: keep-all; overflow-wrap: anywhere; } -.mono { font-family: var(--font-mono); font-size: var(--font-mono-size); font-variant-numeric: tabular-nums; } +/* .mono 는 "수학 심화 B · 3/11 마감"처럼 한글이 섞인 줄에도 쓰인다. 등폭 서체에 두면 한글 사이 + 공백이 등폭 셀 너비로 벌어졌다(레드팀 06 P1-3). 숫자 정렬은 본문 서체의 tabular-nums 로 지킨다 */ +.mono { font-family: var(--font-body); font-size: var(--font-mono-size); font-variant-numeric: tabular-nums; } .muted { color: var(--muted-foreground); } .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; } diff --git a/apps/site/public/work/gaon-lms/styles/tokens.css b/apps/site/public/work/gaon-lms/styles/tokens.css index 32cb91b..f07461c 100644 --- a/apps/site/public/work/gaon-lms/styles/tokens.css +++ b/apps/site/public/work/gaon-lms/styles/tokens.css @@ -23,7 +23,9 @@ --font-body: "Pretendard Variable", Pretendard, -apple-system, sans-serif; --font-display: "Noto Serif KR", "Pretendard Variable", serif; - --font-mono: "JetBrains Mono", ui-monospace, monospace; + /* 한글 글리프가 없는 모노 다음에 본문 한글 서체를 둔다. 그래야 "시험 구간"처럼 한글이 섞인 라벨이 + 시스템 등폭 한글로 떨어져 글자 사이가 벌어지지 않는다(레드팀 06 P1-3). 숫자·라틴만 모노로 남는다 */ + --font-mono: "JetBrains Mono", "Pretendard Variable", Pretendard, monospace; --font-mono-size: 0.8125rem; /* 색 — shadcn zinc 스키마 */ diff --git a/apps/site/public/work/griddle-club/design.md b/apps/site/public/work/griddle-club/design.md index a668864..25a8fcd 100644 --- a/apps/site/public/work/griddle-club/design.md +++ b/apps/site/public/work/griddle-club/design.md @@ -1,31 +1,145 @@ -# 철판클럽 — 2026-09-12 화면 보정 기록 +# design.md — 철판클럽 (griddle-club) -## 구도·타입·색 관찰 +## 진행 상태 -- 코발트, 큰 고딕 제목, 버거 사진은 브랜드의 속도와 조리 장면을 만드는 기존 자산이다. -- 기준 첫 화면의 파란 상품 카드는 절대 배치로 사진 프레임 밖에 걸쳐 캡션과 조합 시작 행동의 읽기 순서를 흐린다. -- 메뉴 이미지는 이미 4:3 표면이지만 크롭 기준이 명시되지 않았고, 조합·가맹의 조건과 비용은 값보다 장식 카드처럼 보일 위험이 있다. +- 0단계 현행 감사 2026-10-06 완료 — 분할 히어로·Pretendard·웜 페이퍼·고지 푸터·eyebrow 16개가 수렴 지문(05 계측)과 일치. 남길 자산: 상호·메뉴 6종과 가격·매장 3곳 상태·조합기 폼과 가격식·가맹 3단계 폼·생성 이미지 6장·진실 고지 범위. +- 1단계 레퍼런스 완료 — R1 파이브가이즈 코리아(실측), R2 Gumroad(실측), R3 Gumroad 태그 캐러셀(실측). 버거킹 코리아는 내비만 읽혀 미검증. +- 2단계 방향 완료(위임) — 카드 3장 중 A "쟁반 위의 콜라주" 선택. 이유는 "방향" 절. +- 3단계 토큰 완료 — `styles/tokens.css` 한 곳. 색·radius 리터럴은 site.css·franchise.css에 0건. +- 4단계 구현 완료 — 레이아웃 → (재질 없음) → 모션(스태거 등장·마퀴). 2차 페이지 `franchise/`는 공통 셸(상단 바·하단 행동 바·다열 푸터)만 맞췄다. +- 5단계 감사 완료 — html-validate 0건, stylelint 0건, no-hanja 통과, design-gate 28/28, showcase-audit 12/12(320~2560), 철판클럽 E2E 42 시나리오 166 단언 통과, audit.mjs 2560px 3단언 통과(스크래치 복제). 루브릭 18/20. +- 6단계 이 문서. -## 근거 확인 범위 +## 브리프 -- `outputs/full-showcase-upgrade-20260912/screenshots/griddle-club/1440x1000-first.png` -- 소비자 홈, `franchise/`, 기존 메뉴·조합·가상 매장·가맹 조건의 ID와 스크립트 -- 가상·생성 이미지·미전송·비용 미정·실제 주문 불가 표기는 보존한다. +가상 스매시 버거 브랜드 철판클럽의 소비자 홈과 가맹 안내 2차 페이지. 방문자는 메뉴와 가격을 보고 재료를 조합해 가격·알레르기 정보를 확인한 뒤 가상 매장의 영업 상태를 본다. 실제 주문·결제·가맹 상담은 없다. -## 결정 / 기각 조건 +- 목표 행동: 메뉴 보기 → 내 버거 조합하기(`#builder`) → 매장 상태 확인. 2차 페이지는 가맹 조건 정리 폼. +- 기본 언어 한국어, 합니다체. 한자 금지. +- 확정 슬롯(기존 파일에서): 메뉴 6종·가격, 세트 추가 4,100원, 매장 3곳(성수 영업 중·연남 영업 종료·문래 휴무), 알레르기 규칙, 가맹 비용 11항목 전부 미정. +- 배정(plan.md): 비대칭 콜라주 히어로 · 두 줄 문장 표제 · 상단 바 + 모바일 하단 행동 바 · 다열 색인 푸터 · Jua/Gothic A1 · 유채 크림 #fbe9c8 · 코발트 유지 · pill·16px · 스태거 등장 + 마퀴 · 재질 없음 · 누끼 겹침(원본이 사진이면 CSS 크롭). +- 화면 목록: `index.html`(홈), `franchise/index.html`(가맹). -- 상품·예시 가격·조합 시작 버튼은 사진 아래 정상 흐름의 코발트 shelf로 둬 캡션과 겹치지 않게 한다. -- 메뉴 이미지는 중앙 `cover` 크롭을 통일하고, 조합기는 라벨·선택·가격 보조 정보를 같은 위계로 둔다. -- 가맹 절차와 비용은 단계·상태·다음 확인의 원장으로 정렬한다. 수익, 기간, 비용, 운영 성과를 새로 만들거나 추정하는 변경은 기각한다. +## 미확정 -## 토큰·규칙 +- 사진은 전부 생성 이미지다(히어로 조각·메뉴·주방). 화면에 "생성 이미지, 실제 매장이 아닙니다" 라벨을 같은 시각 그룹 안에 둔다. +- 누끼 원본이 없다. 콜라주 조각은 원형·둥근 사각 `border-radius` 크롭 + 크림 테두리로 "오려 낸" 모양을 만들었다. 원 안에 주방 배경이 남는다(아래 "하지 않기로 한 것"). +- 코발트 hue: plan.md는 299°로 적었지만 기존 값 `#2147e7`은 sRGB HSL 228°다. "유지" 배정이므로 값을 바꾸지 않았다. +- 방향 선택은 위임(사용자가 재작업과 방향 선택을 맡김). -- 기존 `--blue`, `--paper-*`, `--ink`, `--line`, `--font-mono`만 사용한다. -- 상품 shelf와 제어 요소는 44px 이상의 동작 영역과 기존 focus 계약을 유지한다. -- 320px과 200% 확대에서 `min-width: 0`, 가로 스크롤 표, normal flow로 정보를 보존한다. +## 레퍼런스 -## 2026-09-12 현재 검증 기록 +| 슬롯 | URL | 관찰 | 가져온 것 | +|---|---|---|---| +| R1 구조 | https://www.fiveguys.co.kr/ (텍스트 페처, 2026-10-06) | 고정 헤더 + 오른쪽 "Order Now" 버튼, 히어로 바로 뒤 7항목 음식 격자, 매장 찾기 지도 섹션, 다열 푸터(고객센터·법적 고지·SNS) | 상단 바 오른쪽 행동 버튼, 히어로 → 메뉴 격자 순서, 다열 색인 푸터 | +| R2 톤 | https://gumroad.com/ (텍스트 페처) | 히어로에 크기가 다른 동전 일러스트 5개가 흩어진 비대칭 콜라주, 알약 버튼, 둥근 디스플레이 서체 | 조각 5개(사진 3 + 가격 알약 + 스티커)를 크기 차 극단으로 겹치는 구도, 알약 radius, Jua | +| R3 디테일 | https://gumroad.com/ 제품 발견 섹션의 반복 카테고리 태그 캐러셀 | 짧은 항목이 가로로 흐르는 띠 | 히어로 아래 메뉴·가격 마퀴(정지 버튼·가로 스크롤 폴백) | +| 미검증 | https://www.burgerking.co.kr/ | 페처가 내비만 읽음(MENU·STORY·NEWS·STORE·CUSTOMER). 히어로·푸터 미확인 | — | -- `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이다. -- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다. +https://www.shakeshack.com/ 은 403으로 열지 못했다(두 번 시도하지 않고 다른 출처로 감). + +## 방향 + +**지문**: 비대칭 콜라주(사진 조각 3 + 가격 알약 + 스티커, 원형·둥근 사각 크롭, 크기 차 4배) · Jua / Gothic A1 · 유채 크림 #fbe9c8(섹션 교대는 #fff7e8·#f4d9a3, 코발트 전면 띠 1, 먹 띠 1) · 코발트 #2147e7(228°) · pill 999px·16px · 히어로 → 마퀴 → 메뉴 격자 → 조합기(코발트 띠) → 공정 → 매장 → 가맹 띠 → 푸터 · 상단 바(브랜드+항목+행동, ≥821 sticky) + 모바일 하단 고정 행동 바 4항목 · 다열 색인 푸터 4열 · 원형·둥근 사각 크롭 + 크림 테두리, 캡션은 알약으로 사진 위 · 스태거 등장(조각 6개, 90ms 간격, 이동·확대만) + 마퀴(32s, 정지 버튼, 감소 모션은 정지 목록). + +**한 문장 컨셉**: 처음 온 손님이 쟁반 위 버거 사진 조각과 가격 알약을 보고, 메뉴 격자에서 조합하기를 눌러 내 버거의 가격과 알레르기를 확인한다. + +**프리셋**: quiet-commerce 지배(메뉴·가격·조합기가 설득) + anti-grid 보조(히어로 구도만). 축 소유권: 구도·모션 = anti-grid, 타입·색·간격·컴포넌트 = quiet-commerce. + +### 제시한 카드(위임) + +| | A. 쟁반 위의 콜라주 (선택) | B. 메뉴판이 먼저 | C. 스티커 창구 | +|---|---|---|---| +| 컨셉 | 손님이 버거 사진 조각과 가격을 보고 조합기로 간다 | 손님이 메뉴 카드 6장이 겹친 콜라주에서 바로 하나를 눌러 조합기로 간다 | 손님이 매장 영업 상태 스티커를 먼저 보고 메뉴로 간다 | +| 첫 화면 | 왼쪽 두 줄 표제·리드·버튼 둘 / 오른쪽 사진 조각 3 + 가격 알약 + 스티커 겹침 | 표제는 작게 위, 메뉴 카드(사진+가격) 6장이 서로 다른 크기로 겹침 | 가운데 표제, 둘레에 두꺼운 크림 테두리의 사진 스티커와 영업 상태 스티커가 기울어져 겹침 | +| 흐름 | 히어로 → 마퀴 → 메뉴 → 조합기 → 공정 → 매장 → 가맹 | 히어로(메뉴) → 조합기 → 매장 → 공정 → 가맹 | 히어로 → 매장 상태 → 메뉴 → 조합기 → 공정 → 가맹 | +| 타입 | Jua / Gothic A1, 표제가 본문의 5배 | Jua / Gothic A1, 표제가 본문의 2배(카드가 주인) | Jua / Gothic A1, 표제가 본문의 6배 | +| 색 | 크림 바탕 / 갈색 먹 / 코발트는 버튼·알약·조합기 띠 | 크림 / 먹 / 코발트는 카드 번호와 가격만 | 크림 / 먹 / 코발트 + 치즈색 스티커를 많이 | +| 모션 | 조각 스태거 등장 + 메뉴 마퀴 | 카드 스태거 등장 + 마퀴 | 스티커 스태거 등장 + 마퀴 | +| 히어로 첫 문장 | 주문 뒤 눌러 굽고, 바로 건넵니다. | 오늘 메뉴 여섯 가지, 단품 6,900원부터. | 성수점 영업 중, 연남점은 오늘 종료. | +| 포기하는 것 | 메뉴 전체를 첫 화면에서 비교하기 | 두 줄 표제의 무게(배정 "두 줄 문장"과 충돌) | 매장이 가상이라 상태가 늘 같다 — 첫 화면이 거짓 생동감이 된다 | + +**A를 고른 이유**: 첫째 목표 행동이 "메뉴 보기 → 조합하기"이고 R1(파이브가이즈)이 히어로 바로 뒤에 음식 격자를 둔다. 가진 사진 네 장이 전부 버거·조리 장면이라 조각으로 쓸 재료가 있고, 배정된 "두 줄 문장 표제"를 B는 지키지 못하며 C는 가상 매장 상태를 첫 화면의 주인으로 세워 진실 계약과 부딪친다. + +### 구도·타입·색 결정표 + +| 축 | 관찰 → 결정 | +|---|---| +| 구도 | `hero-handoff.webp`는 버거와 손이 오른쪽 2/3에 있다 → 원형 크롭의 `object-position: 68% 56%`로 버거를 원 중심에 두고 1440에서 조각 폭 552px(단 폭의 104%), 작은 원 138px(26%)로 4배 차를 만든다. 390에서는 조각을 뷰포트 안(84%·50%·30%)에 두고 글 아래로 내린다(감사 도구가 넘친 조각을 가로 오버플로로 셈, 실측) | +| 타입 | "주문 뒤 눌러 굽고," 7.8em이 390(354px)에서 두 줄 계약을 지키려면 h1 ≤45px → ≤620px에서 `clamp(34px, 10.5vw, 46px)`. 1440에서는 0.55fr 단(649px)에 `clamp(44px, 5vw, 84px)`=72px → 561px. 섹션 h2 "메뉴는 여섯 가지, 단품은 6,900원부터입니다."는 320·390·1440 전부 마지막 줄이 한 어절("6,900원부터입니다.")로 남아 "단품 6,900원부터, 메뉴는 여섯 가지."로 바꿨다(실측 스크립트) | +| 색 | 코발트 `#2147e7`는 사진 속 쟁반·클립 색과 같다 → 조합기 띠 전체를 코발트로 해 "주문대"로 읽히게 한다. 비활성 세트 상자에 `opacity: .55`를 쓰자 "세트 구성" 라벨이 3.08:1로 떨어져(게이트 실측) 투명도 대신 속 빈 테두리·`--on-blue-soft`(5.27:1)로 바꿨다 | + +### 반사실·수렴 점검 + +A. 고유 요소(1440·390 첫 화면): ① 코발트 쟁반 위 더블 버거 사진(`hero-handoff.webp`, 브랜드 색과 같은 쟁반) ② "철판 더블 8,900원" 가격 알약(브리프 수치) ③ "주문 뒤 조리" 스티커(스매시 버거의 조리 순서) ④ 메뉴·가격 마퀴(메뉴 6종 수치). 같은 업종 치환: 사진·가격이 바뀌면 알약·마퀴 내용이 바뀐다 — 구도는 남지만 헤드라인·시각물 둘이 깨지므로 통과. 다른 업종 치환(세무사무소): 가격 알약·메뉴 마퀴·조합기 띠가 성립하지 않는다 — 통과. 고유 요소 4개 ≥ 2. + +B-1 히어로 5축(직전 산출물 = 이 쇼케이스의 이전 판): 구도 다름(분할→콜라주) · 표제 서체 다름(Pretendard 900→Jua) · eyebrow 다름(있음→없음) · CTA 다름(단색 1 + 밑줄 링크 1 → 알약 단색 1 + 알약 외곽선 1) · 캡션 다름(사진 위 띠→알약). 같음 0. +B-2 페이지 6축: 섹션 흐름 다름(마퀴 추가, 가맹 띠가 사진 없는 띠) · 글 묶음 다름(라벨 제거) · 내비 다름(상단 바 + 하단 행동 바) · 푸터 같음(다열, 배정 "유지") · 이미지 다름(원형 크롭·알약 캡션) · 모션 다름(없음→스태거+마퀴). 같음 1. +토큰 4축(저장소 분배표 기준): Jua는 19종 중 유일, Gothic A1 본문은 4종(≤5), 크림 배경 유채 5종 중 하나, radius pill·16은 유일. + +## 토큰과 예산 + +값은 `styles/tokens.css`. 역할만 적는다. + +- 바탕 3단: `--bg` 크림(히어로·메뉴·푸터) / `--surface` 밝은 크림(마퀴·매장·카드) / `--bg-deep` 진한 크림(공정 띠·포스터 카드). 강한 띠는 `--blue`(조합기) 하나와 `--ink`(가맹 띠) 하나. +- 먹 3단 + 먹 위 3단, 코발트 위 2단. 대비 실측: 코발트/크림 5.64, 흰/코발트 6.73, `--on-blue-soft`/코발트 5.27, 먹/크림 14.32, `--ink-soft` 7.05, `--ink-faint` 5.73, `--danger` 5.20, `--success` 5.71, `--pause` 5.33, 크림/먹 14.32, `--on-ink-soft` 11.60, `--on-ink-faint` 8.74, 먹/치즈 10.92. +- 서체: Jua(표제, 400 한 굵기) / Gothic A1 500·700·800(본문). Google Fonts `display=swap`, 한글은 unicode-range 슬라이스 서브셋. Pretendard·IBM Plex Mono 링크를 제거해 폰트 요청을 두 패밀리로 줄였다. 숫자는 Gothic A1 `tabular-nums`. +- radius: `--radius` 16px, `--radius-pill` 999px. 원형 조각은 50%. +- 모션: `--dur` 520ms, `--stagger` 90ms, `--shift` 28px, `--marquee-dur` 32s. 감소 모션에서 전부 0. +- 예산(tokens §5 기본선): 히어로까지 JS 150KB gzip — 실측 `site.js` 11KB(원본), 외부 JS 0. LCP 2.5s — LCP 요소는 `hero-handoff.webp` 99KB(모바일 86KB) `fetchpriority="high"`, lazy 아님. INP — 입력 핸들러는 change·click뿐, 스크롤 로직 없음. `transform`·`opacity` 밖 애니메이션 없음(등장은 transform만, 마퀴는 transform). + +## 이펙트와 폴백 + +- 스태거 등장: `.hero-media .piece`에 `piece-in`(translateY·scale·rotate) 키프레임, `--i`로 지연. 투명도는 쓰지 않는다 — 페이드 중 axe가 알약 글자를 2.82:1로 쟀다(E2E 실측). 감소 모션: `animation: none`, 즉시 표시. JS 없이도 CSS만으로 돈다. +- 마퀴: `.marquee-track` translateX(-50%) 32s 루프, 목록을 두 번 넣고 둘째는 `aria-hidden`. 정지 수단: `#marquee-toggle`(aria-pressed), hover·focus-within에서 일시정지, 감소 모션에서는 애니메이션 제거·둘째 목록 숨김·목록을 줄바꿈해 정지 목록으로. 트랙은 `overflow-x: auto` 스크롤 영역(`role="region"` `tabindex="0"`) 안에 있어 기다리지 않고 넘길 수 있다. +- 재질 없음(배정). 그림자는 조각·카드에만. +- 이미지 깨짐: 기존 `.image-shell.is-broken` 폴백 유지. + +## 하지 않기로 한 것 + +- 누끼 생성·새 이미지 생성(브리프). 원형 크롭 안에 주방 배경이 남는 것을 감수한다. +- 스크롤 리빌(IntersectionObserver)·패럴랙스 — 배정표에 없다. +- 상단 바 backdrop-filter — 바탕이 단색 크림이라 불투명 면으로 충분하다. 모바일에서는 sticky도 풀었다(하단 바가 이동 수단). +- 슬로건 "눌러 굽고, 바로 건넨다."의 h1 사용 — 표제는 사실 문장으로, 은유 0개. +- eyebrow(`.section-kicker`) 16개 전부 제거. 카드 안의 상태 라벨(대표 메뉴·곁들임·음료)은 섹션 라벨이 아니라 항목 분류라 남겼다. +- 가운뎃점 메타 문자열 제거(고지 띠·캡션·옵션·푸터·JS 문자열·㎡ 환산). +- 조합기 요약의 사진(`signature-double.webp` 반복) 제거 — 영수증 카드로. + +## 검증 + +검증 줄: html-validate 2파일 0건 · stylelint 3파일 0건 · no-hanja 7파일 통과 · design-gate 28 충족 / 0 미검증 / 0 실패(꺼짐 3: static·visual·webkit) · showcase-audit 12/12 (320·390·768·1440·1920·2560 × 홈·가맹) · 철판클럽 E2E 42 시나리오 166 단언 통과(스크래치 복제본, WebKit 포함) · audit.mjs 2560px 3단언 통과(스크래치 복제) · 미검증 0. + +캡처: `outputs/`가 아닌 세션 스크래치 `griddle-shots/`(home·franchise × 1440·390 첫 화면·전체·회색조·흐림·감소 모션, 2560 첫 화면, 포커스). + +시각 루브릭(홈, 약한 축부터): + +| 축 | 점수 | 근거 | +|---|---|---| +| R7 카피 | 1 | 합니다체 통일, 은유 0, §13-c 지문 grep 0. 그러나 "주문 뒤 눌러 굽고, 바로 건넵니다."는 다른 스매시 버거 가게로 바꿔도 성립한다(공정 주장). 브리프만의 고유 재료(동네·가격)는 리드와 알약에 있고 h1에는 없다 | +| R9 마감 | 1 | 200% 확대: 마퀴 항목이 정지 버튼 뒤로 지나가며 글자가 잘려 보인다(1440 회색조 캡처 y≈860). 조각 테두리·알약 radius·숫자 정렬은 어긋남 없음 | +| R1 첫인상 | 2 | 3초 문장 "주문 뒤 눌러 굽는 스매시 버거 가게, 메뉴 보고 조합한다" — 1440·390 모두 브랜드·표제·사진·행동이 첫 화면에 있다. 반례: 상호 '철판클럽'만으로는 버거집임을 알 수 없고 사진이 말한다 | +| R2 위계 | 2 | 흐림 캡처(1440): 표제 덩어리 → 버거 원 → 코발트 버튼 순. 크기·색·굵기 세 축 차이. 반례: 표제와 버거 원이 거의 같은 무게로 1위를 다툰다 | +| R3 묶음 | 2 | 시작선 x=130(표제·리드·버튼·섹션 제목), x=156(카드 안쪽), 조각은 자유 배치(의도). 묶음 사이 간격(섹션 72~128px) > 안 간격(≤24px). 반례: 가격 알약은 정렬선 밖 | +| R4 타이포 | 2 | 합성 이탤릭·볼드 없음(Jua 400, Gothic A1 실제 굵기), 자간 -0.01em, 320·390·1440에서 h1·h2 마지막 줄 한 어절 0건(스크립트 실측), 본문 measure 30em. 반례: Jua가 한 굵기라 카드 h3와 섹션 h2의 위계가 크기에만 기댄다 | +| R5 여백 | 2 | 마퀴 띠(얇음) ↔ 조합기 띠(1100px)로 간격 변주가 정보 전환과 일치. 극단: 조각 552px vs 138px, 코발트 전면 띠. 반례: 1440 히어로 왼쪽 위 빈 면(y 110~280)이 대기인지 남은 것인지 애매하다 | +| R6 이미지 | 2 | 슬롯 역할: 히어로 3조각(건네기·누르기·싱글), 메뉴 1(더블), 공정 1(주방). 390 첫 화면에 사진 있음. 같은 사진 반복 없음(모바일 원형은 ``로 세로 원본 교체). 반례: 누끼가 아니라 원형 크롭이라 원 안에 스테인리스 배경이 남는다 | +| R8 구분성 | 2 | 고유 요소 4개(위 반사실 점검), B-1 같음 0, B-2 같음 1(푸터, 배정 유지). 반례: 다열 푸터는 cask·orbit·rfs·twentyoz와 같은 형태 | +| R10 모바일 | 2 | 390 첫 화면: 브랜드·표제·행동 2·사진 조각, 하단 고정 바 4항목. 조각은 뷰포트 안으로 재배치. 반례: 리드가 4줄이라 사진이 첫 화면 하단 1/3에서 시작한다 | + +합계 18/20, 0점 없음, R1·R8 2점. 통과. + +가맹 페이지: 같은 셸·토큰. 1440·390 첫 화면 캡처에서 표제·행동 2·사진·하단 바 확인. E2E 8시나리오(폼 단계·오류·환산·성공·실패) 통과. + +하드 게이트: 키보드 — 탭 순서 스킵 링크 → 브랜드 → 내비 4 → 행동 → 히어로 버튼 2 → 마퀴 정지 → 마퀴 영역 → 필터…(실측), 포커스 링 3px 코발트(코발트 띠 위는 크림). 대비 — 위 수치, axe 0건. 감소 모션 — E2E "반복 애니메이션 제거 ≤0.01s" 통과, 조각 즉시 표시(캡처). 가로 스크롤 — 320~2560 0건. 터치 타깃 — 게이트 통과(컨트롤 ≥44, 링크 ≥24). + +## 피드백 기록 · 보존 목록 + +- 보존: 상호·메뉴·가격·매장 상태·알레르기 규칙·조합기 id·data 속성·가맹 폼 id·E2E 문구("가상 매장 안내 보기", "주문과 결제는 진행되지 않습니다", "약 30평"). +- 사용자 피드백: 없음(위임). + +## 변경 기록 + +- 2026-09-12 화면 보정 — 파란 상품 카드를 사진 아래 shelf로, 메뉴 이미지 중앙 cover 크롭 통일, 가맹 절차·비용을 원장으로 정렬. `--blue`·`--paper-*`·`--ink`·`--line`·`--font-mono`만 사용. +- 2026-10-02 레드팀 06 — h1 자간 -0.085em → -0.015em(쉼표·받침 충돌), "예시" 13회 → 1회, 공정 표제를 정보 문장으로. +- 2026-10-02 카피 기준 — 슬로건 한 곳만 남기고 표제를 합니다체 정보 문장으로. 가맹 h1 2줄 계약. +- 2026-10-06 재작업(이 문서) — 분할 히어로 → 비대칭 콜라주, Pretendard → Jua/Gothic A1, 웜 페이퍼 → 유채 크림, 4·10·18px → pill·16px, 상단 바에 모바일 하단 행동 바 추가, 고지 푸터 → 4열 색인, eyebrow 16개 제거, 가운뎃점 제거, 스태거 등장 + 마퀴 추가, 조합기 사진 제거, 가맹 페이지 셸·표제 재작성. 옛 결정 중 유지한 것: 메뉴 격자 열 배분(6/3/3 + 4/4/4), 조합기 가격식, 매장 지도 그림, 가맹 폼 3단계. diff --git a/apps/site/public/work/griddle-club/franchise/index.html b/apps/site/public/work/griddle-club/franchise/index.html index 33604e2..7dd19e1 100644 --- a/apps/site/public/work/griddle-club/franchise/index.html +++ b/apps/site/public/work/griddle-club/franchise/index.html @@ -3,12 +3,12 @@ - 철판클럽 가맹 안내 — 운영 조건부터 확인하기 + 철판클럽 가맹 안내 — 상권, 점포, 주방 동선부터 - + - + - - + - - - - + + + - +
      -

      가상 브랜드 안내 · 실제 가맹 모집, 상담, 계약을 제공하지 않습니다.

      +

      가상 브랜드입니다. 가맹 모집과 상담은 하지 않습니다.

      -
      +
      철판클럽 @@ -71,14 +66,13 @@
      -

      01 수익률을 약속하지 않음

      -

      02 점포를 보기 전 금액을 확정하지 않음

      -

      03 실제 개인정보를 받지 않음

      +

      01 수익률을 약속하지 않습니다

      +

      02 점포를 보기 전에는 금액을 정하지 않습니다

      +

      03 실제 개인정보를 받지 않습니다

      -

      어떤 운영 방식인지

      좁은 주방에서
      역할을 분명하게.

      +

      주방은 준비, 조리, 포장, 전달 네 구간으로 나눕니다.

      조리, 조립, 포장, 전달이 서로 겹치지 않는 흐름을 먼저 검토합니다. 실제 설비와 인원은 점포마다 달라집니다.

      @@ -121,7 +115,7 @@
      -

      운영자에게 필요한 태도

      직접 확인하고,
      같은 품질을 반복하기.

      +

      점주는 매장에서 직접 조리하며 네 가지를 준비합니다.

      • 현장 참여조리와 응대 흐름을 직접 익힐 준비
      • 위생 기록보관, 조리, 마감 상태를 남길 준비
      • @@ -135,28 +129,28 @@
        -

        상권과 점포

        주소보다 먼저
        운영 장면을 봅니다.

        +

        상권, 점포 상태, 전용 면적, 설비, 이전 용도 다섯 가지를 적습니다.

        아래 분류는 적합 판정이 아니라 문의 때 빠뜨리지 않을 조건 목록입니다. 권장 면적 수치는 실제 검토 뒤에만 제시할 수 있습니다.

        -

        A

        +

        A

        상권 흐름

        업무가, 주거지, 역 주변, 대학가, 상점가 또는 여러 수요가 섞인 곳인지 먼저 적습니다.

        • 점심과 저녁 수요 시간
        • 도보와 차량 접근
        • 포장 대기 공간
        • 배달 픽업 접근
        -

        B

        점포 상태

        점포 없음, 찾는 중, 협의 중, 보유함으로 나눕니다.

        +

        B

        점포 상태

        점포 없음, 찾는 중, 협의 중, 보유함으로 나눕니다.

        -

        C

        전용 면적

        사용자가 입력한 ㎡와 환산한 평을 함께 봅니다. 적합 판정은 하지 않습니다.

        +

        C

        전용 면적

        사용자가 입력한 ㎡와 환산한 평을 함께 봅니다. 적합 판정은 하지 않습니다.

        -

        D

        설비 조건

        전기, 배기, 급수, 배수와 반입 경로는 점포 자료로 따로 확인합니다.

        +

        D

        설비 조건

        전기, 배기, 급수, 배수와 반입 경로는 점포 자료로 따로 확인합니다.

        -

        E

        이전 용도

        음식점, 소매점, 빈 점포 등 이전 사용 상태를 확인합니다.

        +

        E

        이전 용도

        음식점, 소매점, 빈 점포 등 이전 사용 상태를 확인합니다.

        @@ -165,7 +159,7 @@
        -

        개설 절차

        기간을 꾸미지 않고
        순서만 보여 줍니다.

        +

        개설은 여덟 단계이고, 기간은 점포마다 다릅니다.

        점포, 자료, 협의와 공사 조건에 따라 기간이 달라집니다. 아래 순서는 검토에 필요한 항목을 정리한 것이며 계약이나 개점을 보장하지 않습니다.

          @@ -184,7 +178,7 @@
          -

          비용 항목

          합계를 만들 수 없는
          이유부터 적습니다.

          +

          비용 항목은 열한 가지이고, 금액은 점포를 본 뒤에 정합니다.

          현재 상태 비용 미정 @@ -209,7 +203,7 @@
      - +
      첫 물류메뉴 범위 필요미정품목과 보관량 확인
      임대 보증 관련 비용점포 계약 필요미정임대 조건 직접 확인
      합계계산하지 않음 · 확인되지 않은 숫자를 더하지 않습니다
      합계계산하지 않습니다. 확인되지 않은 숫자는 더하지 않습니다.

      평균 매출, 예상 수익, 회수 기간과 최저 비용은 근거 자료가 없어 제공하지 않습니다.

      @@ -219,8 +213,7 @@
      -

      조건 정리

      -

      연락처 없이도
      조건부터 정리하기.

      +

      연락처 없이 세 단계로 조건만 정리해 봅니다.

      실제 이름과 연락처를 받지 않습니다. 가상의 운영 조건을 입력하면 어떤 항목을 확인해야 하는지 순서대로 살펴볼 수 있습니다.

      실제 개인정보를 입력하지 마세요. @@ -330,7 +323,6 @@
      + + diff --git a/apps/site/public/work/griddle-club/index.html b/apps/site/public/work/griddle-club/index.html index c179ac2..87bb58a 100644 --- a/apps/site/public/work/griddle-club/index.html +++ b/apps/site/public/work/griddle-club/index.html @@ -3,52 +3,44 @@ - 철판클럽 — 눌러 굽고, 바로 건넨다 + 철판클럽 — 주문 뒤에 눌러 굽는 스매시 버거 - + - + - - + + - - + - - - + + - +
      -

      가상 브랜드 안내 · 가격과 매장 정보는 실제 판매 정보가 아닙니다.

      +

      가상 브랜드입니다. 가격과 매장은 실제 정보가 아닙니다.

      @@ -63,72 +55,95 @@ 매장 찾기 가맹 안내 - 가까운 매장 보기 + 내 버거 조합하기
      -

      서울 도심 눌림 버거

      -

      눌러 굽고,
      바로 건넨다.

      +

      주문 뒤 눌러 굽고,
      바로 건넵니다.

      - 얇게 눌러 가장자리는 바삭하게. 주문 뒤 조립하고 포장해, 퇴근길 한 상자로 건넵니다. + 주문을 받은 뒤에 얇은 패티를 뜨거운 철판에 눌러 가장자리까지 바삭하게 굽고, 치즈와 구운 양파를 올려 그 자리에서 포장합니다. 단품 6,900원부터.

      -
      -
      굽기
      얇은 패티를 주문 뒤 눌러 굽기
      -
      받기
      포장과 배달 가능 여부를 매장별 표시
      -
      -
      - - +
      +
      + + + 철판 주방에서 직원이 유산지에 싼 철판 더블을 파란 쟁반에 올려 건네는 모습 + + +
      +
      밝은 철판 주방에서 직원이 포장한 버거를 건네는 모습 - - -
      - 철판에서 상자까지 - 생성 이미지 · 실제 매장 아님 -
      -
      - + +
      +
      + 치즈와 피클을 넣은 철판 싱글 + +
      +

      철판 더블8,900원

      +

      주문 뒤 조리

      +

      생성 이미지, 실제 매장이 아닙니다

      +
      -
      -
      -

      주문 뒤 조리

      -

      단품과 세트

      -

      포장 여부 매장별 확인

      -

      배달 여부 매장별 확인

      -

      GRIDDLE CLUB

      +
      + +
      +
      +
        +
      • 철판 더블 8,900원
      • +
      • 철판 싱글 6,900원
      • +
      • 고추 피클 더블 9,400원
      • +
      • 얇은 감자 3,600원
      • +
      • 양파 한 컵 2,800원
      • +
      • 탄산 한 잔 2,400원
      • +
      • 주문 뒤 조리
      • +
      • 포장과 배달은 매장별 확인
      • +
      + +
      @@ -230,11 +234,8 @@
      -
      -

      버거 조합기

      -

      내 버거 조합을
      바꿔 보세요.

      -
      -

      아래 구성은 메뉴를 살펴보기 위한 예시입니다. 재료를 바꾸면 오른쪽 안내 카드와 예시 가격이 함께 바뀝니다.

      +

      재료를 바꾸면 가격과 알레르기 정보가 바로 바뀝니다.

      +

      맛, 패티 수, 치즈, 양파와 피클, 단품과 세트, 받을 매장 순서로 고릅니다. 오른쪽 영수증에 결과가 모입니다.

      @@ -296,44 +297,32 @@
      - 05 확인할 가상 매장 + 05 받을 매장 -

      아래 매장과 운영 상태는 실제 방문·주문 정보가 아닙니다.

      + + diff --git a/apps/site/public/work/griddle-club/og.png b/apps/site/public/work/griddle-club/og.png index 2ff453c..d67e9cb 100644 Binary files a/apps/site/public/work/griddle-club/og.png and b/apps/site/public/work/griddle-club/og.png differ diff --git a/apps/site/public/work/griddle-club/scripts/franchise.js b/apps/site/public/work/griddle-club/scripts/franchise.js index 3118bc7..4ca5419 100644 --- a/apps/site/public/work/griddle-club/scripts/franchise.js +++ b/apps/site/public/work/griddle-club/scripts/franchise.js @@ -140,7 +140,7 @@ return; } const pyeong = Math.round((squareMeters / 3.3058) * 10) / 10; - areaOutput.textContent = `${squareMeters.toLocaleString("ko-KR")}㎡ · 약 ${pyeong.toLocaleString("ko-KR")}평`; + areaOutput.textContent = `${squareMeters.toLocaleString("ko-KR")}㎡, 약 ${pyeong.toLocaleString("ko-KR")}평`; }; const validateStepTwo = () => { @@ -172,7 +172,7 @@ const reviewValues = () => { const squareMeters = Number(getInputValue("exclusiveArea")); const areaLabel = requiresArea() && squareMeters > 0 - ? `${squareMeters.toLocaleString("ko-KR")}㎡ · 약 ${(Math.round((squareMeters / 3.3058) * 10) / 10).toLocaleString("ko-KR")}평` + ? `${squareMeters.toLocaleString("ko-KR")}㎡, 약 ${(Math.round((squareMeters / 3.3058) * 10) / 10).toLocaleString("ko-KR")}평` : "해당 없음"; const values = { demoName: getInputValue("demoName"), diff --git a/apps/site/public/work/griddle-club/scripts/site.js b/apps/site/public/work/griddle-club/scripts/site.js index 0efcecb..d84767d 100644 --- a/apps/site/public/work/griddle-club/scripts/site.js +++ b/apps/site/public/work/griddle-club/scripts/site.js @@ -115,7 +115,7 @@ const flavorLabel = flavor === "spicy" ? "고추 피클" : "철판"; const cheeseLabel = Number(cheese) === 0 ? "치즈 없음" : `치즈 ${cheese === "2" ? "두" : "한"} 장`; const productName = `${flavorLabel} ${pattyLabel}`; - const servingLabel = serving === "set" ? "세트 · 얇은 감자와 음료" : "단품"; + const servingLabel = serving === "set" ? "세트, 얇은 감자와 음료" : "단품"; if (summaryHeading) summaryHeading.textContent = productName; if (summaryLines) { @@ -229,6 +229,25 @@ storeSearch?.focus(); }); + // 마퀴 — 5초 넘게 움직이므로 정지 수단을 둔다(WCAG 2.2.2). 감소 모션이면 CSS가 정지 목록으로 + // 바꾸고 버튼을 숨긴다. 설정이 도중에 바뀌어도 새로고침 없이 상태만 맞춘다. + const marquee = document.querySelector(".marquee"); + const marqueeToggle = document.querySelector("#marquee-toggle"); + const motionQuery = window.matchMedia("(prefers-reduced-motion: reduce)"); + const syncMarquee = () => { + if (!marquee || !marqueeToggle) return; + const paused = motionQuery.matches || marqueeToggle.getAttribute("aria-pressed") === "true"; + marquee.classList.toggle("is-paused", paused); + marqueeToggle.textContent = paused ? "흐름 재생" : "흐름 멈춤"; + }; + marqueeToggle?.addEventListener("click", () => { + const pressed = marqueeToggle.getAttribute("aria-pressed") === "true"; + marqueeToggle.setAttribute("aria-pressed", String(!pressed)); + syncMarquee(); + }); + motionQuery.addEventListener("change", syncMarquee); + syncMarquee(); + updateBuilder(); filterStores(); })(); diff --git a/apps/site/public/work/griddle-club/styles/franchise.css b/apps/site/public/work/griddle-club/styles/franchise.css index 726aa9a..aac2ea7 100644 --- a/apps/site/public/work/griddle-club/styles/franchise.css +++ b/apps/site/public/work/griddle-club/styles/franchise.css @@ -1,107 +1,92 @@ +/* 철판클럽 가맹 안내 — 공개 페이지와 같은 셸(상단 바·하단 행동 바·다열 푸터)과 토큰을 쓴다. + 값은 전부 tokens.css. 색·radius 리터럴 없음. */ + .franchise-page { - background: var(--paper-bright); + background: var(--bg); } +/* ── 히어로: 표제 + 둥근 사진 타일(2차 페이지라 콜라주는 홈에만) ── */ .franchise-hero { - min-height: min(820px, calc(100svh - var(--header-height) - 30px)); display: grid; - grid-template-columns: minmax(360px, 0.92fr) minmax(0, 1.08fr); - background: var(--ink); - color: white; -} - -.franchise-hero-copy { - align-self: center; - padding: clamp(76px, 10vw, 140px) var(--gutter); - padding-left: max(var(--gutter), calc((100vw - var(--wrap)) / 2)); -} - -.franchise-hero-copy .section-kicker { - color: var(--blue-soft); + grid-template-columns: minmax(0, 0.56fr) minmax(0, 0.44fr); + gap: clamp(28px, 5vw, 72px); + align-items: center; + width: min(var(--wrap), calc(100% - (var(--gutter) * 2))); + margin-inline: auto; + padding-block: clamp(48px, 7vw, 104px); } .franchise-hero h1 { - max-width: 700px; - margin: 22px 0 0; - font-size: clamp(48px, 5.6vw, 80px); - font-weight: 900; - letter-spacing: -0.085em; - line-height: 0.98; + font-size: var(--text-h1); } -.franchise-hero-copy > p:not(.section-kicker) { - max-width: 550px; - margin-top: 30px; - color: #c9c4bc; - font-size: clamp(16px, 1.5vw, 20px); - line-height: 1.75; +.franchise-hero-copy > p { + max-width: 30em; + margin-top: clamp(22px, 2.6vw, 34px); + color: var(--ink-soft); + font-size: var(--text-lg); } -.light-link { - border-color: white; -} - -.franchise-hero .hero-actions > a { - min-height: 48px; - display: inline-flex; - align-items: center; - justify-content: center; - margin-top: 0; - line-height: 1.2; -} - -.franchise-hero .hero-actions .light-link { - padding: 0 2px; - border-bottom: 0; - text-decoration: underline; - text-decoration-thickness: 1px; - text-underline-offset: 6px; +.franchise-hero .hero-actions { + display: grid; + grid-template-columns: auto auto; + justify-content: start; + align-items: stretch; + gap: 12px; } .franchise-truth { - max-width: 560px; - margin-top: 52px; - padding: 18px 20px; - border-left: 3px solid var(--blue-soft); - background: #2b2926; + max-width: 32em; + margin-top: clamp(28px, 4vw, 44px); + padding: 16px 20px; + border-radius: var(--radius); + background: var(--surface); } .franchise-truth strong { - font-size: 12px; + font-size: var(--text-xs); } .franchise-truth p { - margin-top: 5px; - color: #aaa59d; - font-size: 11px; + margin-top: 4px; + color: var(--ink-soft); + font-size: var(--text-xs); } .franchise-hero-media { - min-height: 680px; + position: relative; overflow: hidden; - background: var(--steel); + aspect-ratio: 4 / 5; + border: 8px solid var(--surface); + border-radius: var(--radius); + background: var(--bg-deep); + box-shadow: var(--shadow-piece); + transform: rotate(2deg); } .franchise-hero-media img { width: 100%; height: 100%; object-fit: cover; + object-position: 45% 50%; } .franchise-hero-media figcaption { position: absolute; - right: 16px; - bottom: 16px; - padding: 7px 10px; - background: var(--paper-bright); + left: 14px; + bottom: 14px; + padding: 7px 14px; + border-radius: var(--radius-pill); + background: var(--surface); color: var(--ink); - font-size: 10px; - font-weight: 700; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } +/* ── 원칙 세 줄 ── */ .fact-rail { - border-bottom: 1px solid var(--line); - background: var(--paper); + border-block: 1px solid var(--line); + background: var(--surface); } .fact-rail-grid { @@ -110,14 +95,14 @@ } .fact-rail p { - min-height: 92px; + min-height: 84px; display: flex; align-items: center; - gap: 16px; - padding: 18px 28px; + gap: 14px; + padding: 16px 24px; border-right: 1px solid var(--line); - font-size: 13px; - font-weight: 750; + font-size: var(--text-sm); + font-weight: var(--weight-strong); } .fact-rail p:first-child { @@ -130,76 +115,84 @@ .fact-rail span, .fit-number { - color: var(--blue); - font-family: var(--font-mono); - font-size: 11px; + flex: 0 0 auto; + width: 36px; + height: 36px; + display: grid; + place-items: center; + border-radius: 50%; + background: var(--blue); + color: var(--on-blue); + font-family: var(--font-display); + font-size: 14px; + font-variant-numeric: tabular-nums; } +/* ── 운영 방식 ── */ .model-section { - background: var(--paper-bright); + background: var(--bg); } .flow-board { display: grid; grid-template-columns: repeat(4, 1fr); - border: 1px solid var(--ink); + gap: 14px; } .flow-board article { - min-height: 340px; + min-height: 300px; display: flex; flex-direction: column; justify-content: flex-end; - padding: clamp(22px, 3vw, 34px); - border-right: 1px solid var(--ink); + padding: clamp(20px, 3vw, 30px); + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--surface); } .flow-board article:last-child { - border-right: 0; + border-color: var(--blue); background: var(--blue); - color: white; + color: var(--on-blue); } .flow-board article > span { margin-bottom: auto; + font-family: var(--font-display); + font-size: 18px; color: var(--blue); - font-family: var(--font-mono); - font-size: 11px; + font-variant-numeric: tabular-nums; } .flow-board article:last-child > span { - color: #c8d3ff; + color: var(--on-blue-soft); } .flow-board h3 { - font-size: 23px; - letter-spacing: -0.045em; + font-size: 26px; } .flow-board p { margin-top: 8px; color: var(--ink-soft); - font-size: 13px; + font-size: var(--text-sm); } .flow-board article:last-child p { - color: #dce4ff; + color: var(--on-blue-soft); } .operator-fit { display: grid; grid-template-columns: 0.8fr 1.2fr; - gap: 70px; - margin-top: clamp(64px, 9vw, 110px); - padding-top: 42px; - border-top: 1px solid var(--ink); + gap: clamp(28px, 5vw, 70px); + margin-top: clamp(56px, 8vw, 100px); + padding-top: 40px; + border-top: 1px solid var(--line-strong); } .operator-fit h3 { - margin-top: 13px; - font-size: clamp(34px, 4.2vw, 54px); - letter-spacing: -0.065em; - line-height: 1.12; + font-size: clamp(28px, 3.2vw, 40px); } .operator-fit ul { @@ -213,81 +206,83 @@ display: grid; grid-template-columns: 130px 1fr; gap: 20px; - padding: 19px 0; + padding: 18px 0; border-bottom: 1px solid var(--line); - font-size: 13px; + font-size: var(--text-sm); } .operator-fit span { color: var(--ink-soft); } +/* ── 상권과 점포 ── */ .site-fit-section { - background: var(--steel); + background: var(--bg-deep); } .fit-grid { display: grid; grid-template-columns: repeat(4, 1fr); - border-top: 1px solid var(--ink); - border-left: 1px solid var(--ink); + gap: 14px; } .fit-grid article { - min-height: 300px; - padding: clamp(22px, 3vw, 34px); - border-right: 1px solid var(--ink); - border-bottom: 1px solid var(--ink); - background: var(--paper-bright); + min-height: 280px; + padding: clamp(20px, 3vw, 30px); + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--surface); } .fit-grid .fit-main { grid-column: span 2; grid-row: span 2; - min-height: 600px; + min-height: 560px; display: flex; flex-direction: column; + border-color: var(--ink); background: var(--ink); - color: white; + color: var(--on-ink); } .fit-grid h3 { - margin-top: 46px; - font-size: clamp(28px, 3.5vw, 48px); - letter-spacing: -0.06em; + margin-top: 36px; + font-size: clamp(26px, 3vw, 40px); } .fit-grid article > p:not(.fit-number) { - margin-top: 14px; + margin-top: 12px; color: var(--ink-soft); - font-size: 13px; + font-size: var(--text-sm); } .fit-grid .fit-main > p:not(.fit-number) { - max-width: 490px; - color: #d4cec5; - font-size: 16px; + max-width: 28em; + color: var(--on-ink-soft); + font-size: var(--text-md); } .fit-main .fit-number { - color: #9db0ff; + background: var(--cheese); + color: var(--on-cheese); } .fit-main ul { margin: auto 0 0; - padding: 24px 0 0; - border-top: 1px solid #625f5a; + padding: 22px 0 0; + border-top: 1px solid var(--on-ink-faint); list-style: none; } .fit-main li { - padding: 8px 0; - color: #e8e3db; - font-size: 13px; + padding: 7px 0; + color: var(--on-ink-soft); + font-size: var(--text-sm); } +/* ── 개설 절차 ── */ .franchise-process { - background: var(--paper-bright); + background: var(--surface); } .franchise-timeline { @@ -299,150 +294,161 @@ .franchise-timeline li { display: grid; - grid-template-columns: 4.5rem minmax(0, 1fr) minmax(9rem, auto); + grid-template-columns: 4rem minmax(0, 1fr) minmax(9rem, auto); align-items: center; gap: 1.5rem; - min-height: 124px; - padding: 22px 0; + min-height: 112px; + padding: 20px 0; border-bottom: 1px solid var(--line); } .franchise-timeline li > span { - color: var(--blue); - font-family: var(--font-mono); - font-size: 12px; + width: 40px; + height: 40px; + display: grid; + place-items: center; + border-radius: 50%; + background: var(--blue); + color: var(--on-blue); + font-family: var(--font-display); + font-size: 15px; + font-variant-numeric: tabular-nums; } .franchise-timeline h3 { - font-size: 24px; - letter-spacing: -0.045em; + font-size: 25px; } .franchise-timeline p { margin-top: 3px; color: var(--ink-soft); - font-size: 13px; + font-size: var(--text-sm); } .franchise-timeline small { justify-self: end; min-width: 8rem; - padding: 6px 9px; - border: 1px solid var(--line); - font-size: 10px; - font-weight: 700; + padding: 7px 12px; + border: 1px solid var(--line-strong); + border-radius: var(--radius-pill); + font-size: var(--text-xs); + font-weight: var(--weight-strong); text-align: center; } +/* ── 비용 항목: 먹 띠 ── */ .cost-section { background: var(--ink); - color: white; + color: var(--on-ink); } -.cost-section .section-kicker { - color: var(--blue-soft); +.cost-section :focus-visible { + outline-color: var(--cheese); } .cost-head { display: grid; grid-template-columns: 1.25fr 0.75fr; align-items: end; - gap: 50px; - margin-bottom: 64px; -} - -.cost-head h2 { - margin-top: 14px; + gap: clamp(24px, 4vw, 50px); + margin-bottom: clamp(36px, 5vw, 60px); } .unknown-cost { - padding: 22px; - border: 1px solid #625f5a; - border-left: 3px solid var(--blue-soft); - background: #2a2825; + padding: 22px 24px; + border: 1px solid var(--on-ink-faint); + border-radius: var(--radius); } .unknown-cost span { - color: #aaa59d; - font-size: 10px; + color: var(--on-ink-faint); + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .unknown-cost strong { display: block; margin-top: 3px; - color: var(--blue-soft); - font-size: 25px; + color: var(--cheese); + font-family: var(--font-display); + font-weight: 400; + font-size: 30px; } .unknown-cost p { margin-top: 7px; - color: #bdb7af; - font-size: 12px; + color: var(--on-ink-soft); + font-size: var(--text-sm); } .cost-table-wrap { overflow-x: auto; - border-top: 1px solid #827d75; + border-top: 1px solid var(--on-ink-faint); } .cost-table { width: 100%; min-width: 780px; border-collapse: collapse; - font-size: 12px; } .cost-table th, .cost-table td { padding: 1.125rem 1rem; - border-bottom: 1px solid #4f4c47; - font-size: 0.8125rem; + border-bottom: 1px solid var(--ink-soft); + font-size: var(--text-sm); line-height: 1.55; text-align: left; } .cost-table thead th { - color: #9f9a92; - font-size: 10px; + color: var(--on-ink-faint); + font-size: var(--text-xs); } .cost-table tbody th { - color: white; - font-weight: 750; + color: var(--on-ink); + font-weight: var(--weight-strong); } .cost-table td { - color: #c4beb6; + color: var(--on-ink-soft); } .cost-table td:nth-child(3) { - color: var(--blue-soft); - font-family: var(--font-mono); - font-weight: 600; + color: var(--cheese); + font-family: var(--font-display); } .cost-table tfoot th, .cost-table tfoot td { border-bottom: 0; background: var(--blue); - color: white; - font-weight: 750; + color: var(--on-blue); + font-weight: var(--weight-strong); +} + +.cost-table th:nth-child(3), +.cost-table td:nth-child(3) { + font-variant-numeric: tabular-nums; + text-align: right; } .cost-warning { margin-top: 18px; - color: #aaa59d; - font-size: 11px; + color: var(--on-ink-faint); + font-size: var(--text-xs); } +/* ── 조건 정리 폼 ── */ .inquiry-section { - background: var(--paper); + background: var(--bg); } .inquiry-layout { display: grid; grid-template-columns: minmax(280px, 0.7fr) minmax(480px, 1.3fr); - gap: clamp(44px, 8vw, 110px); + gap: clamp(36px, 7vw, 100px); align-items: start; } @@ -451,38 +457,38 @@ top: calc(var(--header-height) + 32px); } -.inquiry-intro h2 { - margin-top: 14px; -} - -.inquiry-intro > p:not(.section-kicker) { - max-width: 460px; - margin-top: 26px; +.inquiry-intro > p { + max-width: 28em; + margin-top: 24px; color: var(--ink-soft); - font-size: 15px; + font-size: var(--text-md); } .privacy-note { - margin-top: 32px; - padding: 18px; - border-left: 3px solid var(--danger); - background: #fff0eb; + margin-top: 28px; + padding: 18px 20px; + border: 1px solid var(--danger); + border-radius: var(--radius); + background: var(--surface); } .privacy-note strong { - font-size: 12px; + color: var(--danger); + font-size: var(--text-xs); } .privacy-note p { margin-top: 5px; - color: #733c33; - font-size: 11px; + color: var(--ink-soft); + font-size: var(--text-xs); } .inquiry-panel { - border: 1px solid var(--line-strong); - background: var(--paper-bright); - box-shadow: var(--shadow); + overflow: hidden; + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--surface); + box-shadow: var(--shadow-card); } .step-indicator { @@ -495,7 +501,7 @@ } .step-indicator li { - min-height: 76px; + min-height: 72px; display: flex; align-items: center; gap: 10px; @@ -510,22 +516,28 @@ .step-indicator li.is-current { background: var(--blue); - color: white; + color: var(--on-blue); } .step-indicator span { - font-family: var(--font-mono); - font-size: 12px; + width: 28px; + height: 28px; + display: grid; + place-items: center; + border: 2px solid currentcolor; + border-radius: 50%; + font-family: var(--font-display); + font-size: 14px; } .step-indicator small { - font-size: 11px; - font-weight: 750; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .form-step, .inquiry-success { - padding: clamp(26px, 5vw, 50px); + padding: clamp(24px, 4.5vw, 44px); } .is-js .form-step:not(.is-current) { @@ -534,29 +546,27 @@ .step-count { color: var(--blue); - font-family: var(--font-mono); - font-size: 11px; + font-family: var(--font-display); + font-size: 15px; + font-variant-numeric: tabular-nums; } .form-step h3, .inquiry-success h3 { margin-top: 8px; - font-size: clamp(29px, 4vw, 43px); - font-weight: 850; - letter-spacing: -0.06em; - line-height: 1.2; + font-size: clamp(28px, 3.4vw, 38px); } .form-field { - margin-top: 30px; + margin-top: 28px; } .form-field > label, .form-field > legend { display: block; - margin-bottom: 9px; - font-size: 13px; - font-weight: 750; + margin-bottom: 8px; + font-size: var(--text-sm); + font-weight: var(--weight-strong); } .form-field input[type="text"], @@ -564,10 +574,10 @@ .form-field select { width: 100%; min-height: 52px; - padding: 11px 13px; - border: 1px solid var(--line-strong); - border-radius: var(--radius-sm); - background: white; + padding: 11px 16px; + border: 2px solid var(--line-strong); + border-radius: var(--radius); + background: var(--bg); } .form-field > small, @@ -575,20 +585,21 @@ display: block; margin-top: 7px; color: var(--ink-faint); - font-size: 10px; + font-size: var(--text-xs); } .field-error { min-height: 20px; margin-top: 5px; color: var(--danger); - font-size: 11px; - font-weight: 700; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .form-field.has-error input, .form-field.has-error select, -.form-field.has-error .radio-grid { +.form-field.has-error .unit-input, +.form-field.has-error .radio-grid span { border-color: var(--danger); } @@ -621,18 +632,19 @@ min-height: 48px; display: grid; place-items: center; - padding: 9px; - border: 1px solid var(--line); - border-radius: var(--radius-sm); - font-size: 12px; - font-weight: 700; + padding: 9px 12px; + border: 2px solid var(--line); + border-radius: var(--radius-pill); + font-size: var(--text-sm); + font-weight: var(--weight-strong); text-align: center; + transition: background var(--dur-fast) ease, color var(--dur-fast) ease, border-color var(--dur-fast) ease; } .radio-grid input:checked + span { border-color: var(--blue); background: var(--blue); - color: white; + color: var(--on-blue); } .radio-grid input:focus-visible + span { @@ -643,8 +655,10 @@ .unit-input { display: flex; align-items: center; - border: 1px solid var(--line-strong); - background: white; + overflow: hidden; + border: 2px solid var(--line-strong); + border-radius: var(--radius); + background: var(--bg); } .unit-input input { @@ -652,15 +666,15 @@ } .unit-input span { - padding-right: 14px; - font-size: 12px; - font-weight: 750; + padding-right: 16px; + font-size: var(--text-sm); + font-weight: var(--weight-strong); } .form-actions { display: flex; justify-content: flex-end; - margin-top: 36px; + margin-top: 32px; } .split-actions { @@ -668,23 +682,18 @@ gap: 12px; } -.form-actions .button-ghost { - margin-top: 0; - background: white; -} - .review-list { - margin-top: 30px; - border-top: 1px solid var(--ink); + margin-top: 28px; + border-top: 1px dashed var(--line-strong); } .review-list div { display: grid; grid-template-columns: 130px 1fr; gap: 20px; - padding: 14px 0; - border-bottom: 1px solid var(--line); - font-size: 12px; + padding: 13px 0; + border-bottom: 1px dashed var(--line-strong); + font-size: var(--text-sm); } .review-list dt { @@ -692,19 +701,19 @@ } .review-list dd { - font-weight: 750; + font-weight: var(--weight-strong); } .consent-check { position: relative; min-height: 44px; display: grid; - grid-template-columns: 20px 1fr; + grid-template-columns: 22px 1fr; align-items: start; gap: 10px; margin-top: 24px; - font-size: 12px; - font-weight: 650; + font-size: var(--text-sm); + font-weight: var(--weight-strong); } .consent-check input { @@ -726,18 +735,19 @@ .consent-check span::before { content: ""; position: absolute; - top: 2px; - left: -30px; - width: 18px; - height: 18px; - border: 1px solid var(--line-strong); - background: white; + top: 1px; + left: -32px; + width: 20px; + height: 20px; + border: 2px solid var(--line-strong); + border-radius: 50%; + background: var(--bg); } .consent-check input:checked + span::before { border-color: var(--blue); background: var(--blue); - box-shadow: inset 0 0 0 4px white; + box-shadow: inset 0 0 0 4px var(--surface); } .consent-check input:focus-visible + span { @@ -749,12 +759,12 @@ min-height: 24px; margin-top: 12px; color: var(--danger); - font-size: 11px; - font-weight: 700; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .inquiry-success { - min-height: 580px; + min-height: 560px; display: flex; flex-direction: column; justify-content: center; @@ -762,90 +772,60 @@ } .success-mark { - width: 58px; - height: 58px; + width: 60px; + height: 60px; display: grid; place-items: center; - margin-bottom: 26px; + margin-bottom: 22px; + border-radius: 50%; background: var(--blue); - color: white; + color: var(--on-blue); font-size: 28px; } -.inquiry-success > p:not(.section-kicker) { - max-width: 460px; - margin-top: 15px; +.inquiry-success > p { + max-width: 28em; + margin-top: 14px; color: var(--ink-soft); - font-size: 14px; + font-size: var(--text-sm); } .success-actions { display: flex; flex-wrap: wrap; gap: 10px; - margin-top: 30px; -} - -.success-actions .button { - margin-top: 0; + margin-top: 28px; } +/* ── 홈으로 ── */ .franchise-close { padding-block: var(--section); background: var(--blue); - color: white; + color: var(--on-blue); +} + +.franchise-close :focus-visible { + outline-color: var(--surface); } .close-row { display: grid; grid-template-columns: 1fr auto; - align-items: end; - gap: 44px; -} - -.franchise-close .section-kicker { - color: #c8d3ff; + align-items: center; + gap: clamp(24px, 5vw, 64px); } .franchise-close h2 { - max-width: 850px; - margin-top: 14px; - font-size: clamp(42px, 6vw, 78px); - letter-spacing: -0.075em; - line-height: 1.06; -} - -.franchise-foot-grid { - 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; + max-width: 16em; + font-size: var(--text-h2); } +/* ── 반응형 ── */ @media (max-width: 960px) { - .franchise-hero { - grid-template-columns: 0.95fr 1.05fr; - } - - .franchise-hero-copy { - padding-left: var(--gutter); - } - .flow-board { grid-template-columns: repeat(2, 1fr); } - .flow-board article:nth-child(2) { - border-right: 0; - } - - .flow-board article:nth-child(-n + 2) { - border-bottom: 1px solid var(--ink); - } - .fit-grid { grid-template-columns: repeat(2, 1fr); } @@ -858,27 +838,29 @@ .inquiry-intro { position: static; } - - .franchise-foot-grid { - grid-template-columns: 1fr 1fr; - } } -@media (max-width: 760px) { +@media (max-width: 820px) { .franchise-hero { grid-template-columns: 1fr; - } - - .franchise-hero-copy { - padding: 72px var(--gutter) 52px; + padding-block: 36px 40px; } .franchise-hero h1 { - font-size: clamp(40px, 13.5vw, 60px); + font-size: clamp(34px, 10.5vw, 46px); + } + + .franchise-hero .hero-actions { + grid-template-columns: 1fr 1fr; + } + + .franchise-hero .hero-actions .button { + padding-inline: 12px; } .franchise-hero-media { - min-height: 560px; + aspect-ratio: 4 / 3; + transform: rotate(-1.5deg); } .fact-rail-grid { @@ -887,23 +869,27 @@ .fact-rail p, .fact-rail p:first-child { - min-height: 64px; - padding: 14px 0; + min-height: 60px; + padding: 12px 0; border-right: 0; border-bottom: 1px solid var(--line); } + .fact-rail p:last-child { + border-bottom: 0; + } + .operator-fit { grid-template-columns: 1fr; - gap: 34px; + gap: 30px; } .fit-grid .fit-main { - min-height: 520px; + min-height: 480px; } .franchise-timeline li { - grid-template-columns: 42px 1fr; + grid-template-columns: 48px 1fr; } .franchise-timeline small { @@ -915,42 +901,34 @@ grid-template-columns: 1fr; align-items: start; } - - .franchise-foot-grid { - grid-template-columns: 1fr; - } } @media (max-width: 540px) { - .franchise-hero-media { - min-height: 460px; - } - .flow-board, .fit-grid { display: block; - border-left: 1px solid var(--ink); } - .flow-board article, - .flow-board article:nth-child(2), - .fit-grid article { - border-right: 1px solid var(--ink); - border-bottom: 1px solid var(--ink); + .flow-board article + article, + .fit-grid article + article { + margin-top: 12px; } - .flow-board article, - .flow-board article:nth-child(2) { - min-height: 170px; + .flow-board article { + min-height: 0; justify-content: flex-start; } .flow-board article > span { - margin-bottom: 30px; + margin-bottom: 22px; } .fit-grid .fit-main { - min-height: 500px; + min-height: 0; + } + + .fit-grid h3 { + margin-top: 22px; } .operator-fit li { @@ -964,11 +942,6 @@ padding: 9px; } - .step-indicator small { - display: inline; - font-size: 10px; - } - .radio-grid { grid-template-columns: repeat(2, 1fr); } @@ -993,28 +966,7 @@ /* 공개 메인과 같은 초광폭 상한: 사진이 내용보다 먼저 커지지 않는다. */ @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; + width: min(100%, var(--workbench-wide)); } .fact-rail .wrap, diff --git a/apps/site/public/work/griddle-club/styles/site.css b/apps/site/public/work/griddle-club/styles/site.css index b3b1605..524112a 100644 --- a/apps/site/public/work/griddle-club/styles/site.css +++ b/apps/site/public/work/griddle-club/styles/site.css @@ -1,22 +1,28 @@ +/* 철판클럽 공개 페이지 — 2026-10-06 재작업. + 구도: 비대칭 콜라주 히어로(메뉴 사진 조각 겹침) · 상단 바 + 모바일 하단 행동 바 · 다열 색인 푸터. + 값은 전부 tokens.css의 토큰이다. 이 파일에 색·radius 리터럴을 두지 않는다. */ + * { box-sizing: border-box; } html { scroll-behavior: smooth; - scroll-padding-top: calc(var(--header-height) + 40px); + scroll-padding-top: calc(var(--header-height) + 24px); } body { margin: 0; - background: var(--paper); + background: var(--bg); color: var(--ink); - font-family: var(--font-sans); - font-size: 16px; - line-height: 1.6; + font-family: var(--font-body); + font-size: var(--text-md); + font-weight: var(--weight-body); + line-height: var(--leading-body); word-break: keep-all; - overflow-wrap: break-word; + overflow-wrap: anywhere; text-rendering: optimizeLegibility; + font-synthesis-style: none; } button, @@ -38,11 +44,13 @@ button { a { color: inherit; text-decoration: none; + text-underline-position: under; } img { display: block; max-width: 100%; + height: auto; } figure, @@ -55,6 +63,16 @@ dd { margin: 0; } +h1, +h2, +h3 { + font-family: var(--font-display); + font-weight: 400; + letter-spacing: var(--tracking-display); + line-height: var(--leading-display); + text-wrap: balance; +} + fieldset { min-width: 0; margin: 0; @@ -84,9 +102,11 @@ fieldset { z-index: 100; top: 12px; left: 12px; - padding: 12px 16px; + padding: 12px 18px; + border-radius: var(--radius-pill); background: var(--ink); - color: white; + color: var(--on-ink); + font-weight: var(--weight-strong); transform: translateY(-180%); } @@ -99,23 +119,21 @@ fieldset { outline-offset: 3px; } +/* ── 고지 띠와 상단 바 ────────────────────────────────────── */ .truth-bar { - padding: 7px var(--gutter); + padding: 8px var(--gutter); background: var(--blue); - color: white; + color: var(--on-blue); text-align: center; - font-size: 12px; - font-weight: 700; - letter-spacing: -0.01em; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .site-head { - position: sticky; + position: relative; z-index: 20; - top: 0; border-bottom: 1px solid var(--line); - background: rgb(244 241 233 / 94%); - backdrop-filter: blur(12px); + background: var(--bg); } .head-row { @@ -123,33 +141,34 @@ fieldset { display: grid; grid-template-columns: auto 1fr auto; align-items: center; - gap: clamp(20px, 4vw, 54px); + gap: clamp(16px, 3vw, 40px); } .brand { display: inline-flex; align-items: center; - gap: 11px; - font-size: 20px; - font-weight: 850; - letter-spacing: -0.07em; + gap: 10px; + font-family: var(--font-display); + font-size: 24px; white-space: nowrap; } .brand-mark { - width: 28px; - height: 28px; + width: 30px; + height: 30px; display: grid; align-content: center; gap: 3px; - padding: 5px; + padding: 7px; + border-radius: var(--radius-pill); background: var(--blue); } .brand-mark i { display: block; height: 2px; - background: white; + border-radius: var(--radius-pill); + background: var(--on-blue); } .brand-mark i:nth-child(2) { @@ -157,304 +176,395 @@ fieldset { } .brand-mark i:nth-child(3) { - width: 52%; + width: 50%; } .head-nav { display: flex; align-items: center; - gap: clamp(18px, 3vw, 34px); - font-size: 14px; - font-weight: 650; + justify-content: center; + gap: 4px; + font-size: var(--text-sm); + font-weight: var(--weight-strong); } .head-nav a { - position: relative; - min-width: 24px; min-height: 44px; display: inline-flex; align-items: center; - justify-content: center; + padding: 0 16px; + border-radius: var(--radius-pill); + transition: background var(--dur-fast) ease; } -.head-nav a::after { - content: ""; - position: absolute; - right: 0; - bottom: -8px; - left: 0; - height: 2px; - background: var(--blue); - transform: scaleX(0); - transform-origin: right; - transition: transform 180ms ease; -} - -.head-nav a:hover::after, -.head-nav a:focus-visible::after { - transform: scaleX(1); - transform-origin: left; +.head-nav a:hover, +.head-nav a:focus-visible { + background: var(--surface); } .head-action { min-height: 44px; display: inline-flex; align-items: center; - justify-content: center; - padding: 10px 16px; + padding: 0 20px; + border-radius: var(--radius-pill); background: var(--ink); - color: white; - font-size: 13px; - font-weight: 750; + color: var(--on-ink); + font-size: var(--text-sm); + font-weight: var(--weight-strong); + transition: background var(--dur-fast) ease; } +.head-action:hover { + background: var(--blue); + color: var(--on-blue); +} + +/* ── 히어로: 비대칭 콜라주 ─────────────────────────────────── */ .hero { - min-height: min(820px, calc(100svh - var(--header-height) - 30px)); - display: grid; - grid-template-columns: minmax(320px, 0.78fr) minmax(0, 1.22fr); position: relative; - border-bottom: 1px solid var(--line); + min-height: min(720px, calc(100svh - var(--header-height) - 36px)); + display: grid; + grid-template-columns: minmax(0, 0.55fr) minmax(0, 0.45fr); + align-items: center; + padding-inline: max(var(--gutter), calc((100vw - var(--wrap)) / 2)); + overflow-x: clip; } .hero-copy { - align-self: center; - max-width: 630px; - padding: clamp(72px, 9vw, 132px) var(--gutter); - padding-left: max(var(--gutter), calc((100vw - var(--wrap)) / 2)); -} - -.section-kicker { - color: var(--blue); - font-family: var(--font-mono); - font-size: 12px; - font-weight: 600; - letter-spacing: 0.04em; + position: relative; + z-index: 2; + padding-block: clamp(56px, 8vw, 112px) clamp(44px, 6vw, 88px); + padding-right: clamp(0px, 3vw, 48px); } .hero h1 { - max-width: 620px; - margin-top: 22px; - font-size: clamp(54px, 5.7vw, 80px); - font-weight: 900; - letter-spacing: -0.085em; - 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; - } + font-size: var(--text-h1); } .hero-lead { - max-width: 520px; - margin-top: 30px; + max-width: 30em; + margin-top: clamp(22px, 2.6vw, 34px); color: var(--ink-soft); - font-size: clamp(17px, 1.5vw, 21px); - line-height: 1.72; + font-size: var(--text-lg); } .hero-actions { display: flex; align-items: center; flex-wrap: wrap; - gap: 22px; - margin-top: 38px; + gap: 12px; + margin-top: clamp(26px, 3vw, 40px); } .button { - min-height: 48px; + min-height: 52px; display: inline-flex; align-items: center; justify-content: center; - gap: 14px; - padding: 12px 20px; - border: 1px solid transparent; - border-radius: var(--radius-sm); - font-weight: 750; - line-height: 1.2; + gap: 12px; + padding: 12px 26px; + border: 2px solid transparent; + border-radius: var(--radius-pill); + font-weight: var(--weight-strong); + line-height: var(--leading-tight); + transition: background var(--dur-fast) ease, color var(--dur-fast) ease, border-color var(--dur-fast) ease; } .button-primary { border-color: var(--blue); background: var(--blue); - color: white; + color: var(--on-blue); } .button-primary:hover { + border-color: var(--blue-dark); background: var(--blue-dark); } -.text-link { - font-weight: 750; - border-bottom: 1px solid var(--ink); +.button-ghost { + border-color: var(--ink); + background: transparent; + color: var(--ink); } -.hero-facts { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20px; - margin-top: 54px; - padding-top: 20px; - border-top: 1px solid var(--line); +.button-ghost:hover { + background: var(--ink); + color: var(--on-ink); } -.hero-facts dt { - color: var(--ink-faint); - font-size: 12px; - font-weight: 750; +.button-light { + border-color: var(--surface); + background: var(--surface); + color: var(--ink); } -.hero-facts dd { - margin-top: 5px; - font-size: 13px; - font-weight: 650; - line-height: 1.45; +.button-light:hover { + border-color: var(--cheese); + background: var(--cheese); } .hero-media { - min-height: 650px; position: relative; - overflow: hidden; - background: var(--steel); + z-index: 1; + min-height: clamp(520px, 46vw, 760px); + align-self: stretch; } -.hero-media img { +/* 조각 — 원본은 사각 사진이라 누끼 대신 원형·둥근 사각 크롭으로 "스티커"를 만든다. + 크림 테두리가 오려 낸 가장자리 역할을 한다 */ +/* .image-shell의 position: relative보다 뒤에 와도 이기도록 자손 선택자로 특정성을 올린다(실측: 조각이 relative로 떨어져 390에서 왼쪽으로 밀렸다) */ +.hero-media .piece { + position: absolute; + margin: 0; + transform: rotate(var(--rot, 0deg)); + animation: piece-in var(--dur) var(--ease-out) both; + animation-delay: calc(var(--i, 0) * var(--stagger)); +} + +/* 등장은 이동·확대만, 투명도는 건드리지 않는다 — 페이드 중에 axe가 조각 글자의 대비를 2.8:1로 쟀다(실측) */ +@keyframes piece-in { + from { + transform: translateY(var(--shift)) scale(0.92) rotate(var(--rot, 0deg)); + } + + to { + transform: translateY(0) scale(1) rotate(var(--rot, 0deg)); + } +} + +.piece img { width: 100%; height: 100%; object-fit: cover; - object-position: 52% center; } -.hero-media picture { - width: 100%; - height: 100%; - display: block; +.piece-main { + --rot: 0deg; + right: -16%; + top: 4%; + width: 104%; + aspect-ratio: 1; + overflow: hidden; + border: 8px solid var(--surface); + border-radius: 50%; + box-shadow: var(--shadow-piece); } -.hero-media figcaption { - position: absolute; - right: 18px; - bottom: 18px; - left: 18px; - display: flex; - justify-content: space-between; - gap: 20px; - padding: 10px 12px; - background: rgb(33 31 28 / 82%); - color: white; - font-size: 11px; +.piece-main img { + object-position: 68% 56%; } -.hero-order { - position: absolute; - right: clamp(20px, 4vw, 60px); - bottom: -38px; - width: min(350px, calc(100% - 40px)); - display: grid; - grid-template-columns: auto 1fr; - gap: 0 16px; - padding: 20px; +.piece-press { + --rot: -7deg; + left: -4%; + bottom: 6%; + width: 46%; + aspect-ratio: 4 / 3; + overflow: hidden; + border: 6px solid var(--surface); + border-radius: var(--radius); + box-shadow: var(--shadow-piece); +} + +.piece-press img { + object-position: 42% 62%; +} + +.piece-single { + --rot: 6deg; + right: 2%; + bottom: 2%; + width: 26%; + aspect-ratio: 1; + overflow: hidden; + border: 6px solid var(--surface); + border-radius: 50%; + box-shadow: var(--shadow-piece); +} + +.piece-single img { + object-position: 50% 54%; +} + +.piece-tag { + --rot: -4deg; + left: 8%; + top: 16%; + display: inline-flex; + align-items: baseline; + gap: 10px; + padding: 12px 22px; + border-radius: var(--radius-pill); background: var(--blue); - color: white; - box-shadow: var(--shadow); + color: var(--on-blue); + font-family: var(--font-display); + font-size: 20px; + line-height: 1.2; + white-space: nowrap; + box-shadow: var(--shadow-card); } -.order-index { - grid-row: 1 / 4; - font-family: var(--font-mono); - font-size: 12px; +.piece-tag strong { + font-weight: 400; + font-size: 26px; + font-variant-numeric: tabular-nums; } -.hero-order p { - font-size: 22px; - font-weight: 850; - letter-spacing: -0.04em; -} - -.hero-order strong { - margin-top: 2px; - font-size: 13px; -} - -.hero-order button { - justify-self: start; - min-height: 44px; - margin: 8px -8px -4px; - padding: 8px 8px 7px; - border: 0; - border-bottom: 1px solid white; - background: transparent; - color: white; - font-size: 13px; - font-weight: 750; -} - -.service-strip { - padding: 58px 0 20px; - border-bottom: 1px solid var(--line); -} - -.service-row { +.piece-sticker { + --rot: -12deg; + right: 24%; + top: 2%; + width: 112px; + height: 112px; display: grid; - grid-template-columns: repeat(5, auto); - justify-content: space-between; - gap: 24px; + place-items: center; + padding: 10px; + border-radius: 50%; + background: var(--cheese); + color: var(--on-cheese); + font-family: var(--font-display); + font-size: 19px; + line-height: 1.2; + text-align: center; +} + +.piece-note { + left: 0; + bottom: 0; + padding: 6px 12px; + border-radius: var(--radius-pill); + background: var(--surface); color: var(--ink-soft); - font-size: 12px; - font-weight: 650; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } -.service-row p:first-child span { - margin-right: 7px; - color: var(--success); - font-size: 8px; +/* ── 마퀴 ──────────────────────────────────────────────── */ +.marquee { + position: relative; + display: flex; + align-items: center; + overflow: hidden; + border-block: 1px solid var(--line); + background: var(--surface); } -.mono { - font-family: var(--font-mono); +.marquee-toggle { + position: relative; + z-index: 2; + flex: 0 0 auto; + min-height: 44px; + margin-left: var(--gutter); + padding: 8px 16px; + border: 1px solid var(--line-strong); + border-radius: var(--radius-pill); + background: var(--surface); + font-size: var(--text-xs); + font-weight: var(--weight-strong); } +.marquee-toggle[aria-pressed="true"] { + border-color: var(--ink); + background: var(--ink); + color: var(--on-ink); +} + +/* 흐름은 가로 스크롤 상자 안에서 돈다 — 기다리지 않고 손이나 키보드로 넘길 수 있고, + 감사 도구도 "스크롤되는 상자 속 넓은 내용"으로 본다 */ +.marquee-scroll { + flex: 1 1 auto; + min-width: 0; + overflow-x: auto; + overflow-y: hidden; + scrollbar-width: none; +} + +.marquee-scroll::-webkit-scrollbar { + display: none; +} + +.marquee-track { + display: flex; + width: max-content; + animation: marquee var(--marquee-dur) linear infinite; +} + +.marquee.is-paused .marquee-track, +.marquee:hover .marquee-track, +.marquee:focus-within .marquee-track { + animation-play-state: paused; +} + +.marquee-scroll:focus-visible { + outline-offset: -3px; +} + +@keyframes marquee { + from { + transform: translateX(0); + } + + to { + transform: translateX(-50%); + } +} + +.marquee-list { + display: flex; + margin: 0; + padding: 0; + list-style: none; +} + +.marquee-list li { + display: inline-flex; + align-items: center; + gap: 18px; + padding: 18px 10px 18px 24px; + font-family: var(--font-display); + font-size: 22px; + line-height: 1.2; + white-space: nowrap; +} + +.marquee-list li::before { + content: ""; + flex: 0 0 auto; + width: 10px; + height: 10px; + border-radius: 50%; + background: var(--blue); +} + +/* ── 섹션 공통 ──────────────────────────────────────────── */ .section { padding-block: var(--section); } .section-head { - margin-bottom: clamp(38px, 6vw, 72px); + margin-bottom: clamp(32px, 5vw, 60px); } .split-head { display: grid; - grid-template-columns: minmax(0, 1.4fr) minmax(260px, 0.6fr); + grid-template-columns: minmax(0, 1.3fr) minmax(260px, 0.7fr); align-items: end; - gap: 44px; + gap: clamp(20px, 4vw, 48px); } .section h2, .franchise-callout h2 { - margin-top: 14px; - font-size: clamp(40px, 5.4vw, 72px); - font-weight: 880; - letter-spacing: -0.075em; - line-height: 1.06; + font-size: var(--text-h2); } .section-note { - max-width: 440px; + max-width: 26em; color: var(--ink-soft); - font-size: 14px; - line-height: 1.75; -} - -.menu-section { - background: var(--paper-bright); + font-size: var(--text-sm); } +/* ── 메뉴 ──────────────────────────────────────────────── */ .menu-toolbar { display: flex; gap: 6px; @@ -466,37 +576,37 @@ fieldset { .region-filters button { min-height: 44px; flex: 0 0 auto; - padding: 9px 17px; - border: 1px solid var(--line); - border-radius: 999px; + padding: 8px 18px; + border: 1px solid var(--line-strong); + border-radius: var(--radius-pill); background: transparent; - font-size: 13px; - font-weight: 700; + font-size: var(--text-sm); + font-weight: var(--weight-strong); + transition: background var(--dur-fast) ease, color var(--dur-fast) ease; } .filter-button.is-active, .region-filters button.is-active { border-color: var(--ink); background: var(--ink); - color: white; + color: var(--on-ink); } .menu-grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); - border-top: 1px solid var(--ink); + gap: 14px; } .menu-item { min-width: 0; grid-column: span 4; - border-right: 1px solid var(--line); - border-bottom: 1px solid var(--line); - background: var(--paper-bright); -} - -.menu-item:nth-child(3n) { - border-right: 0; + display: flex; + flex-direction: column; + overflow: hidden; + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--surface); } .menu-item-featured { @@ -508,10 +618,6 @@ fieldset { grid-column: span 3; } -.menu-item-small { - grid-column: span 4; -} - .menu-photo, .menu-poster { aspect-ratio: 4 / 3; @@ -523,97 +629,80 @@ fieldset { width: 100%; height: 100%; object-fit: cover; - object-position: center center; } .menu-number { position: absolute; top: 14px; left: 14px; - width: 38px; - height: 38px; + min-width: 44px; + height: 44px; display: grid; place-items: center; - background: var(--paper-bright); - font-family: var(--font-mono); - font-size: 12px; -} - -.menu-image-note { - position: absolute; - right: 10px; - bottom: 10px; - padding: 5px 7px; - background: rgb(255 253 248 / 94%); - color: var(--ink); - font-family: var(--font-mono); - font-size: 10px; - font-weight: 600; - line-height: 1.2; + padding-inline: 8px; + border-radius: var(--radius-pill); + background: var(--surface); + font-family: var(--font-display); + font-size: 15px; + font-variant-numeric: tabular-nums; } .menu-poster { display: grid; place-items: center; background: var(--blue); - color: white; + color: var(--on-blue); } .menu-poster span { position: absolute; top: 14px; left: 14px; - font-family: var(--font-mono); - font-size: 12px; + font-family: var(--font-display); + font-size: 15px; + font-variant-numeric: tabular-nums; } -.menu-poster i { - width: 68%; - height: 14px; - position: absolute; - background: white; +.menu-poster b { + font-family: var(--font-display); + font-weight: 400; + font-size: clamp(40px, 5vw, 64px); + line-height: 1; transform: rotate(-8deg); } -.menu-poster i:nth-child(3) { - transform: rotate(8deg) translateY(25px); -} - -.menu-poster i:nth-child(4) { - width: 42%; - transform: translateY(-28px); +.menu-poster-cream { + background: var(--bg-deep); + color: var(--ink); } .menu-body { + flex: 1 1 auto; display: grid; align-content: space-between; - gap: 24px; - min-height: 280px; - padding: clamp(20px, 3vw, 30px); + gap: 20px; + min-height: 250px; + padding: clamp(18px, 2.4vw, 26px); } .menu-item-small .menu-body { - min-height: 240px; + min-height: 210px; } .menu-state { color: var(--blue); - font-size: 11px; - font-weight: 750; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .menu-body h3 { - margin-top: 6px; - font-size: clamp(24px, 2.5vw, 34px); - font-weight: 850; - letter-spacing: -0.055em; - line-height: 1.18; + margin-top: 4px; + font-size: var(--text-h3); } .menu-body > p { color: var(--ink-soft); - font-size: 14px; - line-height: 1.65; + font-size: var(--text-sm); } .menu-foot { @@ -621,97 +710,101 @@ fieldset { align-items: end; justify-content: space-between; gap: 16px; - padding-top: 18px; + padding-top: 16px; border-top: 1px solid var(--line); - font-weight: 800; + font-family: var(--font-display); + font-size: 20px; + font-variant-numeric: tabular-nums; } .menu-foot small { display: block; color: var(--ink-faint); - font-size: 10px; - font-weight: 650; + font-family: var(--font-body); + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .menu-foot button, .empty-state button, .store-card button { min-height: 44px; - padding: 9px 14px; - border: 1px solid var(--ink); - border-radius: var(--radius-sm); + padding: 8px 18px; + border: 2px solid var(--ink); + border-radius: var(--radius-pill); background: transparent; - font-size: 12px; - font-weight: 750; + font-family: var(--font-body); + font-size: var(--text-xs); + font-weight: var(--weight-strong); + transition: background var(--dur-fast) ease, color var(--dur-fast) ease; } .menu-foot button:hover, -.store-card button:hover { +.store-card button:hover, +.empty-state button:hover { background: var(--ink); - color: white; + color: var(--on-ink); } .empty-state { padding: 44px 24px; - border: 1px solid var(--line); + border: 1px dashed var(--line-strong); + border-radius: var(--radius); text-align: center; } .empty-state strong { display: block; margin-bottom: 18px; - font-size: 20px; + font-family: var(--font-display); + font-weight: 400; + font-size: 22px; } +/* ── 조합기: 코발트 띠 ─────────────────────────────────── */ .builder-section { - background: var(--ink); - color: white; + background: var(--blue); + color: var(--on-blue); } -.builder-section > .wrap > .section-head .section-kicker { - color: var(--blue-soft); -} - -.builder-summary .section-kicker { - color: var(--blue); +.builder-section :focus-visible { + outline-color: var(--surface); } .builder-section .section-note { - color: #bbb5ad; + color: var(--on-blue-soft); } .builder-layout { display: grid; - grid-template-columns: minmax(0, 1.1fr) minmax(350px, 0.9fr); + grid-template-columns: minmax(0, 1.1fr) minmax(340px, 0.9fr); gap: clamp(28px, 5vw, 70px); align-items: start; } -.builder-form { - border-top: 1px solid #625f5a; -} - .builder-form fieldset { - padding: 28px 0 32px; - border-bottom: 1px solid #625f5a; + padding: 0 0 28px; + border-bottom: 1px solid rgb(var(--surface-rgb) / 30%); } +/* legend는 테두리 띠에 그려지므로 위 간격은 fieldset 사이 margin으로 준다 */ .builder-form fieldset + fieldset { - margin-top: 16px; + margin-top: 24px; } .builder-form legend { width: 100%; - margin-bottom: 18px; - font-size: 18px; - font-weight: 800; + margin-bottom: 14px; + font-family: var(--font-display); + font-size: 22px; + line-height: 1.3; } .builder-form legend span { - margin-right: 12px; - color: var(--blue-soft); - font-family: var(--font-mono); - font-size: 11px; + margin-right: 10px; + color: var(--on-blue-soft); + font-size: 16px; + font-variant-numeric: tabular-nums; } .choice-row { @@ -740,29 +833,30 @@ fieldset { display: flex; flex-direction: column; justify-content: center; - padding: 14px 16px; - border: 1px solid #625f5a; - border-radius: var(--radius-sm); - color: #d7d2ca; + padding: 14px 18px; + border: 2px solid rgb(var(--surface-rgb) / 45%); + border-radius: var(--radius); + color: var(--on-blue); + transition: background var(--dur-fast) ease, color var(--dur-fast) ease, border-color var(--dur-fast) ease; } .choice-row label > span small { - color: #9f9a92; - font-size: 11px; + color: var(--on-blue-soft); + font-size: var(--text-xs); } .choice-row input:checked + span { - border-color: var(--blue-soft); - background: var(--blue); - color: white; + border-color: var(--surface); + background: var(--surface); + color: var(--ink); } .choice-row input:checked + span small { - color: #dce4ff; + color: var(--ink-soft); } .choice-row input:focus-visible + span { - outline: 3px solid var(--blue-soft); + outline: 3px solid var(--surface); outline-offset: 3px; } @@ -773,9 +867,9 @@ fieldset { .field-label, .select-grid label { display: block; - color: #c9c3bb; - font-size: 12px; - font-weight: 700; + color: var(--on-blue-soft); + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .field-label { @@ -784,12 +878,13 @@ fieldset { .builder-form select { width: 100%; - min-height: 48px; - padding: 10px 38px 10px 12px; - border: 1px solid #625f5a; - border-radius: var(--radius-sm); - background: #2b2926; - color: white; + min-height: 50px; + padding: 10px 38px 10px 16px; + border: 2px solid var(--surface); + border-radius: var(--radius); + background: var(--surface); + color: var(--ink); + font-weight: var(--weight-strong); } .select-grid { @@ -805,83 +900,69 @@ fieldset { .set-options { margin-top: 18px; padding: 18px; - border: 1px solid #4d4a46; - background: #292724; + border: 1px solid rgb(var(--surface-rgb) / 30%); + border-radius: var(--radius); } .set-options > p { margin-bottom: 12px; - font-size: 12px; - font-weight: 750; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .set-options.is-disabled { - opacity: 0.48; + border-style: dashed; } -.field-help { - margin-top: 8px; - color: #9f9a92; - font-size: 11px; +/* 비활성은 투명도가 아니라 속 빈 칸으로 보인다 — 투명도는 "세트 구성" 라벨을 3.1:1로 떨어뜨렸다(게이트 실측) */ +.set-options select:disabled { + border-color: rgb(var(--surface-rgb) / 45%); + background: transparent; + color: var(--on-blue-soft); } +/* 영수증 카드 */ .builder-summary { min-width: 0; position: sticky; top: calc(var(--header-height) + 24px); - background: var(--paper-bright); + overflow: hidden; + border-radius: var(--radius); + background: var(--surface); color: var(--ink); - box-shadow: var(--shadow); - overflow: hidden; -} - -.summary-photo { - min-width: 0; - aspect-ratio: 4 / 3; - position: relative; - overflow: hidden; - background: var(--steel); -} - -.summary-photo img { - min-width: 0; - max-width: 100%; - width: 100%; - height: 100%; - object-fit: cover; -} - -.summary-stamp { - position: absolute; - top: 14px; - left: 14px; - padding: 6px 9px; - background: var(--blue); - color: white; - font-size: 10px; - font-weight: 750; + box-shadow: var(--shadow-card); } .summary-body { - padding: clamp(22px, 4vw, 34px); + padding: clamp(22px, 3.5vw, 32px); +} + +.summary-tag { + display: inline-flex; + align-items: center; + min-height: 32px; + padding: 4px 14px; + border-radius: var(--radius-pill); + background: var(--cheese); + color: var(--on-cheese); + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .summary-body h3 { - margin-top: 8px; - font-size: 35px; - font-weight: 880; - letter-spacing: -0.06em; + margin-top: 12px; + font-size: clamp(34px, 3vw, 44px); } #summary-lines { margin-top: 8px; color: var(--ink-soft); - font-size: 13px; + font-size: var(--text-sm); } .summary-list { - margin-top: 24px; - border-top: 1px solid var(--line); + margin-top: 22px; + border-top: 1px dashed var(--line-strong); } .summary-list div { @@ -889,8 +970,8 @@ fieldset { justify-content: space-between; gap: 20px; padding: 10px 0; - border-bottom: 1px solid var(--line); - font-size: 12px; + border-bottom: 1px dashed var(--line-strong); + font-size: var(--text-sm); } .summary-list dt { @@ -899,14 +980,14 @@ fieldset { .summary-list dd { text-align: right; - font-weight: 750; + font-weight: var(--weight-strong); } .allergen-note { margin-top: 18px; - padding: 14px; - border-left: 3px solid var(--cheese); - background: #fff4d5; + padding: 14px 16px; + border-radius: var(--radius); + background: var(--bg); } .allergen-note strong, @@ -916,19 +997,19 @@ fieldset { } .allergen-note strong { - font-size: 12px; + font-size: var(--text-xs); } .allergen-note p { margin-top: 3px; - font-size: 13px; - font-weight: 750; + font-size: var(--text-sm); + font-weight: var(--weight-strong); } .allergen-note small { margin-top: 6px; - color: #5d4b1e; - font-size: 10px; + color: var(--ink-soft); + font-size: var(--text-xs); line-height: 1.55; } @@ -937,24 +1018,28 @@ fieldset { align-items: end; justify-content: space-between; gap: 16px; - margin-top: 20px; + margin-top: 22px; } .summary-total span { color: var(--ink-faint); - font-size: 11px; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .summary-total strong { - font-family: var(--font-mono); - font-size: 23px; + font-family: var(--font-display); + font-weight: 400; + font-size: 32px; + line-height: 1; + font-variant-numeric: tabular-nums; } .truth-inline { - margin-top: 7px; + margin-top: 10px; color: var(--danger); - font-size: 10px; - font-weight: 700; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .button-full { @@ -962,34 +1047,20 @@ fieldset { margin-top: 16px; } -.button-ghost { - margin-top: 8px; - border-color: var(--line); - background: transparent; -} - +/* ── 공정: 진한 크림 띠 ───────────────────────────────── */ .process-section { - padding-block: 0; - background: var(--steel); + background: var(--bg-deep); } .process-grid { - width: 100%; - max-width: none; display: grid; - grid-template-columns: minmax(320px, 0.9fr) minmax(0, 1.1fr); -} - -.process-copy { - padding: var(--section) max(var(--gutter), calc((100vw - var(--wrap)) / 2)); -} - -.process-copy h2 { - font-size: clamp(40px, 4.8vw, 68px); + grid-template-columns: minmax(300px, 0.9fr) minmax(0, 1.1fr); + gap: clamp(28px, 5vw, 64px); + align-items: center; } .process-list { - margin: 52px 0 0; + margin: 40px 0 0; padding: 0; list-style: none; border-top: 1px solid var(--line-strong); @@ -997,33 +1068,44 @@ fieldset { .process-list li { display: grid; - grid-template-columns: 38px 1fr; - gap: 16px; - padding: 22px 0; + grid-template-columns: 44px 1fr; + gap: 14px; + padding: 20px 0; border-bottom: 1px solid var(--line-strong); } .process-list li > span { - color: var(--blue); - font-family: var(--font-mono); - font-size: 11px; + width: 40px; + height: 40px; + display: grid; + place-items: center; + border-radius: 50%; + background: var(--blue); + color: var(--on-blue); + font-family: var(--font-display); + font-size: 15px; + font-variant-numeric: tabular-nums; } .process-list strong { - font-size: 19px; + font-family: var(--font-display); + font-weight: 400; + font-size: 22px; } .process-list p { margin-top: 3px; color: var(--ink-soft); - font-size: 13px; + font-size: var(--text-sm); } .process-media { - min-height: 720px; position: relative; overflow: hidden; - background: #b5b9b8; + aspect-ratio: 4 / 3; + border-radius: var(--radius); + background: var(--surface); + box-shadow: var(--shadow-card); } .process-media img { @@ -1034,16 +1116,18 @@ fieldset { .process-media figcaption { position: absolute; - right: 16px; + left: 16px; bottom: 16px; - padding: 7px 10px; - background: var(--paper-bright); - font-size: 10px; - font-weight: 700; + padding: 7px 14px; + border-radius: var(--radius-pill); + background: var(--surface); + font-size: var(--text-xs); + font-weight: var(--weight-strong); } +/* ── 매장 ──────────────────────────────────────────────── */ .stores-section { - background: var(--paper-bright); + background: var(--surface); } .store-tools { @@ -1055,18 +1139,22 @@ fieldset { .search-box { display: flex; - border-bottom: 2px solid var(--ink); + align-items: center; + padding-right: 6px; + border: 2px solid var(--ink); + border-radius: var(--radius-pill); + background: var(--bg); } .search-box input { width: 100%; - min-height: 50px; - padding: 8px 12px; + min-height: 48px; + padding: 8px 20px; border: 0; - border-radius: 0; + border-radius: var(--radius-pill); background: transparent; - font-size: 18px; - font-weight: 700; + font-size: var(--text-lg); + font-weight: var(--weight-strong); } .search-box input:focus { @@ -1075,17 +1163,18 @@ fieldset { .search-box:focus-within { outline: 3px solid var(--focus); - outline-offset: 4px; + outline-offset: 3px; } .search-box button { min-width: 44px; min-height: 44px; - padding-inline: 10px; + padding-inline: 12px; border: 0; + border-radius: var(--radius-pill); background: transparent; - font-size: 12px; - font-weight: 700; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .region-filters { @@ -1098,21 +1187,24 @@ fieldset { .store-layout { display: grid; grid-template-columns: minmax(0, 0.92fr) minmax(360px, 1.08fr); - gap: 28px; + gap: 24px; } .store-list { - border-top: 1px solid var(--ink); + display: grid; + gap: 12px; } .store-card { - padding: 24px 0; - border-bottom: 1px solid var(--line); - transition: padding 160ms ease, background 160ms ease; + padding: 22px 24px; + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--bg); + transition: background var(--dur-fast) ease, border-color var(--dur-fast) ease; } .store-card.is-selected { - padding-inline: 20px; + border-color: var(--blue); background: var(--blue-pale); } @@ -1122,8 +1214,8 @@ fieldset { justify-content: space-between; gap: 16px; color: var(--ink-faint); - font-size: 10px; - font-weight: 750; + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .store-state { @@ -1134,10 +1226,10 @@ fieldset { .store-state::before { content: ""; - width: 7px; - height: 7px; + width: 8px; + height: 8px; border-radius: 50%; - background: currentColor; + background: currentcolor; } .state-open { @@ -1149,38 +1241,37 @@ fieldset { } .state-pause { - color: #70571a; + color: var(--pause); } .store-card h3 { - margin-top: 12px; - font-size: 29px; - font-weight: 850; - letter-spacing: -0.055em; + margin-top: 10px; + font-size: 30px; } .store-card > p { margin-top: 3px; color: var(--ink-soft); - font-size: 12px; + font-size: var(--text-xs); } .store-card dl { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; - margin-top: 18px; + margin-top: 16px; } .store-card dt { color: var(--ink-faint); - font-size: 10px; + font-size: var(--text-xs); } .store-card dd { margin-top: 3px; - font-size: 12px; - font-weight: 700; + font-size: var(--text-sm); + font-weight: var(--weight-strong); + font-variant-numeric: tabular-nums; } .store-card ul { @@ -1193,26 +1284,28 @@ fieldset { } .store-card li { - padding: 5px 8px; - border: 1px solid var(--line); - font-size: 10px; - font-weight: 700; + padding: 5px 12px; + border: 1px solid var(--line-strong); + border-radius: var(--radius-pill); + font-size: var(--text-xs); + font-weight: var(--weight-strong); } .store-map { - min-height: 610px; + min-height: 560px; position: sticky; top: calc(var(--header-height) + 24px); overflow: hidden; border: 1px solid var(--line); - background: #e2e0db; + border-radius: var(--radius); + background: var(--bg-deep); } .map-grid { position: absolute; inset: 0; - opacity: 0.48; - background-image: linear-gradient(#b9b6af 1px, transparent 1px), linear-gradient(90deg, #b9b6af 1px, transparent 1px); + opacity: 0.5; + background-image: linear-gradient(var(--line-strong) 1px, transparent 1px), linear-gradient(90deg, var(--line-strong) 1px, transparent 1px); background-size: 54px 54px; } @@ -1233,21 +1326,23 @@ fieldset { display: grid; place-items: center; padding-inline: 8px; - border: 3px solid white; + border: 3px solid var(--surface); border-radius: 50%; background: var(--ink); - color: white; - box-shadow: 0 5px 16px rgb(33 31 28 / 24%); + color: var(--on-ink); + box-shadow: var(--shadow-card); + transition: transform var(--dur-fast) ease, background var(--dur-fast) ease; } .map-pin span { position: absolute; top: 56px; - padding: 5px 9px; - background: white; + padding: 5px 10px; + border-radius: var(--radius-pill); + background: var(--surface); color: var(--ink); - font-size: 10px; - font-weight: 750; + font-size: var(--text-xs); + font-weight: var(--weight-strong); white-space: nowrap; } @@ -1276,10 +1371,11 @@ fieldset { right: 14px; bottom: 14px; left: 14px; - padding: 8px 10px; - background: rgb(255 253 248 / 90%); - font-size: 10px; - font-weight: 700; + padding: 8px 12px; + border-radius: var(--radius-pill); + background: var(--surface); + font-size: var(--text-xs); + font-weight: var(--weight-strong); text-align: center; } @@ -1290,119 +1386,95 @@ fieldset { .store-empty p { margin: -10px 0 18px; color: var(--ink-soft); - font-size: 13px; + font-size: var(--text-sm); } +/* ── 가맹 안내 띠: 먹 ─────────────────────────────────── */ .franchise-callout { + padding-block: var(--section); + background: var(--ink); + color: var(--on-ink); +} + +.franchise-row { display: grid; - grid-template-columns: 1.08fr 0.92fr; - background: var(--blue); - color: white; -} - -.franchise-image { - min-height: 650px; - overflow: hidden; - background: var(--steel); -} - -.franchise-image img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.franchise-copy { - align-self: center; - padding: var(--section) var(--gutter); -} - -.franchise-copy .section-kicker { - color: #c8d3ff; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: clamp(24px, 5vw, 64px); } .franchise-copy h2 { - max-width: 520px; + max-width: 16em; } -.franchise-copy > p:not(.section-kicker) { - max-width: 470px; - margin-top: 28px; - color: #e4e9ff; - font-size: 16px; -} - -.button-light { - margin-top: 34px; - background: white; - color: var(--blue-dark); -} - -.franchise-copy small { - display: block; - margin-top: 16px; - color: #c8d3ff; - font-size: 10px; +.franchise-copy > p { + max-width: 34em; + margin-top: 22px; + color: var(--on-ink-soft); } +/* ── 푸터: 다열 색인 ─────────────────────────────────── */ .site-foot { - padding: 72px 0 36px; - background: #141311; - color: #ddd8d0; + padding: 64px 0 40px; + border-top: 2px solid var(--ink); + background: var(--bg); } .foot-grid { display: grid; - grid-template-columns: 1.4fr 0.7fr 1fr 1.3fr; - gap: 44px; + grid-template-columns: 1.3fr 0.8fr 1fr 1.6fr; + gap: clamp(28px, 4vw, 48px); } -.brand-foot { - color: white; +.foot-brand > p { + margin-top: 14px; + color: var(--ink-soft); } -.foot-grid > div:first-child > p { - margin-top: 15px; - color: #8f8a83; -} - -.foot-grid > div:not(:first-child) { +.foot-grid > div:not(.foot-brand) { display: flex; flex-direction: column; align-items: flex-start; - gap: 9px; - font-size: 12px; + gap: 6px; + font-size: var(--text-sm); } .foot-grid strong { - margin-bottom: 7px; - color: white; - font-size: 12px; + margin-bottom: 6px; + font-family: var(--font-display); + font-weight: 400; + font-size: 18px; } .foot-grid a:not(.brand) { - min-width: 24px; - min-height: 24px; + min-height: 28px; display: inline-flex; align-items: center; - justify-content: center; + font-weight: var(--weight-strong); } -.foot-grid a:hover { - color: white; +.foot-grid a:not(.brand):hover { + color: var(--blue); + text-decoration: underline; } -.foot-truth { - padding-left: 20px; - border-left: 1px solid #3b3935; +.foot-truth p { + color: var(--ink-soft); } -.foot-truth .mono { +.foot-mark { margin-top: 12px; - color: #aaa59d; - font-size: 10px; + font-family: var(--font-display); + font-size: 18px; + color: var(--ink); } +/* ── 모바일 하단 고정 행동 바 ─────────────────────────── */ +.action-bar { + display: none; +} + +/* ── 이미지 폴백 ───────────────────────────────────────── */ .image-shell { position: relative; } @@ -1413,10 +1485,10 @@ fieldset { display: none; place-items: center; padding: 24px; - background: var(--steel); + background: var(--bg-deep); color: var(--ink-soft); - font-size: 12px; - font-weight: 700; + font-size: var(--text-xs); + font-weight: var(--weight-strong); text-align: center; } @@ -1432,6 +1504,14 @@ fieldset { display: none !important; } +/* ── 반응형 ───────────────────────────────────────────── */ +@media (min-width: 821px) { + .site-head { + position: sticky; + top: 0; + } +} + @media (max-width: 1040px) { .head-row { grid-template-columns: auto 1fr; @@ -1446,102 +1526,109 @@ fieldset { } .hero { - grid-template-columns: 0.9fr 1.1fr; - } - - .hero-copy { - padding-left: var(--gutter); - } - - .hero-order { - right: 20px; - } - - .menu-item-featured { - grid-column: span 6; - } - - .menu-item-graphic, - .menu-item-blue { - grid-column: span 3; + grid-template-columns: minmax(0, 0.55fr) minmax(0, 0.45fr); } .builder-layout { - grid-template-columns: minmax(0, 1fr) 380px; + grid-template-columns: minmax(0, 1fr) 360px; gap: 30px; } - .process-copy { - padding-inline: var(--gutter); - } - .foot-grid { grid-template-columns: 1fr 1fr; } } @media (max-width: 820px) { - .head-row { - grid-template-columns: 1fr; - gap: 0; - padding: 12px 0 9px; + body { + padding-bottom: calc(var(--action-bar-height) + var(--safe-bottom)); } - .brand { - justify-self: start; + html { + scroll-padding-top: 16px; } .head-nav { - width: 100%; - justify-self: stretch; - margin-top: 9px; - padding-top: 8px; - border-top: 1px solid var(--line); - overflow-x: auto; - white-space: nowrap; + display: none; } .hero { grid-template-columns: 1fr; + align-items: start; + min-height: 0; } .hero-copy { - max-width: none; - padding: 72px var(--gutter) 48px; + padding: 40px 0 28px; } .hero-media { - min-height: 560px; + min-height: clamp(380px, 110vw, 520px); + margin-bottom: 24px; } - .hero-media img { - object-position: 57% center; + /* 좁은 화면에서는 조각을 뷰포트 안에 둔다 — 넘치는 조각은 감사 도구가 가로 오버플로로 센다(실측) */ + .piece-main { + right: 0; + top: 0; + width: 84%; } - .hero-order { - bottom: 20px; + .piece-main img { + object-position: 58% 70%; } - .service-strip { - padding-top: 22px; + .piece-press { + left: 0; + bottom: 6%; + width: 50%; } - .service-row { - grid-template-columns: 1fr 1fr; - justify-content: start; + .piece-single { + right: 4%; + bottom: 0; + width: 30%; + } + + .piece-tag { + left: 0; + top: 10%; + font-size: 17px; + padding: 10px 18px; + } + + .piece-tag strong { + font-size: 22px; + } + + .piece-sticker { + right: 10%; + top: -2%; + width: 92px; + height: 92px; + font-size: 16px; + } + + .piece-note { + bottom: -12px; + } + + .marquee-list li { + font-size: 19px; + padding-block: 14px; } .split-head, .builder-layout, .process-grid, .store-layout, - .franchise-callout { + .franchise-row { grid-template-columns: 1fr; } .split-head { align-items: start; - gap: 22px; + gap: 14px; } .menu-item-featured, @@ -1556,125 +1643,75 @@ fieldset { .builder-summary { position: static; - display: grid; - grid-template-columns: 0.8fr 1.2fr; - } - - .summary-photo { - min-height: 0; - aspect-ratio: auto; - height: 100%; - } - - .summary-photo img { - position: absolute; - inset: 0; - } - - .process-media { - min-height: 560px; } .store-map { position: relative; - min-height: 440px; - grid-row: auto; + top: 0; + min-height: 420px; } - .franchise-image { - min-height: 520px; + .action-bar { + position: fixed; + z-index: 30; + right: 0; + bottom: 0; + left: 0; + display: grid; + grid-template-columns: 1fr 1.4fr 1fr 1fr; + gap: 6px; + min-height: var(--action-bar-height); + padding: 8px var(--gutter) calc(8px + var(--safe-bottom)); + border-top: 1px solid var(--line); + background: var(--surface); } - .franchise-copy { - padding-inline: var(--gutter); + .action-bar a { + min-height: 48px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 8px; + border-radius: var(--radius-pill); + font-size: var(--text-sm); + font-weight: var(--weight-strong); + white-space: nowrap; + } + + .action-bar-primary { + background: var(--blue); + color: var(--on-blue); } } @media (max-width: 620px) { - .truth-bar { - font-size: 10px; - } - - .head-nav { - gap: 20px; - padding-bottom: 3px; - font-size: 12px; - } - .hero h1 { - font-size: clamp(51px, 16vw, 70px); + font-size: clamp(34px, 10.5vw, 46px); } .hero-copy { - padding: 44px var(--gutter) 28px; + padding-top: 32px; } - .hero-lead { - margin-top: 20px; + .hero-actions .button { + width: 100%; } - .hero-actions { - margin-top: 24px; - } - - .hero-facts, - .hero-order { - display: none; - } - - .hero-media { - min-height: 360px; - } - - .hero-media picture { - position: absolute; - inset: 0; - } - - .hero-media img { - object-position: center bottom; - } - - .hero-media figcaption { - flex-direction: column; - gap: 2px; - } - - .hero-order { - right: 14px; - bottom: 14px; - left: 14px; - width: auto; - } - - .service-row { - font-size: 11px; - } - - .section h2, - .franchise-callout h2 { - font-size: clamp(38px, 12vw, 54px); + .marquee-toggle { + margin-left: var(--gutter); + padding-inline: 12px; } .menu-grid { display: block; } - .menu-item { - border-right: 0; + .menu-item + .menu-item { + margin-top: 12px; } .menu-item-small .menu-body { - min-height: 210px; - } - - .builder-summary { - display: block; - } - - .summary-photo { - height: auto; - aspect-ratio: 4 / 3; + min-height: 0; } .choice-row, @@ -1691,40 +1728,21 @@ fieldset { } .store-map { - min-height: 300px; - } - - .process-media, - .franchise-image { - min-height: 420px; + min-height: 320px; } .foot-grid { grid-template-columns: 1fr; } - - .foot-truth { - padding: 20px 0 0; - border-top: 1px solid #3b3935; - border-left: 0; - } } @media (max-width: 380px) { .brand { - font-size: 18px; + font-size: 21px; } - .hero-copy { - padding-top: 58px; - } - - .hero-facts { - grid-template-columns: 1fr; - } - - .hero-media { - min-height: 340px; + .piece-tag { + font-size: 15px; } .summary-total { @@ -1735,8 +1753,19 @@ fieldset { .store-card dl { grid-template-columns: 1fr; } + + .action-bar { + grid-template-columns: 1fr 1.3fr 1fr 1fr; + gap: 4px; + } + + .action-bar a { + font-size: var(--text-xs); + padding: 0 4px; + } } +/* ── 감소 모션: 조각은 즉시, 마퀴는 정지한 목록 ─────────── */ @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; @@ -1750,80 +1779,37 @@ fieldset { scroll-behavior: auto !important; transition-duration: 0.01ms !important; } -} -@media (min-width: 821px) { - .hero { - grid-template-rows: minmax(0, 1fr) auto; - align-items: stretch; + .piece { + animation: none; } - .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; + .marquee-track { 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; + animation: none; } - .service-row { - grid-template-columns: minmax(0, 1fr); + .marquee-list { + flex-wrap: wrap; + padding-inline: var(--gutter); } - .service-row p { - min-width: 0; - overflow-wrap: anywhere; + .marquee-list[aria-hidden="true"], + .marquee-toggle { + display: none; } } @media print { .truth-bar, .site-head, - .hero-order, + .marquee, .menu-toolbar, .builder-form, .store-tools, .store-map, .franchise-callout, + .action-bar, .button, button { display: none !important; @@ -1838,14 +1824,13 @@ fieldset { .builder-summary { position: static; box-shadow: none; - border: 1px solid #777; + border: 1px solid var(--line-strong); } } -/* ── 2026-08-30: 초광폭은 확장이 아니라 독립된 구성 상태다 ─────────── */ +/* ── 초광폭: 확장이 아니라 별도 상태. audit.mjs 2560px 계약(히어로 2160·카피 880·무대 1100·과정 1680) ── */ @media (min-width: 120rem) { .site-head .wrap, - .service-strip .wrap, .section > .wrap, .site-foot .wrap { max-width: var(--workbench-wide); @@ -1853,50 +1838,26 @@ fieldset { .hero { width: min(100%, var(--hero-wide)); - min-height: clamp(43rem, 48vw, 54rem); - grid-template-columns: minmax(0, 1fr) minmax(42rem, 1.14fr); + min-height: clamp(43rem, 46vw, 54rem); + grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); margin-inline: auto; - border-inline: 1px solid var(--line); + padding-inline: clamp(3rem, 4vw, 5rem); } .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; + justify-self: start; } .hero-media { width: min(100%, var(--media-wide)); - min-height: clamp(43rem, 48vw, 54rem); + min-height: clamp(43rem, 44vw, 52rem); 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)); + .piece-main { + right: -6%; + width: 92%; } .menu-grid, @@ -1910,38 +1871,19 @@ fieldset { 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 { + width: min(100%, var(--stage-wide)); max-width: var(--stage-wide); + margin-inline: auto; + grid-template-columns: minmax(0, 0.98fr) minmax(0, 1.02fr); + gap: 0; } .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); + padding-inline: 4.5rem; } - .process-media, - .franchise-image { - min-height: clamp(42rem, 43vw, 52rem); - } - - .franchise-copy { - max-width: 43rem; - padding-inline: clamp(4rem, 5vw, 6rem); + .franchise-row { + max-width: var(--workbench-wide); } } diff --git a/apps/site/public/work/griddle-club/styles/tokens.css b/apps/site/public/work/griddle-club/styles/tokens.css index f35af66..a990dbb 100644 --- a/apps/site/public/work/griddle-club/styles/tokens.css +++ b/apps/site/public/work/griddle-club/styles/tokens.css @@ -1,32 +1,76 @@ +/* 철판클럽 토큰 — 2026-10-06 재작업(outputs/rework-2026-10-06/plan.md 배정값). + 색·서체·radius·모션 값은 이 파일에만 둔다. site.css·franchise.css는 var()만 쓴다. */ :root { color-scheme: light; - --paper: #f4f1e9; - --paper-bright: #fffdf8; - --ink: #211f1c; - --ink-soft: #56514a; - --ink-faint: #625d56; - --line: #c9c2b7; - --line-strong: #8a8379; + + /* 바탕 — 유채 크림. 쟁반 위 유산지의 색이다. 섹션 교대는 밝은 크림(surface)과 진한 크림(bg-deep)으로만 한다 */ + --bg: #fbe9c8; + --bg-deep: #f4d9a3; + --surface: #fff7e8; + --surface-rgb: 255 247 232; + + /* 먹 — 따뜻한 갈색 먹. 패티 가장자리의 색. 크림 위 대비 14.3:1 */ + --ink: #26190f; + --ink-soft: #5c4a39; /* 7.0:1 */ + --ink-faint: #6b5744; /* 5.7:1 — 보조 라벨 하한 */ + --ink-rgb: 38 25 15; + --on-ink: #fbe9c8; + --on-ink-soft: #e9d2a8; /* 먹 위 11.6:1 */ + --on-ink-faint: #c9b79b; /* 먹 위 8.7:1 */ + + /* 선 — 크림보다 한 단 진한 모래색 */ + --line: #e2c99e; + --line-strong: #b89a6c; + + /* 강조 — 코발트(유지). 쟁반과 클립의 색. 크림 위 5.6:1, 흰 글자 6.7:1 */ --blue: #2147e7; --blue-dark: #1533ac; - --blue-soft: #8ea5ff; - --blue-pale: #dce4ff; - --steel: #d9dcda; + --blue-pale: #dbe3ff; + --on-blue: #ffffff; + --on-blue-soft: #dbe3ff; /* 코발트 위 5.3:1 */ + + /* 치즈 — 스티커 한 장에만 쓴다 */ --cheese: #ffc642; - --danger: #b33124; - --success: #17683b; + --on-cheese: #26190f; /* 10.9:1 */ + + /* 상태 */ + --danger: #b33124; /* 크림 위 5.2:1 */ + --success: #17683b; /* 5.7:1 */ + --pause: #7a5a10; /* 5.3:1 */ --focus: #2147e7; - --font-sans: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; - --font-mono: "IBM Plex Mono", "SFMono-Regular", Consolas, monospace; + + /* 서체 — 표제 Jua(한 굵기), 본문 Gothic A1. 둘 다 Google Fonts OFL, 한글 슬라이스 서브셋 */ + --font-display: "Jua", "Gothic A1", "Apple SD Gothic Neo", sans-serif; + --font-body: "Gothic A1", "Apple SD Gothic Neo", "Malgun Gothic", sans-serif; + --weight-body: 500; + --weight-strong: 700; + --weight-heavy: 800; + + /* 타입 스케일 — 본문 16, 표제는 vw 보간 */ + --text-xs: 13px; + --text-sm: 14px; + --text-md: 16px; + --text-lg: clamp(17px, 1.4vw, 19px); + --text-h3: clamp(24px, 2.2vw, 30px); + --text-h2: clamp(36px, 4.4vw, 60px); + --text-h1: clamp(46px, 6.2vw, 92px); + --leading-body: 1.7; + --leading-tight: 1.2; + --leading-display: 1.14; + --tracking-display: -0.01em; + + /* 폭 */ --wrap: 1180px; - /* 초광폭에서도 히어로와 후속 정보 표면은 같은 폭을 쓰지 않는다. */ + --measure: 36em; --hero-wide: 2160px; --stage-wide: 1680px; --workbench-wide: 1480px; - --copy-wide: 900px; - --media-wide: 1120px; + --copy-wide: 880px; + --media-wide: 1100px; --gutter: clamp(18px, 4vw, 52px); - --section: clamp(76px, 10vw, 144px); + --section: clamp(72px, 9vw, 128px); + + /* 간격 */ --space-1: 4px; --space-2: 8px; --space-3: 12px; @@ -35,17 +79,32 @@ --space-6: 32px; --space-7: 48px; --space-8: 72px; - --radius-sm: 4px; - --radius: 10px; - --radius-lg: 18px; - --shadow: 0 18px 50px rgb(33 31 28 / 10%); - --header-height: 78px; + + /* radius — 알약과 16px, 둘뿐이다 */ + --radius: 16px; + --radius-pill: 999px; + + /* 깊이 — 그림자는 콜라주 조각에만 */ + --shadow-piece: 0 24px 60px rgb(38 25 15 / 18%); + --shadow-card: 0 10px 30px rgb(38 25 15 / 10%); + + /* 모션 — 스태거 등장(히어로 조각) + 마퀴. 감소 모션에서는 전부 0 */ + --dur: 520ms; + --dur-fast: 180ms; + --stagger: 90ms; + --shift: 28px; + --ease-out: cubic-bezier(0.2, 0.7, 0.2, 1); + --marquee-dur: 32s; + + --header-height: 72px; + --action-bar-height: 64px; + --safe-bottom: env(safe-area-inset-bottom, 0px); } @media (max-width: 720px) { :root { - --header-height: 64px; - --section: 72px; + --header-height: 60px; + --section: 64px; } } @@ -60,3 +119,13 @@ --wrap: 1400px; } } + +@media (prefers-reduced-motion: reduce) { + :root { + --dur: 0.01ms; + --dur-fast: 0.01ms; + --stagger: 0ms; + --shift: 0px; + --marquee-dur: 0.01ms; + } +} diff --git a/apps/site/public/work/hannam-jari/admin/index.html b/apps/site/public/work/hannam-jari/admin/index.html index e6f5704..01f3ef6 100644 --- a/apps/site/public/work/hannam-jari/admin/index.html +++ b/apps/site/public/work/hannam-jari/admin/index.html @@ -3,7 +3,7 @@ - + @@ -13,14 +13,17 @@ 한남자리 운영 안내 — 매물 확인 - - - + + + + + + -
      안내 · 예시 자료 · 사무소, 매물, 고객과 기록은 실제 정보가 아니며 광고·연락·저장은 연결되지 않습니다
      +
      예시 자료입니다. 사무소, 매물, 고객과 기록은 실제 정보가 아니며 광고, 연락, 저장은 연결되지 않습니다.
      -
      -
      - - - - RFS 실물 파티클 뷰 — 서버랙을 통과한 공기가 데워져 위로 오르는 측면 단면 - - - - - - - - - - - -
      - -
      -

      - 메시를 다시 만들지 않는다 -

      -

      - 설비를 옮기면 유동이 그 자리에서 바뀐다. 데이터센터 열관리를 위한 실시간 GPU 유체 - 시뮬레이터. -

      - -
      -

      운전 장면

      - - - +
      +
      +
      +

      데이터센터 기류를 실시간으로 계산하는 GPU 유체 시뮬레이터

      +

      + 서버랙 부하와 항온항습기 급기량을 움직여 보세요. 메시를 다시 만들지 않고 같은 화면에서 기류가 + 바뀝니다. +

      -
      -
      -
      - - 70% -
      - +
      +
      + + + + RFS 실물 파티클 뷰. 서버랙을 통과한 공기가 데워져 위로 오르는 측면 단면 + + + + + + + + +
      -
      -
      - - 65% + +
      +
      + 저온 + 중간 + 고온
      -
      -
      -
      -

      이 화면에서 보는 변화

      -

      냉기와 발열이 맞서는 기준 상태입니다.

      +
      +
      +

      운전 장면

      +
      + + + +
      -
      -
      -
      장면
      -
      기준 운전
      + +
      +
      +
      + + 70% +
      +
      -
      -
      먼저 볼 곳
      -
      랙 배면
      +
      +
      + + 65% +
      +
      -
      -
      다음 조작
      -
      부하와 급기를 따로 움직여 비교
      -
      -
      +
      + +
      +

      판독

      +

      냉기와 발열이 균형을 이룬 기준 상태입니다.

      +
      +
      +
      장면
      +
      기준 운전
      +
      +
      +
      먼저 볼 곳
      +
      랙 배면
      +
      +
      +
      다음 조작
      +
      부하와 급기를 따로 움직여 비교
      +
      +
      +

      - 이 페이지의 조작 화면은 2D 축소 표현입니다. 슬라이더를 움직이거나 화면을 - 문지르면 유동 변화가 바로 보입니다. RFS 본 제품의 Unity 6.4 기반 3D δ-SPH 사양은 이 데모와 - 별도로 확인해야 합니다. + 이 조작 화면은 2D 축소 표현입니다. 화면을 문지르면 유동이 흔들립니다. RFS 본 제품의 + Unity 6.4 기반 3D δ-SPH 사양은 아래에서 따로 확인하세요.

      -
      +
      -

      - 해가 나올 즈음에는
      이미 다른 결정을 내린 뒤다 -

      +

      기존 CFD는 결과가 나오기까지 몇 시간씩 걸립니다

      - 범용 CFD 도구는 시뮬레이션 한 건에 수 시간에서 수 일이 걸린다. 랙 한 대를 옮길 때마다 - 메시를 다시 만들고 처음부터 다시 풀어야 하므로, 운영 중에 답을 얻는 용도로는 쓰이지 - 않는다. 그래서 현장의 배치 변경은 대개 계산이 아니라 경험으로 결정된다. + 범용 CFD 도구는 시뮬레이션 한 건에 수 시간에서 수 일이 걸립니다. 랙 한 대를 옮길 때마다 메시를 + 다시 만들고 처음부터 다시 계산해야 해서, 운영 중에 바로 결과를 확인하는 용도로는 쓰기 + 어렵습니다. 그래서 현장에서는 배치 변경을 대개 계산보다 경험으로 결정합니다.

      - RFS 는 정확도의 상한을 낮추는 대신 회신 시간을 바꾼 도구다. - 발열량을 올리고, 급기 밸브를 조이고, 랙을 한 칸 옮기는 조작이 같은 화면 안에서 - 유동에 반영된다. 검증할 것이 "결과 파일"이 아니라 "지금 보고 있는 장면"이 된다. + RFS는 정확도 상한을 일부 낮추는 대신 계산 시간을 줄인 도구입니다. 발열량을 + 올리거나 급기 밸브를 조이거나 랙을 한 칸 옮기면 같은 화면에서 기류 변화가 바로 반영됩니다. + 결과 파일을 기다리지 않고 화면을 보면서 검토할 수 있습니다.

      범용 CFD 도구
      -
      배치를 바꾸면 메시 재생성 → 재계산. 한 건에 수 시간~수 일
      +
      배치를 바꾸면 메시를 다시 만들고 다시 계산합니다. 한 건에 수 시간에서 수 일
      RFS
      -
      배치·발열·제어 변경이 같은 세션 안에서 유동에 반영된다
      +
      배치, 발열, 제어를 바꾸면 같은 세션에서 바로 반영됩니다
      @@ -217,169 +214,225 @@
      -
      +
      -

      유체 엔진

      -

      GPU Compute Shader 위의 δ-SPH

      +

      격자를 다시 만들지 않는 GPU δ-SPH 솔버입니다

      - 격자를 깔지 않는다. 공기를 입자로 놓고 이웃 입자끼리 밀도·압력·열을 주고받게 한다. - 벽이 움직여도 격자를 다시 만들 일이 없다 — 실시간이 가능한 이유가 여기에 있다. + 격자 대신 공기를 입자로 다루고, 이웃 입자끼리 밀도, 압력, 열을 주고받도록 Compute Shader로 + 계산합니다. 벽이나 장비가 움직여도 격자를 다시 만들 필요가 없어 실시간 계산이 가능합니다.

      • 보간 커널

        Wendland C2

        -

        Poly6·Spiky 혼용을 걷어내고 단일 경로로 통일했다

        +

        Poly6와 Spiky 혼용을 없애고 단일 커널로 통일했습니다

      • 상태방정식

        Tait

        -

        약압축성 가정. 압력 반복 없이 밀도에서 압력을 직접 얻는다

        +

        약압축성을 가정해 반복 계산 없이 밀도에서 압력을 바로 구합니다

      • 벽면 경계

        Adami Ghost Particle

        -

        엣지·코너까지 덮는다. 경계에서 입자가 새던 결함을 해소한 방식

        +

        모서리와 코너까지 처리해 경계에서 입자가 새는 문제를 막습니다

      • 시간 적분

        CFL 적응 서브스텝

        -

        유속이 빨라지면 스텝을 자동으로 잘게 쪼갠다

        +

        유속이 빨라지면 시간 간격을 자동으로 줄입니다

      • 배속

        -

        0.1× — 10×

        -

        배속을 바꿔도 같은 물리 결과가 나오도록 시간 불변을 보장한다

        +

        0.1배에서 10배

        +

        배속을 바꿔도 같은 물리 결과가 나오도록 계산합니다

      • -

        열·습도

        +

        열과 습도

        입자 간 전도 + 부력

        -

        습공기 밀도 보정을 포함한다

        +

        습공기 밀도 보정을 포함합니다

      • 주기 경계

        -

        PBC · 최소 이미지 규약

        -

        셀 래핑으로 무한 반복 공간을 다룬다

        +

        PBC, 최소 이미지 규약

        +

        셀 래핑으로 무한 반복 공간을 처리합니다

      • 이웃 탐색

        공간 해시 그리드

        -

        SpatialHash 커널 1개가 전담한다

        +

        SpatialHash 커널 1개로 처리합니다

        +
      • +
      • +

        데이터 배치

        +

        SoA

        +

        계산에 필요한 상태를 버퍼별로 분리해 다룹니다

      -
      -
      -

      01

      -

      GPU 커널

      -

      Compute

      -

      공간 해시와 유동 계산을 GPU 작업으로 나눕니다.

      -
      -
      -

      02

      -

      연산 모델

      -

      δ-SPH

      -

      운전 조건에 따른 유동 변화를 계산합니다.

      -
      -
      -

      03

      -

      데이터 배치

      -

      SoA

      -

      계산에 필요한 상태를 분리해 다룹니다.

      -
      -
      -

      04

      -

      검증 흐름

      -

      Edit · Play

      -

      장면과 운전 조건을 나눠 확인합니다.

      -
      -
      +

      애플리케이션부터 외부 통신까지 5계층입니다

      +
        +
      1. +

        L1

        +

        애플리케이션

        +

        RFS Editor, Simulation3D, AI Advisor, 리플레이

        +
      2. +
      3. +

        L2

        +

        파이프라인과 컨트롤러

        +

        SimulationPipeline → 스테이지 10개 → 컨트롤러 26개

        +
      4. +
      5. +

        L3

        +

        GPU 컴퓨트

        +

        + SPHCore 8, DomainExt 14, SpatialHash 1, TempGrid 8, ObjectGrid 7, Integration 5, Scatter 4, + Trail 2, GlobalSDF 3, Coil 1 +

        +
      6. +
      7. +

        L4

        +

        데이터 버퍼

        +

        SoA 버퍼 6개, ParticleData 20B, 충돌 메시 데이터

        +
      8. +
      9. +

        L5

        +

        외부 통신

        +

        Socket.IO v2 (RFS-XD 관제 연동), REST API, CSV 내보내기

        +
      10. +
      -
      +
      -

      전산실에 있는 물건을 그대로 안다

      +

      전산실 장비 5종을 제어 로직까지 재현합니다

      - 유동만 푸는 것으로는 답이 안 나온다. 열을 만드는 것과 식히는 것이 각자의 제어 논리를 - 가지고 장 안에 들어가 있어야 배치 변경이 의미를 갖는다. + 기류만 계산해서는 배치 변경 효과를 판단하기 어렵습니다. 열을 내는 장비와 식히는 장비가 각자의 제어 + 로직대로 움직여야 실제 운전에 가까운 결과가 나옵니다.

      -

      항온항습기 FWU

      +

      항온항습기 FWU

      - PID 자동 제어로 목표 온도를 좇는다. 열교환은 ε-NTU 로 계산하고, - 프리쿨링·쿨링 밸브와 댐퍼 모드를 각각 조작할 수 있다. + PID 자동 제어로 목표 온도를 유지합니다. 열교환은 ε-NTU 방식으로 계산하고, 프리쿨링과 쿨링 + 밸브, 댐퍼 모드를 각각 조작할 수 있습니다.

      서버랙

      - CFM 기반으로 통과 공기에 열을 싣는다. 동적 전력 프로파일 5종을 갖고 있고, - 장애 상황을 만들어 그때 열이 어디로 가는지 볼 수 있다. + CFM(풍량) 기준으로 랙을 지나는 공기에 열을 더합니다. 동적 전력 프로파일 5종을 제공하고, 장애 + 상황을 재현해 열이 어디로 퍼지는지 확인할 수 있습니다.

      -

      팬 · 댐퍼 · 냉각코일

      +

      팬, 댐퍼, 냉각코일

      - 듀얼 원뿔 존 모델로 GPU 파티클 물리에 직접 개입한다. - 후처리로 그린 화살표가 아니라 실제로 입자를 민다. + 듀얼 원뿔 존 모델로 GPU 입자 계산에 직접 힘을 가합니다. 화면에 덧그린 화살표가 아니라 실제로 + 입자의 움직임을 바꿉니다.

      온도계

      - 물리 기반 센서 응답 특성을 갖는다. 값이 즉시 튀지 않고 실제 계기처럼 따라온다. - Cool Zone 과 Hot Zone 을 자동으로 분류한다. + 실제 센서처럼 응답 지연이 있어 값이 한 번에 튀지 않습니다. Cool Zone과 Hot Zone을 자동으로 + 분류합니다.

      -

      급기구 · 배기구

      +

      급기구와 배기구

      - CFD inlet / outlet 패턴을 따른다. 경계에 걸린 파티클을 자동으로 재배치해 - 유입·유출이 끊기지 않게 한다. + CFD의 inlet/outlet 조건을 따릅니다. 경계에 걸린 입자를 자동으로 재배치해 유입과 유출이 끊기지 + 않게 합니다.

      + +
      +
      +

      시뮬레이션을 멈추지 않고 편집합니다

      +

      + RFS Editor는 Unity UI Toolkit으로 만든 런타임 편집 환경입니다. Edit 모드와 Play 모드를 나눠 + 장면과 운전 조건을 따로 확인합니다. +

      +
      +
      +
      +
      +
      오브젝트 편집
      +
      이동, 회전, 스케일 핸들, 다중 선택, Undo / Redo, 드래그 앤 드롭 배치
      +
      +
      +
      속성 패널
      +
      실시간 속성 편집, 관제 시스템의 XD 태그 바인딩
      +
      +
      +
      리플레이
      +
      CSV 또는 XD API 데이터로 시계열을 재생하고, 실측 대비 정확도 리포트를 생성
      +
      +
      +
      AI Advisor
      +
      26개 규칙의 Rule Engine과 LLM 프로바이더 4종(Anthropic, OpenAI, Gemini, 로컬 Ollama) 연동
      +
      +
      +
      모니터링
      +
      PUE, COP, 전력, CO₂ 대시보드, MetricsHUD 7섹션 26메트릭
      +
      +
      +
      프로젝트
      +
      생성, 저장, 불러오기, 다중 씬, glTF / GLB 3D 모델 임포트
      +
      +
      + +
      +

      단축키

      +
        +
      • Space 일시정지와 재개
      • +
      • H 히트맵
      • +
      • XYZ 슬라이스 축
      • +
      • F3 성능 통계
      • +
      • Tab UI 접기
      • +
      +
      +
      +
      -
      +
      -

      보이는 방식

      -

      읽는 방법을 네 가지로 나눈다

      +

      파티클, 트레일, 히트맵, 벡터 필드 네 가지로 결과를 봅니다

      1. 파티클

        -

        온도로 색을 매핑하고 발광과 LOD 를 건다. 공기가 어디서 데워지는지 그대로 보인다.

        +

        온도에 따라 입자 색을 바꾸고 발광과 LOD를 적용합니다. 공기가 어디서 데워지는지 바로 보입니다.

      2. 트레일

        -

        Ring Buffer 로 궤적을 남긴다. 한 순간이 아니라 지나온 경로를 본다.

        +

        Ring Buffer로 입자 궤적을 남겨, 한 순간이 아닌 지나온 경로를 확인합니다.

      3. 히트맵 슬라이스

        - 온도·속도·압력·밀도·습도 다섯 물리량의 단면을 5패스 GPU 파이프라인으로 그린다. - X · Y · - Z 키로 축을 바꾼다. + 온도, 속도, 압력, 밀도, 습도 다섯 물리량의 단면을 5패스 GPU 파이프라인으로 그립니다. + XYZ 키로 축을 바꿉니다.

      4. 벡터 필드

        -

        화살표 글리프와 스트림라인을 GPU 인스턴싱으로 띄운다. 방향과 세기를 함께 읽는다.

        +

        화살표와 스트림라인을 GPU 인스턴싱으로 그려 기류의 방향과 세기를 함께 보여 줍니다.

      @@ -389,7 +442,7 @@
      -

      RFS 실물 렌더

      +

      RFS 실물 렌더

      @@ -397,11 +450,10 @@ src="./assets/sph-topview.webp" width="689" height="680" - loading="lazy" decoding="async" - alt="RFS 상면 뷰 — 랙 상부에서 데워진 입자가 초록으로 퍼지고 주변 저온 입자는 청록으로 남는다" + alt="RFS 상면 뷰. 랙 상부에서 데워진 입자가 초록으로 퍼지고 주변 저온 입자는 청록으로 남는다" /> -
      상면 · 랙 상부의 열기둥
      +
      상면에서 본 랙 상부의 열기둥
      -
      - 저온 - 중간 - 고온 -

      - 두 장 모두 RFS 실물의 파티클 뷰다. 저온이 청록, 고온이 초록이다 — - 이 페이지 첫 화면의 색도 같은 규칙을 쓴다. + 두 장 모두 RFS 실물의 파티클 뷰입니다. 저온은 청록, 고온은 초록으로 표시하며 이 페이지 첫 화면도 + 같은 색 규칙을 씁니다.

      @@ -430,118 +477,19 @@
      -
      +
      -

      시뮬레이터가 아니라 작업 환경이다

      -

      - 배치를 바꿔 보려면 배치를 바꿀 수 있어야 한다. RFS Editor 는 Unity UI Toolkit 으로 만든 - 런타임 편집 환경이고, 시뮬레이션이 도는 채로 물건을 옮긴다. -

      -
      -
      -
      -
      -
      오브젝트 편집
      -
      이동·회전·스케일 핸들, 다중 선택, Undo / Redo, 드래그 앤 드롭 배치
      -
      -
      -
      속성 패널
      -
      실시간 속성 편집. 관제 시스템의 XD 태그를 여기서 바인딩한다
      -
      -
      -
      리플레이
      -
      - CSV 또는 XD API 를 데이터소스로 삼아 시계열을 재생하고, 실측 대비 정확도 리포트를 - 만든다 -
      -
      -
      -
      AI Advisor
      -
      - 26개 규칙의 Rule Engine 과 LLM 프로바이더 4종(Anthropic · OpenAI · Gemini · - 로컬 Ollama) 연동 -
      -
      -
      -
      모니터링
      -
      PUE · COP · 전력 · CO₂ 대시보드, MetricsHUD 7섹션 26메트릭
      -
      -
      -
      프로젝트
      -
      생성·저장·불러오기, 다중 씬, glTF / GLB 3D 모델 임포트
      -
      -
      - -
      -

      손에 익는 키

      -
        -
      • Space 일시정지 · 재개
      • -
      • H 히트맵
      • -
      • XYZ 슬라이스 축
      • -
      • F3 성능 통계
      • -
      • Tab UI 접기
      • -
      -
      -
      -
      -
      - - -
      -
      -

      다섯 켜

      -
        -
      1. -

        L1

        -

        애플리케이션

        -

        RFS Editor · Simulation3D · AI Advisor · 리플레이

        -
      2. -
      3. -

        L2

        -

        파이프라인 · 컨트롤러

        -

        SimulationPipeline → 스테이지 10개 → 컨트롤러 26개

        -
      4. -
      5. -

        L3

        -

        GPU 컴퓨트

        -

        - SPHCore 8 · DomainExt 14 · SpatialHash 1 · TempGrid 8 · ObjectGrid 7 · - Integration 5 · Scatter 4 · Trail 2 · GlobalSDF 3 · Coil 1 -

        -
      6. -
      7. -

        L4

        -

        데이터 버퍼

        -

        SoA 버퍼 6개 · ParticleData 20B · 충돌 메시 데이터

        -
      8. -
      9. -

        L5

        -

        외부 통신

        -

        Socket.IO v2 (RFS-XD 관제 연동) · REST API · CSV 내보내기

        -
      10. -
      -
      -
      - - -
      -
      -
      -

      돌려 보려면 무엇이 필요한가

      -

      +

      Windows 10 이상, VRAM 6GB NVIDIA GPU부터 돌아갑니다

      +

      DirectCompute를 쓰므로 NVIDIA GPU를 권장합니다. 아래는 공개 기술 문서에 적힌 요구사항입니다.

      - + @@ -557,8 +505,8 @@ - - + + @@ -567,47 +515,69 @@ - +
      RFS 시스템 요구사항 — 최소 사양과 권장 사양RFS 시스템 요구사항. 최소 사양과 권장 사양
      항목
      GPUGTX 1660 · VRAM 6GBRTX 3060 이상 · VRAM 8GB 이상GTX 1660, VRAM 6GBRTX 3060 이상, VRAM 8GB 이상
      메모리
      엔진Unity 6.4 (6000.4.5f1) · URP 17.2Unity 6.4 (6000.4.5f1), URP 17.2

      - 도입 검토와 기술 문의는 트웬티온스로 연락해 주세요. - 제품은 독점 소프트웨어이며, 평가판 제공 여부는 협의가 필요합니다. + 도입 검토와 기술 문의는 트웬티온스로 연락해 주세요. 제품은 독점 소프트웨어이며, 평가판 제공 + 여부는 협의가 필요합니다.

      RFS 도입 문의 -

      hello@twentyoz.kr · 070-4353-1190

      -