release: v0.10.0
This commit is contained in:
parent
2b7fa32b5b
commit
6930906583
61 changed files with 15165 additions and 82 deletions
BIN
apps/site/public/work/griddle-club/assets/griddle-press.webp
Normal file
BIN
apps/site/public/work/griddle-club/assets/griddle-press.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
BIN
apps/site/public/work/griddle-club/assets/hero-handoff.webp
Normal file
BIN
apps/site/public/work/griddle-club/assets/hero-handoff.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 97 KiB |
BIN
apps/site/public/work/griddle-club/assets/kitchen-flow.webp
Normal file
BIN
apps/site/public/work/griddle-club/assets/kitchen-flow.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
BIN
apps/site/public/work/griddle-club/assets/signature-double.webp
Normal file
BIN
apps/site/public/work/griddle-club/assets/signature-double.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
358
apps/site/public/work/griddle-club/franchise/index.html
Normal file
358
apps/site/public/work/griddle-club/franchise/index.html
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>철판클럽 가맹 안내 — 운영 조건부터 확인하기</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="가상 버거 브랜드 철판클럽의 상권, 점포, 면적, 개설 절차와 비용 미정 항목을 먼저 살펴보는 가맹 문의 쇼케이스입니다."
|
||||
/>
|
||||
<meta property="og:title" content="철판클럽 가맹 안내 — 운영 조건부터" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="수익 약속 없이 상권, 점포, 주방 동선과 비용 미정 항목을 먼저 보여 주는 국내형 가맹 안내 화면."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="../og.png" />
|
||||
<meta property="og:image:alt" content="직원들이 철판 조리와 포장을 나누어 맡는 주방" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="철판클럽 가맹 안내 — 운영 조건부터" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="상권, 점포, 주방 동선과 비용 미정 항목을 먼저 살펴보는 가맹 안내 쇼케이스."
|
||||
/>
|
||||
<meta name="twitter:image" content="../og.png" />
|
||||
<meta name="theme-color" content="#fffdf8" />
|
||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500;600&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="../styles/tokens.css?v=1" />
|
||||
<link rel="stylesheet" href="../styles/site.css?v=1" />
|
||||
<link rel="stylesheet" href="../styles/franchise.css?v=1" />
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%232147e7'/%3E%3Cpath d='M6 9h20M8 16h16M10 23h12' stroke='%23fffdf8' stroke-width='3' stroke-linecap='square'/%3E%3C/svg%3E"
|
||||
/>
|
||||
<script>document.documentElement.classList.add("is-js");</script>
|
||||
<script src="../scripts/franchise.js?v=1" defer></script>
|
||||
</head>
|
||||
<body class="franchise-page">
|
||||
<a class="skip-link" href="#main">본문으로 건너뛰기</a>
|
||||
|
||||
<div class="truth-bar" role="note">
|
||||
<p>쇼케이스용 가상 브랜드 · 실제 가맹 모집, 상담, 계약을 제공하지 않음</p>
|
||||
</div>
|
||||
|
||||
<header class="site-head">
|
||||
<div class="wrap head-row franchise-head-row">
|
||||
<a class="brand" href="../" aria-label="철판클럽 홈으로">
|
||||
<span class="brand-mark" aria-hidden="true"><i></i><i></i><i></i></span>
|
||||
<span>철판클럽</span>
|
||||
</a>
|
||||
<nav class="head-nav" aria-label="가맹 안내 메뉴">
|
||||
<a href="#model">운영 방식</a>
|
||||
<a href="#site-fit">상권과 점포</a>
|
||||
<a href="#process">개설 절차</a>
|
||||
<a href="#cost">비용 항목</a>
|
||||
</nav>
|
||||
<a class="head-action" href="#inquiry">문의 준비하기</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main">
|
||||
<section class="franchise-hero" aria-labelledby="franchise-hero-heading">
|
||||
<div class="franchise-hero-copy">
|
||||
<p class="section-kicker">철판클럽 가맹 안내</p>
|
||||
<h1 id="franchise-hero-heading">매출 약속보다<br />운영 조건부터.</h1>
|
||||
<p>
|
||||
버거 사진만 보고 결정하지 않도록 상권, 점포, 전용 면적, 주방 동선과 아직 정할 수 없는 비용을 순서대로 보여 줍니다.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="button button-light" href="#site-fit">내 조건 살펴보기</a>
|
||||
<a class="text-link light-link" href="#cost">비용 미정 항목 보기 <span aria-hidden="true">↓</span></a>
|
||||
</div>
|
||||
<div class="franchise-truth">
|
||||
<strong>먼저 확인해 주세요</strong>
|
||||
<p>실제 가맹 모집이 아닌 디자인 쇼케이스입니다. 금액, 수익, 매장 수와 개점 기간을 꾸며내지 않습니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
<figure class="franchise-hero-media image-shell">
|
||||
<img
|
||||
src="../assets/kitchen-flow.webp"
|
||||
width="1400"
|
||||
height="933"
|
||||
alt="직원 두 명이 좁은 주방에서 철판 조리와 포장 작업을 나누어 맡는 모습"
|
||||
fetchpriority="high"
|
||||
/>
|
||||
<span class="image-fallback" aria-hidden="true">주방 운영 동선 장면</span>
|
||||
<figcaption>생성 이미지 · 실제 매장과 가맹 시설 아님</figcaption>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<section class="fact-rail" aria-label="가맹 안내 원칙">
|
||||
<div class="wrap fact-rail-grid">
|
||||
<p><span>01</span> 수익률을 약속하지 않음</p>
|
||||
<p><span>02</span> 점포를 보기 전 금액을 확정하지 않음</p>
|
||||
<p><span>03</span> 실제 개인정보를 받지 않음</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section model-section" id="model" aria-labelledby="model-heading">
|
||||
<div class="wrap">
|
||||
<div class="section-head split-head">
|
||||
<div class="model-heading-copy"><p class="section-kicker">어떤 운영 방식인지</p><h2 id="model-heading">좁은 주방에서<br />역할을 분명하게.</h2></div>
|
||||
<p class="section-note">조리, 조립, 포장, 전달이 서로 겹치지 않는 흐름을 먼저 검토합니다. 실제 설비와 인원은 점포마다 달라집니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="flow-board">
|
||||
<article><span>01</span><h3>재료 준비</h3><p>냉장 보관과 당일 준비 동선을 분리합니다.</p></article>
|
||||
<article><span>02</span><h3>철판 조리</h3><p>패티와 양파가 움직이는 구간을 짧게 둡니다.</p></article>
|
||||
<article><span>03</span><h3>조립과 포장</h3><p>주문표, 유산지, 상자를 한 방향에 둡니다.</p></article>
|
||||
<article><span>04</span><h3>전달과 픽업</h3><p>매장 손님과 배달 픽업이 부딪히지 않게 나눕니다.</p></article>
|
||||
</div>
|
||||
|
||||
<div class="operator-fit">
|
||||
<div class="operator-heading-copy"><p class="section-kicker">운영자에게 필요한 태도</p><h3>직접 확인하고,<br />같은 품질을 반복하기.</h3></div>
|
||||
<ul>
|
||||
<li><strong>현장 참여</strong><span>조리와 응대 흐름을 직접 익힐 준비</span></li>
|
||||
<li><strong>위생 기록</strong><span>보관, 조리, 마감 상태를 남길 준비</span></li>
|
||||
<li><strong>지역 이해</strong><span>점심과 저녁 수요가 다른 상권을 살필 준비</span></li>
|
||||
<li><strong>팀 운영</strong><span>작은 인원에서 역할을 나누고 교육할 준비</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section site-fit-section" id="site-fit" aria-labelledby="site-fit-heading">
|
||||
<div class="wrap">
|
||||
<div class="section-head split-head">
|
||||
<div class="site-fit-heading-copy"><p class="section-kicker">상권과 점포</p><h2 id="site-fit-heading">주소보다 먼저<br />운영 장면을 봅니다.</h2></div>
|
||||
<p class="section-note">아래 분류는 적합 판정이 아니라 문의 때 빠뜨리지 않을 조건 목록입니다. 권장 면적 수치는 실제 검토 뒤에만 제시할 수 있습니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="fit-grid">
|
||||
<article class="fit-main">
|
||||
<p class="fit-number mono">A</p>
|
||||
<h3>상권 흐름</h3>
|
||||
<p>업무가, 주거지, 역 주변, 대학가, 상점가 또는 여러 수요가 섞인 곳인지 먼저 적습니다.</p>
|
||||
<ul><li>점심과 저녁 수요 시간</li><li>도보와 차량 접근</li><li>포장 대기 공간</li><li>배달 픽업 접근</li></ul>
|
||||
</article>
|
||||
<article>
|
||||
<p class="fit-number mono">B</p><h3>점포 상태</h3><p>점포 없음, 찾는 중, 협의 중, 보유함으로 나눕니다.</p>
|
||||
</article>
|
||||
<article>
|
||||
<p class="fit-number mono">C</p><h3>전용 면적</h3><p>사용자가 입력한 ㎡와 환산한 평을 함께 봅니다. 적합 판정은 하지 않습니다.</p>
|
||||
</article>
|
||||
<article>
|
||||
<p class="fit-number mono">D</p><h3>설비 조건</h3><p>전기, 배기, 급수, 배수와 반입 경로는 점포 자료로 따로 확인합니다.</p>
|
||||
</article>
|
||||
<article>
|
||||
<p class="fit-number mono">E</p><h3>이전 용도</h3><p>음식점, 소매점, 빈 점포 등 이전 사용 상태를 확인합니다.</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section franchise-process" id="process" aria-labelledby="process-heading-franchise">
|
||||
<div class="wrap">
|
||||
<div class="section-head split-head">
|
||||
<div class="process-heading-copy"><p class="section-kicker">개설 절차</p><h2 id="process-heading-franchise">기간을 꾸미지 않고<br />순서만 보여 줍니다.</h2></div>
|
||||
<p class="section-note">점포, 자료, 협의와 공사 조건에 따라 기간이 달라집니다. 아래 흐름은 화면 설명용 예시이며 계약이나 개점을 보장하지 않습니다.</p>
|
||||
</div>
|
||||
<ol class="franchise-timeline">
|
||||
<li><span>01</span><div><h3>문의 준비</h3><p>희망 지역과 시작 시기를 정리합니다.</p></div><small>준비 전</small></li>
|
||||
<li><span>02</span><div><h3>희망 조건 확인</h3><p>상권 종류, 점포 상태와 운영 경험을 봅니다.</p></div><small>자료 필요</small></li>
|
||||
<li><span>03</span><div><h3>상권과 점포 검토</h3><p>주소가 정해졌다면 면적, 설비와 동선을 확인합니다.</p></div><small>조건에 따라 다름</small></li>
|
||||
<li><span>04</span><div><h3>정보 제공과 협의</h3><p>확인 가능한 조건과 미정 항목을 나눕니다.</p></div><small>검토 필요</small></li>
|
||||
<li><span>05</span><div><h3>계약 내용 검토</h3><p>실제 자료와 조건을 받은 뒤 각 항목을 확인합니다.</p></div><small>자동 확정 아님</small></li>
|
||||
<li><span>06</span><div><h3>설계와 공사 계획</h3><p>주방, 포장대와 픽업 동선을 점포에 맞춥니다.</p></div><small>점포별 다름</small></li>
|
||||
<li><span>07</span><div><h3>조리와 응대 교육</h3><p>조리 순서와 위생 기록을 함께 익힙니다.</p></div><small>계획 필요</small></li>
|
||||
<li><span>08</span><div><h3>개점 전 점검</h3><p>설비, 재료, 동선과 운영 준비를 확인합니다.</p></div><small>완료 보장 아님</small></li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section cost-section" id="cost" aria-labelledby="cost-heading">
|
||||
<div class="wrap">
|
||||
<div class="cost-head">
|
||||
<div class="cost-heading-copy"><p class="section-kicker">비용 항목</p><h2 id="cost-heading">합계를 만들 수 없는<br />이유부터 적습니다.</h2></div>
|
||||
<div class="unknown-cost">
|
||||
<span>현재 상태</span>
|
||||
<strong>비용 미정</strong>
|
||||
<p>점포, 면적, 설비와 계약 자료가 확인되지 않아 금액을 제시하지 않습니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cost-table-wrap" role="region" aria-label="비용 항목 표" tabindex="0">
|
||||
<table class="cost-table">
|
||||
<caption class="sr-only">가맹 비용 항목과 현재 확인 상태</caption>
|
||||
<thead><tr><th scope="col">항목</th><th scope="col">현재 상태</th><th scope="col">금액</th><th scope="col">다음 확인</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><th scope="row">가맹 관련 비용</th><td>자료 미제공</td><td>미정</td><td>실제 안내 자료 확인</td></tr>
|
||||
<tr><th scope="row">교육 비용</th><td>범위 미정</td><td>미정</td><td>인원과 교육 범위 확인</td></tr>
|
||||
<tr><th scope="row">계약 보증 관련 비용</th><td>조건 미정</td><td>미정</td><td>실제 계약 자료 확인</td></tr>
|
||||
<tr><th scope="row">주방 설비</th><td>점포 필요</td><td>미정</td><td>전기, 배기와 설비 확인</td></tr>
|
||||
<tr><th scope="row">가구와 집기</th><td>사양 필요</td><td>미정</td><td>좌석과 포장 방식 확인</td></tr>
|
||||
<tr><th scope="row">인테리어</th><td>면적 필요</td><td>미정</td><td>전용 면적과 현장 확인</td></tr>
|
||||
<tr><th scope="row">간판과 외부 표시</th><td>건물 조건 필요</td><td>미정</td><td>건물과 지역 조건 확인</td></tr>
|
||||
<tr><th scope="row">주문 기기</th><td>운영 방식 필요</td><td>미정</td><td>주문과 결제 방식 확인</td></tr>
|
||||
<tr><th scope="row">허가와 신고</th><td>지역 조건 필요</td><td>미정</td><td>관할 안내 확인</td></tr>
|
||||
<tr><th scope="row">첫 물류</th><td>메뉴 범위 필요</td><td>미정</td><td>품목과 보관량 확인</td></tr>
|
||||
<tr><th scope="row">임대 보증 관련 비용</th><td>점포 계약 필요</td><td>미정</td><td>임대 조건 직접 확인</td></tr>
|
||||
</tbody>
|
||||
<tfoot><tr><th scope="row" colspan="2">합계</th><td colspan="2">계산하지 않음 · 확인되지 않은 숫자를 더하지 않습니다</td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<p class="cost-warning">평균 매출, 예상 수익, 회수 기간과 최저 비용은 근거 자료가 없어 제공하지 않습니다.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section inquiry-section" id="inquiry" aria-labelledby="inquiry-heading">
|
||||
<div class="wrap inquiry-layout">
|
||||
<div class="inquiry-intro">
|
||||
<p class="section-kicker">문의 준비 체험</p>
|
||||
<h2 id="inquiry-heading">연락처 없이도<br />조건부터 정리하기.</h2>
|
||||
<p>실제 이름과 연락처를 받지 않습니다. 화면 안에서 상권과 점포 조건을 정리하고 전송 전 검토 상태를 체험합니다.</p>
|
||||
<div class="privacy-note" role="note">
|
||||
<strong>실제 개인정보를 입력하지 마세요.</strong>
|
||||
<p>입력 내용은 서버로 보내거나 저장하지 않습니다. 성공 화면도 실제 접수가 아닙니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inquiry-panel">
|
||||
<ol class="step-indicator" aria-label="문의 준비 단계">
|
||||
<li class="is-current" data-step-indicator="1" aria-current="step"><span>1</span><small>상권</small></li>
|
||||
<li data-step-indicator="2"><span>2</span><small>점포</small></li>
|
||||
<li data-step-indicator="3"><span>3</span><small>검토</small></li>
|
||||
</ol>
|
||||
|
||||
<form id="franchise-form" novalidate>
|
||||
<section class="form-step" data-step="1" aria-labelledby="step-one-heading">
|
||||
<p class="step-count">1 / 3</p>
|
||||
<h3 id="step-one-heading">어느 지역에서 시작할까요?</h3>
|
||||
<div class="form-field">
|
||||
<label for="demo-name">예시 호칭</label>
|
||||
<input id="demo-name" name="demoName" type="text" minlength="2" autocomplete="off" placeholder="실명 대신 예시 호칭" required />
|
||||
<small>실제 이름을 입력하지 마세요.</small>
|
||||
<p class="field-error" id="demo-name-error"></p>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label for="region">희망 지역</label>
|
||||
<select id="region" name="region" required>
|
||||
<option value="">지역 고르기</option><option>서울 동쪽</option><option>서울 서쪽</option><option>서울 남쪽</option><option>서울 북쪽</option><option>수도권</option><option>아직 모름</option>
|
||||
</select>
|
||||
<p class="field-error" id="region-error"></p>
|
||||
</div>
|
||||
<fieldset class="form-field">
|
||||
<legend>희망 상권</legend>
|
||||
<div class="radio-grid">
|
||||
<label><input type="radio" name="areaKind" value="업무가" required /><span>업무가</span></label>
|
||||
<label><input type="radio" name="areaKind" value="주거지" /><span>주거지</span></label>
|
||||
<label><input type="radio" name="areaKind" value="역 주변" /><span>역 주변</span></label>
|
||||
<label><input type="radio" name="areaKind" value="대학가" /><span>대학가</span></label>
|
||||
<label><input type="radio" name="areaKind" value="상점가" /><span>상점가</span></label>
|
||||
<label><input type="radio" name="areaKind" value="아직 모름" /><span>아직 모름</span></label>
|
||||
</div>
|
||||
<p class="field-error" id="area-kind-error"></p>
|
||||
</fieldset>
|
||||
<div class="form-actions"><button class="button button-primary" type="button" data-next>점포 조건으로</button></div>
|
||||
</section>
|
||||
|
||||
<section class="form-step" data-step="2" aria-labelledby="step-two-heading">
|
||||
<p class="step-count">2 / 3</p>
|
||||
<h3 id="step-two-heading">점포는 어느 단계인가요?</h3>
|
||||
<fieldset class="form-field">
|
||||
<legend>점포 상태</legend>
|
||||
<div class="radio-grid">
|
||||
<label><input type="radio" name="storePossession" value="아직 없음" required /><span>아직 없음</span></label>
|
||||
<label><input type="radio" name="storePossession" value="찾는 중" /><span>찾는 중</span></label>
|
||||
<label><input type="radio" name="storePossession" value="협의 중" /><span>협의 중</span></label>
|
||||
<label><input type="radio" name="storePossession" value="보유함" /><span>보유함</span></label>
|
||||
</div>
|
||||
<p class="field-error" id="store-possession-error"></p>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-field area-field" id="area-field" hidden>
|
||||
<label for="exclusive-area">전용 면적</label>
|
||||
<div class="unit-input"><input id="exclusive-area" name="exclusiveArea" type="number" min="0.1" step="0.1" inputmode="decimal" /><span>㎡</span></div>
|
||||
<output id="area-conversion" for="exclusive-area">면적을 입력하면 평으로도 보여 드립니다.</output>
|
||||
<small>환산값은 적합 판정이 아닙니다.</small>
|
||||
<p class="field-error" id="exclusive-area-error"></p>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label for="opening-window">희망 시작 시기</label>
|
||||
<select id="opening-window" name="openingWindow" required>
|
||||
<option value="">시기 고르기</option><option>석 달 안</option><option>석 달에서 여섯 달</option><option>여섯 달에서 열두 달</option><option>아직 모름</option>
|
||||
</select>
|
||||
<p class="field-error" id="opening-window-error"></p>
|
||||
</div>
|
||||
|
||||
<fieldset class="form-field">
|
||||
<legend>매장 운영 경험</legend>
|
||||
<div class="radio-grid radio-grid-two">
|
||||
<label><input type="radio" name="experience" value="있음" required /><span>있음</span></label>
|
||||
<label><input type="radio" name="experience" value="없음" /><span>없음</span></label>
|
||||
</div>
|
||||
<p class="field-error" id="experience-error"></p>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-actions split-actions"><button class="button button-ghost" type="button" data-back>이전</button><button class="button button-primary" type="button" data-next>입력 내용 검토</button></div>
|
||||
</section>
|
||||
|
||||
<section class="form-step" data-step="3" aria-labelledby="step-three-heading">
|
||||
<p class="step-count">3 / 3</p>
|
||||
<h3 id="step-three-heading">보내기 전에 확인하세요.</h3>
|
||||
<dl class="review-list" id="review-list">
|
||||
<div><dt>예시 호칭</dt><dd data-review="demoName">—</dd></div>
|
||||
<div><dt>희망 지역</dt><dd data-review="region">—</dd></div>
|
||||
<div><dt>희망 상권</dt><dd data-review="areaKind">—</dd></div>
|
||||
<div><dt>점포 상태</dt><dd data-review="storePossession">—</dd></div>
|
||||
<div><dt>전용 면적</dt><dd data-review="exclusiveArea">해당 없음</dd></div>
|
||||
<div><dt>희망 시기</dt><dd data-review="openingWindow">—</dd></div>
|
||||
<div><dt>운영 경험</dt><dd data-review="experience">—</dd></div>
|
||||
</dl>
|
||||
<label class="consent-check"><input type="checkbox" id="demo-consent" required /><span>실제 문의가 아니며 개인정보를 입력하지 않았음을 확인했습니다.</span></label>
|
||||
<p class="field-error" id="demo-consent-error"></p>
|
||||
<div class="form-actions split-actions"><button class="button button-ghost" type="button" data-back>수정하기</button><button class="button button-primary" type="submit" id="submit-inquiry">문의 준비 마치기</button></div>
|
||||
<p class="submit-state" id="submit-state" role="alert"></p>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<section class="inquiry-success" id="inquiry-success" hidden aria-labelledby="success-heading" tabindex="-1">
|
||||
<span class="success-mark" aria-hidden="true">✓</span>
|
||||
<p class="section-kicker">화면 체험 완료</p>
|
||||
<h3 id="success-heading">문의 준비가 끝났어요.</h3>
|
||||
<p>제출 절차를 보여 주는 예시이며 실제 담당자에게 전송되지 않았습니다.</p>
|
||||
<div class="success-actions"><button class="button button-ghost" type="button" id="review-again">입력 내용 다시 보기</button><button class="button button-primary" type="button" id="restart-form">처음부터 보기</button></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="franchise-close" aria-labelledby="close-heading">
|
||||
<div class="wrap close-row">
|
||||
<div class="close-heading-copy"><p class="section-kicker">철판클럽</p><h2 id="close-heading">숫자를 꾸미지 않아야<br />조건을 제대로 볼 수 있습니다.</h2></div>
|
||||
<a class="button button-light" href="../">소비자 홈으로 돌아가기</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="site-foot">
|
||||
<div class="wrap foot-grid franchise-foot-grid">
|
||||
<div><a class="brand brand-foot" href="../"><span class="brand-mark" aria-hidden="true"><i></i><i></i><i></i></span><span>철판클럽</span></a><p>눌러 굽고, 바로 건넨다.</p></div>
|
||||
<div><strong>가맹 안내</strong><a href="#model">운영 방식</a><a href="#site-fit">상권과 점포</a><a href="#process">개설 절차</a><a href="#cost">비용 항목</a></div>
|
||||
<div class="foot-truth"><strong>쇼케이스 고지</strong><p>가상 브랜드이며 실제 가맹 모집, 수익 안내, 계약과 문의 전송을 제공하지 않습니다.</p><p class="mono">DESIGN SHOWCASE · 2026</p></div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
462
apps/site/public/work/griddle-club/index.html
Normal file
462
apps/site/public/work/griddle-club/index.html
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>철판클럽 — 눌러 굽고, 바로 건넨다</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="서울의 빠른 한 끼를 위한 가상 눌림 버거 브랜드 철판클럽의 메뉴, 조합기, 예시 매장과 운영 방식을 살펴보세요."
|
||||
/>
|
||||
<meta property="og:title" content="철판클럽 — 눌러 굽고, 바로 건넨다" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="메뉴를 고르고, 재료를 조합하고, 예시 매장 운영 상태까지 확인하는 국내형 버거 쇼케이스."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="./og.png" />
|
||||
<meta property="og:image:alt" content="철판 주방에서 포장한 버거를 건네는 장면" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="철판클럽 — 눌러 굽고, 바로 건넨다" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="메뉴를 고르고 재료를 조합해 보는 국내형 버거 브랜드 쇼케이스."
|
||||
/>
|
||||
<meta name="twitter:image" content="./og.png" />
|
||||
<meta name="theme-color" content="#f4f1e9" />
|
||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500;600&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles/tokens.css?v=1" />
|
||||
<link rel="stylesheet" href="./styles/site.css?v=1" />
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%232147e7'/%3E%3Cpath d='M6 9h20M8 16h16M10 23h12' stroke='%23fffdf8' stroke-width='3' stroke-linecap='square'/%3E%3C/svg%3E"
|
||||
/>
|
||||
<script>document.documentElement.classList.add("is-js");</script>
|
||||
<script src="./scripts/site.js?v=1" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">본문으로 건너뛰기</a>
|
||||
|
||||
<div class="truth-bar" role="note">
|
||||
<p>쇼케이스용 가상 브랜드 · 가격과 매장 정보는 화면 동작 확인용 예시</p>
|
||||
</div>
|
||||
|
||||
<header class="site-head" id="top">
|
||||
<div class="wrap head-row">
|
||||
<a class="brand" href="#top" aria-label="철판클럽 처음으로">
|
||||
<span class="brand-mark" aria-hidden="true"><i></i><i></i><i></i></span>
|
||||
<span>철판클럽</span>
|
||||
</a>
|
||||
<nav class="head-nav" aria-label="주요 메뉴">
|
||||
<a href="#menu">메뉴</a>
|
||||
<a href="#builder">조합하기</a>
|
||||
<a href="#stores">매장 찾기</a>
|
||||
<a href="./franchise/">가맹 안내</a>
|
||||
</nav>
|
||||
<a class="head-action" href="#stores">가까운 매장 보기</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main">
|
||||
<section class="hero" aria-labelledby="hero-heading">
|
||||
<div class="hero-copy">
|
||||
<p class="section-kicker">서울 도심 눌림 버거</p>
|
||||
<h1 id="hero-heading">눌러 굽고,<br />바로 건넨다.</h1>
|
||||
<p class="hero-lead">
|
||||
얇게 눌러 가장자리는 바삭하게. 주문 뒤 조립하고 포장해, 퇴근길 한 상자로 건넵니다.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="button button-primary" href="#menu">오늘 메뉴 보기</a>
|
||||
<a class="text-link" href="#builder">내 버거 조합하기 <span aria-hidden="true">→</span></a>
|
||||
</div>
|
||||
<dl class="hero-facts">
|
||||
<div><dt>굽기</dt><dd>얇은 패티를 주문 뒤 눌러 굽기</dd></div>
|
||||
<div><dt>받기</dt><dd>포장과 배달 가능 여부를 매장별 표시</dd></div>
|
||||
</dl>
|
||||
</div>
|
||||
<figure class="hero-media image-shell">
|
||||
<picture>
|
||||
<source media="(max-width: 820px)" srcset="./assets/hero-handoff-mobile.webp" />
|
||||
<img
|
||||
src="./assets/hero-handoff.webp"
|
||||
width="1600"
|
||||
height="900"
|
||||
alt="밝은 철판 주방에서 직원이 포장한 버거를 건네는 모습"
|
||||
fetchpriority="high"
|
||||
/>
|
||||
</picture>
|
||||
<span class="image-fallback" aria-hidden="true">버거 포장 장면</span>
|
||||
<figcaption>
|
||||
<span>철판에서 상자까지</span>
|
||||
<span>생성 이미지 · 실제 매장 아님</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<aside class="hero-order" aria-label="오늘 고르기">
|
||||
<span class="order-index">01</span>
|
||||
<p>철판 더블</p>
|
||||
<strong>예시 단품 8,900원</strong>
|
||||
<button type="button" data-preset="double">이 조합으로 시작</button>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
<section class="service-strip" aria-label="이용 방식">
|
||||
<div class="wrap service-row">
|
||||
<p><span aria-hidden="true">●</span> 주문 뒤 조리</p>
|
||||
<p>단품과 세트</p>
|
||||
<p>포장 여부 매장별 확인</p>
|
||||
<p>배달 여부 매장별 확인</p>
|
||||
<p class="mono">GRIDDLE CLUB</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section menu-section" id="menu" aria-labelledby="menu-heading">
|
||||
<div class="wrap">
|
||||
<div class="section-head split-head">
|
||||
<div>
|
||||
<p class="section-kicker">오늘 고를 메뉴</p>
|
||||
<h2 id="menu-heading">버거가 먼저 보이게.</h2>
|
||||
</div>
|
||||
<p class="section-note">
|
||||
모든 가격은 조합기 동작을 보여 주기 위한 예시입니다. 실제 판매와 주문은 이루어지지 않습니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="menu-toolbar" role="group" aria-label="메뉴 분류">
|
||||
<button type="button" class="filter-button is-active" data-menu-filter="all" aria-pressed="true">전체</button>
|
||||
<button type="button" class="filter-button" data-menu-filter="burger" aria-pressed="false">버거</button>
|
||||
<button type="button" class="filter-button" data-menu-filter="side" aria-pressed="false">곁들임</button>
|
||||
<button type="button" class="filter-button" data-menu-filter="drink" aria-pressed="false">음료</button>
|
||||
</div>
|
||||
|
||||
<div class="menu-grid" id="menu-grid">
|
||||
<article class="menu-item menu-item-featured" data-menu-category="burger">
|
||||
<div class="menu-photo image-shell">
|
||||
<img
|
||||
src="./assets/signature-double.webp"
|
||||
width="1400"
|
||||
height="933"
|
||||
alt="철판 패티 두 장과 치즈, 구운 양파를 넣은 버거"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span class="image-fallback" aria-hidden="true">철판 더블 사진</span>
|
||||
<span class="menu-number">01</span>
|
||||
</div>
|
||||
<div class="menu-body">
|
||||
<div><p class="menu-state">대표 메뉴</p><h3>철판 더블</h3></div>
|
||||
<p>얇은 패티 두 장, 치즈, 구운 양파, 피클, 집 소스.</p>
|
||||
<div class="menu-foot">
|
||||
<span><small>예시 단품</small> 8,900원</span>
|
||||
<button type="button" data-preset="double">조합하기</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="menu-item menu-item-graphic" data-menu-category="burger">
|
||||
<div class="ingredient-stack" aria-hidden="true">
|
||||
<span class="bun"></span><span class="pickle"></span><span class="cheese"></span><span class="patty"></span><span class="bun bottom"></span>
|
||||
<b>02</b>
|
||||
</div>
|
||||
<div class="menu-body">
|
||||
<div><p class="menu-state">가볍게</p><h3>철판 싱글</h3></div>
|
||||
<p>패티 한 장, 치즈, 피클, 집 소스. 단품으로 빠르게.</p>
|
||||
<div class="menu-foot">
|
||||
<span><small>예시 단품</small> 6,900원</span>
|
||||
<button type="button" data-preset="single">조합하기</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="menu-item menu-item-blue" data-menu-category="burger">
|
||||
<div class="menu-poster" aria-hidden="true"><span>03</span><i></i><i></i><i></i></div>
|
||||
<div class="menu-body">
|
||||
<div><p class="menu-state">매운 피클</p><h3>고추 피클 더블</h3></div>
|
||||
<p>고추 피클과 매운 소스를 더한 더블. 맵기는 예시입니다.</p>
|
||||
<div class="menu-foot">
|
||||
<span><small>예시 단품</small> 9,400원</span>
|
||||
<button type="button" data-preset="spicy">조합하기</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="menu-item menu-item-small" data-menu-category="side">
|
||||
<div class="menu-body">
|
||||
<div><p class="menu-state">곁들임</p><h3>얇은 감자</h3></div>
|
||||
<p>가늘게 튀겨 소금으로 마무리합니다.</p>
|
||||
<div class="menu-foot"><span><small>예시 가격</small> 3,600원</span></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="menu-item menu-item-small" data-menu-category="side">
|
||||
<div class="menu-body">
|
||||
<div><p class="menu-state">곁들임</p><h3>양파 한 컵</h3></div>
|
||||
<p>철판에서 단맛을 낸 양파를 따로 담습니다.</p>
|
||||
<div class="menu-foot"><span><small>예시 가격</small> 2,800원</span></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="menu-item menu-item-small" data-menu-category="drink">
|
||||
<div class="menu-body">
|
||||
<div><p class="menu-state">음료</p><h3>탄산 한 잔</h3></div>
|
||||
<p>매장에서 고른 음료 한 잔. 제품별 성분을 확인해 주세요.</p>
|
||||
<div class="menu-foot"><span><small>예시 가격</small> 2,400원</span></div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="empty-state" id="menu-empty" hidden role="status">
|
||||
<strong>이 분류에 준비된 예시 메뉴가 없어요.</strong>
|
||||
<button type="button" data-show-all-menu>모든 메뉴 보기</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section builder-section" id="builder" aria-labelledby="builder-heading">
|
||||
<div class="wrap">
|
||||
<div class="section-head split-head">
|
||||
<div>
|
||||
<p class="section-kicker">버거 조합기</p>
|
||||
<h2 id="builder-heading">상자에 담길 내용을<br />먼저 정하세요.</h2>
|
||||
</div>
|
||||
<p class="section-note">재료 선택과 예시 총액은 실제로 바뀝니다. 주문 전송 기능은 없습니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="builder-layout">
|
||||
<form class="builder-form" id="burger-builder" novalidate>
|
||||
<fieldset>
|
||||
<legend><span>01</span> 맛 고르기</legend>
|
||||
<div class="choice-row">
|
||||
<label><input type="radio" name="flavor" value="classic" checked /><span><strong>기본</strong><small>집 소스와 피클</small></span></label>
|
||||
<label><input type="radio" name="flavor" value="spicy" /><span><strong>매운 피클</strong><small>고추 피클과 매운 소스</small></span></label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend><span>02</span> 크기와 치즈</legend>
|
||||
<div class="choice-row choice-row-compact">
|
||||
<label><input type="radio" name="patty" value="1" /><span>패티 한 장</span></label>
|
||||
<label><input type="radio" name="patty" value="2" checked /><span>패티 두 장</span></label>
|
||||
</div>
|
||||
<label class="field-label" for="cheese">치즈</label>
|
||||
<select id="cheese" name="cheese">
|
||||
<option value="0">치즈 없음</option>
|
||||
<option value="1" selected>치즈 한 장</option>
|
||||
<option value="2">치즈 두 장</option>
|
||||
</select>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend><span>03</span> 양파와 피클</legend>
|
||||
<div class="select-grid">
|
||||
<label>구운 양파
|
||||
<select name="onion"><option value="none">없음</option><option value="regular" selected>기본</option><option value="extra">많이</option></select>
|
||||
</label>
|
||||
<label>피클
|
||||
<select name="pickle"><option value="none">없음</option><option value="regular" selected>기본</option><option value="extra">많이</option></select>
|
||||
</label>
|
||||
<label>소스
|
||||
<select name="sauce"><option value="none">없음</option><option value="regular" selected>기본</option><option value="side">따로</option></select>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend><span>04</span> 단품 또는 세트</legend>
|
||||
<div class="choice-row choice-row-compact">
|
||||
<label><input type="radio" name="serving" value="single" checked /><span>단품</span></label>
|
||||
<label><input type="radio" name="serving" value="set" /><span>세트 더하기</span></label>
|
||||
</div>
|
||||
<div class="set-options" id="set-options">
|
||||
<p id="set-heading">세트 구성</p>
|
||||
<div class="select-grid">
|
||||
<label>곁들임
|
||||
<select name="side" disabled><option value="fries">얇은 감자</option><option value="onion">양파 한 컵</option></select>
|
||||
</label>
|
||||
<label>음료
|
||||
<select name="drink" disabled><option value="cola">탄산 한 잔</option><option value="water">물 한 병</option></select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend><span>05</span> 받을 예시 매장</legend>
|
||||
<label class="field-label" for="builder-store">매장</label>
|
||||
<select id="builder-store" name="store">
|
||||
<option value="seongsu">성수 예시점 · 영업 중 예시</option>
|
||||
<option value="yeonnam">연남 예시점 · 영업 종료 예시</option>
|
||||
<option value="mullae">문래 예시점 · 오늘은 쉬어요</option>
|
||||
</select>
|
||||
<p class="field-help">매장과 영업 상태도 화면 동작용 예시입니다.</p>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<aside class="builder-summary" aria-labelledby="summary-heading">
|
||||
<div class="summary-photo image-shell">
|
||||
<img
|
||||
src="./assets/signature-double.webp"
|
||||
width="1400"
|
||||
height="933"
|
||||
alt="조합 결과를 설명하는 철판 더블 버거"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span class="image-fallback" aria-hidden="true">조합 버거 사진</span>
|
||||
<span class="summary-stamp">화면 예시</span>
|
||||
</div>
|
||||
<div class="summary-body">
|
||||
<p class="section-kicker">고른 내용</p>
|
||||
<h3 id="summary-heading">철판 더블</h3>
|
||||
<p id="summary-lines">패티 두 장 / 치즈 한 장 / 양파 기본 / 피클 기본 / 소스 기본</p>
|
||||
<dl class="summary-list">
|
||||
<div><dt>받기</dt><dd id="summary-serving">단품</dd></div>
|
||||
<div><dt>예시 매장</dt><dd id="summary-store">성수 예시점</dd></div>
|
||||
<div><dt>상태</dt><dd id="summary-store-state">영업 중 예시</dd></div>
|
||||
</dl>
|
||||
<div class="allergen-note" id="allergen-note">
|
||||
<strong>알레르기 예시</strong>
|
||||
<p id="summary-allergens">알, 우유, 대두, 밀, 쇠고기</p>
|
||||
<small>같은 철판과 조리 도구를 함께 씁니다. 재료를 빼도 알레르기 안전을 보장하지 않습니다.</small>
|
||||
</div>
|
||||
<div class="summary-total"><span>화면 계산용 예시 총액</span><strong id="summary-total">8,900원</strong></div>
|
||||
<p class="truth-inline">실제 주문이나 결제로 전송되지 않습니다.</p>
|
||||
<button class="button button-primary button-full" type="button" id="save-builder">선택 내용 저장</button>
|
||||
<a class="button button-ghost button-full" href="#stores" id="other-store-link">다른 매장 찾기</a>
|
||||
<p class="save-status" id="save-status" role="status" aria-live="polite"></p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section process-section" id="process" aria-labelledby="process-heading">
|
||||
<div class="wrap process-grid">
|
||||
<div class="process-copy">
|
||||
<p class="section-kicker">철판에서 상자까지</p>
|
||||
<h2 id="process-heading">빠르다는 말 대신,<br />순서를 보여 줍니다.</h2>
|
||||
<ol class="process-list">
|
||||
<li><span>01</span><div><strong>누르기</strong><p>얇은 패티를 뜨거운 철판에 눌러 가장자리를 만듭니다.</p></div></li>
|
||||
<li><span>02</span><div><strong>겹치기</strong><p>치즈, 구운 양파, 피클을 고른 순서대로 올립니다.</p></div></li>
|
||||
<li><span>03</span><div><strong>건네기</strong><p>유산지와 상자에 담고 포장 상태를 확인합니다.</p></div></li>
|
||||
</ol>
|
||||
</div>
|
||||
<figure class="process-media image-shell">
|
||||
<img
|
||||
src="./assets/griddle-press.webp"
|
||||
width="1400"
|
||||
height="933"
|
||||
alt="조리사가 철판에서 패티를 눌러 굽고 양파를 익히는 모습"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span class="image-fallback" aria-hidden="true">철판 조리 장면</span>
|
||||
<figcaption>생성 이미지 · 실제 조리 시설 아님</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section stores-section" id="stores" aria-labelledby="stores-heading">
|
||||
<div class="wrap">
|
||||
<div class="section-head split-head">
|
||||
<div><p class="section-kicker">매장 찾기</p><h2 id="stores-heading">지금 받을 수 있는지<br />먼저 확인하세요.</h2></div>
|
||||
<p class="section-note">주소, 전화, 거리와 운영 상태를 꾸며내지 않습니다. 아래 세 곳은 상태 화면을 위한 예시 매장입니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="store-tools">
|
||||
<div class="search-box">
|
||||
<input id="store-search" type="search" placeholder="동네 또는 매장명 검색" autocomplete="off" aria-label="예시 매장 검색" />
|
||||
<button type="button" id="clear-store-search" hidden>지우기</button>
|
||||
</div>
|
||||
<div class="region-filters" role="group" aria-label="지역 고르기">
|
||||
<button type="button" class="is-active" data-region="all" aria-pressed="true">전체</button>
|
||||
<button type="button" data-region="성동" aria-pressed="false">성동</button>
|
||||
<button type="button" data-region="마포" aria-pressed="false">마포</button>
|
||||
<button type="button" data-region="영등포" aria-pressed="false">영등포</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="store-layout">
|
||||
<div class="store-list" id="store-list">
|
||||
<article class="store-card is-selected" data-store="seongsu" data-region="성동" data-search="성수 예시점 성동 성수동" tabindex="-1">
|
||||
<div class="store-card-top"><span class="store-state state-open">영업 중 예시</span><span>예시 매장</span></div>
|
||||
<h3>성수 예시점</h3>
|
||||
<p>서울 성동구 성수동 일대 · 상세 주소 없음</p>
|
||||
<dl><div><dt>운영</dt><dd>예시 11:00–21:30</dd></div><div><dt>마지막 주문</dt><dd>예시 21:00</dd></div></dl>
|
||||
<ul aria-label="이용 방식"><li>포장 가능 예시</li><li>배달 가능 예시</li></ul>
|
||||
<button type="button" data-select-store="seongsu">이 매장으로 조합 보기</button>
|
||||
</article>
|
||||
|
||||
<article class="store-card" data-store="yeonnam" data-region="마포" data-search="연남 예시점 마포 연남동" tabindex="-1">
|
||||
<div class="store-card-top"><span class="store-state state-closed">영업 종료 예시</span><span>예시 매장</span></div>
|
||||
<h3>연남 예시점</h3>
|
||||
<p>서울 마포구 연남동 일대 · 상세 주소 없음</p>
|
||||
<dl><div><dt>운영</dt><dd>예시 11:30–20:30</dd></div><div><dt>마지막 주문</dt><dd>오늘 마감</dd></div></dl>
|
||||
<ul aria-label="이용 방식"><li>포장 마감</li><li>배달 마감</li></ul>
|
||||
<button type="button" data-select-store="yeonnam">종료 상태로 조합 보기</button>
|
||||
</article>
|
||||
|
||||
<article class="store-card" data-store="mullae" data-region="영등포" data-search="문래 예시점 영등포 문래동" tabindex="-1">
|
||||
<div class="store-card-top"><span class="store-state state-pause">오늘은 쉬어요</span><span>예시 매장</span></div>
|
||||
<h3>문래 예시점</h3>
|
||||
<p>서울 영등포구 문래동 일대 · 상세 주소 없음</p>
|
||||
<dl><div><dt>운영</dt><dd>임시 휴무 예시</dd></div><div><dt>다음 확인</dt><dd>매장별 안내 필요</dd></div></dl>
|
||||
<ul aria-label="이용 방식"><li>포장 불가</li><li>배달 불가</li></ul>
|
||||
<button type="button" data-select-store="mullae">휴무 상태로 조합 보기</button>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<aside class="store-map" aria-label="예시 매장 분포 그림">
|
||||
<div class="map-grid" aria-hidden="true"></div>
|
||||
<span class="map-river" aria-hidden="true"></span>
|
||||
<button type="button" class="map-pin pin-seongsu is-active" data-map-store="seongsu" aria-label="성수 예시점 고르기"><span>성수</span></button>
|
||||
<button type="button" class="map-pin pin-yeonnam" data-map-store="yeonnam" aria-label="연남 예시점 고르기"><span>연남</span></button>
|
||||
<button type="button" class="map-pin pin-mullae" data-map-store="mullae" aria-label="문래 예시점 고르기"><span>문래</span></button>
|
||||
<p>실제 지도와 위치가 아닌 화면 설명용 도식</p>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div class="empty-state store-empty" id="store-empty" hidden role="status">
|
||||
<strong>찾는 조건과 맞는 예시 매장이 없어요.</strong>
|
||||
<p>검색어와 지역 조건을 지우고 다시 확인해 주세요.</p>
|
||||
<button type="button" id="reset-store-search">검색 조건 지우기</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="franchise-callout" aria-labelledby="franchise-heading">
|
||||
<figure class="franchise-image image-shell">
|
||||
<img
|
||||
src="./assets/kitchen-flow.webp"
|
||||
width="1400"
|
||||
height="933"
|
||||
alt="좁고 밝은 주방에서 두 직원이 철판 조리와 포장을 나누어 맡는 모습"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span class="image-fallback" aria-hidden="true">주방 운영 동선 장면</span>
|
||||
</figure>
|
||||
<div class="franchise-copy">
|
||||
<p class="section-kicker">가맹을 생각한다면</p>
|
||||
<h2 id="franchise-heading">매출 약속보다<br />운영 조건부터.</h2>
|
||||
<p>상권, 점포, 면적, 조리 동선과 비용 미정 항목을 먼저 확인할 수 있게 분리했습니다.</p>
|
||||
<a class="button button-light" href="./franchise/">가맹 운영 조건 보기 <span aria-hidden="true">→</span></a>
|
||||
<small>실제 가맹 모집과 상담이 아닌 쇼케이스용 흐름입니다.</small>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="site-foot">
|
||||
<div class="wrap foot-grid">
|
||||
<div><a class="brand brand-foot" href="#top"><span class="brand-mark" aria-hidden="true"><i></i><i></i><i></i></span><span>철판클럽</span></a><p>눌러 굽고, 바로 건넨다.</p></div>
|
||||
<div><strong>살펴보기</strong><a href="#menu">메뉴</a><a href="#builder">조합기</a><a href="#stores">예시 매장</a><a href="./franchise/">가맹 안내</a></div>
|
||||
<div><strong>정보 안내</strong><p>알레르기 정보도 예시입니다.</p><p>원산지와 영양 정보는 실제 제품 자료가 아닙니다.</p></div>
|
||||
<div class="foot-truth"><strong>쇼케이스 고지</strong><p>가상 브랜드이며 실제 주문, 결제, 매장 방문, 가맹 문의를 제공하지 않습니다.</p><p class="mono">DESIGN SHOWCASE · 2026</p></div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
BIN
apps/site/public/work/griddle-club/og.png
Normal file
BIN
apps/site/public/work/griddle-club/og.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 477 KiB |
300
apps/site/public/work/griddle-club/scripts/franchise.js
Normal file
300
apps/site/public/work/griddle-club/scripts/franchise.js
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
(() => {
|
||||
"use strict";
|
||||
|
||||
const fixture = new URLSearchParams(window.location.search).get("fixture") || "";
|
||||
const form = document.querySelector("#franchise-form");
|
||||
const formSteps = [...document.querySelectorAll("[data-step]")];
|
||||
const indicators = [...document.querySelectorAll("[data-step-indicator]")];
|
||||
const indicatorList = document.querySelector(".step-indicator");
|
||||
const areaField = document.querySelector("#area-field");
|
||||
const areaInput = document.querySelector("#exclusive-area");
|
||||
const areaOutput = document.querySelector("#area-conversion");
|
||||
const successPanel = document.querySelector("#inquiry-success");
|
||||
const submitState = document.querySelector("#submit-state");
|
||||
const submitButton = document.querySelector("#submit-inquiry");
|
||||
let currentStep = 1;
|
||||
|
||||
const markBrokenImage = (image) => {
|
||||
const shell = image.closest(".image-shell");
|
||||
if (shell) shell.classList.add("is-broken");
|
||||
};
|
||||
|
||||
document.querySelectorAll(".image-shell img").forEach((image) => {
|
||||
image.addEventListener("error", () => markBrokenImage(image));
|
||||
if (image.complete && image.naturalWidth === 0) markBrokenImage(image);
|
||||
});
|
||||
|
||||
if (!form) return;
|
||||
|
||||
const getRadioValue = (name) => form.querySelector(`input[name="${name}"]:checked`)?.value || "";
|
||||
const getSelectValue = (name) => {
|
||||
const field = form.elements.namedItem(name);
|
||||
return field instanceof HTMLSelectElement ? field.value : "";
|
||||
};
|
||||
const getInputValue = (name) => {
|
||||
const field = form.elements.namedItem(name);
|
||||
return field instanceof HTMLInputElement ? field.value.trim() : "";
|
||||
};
|
||||
|
||||
const setStep = (step) => {
|
||||
currentStep = step;
|
||||
formSteps.forEach((section) => section.classList.toggle("is-current", Number(section.dataset.step) === step));
|
||||
indicators.forEach((item) => {
|
||||
const active = Number(item.dataset.stepIndicator) === step;
|
||||
item.classList.toggle("is-current", active);
|
||||
if (active) item.setAttribute("aria-current", "step");
|
||||
else item.removeAttribute("aria-current");
|
||||
});
|
||||
const heading = form.querySelector(`[data-step="${step}"] h3`);
|
||||
if (heading instanceof HTMLElement) {
|
||||
heading.setAttribute("tabindex", "-1");
|
||||
heading.focus({ preventScroll: true });
|
||||
}
|
||||
};
|
||||
|
||||
const clearFieldError = (fieldId, errorId) => {
|
||||
const field = document.querySelector(`#${fieldId}`);
|
||||
const error = document.querySelector(`#${errorId}`);
|
||||
const wrapper = field?.closest(".form-field");
|
||||
wrapper?.classList.remove("has-error");
|
||||
field?.removeAttribute("aria-invalid");
|
||||
field?.removeAttribute("aria-describedby");
|
||||
if (error) error.textContent = "";
|
||||
};
|
||||
|
||||
const setFieldError = (fieldId, errorId, message) => {
|
||||
const field = document.querySelector(`#${fieldId}`);
|
||||
const error = document.querySelector(`#${errorId}`);
|
||||
const wrapper = field?.closest(".form-field");
|
||||
wrapper?.classList.add("has-error");
|
||||
field?.setAttribute("aria-invalid", "true");
|
||||
field?.setAttribute("aria-describedby", errorId);
|
||||
if (error) error.textContent = message;
|
||||
return field;
|
||||
};
|
||||
|
||||
const clearRadioError = (name, errorId) => {
|
||||
const radios = [...form.querySelectorAll(`input[name="${name}"]`)];
|
||||
const wrapper = radios[0]?.closest(".form-field");
|
||||
wrapper?.classList.remove("has-error");
|
||||
radios.forEach((radio) => {
|
||||
radio.removeAttribute("aria-invalid");
|
||||
radio.removeAttribute("aria-describedby");
|
||||
});
|
||||
const error = document.querySelector(`#${errorId}`);
|
||||
if (error) error.textContent = "";
|
||||
};
|
||||
|
||||
const setRadioError = (name, errorId, message) => {
|
||||
const radios = [...form.querySelectorAll(`input[name="${name}"]`)];
|
||||
const wrapper = radios[0]?.closest(".form-field");
|
||||
wrapper?.classList.add("has-error");
|
||||
radios.forEach((radio) => {
|
||||
radio.setAttribute("aria-invalid", "true");
|
||||
radio.setAttribute("aria-describedby", errorId);
|
||||
});
|
||||
const error = document.querySelector(`#${errorId}`);
|
||||
if (error) error.textContent = message;
|
||||
return radios[0];
|
||||
};
|
||||
|
||||
const validateStepOne = () => {
|
||||
const invalid = [];
|
||||
clearFieldError("demo-name", "demo-name-error");
|
||||
clearFieldError("region", "region-error");
|
||||
clearRadioError("areaKind", "area-kind-error");
|
||||
|
||||
if (getInputValue("demoName").length < 2) {
|
||||
invalid.push(setFieldError("demo-name", "demo-name-error", "예시 호칭을 두 글자 이상 입력해 주세요."));
|
||||
}
|
||||
if (!getSelectValue("region")) {
|
||||
invalid.push(setFieldError("region", "region-error", "희망 지역을 골라 주세요."));
|
||||
}
|
||||
if (!getRadioValue("areaKind")) {
|
||||
invalid.push(setRadioError("areaKind", "area-kind-error", "희망 상권을 골라 주세요."));
|
||||
}
|
||||
invalid[0]?.focus();
|
||||
return invalid.length === 0;
|
||||
};
|
||||
|
||||
const requiresArea = () => ["협의 중", "보유함"].includes(getRadioValue("storePossession"));
|
||||
|
||||
const updateAreaField = () => {
|
||||
const required = requiresArea();
|
||||
if (areaField) areaField.hidden = !required;
|
||||
if (areaInput instanceof HTMLInputElement) {
|
||||
areaInput.required = required;
|
||||
if (!required) {
|
||||
areaInput.value = "";
|
||||
clearFieldError("exclusive-area", "exclusive-area-error");
|
||||
if (areaOutput) areaOutput.textContent = "면적을 입력하면 평으로도 보여 드립니다.";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const updateAreaConversion = () => {
|
||||
if (!(areaInput instanceof HTMLInputElement) || !areaOutput) return;
|
||||
const squareMeters = Number(areaInput.value);
|
||||
if (!Number.isFinite(squareMeters) || squareMeters <= 0) {
|
||||
areaOutput.textContent = "면적을 입력하면 평으로도 보여 드립니다.";
|
||||
return;
|
||||
}
|
||||
const pyeong = Math.round((squareMeters / 3.3058) * 10) / 10;
|
||||
areaOutput.textContent = `${squareMeters.toLocaleString("ko-KR")}㎡ · 약 ${pyeong.toLocaleString("ko-KR")}평`;
|
||||
};
|
||||
|
||||
const validateStepTwo = () => {
|
||||
const invalid = [];
|
||||
clearRadioError("storePossession", "store-possession-error");
|
||||
clearFieldError("exclusive-area", "exclusive-area-error");
|
||||
clearFieldError("opening-window", "opening-window-error");
|
||||
clearRadioError("experience", "experience-error");
|
||||
|
||||
if (!getRadioValue("storePossession")) {
|
||||
invalid.push(setRadioError("storePossession", "store-possession-error", "점포 상태를 골라 주세요."));
|
||||
}
|
||||
if (requiresArea()) {
|
||||
const value = Number(getInputValue("exclusiveArea"));
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
invalid.push(setFieldError("exclusive-area", "exclusive-area-error", "면적은 0보다 큰 숫자로 입력해 주세요."));
|
||||
}
|
||||
}
|
||||
if (!getSelectValue("openingWindow")) {
|
||||
invalid.push(setFieldError("opening-window", "opening-window-error", "희망 시작 시기를 골라 주세요."));
|
||||
}
|
||||
if (!getRadioValue("experience")) {
|
||||
invalid.push(setRadioError("experience", "experience-error", "운영 경험 여부를 골라 주세요."));
|
||||
}
|
||||
invalid[0]?.focus();
|
||||
return invalid.length === 0;
|
||||
};
|
||||
|
||||
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")}평`
|
||||
: "해당 없음";
|
||||
const values = {
|
||||
demoName: getInputValue("demoName"),
|
||||
region: getSelectValue("region"),
|
||||
areaKind: getRadioValue("areaKind"),
|
||||
storePossession: getRadioValue("storePossession"),
|
||||
exclusiveArea: areaLabel,
|
||||
openingWindow: getSelectValue("openingWindow"),
|
||||
experience: getRadioValue("experience"),
|
||||
};
|
||||
Object.entries(values).forEach(([key, value]) => {
|
||||
const target = document.querySelector(`[data-review="${key}"]`);
|
||||
if (target) target.textContent = value || "—";
|
||||
});
|
||||
};
|
||||
|
||||
form.querySelectorAll("[data-next]").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
if (currentStep === 1 && validateStepOne()) setStep(2);
|
||||
else if (currentStep === 2 && validateStepTwo()) {
|
||||
reviewValues();
|
||||
setStep(3);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
form.querySelectorAll("[data-back]").forEach((button) => {
|
||||
button.addEventListener("click", () => setStep(Math.max(1, currentStep - 1)));
|
||||
});
|
||||
|
||||
form.querySelectorAll("input[name='storePossession']").forEach((input) => {
|
||||
input.addEventListener("change", updateAreaField);
|
||||
});
|
||||
areaInput?.addEventListener("input", updateAreaConversion);
|
||||
|
||||
form.addEventListener("input", (event) => {
|
||||
const field = event.target;
|
||||
if (!(field instanceof HTMLElement) || !field.id) return;
|
||||
const errorId = `${field.id}-error`;
|
||||
if (document.querySelector(`#${errorId}`)) clearFieldError(field.id, errorId);
|
||||
});
|
||||
|
||||
form.addEventListener("change", (event) => {
|
||||
const field = event.target;
|
||||
if (!(field instanceof HTMLInputElement)) return;
|
||||
if (field.type === "radio") {
|
||||
const names = {
|
||||
areaKind: "area-kind-error",
|
||||
storePossession: "store-possession-error",
|
||||
experience: "experience-error",
|
||||
};
|
||||
if (names[field.name]) clearRadioError(field.name, names[field.name]);
|
||||
}
|
||||
});
|
||||
|
||||
form.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
const consent = document.querySelector("#demo-consent");
|
||||
const consentError = document.querySelector("#demo-consent-error");
|
||||
if (consent instanceof HTMLInputElement) {
|
||||
consent.removeAttribute("aria-invalid");
|
||||
consent.removeAttribute("aria-describedby");
|
||||
}
|
||||
if (consentError) consentError.textContent = "";
|
||||
if (!(consent instanceof HTMLInputElement) || !consent.checked) {
|
||||
consent?.setAttribute("aria-invalid", "true");
|
||||
consent?.setAttribute("aria-describedby", "demo-consent-error");
|
||||
if (consentError) consentError.textContent = "문의 체험 안내를 확인해 주세요.";
|
||||
consent?.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (submitButton instanceof HTMLButtonElement) {
|
||||
submitButton.disabled = true;
|
||||
submitButton.textContent = "화면 상태 확인 중";
|
||||
}
|
||||
if (submitState) submitState.textContent = "";
|
||||
|
||||
window.setTimeout(() => {
|
||||
if (fixture === "inquiry-fail") {
|
||||
if (submitButton instanceof HTMLButtonElement) {
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = "다시 시도하기";
|
||||
}
|
||||
if (submitState) {
|
||||
submitState.textContent = "지금은 문의 준비 상태를 바꾸지 못했어요. 입력 내용은 화면에 그대로 두었습니다.";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
form.hidden = true;
|
||||
if (indicatorList) indicatorList.hidden = true;
|
||||
if (successPanel) {
|
||||
successPanel.hidden = false;
|
||||
successPanel.focus();
|
||||
}
|
||||
if (submitButton instanceof HTMLButtonElement) {
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = "문의 준비 마치기";
|
||||
}
|
||||
}, 450);
|
||||
});
|
||||
|
||||
document.querySelector("#review-again")?.addEventListener("click", () => {
|
||||
if (successPanel) successPanel.hidden = true;
|
||||
form.hidden = false;
|
||||
if (indicatorList) indicatorList.hidden = false;
|
||||
setStep(3);
|
||||
});
|
||||
|
||||
document.querySelector("#restart-form")?.addEventListener("click", () => {
|
||||
form.reset();
|
||||
form.hidden = false;
|
||||
if (successPanel) successPanel.hidden = true;
|
||||
if (indicatorList) indicatorList.hidden = false;
|
||||
if (submitState) submitState.textContent = "";
|
||||
document.querySelectorAll(".field-error").forEach((error) => { error.textContent = ""; });
|
||||
document.querySelectorAll(".has-error").forEach((field) => field.classList.remove("has-error"));
|
||||
updateAreaField();
|
||||
setStep(1);
|
||||
});
|
||||
|
||||
updateAreaField();
|
||||
setStep(1);
|
||||
})();
|
||||
261
apps/site/public/work/griddle-club/scripts/site.js
Normal file
261
apps/site/public/work/griddle-club/scripts/site.js
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
(() => {
|
||||
"use strict";
|
||||
|
||||
const fixture = new URLSearchParams(window.location.search).get("fixture") || "";
|
||||
const formatWon = (value) => `${new Intl.NumberFormat("ko-KR").format(value)}원`;
|
||||
|
||||
const markBrokenImage = (image) => {
|
||||
const shell = image.closest(".image-shell");
|
||||
if (shell) shell.classList.add("is-broken");
|
||||
};
|
||||
|
||||
document.querySelectorAll(".image-shell img").forEach((image) => {
|
||||
image.addEventListener("error", () => markBrokenImage(image));
|
||||
if (image.complete && image.naturalWidth === 0) markBrokenImage(image);
|
||||
});
|
||||
|
||||
const menuButtons = [...document.querySelectorAll("[data-menu-filter]")];
|
||||
const menuItems = [...document.querySelectorAll("[data-menu-category]")];
|
||||
const menuEmpty = document.querySelector("#menu-empty");
|
||||
|
||||
const setMenuFilter = (filter) => {
|
||||
const forceMenuEmpty = new URLSearchParams(window.location.search).get("fixture") === "menu-empty";
|
||||
let visibleCount = 0;
|
||||
menuButtons.forEach((button) => {
|
||||
const active = button.dataset.menuFilter === filter;
|
||||
button.classList.toggle("is-active", active);
|
||||
button.setAttribute("aria-pressed", String(active));
|
||||
});
|
||||
|
||||
menuItems.forEach((item) => {
|
||||
const visible = !forceMenuEmpty && (filter === "all" || item.dataset.menuCategory === filter);
|
||||
item.hidden = !visible;
|
||||
if (visible) visibleCount += 1;
|
||||
});
|
||||
|
||||
if (menuEmpty) menuEmpty.hidden = visibleCount > 0;
|
||||
};
|
||||
|
||||
menuButtons.forEach((button) => {
|
||||
button.addEventListener("click", () => setMenuFilter(button.dataset.menuFilter || "all"));
|
||||
});
|
||||
|
||||
document.querySelector("[data-show-all-menu]")?.addEventListener("click", () => {
|
||||
if (fixture === "menu-empty") {
|
||||
const nextUrl = new URL(window.location.href);
|
||||
nextUrl.searchParams.delete("fixture");
|
||||
window.history.replaceState({}, "", nextUrl);
|
||||
}
|
||||
setMenuFilter("all");
|
||||
});
|
||||
|
||||
if (fixture === "menu-empty") setMenuFilter("all");
|
||||
|
||||
const builder = document.querySelector("#burger-builder");
|
||||
const builderSection = document.querySelector("#builder");
|
||||
const setOptions = document.querySelector("#set-options");
|
||||
const summaryHeading = document.querySelector("#summary-heading");
|
||||
const summaryLines = document.querySelector("#summary-lines");
|
||||
const summaryServing = document.querySelector("#summary-serving");
|
||||
const summaryStore = document.querySelector("#summary-store");
|
||||
const summaryStoreState = document.querySelector("#summary-store-state");
|
||||
const summaryAllergens = document.querySelector("#summary-allergens");
|
||||
const summaryTotal = document.querySelector("#summary-total");
|
||||
const saveButton = document.querySelector("#save-builder");
|
||||
const saveStatus = document.querySelector("#save-status");
|
||||
|
||||
const stores = {
|
||||
seongsu: { label: "성수 예시점", state: "영업 중 예시", available: true },
|
||||
yeonnam: { label: "연남 예시점", state: "오늘 영업 종료 예시", available: false },
|
||||
mullae: { label: "문래 예시점", state: "오늘은 쉬어요", available: false },
|
||||
};
|
||||
|
||||
const fieldValue = (name) => {
|
||||
const field = builder?.elements.namedItem(name);
|
||||
if (!field) return "";
|
||||
if (field instanceof RadioNodeList) return field.value;
|
||||
return field.value;
|
||||
};
|
||||
|
||||
const setRadio = (name, value) => {
|
||||
const radio = builder?.querySelector(`input[name="${name}"][value="${value}"]`);
|
||||
if (radio instanceof HTMLInputElement) radio.checked = true;
|
||||
};
|
||||
|
||||
const builderPrice = ({ flavor, patty, cheese, serving }) => {
|
||||
const flavorBase = flavor === "spicy" ? 7400 : 6900;
|
||||
const pattyAdd = Number(patty) === 2 ? 2000 : 0;
|
||||
const cheeseAdd = Number(cheese) === 0 ? -400 : Number(cheese) === 2 ? 400 : 0;
|
||||
const setAdd = serving === "set" ? 4100 : 0;
|
||||
return flavorBase + pattyAdd + cheeseAdd + setAdd;
|
||||
};
|
||||
|
||||
const updateBuilder = () => {
|
||||
if (!builder) return;
|
||||
const flavor = fieldValue("flavor");
|
||||
const patty = fieldValue("patty");
|
||||
const cheese = fieldValue("cheese");
|
||||
const onion = fieldValue("onion");
|
||||
const pickle = fieldValue("pickle");
|
||||
const sauce = fieldValue("sauce");
|
||||
const serving = fieldValue("serving");
|
||||
const storeKey = fieldValue("store");
|
||||
const store = stores[storeKey] || stores.seongsu;
|
||||
const soldOut = fixture === "sold-out";
|
||||
|
||||
const setEnabled = serving === "set";
|
||||
builder.querySelectorAll("select[name='side'], select[name='drink']").forEach((select) => {
|
||||
select.disabled = !setEnabled;
|
||||
});
|
||||
setOptions?.classList.toggle("is-disabled", !setEnabled);
|
||||
|
||||
const names = {
|
||||
onion: { none: "양파 없음", regular: "양파 기본", extra: "양파 많이" },
|
||||
pickle: { none: "피클 없음", regular: "피클 기본", extra: "피클 많이" },
|
||||
sauce: { none: "소스 없음", regular: "소스 기본", side: "소스 따로" },
|
||||
};
|
||||
const pattyLabel = Number(patty) === 2 ? "더블" : "싱글";
|
||||
const flavorLabel = flavor === "spicy" ? "고추 피클" : "철판";
|
||||
const cheeseLabel = Number(cheese) === 0 ? "치즈 없음" : `치즈 ${cheese === "2" ? "두" : "한"} 장`;
|
||||
const productName = `${flavorLabel} ${pattyLabel}`;
|
||||
const servingLabel = serving === "set" ? "세트 · 얇은 감자와 음료" : "단품";
|
||||
|
||||
if (summaryHeading) summaryHeading.textContent = productName;
|
||||
if (summaryLines) {
|
||||
summaryLines.textContent = `패티 ${patty === "2" ? "두" : "한"} 장 / ${cheeseLabel} / ${names.onion[onion]} / ${names.pickle[pickle]} / ${names.sauce[sauce]}`;
|
||||
}
|
||||
if (summaryServing) summaryServing.textContent = servingLabel;
|
||||
if (summaryStore) summaryStore.textContent = store.label;
|
||||
if (summaryStoreState) summaryStoreState.textContent = soldOut ? "오늘 품절 예시" : store.state;
|
||||
|
||||
const allergens = ["알", "대두", "밀", "쇠고기"];
|
||||
if (Number(cheese) > 0) allergens.splice(1, 0, "우유");
|
||||
if (flavor === "spicy") allergens.push("토마토");
|
||||
if (summaryAllergens) summaryAllergens.textContent = allergens.join(", ");
|
||||
if (summaryTotal) summaryTotal.textContent = formatWon(builderPrice({ flavor, patty, cheese, serving }));
|
||||
|
||||
if (saveButton) {
|
||||
saveButton.disabled = soldOut || !store.available;
|
||||
if (soldOut) saveButton.textContent = "오늘 품절 예시";
|
||||
else if (!store.available) saveButton.textContent = "다른 매장을 골라 주세요";
|
||||
else saveButton.textContent = "선택 내용 저장";
|
||||
}
|
||||
if (saveStatus) {
|
||||
saveStatus.textContent = "";
|
||||
saveStatus.classList.remove("is-error");
|
||||
}
|
||||
};
|
||||
|
||||
builder?.addEventListener("change", updateBuilder);
|
||||
|
||||
const applyPreset = (preset) => {
|
||||
if (!builder) return;
|
||||
if (preset === "single") {
|
||||
setRadio("flavor", "classic");
|
||||
setRadio("patty", "1");
|
||||
} else if (preset === "spicy") {
|
||||
setRadio("flavor", "spicy");
|
||||
setRadio("patty", "2");
|
||||
} else {
|
||||
setRadio("flavor", "classic");
|
||||
setRadio("patty", "2");
|
||||
}
|
||||
updateBuilder();
|
||||
builderSection?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
window.setTimeout(() => builder.querySelector("input:checked")?.focus({ preventScroll: true }), 350);
|
||||
};
|
||||
|
||||
document.querySelectorAll("[data-preset]").forEach((button) => {
|
||||
button.addEventListener("click", () => applyPreset(button.dataset.preset || "double"));
|
||||
});
|
||||
|
||||
saveButton?.addEventListener("click", () => {
|
||||
if (!builder || !saveStatus) return;
|
||||
const snapshot = Object.fromEntries(new FormData(builder).entries());
|
||||
try {
|
||||
if (fixture === "save-fail") throw new Error("fixture");
|
||||
sessionStorage.setItem("griddle-club-demo-selection", JSON.stringify(snapshot));
|
||||
saveStatus.classList.remove("is-error");
|
||||
saveStatus.textContent = "이 기기의 현재 탭에 선택 내용을 남겼어요. 실제 주문으로 전송되지 않습니다.";
|
||||
} catch {
|
||||
saveStatus.classList.add("is-error");
|
||||
saveStatus.textContent = "선택 내용을 남기지 못했어요. 고른 내용은 화면에 그대로 두었습니다.";
|
||||
}
|
||||
});
|
||||
|
||||
const storeCards = [...document.querySelectorAll("[data-store]")];
|
||||
const mapPins = [...document.querySelectorAll("[data-map-store]")];
|
||||
const storeSearch = document.querySelector("#store-search");
|
||||
const clearStoreSearch = document.querySelector("#clear-store-search");
|
||||
const storeEmpty = document.querySelector("#store-empty");
|
||||
const regionButtons = [...document.querySelectorAll("[data-region]")];
|
||||
let activeRegion = "all";
|
||||
|
||||
const selectStore = (storeKey, shouldMove = false) => {
|
||||
const select = builder?.elements.namedItem("store");
|
||||
if (select instanceof HTMLSelectElement) select.value = storeKey;
|
||||
storeCards.forEach((card) => card.classList.toggle("is-selected", card.dataset.store === storeKey));
|
||||
mapPins.forEach((pin) => pin.classList.toggle("is-active", pin.dataset.mapStore === storeKey));
|
||||
updateBuilder();
|
||||
if (shouldMove) builderSection?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
};
|
||||
|
||||
document.querySelectorAll("[data-select-store]").forEach((button) => {
|
||||
button.addEventListener("click", () => selectStore(button.dataset.selectStore, true));
|
||||
});
|
||||
mapPins.forEach((button) => {
|
||||
button.addEventListener("click", () => selectStore(button.dataset.mapStore, false));
|
||||
});
|
||||
|
||||
const filterStores = () => {
|
||||
const query = storeSearch instanceof HTMLInputElement ? storeSearch.value.trim().toLocaleLowerCase("ko-KR") : "";
|
||||
let visibleCount = 0;
|
||||
storeCards.forEach((card) => {
|
||||
const regionMatch = activeRegion === "all" || card.dataset.region === activeRegion;
|
||||
const queryMatch = !query || (card.dataset.search || "").toLocaleLowerCase("ko-KR").includes(query);
|
||||
const visible = fixture !== "store-none" && regionMatch && queryMatch;
|
||||
card.hidden = !visible;
|
||||
if (visible) visibleCount += 1;
|
||||
const pin = mapPins.find((item) => item.dataset.mapStore === card.dataset.store);
|
||||
if (pin) pin.hidden = !visible;
|
||||
});
|
||||
if (storeEmpty) storeEmpty.hidden = visibleCount > 0;
|
||||
if (clearStoreSearch) clearStoreSearch.hidden = !query;
|
||||
};
|
||||
|
||||
storeSearch?.addEventListener("input", filterStores);
|
||||
clearStoreSearch?.addEventListener("click", () => {
|
||||
if (storeSearch instanceof HTMLInputElement) {
|
||||
storeSearch.value = "";
|
||||
storeSearch.focus();
|
||||
}
|
||||
filterStores();
|
||||
});
|
||||
|
||||
regionButtons.forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
activeRegion = button.dataset.region || "all";
|
||||
regionButtons.forEach((item) => {
|
||||
const active = item === button;
|
||||
item.classList.toggle("is-active", active);
|
||||
item.setAttribute("aria-pressed", String(active));
|
||||
});
|
||||
filterStores();
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelector("#reset-store-search")?.addEventListener("click", () => {
|
||||
activeRegion = "all";
|
||||
if (storeSearch instanceof HTMLInputElement) storeSearch.value = "";
|
||||
regionButtons.forEach((button) => {
|
||||
const active = button.dataset.region === "all";
|
||||
button.classList.toggle("is-active", active);
|
||||
button.setAttribute("aria-pressed", String(active));
|
||||
});
|
||||
filterStores();
|
||||
storeSearch?.focus();
|
||||
});
|
||||
|
||||
updateBuilder();
|
||||
filterStores();
|
||||
})();
|
||||
979
apps/site/public/work/griddle-club/styles/franchise.css
Normal file
979
apps/site/public/work/griddle-club/styles/franchise.css
Normal file
|
|
@ -0,0 +1,979 @@
|
|||
.franchise-page {
|
||||
background: var(--paper-bright);
|
||||
}
|
||||
|
||||
.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: #8ea5ff;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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-truth {
|
||||
max-width: 560px;
|
||||
margin-top: 52px;
|
||||
padding: 18px 20px;
|
||||
border-left: 3px solid #8ea5ff;
|
||||
background: #2b2926;
|
||||
}
|
||||
|
||||
.franchise-truth strong {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.franchise-truth p {
|
||||
margin-top: 5px;
|
||||
color: #aaa59d;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.franchise-hero-media {
|
||||
min-height: 680px;
|
||||
overflow: hidden;
|
||||
background: var(--steel);
|
||||
}
|
||||
|
||||
.franchise-hero-media img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.franchise-hero-media figcaption {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
padding: 7px 10px;
|
||||
background: var(--paper-bright);
|
||||
color: var(--ink);
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.fact-rail {
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
.fact-rail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.fact-rail p {
|
||||
min-height: 92px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 18px 28px;
|
||||
border-right: 1px solid var(--line);
|
||||
font-size: 13px;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.fact-rail p:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.fact-rail p:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.fact-rail span,
|
||||
.fit-number {
|
||||
color: var(--blue);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.model-section {
|
||||
background: var(--paper-bright);
|
||||
}
|
||||
|
||||
.flow-board {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
border: 1px solid var(--ink);
|
||||
}
|
||||
|
||||
.flow-board article {
|
||||
min-height: 340px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
padding: clamp(22px, 3vw, 34px);
|
||||
border-right: 1px solid var(--ink);
|
||||
}
|
||||
|
||||
.flow-board article:last-child {
|
||||
border-right: 0;
|
||||
background: var(--blue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.flow-board article > span {
|
||||
margin-bottom: auto;
|
||||
color: var(--blue);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.flow-board article:last-child > span {
|
||||
color: #c8d3ff;
|
||||
}
|
||||
|
||||
.flow-board h3 {
|
||||
font-size: 23px;
|
||||
letter-spacing: -0.045em;
|
||||
}
|
||||
|
||||
.flow-board p {
|
||||
margin-top: 8px;
|
||||
color: var(--ink-soft);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.flow-board article:last-child p {
|
||||
color: #dce4ff;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.operator-fit h3 {
|
||||
margin-top: 13px;
|
||||
font-size: clamp(34px, 4.2vw, 54px);
|
||||
letter-spacing: -0.065em;
|
||||
line-height: 1.12;
|
||||
}
|
||||
|
||||
.operator-fit ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.operator-fit li {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
gap: 20px;
|
||||
padding: 19px 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.operator-fit span {
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
.site-fit-section {
|
||||
background: var(--steel);
|
||||
}
|
||||
|
||||
.fit-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
border-top: 1px solid var(--ink);
|
||||
border-left: 1px solid var(--ink);
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.fit-grid .fit-main {
|
||||
grid-column: span 2;
|
||||
grid-row: span 2;
|
||||
min-height: 600px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--ink);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.fit-grid h3 {
|
||||
margin-top: 46px;
|
||||
font-size: clamp(28px, 3.5vw, 48px);
|
||||
letter-spacing: -0.06em;
|
||||
}
|
||||
|
||||
.fit-grid article > p:not(.fit-number) {
|
||||
margin-top: 14px;
|
||||
color: var(--ink-soft);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.fit-grid .fit-main > p:not(.fit-number) {
|
||||
max-width: 490px;
|
||||
color: #d4cec5;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.fit-main .fit-number {
|
||||
color: #9db0ff;
|
||||
}
|
||||
|
||||
.fit-main ul {
|
||||
margin: auto 0 0;
|
||||
padding: 24px 0 0;
|
||||
border-top: 1px solid #625f5a;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.fit-main li {
|
||||
padding: 8px 0;
|
||||
color: #e8e3db;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.franchise-process {
|
||||
background: var(--paper-bright);
|
||||
}
|
||||
|
||||
.franchise-timeline {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
border-top: 1px solid var(--ink);
|
||||
}
|
||||
|
||||
.franchise-timeline li {
|
||||
display: grid;
|
||||
grid-template-columns: 56px 1fr auto;
|
||||
align-items: center;
|
||||
gap: 28px;
|
||||
min-height: 124px;
|
||||
padding: 22px 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.franchise-timeline li > span {
|
||||
color: var(--blue);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.franchise-timeline h3 {
|
||||
font-size: 24px;
|
||||
letter-spacing: -0.045em;
|
||||
}
|
||||
|
||||
.franchise-timeline p {
|
||||
margin-top: 3px;
|
||||
color: var(--ink-soft);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.franchise-timeline small {
|
||||
padding: 6px 9px;
|
||||
border: 1px solid var(--line);
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.cost-section {
|
||||
background: var(--ink);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cost-section .section-kicker {
|
||||
color: #8ea5ff;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.unknown-cost {
|
||||
padding: 22px;
|
||||
border: 1px solid #625f5a;
|
||||
background: #2a2825;
|
||||
}
|
||||
|
||||
.unknown-cost span {
|
||||
color: #aaa59d;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.unknown-cost strong {
|
||||
display: block;
|
||||
margin-top: 3px;
|
||||
color: #8ea5ff;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.unknown-cost p {
|
||||
margin-top: 7px;
|
||||
color: #bdb7af;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.cost-table-wrap {
|
||||
overflow-x: auto;
|
||||
border-top: 1px solid #827d75;
|
||||
}
|
||||
|
||||
.cost-table {
|
||||
width: 100%;
|
||||
min-width: 780px;
|
||||
border-collapse: collapse;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.cost-table th,
|
||||
.cost-table td {
|
||||
padding: 17px 14px;
|
||||
border-bottom: 1px solid #4f4c47;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.cost-table thead th {
|
||||
color: #9f9a92;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.cost-table tbody th {
|
||||
color: white;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.cost-table td {
|
||||
color: #c4beb6;
|
||||
}
|
||||
|
||||
.cost-table td:nth-child(3) {
|
||||
color: #8ea5ff;
|
||||
font-family: var(--font-mono);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cost-table tfoot th,
|
||||
.cost-table tfoot td {
|
||||
border-bottom: 0;
|
||||
background: var(--blue);
|
||||
color: white;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.cost-warning {
|
||||
margin-top: 18px;
|
||||
color: #aaa59d;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.inquiry-section {
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
.inquiry-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 0.7fr) minmax(480px, 1.3fr);
|
||||
gap: clamp(44px, 8vw, 110px);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.inquiry-intro {
|
||||
position: sticky;
|
||||
top: calc(var(--header-height) + 32px);
|
||||
}
|
||||
|
||||
.inquiry-intro h2 {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.inquiry-intro > p:not(.section-kicker) {
|
||||
max-width: 460px;
|
||||
margin-top: 26px;
|
||||
color: var(--ink-soft);
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.privacy-note {
|
||||
margin-top: 32px;
|
||||
padding: 18px;
|
||||
border-left: 3px solid var(--danger);
|
||||
background: #fff0eb;
|
||||
}
|
||||
|
||||
.privacy-note strong {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.privacy-note p {
|
||||
margin-top: 5px;
|
||||
color: #733c33;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.inquiry-panel {
|
||||
border: 1px solid var(--line-strong);
|
||||
background: var(--paper-bright);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.step-indicator {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.step-indicator li {
|
||||
min-height: 76px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 18px;
|
||||
border-right: 1px solid var(--line);
|
||||
color: var(--ink-faint);
|
||||
}
|
||||
|
||||
.step-indicator li:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.step-indicator li.is-current {
|
||||
background: var(--blue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.step-indicator span {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.step-indicator small {
|
||||
font-size: 11px;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.form-step,
|
||||
.inquiry-success {
|
||||
padding: clamp(26px, 5vw, 50px);
|
||||
}
|
||||
|
||||
.is-js .form-step:not(.is-current) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.step-count {
|
||||
color: var(--blue);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.form-field > label,
|
||||
.form-field > legend {
|
||||
display: block;
|
||||
margin-bottom: 9px;
|
||||
font-size: 13px;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.form-field input[type="text"],
|
||||
.form-field input[type="number"],
|
||||
.form-field select {
|
||||
width: 100%;
|
||||
min-height: 52px;
|
||||
padding: 11px 13px;
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: var(--radius-sm);
|
||||
background: white;
|
||||
}
|
||||
|
||||
.form-field > small,
|
||||
.form-field output {
|
||||
display: block;
|
||||
margin-top: 7px;
|
||||
color: var(--ink-faint);
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.field-error {
|
||||
min-height: 20px;
|
||||
margin-top: 5px;
|
||||
color: var(--danger);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.form-field.has-error input,
|
||||
.form-field.has-error select,
|
||||
.form-field.has-error .radio-grid {
|
||||
border-color: var(--danger);
|
||||
}
|
||||
|
||||
.radio-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.radio-grid-two {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.radio-grid label {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.radio-grid input {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio-grid span {
|
||||
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;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.radio-grid input:checked + span {
|
||||
border-color: var(--blue);
|
||||
background: var(--blue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.radio-grid input:focus-visible + span {
|
||||
outline: 3px solid var(--focus);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.unit-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--line-strong);
|
||||
background: white;
|
||||
}
|
||||
|
||||
.unit-input input {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.unit-input span {
|
||||
padding-right: 14px;
|
||||
font-size: 12px;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 36px;
|
||||
}
|
||||
|
||||
.split-actions {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.form-actions .button-ghost {
|
||||
margin-top: 0;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.review-list {
|
||||
margin-top: 30px;
|
||||
border-top: 1px solid var(--ink);
|
||||
}
|
||||
|
||||
.review-list div {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
gap: 20px;
|
||||
padding: 14px 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.review-list dt {
|
||||
color: var(--ink-faint);
|
||||
}
|
||||
|
||||
.review-list dd {
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.consent-check {
|
||||
position: relative;
|
||||
min-height: 44px;
|
||||
display: grid;
|
||||
grid-template-columns: 20px 1fr;
|
||||
align-items: start;
|
||||
gap: 10px;
|
||||
margin-top: 24px;
|
||||
font-size: 12px;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.consent-check input {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.consent-check span {
|
||||
position: relative;
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.consent-check span::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: -30px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 1px solid var(--line-strong);
|
||||
background: white;
|
||||
}
|
||||
|
||||
.consent-check input:checked + span::before {
|
||||
border-color: var(--blue);
|
||||
background: var(--blue);
|
||||
box-shadow: inset 0 0 0 4px white;
|
||||
}
|
||||
|
||||
.consent-check input:focus-visible + span {
|
||||
outline: 3px solid var(--focus);
|
||||
outline-offset: 5px;
|
||||
}
|
||||
|
||||
.submit-state {
|
||||
min-height: 24px;
|
||||
margin-top: 12px;
|
||||
color: var(--danger);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.inquiry-success {
|
||||
min-height: 580px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.success-mark {
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
margin-bottom: 26px;
|
||||
background: var(--blue);
|
||||
color: white;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.inquiry-success > p:not(.section-kicker) {
|
||||
max-width: 460px;
|
||||
margin-top: 15px;
|
||||
color: var(--ink-soft);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.success-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.success-actions .button {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.franchise-close {
|
||||
padding-block: var(--section);
|
||||
background: var(--blue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.close-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
align-items: end;
|
||||
gap: 44px;
|
||||
}
|
||||
|
||||
.franchise-close .section-kicker {
|
||||
color: #c8d3ff;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
.cost-head,
|
||||
.inquiry-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.inquiry-intro {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.franchise-foot-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.franchise-hero {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.franchise-hero-copy {
|
||||
padding: 72px var(--gutter) 52px;
|
||||
}
|
||||
|
||||
.franchise-hero h1 {
|
||||
font-size: clamp(40px, 13.5vw, 60px);
|
||||
}
|
||||
|
||||
.franchise-hero-media {
|
||||
min-height: 560px;
|
||||
}
|
||||
|
||||
.fact-rail-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.fact-rail p,
|
||||
.fact-rail p:first-child {
|
||||
min-height: 64px;
|
||||
padding: 14px 0;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.operator-fit {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 34px;
|
||||
}
|
||||
|
||||
.fit-grid .fit-main {
|
||||
min-height: 520px;
|
||||
}
|
||||
|
||||
.franchise-timeline li {
|
||||
grid-template-columns: 42px 1fr;
|
||||
}
|
||||
|
||||
.franchise-timeline small {
|
||||
grid-column: 2;
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.close-row {
|
||||
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,
|
||||
.flow-board article:nth-child(2) {
|
||||
min-height: 170px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.flow-board article > span {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.fit-grid .fit-main {
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.operator-fit li {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.step-indicator li {
|
||||
min-height: 64px;
|
||||
justify-content: center;
|
||||
padding: 9px;
|
||||
}
|
||||
|
||||
.step-indicator small {
|
||||
display: inline;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.radio-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.form-actions,
|
||||
.split-actions,
|
||||
.success-actions {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.form-actions .button,
|
||||
.success-actions .button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.review-list div {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 3px;
|
||||
}
|
||||
}
|
||||
1807
apps/site/public/work/griddle-club/styles/site.css
Normal file
1807
apps/site/public/work/griddle-club/styles/site.css
Normal file
File diff suppressed because it is too large
Load diff
43
apps/site/public/work/griddle-club/styles/tokens.css
Normal file
43
apps/site/public/work/griddle-club/styles/tokens.css
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
:root {
|
||||
color-scheme: light;
|
||||
--paper: #f4f1e9;
|
||||
--paper-bright: #fffdf8;
|
||||
--ink: #211f1c;
|
||||
--ink-soft: #56514a;
|
||||
--ink-faint: #625d56;
|
||||
--line: #c9c2b7;
|
||||
--line-strong: #8a8379;
|
||||
--blue: #2147e7;
|
||||
--blue-dark: #1533ac;
|
||||
--blue-pale: #dce4ff;
|
||||
--steel: #d9dcda;
|
||||
--cheese: #ffc642;
|
||||
--danger: #b33124;
|
||||
--success: #17683b;
|
||||
--focus: #2147e7;
|
||||
--font-sans: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: "IBM Plex Mono", "SFMono-Regular", Consolas, monospace;
|
||||
--wrap: 1180px;
|
||||
--gutter: clamp(18px, 4vw, 52px);
|
||||
--section: clamp(76px, 10vw, 144px);
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 24px;
|
||||
--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;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
:root {
|
||||
--header-height: 64px;
|
||||
--section: 72px;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue