diff --git a/css/style.css b/css/style.css
index 15f3ee7..7f322a5 100644
--- a/css/style.css
+++ b/css/style.css
@@ -1201,992 +1201,151 @@
border-radius: 6px;
}
-/* ===== Autocomplete Search Suggestions ===== */
-.search-suggestions {
- position: absolute;
- top: 100%;
- left: 0;
- width: 100%;
- max-width: 250px;
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(10px);
- border-radius: 12px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
- margin-top: 0.5rem;
- z-index: 1001;
- overflow: hidden;
- border: 1px solid rgba(255, 87, 34, 0.2);
- animation: slideDown 0.3s cubic-bezier(0.16, 1, 0.3, 1);
-}
-
-@keyframes slideDown {
- from { opacity: 0; transform: translateY(-10px); }
- to { opacity: 1; transform: translateY(0); }
-}
-
-.suggestion-item {
- padding: 0.8rem 1.2rem;
- cursor: pointer;
- display: flex;
- justify-content: space-between;
- align-items: center;
- border-bottom: 1px solid rgba(255, 87, 34, 0.1);
- transition: background 0.2s ease, padding-left 0.2s ease;
-}
-
-.suggestion-item:last-child {
- border-bottom: none;
-}
-
-.suggestion-item:hover {
- background: rgba(255, 87, 34, 0.1);
- padding-left: 1.5rem;
-}
-
-.suggestion-item.no-matches {
- color: #bf360c;
- font-weight: 500;
- text-align: center;
- justify-content: center;
- cursor: default;
- background: transparent !important;
- padding: 1rem;
-}
-
-.suggestion-name {
- font-weight: 600;
- color: #3e2723;
- font-size: 0.95rem;
-}
-
-.suggestion-category {
- font-size: 0.75rem;
- background: rgba(255, 87, 34, 0.2);
- color: #bf360c;
- padding: 0.2rem 0.5rem;
- border-radius: 20px;
- font-weight: 700;
-}
-
-/* ===== Advanced Filters UI ===== */
-.filter-toggle-container {
- display: flex;
- justify-content: center;
- margin-top: 1rem;
- margin-bottom: 2rem;
-}
-
-.btn-filter-toggle {
- background: white;
- border: 2px solid #ff5722;
- color: #ff5722;
- padding: 0.6rem 1.5rem;
- border-radius: 50px;
- font-weight: 600;
- font-size: 0.95rem;
- display: flex;
- align-items: center;
- gap: 0.5rem;
- transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
- box-shadow: 0 4px 15px rgba(255, 87, 34, 0.1);
-}
-
-.btn-filter-toggle:hover {
- background: #fbe9e7;
- transform: translateY(-2px);
- box-shadow: 0 6px 20px rgba(255, 87, 34, 0.2);
-}
-
-.btn-filter-toggle.active {
- background: #ff5722;
- color: white;
- box-shadow: 0 6px 20px rgba(255, 87, 34, 0.4);
-}
-
-.advanced-filters {
- max-width: 800px;
- margin: 0 auto 3rem;
- background: rgba(255, 255, 255, 0.8);
- backdrop-filter: blur(15px);
- border: 1px solid rgba(255, 87, 34, 0.15);
- border-radius: 20px;
- padding: 2rem;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
- animation: fadeIn 0.4s ease;
-}
-
-@keyframes fadeIn {
- from { opacity: 0; transform: translateY(10px); }
- to { opacity: 1; transform: translateY(0); }
-}
-
-.filter-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 2rem;
-}
-
-.filter-group {
+.food-meta {
+ margin-top: 10px;
display: flex;
flex-direction: column;
- gap: 0.6rem;
-}
-
-.filter-group label {
- font-weight: 700;
- color: #bf360c;
- font-size: 0.95rem;
-}
-
-/* Slider Custom Styles */
-.filter-group input[type="range"] {
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- width: 100%;
- height: 6px;
- border-radius: 3px;
- background: #ffe0b2;
- outline: none;
-}
-
-.filter-group input[type="range"]::-webkit-slider-thumb {
- -webkit-appearance: none;
- appearance: none;
- width: 18px;
- height: 18px;
- border-radius: 50%;
- background: #ff5722;
- cursor: pointer;
- transition: transform 0.1s ease;
- box-shadow: 0 0 8px rgba(255, 87, 34, 0.4);
-}
-
-.filter-group input[type="range"]::-webkit-slider-thumb:hover {
- transform: scale(1.2);
-}
-
-#price-slider-val {
- font-weight: 700;
- color: #ff5722;
-}
-
-/* Selectors custom styles */
-.filter-group select {
- padding: 0.6rem 0.8rem;
- border-radius: 10px;
- border: 2px solid #ffcc99;
- background: white;
- font-size: 0.95rem;
- color: #5d4037;
- outline: none;
- cursor: pointer;
- font-family: inherit;
- transition: border-color 0.3s ease;
-}
-
-.filter-group select:focus {
- border-color: #ff5722;
-}
-
-/* Dietary custom checkboxes */
-.checkbox-container {
- display: flex;
- flex-direction: column;
- gap: 0.6rem;
- margin-top: 0.2rem;
-}
-
-.custom-checkbox {
- display: flex;
- align-items: center;
- gap: 0.6rem;
- cursor: pointer;
- user-select: none;
-}
-
-.custom-checkbox input[type="checkbox"] {
- width: 18px;
- height: 18px;
- accent-color: #ff5722;
- cursor: pointer;
+ gap: 8px;
}
-.checkbox-label {
- font-size: 0.95rem;
+.food-rating {
+ font-size: 14px;
+ color: #f4b400;
font-weight: 600;
- color: #5d4037 !important;
}
-.filter-actions {
+.rating-stars {
display: flex;
- justify-content: flex-end;
- margin-top: 2rem;
- padding-top: 1.5rem;
- border-top: 1px solid rgba(255, 87, 34, 0.1);
+ gap: 4px;
}
-.btn-reset-filters {
- background: transparent;
- border: 2px solid #bf360c;
- color: #bf360c;
- padding: 0.5rem 1.5rem;
- border-radius: 50px;
- font-weight: 600;
- font-size: 0.9rem;
- transition: all 0.3s ease;
-}
-
-.btn-reset-filters:hover {
- background: #bf360c;
- color: white;
- box-shadow: 0 4px 15px rgba(191, 54, 12, 0.3);
-}
-
-/* ===== Highlights & Food Metadata Styles ===== */
-mark.highlight {
- background: linear-gradient(120deg, #ffe082 0%, #ffd54f 100%);
- color: #3e2723;
- padding: 0.1rem 0.3rem;
- border-radius: 4px;
- font-weight: inherit;
- box-shadow: 0 2px 8px rgba(255, 213, 79, 0.4);
-}
-
-.card-meta {
- display: flex;
- justify-content: space-between;
- width: 100%;
- margin-bottom: 0.5rem;
- font-size: 0.85rem;
- font-weight: 700;
-}
-
-.card-meta .rating {
- color: #ffb300;
-}
-
-.card-meta .spice {
- color: #bf360c;
-}
-
-.card-tags {
- display: flex;
- gap: 0.4rem;
- flex-wrap: wrap;
- margin-top: 0.8rem;
-}
-
-.tag {
- font-size: 0.7rem;
- padding: 0.2rem 0.6rem;
- border-radius: 12px;
- font-weight: 700;
- text-transform: uppercase;
- letter-spacing: 0.5px;
-}
-
-.tag-vegan {
- background: #e8f5e9;
- color: #2e7d32;
- border: 1px solid rgba(46, 125, 50, 0.2);
-}
-
-.tag-gluten-free {
- background: #e0f7fa;
- color: #00838f;
- border: 1px solid rgba(0, 131, 143, 0.2);
-}
-
-/* Fix description layout on food card */
-.card-content {
- width: 100%;
- display: flex;
- flex-direction: column;
- flex-grow: 1;
-}
-
-.card-content h3 {
- margin-bottom: 0.4rem;
-}
-
-.card-content p {
- font-size: 0.9rem;
- color: #5d4037;
- line-height: 1.4;
- margin-bottom: auto;
-}
-
-.card-footer {
- width: 100%;
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-top: 1.2rem;
- padding-top: 0.8rem;
- border-top: 1px solid #fbe9e7;
-}
-
-.card-footer .price {
- font-size: 1.2rem;
- font-weight: 700;
- color: #bf360c;
-}
-
-.card-footer .add-btn {
- background: #2e7d32;
- color: white;
- padding: 0.4rem 1.2rem;
- border-radius: 50px;
- border: none;
- font-weight: 700;
- box-shadow: 0 4px 10px rgba(46, 125, 50, 0.3);
- transition: all 0.3s ease;
-}
-
-.card-footer .add-btn:hover {
- background: #1b5e20;
- box-shadow: 0 6px 15px rgba(27, 94, 32, 0.5);
- transform: translateY(-1px);
-}
-
-/* ===== Orders Dashboard Layout ===== */
-.orders-page {
- padding-top: 4rem;
- padding-bottom: 5rem;
-}
-
-.orders-page h1 {
- font-size: 2.2rem;
- color: #bf360c;
- margin-bottom: 2.5rem;
- text-align: center;
- font-weight: 700;
-}
-
-.orders-container {
- display: flex;
- flex-direction: column;
- gap: 2.5rem;
- max-width: 800px;
- margin: 0 auto;
-}
-
-/* Empty Orders Page */
-.empty-orders {
- text-align: center;
- background: rgba(255, 255, 255, 0.85);
- backdrop-filter: blur(15px);
- border: 1px solid rgba(255, 87, 34, 0.15);
- border-radius: 20px;
- padding: 4rem 2rem;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
-}
-
-.empty-orders h2 {
- font-size: 1.6rem;
- color: #3e2723;
- margin-bottom: 0.8rem;
-}
-
-.empty-orders p {
- color: #5d4037;
- font-size: 1.05rem;
- margin-bottom: 1.5rem;
-}
-
-/* Order Progress Card */
-.order-card {
- background: rgba(255, 255, 255, 0.9);
- backdrop-filter: blur(10px);
- border: 1px solid rgba(255, 87, 34, 0.15);
- border-radius: 20px;
- padding: 2rem;
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.06);
- transition: transform 0.3s ease, box-shadow 0.3s ease;
-}
-
-.order-card:hover {
- transform: translateY(-2px);
- box-shadow: 0 12px 35px rgba(255, 87, 34, 0.1);
-}
-
-.order-card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- border-bottom: 1px solid #fbe9e7;
- padding-bottom: 1.2rem;
- margin-bottom: 1.5rem;
- flex-wrap: wrap;
- gap: 1rem;
-}
-
-.order-meta-info {
- display: flex;
- flex-direction: column;
- gap: 0.3rem;
-}
-
-.order-id {
- font-size: 1.1rem;
- color: #3e2723;
+.star {
+ cursor: pointer;
+ font-size: 18px;
+ color: #d3d3d3;
+ transition: 0.2s ease;
}
-.order-date {
- font-size: 0.85rem;
- color: #8d6e63;
+.star:hover {
+ color: #f4b400;
+ transform: scale(1.1);
}
-/* Status Badges */
-.status-badge {
- font-size: 0.85rem;
- font-weight: 700;
- padding: 0.4rem 1rem;
+.trending-badge {
+ width: fit-content;
+ padding: 5px 10px;
border-radius: 20px;
- text-transform: uppercase;
- letter-spacing: 0.5px;
- display: inline-block;
-}
-
-.status-badge.status-pending {
- background: #fff8e1;
- color: #ffb300;
- border: 1px solid rgba(255, 179, 0, 0.3);
-}
-.status-badge.status-preparing {
- background: #fbe9e7;
- color: #ff5722;
- border: 1px solid rgba(255, 87, 34, 0.3);
-}
-
-.status-badge.status-on-the-way {
- background: #e1f5fe;
- color: #0288d1;
- border: 1px solid rgba(2, 136, 209, 0.3);
-}
+ background: rgba(255, 140, 0, 0.12);
+ color: #ff7a00;
-.status-badge.status-delivered {
- background: #e8f5e9;
- color: #2e7d32;
- border: 1px solid rgba(46, 125, 50, 0.3);
-}
-
-/* Graphical Progress Stepper Track */
-.order-timeline {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin: 2rem 0 2.5rem;
- padding: 0 1rem;
-}
-
-.timeline-step {
- display: flex;
- flex-direction: column;
- align-items: center;
- position: relative;
- z-index: 2;
- gap: 0.5rem;
-}
-
-.step-circle {
- width: 32px;
- height: 32px;
- border-radius: 50%;
- background: #e0e0e0;
- color: #757575;
- display: flex;
- align-items: center;
- justify-content: center;
- font-weight: 700;
- font-size: 0.9rem;
- transition: all 0.4s ease;
- border: 2px solid transparent;
-}
-
-.step-label {
- font-size: 0.8rem;
+ font-size: 12px;
font-weight: 600;
- color: #9e9e9e;
- transition: all 0.4s ease;
-}
-
-/* Timeline Connectors */
-.timeline-line {
- flex: 1;
- height: 4px;
- background: #e0e0e0;
- margin-top: -1.2rem;
- z-index: 1;
- transition: all 0.4s ease;
-}
-
-/* Highlighted Progress Active Steps states */
-.timeline-step.active .step-circle {
- background: #ffe0b2;
- color: #ff5722;
- border-color: #ff5722;
-}
-
-.timeline-step.active .step-label {
- color: #ff5722;
-}
-
-.timeline-step.current .step-circle {
- background: #ff5722;
- color: white;
- box-shadow: 0 0 12px rgba(255, 87, 34, 0.5);
- transform: scale(1.15);
}
-.timeline-step.current .step-label {
- font-weight: 700;
- color: #bf360c;
-}
-
-.timeline-line.active {
- background: #ffaa66;
-}
-
-/* Order Item Row Summary Layout */
-.order-items-list {
+.food-meta {
+ margin-top: 10px;
display: flex;
flex-direction: column;
- gap: 0.6rem;
- background: #fafafa;
- border-radius: 12px;
- padding: 1.2rem;
- margin-bottom: 1.5rem;
- border: 1px solid #fbe9e7;
+ gap: 8px;
}
-.order-item-row {
- display: flex;
- justify-content: space-between;
- font-size: 0.95rem;
- color: #5d4037;
-}
-
-.order-item-row span:first-child {
+.food-rating {
+ font-size: 14px;
+ color: #f4b400;
font-weight: 600;
}
-.order-item-row span:last-child {
- font-weight: 700;
-}
-
-/* Order Card Footer */
-.order-card-footer {
+.rating-stars {
display: flex;
- justify-content: space-between;
- align-items: center;
- flex-wrap: wrap;
- gap: 1rem;
-}
-
-.order-total-price {
- font-size: 1.05rem;
- color: #5d4037;
+ gap: 4px;
}
-.order-total-price strong {
- font-size: 1.3rem;
- color: #bf360c;
- margin-left: 0.4rem;
-}
-
-/* Reorder Button styling */
-.btn-reorder {
- background: #ff5722;
- color: white;
- border: none;
- padding: 0.6rem 1.6rem;
- border-radius: 50px;
- font-weight: 700;
- font-size: 0.95rem;
- box-shadow: 0 4px 15px rgba(255, 87, 34, 0.3);
- transition: all 0.3s ease;
+.star {
cursor: pointer;
+ font-size: 18px;
+ color: #d3d3d3;
+ transition: 0.2s ease;
}
-.btn-reorder:hover {
- background: #bf360c;
- box-shadow: 0 6px 20px rgba(191, 54, 12, 0.4);
- transform: translateY(-1px);
-}
-/* ===== Dark Mode Theme ===== */
-.theme-toggle {
- padding: 8px 12px;
- border: none;
- border-radius: 6px;
- background: #e64a19;
- color: white;
- font-size: 16px;
- cursor: pointer;
- transition: 0.3s;
-}
-
-.theme-toggle:hover {
- background: #e66900;
-}
-/* body */
-body.dark {
- background: #1e1e1e;
- color: #f5f5f5;
-}
-
-/* Headings */
-body.dark h1,
-body.dark h2,
-body.dark h3,
-body.dark h4{
- color: #ff5722;
-}
-body.dark p{
- color: #ffab91;
+.star:hover {
+ color: #f4b400;
+ transform: scale(1.1);
}
-/* Header & Navigation */
-body.dark header {
- background: #2c2c2c;
- box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
-}
-body.dark nav a {
- color: #f5f5f5;
-}
-body.dark nav a:hover,
-body.dark nav a.active {
- background: #ff5722;
- box-shadow: 0 0 8px rgba(255, 87, 34, 0.8);
-}
-body.dark .dropdown-menu {
- background: #333;
-}
-body.dark .dropdown-menu a:hover {
- background: #ff5722;
-}
-
-/* Hero Section */
-body.dark .hero {
- background: linear-gradient(120deg, #3b1f17, #2c150d);
- color: #fff3e0;
-}
-body.dark .btn-primary {
- background: #ff5722;
- color: #fff;
-}
-body.dark .btn-primary:hover {
- background: #e64a19;
-}
-
-/* Cards & Containers */
-body.dark .card,
-body.dark .skeleton-card,
-body.dark .skeleton-cart-item {
- background: #2a2a2a;
- color: #eee;
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4);
-}
-body.dark .card h3 {
- color: #ff5722;
-}
-body.dark .card p.price {
- color: #ffab91;
-}
-body.dark .card p.description {
- color: #ccc;
-}
-
-/* Sections */
-body.dark section.menu,
-body.dark section.specials,
-body.dark section.about,
-body.dark section.testimonials {
- background: #252525;
- color: #eee;
-}
-body.dark .filter-btn {
- background: transparent;
- border: 2px solid #ff5722;
- color: #ff5722;
-}
-body.dark .filter-btn.active,
-body.dark .filter-btn:hover {
- background: #ff5722;
- color: #fff;
-}
-
-/* Cart Sidebar */
-body.dark #cart-sidebar {
- background: #2b2b2b;
- box-shadow: -6px 0 25px rgba(0, 0, 0, 0.6);
-}
-body.dark #cart-header {
- background: #ff5722;
-}
-body.dark .cart-item {
- background: #333;
-}
+.trending-badge {
+ width: fit-content;
+ padding: 5px 10px;
+ border-radius: 20px;
-body.dark #checkout-btn {
- background: #ff5722;
- color: #fff;
-}
+ background: rgba(255, 140, 0, 0.12);
+ color: #ff7a00;
-/* Forms */
-body.dark section.contact {
- background: #252525;
- color: #eee;
-}
-body.dark section.contact h2 {
- color: #ff5722;
-}
-body.dark form.contact-form {
- background: #2b2b2b;
- box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4);
-}
-body.dark form.contact-form input,
-body.dark form.contact-form textarea {
- background: #1e1e1e;
- color: #f5f5f5;
- border-color: #444;
-}
-body.dark form.contact-form label {
- color: #ffab91;
-}
-body.dark .btn-submit {
- background: #ff5722;
-}
-
-/* Testimonials */
-body.dark .testimonial {
- background: #2a2a2a;
- color: #ddd;
-}
-body.dark .testimonial::before {
- color: #ff5722;
+ font-size: 12px;
+ font-weight: 600;
}
-/* Newsletter */
-body.dark section.newsletter {
- background: #2c2c2c;
- color: #fff;
-}
-body.dark .newsletter-form button {
- background: #ff5722;
- color: #fff;
+.star.active {
+ color: #f4b400;
}
-/* Footer */
-body.dark footer {
- background: #111;
- color: #eee;
-}
-body.dark .social-icons a:hover {
- color: #ff5722;
-}
+.toast-message {
-/* Skeleton Animation background */
-body.dark .skeleton {
- background: linear-gradient(
- 90deg,
- #534949 25%,
- #444 50%,
- #3a3a3a 75%
- );
-}
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
-/* ===== Custom Scrollbar ===== */
-::-webkit-scrollbar {
- width: 8px;
-}
-::-webkit-scrollbar-track {
- background: #fff3e0;
-}
-::-webkit-scrollbar-thumb {
- background: #ff5722;
- border-radius: 999px;
-}
-::-webkit-scrollbar-thumb:hover {
- background: #e64a19;
-/* ===== Accessibility Styles ===== */
-
-/* Skip link - visible on focus */
-.skip-link {
- position: absolute;
- top: -40px;
- left: 0;
- background: #ff5722;
+ background: #333;
color: white;
- padding: 0.6rem 1rem;
- text-decoration: none;
- z-index: 10000;
- border-radius: 0 0 8px 0;
- font-weight: 600;
-}
-
-.skip-link:focus {
- top: 0;
-}
-
-/* Enhanced focus states for keyboard navigation */
-body.keyboard-nav button:focus,
-body.keyboard-nav a:focus,
-body.keyboard-nav input:focus,
-body.keyboard-nav textarea:focus,
-body.keyboard-nav select:focus {
- outline: 3px solid #ff5722;
- outline-offset: 2px;
-}
-
-/* Focus states for all interactive elements */
-button:focus-visible,
-a:focus-visible,
-input:focus-visible,
-textarea:focus-visible,
-select:focus-visible {
- outline: 3px solid #ff5722;
- outline-offset: 2px;
- border-radius: 4px;
-}
-/* Card focus state */
-.card:focus {
- outline: 3px solid #ff5722;
- outline-offset: 2px;
- box-shadow: 0 0 20px rgba(255, 87, 34, 0.5);
-}
+ padding: 12px 18px;
-/* Search suggestion focused state */
-.suggestion-item.focused {
- background: #ffe0d0;
- color: #3e3e3e;
-}
+ border-radius: 10px;
-.suggestion-item:focus-visible {
- outline: 2px solid #ff5722;
- outline-offset: 1px;
-}
+ opacity: 0;
-/* Improved focus for navigation links */
-nav a:focus-visible {
- box-shadow: 0 0 8px #ff5722;
-}
+ transform: translateY(20px);
-/* Filter button focus state */
-.filter-btn:focus-visible,
-.menu-filter:focus-visible {
- outline: 2px solid #ff5722;
- outline-offset: 2px;
-}
+ transition: 0.3s ease;
-/* Checkbox and radio focus states */
-input[type="checkbox"]:focus-visible,
-input[type="radio"]:focus-visible {
- outline: 2px solid #ff5722;
- outline-offset: 2px;
+ z-index: 9999;
}
-/* Range slider focus state */
-input[type="range"]:focus-visible {
- outline: 2px solid #ff5722;
- outline-offset: 4px;
+.toast-message.show {
+ opacity: 1;
+ transform: translateY(0);
}
-/* Select dropdown focus state */
-select:focus-visible {
- outline: 2px solid #ff5722;
- outline-offset: 2px;
+.first-try-section {
+ margin-top: 10px;
}
-/* High contrast mode support */
-@media (prefers-contrast: more) {
- button:focus-visible,
- a:focus-visible,
- input:focus-visible,
- textarea:focus-visible,
- select:focus-visible {
- outline-width: 4px;
- }
-
- .card:focus {
- outline-width: 4px;
- box-shadow: 0 0 30px rgba(255, 87, 34, 0.8);
- }
+.first-try-count {
+ font-size: 13px;
+ color: #555;
+ margin-bottom: 6px;
}
-/* Reduced motion support */
-@media (prefers-reduced-motion: reduce) {
- * {
- animation-duration: 0.01ms !important;
- animation-iteration-count: 1 !important;
- transition-duration: 0.01ms !important;
- }
-}
+.first-try-btn {
-/* Dark mode support for better contrast */
-@media (prefers-color-scheme: dark) {
- body {
- background: #1a1a1a;
- color: #e0e0e0;
- }
+ border: none;
- .card {
- background: #2d2d2d;
- }
+ padding: 6px 12px;
- .suggestion-item {
- background: #2d2d2d;
- color: #e0e0e0;
- }
+ border-radius: 8px;
- .suggestion-item.focused {
- background: #bf360c;
- color: #fff;
- }
-}
+ cursor: pointer;
-/* Ensure links are underlined for clarity */
-a[href] {
- text-decoration: underline;
- text-decoration-color: rgba(255, 87, 34, 0.5);
-}
+ background: #fff3e0;
-/* Improve contrast of disabled buttons */
-button:disabled {
- opacity: 0.6;
- cursor: not-allowed;
-}
+ color: #ff7a00;
-/* Out of Stock Badge */
-.out-of-stock-badge {
- background-color: #6c757d !important; /* Grey color */
- color: white;
- padding: 5px 10px;
- border-radius: 5px;
- font-size: 12px;
font-weight: 600;
- display: inline-block;
- margin: 10px 0 5px 0;
- letter-spacing: 0.5px;
- text-align: center;
- width: fit-content;
-}
-/* Disabled button styling */
-.add-btn:disabled {
- opacity: 0.6;
- cursor: not-allowed;
- background-color: #cccccc !important;
- transform: none !important;
+ transition: 0.3s ease;
}
-/* Hover effect for disabled button */
-.add-btn:disabled:hover {
- transform: none !important;
- opacity: 0.6;
-}
+.first-try-btn:hover {
+ background: #ffe0b2;
}
\ No newline at end of file
diff --git a/data/menu.json b/data/menu.json
index b7ac808..a0f3aae 100644
--- a/data/menu.json
+++ b/data/menu.json
@@ -1,242 +1,61 @@
-[
- {
- "id": "samosa-001",
- "name": "Aloo Samosa",
- "description": "Crispy triangular pastry filled with spiced potato and peas",
- "price": 20,
- "image": "img/1.avif",
- "category": "Snacks",
- "rating": 4.8,
- "spice": "Medium",
- "dietary": ["vegan"],
- "available" : false
- },
- {
- "id": "kachori-002",
- "name": "Pyaaz Ki Kachori",
- "description": "Golden fried kachori with aromatic onion filling",
- "price": 25,
- "image": "img/2.avif",
- "category": "Snacks",
- "rating": 4.7,
- "spice": "High",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "panipuri-003",
- "name": "Pani Puri",
- "description": "Crispy puris filled with moong sprouts, served with sweet and spicy water",
- "price": 30,
- "image": "img/7.avif",
- "category": "Chaat",
- "rating": 4.9,
- "spice": "High",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "bhelpuri-004",
- "name": "Bhel Puri",
- "description": "Mix of puffed rice, vegetables, and tangy tamarind chutney",
- "price": 35,
- "image": "img/8.avif",
- "category": "Chaat",
- "rating": 4.6,
- "spice": "Medium",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "lassi-005",
- "name": "Mango Lassi",
- "description": "Refreshing yogurt-based drink blended with fresh mango pulp",
- "price": 40,
- "image": "img/9.avif",
- "category": "Beverages",
- "rating": 4.8,
- "spice": "Low",
- "dietary": [],
- "available": true
- },
- {
- "id": "dahi-bhalle-006",
- "name": "Dahi Bhalle",
- "description": "Soft lentil dumplings dunked in sweetened yogurt",
- "price": 45,
- "image": "img/1.avif",
- "category": "Chaat",
- "rating": 4.7,
- "spice": "Low",
- "dietary": [],
- "available": true
- },
- {
- "id": "chikhalwali-007",
- "name": "Chole Bhature",
- "description": "Fluffy fried bread served with spiced chickpea curry",
- "price": 50,
- "image": "img/2.avif",
- "category": "Snacks",
- "rating": 4.9,
- "spice": "High",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "aloo-tikki-008",
- "name": "Aloo Tikki",
- "description": "Crispy potato patty topped with chutneys and yogurt",
- "price": 25,
- "image": "img/7.avif",
- "category": "Snacks",
- "rating": 4.7,
- "spice": "Medium",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "gol-gappa-009",
- "name": "Gol Gappa",
- "description": "Hollow crispy balls filled with chickpeas and potatoes, served with tangy water",
- "price": 25,
- "image": "img/8.avif",
- "category": "Chaat",
- "rating": 4.8,
- "spice": "High",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "masala-chai-010",
- "name": "Masala Chai",
- "description": "Traditional Indian spiced tea with cardamom, ginger, and cloves",
- "price": 15,
- "image": "img/9.avif",
- "category": "Beverages",
- "rating": 4.6,
- "spice": "Medium",
- "dietary": [],
- "available": true
- },
- {
- "id": "pakora-011",
- "name": "Mixed Vegetable Pakora",
- "description": "Battered and deep-fried assorted vegetables with chickpea flour",
- "price": 30,
- "image": "img/1.avif",
- "category": "Snacks",
- "rating": 4.8,
- "spice": "Medium",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "dosa-012",
- "name": "Masala Dosa",
- "description": "Crispy fermented rice pancake filled with spiced potatoes, served with sambar and chutney",
- "price": 55,
- "image": "img/2.avif",
- "category": "Chaat",
- "rating": 4.9,
- "spice": "Medium",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "jalebi-013",
- "name": "Jalebi",
- "description": "Golden coiled sweet treat soaked in sugar syrup",
- "price": 20,
- "image": "img/7.avif",
- "category": "Snacks",
- "rating": 4.7,
- "spice": "Low",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "idli-014",
- "name": "Idli with Sambar",
- "description": "Fluffy steamed rice cakes served with spiced lentil vegetable stew",
- "price": 35,
- "image": "img/8.avif",
- "category": "Chaat",
- "rating": 4.8,
- "spice": "Medium",
- "dietary": ["vegan", "gluten-free"],
- "available": true
- },
- {
- "id": "aam-panna-015",
- "name": "Aam Panna",
- "description": "Tangy and sweet raw mango drink perfect for summer",
- "price": 30,
- "image": "img/9.avif",
- "category": "Beverages",
- "rating": 4.6,
- "spice": "Low",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "vada-pav-016",
- "name": "Vada Pav",
- "description": "Crispy potato fritter inside soft bread, Mumbai street food favorite",
- "price": 20,
- "image": "img/1.avif",
- "category": "Snacks",
- "rating": 4.9,
- "spice": "Medium",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "sev-tameta-017",
- "name": "Sev Tameta",
- "description": "Crispy noodles mixed with tomato curry and spices",
- "price": 28,
- "image": "img/2.avif",
- "category": "Chaat",
- "rating": 4.7,
- "spice": "High",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "sweet-lassi-018",
- "name": "Sweet Lassi",
- "description": "Creamy yogurt drink sweetened with sugar and spiced with cardamom",
- "price": 35,
- "image": "img/7.avif",
- "category": "Beverages",
- "rating": 4.8,
- "spice": "Low",
- "dietary": [],
- "available": true
- },
- {
- "id": "raj-kachori-019",
- "name": "Raj Kachori",
- "description": "Large crispy kachori stuffed with moong sprouts, pomegranate, and chutneys",
- "price": 60,
- "image": "img/8.avif",
- "category": "Chaat",
- "rating": 4.9,
- "spice": "Medium",
- "dietary": ["vegan"],
- "available": true
- },
- {
- "id": "shikanji-020",
- "name": "Shikanji (Lemonade)",
- "description": "Refreshing lemonade with mint and black salt",
- "price": 20,
- "image": "img/9.avif",
- "category": "Beverages",
- "rating": 4.7,
- "spice": "Low",
- "dietary": ["vegan", "gluten-free"],
- "available": true
- }
-]
+[
+ {
+ "id": 1,
+ "name": "Samosa",
+ "category": "Snacks",
+ "price": 15,
+ "spice": "Medium",
+ "image": "../img/samosa.jpg",
+
+ "rating": 4.5,
+ "recommendedCount": 32
+ },
+
+ {
+ "id": 2,
+ "name": "Kachori",
+ "category": "Snacks",
+ "price": 20,
+ "spice": "Medium",
+ "image": "../img/9.avif",
+
+ "rating": 4.2,
+ "recommendedCount": 18
+ },
+
+ {
+ "id": 3,
+ "name": "Pani Puri",
+ "category": "Chaat",
+ "price": 30,
+ "spice": "High",
+ "image": "../img/panipuri.jpg",
+
+ "rating": 4.8,
+ "recommendedCount": 67
+ },
+
+ {
+ "id": 4,
+ "name": "Sev Puri",
+ "category": "Chaat",
+ "price": 35,
+ "spice": "High",
+ "image": "../img/sevpuri.jpg",
+
+ "rating": 4.4,
+ "recommendedCount": 24
+ },
+
+ {
+ "id": 5,
+ "name": "Chai",
+ "category": "Beverages",
+ "price": 10,
+ "spice": "Low",
+ "image": "../img/chai.jpg",
+
+ "rating": 4.7,
+ "recommendedCount": 51
+ }
+]
diff --git a/index.html b/index.html
index 61d7174..f7a29b8 100644
--- a/index.html
+++ b/index.html
@@ -1,280 +1,166 @@
-
-
-
-
-
- ChaatBazar 🍴 | Authentic Indian Street Food Delivered Hot
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Authentic Indian Street Food Delivered Hot!
-
Delicious Samosa, Kachori, Pani Puri and more, right to your doorstep within 5 km.
-
Order Now
-
-
-
-
-
-
-
-
- Today's Specials
-
-
-
-
-
-
-
- Recently Viewed
-
-
-
-
-
-
-
-
-
-
-
-
-
About Us
-
At ChaatBazar , we bring you the authentic flavors of India's vibrant street food culture, freshly prepared and delivered hot right to your door. Our passion is to share the love of chaat, snacks, and beverages made from time-tested recipes.
-
-
-
-
-
-
-
-
-
Priya Sharma
-
Founder & Head Chef - Crafting every dish with love and authenticity.
-
-
-
-
About Priya
-
10+ years of culinary expertise specializing in authentic Indian street food and handcrafted signature recipes.
-
-
-
-
-
-
-
-
-
-
-
Anjali Verma
-
Customer Support - Always here to assist you with your orders.
-
-
-
-
About Anjali
-
Ensures smooth customer experience, quick assistance, and seamless order support from start to finish.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- What Our Customers Say
-
-
- The Samosas were crispy and flavorful, delivered piping hot! Amazing service and quality.
- - Meera K.
-
-
- Love the variety and authentic taste. The Pani Puri is the best I've had outside India!
- - Arjun P.
-
-
- Quick delivery and very friendly staff. The Kachoris are my go-to snack now. Highly recommended!
- - Sneha M.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ ChaatBazar 🍴 | Authentic Indian Street Food Delivered Hot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Authentic Indian Street Food Delivered Hot!
+
Delicious Samosa, Kachori, Pani Puri and more, right to your doorstep within 5 km.
+
Order Now
+
+
+
+
+
+
+
+
+ Today's Specials
+
+
+
+
+
+
+
+
+
+
+
+
About Us
+
At ChaatBazar , we bring you the authentic flavors of India's vibrant street food culture, freshly prepared and delivered hot right to your door. Our passion is to share the love of chaat, snacks, and beverages made from time-tested recipes.
+
+
+
+ Priya Sharma
+ Founder & Head Chef - Crafting every dish with love and authenticity.
+
+
+
+ Anjali Verma
+ Customer Support - Always here to assist you with your orders.
+
+
+
+
+
Contact Us
+
+ Name *
+
+
+
+ Email *
+
+
+
+ Message *
+
+
+
+
+ Your message has been sent. We'll get back to you soon.
+
+ Send Message
+
+
+
+
+
+
+ What Our Customers Say
+
+
+ The Samosas were crispy and flavorful, delivered piping hot! Amazing service and quality.
+ - Meera K.
+
+
+ Love the variety and authentic taste. The Pani Puri is the best I've had outside India!
+ - Arjun P.
+
+
+ Quick delivery and very friendly staff. The Kachoris are my go-to snack now. Highly recommended!
+ - Sneha M.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/js/main.js b/js/main.js
index 12d6de0..d8aff16 100644
--- a/js/main.js
+++ b/js/main.js
@@ -1,1087 +1,729 @@
-let menuItems = [];
-let currentCategory = "All";
-let orders = JSON.parse(localStorage.getItem('chaatOrders')) || [];
-
-// Initialize cart from cart manager (will be set after DOM loads)
-let cart = [];
-
-// Will be initialized in setupCartManager() after document loads
-function setupCartManager() {
- cart = cartManager.getItems();
-
- // Subscribe to cart changes to keep cart variable in sync
- cartManager.subscribe((items) => {
- cart = [...items];
- });
-
- // Validate cart integrity
- cartManager.validate();
-}
-async function loadMenuData() {
- try {
- const response = await fetch("data/menu.json");
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- menuItems = await response.json();
- } catch (error) {
- console.error("Failed to load menu data:", error);
- menuItems = [];
- }
-}
-
-// ===== Globals =====
-const specialsContainer = document.getElementById("specials-cards");
-const menuContainer = document.getElementById("menu-cards") || document.getElementById("menu-container");
-const cartCount = document.getElementById("cart-count");
-const cartSidebar = document.getElementById("cart-sidebar");
-const cartItemsContainer = document.getElementById("cart-items");
-const cartTotal = document.getElementById("cart-total") || document.getElementById("total-price");
-const checkoutBtn = document.getElementById("checkout-btn");
-
-// Cart is managed by CartManager - initialized in main startup
-
-function formatPrice(price) {
- return `₹${price}`;
-}
-
-// ===== Fuzzy Match & Highlighter Utilities =====
-
-function fuzzyMatch(target, query) {
- if (!target || !query) return false;
- const t = target.toLowerCase();
- const q = query.toLowerCase();
-
- // 1. Direct Substring Match
- if (t.includes(q)) return true;
-
- // 2. Fuzzy sequencing character lookup (character-by-character in order)
- let qIdx = 0;
- for (let i = 0; i < t.length; i++) {
- if (t[i] === q[qIdx]) {
- qIdx++;
- if (qIdx === q.length) return true;
- }
- }
- return false;
-}
-
-function highlightText(text, query) {
- if (!text) return "";
- if (!query) return text;
- const escapedQuery = query.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
- const regex = new RegExp(`(${escapedQuery})`, "gi");
- return text.replace(regex, "$1 ");
-}
-
-// ===== Render Functions =====
-
-function createCard(item, highlightQuery = "") {
- const card = document.createElement("article");
- card.className = "card";
- card.tabIndex = 0;
- card.setAttribute("aria-label", `${item.name} - ${item.description}. Price: ${formatPrice(item.price)}.`);
-
- const ratingStars = "⭐".repeat(Math.round(item.rating || 5));
- const dietaryTags = item.dietary ? item.dietary.map(d => `${d} `).join(" ") : "";
- const spiceIcon = item.spice === "High" ? "🌶️🌶️🌶️" : item.spice === "Medium" ? "🌶️🌶️" : "🌶️";
-
- const highlightedName = highlightText(item.name, highlightQuery);
- const highlightedDesc = highlightText(item.description, highlightQuery);
-
- //Check if item is available (default to true if field doesn't exist)
- const isAvailable = item.available !== undefined ? item.available : true;
-
- //Creates out of stock badge (ONLY if unavailable)
- const outOfStockBadge = !isAvailable ? 'Out of Stock ❌ ' : '';
-
- //Disables button and change color if out of stock
- const buttonDisabled = !isAvailable ? 'disabled' : '';
- const buttonColor = isAvailable ? '#28a745' : '#cccccc';
-
- card.innerHTML = `
-
-
-
- ${ratingStars} ${item.rating || '5.0'}
- ${spiceIcon}
-
-
${highlightedName}
-
${highlightedDesc}
-
${dietaryTags}
- ${outOfStockBadge}
-
-
- `;
-
- const addBtn = card.querySelector(".add-btn");
- //Only add event listener if item is available
- if (isAvailable) {
- addBtn.addEventListener("click", () => addToCart(item.id));
- } else {
- // Optional: Add click handler to show alert
- addBtn.addEventListener("click", () => {
- alert(`${item.name} is currently out of stock!`);
- });
- }
-
-
- card.addEventListener("click", () => {
- RecentlyViewed.addItem(item);
- renderRecentlyViewed();
- });
-
- return card;
-}
-
-function renderSpecials() {
- if (!specialsContainer) return;
- const specials = menuItems.slice(0, 3);
-
- showSkeletonCards(specialsContainer, specials.length);
-
- setTimeout(() => {
- specialsContainer.innerHTML = "";
- specials.forEach(item => {
- specialsContainer.appendChild(createCard(item));
- });
- }, 1500);
-}
-
-function renderMenu(filter = "All") {
- currentCategory = filter;
- applyAllFilters();
-}
-
-function renderRecentlyViewed() {
- const recentlyViewedContainer = document.getElementById("recently-viewed-cards");
- const recentlyViewedSection = document.getElementById("recently-viewed");
- if (!recentlyViewedContainer || !recentlyViewedSection) return;
-
- const recentItems = RecentlyViewed.getItems();
- recentlyViewedContainer.innerHTML = "";
-
- if (recentItems.length === 0) {
- recentlyViewedSection.style.display = "none";
- return;
- }
-
- recentlyViewedSection.style.display = "block";
- recentItems.forEach(item => {
- recentlyViewedContainer.appendChild(createCard(item));
- });
-}
-
-// ===== Unified Interactive Filter Engine =====
-
-function applyAllFilters() {
- if (!menuContainer) return;
-
- showSkeletonCards(menuContainer, 4);
-
- setTimeout(() => {
- menuContainer.innerHTML = "";
-
- const searchInput = document.getElementById("search-input");
- const query = searchInput ? searchInput.value.trim() : "";
-
- const priceSlider = document.getElementById("price-range-slider");
- const maxPrice = priceSlider ? parseFloat(priceSlider.value) : 100;
-
- const spiceSelect = document.getElementById("spice-level-select");
- const selectedSpice = spiceSelect ? spiceSelect.value : "All";
-
- const ratingSelect = document.getElementById("rating-select");
- const minRating = ratingSelect ? ratingSelect.value : "All";
-
- const veganCheck = document.getElementById("dietary-vegan");
- const gfCheck = document.getElementById("dietary-gf");
-
- let filtered = menuItems;
-
- if (currentCategory !== "All") {
- filtered = filtered.filter(item => item.category === currentCategory);
- }
-
- if (query) {
- filtered = filtered.filter(item =>
- fuzzyMatch(item.name, query) ||
- (item.description && fuzzyMatch(item.description, query)) ||
- (item.category && fuzzyMatch(item.category, query))
- );
- }
-
- filtered = filtered.filter(item => item.price <= maxPrice);
-
- if (selectedSpice !== "All") {
- filtered = filtered.filter(item => item.spice === selectedSpice);
- }
-
- if (minRating !== "All") {
- const ratingVal = parseFloat(minRating);
- filtered = filtered.filter(item => (item.rating || 5) >= ratingVal);
- }
-
- if (veganCheck && veganCheck.checked) {
- filtered = filtered.filter(item => item.dietary && item.dietary.includes("vegan"));
- }
- if (gfCheck && gfCheck.checked) {
- filtered = filtered.filter(item => item.dietary && item.dietary.includes("gluten-free"));
- }
-
- if (filtered.length === 0) {
- menuContainer.innerHTML = `
-
- No items found matching your filters.
-
`;
- return;
- }
-
- filtered.forEach(item => {
- menuContainer.appendChild(createCard(item, query));
- });
- }, 800);
-}
-
-function renderCart() {
- if (!cartItemsContainer) return;
-
- if (cart.length > 0) {
- showSkeletonCartItems(cart.length);
- }
-
- setTimeout(() => {
- cartItemsContainer.innerHTML = "";
-
- if (cart.length === 0) {
- cartItemsContainer.innerHTML =
- `
- Your cart is empty.
-
`;
- if (checkoutBtn) checkoutBtn.disabled = true;
- if (cartTotal) cartTotal.textContent = "Total: ₹0";
- return;
- }
-
- cart.forEach(({ item, quantity }) => {
- const cartItem = document.createElement("div");
- cartItem.className = "cart-item";
- cartItem.tabIndex = 0;
- cartItem.setAttribute(
- "aria-label",
- `${item.name}, quantity ${quantity},
- price ${formatPrice(item.price * quantity)}`
- );
-
- cartItem.innerHTML = `
-
-
-
${item.name}
-
${formatPrice(item.price)} each
-
- −
- ${quantity}
- +
-
-
-
-
- ${formatPrice(item.price * quantity)}
-
-
Remove
-
- `;
-
- const decreaseBtn = cartItem.querySelector(".qty-decrease");
- if (decreaseBtn) {
- decreaseBtn.addEventListener("click", () => removeFromCart(item.id));
- }
-
- const increaseBtn = cartItem.querySelector(".qty-increase");
- if (increaseBtn) {
- increaseBtn.addEventListener("click", () => addToCart(item.id));
- }
-
- const removeBtn = cartItem.querySelector(".cart-item-remove");
- if (removeBtn) {
- removeBtn.addEventListener("click", () => {
- cart = cart.filter(ci => ci.item.id !== item.id);
- updateCartCount();
- updateFavCount();
- renderCart();
- saveCart();
- });
- }
-
- cartItemsContainer.appendChild(cartItem);
- });
-
- const total = cart.reduce(
- (sum, ci) => sum + ci.item.price * ci.quantity,
- 0
- );
- if (cartTotal) cartTotal.textContent = `Total: ${formatPrice(total)}`;
- if (checkoutBtn) checkoutBtn.disabled = false;
-
- }, 600);
-}
-
-function updateCartCount() {
- if (cartCount) {
- const totalCount = cart.reduce((sum, cartItem) => sum + cartItem.quantity, 0);
- cartCount.textContent = totalCount;
- }
-}
-
-function updateFavCount() {
- const favCount = document.getElementById("fav-count");
- if (favCount) {
- const recentItems = RecentlyViewed.getItems();
- favCount.textContent = recentItems.length;
- }
-}
-
-function saveCart() {
- if (cartManager) {
- cartManager.saveToStorage();
- }
-}
-
-// ===== My Orders Dashboard & Real-Time Tracking Engine =====
-
-function updateOrderStatuses() {
- let changed = false;
- const now = Date.now();
-
- orders.forEach(order => {
- if (order.status === "Delivered") return;
-
- // Time elapsed in seconds since order checkout
- const elapsedSeconds = (now - order.timestamp) / 1000;
- let targetStatus = "Pending";
-
- if (elapsedSeconds >= 45) {
- targetStatus = "Delivered";
- } else if (elapsedSeconds >= 25) {
- targetStatus = "On the Way";
- } else if (elapsedSeconds >= 10) {
- targetStatus = "Preparing";
- }
-
- if (order.status !== targetStatus) {
- order.status = targetStatus;
- changed = true;
- }
- });
-
- if (changed) {
- localStorage.setItem('chaatOrders', JSON.stringify(orders));
- renderOrdersList();
- }
-}
-
-function renderOrdersList() {
- const container = document.getElementById("orders-container");
- if (!container) return;
-
- if (orders.length === 0) {
- container.innerHTML = `
-
-
No Orders Found
-
You haven't placed any orders yet. Explore our delicious street food menu!
-
Explore Menu
-
- `;
- return;
- }
-
- container.innerHTML = "";
-
- orders.forEach(order => {
- const card = document.createElement("article");
- card.className = "order-card";
-
- const isPreparing = order.status === "Preparing" || order.status === "On the Way" || order.status === "Delivered" ? "active" : "";
- const isOnWay = order.status === "On the Way" || order.status === "Delivered" ? "active" : "";
- const isDelivered = order.status === "Delivered" ? "active" : "";
-
- const statusClass = "status-" + order.status.toLowerCase().replace(/\s+/g, "-");
-
- let itemsHtml = "";
- order.items.forEach(ci => {
- itemsHtml += `
-
- ${ci.item.name} × ${ci.quantity}
- ${formatPrice(ci.item.price * ci.quantity)}
-
- `;
- });
-
- card.innerHTML = `
-
-
-
-
-
- ${itemsHtml}
-
-
-
- `;
-
- container.appendChild(card);
- });
-}
-
-// ===== Global Window Handlers for Multi-page support =====
-
-window.filterCategory = function(category) {
- currentCategory = category;
- applyAllFilters();
-
- const buttons = document.querySelectorAll(".filter-btn, .filter button");
- buttons.forEach(btn => {
- const filterAttr = btn.dataset.filter || (btn.getAttribute("onclick") ? btn.getAttribute("onclick").match(/'([^']+)'/)[1] : "");
- if (filterAttr === category || btn.textContent.trim() === category) {
- btn.classList.add("active");
- btn.setAttribute("aria-pressed", "true");
- } else {
- btn.classList.remove("active");
- btn.setAttribute("aria-pressed", "false");
- }
- });
-};
-
-window.checkout = async function() {
- if (cart.length === 0) {
- alert("Your cart is empty!");
- return;
- }
-
- const validationResult = await validateDeliveryLocation();
-
- if (!validationResult.valid) {
- alert(validationResult.error);
- return;
- }
-
- const newOrder = {
- id: "CB-" + Math.floor(100000 + Math.random() * 900000),
- date: new Date().toLocaleDateString(undefined, {
- month: 'short', day: 'numeric', year: 'numeric',
- hour: '2-digit', minute: '2-digit'
- }),
- timestamp: Date.now(),
- items: JSON.parse(JSON.stringify(cart)),
- total: cart.reduce((sum, ci) => sum + ci.item.price * ci.quantity, 0),
- status: "Pending",
- deliveryAddress: {
- latitude: validationResult.userLocation.latitude,
- longitude: validationResult.userLocation.longitude,
- source: validationResult.userLocation.source
- },
- deliveryDistance: validationResult.distance,
- restaurantLocation: validationResult.restaurantLocation
- };
-
- orders.unshift(newOrder);
- localStorage.setItem('chaatOrders', JSON.stringify(orders));
-
- cartManager.clear();
- updateCartCount();
- updateFavCount();
- renderCart();
-
- // Launch the animation simulation modal if available.
- if (typeof window.triggerDeliverySimulation === 'function') {
- window.triggerDeliverySimulation();
- } else {
- console.warn('Delivery tracker is not ready yet. Order has been placed.');
- }
-};
-
-window.reorderOrder = function(orderId) {
- const pastOrder = orders.find(o => o.id === orderId);
- if (!pastOrder) return;
-
- pastOrder.items.forEach(orderItem => {
- cartManager.addItem(orderItem.item, orderItem.quantity);
- });
-
- updateCartCount();
- updateFavCount();
- renderCart();
-
- alert("Items added back to your cart successfully!");
-
- const sidebar = document.getElementById("cart-sidebar");
- if (sidebar) {
- sidebar.setAttribute("aria-hidden", "false");
- sidebar.classList.add("open");
- }
-};
-
-// ===== Cart Operations =====
-
-// ===== Toast Notification =====
-
-function showToast(message) {
- const toast = document.getElementById("toast-notification");
-
- if (!toast) return;
-
- toast.textContent = message;
- toast.classList.add("show");
-
- clearTimeout(toast.hideTimeout);
-
- toast.hideTimeout = setTimeout(() => {
- toast.classList.remove("show");
- }, 2500);
-}
-
-function addToCart(id) {
- const item = menuItems.find(i => i.id === id);
- if (!item) return;
-
- //Check if item is available
- const isAvailable = item.available !== undefined ? item.available : true;
- if (!isAvailable) {
- alert(`${item.name} is currently out of stock!`);
- return;
- }
-
- cartManager.addItem(item, 1);
- updateCartCount();
- updateFavCount();
- renderCart();
- saveCart();
- showToast(`🛒 ${item.name} added to cart`);
- if (cartCount) {
- cartCount.classList.add("cart-bounce");
-
- setTimeout(() => {
- cartCount.classList.remove("cart-bounce");
- }, 400);
-}
-
- if (cartSidebar) {
- cartSidebar.setAttribute("aria-hidden", "false");
- cartSidebar.classList.add("open");
- }
-}
-
-function removeFromCart(id) {
- const cartIndex = cart.findIndex(ci => ci.item.id === id);
-
- if (cartIndex === -1) return;
-
- const removedItem = cart[cartIndex].item;
-
- if (cart[cartIndex].quantity > 1) {
- cart[cartIndex].quantity--;
- } else {
- cartManager.removeItem(id);
- }
-
- updateCartCount();
- updateFavCount();
- renderCart();
- saveCart();
-
- showToast(`🗑️ ${removedItem.name} removed from cart`);
-}
-// ===== Event Listeners =====
-
-function setupFilterButtons() {
- const filterButtons = document.querySelectorAll(".filter-btn");
- filterButtons.forEach(btn => {
- btn.addEventListener("click", () => {
- filterButtons.forEach(b => {
- b.classList.remove("active");
- b.setAttribute("aria-pressed", "false");
- });
- btn.classList.add("active");
- btn.setAttribute("aria-pressed", "true");
- renderMenu(btn.dataset.filter);
- });
- });
-}
-
-function setupCartToggle() {
- const cartOpenBtn = document.getElementById("cart-open-btn");
- const cartCloseBtn = document.getElementById("cart-close");
- if (!cartOpenBtn || !cartCloseBtn || !cartSidebar) return;
-
- cartOpenBtn.addEventListener("click", (e) => {
- e.preventDefault();
- cartSidebar.setAttribute("aria-hidden", "false");
- cartSidebar.classList.add("open");
- });
-
- cartCloseBtn.addEventListener("click", () => {
- cartSidebar.setAttribute("aria-hidden", "true");
- cartSidebar.classList.remove("open");
- });
-
- document.addEventListener("keydown", (e) => {
- if (e.key === "Escape" && cartSidebar.getAttribute("aria-hidden") === "false") {
- cartSidebar.setAttribute("aria-hidden", "true");
- cartSidebar.classList.remove("open");
- }
- });
-}
-
-function setupOrderNowScroll() {
- const orderNowBtn = document.getElementById("order-now-btn");
- const menuSection = document.getElementById("menu");
- if (!orderNowBtn || !menuSection) return;
-
- orderNowBtn.addEventListener("click", () => {
- menuSection.scrollIntoView({ behavior: "smooth" });
- });
-}
-
-// ===== Autocomplete & Search Panel =====
-
-function setupSearchSuggestions() {
- const searchInput = document.getElementById("search-input");
- const suggestionsContainer = document.getElementById("search-suggestions");
- if (!searchInput || !suggestionsContainer) return;
-
- function showSuggestions() {
- const query = searchInput.value.trim().toLowerCase();
- suggestionsContainer.innerHTML = "";
-
- if (!query) {
- suggestionsContainer.style.display = "none";
- return;
- }
-
- const matches = menuItems.filter(item =>
- item.name.toLowerCase().includes(query) ||
- (item.category && item.category.toLowerCase().includes(query))
- ).slice(0, 5);
-
- if (matches.length === 0) {
- const div = document.createElement("div");
- div.className = "suggestion-item no-matches";
- div.textContent = "No matches found";
- suggestionsContainer.appendChild(div);
- suggestionsContainer.style.display = "block";
- return;
- }
-
- matches.forEach(item => {
- const div = document.createElement("div");
- div.className = "suggestion-item";
- div.innerHTML = `
- ${highlightText(item.name, query)}
- ${item.category}
- `;
- div.addEventListener("click", () => {
- searchInput.value = item.name;
- suggestionsContainer.style.display = "none";
-
- const menuSection = document.getElementById("menu");
- if (menuSection) {
- menuSection.scrollIntoView({ behavior: "smooth" });
- }
-
- applyAllFilters();
- });
- suggestionsContainer.appendChild(div);
- });
-
- suggestionsContainer.style.display = "block";
- }
-
- searchInput.addEventListener("input", showSuggestions);
- searchInput.addEventListener("focus", showSuggestions);
-
- document.addEventListener("click", (e) => {
- if (!searchInput.contains(e.target) && !suggestionsContainer.contains(e.target)) {
- suggestionsContainer.style.display = "none";
- }
- });
-}
-
-function setupSearch() {
- const searchInput = document.getElementById("search-input");
- const searchBtn = document.getElementById("search-btn");
- if (!searchInput || !searchBtn) return;
-
- function handleSearchClick() {
- const menuSection = document.getElementById("menu");
- if (menuSection) {
- menuSection.scrollIntoView({ behavior: "smooth" });
- }
- applyAllFilters();
- }
-
- searchBtn.addEventListener("click", handleSearchClick);
- searchInput.addEventListener("keydown", (e) => {
- if (e.key === "Enter") {
- handleSearchClick();
- const suggestionsContainer = document.getElementById("search-suggestions");
- if (suggestionsContainer) suggestionsContainer.style.display = "none";
- }
- });
-}
-
-// ===== Advanced Expandable Filters Panel =====
-
-function setupAdvancedFilters() {
- const toggleBtn = document.getElementById("filter-toggle-btn");
- const filterPanel = document.getElementById("advanced-filters");
- if (!toggleBtn || !filterPanel) return;
-
- toggleBtn.addEventListener("click", () => {
- const isExpanded = toggleBtn.getAttribute("aria-expanded") === "true";
- toggleBtn.setAttribute("aria-expanded", !isExpanded);
- if (isExpanded) {
- filterPanel.style.display = "none";
- toggleBtn.classList.remove("active");
- } else {
- filterPanel.style.display = "block";
- toggleBtn.classList.add("active");
- }
- });
-
- const priceSlider = document.getElementById("price-range-slider");
- const priceSliderVal = document.getElementById("price-slider-val");
- if (priceSlider && priceSliderVal) {
- priceSlider.addEventListener("input", () => {
- priceSliderVal.textContent = `₹${priceSlider.value}`;
- priceSlider.setAttribute("aria-valuenow", priceSlider.value);
- applyAllFilters();
- });
- }
-
- const spiceSelect = document.getElementById("spice-level-select");
- if (spiceSelect) {
- spiceSelect.addEventListener("change", applyAllFilters);
- }
-
- const ratingSelect = document.getElementById("rating-select");
- if (ratingSelect) {
- ratingSelect.addEventListener("change", applyAllFilters);
- }
-
- const veganCheck = document.getElementById("dietary-vegan");
- if (veganCheck) {
- veganCheck.addEventListener("change", applyAllFilters);
- }
-
- const gfCheck = document.getElementById("dietary-gf");
- if (gfCheck) {
- gfCheck.addEventListener("change", applyAllFilters);
- }
-
- const resetBtn = document.getElementById("reset-filters-btn");
- if (resetBtn) {
- resetBtn.addEventListener("click", () => {
- if (priceSlider) {
- priceSlider.value = 100;
- priceSliderVal.textContent = "₹100";
- priceSlider.setAttribute("aria-valuenow", 100);
- }
- if (spiceSelect) spiceSelect.value = "All";
- if (ratingSelect) ratingSelect.value = "All";
- if (veganCheck) veganCheck.checked = false;
- if (gfCheck) gfCheck.checked = false;
-
- const searchInput = document.getElementById("search-input");
- if (searchInput) searchInput.value = "";
-
- currentCategory = "All";
-
- const buttons = document.querySelectorAll(".filter-btn, .filter button");
- buttons.forEach(btn => {
- const filterAttr = btn.dataset.filter || (btn.getAttribute("onclick") ? btn.getAttribute("onclick").match(/'([^']+)'/)[1] : "");
- if (filterAttr === "All" || btn.textContent.trim() === "All") {
- btn.classList.add("active");
- btn.setAttribute("aria-pressed", "true");
- } else {
- btn.classList.remove("active");
- btn.setAttribute("aria-pressed", "false");
- }
- });
-
- applyAllFilters();
- });
- }
-}
-
-// ===== Contact Form =====
-
-function setupContactForm() {
- const form = document.getElementById("contact-form");
- const formSuccess = document.getElementById("form-success");
- if (!form || !formSuccess) return;
-
- const nameInput = form.querySelector("#name");
- const emailInput = form.querySelector("#email");
- const messageInput = form.querySelector("#message");
-
- const errorName = form.querySelector("#error-name");
- const errorEmail = form.querySelector("#error-email");
- const errorMessage = form.querySelector("#error-message");
-
- form.addEventListener("submit", (e) => {
- e.preventDefault();
-
- errorName.textContent = "";
- errorEmail.textContent = "";
- errorMessage.textContent = "";
- formSuccess.style.display = "none";
-
- const validation = validateAndSanitizeContactForm(nameInput.value, emailInput.value, messageInput.value);
-
- if (!validation.valid) {
- if (validation.errors.name) errorName.textContent = validation.errors.name;
- if (validation.errors.email) errorEmail.textContent = validation.errors.email;
- if (validation.errors.message) errorMessage.textContent = validation.errors.message;
- return;
- }
-
- formSuccess.style.display = "block";
- setTimeout(() => {
- form.reset();
- formSuccess.style.display = "none";
- }, 3000);
- });
-}
-
-function setupNewsletterForm() {
- const newsletterForm = document.getElementById("newsletter-form");
- if (!newsletterForm) return;
- const emailInput = newsletterForm.querySelector("#newsletter-email");
-
- newsletterForm.addEventListener("submit", (e) => {
- e.preventDefault();
-
- const validation = validateAndSanitizeEmail(emailInput.value);
-
- if (!validation.valid) {
- alert(validation.error);
- return;
- }
-
- alert("Thank you for subscribing!");
- newsletterForm.reset();
- });
-}
-
-function setupActiveNavbar() {
- const navLinks = document.querySelectorAll(".nav-link");
- const sections = document.querySelectorAll("section");
-
- // Click active state
- navLinks.forEach(link => {
- link.addEventListener("click", () => {
- navLinks.forEach(nav => nav.classList.remove("active"));
- link.classList.add("active");
- });
- });
-
- // Scroll active state
- window.addEventListener("scroll", () => {
- let current = "";
-
- sections.forEach((section) => {
- const sectionTop = section.offsetTop - 120;
- const sectionHeight = section.clientHeight;
-
- if (
- window.scrollY >= sectionTop &&
- window.scrollY < sectionTop + sectionHeight
- ) {
- current = section.getAttribute("id");
- }
- });
-
- navLinks.forEach((link) => {
- link.classList.remove("active");
-
- const href = link.getAttribute("href");
-
- if (
- href === `#${current}` ||
- (current === "specials" && href === "#menu")
- ) {
- link.classList.add("active");
- }
- });
- });
-}
-
-// ===== Initialization =====
-
-async function init() {
- // Initialize cart manager first to sync cart state
- setupCartManager();
-
- // Bind interactive UI listeners immediately for instant input responsiveness (high INP)
- setupCartToggle();
- setupFilterButtons();
- setupOrderNowScroll();
- setupSearchSuggestions();
- setupSearch();
- setupAdvancedFilters();
- setupContactForm();
- setupNewsletterForm();
- setupActiveNavbar();
-
- if (checkoutBtn) {
- checkoutBtn.addEventListener("click", () => {
- window.checkout();
- });
- }
-
- // Load database items asynchronously without blocking UI interactions
- await loadMenuData();
-
- renderSpecials();
- renderRecentlyViewed();
- applyAllFilters();
- updateCartCount();
- updateFavCount();
- renderCart();
-
- // Run dynamic order rendering and simulated status progress updates
- renderOrdersList();
- updateOrderStatuses();
- setInterval(updateOrderStatuses, 3000); // Check status progress every 3s
-}
-
-// Prevent race condition if DOMContentLoaded has already fired on direct reload
-if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", init);
-} else {
- init();
-}
-
-// ===== Skeleton UI Helpers =====
-
-function createSkeletonCard() {
- const el = document.createElement("div");
- el.className = "skeleton-card";
- el.setAttribute("aria-hidden", "true");
-
- el.innerHTML = `
-
-
-
-
-
-
- `;
-
- return el;
-}
-
-function showSkeletonCards(container, count = 3) {
- if (!container) return;
- container.innerHTML = "";
-
- for (let i = 0; i < count; i++) {
- container.appendChild(createSkeletonCard());
- }
-}
-
-function createSkeletonCartItem() {
- const el = document.createElement("div");
- el.className = "skeleton-cart-item";
- el.setAttribute("aria-hidden", "true");
-
- el.innerHTML = `
-
-
-
-
-
-
- `;
-
- return el;
-}
-
-function showSkeletonCartItems(count = 2) {
- if (!cartItemsContainer) return;
- cartItemsContainer.innerHTML = "";
-
- for (let i = 0; i < count; i++) {
- cartItemsContainer.appendChild(createSkeletonCartItem());
- }
-}
-// dark-mode
-const toggleBtn = document.getElementById("theme-toggle");
-
-// Load saved theme on page load
-document.addEventListener("DOMContentLoaded", () => {
- const savedTheme = localStorage.getItem("theme");
-
- if (savedTheme === "dark") {
- document.body.classList.add("dark");
- if (toggleBtn) toggleBtn.textContent = "☀️";
- } else {
- if (toggleBtn) toggleBtn.textContent = "🌙";
- }
-});
-
-// Toggle dark/light mode
-if (toggleBtn) {
- toggleBtn.addEventListener("click", () => {
- document.body.classList.toggle("dark");
-
- if (document.body.classList.contains("dark")) {
- toggleBtn.textContent = "☀️";
- localStorage.setItem("theme", "dark");
- } else {
- toggleBtn.textContent = "🌙";
- localStorage.setItem("theme", "light");
- }
- });
+const menuItems = [
+ {
+ id: 1,
+ name: "Samosa",
+ category: "Snacks",
+ price: 30,
+ description: "Crispy golden triangle stuffed with spiced potatoes.",
+ image: "img/8.avif",
+
+ rating: 4.5,
+ recommendedCount: 32
+ },
+
+ {
+ id: 2,
+ name: "Pani Puri",
+ category: "Chaat",
+ price: 50,
+ description: "Hollow crispy puris filled with spicy, tangy water and chickpeas.",
+ image: "img/2.avif",
+
+ rating: 4.8,
+ recommendedCount: 67
+ },
+
+ {
+ id: 3,
+ name: "Masala Chai",
+ category: "Beverages",
+ price: 20,
+ description: "Aromatic tea brewed with spices and milk.",
+ image: "img/7.avif",
+
+ rating: 4.6,
+ recommendedCount: 41
+ },
+
+ {
+ id: 4,
+ name: "Kachori",
+ category: "Snacks",
+ price: 35,
+ description: "Deep-fried pastry filled with spicy lentils.",
+ image: "img/9.avif",
+
+ rating: 4.3,
+ recommendedCount: 24
+ },
+
+ {
+ id: 5,
+ name: "Bhel Puri",
+ category: "Chaat",
+ price: 45,
+ description: "Crunchy puffed rice mixed with tangy tamarind chutney.",
+ image: "img/1.avif",
+
+ rating: 4.7,
+ recommendedCount: 58
+ },
+];
+
+// ===== Globals =====
+const specialsContainer = document.getElementById("specials-cards");
+const menuContainer = document.getElementById("menu-cards");
+const cartCount = document.getElementById("cart-count");
+const cartSidebar = document.getElementById("cart-sidebar");
+const cartItemsContainer = document.getElementById("cart-items");
+const cartTotal = document.getElementById("cart-total");
+const checkoutBtn = document.getElementById("checkout-btn");
+
+let cart = JSON.parse(localStorage.getItem('chaatCart')) || [];
+let ratingsData =
+ JSON.parse(localStorage.getItem("chaatRatings")) || {};
+let firstTryData =
+ JSON.parse(
+ localStorage.getItem("firstTryRecommendations")
+ ) || {};
+
+function saveCart() {
+ localStorage.setItem('chaatCart', JSON.stringify(cart));
+}
+
+
+function formatPrice(price) {
+ return `₹${price}`;
+}
+
+function saveRatings() {
+ localStorage.setItem(
+ "chaatRatings",
+ JSON.stringify(ratingsData)
+ );
+}
+function saveFirstTryData() {
+
+ localStorage.setItem(
+ "firstTryRecommendations",
+ JSON.stringify(firstTryData)
+ );
+
+}
+
+function getItemRatings(itemId, defaultRating) {
+
+ const ratings = ratingsData[itemId];
+
+ if (!ratings || ratings.length === 0) {
+ return {
+ average: defaultRating,
+ count: 0
+ };
+ }
+
+ const total =
+ ratings.reduce((sum, r) => sum + r, 0);
+
+ return {
+ average: (total / ratings.length).toFixed(1),
+ count: ratings.length
+ };
+}
+
+// ===== Render Functions =====
+
+function createCard(item) {
+ const card = document.createElement("article");
+ card.className = "card";
+ card.tabIndex = 0;
+ card.setAttribute("aria-label", `${item.name} - ${item.description}. Price: ${formatPrice(item.price)}.`);
+
+ const ratingData =
+ getItemRatings(item.id, item.rating);
+
+ const isTrending =
+ ratingData.average >= 4.7 &&
+ ratingData.count >= 3;
+
+ card.innerHTML = `
+
+
+
${item.name}
+
${item.description}
+
+
+
+
+ `;
+
+ const addBtn = card.querySelector(".add-btn");
+ addBtn.addEventListener("click", () => addToCart(item.id));
+ const stars = card.querySelectorAll(".star");
+
+stars.forEach(star => {
+
+ star.addEventListener("click", () => {
+
+ const value =
+ Number(star.dataset.rating);
+
+ const alreadyRated =
+ localStorage.getItem(
+ `rated-${item.id}`
+ );
+
+ if (alreadyRated) {
+ showToast("You already rated this item!");
+ return;
+ }
+
+ if (!ratingsData[item.id]) {
+ ratingsData[item.id] = [];
+ }
+
+ ratingsData[item.id].push(value);
+
+ saveRatings();
+
+ localStorage.setItem(
+ `rated-${item.id}`,
+ true
+ );
+
+ renderMenu();
+
+ });
+
+ });
+
+ const firstTryBtn =
+ card.querySelector(".first-try-btn");
+
+ firstTryBtn.addEventListener(
+ "click",
+ () => {
+
+ const alreadyRecommended =
+ localStorage.getItem(
+ `recommended-${item.id}`
+ );
+
+ if (alreadyRecommended) {
+
+ showToast(
+ "You already recommended this item!"
+ );
+
+ return;
+ }
+
+ firstTryData[item.id] =
+ (firstTryData[item.id] || 0) + 1;
+
+ saveFirstTryData();
+
+ localStorage.setItem(
+ `recommended-${item.id}`,
+ true
+ );
+
+ renderMenu();
+
+ }
+ );
+
+ return card;
+}
+
+function renderSpecials() {
+ const specials = menuItems.slice(0, 3);
+
+ // 1. Show skeletons immediately
+ showSkeletonCards(specialsContainer, specials.length);
+
+ // 2. Simulate async load (e.g., a real fetch would replace this timeout)
+ setTimeout(() => {
+ specialsContainer.innerHTML = "";
+ specials.forEach(item => {
+ specialsContainer.appendChild(createCard(item));
+ });
+ }, 1500); // remove/reduce when using a real API
+}
+
+function renderMenu(filter = "All") {
+ // 1. Show skeletons immediately
+ showSkeletonCards(menuContainer, 4);
+
+ // 2. Apply filter then render real cards after delay
+ setTimeout(() => {
+ menuContainer.innerHTML = "";
+
+ const filteredItems =
+ filter === "All"
+ ? menuItems
+ : menuItems.filter(item => item.category === filter);
+
+ if (filteredItems.length === 0) {
+ menuContainer.innerHTML =
+ `
+ No items found for "${filter} ".
+
`;
+ return;
+ }
+
+ filteredItems.forEach(item => {
+ menuContainer.appendChild(createCard(item));
+ });
+ }, 1200); // remove/reduce when using a real API
+}
+
+function renderCart() {
+ // 1. Show skeletons briefly when cart first opens
+ if (cart.length > 0) {
+ showSkeletonCartItems(cart.length);
+ }
+
+ setTimeout(() => {
+ cartItemsContainer.innerHTML = "";
+
+ if (cart.length === 0) {
+ cartItemsContainer.innerHTML =
+ `
+ Your cart is empty.
+
`;
+ checkoutBtn.disabled = true;
+ cartTotal.textContent = "Total: ₹0";
+ return;
+ }
+
+ cart.forEach(({ item, quantity }) => {
+ const cartItem = document.createElement("div");
+ cartItem.className = "cart-item";
+ cartItem.tabIndex = 0;
+ cartItem.setAttribute(
+ "aria-label",
+ `${item.name}, quantity ${quantity},
+ price ${formatPrice(item.price * quantity)}`
+ );
+
+ cartItem.innerHTML = `
+
+
+
${item.name}
+
${formatPrice(item.price)} each
+
+ −
+ ${quantity}
+ +
+
+
+
+
+ ${formatPrice(item.price * quantity)}
+
+
Remove
+
+ `;
+
+ // Decrease quantity
+ cartItem
+ .querySelector(".qty-decrease")
+ .addEventListener("click", () => removeFromCart(item.id));
+
+ // Increase quantity
+ cartItem
+ .querySelector(".qty-increase")
+ .addEventListener("click", () => addToCart(item.id));
+
+ // Remove entirely
+ cartItem
+ .querySelector(".cart-item-remove")
+ .addEventListener("click", () => {
+ cart = cart.filter(ci => ci.item.id !== item.id);
+ updateCartCount();
+ renderCart();
+ });
+
+ cartItemsContainer.appendChild(cartItem);
+ });
+
+ const total = cart.reduce(
+ (sum, ci) => sum + ci.item.price * ci.quantity,
+ 0
+ );
+ cartTotal.textContent = `Total: ${formatPrice(total)}`;
+ checkoutBtn.disabled = false;
+
+ }, 600); // short delay — cart data is already local
+}
+
+function updateCartCount() {
+ const totalCount = cart.reduce((sum, cartItem) => sum + cartItem.quantity, 0);
+ cartCount.textContent = totalCount;
+}
+
+// ===== Cart Operations =====
+
+function addToCart(id) {
+ const item = menuItems.find(i => i.id === id);
+ if (!item) return;
+
+ const cartItem = cart.find(ci => ci.item.id === id);
+ if (cartItem) {
+ cartItem.quantity++;
+ } else {
+ cart.push({ item, quantity: 1 });
+ }
+ updateCartCount();
+ renderCart();
+ saveCart();
+}
+
+function removeFromCart(id) {
+ const cartIndex = cart.findIndex(ci => ci.item.id === id);
+ if (cartIndex === -1) return;
+
+ if (cart[cartIndex].quantity > 1) {
+ cart[cartIndex].quantity--;
+ } else {
+ cart.splice(cartIndex, 1);
+ }
+ updateCartCount();
+ renderCart();
+ saveCart();
+}
+
+// ===== Event Listeners =====
+
+function setupFilterButtons() {
+ const filterButtons = document.querySelectorAll(".filter-btn");
+ filterButtons.forEach(btn => {
+ btn.addEventListener("click", () => {
+ filterButtons.forEach(b => {
+ b.classList.remove("active");
+ b.setAttribute("aria-pressed", "false");
+ });
+ btn.classList.add("active");
+ btn.setAttribute("aria-pressed", "true");
+ renderMenu(btn.dataset.filter);
+ });
+ });
+}
+
+function setupCartToggle() {
+ const cartOpenBtn = document.getElementById("cart-open-btn");
+ const cartCloseBtn = document.getElementById("cart-close");
+
+ cartOpenBtn.addEventListener("click", (e) => {
+ e.preventDefault();
+ cartSidebar.setAttribute("aria-hidden", "false");
+ cartSidebar.style.transform = "translateX(0)";
+ });
+
+ cartCloseBtn.addEventListener("click", () => {
+ cartSidebar.setAttribute("aria-hidden", "true");
+ cartSidebar.style.transform = "translateX(100%)";
+ });
+
+ // Close cart on Escape key when open
+ document.addEventListener("keydown", (e) => {
+ if (e.key === "Escape" && cartSidebar.getAttribute("aria-hidden") === "false") {
+ cartSidebar.setAttribute("aria-hidden", "true");
+ cartSidebar.style.transform = "translateX(100%)";
+ }
+ });
+}
+
+function setupOrderNowScroll() {
+ const orderNowBtn = document.getElementById("order-now-btn");
+ const menuSection = document.getElementById("menu");
+
+ orderNowBtn.addEventListener("click", () => {
+ menuSection.scrollIntoView({ behavior: "smooth" });
+ });
+}
+
+//Functional Search bar
+function setupSearch() {
+
+ const searchInput = document.getElementById("search-input");
+ const searchBtn = document.getElementById("search-btn");
+
+ if (!searchInput || !searchBtn || !menuContainer) {
+ return;
+ }
+
+ function searchMenu() {
+
+ const query = searchInput.value.trim().toLowerCase();
+ document.getElementById("menu").scrollIntoView({
+ behavior: "smooth"
+});
+
+ if (query === "") {
+ renderMenu("All");
+ return;
+ }
+
+ // Filter matching items
+ const filtered = menuItems.filter(item =>
+ item.name.toLowerCase().includes(query) ||
+ item.description.toLowerCase().includes(query) ||
+ item.category.toLowerCase().includes(query)
+ );
+
+ menuContainer.innerHTML = "";
+
+ // Show matching items
+ if (filtered.length > 0) {
+
+ filtered.forEach(item => {
+ menuContainer.appendChild(createCard(item));
+ });
+
+ } else {
+
+ menuContainer.innerHTML = `
+
+ No items found
+
+ `;
+
+ }
+ }
+
+ searchInput.addEventListener("keyup", searchMenu);
+ searchBtn.addEventListener("click", searchMenu);
+
+ searchInput.addEventListener("keydown", (e) => {
+ if (e.key === "Enter") {
+ searchMenu();
+ }
+ });
+
+}
+
+function setupContactForm() {
+ const form = document.getElementById("contact-form");
+ const formSuccess = document.getElementById("form-success");
+
+ const nameInput = form.querySelector("#name");
+ const emailInput = form.querySelector("#email");
+ const messageInput = form.querySelector("#message");
+
+ const errorName = form.querySelector("#error-name");
+ const errorEmail = form.querySelector("#error-email");
+ const errorMessage = form.querySelector("#error-message");
+
+ form.addEventListener("submit", (e) => {
+ e.preventDefault();
+
+ // Clear previous errors and hide any success banner
+ errorName.textContent = "";
+ errorEmail.textContent = "";
+ errorMessage.textContent = "";
+ formSuccess.style.display = "none";
+
+ const nameVal = nameInput.value.trim();
+ const emailVal = emailInput.value.trim();
+ const messageVal = messageInput.value.trim();
+
+ let valid = true;
+
+ // Validate Name — empty check first, then length
+ if (nameVal === "") {
+ errorName.textContent = "Name is required.";
+ valid = false;
+ } else if (nameVal.length < 2) {
+ errorName.textContent = "Name must be at least 2 characters.";
+ valid = false;
+ }
+
+ // Validate Email — empty check first, then format
+ if (emailVal === "") {
+ errorEmail.textContent = "Email is required.";
+ valid = false;
+ } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailVal)) {
+ errorEmail.textContent = "Please enter a valid email address.";
+ valid = false;
+ }
+
+ // Validate Message — empty check first, then length
+ if (messageVal === "") {
+ errorMessage.textContent = "Message is required.";
+ valid = false;
+ } else if (messageVal.length < 10) {
+ errorMessage.textContent = "Message must be at least 10 characters.";
+ valid = false;
+ }
+
+ if (!valid) return;
+
+ // Show inline success banner and reset form after 3 s
+ formSuccess.style.display = "block";
+ setTimeout(() => {
+ form.reset();
+ formSuccess.style.display = "none";
+ }, 3000);
+ });
+}
+
+function setupNewsletterForm() {
+ const newsletterForm = document.getElementById("newsletter-form");
+ const emailInput = newsletterForm.querySelector("#newsletter-email");
+
+ newsletterForm.addEventListener("submit", (e) => {
+ e.preventDefault();
+
+ const emailVal = emailInput.value.trim();
+ if (!emailVal || !/\S+@\S+\.\S+/.test(emailVal)) {
+ alert("Please enter a valid email address.");
+ return;
+ }
+
+ alert("Thank you for subscribing!");
+
+ newsletterForm.reset();
+ });
+}
+
+// ===== Initialization =====
+function showToast(message) {
+
+ const toast =
+ document.createElement("div");
+
+ toast.className = "toast-message";
+
+ toast.textContent = message;
+
+ document.body.appendChild(toast);
+
+ setTimeout(() => {
+ toast.classList.add("show");
+ }, 100);
+
+ setTimeout(() => {
+ toast.remove();
+ }, 3000);
+ }
+
+function init() {
+ renderSpecials();
+ renderMenu("All");
+ updateCartCount();
+ renderCart();
+
+ setupFilterButtons();
+ setupCartToggle();
+ setupOrderNowScroll();
+ setupSearch();
+ setupContactForm();
+ setupNewsletterForm();
+}
+
+document.addEventListener("DOMContentLoaded", init);
+
+// ===== Skeleton UI Helpers =====
+
+/**
+ * Creates one skeleton card element that matches .card dimensions.
+ */
+function createSkeletonCard() {
+ const el = document.createElement("div");
+ el.className = "skeleton-card";
+ el.setAttribute("aria-hidden", "true");
+
+ el.innerHTML = `
+
+
+
+
+
+
+ `;
+
+ return el;
+}
+
+/**
+ * Injects `count` skeleton cards into a container.
+ * @param {HTMLElement} container
+ * @param {number} count
+ */
+function showSkeletonCards(container, count = 3) {
+ container.innerHTML = "";
+
+ for (let i = 0; i < count; i++) {
+ container.appendChild(createSkeletonCard());
+ }
+}
+
+/**
+ * Creates one skeleton cart-item element that matches .cart-item dimensions.
+ */
+function createSkeletonCartItem() {
+ const el = document.createElement("div");
+ el.className = "skeleton-cart-item";
+ el.setAttribute("aria-hidden", "true");
+
+ el.innerHTML = `
+
+
+
+
+
+
+ `;
+
+ return el;
+}
+
+/**
+ * Injects `count` skeleton cart items into the cart panel.
+ * @param {number} count
+ */
+function showSkeletonCartItems(count = 2) {
+ cartItemsContainer.innerHTML = "";
+
+ for (let i = 0; i < count; i++) {
+ cartItemsContainer.appendChild(createSkeletonCartItem());
+ }
}
\ No newline at end of file