Files
DeveloperandClaude Opus 4.6 94647e2b4b perf: 性能与资源优化 - 提取内联资源、数据库查询优化、限流与缓存增强
主要优化:
- templates/index.html: 220KB → 28KB,内联CSS/JS提取为静态文件
- menu/serializers.py: 修复 Order.ingredients_summary N+1 查询问题
- menu/services/dish_service.py: 移除多余查询,添加 only() 限制字段
- menu/views.py: 上传接口添加每分钟20次限流
- static/js/api.js: 统一API错误处理
- zhangmenu/settings.py: 添加Whitenoise压缩、Redis缓存支持、环境变量配置
- static/css/app.css, static/js/app.js: 提取的内联资源文件
- 新增 migrations, requirements.txt, .env.example

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 17:18:59 +08:00

4603 lines
135 KiB
CSS
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<style>
:root {
/* 温暖厨房风格配色 - 陶土色系 */
--primary: #c2410c;
--primary-light: #ea580c;
--primary-dark: #9a3412;
--primary-soft: #fed7aa;
--secondary: #059669;
--secondary-light: #10b981;
--secondary-soft: #d1fae5;
--accent: #f59e0b;
--accent-soft: #fef3c7;
--bg-primary: #fffaf0;
--bg-secondary: #fff7ed;
--bg-card: #ffffff;
--bg-dark: #1c1917;
--text-primary: #1c1917;
--text-secondary: #57534e;
--text-muted: #a8a29e;
--text-inverse: #ffffff;
--border-light: #fed7aa;
--border-medium: #fde68a;
--shadow-sm: 0 1px 3px rgba(194, 65, 12, 0.08), 0 1px 2px rgba(194, 65, 12, 0.04);
--shadow-md: 0 4px 12px rgba(194, 65, 12, 0.12), 0 2px 4px rgba(194, 65, 12, 0.08);
--shadow-lg: 0 12px 32px rgba(194, 65, 12, 0.18), 0 4px 8px rgba(194, 65, 12, 0.12);
--shadow-xl: 0 24px 48px rgba(194, 65, 12, 0.22), 0 8px 16px rgba(194, 65, 12, 0.16);
--radius-sm: 12px;
--radius-md: 20px;
--radius-lg: 28px;
--radius-xl: 36px;
--radius-full: 9999px;
--font-display: 'ZCOOL XiaoWei', 'Noto Sans SC', serif;
--font-body: 'Nunito', 'Noto Sans SC', sans-serif;
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-normal: 250ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-slow: 400ms cubic-bezier(0.4, 0, 0.2, 1);
--header-height: 180px;
--bottom-nav-height: 80px;
}
* {
-webkit-tap-highlight-color: transparent;
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
}
body {
font-family: var(--font-body);
background: linear-gradient(180deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);
min-height: 100vh;
color: var(--text-primary);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding-bottom: var(--bottom-nav-height);
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
/* ============ HEADER ============ */
.app-header {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 50%, #fb923c 100%);
padding: 16px 20px 28px;
border-radius: 0 0 var(--radius-xl) var(--radius-xl);
box-shadow: var(--shadow-lg);
position: relative;
overflow: hidden;
}
.app-header::before {
content: '';
position: absolute;
top: -60%;
right: -10%;
width: 300px;
height: 300px;
background: radial-gradient(circle, rgba(255,255,255,0.15) 0%, transparent 70%);
pointer-events: none;
}
.app-header::after {
content: '';
position: absolute;
bottom: -40%;
left: -20%;
width: 200px;
height: 200px;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
pointer-events: none;
}
.header-content {
position: relative;
z-index: 1;
}
.brand-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.brand {
display: flex;
align-items: center;
gap: 14px;
}
.brand-icon {
width: 52px;
height: 52px;
background: rgba(255,255,255,0.22);
backdrop-filter: blur(12px);
border-radius: 18px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 26px;
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
}
.brand-text {
color: white;
font-size: 24px;
font-weight: 800;
letter-spacing: -0.5px;
text-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
.header-actions {
display: flex;
gap: 10px;
}
.header-btn {
width: 42px;
height: 42px;
border-radius: 14px;
background: rgba(255,255,255,0.2);
backdrop-filter: blur(12px);
border: none;
color: white;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all var(--transition-fast);
}
.header-btn:hover {
background: rgba(255,255,255,0.3);
transform: scale(1.05);
}
/* ============ MENU HEADER (No Background) ============ */
.menu-header {
padding: 16px 20px;
display: flex;
flex-direction: column;
gap: 12px;
}
.menu-header .search-container {
width: 100%;
}
.category-filter {
display: flex;
overflow-x: auto;
gap: 10px;
padding-bottom: 4px;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.category-filter::-webkit-scrollbar {
display: none;
}
.category-filter .category-chip {
flex-shrink: 0;
padding: 8px 16px;
background: white;
border-radius: var(--radius-full);
font-size: 13px;
font-weight: 600;
cursor: pointer;
white-space: nowrap;
box-shadow: var(--shadow-xs);
transition: all var(--transition-fast);
border: 1px solid #e7e5e4;
color: var(--text-secondary);
}
.category-filter .category-chip:hover {
border-color: var(--primary);
color: var(--primary);
}
.category-filter .category-chip.active {
background: var(--primary);
color: white;
border-color: var(--primary);
box-shadow: none;
transform: none;
}
/* ============ SEARCH ============ */
.search-container {
background: white;
border-radius: var(--radius-lg);
padding: 6px 8px 6px 18px;
display: flex;
align-items: center;
box-shadow: var(--shadow-md);
border: 2px solid transparent;
transition: all var(--transition-normal);
}
.search-container:focus-within {
border-color: rgba(194, 65, 12, 0.3);
box-shadow: var(--shadow-lg);
transform: translateY(-2px);
}
.search-container i {
color: var(--primary);
font-size: 20px;
opacity: 0.8;
}
.search-container input {
border: none;
padding: 14px;
flex: 1;
font-size: 16px;
outline: none;
background: transparent;
font-family: var(--font-body);
color: var(--text-primary);
}
.search-container input::placeholder {
color: var(--text-muted);
}
.search-btn {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
border: none;
border-radius: var(--radius-md);
padding: 12px 20px;
color: white;
font-weight: 700;
font-size: 14px;
cursor: pointer;
transition: all var(--transition-fast);
}
.search-btn:hover {
transform: scale(1.02);
box-shadow: 0 4px 12px rgba(194, 65, 12, 0.3);
}
/* ============ CATEGORIES ============ */
.categories-section {
padding: 24px 0 16px;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
margin-bottom: 16px;
}
.section-title {
font-size: 20px;
font-weight: 800;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 10px;
}
.section-title i {
color: var(--primary);
font-size: 22px;
}
.see-all {
color: var(--primary);
font-weight: 700;
font-size: 14px;
text-decoration: none;
display: flex;
align-items: center;
gap: 4px;
}
.see-all:hover {
color: var(--primary-dark);
}
.category-scroll {
display: flex;
overflow-x: auto;
padding: 0 20px 8px;
gap: 12px;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.category-scroll::-webkit-scrollbar {
display: none;
}
.category-chip {
flex-shrink: 0;
padding: 12px 24px;
background: white;
border-radius: var(--radius-full);
font-size: 15px;
font-weight: 700;
cursor: pointer;
white-space: nowrap;
box-shadow: var(--shadow-sm);
transition: all var(--transition-normal);
border: 2px solid transparent;
color: var(--text-secondary);
}
.category-chip:hover {
box-shadow: var(--shadow-md);
color: var(--text-primary);
transform: translateY(-2px);
}
.category-chip:active {
transform: scale(0.96);
}
.category-chip.active {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
color: white;
box-shadow: none;
transform: none;
}
/* ============ DISH GRID ============ */
.dish-section {
padding: 8px 0 24px;
}
.dish-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
padding: 0 20px;
}
.dish-card {
background: white;
border-radius: var(--radius-xl);
overflow: hidden;
box-shadow: var(--shadow-sm);
cursor: pointer;
transition: all var(--transition-normal);
border: 1px solid rgba(194, 65, 12, 0.06);
}
.dish-card:active {
transform: scale(0.97);
}
.dish-card:hover {
transform: translateY(-8px);
box-shadow: var(--shadow-xl);
}
.dish-card .img-wrapper {
position: relative;
padding-top: 70%;
overflow: hidden;
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 50%, #fed7aa 100%);
}
.dish-card img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.dish-card:hover img {
transform: scale(1.12);
}
.dish-card .category-tag {
position: absolute;
top: 12px;
left: 12px;
background: rgba(255,255,255,0.95);
backdrop-filter: blur(12px);
padding: 6px 14px;
border-radius: 20px;
font-size: 11px;
font-weight: 700;
color: var(--primary);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.dish-card .order-indicator {
position: absolute;
top: 12px;
right: 12px;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
transition: transform var(--transition-fast);
}
.dish-card .order-indicator.in-order {
background: linear-gradient(135deg, var(--secondary) 0%, var(--secondary-light) 100%);
color: white;
animation: popIn 0.3s ease;
}
.dish-card .order-indicator.not-in-order {
background: rgba(255,255,255,0.95);
backdrop-filter: blur(12px);
color: #d6d3d1;
border: 2px solid #e7e5e4;
}
@keyframes popIn {
0% { transform: scale(0); }
70% { transform: scale(1.15); }
100% { transform: scale(1); }
}
.dish-card .card-content {
padding: 16px;
}
.dish-card .dish-name {
font-size: 16px;
font-weight: 800;
color: var(--text-primary);
margin-bottom: 6px;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.dish-card .dish-meta {
font-size: 13px;
color: var(--text-muted);
font-weight: 600;
display: flex;
align-items: center;
gap: 6px;
}
.dish-card .dish-meta::before {
content: '';
width: 6px;
height: 6px;
background: var(--accent);
border-radius: 50%;
}
/* ============ BOTTOM NAV ============ */
.bottom-nav {
position: fixed;
bottom: 16px;
left: 16px;
right: 16px;
background: rgba(255,255,255,0.98);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
box-shadow: 0 8px 40px rgba(194, 65, 12, 0.15), 0 0 0 1px rgba(254, 215, 170, 0.3);
z-index: 1000;
padding: 10px 8px 10px;
border-radius: var(--radius-xl);
}
.nav-items {
display: flex;
justify-content: space-around;
align-items: center;
}
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 8px 16px;
color: var(--text-muted);
font-size: 11px;
font-weight: 700;
cursor: pointer;
transition: all var(--transition-normal);
position: relative;
border-radius: var(--radius-md);
}
.nav-item::after {
content: '';
position: absolute;
bottom: 2px;
left: 50%;
transform: translateX(-50%) scaleX(0);
width: 20px;
height: 3px;
background: linear-gradient(90deg, var(--primary) 0%, var(--primary-light) 100%);
border-radius: 3px;
transition: transform var(--transition-fast);
}
.nav-item.active {
color: var(--primary);
}
.nav-item.active::after {
transform: translateX(-50%) scaleX(1);
}
.nav-item i {
font-size: 24px;
transition: transform var(--transition-fast);
}
.nav-item.active i {
transform: none;
}
.nav-item:hover:not(.active) {
color: var(--text-secondary);
background: var(--bg-secondary);
}
/* ============ FLOATING ACTION BUTTON ============ */
.fab {
position: fixed;
bottom: 130px;
right: 20px;
width: 60px;
height: 60px;
border-radius: 20px;
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
box-shadow: 0 12px 28px rgba(194, 65, 12, 0.4);
cursor: pointer;
z-index: 999;
border: none;
transition: transform 0.2s ease;
}
.fab-top {
bottom: 200px;
background: linear-gradient(135deg, #6b7280 0%, #9ca3af 100%);
}
.fab:hover {
transform: scale(1.1);
}
.fab:active {
transform: scale(0.92);
}
/* ============ ORDERS PAGE ============ */
.orders-header {
padding: 16px 20px 0;
background: linear-gradient(180deg, var(--bg-primary) 0%, transparent 100%);
}
.page-title {
font-size: 28px;
font-weight: 800;
color: var(--text-primary);
margin-bottom: 20px;
}
.filter-pills {
display: flex;
gap: 10px;
overflow-x: auto;
padding-bottom: 8px;
scrollbar-width: none;
}
.filter-pills::-webkit-scrollbar {
display: none;
}
.filter-pill {
flex-shrink: 0;
padding: 10px 20px;
background: white;
border-radius: var(--radius-full);
font-size: 14px;
font-weight: 700;
cursor: pointer;
box-shadow: var(--shadow-sm);
transition: all var(--transition-normal);
border: 2px solid transparent;
color: var(--text-secondary);
}
.filter-pill:hover {
box-shadow: var(--shadow-md);
color: var(--text-primary);
}
.filter-pill.active {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
color: white;
box-shadow: none;
transform: none;
}
.order-list {
padding: 8px 20px 24px;
}
.order-card {
background: white;
border-radius: var(--radius-xl);
padding: 20px;
margin-bottom: 14px;
box-shadow: var(--shadow-sm);
cursor: pointer;
transition: all var(--transition-normal);
border: 1px solid rgba(194, 65, 12, 0.06);
position: relative;
overflow: hidden;
}
.order-card::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 5px;
background: linear-gradient(180deg, var(--primary) 0%, var(--primary-light) 100%);
}
.order-card:active {
transform: scale(0.98);
}
.order-card:hover {
box-shadow: var(--shadow-lg);
transform: translateY(-4px);
}
.order-card .order-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12px;
}
.order-card .order-name {
font-size: 18px;
font-weight: 800;
color: var(--text-primary);
}
.order-card .order-status {
padding: 6px 14px;
border-radius: 20px;
font-size: 12px;
font-weight: 700;
}
.order-card .status-progress {
background: var(--accent-soft);
color: #b45309;
box-shadow: 0 2px 8px rgba(245, 158, 11, 0.2);
}
.order-card .status-done {
background: var(--secondary-soft);
color: #047857;
box-shadow: 0 2px 8px rgba(5, 150, 105, 0.2);
}
.order-card .order-date {
font-size: 13px;
color: var(--text-muted);
margin-bottom: 14px;
font-weight: 600;
}
.order-card .dish-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.order-card .dish-tag {
padding: 8px 14px;
background: linear-gradient(135deg, var(--primary-soft) 0%, #fff7ed 100%);
border: 1px solid rgba(194, 65, 12, 0.15);
border-radius: 20px;
font-size: 13px;
color: var(--primary);
font-weight: 600;
}
.order-card .dish-tag-cold {
background: linear-gradient(135deg, #dbeafe 0%, #eff6ff 100%);
border-color: rgba(59, 130, 246, 0.2);
color: #2563eb;
}
/* Gathering detail dish list */
#order-detail-dishes {
display: flex;
flex-direction: column;
gap: 10px;
}
.dish-item-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
background: var(--bg-secondary);
border-radius: var(--radius-lg);
transition: all var(--transition-fast);
}
.dish-item-row:hover {
background: var(--primary-soft);
}
.dish-item-row .dish-info {
display: flex;
align-items: center;
gap: 12px;
}
.dish-item-row .dish-thumb {
width: 48px;
height: 48px;
border-radius: 12px;
object-fit: cover;
}
.dish-item-row .dish-name {
font-size: 15px;
font-weight: 700;
color: var(--text-primary);
}
.dish-item-row .remove-dish-btn {
width: 32px;
height: 32px;
border-radius: 50%;
background: #fee2e2;
border: none;
color: #dc2626;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all var(--transition-fast);
flex-shrink: 0;
}
.dish-item-row .remove-dish-btn:hover {
background: #fecaca;
transform: scale(1.1);
}
.order-card .dish-tag i:hover {
opacity: 1;
color: #dc2626;
}
/* ============ PROFILE PAGE ============ */
.profile-header {
padding: 20px;
text-align: center;
}
.profile-avatar {
width: 96px;
height: 96px;
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 16px;
font-size: 40px;
color: white;
box-shadow: 0 8px 24px rgba(194, 65, 12, 0.3);
}
.profile-name {
font-size: 24px;
font-weight: 800;
color: var(--text-primary);
margin-bottom: 4px;
}
.profile-subtitle {
font-size: 14px;
color: var(--text-muted);
font-weight: 600;
}
.profile-section {
padding: 20px 20px 24px;
}
.profile-card {
background: white;
border-radius: var(--radius-xl);
overflow: hidden;
box-shadow: var(--shadow-sm);
border: 1px solid rgba(194, 65, 12, 0.06);
}
.profile-menu-item {
display: flex;
align-items: center;
padding: 18px 20px;
cursor: pointer;
transition: all var(--transition-fast);
border-bottom: 1px solid #f5f5f4;
}
.profile-menu-item:last-child {
border-bottom: none;
}
.profile-menu-item:hover {
background: var(--bg-secondary);
}
.profile-menu-item .menu-icon {
width: 44px;
height: 44px;
background: var(--primary-soft);
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
color: var(--primary);
margin-right: 14px;
}
.profile-menu-item .menu-text {
flex: 1;
font-size: 16px;
font-weight: 700;
color: var(--text-primary);
}
.profile-menu-item .menu-arrow {
color: var(--text-muted);
font-size: 18px;
transition: transform var(--transition-fast);
}
.profile-menu-item:hover .menu-arrow {
transform: translateX(4px);
color: var(--primary);
}
/* ============ EMPTY STATE ============ */
.empty-state {
text-align: center;
padding: 60px 24px;
}
.empty-state .empty-icon {
width: 100px;
height: 100px;
background: linear-gradient(135deg, var(--primary-soft) 0%, #fef3c7 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 20px;
font-size: 44px;
color: var(--primary);
}
.empty-state .empty-title {
font-size: 18px;
font-weight: 800;
color: var(--text-primary);
margin-bottom: 8px;
}
.empty-state .empty-desc {
font-size: 14px;
color: var(--text-muted);
margin-bottom: 20px;
font-weight: 600;
}
.empty-state .empty-action {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 14px 28px;
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
color: white;
border-radius: var(--radius-full);
font-weight: 700;
font-size: 15px;
text-decoration: none;
box-shadow: 0 8px 24px rgba(194, 65, 12, 0.3);
transition: all var(--transition-fast);
}
.empty-state .empty-action:hover {
transform: translateY(-3px);
box-shadow: 0 12px 32px rgba(194, 65, 12, 0.4);
}
/* ============ MODALS ============ */
.modal-content {
border-radius: var(--radius-xl);
border: none;
box-shadow: 0 24px 80px rgba(0,0,0,0.2);
overflow: hidden;
}
.modal-dialog.modal-fullscreen {
margin: 0;
width: 100vw;
max-width: none;
min-height: 100dvh;
height: auto;
padding: 0;
display: flex;
align-items: stretch;
}
#dish-detail-modal,
#order-detail-modal {
overflow-y: auto;
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
touch-action: pan-y;
}
#dish-detail-modal .modal-dialog.modal-fullscreen,
#order-detail-modal .modal-dialog.modal-fullscreen {
min-height: 100%;
}
.modal-dialog.modal-fullscreen .modal-content {
width: 100%;
min-height: 100dvh;
height: auto;
border-radius: 0;
display: flex;
flex-direction: column;
overflow: visible;
touch-action: pan-y;
}
.modal-header {
border-bottom: 1px solid #f5f5f4;
padding: 24px;
background: white;
}
.modal-title {
font-weight: 800;
font-size: 20px;
color: var(--text-primary);
}
.modal-body {
padding: 24px;
background: #fafaf9;
}
.modal-footer {
border-top: 1px solid #f5f5f4;
padding: 20px 24px;
background: white;
}
.btn-close {
opacity: 0.5;
padding: 10px;
margin: -10px -10px -10px auto;
transition: all var(--transition-fast);
border-radius: 50%;
background: #f5f5f4;
}
.btn-close:hover {
opacity: 1;
transform: rotate(90deg);
background: #e7e5e4;
}
/* ============ FORMS ============ */
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
font-weight: 700;
font-size: 14px;
color: var(--text-primary);
margin-bottom: 8px;
}
.form-control, .form-select {
width: 100%;
border-radius: var(--radius-lg);
border: 2px solid #e7e5e4;
padding: 16px 18px;
font-size: 16px;
transition: all var(--transition-fast);
font-family: var(--font-body);
background: white;
}
.form-control:focus, .form-select:focus {
border-color: var(--primary);
box-shadow: 0 0 0 4px rgba(194, 65, 12, 0.12);
outline: none;
}
.form-control::placeholder {
color: var(--text-muted);
}
/* ============ BUTTONS ============ */
.btn-primary {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
border: none;
border-radius: var(--radius-lg);
padding: 16px 28px;
font-weight: 800;
font-size: 15px;
color: white;
box-shadow: 0 8px 24px rgba(194, 65, 12, 0.35);
transition: all var(--transition-normal);
font-family: var(--font-body);
}
.btn-primary:hover {
transform: translateY(-3px);
box-shadow: 0 12px 32px rgba(194, 65, 12, 0.45);
}
.btn-primary:active {
transform: translateY(0);
box-shadow: none;
}
.btn-secondary {
background: #f5f5f4;
border: none;
border-radius: var(--radius-lg);
padding: 16px 28px;
font-weight: 700;
font-size: 15px;
color: var(--text-secondary);
transition: all var(--transition-fast);
font-family: var(--font-body);
}
.btn-secondary:hover {
background: #e7e5e4;
color: var(--text-primary);
}
.btn-success {
background: linear-gradient(135deg, var(--secondary) 0%, var(--secondary-light) 100%);
border: none;
border-radius: var(--radius-lg);
padding: 16px 28px;
font-weight: 800;
font-size: 15px;
color: white;
box-shadow: 0 8px 24px rgba(5, 150, 105, 0.35);
transition: all var(--transition-normal);
font-family: var(--font-body);
}
.btn-success:hover {
transform: translateY(-3px);
box-shadow: 0 12px 32px rgba(5, 150, 105, 0.45);
}
.btn-danger {
background: linear-gradient(135deg, #dc2626 0%, #ef4444 100%);
border: none;
border-radius: var(--radius-lg);
padding: 16px 28px;
font-weight: 800;
font-size: 15px;
color: white;
box-shadow: 0 8px 24px rgba(220, 38, 38, 0.35);
transition: all var(--transition-normal);
font-family: var(--font-body);
}
.btn-danger:hover {
transform: translateY(-3px);
box-shadow: 0 12px 32px rgba(220, 38, 38, 0.45);
}
/* ============ DETAIL MODAL ============ */
.detail-cover {
width: 100%;
height: 280px;
object-fit: cover;
}
.detail-cover-wrapper {
position: relative;
}
.detail-close-btn {
position: absolute;
top: 16px;
right: 16px;
width: 44px;
height: 44px;
border-radius: 50%;
background: rgba(255,255,255,0.95);
backdrop-filter: blur(12px);
border: none;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
color: var(--text-primary);
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
cursor: pointer;
transition: all var(--transition-fast);
}
.detail-close-btn:hover {
transform: scale(1.1);
}
.detail-content {
background: white;
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
margin-top: -40px;
position: relative;
padding: 28px 24px 120px;
box-shadow: 0 -8px 40px rgba(0,0,0,0.1);
max-height: none;
overflow-y: visible;
}
.detail-name {
font-size: 26px;
font-weight: 800;
color: var(--text-primary);
margin-bottom: 12px;
line-height: 1.3;
}
.detail-category-tag {
display: inline-block;
padding: 8px 16px;
background: var(--primary-soft);
color: var(--primary);
border-radius: 20px;
font-size: 13px;
font-weight: 700;
border: 1px solid rgba(194, 65, 12, 0.2);
}
.detail-section {
background: white;
border-radius: var(--radius-xl);
padding: 22px;
margin: 20px 0;
box-shadow: var(--shadow-sm);
border: 1px solid rgba(194, 65, 12, 0.06);
}
.detail-section .md-content {
font-size: 15px;
color: var(--text-secondary);
line-height: 1.8;
}
.detail-section .md-content img {
max-width: 50%;
border-radius: 12px;
margin: 12px 0;
display: block;
}
.detail-section-title {
font-size: 17px;
font-weight: 800;
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 10px;
color: var(--text-primary);
}
.detail-section-title i {
color: var(--primary);
font-size: 20px;
}
.ingredient-row {
display: flex;
justify-content: space-between;
padding: 14px 0;
border-bottom: 1px solid #f5f5f4;
font-size: 15px;
transition: background var(--transition-fast);
margin: 0 -8px;
padding-left: 8px;
padding-right: 8px;
border-radius: 10px;
}
.ingredient-row:hover {
background: var(--bg-secondary);
}
.ingredient-row:last-child {
border-bottom: none;
}
.ingredient-row .ingredient-name {
color: var(--text-primary);
font-weight: 600;
}
.ingredient-row .ingredient-amount {
color: var(--primary);
font-weight: 800;
}
.detail-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
padding: 16px 20px 24px;
box-shadow: 0 -8px 32px rgba(0,0,0,0.1);
display: flex;
gap: 12px;
z-index: 100;
}
.detail-footer .btn {
flex: 1;
padding: 16px;
font-size: 15px;
}
/* ============ INGREDIENT ITEMS ============ */
.ingredient-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 0;
border-bottom: 1px solid #f5f5f4;
}
.ingredient-item input {
flex: 1;
border: 2px solid #e7e5e4;
border-radius: var(--radius-md);
padding: 12px 14px;
font-size: 14px;
transition: all var(--transition-fast);
}
.ingredient-item input:focus {
border-color: var(--primary);
outline: none;
}
.ingredient-item .remove-btn {
width: 36px;
height: 36px;
border-radius: 50%;
background: #fee2e2;
border: none;
color: #dc2626;
cursor: pointer;
transition: all var(--transition-fast);
display: flex;
align-items: center;
justify-content: center;
}
.ingredient-item .remove-btn:hover {
background: #fecaca;
transform: scale(1.1);
}
.add-ingredient-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 100%;
padding: 14px;
margin-top: 12px;
background: var(--bg-secondary);
border: 2px dashed #d6d3d1;
border-radius: var(--radius-lg);
color: var(--text-secondary);
font-weight: 700;
font-size: 14px;
cursor: pointer;
transition: all var(--transition-fast);
}
.add-ingredient-btn:hover {
border-color: var(--primary);
color: var(--primary);
background: var(--primary-soft);
}
/* ============ CHECKBOX ============ */
.dish-checkbox {
display: flex;
align-items: center;
padding: 14px;
background: white;
border-radius: var(--radius-lg);
margin-bottom: 10px;
cursor: pointer;
transition: all var(--transition-fast);
border: 2px solid transparent;
}
.dish-checkbox:hover {
background: var(--bg-secondary);
}
.dish-checkbox input {
width: 22px;
height: 22px;
margin-right: 14px;
accent-color: var(--primary);
}
.dish-checkbox .dish-label {
font-size: 16px;
font-weight: 700;
color: var(--text-primary);
}
/* ============ STEP EDITOR ============ */
.step-editor {
background: white;
border-radius: var(--radius-xl);
padding: 20px;
box-shadow: var(--shadow-sm);
}
.step-editor-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.step-editor-title {
font-size: 16px;
font-weight: 800;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 8px;
}
.step-editor-title i {
color: var(--primary);
}
.step-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.step-item {
background: var(--bg-secondary);
border-radius: var(--radius-lg);
padding: 16px;
position: relative;
border: 2px solid transparent;
transition: all var(--transition-fast);
}
.step-item:hover {
border-color: rgba(194, 65, 12, 0.15);
}
.step-item-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.step-number {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
color: white;
border-radius: 50%;
font-weight: 800;
font-size: 14px;
}
.step-delete-btn {
width: 28px;
height: 28px;
border-radius: 50%;
background: #fee2e2;
border: none;
color: #dc2626;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all var(--transition-fast);
opacity: 0.7;
}
.step-delete-btn:hover {
background: #fecaca;
opacity: 1;
transform: scale(1.1);
}
.step-input {
width: 100%;
border: 2px solid #e7e5e4;
border-radius: var(--radius-md);
padding: 14px;
font-size: 15px;
font-family: var(--font-body);
resize: vertical;
min-height: 80px;
transition: all var(--transition-fast);
background: white;
}
.step-input:focus {
border-color: var(--primary);
outline: none;
box-shadow: 0 0 0 4px rgba(194, 65, 12, 0.1);
}
.step-input::placeholder {
color: var(--text-muted);
}
.step-image-upload {
display: flex;
align-items: center;
gap: 10px;
margin-top: 12px;
}
.step-image-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 10px 16px;
background: white;
border: 2px dashed #d6d3d1;
border-radius: var(--radius-md);
color: var(--text-secondary);
font-weight: 700;
font-size: 13px;
cursor: pointer;
transition: all var(--transition-fast);
}
.step-image-btn:hover {
border-color: var(--primary);
color: var(--primary);
background: var(--primary-soft);
}
.step-image-preview {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 12px;
}
.step-image-preview img {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
}
.step-preview {
margin-top: 14px;
padding: 14px;
background: white;
border-radius: var(--radius-md);
border: 1px solid #e7e5e4;
}
.step-preview-label {
font-size: 12px;
font-weight: 700;
color: var(--text-muted);
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.step-preview-content {
font-size: 14px;
color: var(--text-secondary);
line-height: 1.8;
}
.step-preview-content img {
max-width: 50%;
border-radius: var(--radius-md);
margin: 12px 0;
display: block;
}
.add-step-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 100%;
padding: 16px;
margin-top: 12px;
background: white;
border: 2px dashed #d6d3d1;
border-radius: var(--radius-lg);
color: var(--text-secondary);
font-weight: 700;
font-size: 14px;
cursor: pointer;
transition: all var(--transition-fast);
}
.add-step-btn:hover {
border-color: var(--primary);
color: var(--primary);
background: var(--primary-soft);
transform: translateY(-2px);
}
/* ============ PARSE RESULT ============ */
.parse-result-card {
background: linear-gradient(135deg, var(--primary-soft) 0%, #fff7ed 100%);
border-radius: var(--radius-xl);
padding: 20px;
margin-top: 16px;
border: 1px solid rgba(194, 65, 12, 0.15);
}
.parse-result-card .parse-dish-name {
font-size: 18px;
font-weight: 800;
color: var(--text-primary);
margin-bottom: 8px;
}
.parse-result-card .parse-info {
font-size: 14px;
color: var(--text-secondary);
font-weight: 600;
}
/* ============ PAGES ============ */
.page {
display: none;
min-height: 100vh;
animation: fadeIn 0.3s ease;
}
.page.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* ============ SEARCH PANEL ============ */
.search-panel {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(180deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);
z-index: 2000;
overflow-y: auto;
padding: 20px;
}
.search-panel.active {
display: block;
animation: slideUp 0.3s ease;
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.search-panel-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
}
.search-panel-back {
width: 44px;
height: 44px;
border-radius: 14px;
background: white;
border: none;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
color: var(--text-primary);
box-shadow: var(--shadow-sm);
cursor: pointer;
}
.search-panel-back:hover {
background: var(--bg-secondary);
}
/* ============ ORDER DETAIL MODAL ============ */
.order-detail-header {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
padding: 32px 24px 48px;
color: white;
}
.order-detail-title {
font-size: 28px;
font-weight: 800;
margin-bottom: 12px;
text-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
.order-detail-status {
display: inline-block;
padding: 8px 16px;
border-radius: 20px;
font-size: 13px;
font-weight: 700;
}
.order-detail-date {
color: var(--text-muted);
font-size: 14px;
}
.order-detail-body {
background: white;
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
margin-top: -24px;
padding: 28px 24px 120px;
box-shadow: 0 -8px 40px rgba(0,0,0,0.1);
max-height: none;
overflow-y: visible;
}
.order-detail-section {
background: var(--bg-secondary);
border-radius: var(--radius-xl);
padding: 22px;
margin-bottom: 16px;
}
.order-detail-section-title {
font-size: 16px;
font-weight: 800;
margin-bottom: 14px;
display: flex;
align-items: center;
gap: 10px;
color: var(--text-primary);
}
.order-detail-section-title i {
color: var(--primary);
}
/* ============ STATS PAGE ============ */
.stats-header {
padding: 20px;
background: linear-gradient(180deg, var(--bg-primary) 0%, transparent 100%);
}
.stats-content {
padding: 20px 20px 24px;
}
.stats-section {
background: white;
border-radius: var(--radius-xl);
padding: 20px;
margin-bottom: 16px;
box-shadow: var(--shadow-sm);
border: 1px solid rgba(194, 65, 12, 0.06);
}
.stats-section-header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 18px;
}
.stats-section-icon {
width: 48px;
height: 48px;
background: linear-gradient(135deg, var(--primary-soft), #fef3c7);
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
color: var(--primary);
}
.stats-section-title h3 {
font-size: 18px;
font-weight: 800;
color: var(--text-primary);
margin-bottom: 2px;
}
.stats-section-title p {
font-size: 13px;
color: var(--text-muted);
font-weight: 600;
margin: 0;
}
.stats-cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.stat-card {
background: var(--bg-secondary);
border-radius: var(--radius-lg);
padding: 16px 12px;
text-align: center;
}
.stat-icon {
width: 44px;
height: 44px;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 10px;
font-size: 20px;
}
.stat-value {
font-size: 24px;
font-weight: 800;
color: var(--text-primary);
line-height: 1;
}
.stat-label {
font-size: 11px;
color: var(--text-muted);
font-weight: 600;
margin-top: 4px;
}
.dish-rank-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.rank-item {
display: flex;
align-items: center;
gap: 14px;
padding: 14px;
background: var(--bg-secondary);
border-radius: var(--radius-lg);
transition: all var(--transition-fast);
}
.rank-item:hover {
transform: translateX(4px);
background: var(--primary-soft);
}
.rank-number {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 800;
font-size: 14px;
flex-shrink: 0;
}
.rank-number.gold {
background: linear-gradient(135deg, #fcd34d, #fbbf24);
color: #92400e;
}
.rank-number.silver {
background: linear-gradient(135deg, #e5e7eb, #d1d5db);
color: #374151;
}
.rank-number.bronze {
background: linear-gradient(135deg, #fed7aa, #fb923c);
color: #9a3412;
}
.rank-number.default {
background: white;
color: var(--text-muted);
border: 2px solid #e7e5e4;
}
.rank-img {
width: 48px;
height: 48px;
border-radius: 12px;
object-fit: cover;
flex-shrink: 0;
}
.rank-info {
flex: 1;
min-width: 0;
}
.rank-name {
font-size: 15px;
font-weight: 700;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.rank-count {
font-size: 12px;
color: var(--text-muted);
font-weight: 600;
margin-top: 2px;
}
.loading-text {
text-align: center;
padding: 20px;
color: var(--text-muted);
font-weight: 600;
}
.empty-rank {
text-align: center;
padding: 30px 20px;
color: var(--text-muted);
}
.empty-rank i {
font-size: 40px;
margin-bottom: 10px;
opacity: 0.5;
}
/* ============ RESPONSIVE ============ */
@media (max-width: 380px) {
.brand-text {
font-size: 20px;
}
.dish-grid {
gap: 12px;
padding: 0 16px;
}
.bottom-nav {
left: 8px;
right: 8px;
bottom: 8px;
}
.fab {
right: 16px;
bottom: 100px;
}
}
.party-images-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
margin-bottom: 12px;
}
.party-image-item {
position: relative;
aspect-ratio: 1;
border-radius: 8px;
overflow: hidden;
}
.party-image-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
.party-image-item .remove-image-btn {
position: absolute;
top: 4px;
right: 4px;
width: 20px;
height: 20px;
background: rgba(0,0,0,0.6);
color: white;
border: none;
border-radius: 50%;
font-size: 12px;
cursor: pointer;
}
.upload-image-btn-wrapper {
text-align: center;
}
.upload-image-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 12px 24px;
background: linear-gradient(135deg, var(--secondary) 0%, var(--secondary-light) 100%);
color: white;
border-radius: 24px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.2s;
box-shadow: 0 2px 8px rgba(5, 150, 105, 0.3);
}
.upload-image-btn:hover {
background: var(--secondary);
color: white;
}
/* ============ COOKBOOK REDESIGN OVERRIDES ============ */
:root {
--primary: #a85a32;
--primary-light: #c97a52;
--primary-dark: #7f4224;
--primary-soft: #f2dcc8;
--secondary: #6f8b63;
--secondary-light: #89a37d;
--secondary-soft: #e5efdf;
--accent: #d4a35f;
--accent-soft: #f5e6c8;
--bg-primary: #f6efe3;
--bg-secondary: #efe4d3;
--bg-card: #fffaf2;
--paper: #fffdf8;
--paper-strong: #fff7ec;
--wood: #d7b894;
--ink: #33251d;
--line: rgba(110, 76, 51, 0.14);
--line-strong: rgba(110, 76, 51, 0.24);
--shadow-sm: 0 6px 16px rgba(103, 72, 45, 0.08);
--shadow-md: 0 12px 28px rgba(103, 72, 45, 0.12);
--shadow-lg: 0 22px 44px rgba(103, 72, 45, 0.14);
--shadow-xl: 0 28px 56px rgba(103, 72, 45, 0.16);
--radius-sm: 10px;
--radius-md: 18px;
--radius-lg: 24px;
--radius-xl: 32px;
--radius-full: 999px;
--bottom-nav-height: 88px;
}
html {
background: var(--bg-primary);
}
body {
background:
radial-gradient(circle at top left, rgba(212, 163, 95, 0.12), transparent 28%),
radial-gradient(circle at top right, rgba(111, 139, 99, 0.08), transparent 24%),
linear-gradient(180deg, #f8f2e9 0%, #f3eadc 52%, #f0e6d8 100%);
color: var(--ink);
padding-bottom: calc(var(--bottom-nav-height) + 24px);
}
body::before {
content: '';
position: fixed;
inset: 0;
pointer-events: none;
background-image:
linear-gradient(rgba(255,255,255,0.35), rgba(255,255,255,0.35)),
repeating-linear-gradient(0deg, transparent 0 23px, rgba(129, 92, 59, 0.03) 23px 24px);
opacity: 0.5;
z-index: -1;
}
a, button, input, select, textarea, .nav-item, .category-chip, .filter-pill, .dish-card, .order-card, .profile-menu-item, .search-container {
-webkit-tap-highlight-color: transparent;
}
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
.nav-item:focus-visible,
.category-chip:focus-visible,
.filter-pill:focus-visible,
.dish-card:focus-visible,
.order-card:focus-visible,
.profile-menu-item:focus-visible,
.search-container:focus-visible,
.search-panel-back:focus-visible,
.fab:focus-visible {
outline: 3px solid rgba(168, 90, 50, 0.22);
outline-offset: 2px;
box-shadow: 0 0 0 5px rgba(168, 90, 50, 0.12);
}
.page {
min-height: calc(100vh - var(--bottom-nav-height));
padding-bottom: 24px;
animation: fadeIn 200ms ease;
}
.menu-header,
.orders-header,
.stats-content,
.profile-section {
padding-top: 18px;
padding-left: 18px;
padding-right: 18px;
background: none;
}
.menu-header .cookbook-hero {
margin-bottom: 14px;
}
.cookbook-hero {
position: relative;
margin: 0;
padding: 14px 16px 14px;
border-radius: 28px;
background:
linear-gradient(180deg, rgba(255,255,255,0.72), rgba(255,250,242,0.94)),
linear-gradient(135deg, rgba(242,220,200,0.95), rgba(255,247,236,0.95));
border: 1px solid rgba(132, 93, 62, 0.14);
box-shadow: var(--shadow-md);
overflow: hidden;
}
.cookbook-hero::before,
.cookbook-hero::after {
content: '';
position: absolute;
border-radius: 50%;
background: rgba(201, 122, 82, 0.08);
pointer-events: none;
}
.cookbook-hero::before {
width: 180px;
height: 180px;
top: -90px;
right: -70px;
}
.cookbook-hero::after {
width: 120px;
height: 120px;
bottom: -56px;
left: -34px;
background: rgba(111, 139, 99, 0.08);
}
.hero-bookmark {
position: absolute;
top: 0;
right: 18px;
width: 38px;
height: 52px;
background: linear-gradient(180deg, #bb6d44 0%, #965332 100%);
border-radius: 0 0 12px 12px;
box-shadow: 0 8px 16px rgba(127, 66, 36, 0.16);
}
.hero-bookmark::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: -1px;
margin: auto;
width: 0;
height: 0;
border-left: 19px solid transparent;
border-right: 19px solid transparent;
border-bottom: 14px solid var(--bg-primary);
}
.hero-intro,
.hero-actions,
.hero-search-shell,
.search-summary-row,
.menu-section-header,
.result-feedback,
.page-section-title,
.modal-note,
.order-summary-strip,
.search-panel-title-row {
position: relative;
z-index: 1;
}
.hero-intro {
padding-right: 36px;
}
.hero-kicker,
.section-kicker,
.page-kicker,
.modal-kicker {
display: inline-flex;
align-items: center;
gap: 8px;
min-height: 28px;
padding: 5px 11px;
border-radius: var(--radius-full);
background: rgba(255, 248, 238, 0.95);
border: 1px solid rgba(168, 90, 50, 0.16);
color: var(--primary-dark);
font-size: 11px;
font-weight: 800;
letter-spacing: 0.08em;
}
.hero-title,
.page-title,
.modal-feature-title,
.detail-name,
.order-detail-title {
font-family: var(--font-display);
letter-spacing: 0.02em;
}
.hero-title {
margin: 8px 0 4px;
font-size: 26px;
line-height: 1.08;
color: #5e3721;
}
.hero-subtitle {
margin: 0;
max-width: 25em;
color: #6d5848;
font-size: 13px;
line-height: 1.45;
font-weight: 700;
}
.hero-actions {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
margin-top: 12px;
}
.hero-action {
min-height: 46px;
padding: 9px 10px;
border-radius: 16px;
border: 1px solid rgba(125, 88, 58, 0.12);
background: rgba(255, 252, 247, 0.96);
color: var(--ink);
text-align: left;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
transition: background var(--transition-fast), border-color var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast);
box-shadow: var(--shadow-sm);
}
.hero-action:hover,
.hero-action:active {
background: #fffdf8;
border-color: rgba(168, 90, 50, 0.22);
transform: translateY(-1px);
}
.hero-action-icon {
width: 34px;
height: 34px;
border-radius: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
background: linear-gradient(180deg, #f5e5d6, #f8efe3);
color: var(--primary-dark);
font-size: 15px;
flex-shrink: 0;
}
.hero-action-copy strong,
.search-summary-copy strong {
display: block;
font-size: 13px;
color: #4f3323;
font-weight: 800;
}
.hero-action-copy span,
.search-summary-copy span {
display: block;
margin-top: 1px;
font-size: 11px;
color: #88705f;
font-weight: 700;
line-height: 1.3;
}
.menu-header {
display: block;
padding-bottom: 0;
}
.hero-search-shell,
.page-shell,
.stats-section,
.profile-card,
.search-panel-shell,
.search-panel-results,
.modal-paper,
.detail-content,
.order-detail-body,
.order-detail-header {
background: rgba(255, 252, 247, 0.92);
border: 1px solid var(--line);
box-shadow: var(--shadow-md);
}
.hero-search-shell {
margin-top: 12px;
padding: 14px;
border-radius: 24px;
background: rgba(255, 252, 247, 0.7);
border: 1px solid rgba(126, 93, 67, 0.1);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.7);
}
.search-summary-row {
display: grid;
grid-template-columns: 1fr auto;
gap: 12px;
align-items: center;
margin-bottom: 12px;
}
.search-summary-chip {
display: inline-flex;
align-items: center;
gap: 8px;
min-height: 34px;
padding: 6px 12px;
border-radius: var(--radius-full);
background: #f8f0e6;
color: #8b6445;
font-size: 12px;
font-weight: 800;
border: 1px solid rgba(168, 90, 50, 0.1);
}
.search-container {
min-height: 56px;
border-radius: 22px;
padding: 6px 10px 6px 16px;
background: var(--paper);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.8);
border: 1px solid rgba(126, 93, 67, 0.12);
}
.search-container:focus-within {
border-color: rgba(168, 90, 50, 0.28);
box-shadow: 0 0 0 4px rgba(168, 90, 50, 0.08);
transform: none;
}
.search-container i {
color: #9b6f52;
font-size: 18px;
}
.search-container input {
font-size: 16px;
font-weight: 700;
color: var(--ink);
padding: 12px 12px 12px 10px;
}
.search-container input[readonly] {
cursor: pointer;
}
.category-filter,
.filter-pills {
display: flex;
gap: 10px;
overflow-x: auto;
padding: 4px 2px 2px;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
}
.category-filter::-webkit-scrollbar,
.filter-pills::-webkit-scrollbar {
display: none;
}
.category-chip,
.filter-pill {
flex-shrink: 0;
min-height: 42px;
padding: 10px 16px;
border-radius: var(--radius-full);
background: #fffaf3;
border: 1px solid rgba(125, 88, 58, 0.12);
color: #7b6453;
font-size: 13px;
font-weight: 800;
box-shadow: none;
transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast), transform var(--transition-fast);
}
.category-chip:hover,
.filter-pill:hover {
color: var(--primary-dark);
border-color: rgba(168, 90, 50, 0.2);
background: #fff6ec;
transform: none;
box-shadow: none;
}
.category-chip:active,
.filter-pill:active {
transform: scale(0.98);
}
.category-chip.active,
.filter-pill.active,
.category-filter .category-chip.active {
background: linear-gradient(180deg, #b96c43 0%, #a85a32 100%);
color: white;
border-color: transparent;
}
.dish-section,
.order-list {
padding: 0 18px 24px;
}
.dish-section {
padding-top: 0;
}
.page-shell {
border-radius: 28px;
padding: 18px 14px 20px;
}
.menu-section-header,
.page-section-head {
display: flex;
align-items: end;
justify-content: space-between;
gap: 14px;
margin-bottom: 16px;
}
.page-title {
margin: 10px 0 4px;
font-size: 32px;
line-height: 1.15;
color: #543320;
}
.page-subtitle,
.menu-section-subtitle,
.result-feedback,
.modal-copy,
.order-summary-strip,
.page-summary-note {
font-size: 13px;
line-height: 1.65;
color: #856c59;
font-weight: 700;
}
.menu-section-title,
.search-panel-title,
.stats-section-title h3,
.modal-title {
font-size: 20px;
font-weight: 900;
color: #4d3021;
}
.dish-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14px;
padding: 0;
}
.dish-grid.search-results-grid {
padding: 0;
}
.dish-card {
display: flex;
flex-direction: column;
min-height: 100%;
background: linear-gradient(180deg, rgba(255,253,249,0.98), rgba(255,247,236,0.98));
border-radius: 24px;
border: 1px solid rgba(124, 88, 58, 0.12);
box-shadow: var(--shadow-sm);
overflow: hidden;
cursor: pointer;
transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast);
}
.dish-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
border-color: rgba(168, 90, 50, 0.22);
}
.dish-card:active {
transform: scale(0.985);
}
.dish-card .img-wrapper {
position: relative;
padding-top: 66%;
background: linear-gradient(135deg, #f4e3cf, #ead2b3);
}
.dish-card img {
transition: opacity 180ms ease;
}
.dish-card:hover img,
.dish-card .img-wrapper:hover img {
transform: none;
}
.dish-card .category-tag {
top: 12px;
left: 12px;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: var(--radius-full);
background: rgba(255, 249, 240, 0.96);
color: #8b5c3f;
border: 1px solid rgba(125, 88, 58, 0.1);
box-shadow: none;
backdrop-filter: none;
font-size: 11px;
font-weight: 800;
letter-spacing: 0.04em;
text-transform: none;
}
.dish-card .order-indicator {
top: auto;
right: 12px;
bottom: 12px;
width: auto;
min-width: 44px;
height: 30px;
padding: 0 10px;
border-radius: var(--radius-full);
font-size: 12px;
font-weight: 800;
box-shadow: none;
}
.dish-card .order-indicator.in-order {
background: rgba(111, 139, 99, 0.95);
color: white;
animation: none;
}
.dish-card .card-content {
display: flex;
flex: 1;
flex-direction: column;
gap: 10px;
padding: 14px 14px 16px;
}
.dish-card .dish-name {
margin: 0;
font-size: 17px;
font-weight: 900;
line-height: 1.4;
color: #452a1b;
}
.dish-card .dish-meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
font-size: 12px;
color: #876f5d;
font-weight: 800;
}
.dish-card .dish-meta::before {
display: none;
}
.dish-card-note {
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 30px;
width: fit-content;
max-width: 100%;
padding: 6px 11px;
border-radius: 999px;
background: rgba(168, 90, 50, 0.12);
color: #8a5432;
font-size: 12px;
line-height: 1.35;
font-weight: 800;
}
.dish-card-note::before {
content: '';
width: 7px;
height: 7px;
border-radius: 999px;
background: currentColor;
flex-shrink: 0;
opacity: 0.7;
}
.dish-card-note.is-in-order {
background: rgba(111, 139, 99, 0.16);
color: #4f6a44;
}
.dish-status-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.dish-note,
.dish-usage,
.dish-state-pill,
.inline-note,
.order-mini-pill,
.participant-chip,
.ingredient-chip,
.feedback-pill {
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 28px;
padding: 5px 10px;
border-radius: var(--radius-full);
font-size: 12px;
font-weight: 800;
line-height: 1.3;
}
.dish-note {
color: #8f755f;
background: #f8efe3;
}
.dish-usage {
color: #6a5b50;
background: #f3ede6;
}
.dish-state-pill.is-in-order {
background: rgba(111, 139, 99, 0.16);
color: #4f6a44;
}
.dish-state-pill.is-ready {
background: rgba(168, 90, 50, 0.14);
color: #8a522f;
}
.result-feedback {
min-height: 22px;
margin-bottom: 14px;
}
.result-feedback strong {
color: #5a3925;
}
.empty-state {
padding: 44px 20px;
border-radius: 24px;
background: linear-gradient(180deg, rgba(255,252,247,0.95), rgba(248,239,227,0.96));
border: 1px dashed rgba(143, 112, 84, 0.28);
}
.empty-state .empty-icon {
width: 74px;
height: 74px;
margin-bottom: 16px;
font-size: 30px;
background: #f4e7d7;
color: #996244;
}
.empty-state .empty-title {
font-size: 18px;
color: #4f3021;
}
.empty-state .empty-desc {
max-width: 20em;
margin-left: auto;
margin-right: auto;
color: #8a715f;
font-size: 13px;
line-height: 1.7;
}
.empty-state .empty-action {
min-height: 46px;
padding: 12px 20px;
border-radius: 18px;
background: linear-gradient(180deg, #b96c43, #a85a32);
box-shadow: none;
}
.empty-state .empty-action:hover {
transform: none;
box-shadow: none;
background: linear-gradient(180deg, #c1734a, #a85a32);
}
.page-header-card {
padding: 18px;
border-radius: 28px;
background: rgba(255, 252, 247, 0.92);
border: 1px solid var(--line);
box-shadow: var(--shadow-md);
margin-bottom: 14px;
}
.order-list {
padding-top: 0;
}
.order-card {
padding: 18px;
margin-bottom: 14px;
border-radius: 24px;
background: linear-gradient(180deg, rgba(255,253,249,0.98), rgba(255,246,234,0.96));
box-shadow: var(--shadow-sm);
border: 1px solid rgba(124, 88, 58, 0.12);
}
.order-card::before {
width: 4px;
background: linear-gradient(180deg, #c07c4c, #9f5b33);
}
.order-card:hover {
transform: none;
box-shadow: var(--shadow-md);
}
.order-top {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 10px;
margin-bottom: 8px;
}
.order-name {
font-size: 19px;
font-weight: 900;
color: #4b2e1f;
line-height: 1.35;
}
.order-date {
font-size: 13px;
color: #806858;
font-weight: 800;
margin-bottom: 12px;
}
.order-status,
.order-detail-status {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 30px;
padding: 5px 12px;
border-radius: var(--radius-full);
font-size: 12px;
font-weight: 900;
}
.status-progress {
background: rgba(111, 139, 99, 0.16);
color: #4e6a45;
}
.status-done {
background: rgba(168, 90, 50, 0.14);
color: #8a512e;
}
.dish-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.dish-tag {
display: inline-flex;
align-items: center;
min-height: 30px;
padding: 6px 12px;
border-radius: 16px;
font-size: 12px;
font-weight: 800;
color: #715847;
background: #f7ede1;
border: 1px solid rgba(124, 88, 58, 0.1);
}
.dish-tag-cold {
background: #edf4ef;
color: #5d7555;
}
.profile-card {
border-radius: 28px;
padding: 8px;
}
.profile-menu-item {
min-height: 72px;
border-radius: 22px;
padding: 16px;
background: rgba(255,255,255,0.6);
}
.profile-menu-item + .profile-menu-item {
margin-top: 8px;
}
.menu-icon {
width: 48px;
height: 48px;
border-radius: 16px;
background: #f4e7d7;
color: var(--primary-dark);
}
.profile-menu-item:hover {
transform: none;
background: rgba(255, 248, 240, 0.92);
}
.stats-content {
padding-top: 18px;
}
.stats-section {
padding: 18px;
border-radius: 28px;
margin-bottom: 14px;
}
.stats-section-header {
align-items: center;
margin-bottom: 16px;
}
.stats-section-icon,
.stat-icon {
background: #f3e5d4 !important;
color: var(--primary-dark);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.7);
}
.stats-section-title p,
.stat-label,
.rank-count,
.loading-text,
.empty-rank {
color: #8a715f;
}
.stats-cards {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.stat-card {
padding: 16px 12px;
border-radius: 20px;
background: #f8efe4;
border: 1px solid rgba(124, 88, 58, 0.08);
}
.rank-item {
padding: 14px;
background: #fbf4e8;
border-radius: 20px;
}
.rank-item:hover {
transform: none;
background: #f7ecde;
}
.bottom-nav {
left: 12px;
right: 12px;
bottom: 12px;
padding: 10px 8px;
border-radius: 26px;
background: rgba(255, 250, 242, 0.96);
backdrop-filter: blur(12px);
box-shadow: 0 18px 34px rgba(88, 60, 38, 0.16);
border: 1px solid rgba(124, 88, 58, 0.12);
}
.nav-item {
min-width: 64px;
min-height: 60px;
justify-content: center;
gap: 5px;
color: #8c7564;
font-size: 11px;
font-weight: 800;
}
.nav-item::after {
width: 24px;
height: 4px;
bottom: 0;
background: linear-gradient(90deg, #c07c4c, #a85a32);
}
.nav-item.active {
color: #9a5632;
background: rgba(255, 245, 234, 0.86);
}
.nav-item i {
font-size: 21px;
}
.nav-item:hover:not(.active) {
background: rgba(247, 236, 221, 0.8);
}
.fab {
right: 18px;
bottom: 118px;
width: auto;
min-width: 62px;
height: 62px;
padding: 0 18px;
border-radius: 22px;
font-size: 24px;
gap: 10px;
background: linear-gradient(180deg, #be7148, #a85a32);
box-shadow: 0 16px 28px rgba(127, 66, 36, 0.28);
transition: transform var(--transition-fast), box-shadow var(--transition-fast), opacity var(--transition-fast);
}
.fab:hover {
transform: translateY(-1px);
}
.fab:active {
transform: scale(0.98);
}
.fab-top {
width: 52px;
min-width: 52px;
height: 52px;
bottom: 190px;
padding: 0;
border-radius: 18px;
background: rgba(80, 62, 49, 0.88);
box-shadow: 0 10px 22px rgba(50, 39, 31, 0.2);
}
.fab-label {
font-size: 14px;
font-weight: 900;
letter-spacing: 0.02em;
}
.search-panel {
padding: 18px;
background: rgba(246, 239, 227, 0.96);
}
.search-panel.active {
animation: fadeIn 180ms ease;
}
.search-panel-shell {
min-height: calc(100vh - 36px);
padding: 18px;
border-radius: 28px;
overflow: hidden;
}
.search-panel-header {
display: block;
margin-bottom: 14px;
}
.search-panel-title-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 14px;
}
.search-panel-back {
width: 46px;
height: 46px;
border-radius: 16px;
background: #fffaf3;
box-shadow: none;
border: 1px solid rgba(124, 88, 58, 0.12);
}
.search-panel-results {
margin-top: 14px;
padding: 14px;
border-radius: 24px;
}
.modal-dialog {
padding: 12px;
}
.modal-content {
border-radius: 30px;
background: linear-gradient(180deg, rgba(255,253,249,0.99), rgba(248,239,227,0.98));
box-shadow: 0 24px 54px rgba(70, 48, 31, 0.18);
}
.modal-header,
.modal-footer,
.modal-body {
background: transparent;
}
.modal-header {
position: sticky;
top: 0;
z-index: 3;
padding: 20px 20px 10px;
border-bottom: 1px solid rgba(124, 88, 58, 0.1);
}
.modal-body {
padding: 18px 20px;
}
.modal-footer {
position: sticky;
bottom: 0;
z-index: 3;
padding: 14px 20px 20px;
border-top: 1px solid rgba(124, 88, 58, 0.1);
gap: 10px;
}
.modal-title {
font-size: 22px;
}
.modal-subtitle {
margin-top: 4px;
color: #8b715f;
font-size: 13px;
line-height: 1.6;
font-weight: 700;
}
.btn,
.btn-primary,
.btn-secondary,
.btn-success,
.btn-danger,
.upload-image-btn,
.search-btn,
.add-ingredient-btn,
.add-step-btn,
.step-image-btn {
min-height: 46px;
border-radius: 18px;
font-weight: 800;
font-size: 14px;
transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast), transform var(--transition-fast), opacity var(--transition-fast);
box-shadow: none;
}
.btn:hover,
.btn-primary:hover,
.btn-secondary:hover,
.btn-success:hover,
.btn-danger:hover,
.upload-image-btn:hover,
.search-btn:hover,
.add-ingredient-btn:hover,
.add-step-btn:hover,
.step-image-btn:hover {
transform: none;
box-shadow: none;
}
.btn:disabled,
.btn.disabled,
.upload-image-btn:disabled,
.search-btn:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.btn-primary,
.search-btn {
background: linear-gradient(180deg, #bc7046 0%, #a85a32 100%);
}
.btn-success,
.upload-image-btn {
background: linear-gradient(180deg, #86a178 0%, #6f8b63 100%);
}
.btn-secondary {
background: #efe5d8;
color: #6d5646;
}
.btn-danger {
background: linear-gradient(180deg, #cb6e5a 0%, #b45444 100%);
}
.form-control,
.form-select,
.ingredient-item input,
.step-input {
min-height: 48px;
border-radius: 18px;
border: 1px solid rgba(124, 88, 58, 0.16);
background: rgba(255, 253, 249, 0.98);
color: #412a1f;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.8);
}
.form-control:focus,
.form-select:focus,
.ingredient-item input:focus,
.step-input:focus {
border-color: rgba(168, 90, 50, 0.32);
box-shadow: 0 0 0 4px rgba(168, 90, 50, 0.08);
}
.form-label {
color: #624837;
font-size: 13px;
font-weight: 800;
letter-spacing: 0.02em;
}
.ingredient-item,
.step-item,
.dish-checkbox,
.parse-result-card,
.order-detail-section,
.detail-section,
.participant-entry,
.selection-summary,
.selection-section {
background: rgba(255, 252, 247, 0.8);
border: 1px solid rgba(124, 88, 58, 0.1);
border-radius: 20px;
}
.ingredient-item {
padding: 12px;
gap: 10px;
border-bottom: none;
margin-bottom: 10px;
}
.ingredient-item .remove-btn,
.step-delete-btn,
.remove-dish-btn,
.remove-image-btn {
min-width: 36px;
width: 36px;
height: 36px;
border-radius: 14px;
background: #f5e0db;
color: #9d4f47;
border: none;
}
.step-item {
padding: 14px;
margin-bottom: 12px;
}
.step-number {
width: 34px;
height: 34px;
border-radius: 12px;
background: #f3e5d4;
color: var(--primary-dark);
display: flex;
align-items: center;
justify-content: center;
font-weight: 900;
}
.step-input {
min-height: 120px;
padding: 14px;
}
.step-preview {
border-radius: 16px;
background: #f8efe4;
}
.dish-checkbox {
display: flex;
align-items: center;
gap: 10px;
min-height: 54px;
padding: 12px 14px;
margin-bottom: 10px;
}
.dish-checkbox input {
width: 18px;
height: 18px;
accent-color: var(--primary);
flex-shrink: 0;
}
.dish-label {
font-weight: 800;
color: #513426;
}
.detail-cover-wrapper {
background: linear-gradient(180deg, rgba(243, 229, 212, 0.96), rgba(233, 208, 179, 0.92));
padding: 18px 18px 0;
}
.detail-cover {
height: 270px;
border-radius: 26px;
box-shadow: var(--shadow-md);
}
.detail-close-btn {
top: 28px;
right: 28px;
width: 46px;
height: 46px;
border-radius: 16px;
background: rgba(255, 251, 244, 0.94);
backdrop-filter: none;
box-shadow: none;
}
.detail-content {
margin: -18px 12px 0;
padding: 22px 18px 110px;
border-radius: 28px 28px 0 0;
max-height: none;
overflow: visible;
touch-action: pan-y;
}
.modal-dialog.modal-fullscreen .detail-cover-wrapper,
.modal-dialog.modal-fullscreen .order-detail-header,
.modal-dialog.modal-fullscreen .detail-content,
.modal-dialog.modal-fullscreen .order-detail-body,
.modal-dialog.modal-fullscreen .detail-footer {
touch-action: pan-y;
}
.modal-dialog.modal-fullscreen .detail-content,
.modal-dialog.modal-fullscreen .order-detail-body {
flex: 0 0 auto;
min-height: auto;
overflow: visible;
}
.detail-header-block,
.order-detail-heading,
.modal-section-head {
margin-bottom: 16px;
}
.detail-category-tag {
margin-top: 10px;
background: #f2e3d3;
color: #8a5634;
border: 1px solid rgba(168, 90, 50, 0.12);
}
.detail-summary {
margin-top: 12px;
color: #7f6958;
font-size: 14px;
line-height: 1.7;
font-weight: 700;
}
.order-summary-strip {
display: block;
margin-top: 12px;
padding: 12px 14px;
border-radius: 18px;
background: rgba(255, 246, 236, 0.18);
color: rgba(255,255,255,0.9);
border: 1px solid rgba(255,255,255,0.16);
}
.detail-empty-note {
margin: 0;
padding: 14px 16px;
border-radius: 16px;
background: rgba(243, 230, 215, 0.72);
color: #7c654f;
font-size: 13px;
line-height: 1.65;
font-weight: 700;
}
.detail-section,
.order-detail-section {
padding: 16px;
margin: 0 0 14px;
box-shadow: none;
background: rgba(255,255,255,0.58);
}
.detail-section .md-content,
.order-detail-copy {
color: #5f4a3b;
line-height: 1.8;
}
.detail-footer {
position: sticky;
left: 0;
right: 0;
bottom: 0;
z-index: 3;
margin-top: auto;
display: grid;
grid-template-columns: 1fr 1fr 1.3fr;
gap: 10px;
padding: 14px 16px calc(env(safe-area-inset-bottom, 0px) + 16px);
background: rgba(255, 251, 244, 0.97);
box-shadow: 0 -10px 24px rgba(70, 48, 31, 0.08);
backdrop-filter: blur(12px);
}
.detail-footer .btn {
min-width: 0;
padding: 0 12px;
}
.order-detail-header {
margin: 12px 12px 0;
padding: 20px 18px 28px;
border-radius: 28px 28px 22px 22px;
background: linear-gradient(180deg, rgba(189, 113, 72, 0.95), rgba(162, 88, 51, 0.95));
color: white;
border-color: rgba(255,255,255,0.14);
}
.order-detail-date {
color: rgba(255,255,255,0.85);
font-weight: 700;
}
.order-detail-body {
margin: -14px 12px 0;
padding: 18px 18px 112px;
border-radius: 28px 28px 0 0;
max-height: none;
overflow: visible;
touch-action: pan-y;
}
.order-detail-section {
padding: 16px;
margin: 0 0 14px;
border-radius: 24px;
box-shadow: none;
background: rgba(255,255,255,0.58);
}
.order-detail-section-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
}
.order-section-note {
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 32px;
padding: 6px 12px;
border-radius: 999px;
background: rgba(242, 227, 211, 0.8);
color: #8a5a38;
font-size: 12px;
font-weight: 800;
border: 1px solid rgba(168, 90, 50, 0.12);
white-space: nowrap;
}
#order-detail-dishes {
display: flex;
flex-direction: column;
gap: 10px;
}
.dish-item-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 12px;
border: 1px solid rgba(124, 88, 58, 0.08);
border-radius: 20px;
background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(248,240,231,0.92));
box-shadow: inset 0 1px 0 rgba(255,255,255,0.7);
}
.dish-item-row + .dish-item-row {
margin-top: 0;
}
.dish-item-row:last-child {
border-bottom: 1px solid rgba(124, 88, 58, 0.08);
}
.dish-info {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
flex: 1;
}
.dish-thumb {
width: 56px;
height: 56px;
border-radius: 18px;
object-fit: cover;
background: #f3e5d4;
flex-shrink: 0;
}
.dish-copy {
min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.dish-item-row .dish-name {
font-size: 15px;
font-weight: 900;
color: #4e3020;
margin: 0;
line-height: 1.35;
}
.dish-item-meta {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.dish-meta-chip {
display: inline-flex;
align-items: center;
min-height: 24px;
padding: 4px 10px;
border-radius: 999px;
background: rgba(242, 227, 211, 0.85);
color: #8a5a38;
font-size: 11px;
font-weight: 800;
border: 1px solid rgba(168, 90, 50, 0.1);
}
.dish-meta-note {
font-size: 12px;
color: #7d6656;
font-weight: 700;
}
.remove-dish-btn {
width: 38px;
height: 38px;
border-radius: 14px;
background: rgba(254, 226, 226, 0.9);
border: 1px solid rgba(239, 68, 68, 0.12);
color: #dc2626;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background var(--transition-fast), transform var(--transition-fast), color var(--transition-fast);
flex-shrink: 0;
}
.remove-dish-btn:hover,
.remove-dish-btn:active {
background: #fecaca;
transform: none;
}
.participant-chip {
margin: 0 8px 8px 0;
background: #f2e4d4;
color: #7d573b;
}
.participants-wrap {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
/* Participant editing in detail modal */
.participant-edit-wrapper {
margin-top: 8px;
}
.participant-edit-input .form-control {
background: rgba(255,255,255,0.9);
border: 1px solid #e5d3c3;
border-radius: 20px;
padding: 8px 16px;
font-size: 14px;
}
.participant-edit-input .form-control:focus {
background: #fff;
border-color: #c9a86c;
box-shadow: 0 0 0 3px rgba(201,168,108,0.15);
}
.participant-tag {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: linear-gradient(135deg, #f8f4ed 0%, #efe6d8 100%);
border: 1px solid #e5d3c3;
border-radius: 20px;
font-size: 14px;
color: #6b5344;
animation: tagAppear 0.2s ease-out;
}
@keyframes tagAppear {
from { opacity: 0; transform: scale(0.8); }
to { opacity: 1; transform: scale(1); }
}
.participant-remove-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
padding: 0;
border: none;
background: rgba(180,140,100,0.2);
border-radius: 50%;
font-size: 14px;
line-height: 1;
color: #8b6f5a;
cursor: pointer;
transition: all 0.15s;
}
.participant-remove-btn:hover {
background: #e74c3c;
color: white;
}
/* Ranking styles */
.ranking-item {
margin-bottom: 16px;
padding: 12px 14px;
background: rgba(255,255,255,0.5);
border-radius: 12px;
border: 1px solid rgba(0,0,0,0.04);
transition: transform 0.2s, box-shadow 0.2s;
}
.ranking-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
}
.ranking-item.rank-gold {
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
border-color: #fbbf24;
}
.ranking-item.rank-silver {
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
border-color: #94a3b8;
}
.ranking-item.rank-bronze {
background: linear-gradient(135deg, #fef3c7 0%, #fed7aa 100%);
border-color: #d97706;
}
.ranking-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 8px;
}
.ranking-rank {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 24px;
height: 24px;
padding: 0 6px;
background: #e5e7eb;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
color: #6b7280;
}
.rank-gold .ranking-rank {
background: linear-gradient(135deg, #fbbf24, #f59e0b);
color: #78350f;
}
.rank-silver .ranking-rank {
background: linear-gradient(135deg, #94a3b8, #64748b);
color: white;
}
.rank-bronze .ranking-rank {
background: linear-gradient(135deg, #d97706, #b45309);
color: white;
}
.ranking-name {
flex: 1;
font-size: 15px;
font-weight: 500;
color: #374151;
}
.ranking-count {
font-size: 14px;
font-weight: 600;
color: #6b7280;
}
.rank-gold .ranking-count {
color: #b45309;
}
.ranking-progress {
height: 10px;
background: rgba(0,0,0,0.06);
border-radius: 5px;
overflow: hidden;
}
.ranking-bar {
height: 100%;
border-radius: 5px;
transition: width 0.5s ease-out;
}
.ingredient-row {
margin: 0 0 6px;
padding: 12px 10px;
border-radius: 16px;
border-bottom: none;
background: rgba(255,255,255,0.54);
}
.ingredient-row:hover {
background: rgba(255,255,255,0.7);
}
.party-images-container {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 10px;
}
.party-image-item {
border-radius: 16px;
background: #f3e5d4;
}
.upload-image-btn-wrapper {
text-align: left;
}
.parse-result-card {
padding: 16px;
margin-top: 14px;
background: rgba(255, 252, 247, 0.94);
}
.loading-paper {
padding: 24px 18px;
border-radius: 28px;
background: linear-gradient(180deg, rgba(255,253,249,0.98), rgba(247,238,225,0.98));
border: 1px solid rgba(124, 88, 58, 0.12);
}
.loading-spinner-shell {
width: 62px;
height: 62px;
margin: 0 auto 16px;
border-radius: 20px;
background: #f4e7d7;
display: flex;
align-items: center;
justify-content: center;
}
.loading-spinner-shell .spinner-border {
width: 2rem !important;
height: 2rem !important;
border-width: 0.24rem;
color: var(--primary) !important;
}
.feedback-pill {
background: #f3e6d7;
color: #7c654f;
}
.visually-soft {
color: #8b715f;
}
.btn-close {
border-radius: 14px;
background: #f2e6d9;
opacity: 1;
}
.btn-close:hover {
transform: none;
background: #ead8c4;
}
.toast-stack {
position: fixed;
top: max(16px, env(safe-area-inset-top, 0px) + 8px);
left: 16px;
right: 16px;
z-index: 1200;
display: flex;
flex-direction: column;
gap: 10px;
pointer-events: none;
}
.app-toast {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 14px 16px;
border-radius: 20px;
border: 1px solid rgba(124, 88, 58, 0.12);
background: rgba(255, 251, 244, 0.98);
box-shadow: var(--shadow-md);
color: #583a29;
pointer-events: auto;
}
.app-toast.is-success {
border-color: rgba(111, 139, 99, 0.2);
}
.app-toast.is-error {
border-color: rgba(173, 87, 78, 0.24);
background: rgba(255, 247, 245, 0.98);
}
.app-toast-icon {
width: 34px;
height: 34px;
border-radius: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background: #f4e7d7;
color: #9a603f;
font-size: 16px;
}
.app-toast.is-success .app-toast-icon {
background: rgba(111, 139, 99, 0.16);
color: #4f6a44;
}
.app-toast.is-error .app-toast-icon {
background: rgba(173, 87, 78, 0.14);
color: #a14d46;
}
.app-toast-copy {
min-width: 0;
flex: 1;
font-size: 13px;
line-height: 1.65;
font-weight: 700;
}
.confirm-sheet .modal-content {
border-radius: 26px;
overflow: hidden;
}
.confirm-sheet-body {
padding: 22px 20px 18px;
}
.confirm-sheet-icon {
width: 52px;
height: 52px;
margin-bottom: 14px;
border-radius: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
background: #f4e7d7;
color: #9a603f;
font-size: 22px;
}
.confirm-sheet-title {
margin: 0 0 8px;
font-size: 22px;
line-height: 1.25;
color: #4d3021;
}
.confirm-sheet-text {
margin: 0;
color: #7c654f;
font-size: 14px;
line-height: 1.75;
font-weight: 700;
}
.confirm-sheet .modal-footer {
padding-top: 0;
}
@media (max-width: 760px) {
.hero-title {
font-size: 24px;
}
.hero-actions,
.stats-cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.stats-cards .stat-card:last-child {
grid-column: span 2;
}
}
@media (max-width: 560px) {
.dish-grid,
.dish-grid.search-results-grid {
grid-template-columns: 1fr 1fr;
}
.detail-footer,
.modal-footer {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.detail-footer .btn,
.modal-footer .btn {
width: 100%;
min-width: 0;
}
}
@media (max-width: 420px) {
.cookbook-hero,
.page-shell,
.page-header-card,
.search-panel-shell,
.stats-section,
.profile-card,
.detail-content,
.order-detail-header,
.order-detail-body {
border-radius: 24px;
}
.menu-header {
padding-left: 14px;
padding-right: 14px;
}
#dish-detail-modal .modal-dialog.modal-fullscreen,
#order-detail-modal .modal-dialog.modal-fullscreen,
#dish-detail-modal .modal-dialog.modal-fullscreen .modal-content,
#order-detail-modal .modal-dialog.modal-fullscreen .modal-content {
min-height: 100svh;
}
.cookbook-hero,
.dish-section,
.orders-header,
.order-list,
.stats-content,
.profile-section,
.search-panel {
margin-left: 0;
margin-right: 0;
}
.cookbook-hero {
margin: 0 0 8px;
padding: 12px 12px 12px;
}
.dish-section,
.orders-header,
.order-list,
.stats-content,
.profile-section {
padding-left: 14px;
padding-right: 14px;
}
.page-title {
font-size: 28px;
}
.hero-title {
font-size: 24px;
}
.hero-subtitle {
max-width: none;
font-size: 12px;
}
.stats-cards,
.dish-grid,
.dish-grid.search-results-grid {
grid-template-columns: 1fr;
}
.hero-actions {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.search-summary-row,
.order-detail-section-head {
grid-template-columns: 1fr;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.stats-cards .stat-card:last-child {
grid-column: auto;
}
.bottom-nav {
left: 8px;
right: 8px;
bottom: 8px;
}
.fab {
right: 14px;
bottom: 108px;
min-width: 58px;
height: 58px;
padding: 0 16px;
}
.fab-top {
bottom: 176px;
width: 48px;
min-width: 48px;
height: 48px;
}
.fab-label {
font-size: 13px;
}
}
</style>
</head>
<body>
<!-- Search Panel -->
<div class="search-panel" id="search-panel">
<div class="search-panel-shell">
<div class="search-panel-header">
<div class="search-panel-title-row">
<button class="search-panel-back" id="btn-close-search" aria-label="关闭搜索">
<i class="bi bi-arrow-left"></i>
</button>
<div>
<div class="section-kicker"><i class="bi bi-journal-bookmark"></i> 菜谱检索</div>
<div class="search-panel-title">找一道今天想做的家常菜</div>
</div>
</div>
<div class="search-container" style="flex: 1;">
<i class="bi bi-search"></i>
<input type="text" placeholder="搜菜名、食材、凉菜或汤" id="search-input">
</div>
</div>
<div class="result-feedback" id="search-feedback">输入关键词后这里会显示匹配到的菜谱</div>
<div class="search-panel-results">
<div class="dish-grid search-results-grid" id="search-results"></div>
</div>
</div>
</div>
<!-- Menu Page -->
<div class="page active" id="page-menu">
<div class="menu-header">
<section class="cookbook-hero">
<div class="hero-bookmark"></div>
<div class="hero-intro">
<div class="hero-kicker"><i class="bi bi-journal-richtext"></i> 家庭菜谱本</div>
<h1 class="hero-title">今天做什么翻开就知道</h1>
<p class="hero-subtitle">先挑今天想做的再翻翻常做菜和备菜安排</p>
</div>
<div class="hero-actions">
<button class="hero-action" type="button" id="hero-search-trigger">
<span class="hero-action-icon"><i class="bi bi-search"></i></span>
<span class="hero-action-copy">
<strong>找菜</strong>
<span>菜名或食材</span>
</span>
</button>
<button class="hero-action" type="button" id="hero-order-trigger">
<span class="hero-action-icon"><i class="bi bi-basket2"></i></span>
<span class="hero-action-copy">
<strong>看聚会单</strong>
<span>整理备菜</span>
</span>
</button>
</div>
<div class="hero-search-shell">
<span class="visually-hidden" id="menu-current-filter">全部菜谱</span>
<div class="category-filter" id="category-filter">
<div class="category-chip active" data-id="">全部</div>
</div>
</div>
</section>
</div>
<section class="dish-section">
<div class="page-shell">
<div class="result-feedback" id="dish-feedback">正在整理菜谱目录</div>
<div class="dish-grid" id="dish-list">
<div class="empty-state" style="grid-column: 1 / -1;">
<div class="empty-icon">
<i class="bi bi-cup-hot"></i>
</div>
<div class="empty-title">还没有菜品</div>
<div class="empty-desc">先记下第一道拿手菜菜谱本就能慢慢丰富起来</div>
</div>
</div>
</div>
</section>
</div>
<!-- Orders Page -->
<div class="page" id="page-orders">
<div class="orders-header">
<div class="page-header-card">
<div class="page-kicker">备菜清单</div>
<h2 class="page-title">聚会安排</h2>
<p class="page-subtitle">把时间参与人和待做菜品收在一起做饭前先把单子列清楚</p>
<div class="filter-pills">
<div class="filter-pill active" data-status="">全部</div>
<div class="filter-pill" data-status="in_progress">进行中</div>
<div class="filter-pill" data-status="completed">已完成</div>
</div>
</div>
</div>
<div class="order-list" id="order-list">
<div class="empty-state">
<div class="empty-icon">
<i class="bi bi-receipt"></i>
</div>
<div class="empty-title">还没有聚会单</div>
<div class="empty-desc">点右下角新建一张备菜清单把要做的菜先安排起来</div>
</div>
</div>
</div>
<!-- Profile Page -->
<div class="page" id="page-profile">
<div class="profile-section">
<div class="page-header-card">
<div class="page-kicker">我的菜谱本</div>
<h2 class="page-title">收纳与备份</h2>
<p class="page-subtitle">把这本家里的菜谱本保存好换设备时也能继续翻</p>
</div>
<div class="profile-card">
<a href="/accounts/logout/" class="profile-menu-item" style="text-decoration: none; color: inherit;">
<div class="menu-icon" style="color: #dc3545;">
<i class="bi bi-box-arrow-right"></i>
</div>
<span class="menu-text" style="color: #dc3545;">退出登录</span>
<i class="bi bi-chevron-right menu-arrow"></i>
</a>
<div class="profile-menu-item" id="btn-export">
<div class="menu-icon">
<i class="bi bi-download"></i>
</div>
<span class="menu-text">导出数据备份</span>
<i class="bi bi-chevron-right menu-arrow"></i>
</div>
<div class="profile-menu-item">
<div class="menu-icon">
<i class="bi bi-info-circle"></i>
</div>
<span class="menu-text">版本 v1.0.0</span>
<i class="bi bi-chevron-right menu-arrow"></i>
</div>
</div>
</div>
</div>
<!-- Stats Page -->
<div class="page" id="page-stats">
<div class="stats-content">
<div class="page-header-card">
<div class="page-kicker">常做记录</div>
<h2 class="page-title">家里最常做什么</h2>
<p class="page-subtitle">从聚会次数和参与记录里看看哪些菜最常上桌</p>
</div>
<div class="stats-section">
<div class="stats-section-header">
<div class="stats-section-icon">
<i class="bi bi-trophy-fill"></i>
</div>
<div class="stats-section-title">
<h3>菜品排行榜</h3>
<p>按聚会次数排序</p>
</div>
</div>
<div class="dish-rank-list" id="dish-rank-list">
<div class="loading-text">加载中...</div>
</div>
</div>
<div class="stats-section">
<div class="stats-section-header">
<div class="stats-section-icon">
<i class="bi bi-bar-chart-fill"></i>
</div>
<div class="stats-section-title">
<h3>数据概览</h3>
<p>这本菜谱本目前记了多少内容</p>
</div>
</div>
<div class="stats-cards">
<div class="stat-card">
<div class="stat-icon">
<i class="bi bi-cup-hot" style="color: #a85a32;"></i>
</div>
<div class="stat-info">
<div class="stat-value" id="total-dishes">0</div>
<div class="stat-label">菜品总数</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<i class="bi bi-basket2" style="color: #6f8b63;"></i>
</div>
<div class="stat-info">
<div class="stat-value" id="total-gatherings">0</div>
<div class="stat-label">聚会总数</div>
</div>
</div>
<div class="stat-card" style="cursor: pointer;" onclick="showParticipantRanking()">
<div class="stat-icon">
<i class="bi bi-people" style="color: #8a5634;"></i>
</div>
<div class="stat-info">
<div class="stat-value" id="total-participants">0</div>
<div class="stat-label">参与人数</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Bottom Navigation -->
<nav class="bottom-nav">
<div class="nav-items">
<div class="nav-item active" data-page="menu">
<i class="bi bi-book-fill"></i>
<span>菜单</span>
</div>
<div class="nav-item" data-page="orders">
<i class="bi bi-receipt"></i>
<span>聚会</span>
</div>
<div class="nav-item" data-page="stats">
<i class="bi bi-bar-chart-fill"></i>
<span>统计</span>
</div>
<div class="nav-item" data-page="profile">
<i class="bi bi-person-fill"></i>
<span>我的</span>
</div>
</div>
</nav>
<!-- Loading Modal -->
<div class="modal fade" id="loading-modal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered modal-sm">
<div class="modal-content loading-paper">
<div class="modal-body text-center py-4">
<div class="loading-spinner-shell">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div class="modal-kicker">生成分享图</div>
<p class="mb-0 mt-3" style="font-size: 15px; font-weight: 700; color: var(--text-primary);">正在把聚会清单整理成一张好看的菜单卡</p>
</div>
</div>
</div>
</div>
<!-- Participant Ranking Modal -->
<div class="modal fade" id="participant-ranking-modal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">参与次数排行</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="participant-ranking-list"></div>
</div>
</div>
</div>
</div>
<!-- FAB -->
<button class="fab fab-top" id="fab-top" onclick="window.scroll(0, 0)" aria-label="回到顶部">
<i class="bi bi-arrow-up"></i>
</button>
<button class="fab" id="fab-add" aria-label="新增">
<i class="bi bi-plus-lg"></i>
<span class="fab-label" id="fab-label">记菜</span>
</button>
<!-- Dish Detail Modal -->
<div class="modal fade" id="dish-detail-modal" tabindex="-1">
<div class="modal-dialog modal-fullscreen">
<div class="modal-content">
<div class="detail-cover-wrapper">
<img src="" alt="菜品封面" class="detail-cover" id="detail-cover">
<button class="detail-close-btn" data-bs-dismiss="modal" aria-label="关闭菜品详情">
<i class="bi bi-x-lg"></i>
</button>
</div>
<div class="detail-content">
<div class="detail-header-block">
<div class="modal-kicker">菜谱详情</div>
<h2 class="detail-name" id="detail-name"></h2>
<span class="detail-category-tag" id="detail-category"></span>
<div class="detail-summary" id="detail-summary-text">翻翻这道菜需要什么材料再决定要不要加入聚会单</div>
</div>
<div class="detail-section" id="detail-ingredients">
<h3 class="detail-section-title">
<i class="bi bi-basket-fill"></i>
所需材料
</h3>
<div id="detail-ingredients-list"></div>
</div>
<div class="detail-section">
<h3 class="detail-section-title">
<i class="bi bi-list-ol"></i>
做法步骤
</h3>
<div class="md-content" id="detail-description"></div>
</div>
</div>
<div class="detail-footer">
<button class="btn btn-secondary" id="btn-delete-dish">删除</button>
<button class="btn btn-primary" id="btn-edit-dish">编辑</button>
<button class="btn btn-success" id="btn-add-to-order">添加到聚会</button>
</div>
</div>
</div>
</div>
<!-- Dish Form Modal -->
<div class="modal fade" id="dish-form-modal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<div>
<div class="modal-kicker">记录一道新菜</div>
<h5 class="modal-title" id="dish-form-title">添加菜品</h5>
<div class="modal-subtitle">保留封面材料和做法方便下次直接翻出来做</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="关闭添加菜品"></button>
</div>
<div class="modal-body">
<form id="dish-form">
<input type="hidden" id="dish-id">
<div class="form-group">
<label class="form-label">菜品名称</label>
<input type="text" class="form-control" id="dish-name" placeholder="请输入菜品名称" required>
</div>
<div class="form-group">
<label class="form-label">菜品分类</label>
<select class="form-select" id="dish-category"></select>
</div>
<div class="form-group">
<label class="form-label">封面图片</label>
<input type="file" class="form-control" id="dish-image" accept="image/*">
<input type="hidden" id="dish-image-url">
<div id="image-preview" class="mt-3" style="display: none;">
<img src="" style="width: 100px; height: 100px; border-radius: 12px; object-fit: cover;">
</div>
</div>
<div class="form-group">
<label class="form-label">材料清单</label>
<div id="ingredients-list"></div>
<button type="button" class="add-ingredient-btn" id="btn-add-ingredient">
<i class="bi bi-plus-lg"></i>
添加材料
</button>
</div>
<div class="form-group">
<div class="step-editor">
<div class="step-editor-header">
<div class="step-editor-title">
<i class="bi bi-list-ol"></i>
做法步骤
</div>
</div>
<div class="step-list" id="step-list"></div>
<button type="button" class="add-step-btn" id="btn-add-step">
<i class="bi bi-plus-lg"></i>
添加步骤
</button>
</div>
<input type="hidden" id="dish-description">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="btn-save-dish">保存菜品</button>
</div>
</div>
</div>
</div>
<!-- Parse Modal -->
<div class="modal fade" id="parse-modal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<div>
<div class="modal-kicker">链接导入</div>
<h5 class="modal-title">从链接解析菜品</h5>
<div class="modal-subtitle">粘贴外部菜谱地址先抓取内容再手动确认后保存</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="关闭链接解析"></button>
</div>
<div class="modal-body">
<div class="form-group">
<label class="form-label">菜谱链接</label>
<input type="url" class="form-control" id="parse-url" placeholder="粘贴下厨房、美食杰等菜谱链接">
</div>
<div id="parse-result" style="display: none;">
<div class="parse-result-card">
<div class="parse-dish-name" id="parse-name"></div>
<div class="parse-info" id="parse-ingredients-count"></div>
</div>
<button class="btn btn-primary w-100 mt-3" id="btn-apply-parse">应用并编辑</button>
</div>
</div>
</div>
</div>
</div>
<!-- Order Form Modal -->
<div class="modal fade" id="order-form-modal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<div>
<div class="modal-kicker">备菜清单</div>
<h5 class="modal-title" id="order-form-title">新建聚会</h5>
<div class="modal-subtitle">先定时间和参与人再把想做的菜一并放进这张清单里</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="关闭聚会表单"></button>
</div>
<div class="modal-body">
<input type="hidden" id="order-id">
<div class="selection-section p-3 mb-3">
<div class="modal-section-head">
<div class="section-kicker">基础信息</div>
</div>
<div class="form-group mb-3">
<label class="form-label">聚会名称</label>
<input type="text" class="form-control" id="order-name" placeholder="例如:周末家宴、除夕晚餐" required>
</div>
<div class="form-group mb-0">
<label class="form-label">聚会时间</label>
<input type="datetime-local" class="form-control" id="order-party-date">
</div>
</div>
<div class="selection-section p-3 mb-3">
<div class="modal-section-head">
<div class="section-kicker">参与人</div>
<div class="modal-copy">输入人名回车即可加入方便统计这次聚餐都有谁</div>
</div>
<div id="order-participants-container">
<div class="input-group mb-2">
<input type="text" class="form-control participant-input" placeholder="输入人名后回车添加">
</div>
</div>
<div id="order-participants-tags" class="d-flex flex-wrap gap-1 mt-2"></div>
</div>
<div class="selection-section p-3">
<div class="modal-section-head">
<div class="section-kicker">已选菜品</div>
<div class="modal-copy" id="order-dish-count-label">从下面勾选想做的菜</div>
</div>
<div id="order-dish-select" style="max-height: 320px; overflow-y: auto;"></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="btn-save-order">保存聚会</button>
</div>
</div>
</div>
</div>
<!-- Order Detail Modal -->
<div class="modal fade" id="order-detail-modal" tabindex="-1">
<div class="modal-dialog modal-fullscreen">
<div class="modal-content">
<div class="order-detail-header">
<div style="display: flex; justify-content: space-between; align-items: flex-start; gap: 14px;">
<div class="order-detail-heading">
<div class="modal-kicker" style="background: rgba(255,255,255,0.16); color: rgba(255,255,255,0.94); border-color: rgba(255,255,255,0.18);">聚会详情</div>
<h2 class="order-detail-title" id="order-detail-title">聚会名称</h2>
<div class="order-detail-meta">
<span class="order-detail-status" id="order-detail-status"></span>
<span class="order-detail-date" id="order-detail-date"></span>
</div>
<div class="order-summary-strip" id="order-detail-summary">看看这次备菜都安排了什么再决定是否完成清单</div>
</div>
<button class="detail-close-btn" data-bs-dismiss="modal" aria-label="关闭聚会详情">
<i class="bi bi-x-lg"></i>
</button>
</div>
</div>
<div class="order-detail-body">
<div class="order-detail-section">
<div class="order-detail-section-head">
<h3 class="order-detail-section-title">
<i class="bi bi-cup-hot-fill"></i>
已选菜品
</h3>
<div class="order-section-note" id="order-detail-dish-note">正在整理这次要做的菜</div>
</div>
<div class="dish-list" id="order-detail-dishes"></div>
</div>
<div class="order-detail-section">
<h3 class="order-detail-section-title">
<i class="bi bi-people-fill"></i>
参与人员
</h3>
<div class="participant-edit-wrapper">
<div class="input-group participant-edit-input">
<input type="text" class="form-control" id="detail-participant-input" placeholder="输入人名后回车添加">
</div>
<div id="detail-participants-tags" class="d-flex flex-wrap gap-2 mt-2"></div>
</div>
</div>
<div class="order-detail-section" id="order-ingredients-section">
<h3 class="order-detail-section-title">
<i class="bi bi-basket-fill"></i>
食材汇总
</h3>
<div id="order-detail-ingredients"></div>
</div>
<div class="order-detail-section" id="order-images-section">
<h3 class="order-detail-section-title">
<i class="bi bi-camera-fill"></i>
聚会照片
</h3>
<div class="party-images-container" id="order-detail-images"></div>
<div class="upload-image-btn-wrapper">
<label class="upload-image-btn">
<i class="bi bi-plus-lg"></i> 上传照片
<input type="file" accept="image/*" multiple id="party-image-upload" style="display: none;">
</label>
</div>
</div>
</div>
<div class="detail-footer">
<button class="btn btn-primary" id="btn-share-order">
<i class="bi bi-share"></i> 分享
</button>
<button class="btn btn-danger" id="btn-delete-order">删除</button>
<button class="btn btn-success" id="btn-complete-order">完成</button>
</div>
</div>
</div>
</div>
<div class="toast-stack" id="toast-stack" aria-live="polite" aria-atomic="true"></div>
<!-- Confirm Modal -->
<div class="modal fade" id="confirm-modal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered confirm-sheet">
<div class="modal-content">
<div class="confirm-sheet-body">
<div class="confirm-sheet-icon">
<i class="bi bi-journal-check"></i>
</div>
<h5 class="confirm-sheet-title" id="confirm-modal-title">确认一下这步操作</h5>
<p class="confirm-sheet-text" id="confirm-modal-message">确定要继续吗</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="confirm-modal-cancel">再想想</button>
<button type="button" class="btn btn-primary" id="confirm-modal-confirm">继续</button>
</div>
</div>
</div>
</div>