diff --git a/.playwright-mcp/prototype-01-login.png b/.playwright-mcp/prototype-01-login.png new file mode 100644 index 0000000..73a0a5f Binary files /dev/null and b/.playwright-mcp/prototype-01-login.png differ diff --git a/.playwright-mcp/prototype-05-dashboard.png b/.playwright-mcp/prototype-05-dashboard.png new file mode 100644 index 0000000..774f6cf Binary files /dev/null and b/.playwright-mcp/prototype-05-dashboard.png differ diff --git a/.playwright-mcp/prototype-07-purpose-loading.png b/.playwright-mcp/prototype-07-purpose-loading.png new file mode 100644 index 0000000..0b87f48 Binary files /dev/null and b/.playwright-mcp/prototype-07-purpose-loading.png differ diff --git a/design/uiux/prototype/01-로그인.html b/design/uiux/prototype/01-로그인.html new file mode 100644 index 0000000..53e723a --- /dev/null +++ b/design/uiux/prototype/01-로그인.html @@ -0,0 +1,196 @@ + + +
+ + +1/3 단계
+로그인에 사용할 이메일과 비밀번호를 설정합니다
+ + +오늘도 성공적인 이벤트를 준비해보세요
+AI가 목적에 맞는 최적의 이벤트를 추천해드립니다
++ 선택하신 목적에 따라 AI가 업종, 지역, 계절 트렌드를 분석하여 가장 효과적인 이벤트를 추천합니다. +
+음식점업 신년 프로모션 트렌드
+강남구 음식점 할인 이벤트 증가
+설 연휴 특수 대비 고객 유치 전략
++ 각 예산별 2가지 방식 (온라인 1개, 오프라인 1개)을 추천합니다 +
+모든 정보를 검토한 후 배포하세요
+이벤트 기간
+2025.02.01 ~ 2025.02.28
+목표 참여자
+180명
+예상 비용
+250,000원
+예상 ROI
+520%
+이벤트 제목
+SNS 팔로우 이벤트
+경품
+커피 쿠폰
+이벤트 설명
+
+ SNS를 팔로우하고 커피 쿠폰을 받으세요!
+ 많은 참여 부탁드립니다.
+
참여 방법
+SNS 팔로우
++ 2025.01.15 ~ 2025.02.15 +
+참여자 추이 차트
+경품
+커피 쿠폰
+참여 방법
+SNS 팔로우
+예상 비용
+250,000원
+배포 채널
+${message}
` : ''} ++ ${Utils.formatDate(startDate)} ~ ${Utils.formatDate(endDate)} +
+${description}
` : ''} + ${content || ''} + `; + + card.addEventListener('click', () => { + document.querySelectorAll('.option-card').forEach(c => c.classList.remove('selected')); + card.classList.add('selected'); + const radio = card.querySelector('input[type="radio"]'); + radio.checked = true; + + if (onSelect) { + onSelect(id); + } + }); + + return card; + } + }; + + // ================================================================= + // 6. Session Management + // ================================================================= + const Session = { + /** + * 사용자 정보 저장 + */ + saveUser(user) { + return Utils.saveToStorage('kt_event_user', user); + }, + + /** + * 사용자 정보 가져오기 + */ + getUser() { + return Utils.getFromStorage('kt_event_user'); + }, + + /** + * 로그인 여부 확인 + */ + isLoggedIn() { + return this.getUser() !== null; + }, + + /** + * 로그아웃 + */ + logout() { + Utils.removeFromStorage('kt_event_user'); + window.location.href = '01-로그인.html'; + }, + + /** + * 로그인 필요 페이지 보호 + */ + requireAuth() { + if (!this.isLoggedIn()) { + window.location.href = '01-로그인.html'; + } + } + }; + + // ================================================================= + // 7. Mock Data + // ================================================================= + const MockData = { + /** + * 예제 이벤트 데이터 + */ + getEvents() { + return [ + { + id: 'evt001', + title: 'SNS 팔로우 이벤트', + status: '진행중', + purpose: '신규 고객 유치', + startDate: '2025-01-15', + endDate: '2025-02-15', + participants: 142, + views: 856, + roi: 520, + budget: '저비용', + channel: '온라인', + prize: '커피 쿠폰' + }, + { + id: 'evt002', + title: '설 맞이 할인 이벤트', + status: '예정', + purpose: '매출 증대', + startDate: '2025-02-01', + endDate: '2025-02-10', + participants: 0, + views: 0, + roi: 0, + budget: '중비용', + channel: '오프라인', + prize: '10% 할인권' + }, + { + id: 'evt003', + title: '고객 만족도 조사', + status: '종료', + purpose: '고객 유지', + startDate: '2024-12-01', + endDate: '2024-12-31', + participants: 287, + views: 1234, + roi: 340, + budget: '저비용', + channel: '온라인', + prize: '포인트 적립' + } + ]; + }, + + /** + * 예제 사용자 데이터 + */ + getDefaultUser() { + return { + id: 'user001', + name: '홍길동', + email: 'hong@example.com', + phone: '010-1234-5678', + businessName: '홍길동 고깃집', + businessType: '음식점', + joinDate: '2025-01-01' + }; + }, + + /** + * 이벤트 목적 옵션 + */ + getEventPurposes() { + return [ + { + id: 'new_customers', + title: '신규 고객 유치', + description: '새로운 고객을 매장으로 끌어들이고 싶어요', + icon: 'person_add' + }, + { + id: 'sales', + title: '매출 증대', + description: '단기간에 매출을 올리고 싶어요', + icon: 'trending_up' + }, + { + id: 'retention', + title: '고객 유지', + description: '기존 고객이 계속 방문하도록 하고 싶어요', + icon: 'favorite' + }, + { + id: 'awareness', + title: '브랜드 인지도', + description: '우리 가게를 더 많은 사람에게 알리고 싶어요', + icon: 'campaign' + } + ]; + }, + + /** + * AI 추천 이벤트 데이터 + */ + getAIRecommendations() { + return { + trends: { + industry: '음식점업 신년 프로모션 트렌드', + location: '강남구 음식점 할인 이벤트 증가', + season: '설 연휴 특수 대비 고객 유치 전략' + }, + recommendations: [ + // 저비용 + { + budget: '저비용', + type: '온라인', + title: 'SNS 팔로우 이벤트', + prize: '커피 쿠폰', + method: 'SNS 팔로우', + participants: 180, + cost: 250000, + roi: 520 + }, + { + budget: '저비용', + type: '오프라인', + title: '전화번호 등록 이벤트', + prize: '커피 쿠폰', + method: '전화번호 등록', + participants: 150, + cost: 300000, + roi: 450 + }, + // 중비용 + { + budget: '중비용', + type: '온라인', + title: '리뷰 작성 이벤트', + prize: '5천원 상품권', + method: '리뷰 작성', + participants: 250, + cost: 1500000, + roi: 380 + }, + { + budget: '중비용', + type: '오프라인', + title: '방문 도장 적립 이벤트', + prize: '무료 식사권', + method: '방문 도장 적립', + participants: 200, + cost: 1800000, + roi: 320 + }, + // 고비용 + { + budget: '고비용', + type: '온라인', + title: '인플루언서 협업 이벤트', + prize: '1만원 할인권', + method: '인플루언서 팔로우', + participants: 400, + cost: 5000000, + roi: 280 + }, + { + budget: '고비용', + type: '오프라인', + title: 'VIP 고객 초대 이벤트', + prize: '특별 메뉴 제공', + method: 'VIP 초대장', + participants: 300, + cost: 6000000, + roi: 240 + } + ] + }; + } + }; + + // ================================================================= + // Public API + // ================================================================= + return { + Utils, + Navigation, + Form, + Feedback, + Cards, + Session, + MockData + }; +})(); + +// Material Icons 폰트 로드 +if (!document.querySelector('link[href*="material-icons"]')) { + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = 'https://fonts.googleapis.com/icon?family=Material+Icons'; + document.head.appendChild(link); +} + +// Pretendard 폰트 로드 +if (!document.querySelector('link[href*="pretendard"]')) { + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = 'https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css'; + document.head.appendChild(link); +} diff --git a/design/uiux/prototype/styles.css b/design/uiux/prototype/styles.css new file mode 100644 index 0000000..0be54e8 --- /dev/null +++ b/design/uiux/prototype/styles.css @@ -0,0 +1,973 @@ +/* ================================================================= + KT AI 이벤트 마케팅 서비스 - 공통 스타일시트 + Version: 1.0.0 + Mobile First Design Philosophy + ================================================================= */ + +/* ================================================================= + 1. CSS Variables - Design System + ================================================================= */ +:root { + /* Brand Colors */ + --color-kt-red: #E31E24; + --color-ai-blue: #0066FF; + + /* Grayscale */ + --color-gray-50: #F9FAFB; + --color-gray-100: #F3F4F6; + --color-gray-200: #E5E7EB; + --color-gray-300: #D1D5DB; + --color-gray-400: #9CA3AF; + --color-gray-500: #6B7280; + --color-gray-600: #4B5563; + --color-gray-700: #374151; + --color-gray-800: #1F2937; + --color-gray-900: #111827; + + /* Semantic Colors */ + --color-success: #10B981; + --color-warning: #F59E0B; + --color-error: #EF4444; + --color-info: #3B82F6; + + /* Background */ + --color-bg-primary: #FFFFFF; + --color-bg-secondary: #F9FAFB; + --color-bg-tertiary: #F3F4F6; + + /* Text */ + --color-text-primary: #111827; + --color-text-secondary: #4B5563; + --color-text-tertiary: #9CA3AF; + --color-text-inverse: #FFFFFF; + + /* Gradients */ + --gradient-primary: linear-gradient(135deg, #E31E24 0%, #B71419 100%); + --gradient-ai: linear-gradient(135deg, #0066FF 0%, #0052CC 100%); + + /* Typography Scale */ + --font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, system-ui, sans-serif; + --font-size-display: 28px; + --font-size-title-large: 24px; + --font-size-title: 20px; + --font-size-headline: 18px; + --font-size-body-large: 16px; + --font-size-body: 14px; + --font-size-body-small: 13px; + --font-size-caption: 12px; + + --line-height-display: 1.2; + --line-height-title: 1.3; + --line-height-body: 1.5; + --line-height-caption: 1.4; + + --font-weight-bold: 700; + --font-weight-semibold: 600; + --font-weight-medium: 500; + --font-weight-regular: 400; + + /* Spacing System (4px grid) */ + --spacing-xs: 4px; + --spacing-sm: 8px; + --spacing-md: 16px; + --spacing-lg: 24px; + --spacing-xl: 32px; + --spacing-2xl: 48px; + + /* Shadows */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07); + --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); + --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15); + + /* Border Radius */ + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 16px; + --radius-full: 9999px; + + /* Z-Index */ + --z-dropdown: 1000; + --z-sticky: 1020; + --z-fixed: 1030; + --z-modal-backdrop: 1040; + --z-modal: 1050; + --z-popover: 1060; + --z-tooltip: 1070; + + /* Animation */ + --duration-instant: 0ms; + --duration-fast: 150ms; + --duration-normal: 300ms; + --duration-slow: 500ms; + + --ease-in: cubic-bezier(0.4, 0, 1, 1); + --ease-out: cubic-bezier(0, 0, 0.2, 1); + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Tablet Responsive Variables */ +@media (min-width: 768px) { + :root { + --font-size-display: 32px; + --font-size-title-large: 28px; + --font-size-title: 24px; + --font-size-headline: 20px; + } +} + +/* Desktop Responsive Variables */ +@media (min-width: 1024px) { + :root { + --font-size-display: 36px; + --font-size-title-large: 32px; + --font-size-title: 28px; + --font-size-headline: 22px; + } +} + +/* ================================================================= + 2. Reset & Base Styles + ================================================================= */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + font-size: 16px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + font-family: var(--font-family); + font-size: var(--font-size-body); + line-height: var(--line-height-body); + color: var(--color-text-primary); + background-color: var(--color-bg-primary); + overflow-x: hidden; +} + +/* ================================================================= + 3. Typography + ================================================================= */ +.text-display { + font-size: var(--font-size-display); + line-height: var(--line-height-display); + font-weight: var(--font-weight-bold); +} + +.text-title-large { + font-size: var(--font-size-title-large); + line-height: var(--line-height-title); + font-weight: var(--font-weight-bold); +} + +.text-title { + font-size: var(--font-size-title); + line-height: var(--line-height-title); + font-weight: var(--font-weight-semibold); +} + +.text-headline { + font-size: var(--font-size-headline); + line-height: var(--line-height-title); + font-weight: var(--font-weight-semibold); +} + +.text-body-large { + font-size: var(--font-size-body-large); + line-height: var(--line-height-body); + font-weight: var(--font-weight-regular); +} + +.text-body { + font-size: var(--font-size-body); + line-height: var(--line-height-body); + font-weight: var(--font-weight-regular); +} + +.text-body-small { + font-size: var(--font-size-body-small); + line-height: var(--line-height-body); + font-weight: var(--font-weight-regular); +} + +.text-caption { + font-size: var(--font-size-caption); + line-height: var(--line-height-caption); + font-weight: var(--font-weight-regular); +} + +.text-primary { color: var(--color-text-primary); } +.text-secondary { color: var(--color-text-secondary); } +.text-tertiary { color: var(--color-text-tertiary); } +.text-inverse { color: var(--color-text-inverse); } +.text-kt-red { color: var(--color-kt-red); } +.text-ai-blue { color: var(--color-ai-blue); } +.text-success { color: var(--color-success); } +.text-warning { color: var(--color-warning); } +.text-error { color: var(--color-error); } + +.text-bold { font-weight: var(--font-weight-bold); } +.text-semibold { font-weight: var(--font-weight-semibold); } +.text-medium { font-weight: var(--font-weight-medium); } + +.text-center { text-align: center; } +.text-left { text-align: left; } +.text-right { text-align: right; } + +/* ================================================================= + 4. Layout Utilities + ================================================================= */ +.container { + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 0 var(--spacing-md); +} + +.page { + min-height: 100vh; + background-color: var(--color-bg-secondary); + padding-bottom: 76px; /* Bottom nav height + spacing */ +} + +.page-with-header { + padding-top: 56px; /* Header height */ +} + +/* ================================================================= + 5. Button Components + ================================================================= */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--spacing-sm); + border: none; + border-radius: var(--radius-md); + font-family: var(--font-family); + font-weight: var(--font-weight-semibold); + cursor: pointer; + transition: all var(--duration-fast) var(--ease-out); + text-decoration: none; + user-select: none; +} + +.btn:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +/* Button Sizes */ +.btn-large { + height: 48px; + padding: 0 var(--spacing-lg); + font-size: var(--font-size-body-large); +} + +.btn-medium { + height: 44px; + padding: 0 var(--spacing-md); + font-size: var(--font-size-body); +} + +.btn-small { + height: 36px; + padding: 0 var(--spacing-md); + font-size: var(--font-size-body-small); +} + +/* Button Variants */ +.btn-primary { + background: var(--gradient-primary); + color: var(--color-text-inverse); + box-shadow: var(--shadow-sm); +} + +.btn-primary:hover:not(:disabled) { + box-shadow: var(--shadow-md); + transform: translateY(-1px); +} + +.btn-primary:active:not(:disabled) { + transform: translateY(0); + box-shadow: var(--shadow-sm); +} + +.btn-secondary { + background-color: var(--color-bg-primary); + color: var(--color-kt-red); + border: 1px solid var(--color-gray-300); +} + +.btn-secondary:hover:not(:disabled) { + background-color: var(--color-gray-50); + border-color: var(--color-kt-red); +} + +.btn-text { + background-color: transparent; + color: var(--color-kt-red); +} + +.btn-text:hover:not(:disabled) { + background-color: rgba(227, 30, 36, 0.08); +} + +.btn-full { + width: 100%; +} + +/* ================================================================= + 6. Card Components + ================================================================= */ +.card { + background-color: var(--color-bg-primary); + border-radius: var(--radius-lg); + padding: var(--spacing-lg); + box-shadow: var(--shadow-sm); + transition: box-shadow var(--duration-fast) var(--ease-out); +} + +.card:hover { + box-shadow: var(--shadow-md); +} + +.card-clickable { + cursor: pointer; +} + +.card-clickable:active { + transform: scale(0.98); +} + +.event-card { + display: flex; + flex-direction: column; + gap: var(--spacing-md); +} + +.event-card-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: var(--spacing-sm); +} + +.event-card-badge { + display: inline-flex; + align-items: center; + padding: 4px 12px; + border-radius: var(--radius-full); + font-size: var(--font-size-caption); + font-weight: var(--font-weight-medium); +} + +.badge-active { + background-color: rgba(16, 185, 129, 0.1); + color: var(--color-success); +} + +.badge-scheduled { + background-color: rgba(59, 130, 246, 0.1); + color: var(--color-info); +} + +.badge-ended { + background-color: rgba(156, 163, 175, 0.1); + color: var(--color-gray-500); +} + +.event-card-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--spacing-md); + padding-top: var(--spacing-md); + border-top: 1px solid var(--color-gray-200); +} + +.stat-item { + text-align: center; +} + +.stat-label { + font-size: var(--font-size-caption); + color: var(--color-text-tertiary); + margin-bottom: 4px; +} + +.stat-value { + font-size: var(--font-size-headline); + font-weight: var(--font-weight-bold); + color: var(--color-text-primary); +} + +/* KPI Card */ +.kpi-card { + display: flex; + align-items: center; + gap: var(--spacing-md); + padding: var(--spacing-md); +} + +.kpi-icon { + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-md); + font-size: 24px; +} + +.kpi-icon-primary { + background: rgba(227, 30, 36, 0.1); + color: var(--color-kt-red); +} + +.kpi-icon-ai { + background: rgba(0, 102, 255, 0.1); + color: var(--color-ai-blue); +} + +.kpi-icon-success { + background: rgba(16, 185, 129, 0.1); + color: var(--color-success); +} + +.kpi-content { + flex: 1; +} + +.kpi-label { + font-size: var(--font-size-body-small); + color: var(--color-text-secondary); + margin-bottom: 4px; +} + +.kpi-value { + font-size: var(--font-size-title); + font-weight: var(--font-weight-bold); + color: var(--color-text-primary); +} + +/* Option Card for Selection */ +.option-card { + position: relative; + border: 2px solid var(--color-gray-200); + transition: all var(--duration-fast) var(--ease-out); +} + +.option-card:hover { + border-color: var(--color-kt-red); +} + +.option-card.selected { + border-color: var(--color-kt-red); + background-color: rgba(227, 30, 36, 0.02); +} + +.option-card-radio { + position: absolute; + top: var(--spacing-md); + right: var(--spacing-md); +} + +/* ================================================================= + 7. Form Components + ================================================================= */ +.form-group { + margin-bottom: var(--spacing-lg); +} + +.form-label { + display: block; + font-size: var(--font-size-body); + font-weight: var(--font-weight-medium); + color: var(--color-text-primary); + margin-bottom: var(--spacing-sm); +} + +.form-label-required::after { + content: " *"; + color: var(--color-error); +} + +.form-input, +.form-select, +.form-textarea { + width: 100%; + padding: 12px var(--spacing-md); + border: 1px solid var(--color-gray-300); + border-radius: var(--radius-md); + font-family: var(--font-family); + font-size: var(--font-size-body); + color: var(--color-text-primary); + background-color: var(--color-bg-primary); + transition: all var(--duration-fast) var(--ease-out); +} + +.form-input::placeholder, +.form-textarea::placeholder { + color: var(--color-text-tertiary); +} + +.form-input:focus, +.form-select:focus, +.form-textarea:focus { + outline: none; + border-color: var(--color-kt-red); + box-shadow: 0 0 0 3px rgba(227, 30, 36, 0.1); +} + +.form-input:disabled, +.form-select:disabled, +.form-textarea:disabled { + background-color: var(--color-gray-100); + cursor: not-allowed; +} + +.form-textarea { + min-height: 100px; + resize: vertical; +} + +.form-error { + display: block; + margin-top: var(--spacing-sm); + font-size: var(--font-size-body-small); + color: var(--color-error); +} + +.form-hint { + display: block; + margin-top: var(--spacing-sm); + font-size: var(--font-size-body-small); + color: var(--color-text-tertiary); +} + +/* Checkbox & Radio */ +.form-check { + display: flex; + align-items: center; + gap: var(--spacing-sm); + cursor: pointer; +} + +.form-check-input { + width: 20px; + height: 20px; + cursor: pointer; +} + +.form-check-label { + font-size: var(--font-size-body); + color: var(--color-text-primary); + cursor: pointer; +} + +/* ================================================================= + 8. Navigation Components + ================================================================= */ +.header { + position: fixed; + top: 0; + left: 0; + right: 0; + height: 56px; + background-color: var(--color-bg-primary); + border-bottom: 1px solid var(--color-gray-200); + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 var(--spacing-md); + z-index: var(--z-sticky); +} + +.header-left, +.header-right { + display: flex; + align-items: center; + gap: var(--spacing-sm); +} + +.header-title { + font-size: var(--font-size-headline); + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); +} + +.header-icon-btn { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + border: none; + background: transparent; + color: var(--color-text-primary); + cursor: pointer; + border-radius: var(--radius-md); + transition: background-color var(--duration-fast) var(--ease-out); +} + +.header-icon-btn:hover { + background-color: var(--color-gray-100); +} + +.bottom-nav { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 60px; + background-color: var(--color-bg-primary); + border-top: 1px solid var(--color-gray-200); + display: flex; + align-items: center; + justify-content: space-around; + padding: 0 var(--spacing-sm); + z-index: var(--z-sticky); +} + +.bottom-nav-item { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 4px; + padding: var(--spacing-sm); + color: var(--color-text-tertiary); + text-decoration: none; + font-size: var(--font-size-caption); + transition: color var(--duration-fast) var(--ease-out); + cursor: pointer; +} + +.bottom-nav-item:hover { + color: var(--color-text-secondary); +} + +.bottom-nav-item.active { + color: var(--color-kt-red); +} + +.bottom-nav-icon { + font-size: 24px; +} + +/* FAB (Floating Action Button) */ +.fab { + position: fixed; + bottom: 76px; /* Bottom nav height + spacing */ + right: var(--spacing-md); + width: 56px; + height: 56px; + background: var(--gradient-primary); + color: var(--color-text-inverse); + border: none; + border-radius: var(--radius-full); + box-shadow: var(--shadow-lg); + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; + cursor: pointer; + transition: all var(--duration-fast) var(--ease-out); + z-index: var(--z-fixed); +} + +.fab:hover { + box-shadow: var(--shadow-xl); + transform: scale(1.05); +} + +.fab:active { + transform: scale(0.95); +} + +/* ================================================================= + 9. Feedback Components + ================================================================= */ +/* Toast */ +.toast { + position: fixed; + bottom: 92px; /* Bottom nav + spacing */ + left: 50%; + transform: translateX(-50%); + padding: var(--spacing-md) var(--spacing-lg); + background-color: var(--color-gray-900); + color: var(--color-text-inverse); + border-radius: var(--radius-md); + box-shadow: var(--shadow-lg); + font-size: var(--font-size-body); + z-index: var(--z-tooltip); + animation: slideUp var(--duration-normal) var(--ease-out); +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translate(-50%, 20px); + } + to { + opacity: 1; + transform: translate(-50%, 0); + } +} + +/* Modal */ +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + z-index: var(--z-modal-backdrop); + animation: fadeIn var(--duration-normal) var(--ease-out); +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +.modal { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: var(--color-bg-primary); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-xl); + z-index: var(--z-modal); + max-width: 90%; + max-height: 90vh; + overflow-y: auto; + animation: scaleIn var(--duration-normal) var(--ease-out); +} + +@keyframes scaleIn { + from { + opacity: 0; + transform: translate(-50%, -50%) scale(0.95); + } + to { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } +} + +.modal-header { + padding: var(--spacing-lg); + border-bottom: 1px solid var(--color-gray-200); +} + +.modal-title { + font-size: var(--font-size-title); + font-weight: var(--font-weight-semibold); +} + +.modal-body { + padding: var(--spacing-lg); +} + +.modal-footer { + padding: var(--spacing-lg); + border-top: 1px solid var(--color-gray-200); + display: flex; + gap: var(--spacing-sm); + justify-content: flex-end; +} + +/* Bottom Sheet */ +.bottom-sheet { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background-color: var(--color-bg-primary); + border-radius: var(--radius-xl) var(--radius-xl) 0 0; + box-shadow: var(--shadow-xl); + z-index: var(--z-modal); + max-height: 80vh; + overflow-y: auto; + animation: slideUpSheet var(--duration-normal) var(--ease-out); +} + +@keyframes slideUpSheet { + from { + transform: translateY(100%); + } + to { + transform: translateY(0); + } +} + +.bottom-sheet-handle { + width: 40px; + height: 4px; + background-color: var(--color-gray-300); + border-radius: var(--radius-full); + margin: var(--spacing-sm) auto; +} + +.bottom-sheet-content { + padding: var(--spacing-lg); +} + +/* Spinner */ +.spinner { + width: 40px; + height: 40px; + border: 4px solid var(--color-gray-200); + border-top-color: var(--color-kt-red); + border-radius: var(--radius-full); + animation: spin 1s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +.spinner-center { + display: flex; + align-items: center; + justify-content: center; + padding: var(--spacing-2xl); +} + +/* Progress Bar */ +.progress { + width: 100%; + height: 8px; + background-color: var(--color-gray-200); + border-radius: var(--radius-full); + overflow: hidden; +} + +.progress-bar { + height: 100%; + background: var(--gradient-primary); + border-radius: var(--radius-full); + transition: width var(--duration-normal) var(--ease-out); +} + +/* ================================================================= + 10. Utility Classes + ================================================================= */ +/* Spacing */ +.m-0 { margin: 0; } +.mt-xs { margin-top: var(--spacing-xs); } +.mt-sm { margin-top: var(--spacing-sm); } +.mt-md { margin-top: var(--spacing-md); } +.mt-lg { margin-top: var(--spacing-lg); } +.mt-xl { margin-top: var(--spacing-xl); } +.mt-2xl { margin-top: var(--spacing-2xl); } + +.mb-xs { margin-bottom: var(--spacing-xs); } +.mb-sm { margin-bottom: var(--spacing-sm); } +.mb-md { margin-bottom: var(--spacing-md); } +.mb-lg { margin-bottom: var(--spacing-lg); } +.mb-xl { margin-bottom: var(--spacing-xl); } +.mb-2xl { margin-bottom: var(--spacing-2xl); } + +.p-0 { padding: 0; } +.p-xs { padding: var(--spacing-xs); } +.p-sm { padding: var(--spacing-sm); } +.p-md { padding: var(--spacing-md); } +.p-lg { padding: var(--spacing-lg); } +.p-xl { padding: var(--spacing-xl); } +.p-2xl { padding: var(--spacing-2xl); } + +.gap-xs { gap: var(--spacing-xs); } +.gap-sm { gap: var(--spacing-sm); } +.gap-md { gap: var(--spacing-md); } +.gap-lg { gap: var(--spacing-lg); } + +/* Flexbox */ +.flex { display: flex; } +.flex-col { flex-direction: column; } +.flex-row { flex-direction: row; } +.items-center { align-items: center; } +.items-start { align-items: flex-start; } +.items-end { align-items: flex-end; } +.justify-center { justify-content: center; } +.justify-between { justify-content: space-between; } +.justify-end { justify-content: flex-end; } +.flex-1 { flex: 1; } +.flex-wrap { flex-wrap: wrap; } + +/* Grid */ +.grid { display: grid; } +.grid-cols-2 { grid-template-columns: repeat(2, 1fr); } +.grid-cols-3 { grid-template-columns: repeat(3, 1fr); } +.grid-cols-4 { grid-template-columns: repeat(4, 1fr); } + +/* Display */ +.hidden { display: none; } +.block { display: block; } +.inline-block { display: inline-block; } + +/* Width */ +.w-full { width: 100%; } +.w-auto { width: auto; } + +/* Background */ +.bg-primary { background-color: var(--color-bg-primary); } +.bg-secondary { background-color: var(--color-bg-secondary); } +.bg-tertiary { background-color: var(--color-bg-tertiary); } + +/* Border */ +.border { border: 1px solid var(--color-gray-200); } +.border-t { border-top: 1px solid var(--color-gray-200); } +.border-b { border-bottom: 1px solid var(--color-gray-200); } +.border-none { border: none; } + +.rounded-sm { border-radius: var(--radius-sm); } +.rounded-md { border-radius: var(--radius-md); } +.rounded-lg { border-radius: var(--radius-lg); } +.rounded-xl { border-radius: var(--radius-xl); } +.rounded-full { border-radius: var(--radius-full); } + +/* Shadow */ +.shadow-sm { box-shadow: var(--shadow-sm); } +.shadow-md { box-shadow: var(--shadow-md); } +.shadow-lg { box-shadow: var(--shadow-lg); } +.shadow-xl { box-shadow: var(--shadow-xl); } +.shadow-none { box-shadow: none; } + +/* Cursor */ +.cursor-pointer { cursor: pointer; } +.cursor-not-allowed { cursor: not-allowed; } + +/* ================================================================= + 11. Responsive Grid System + ================================================================= */ +@media (min-width: 768px) { + .container { + padding: 0 var(--spacing-lg); + } + + .tablet\:grid-cols-2 { grid-template-columns: repeat(2, 1fr); } + .tablet\:grid-cols-3 { grid-template-columns: repeat(3, 1fr); } + .tablet\:grid-cols-4 { grid-template-columns: repeat(4, 1fr); } +} + +@media (min-width: 1024px) { + .container { + padding: 0 var(--spacing-xl); + } + + .desktop\:grid-cols-2 { grid-template-columns: repeat(2, 1fr); } + .desktop\:grid-cols-3 { grid-template-columns: repeat(3, 1fr); } + .desktop\:grid-cols-4 { grid-template-columns: repeat(4, 1fr); } + .desktop\:grid-cols-5 { grid-template-columns: repeat(5, 1fr); } +}