inner pages updated

This commit is contained in:
Selvi 2026-06-10 19:11:59 +05:30
parent 33a513f40e
commit dcb91cc507
63 changed files with 6070 additions and 183 deletions

53
package-lock.json generated
View File

@ -10,7 +10,9 @@
"dependencies": { "dependencies": {
"next": "16.2.7", "next": "16.2.7",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4" "react-dom": "19.2.4",
"react-google-recaptcha": "^3.1.0",
"react-icons": "^5.6.0"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^9", "eslint": "^9",
@ -3628,6 +3630,15 @@
"hermes-estree": "0.25.1" "hermes-estree": "0.25.1"
} }
}, },
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"license": "BSD-3-Clause",
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@ -4131,7 +4142,6 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/js-yaml": { "node_modules/js-yaml": {
@ -4291,7 +4301,6 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0" "js-tokens": "^3.0.0 || ^4.0.0"
@ -4501,7 +4510,6 @@
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -4799,7 +4807,6 @@
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
@ -4847,6 +4854,19 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-async-script": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz",
"integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==",
"license": "MIT",
"dependencies": {
"hoist-non-react-statics": "^3.3.0",
"prop-types": "^15.5.0"
},
"peerDependencies": {
"react": ">=16.4.1"
}
},
"node_modules/react-dom": { "node_modules/react-dom": {
"version": "19.2.4", "version": "19.2.4",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
@ -4859,11 +4879,32 @@
"react": "^19.2.4" "react": "^19.2.4"
} }
}, },
"node_modules/react-google-recaptcha": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz",
"integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==",
"license": "MIT",
"dependencies": {
"prop-types": "^15.5.0",
"react-async-script": "^1.2.0"
},
"peerDependencies": {
"react": ">=16.4.1"
}
},
"node_modules/react-icons": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz",
"integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==",
"license": "MIT",
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-is": { "node_modules/react-is": {
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/reflect.getprototypeof": { "node_modules/reflect.getprototypeof": {

View File

@ -11,7 +11,9 @@
"dependencies": { "dependencies": {
"next": "16.2.7", "next": "16.2.7",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4" "react-dom": "19.2.4",
"react-google-recaptcha": "^3.1.0",
"react-icons": "^5.6.0"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^9", "eslint": "^9",

38
src/app/about/page.js Normal file
View File

@ -0,0 +1,38 @@
import Navbar from '@/components/Navbar';
import Footer from '@/components/Footer';
import ScrollToTop from '@/components/ScrollToTop';
import HeroBanner from '@/components/HeroBanner';
import AboutStory from '@/components/about/AboutStory';
import AboutCards from '@/components/about/AboutCards';
import WhyChooseUs from '@/components/about/WhyChooseUs';
import AboutTestimonial from '@/components/about/AboutTestimonial';
import AboutFAQ from '@/components/about/AboutFAQ';
import AboutCTA from '@/components/about/AboutCTA';
export const metadata = {
title: 'About Us | My Dosa Place Restaurant',
description: 'Learn about our story, passion, and commitment to authentic South Indian cuisine in Waterloo.',
};
export default function AboutPage() {
return (
<>
<Navbar />
<main>
<HeroBanner
title="About Us"
bgImage="/images/story-heritage.png"
/>
<AboutStory />
<AboutCards />
<WhyChooseUs />
<AboutTestimonial />
<AboutFAQ />
<AboutCTA />
</main>
<Footer />
<ScrollToTop />
</>
);
}

View File

@ -0,0 +1,32 @@
export async function GET() {
const apiKey = process.env.SERPAPI_KEY || "6f49f63fbe238dc99a431723418b46861c1ad9fd2662cefd9f747b2d16196d3a";
const placeId = process.env.SERPAPI_PLACE_ID || "ChIJ9b7ftlX3K4gRb7-4SkJNGCE";
try {
const url = `https://serpapi.com/search.json?engine=google_maps_reviews&hl=en&api_key=${apiKey}&place_id=${placeId}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`SerpAPI request failed with status ${response.status}`);
}
const data = await response.json();
console.log('SerpAPI reviews response', data);
if (data.error) {
throw new Error(data.error.message || data.error);
}
const reviews = Array.isArray(data.reviews) ? data.reviews : [];
return new Response(JSON.stringify({ reviews, total: reviews.length }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
} catch (error) {
console.error('Error fetching reviews:', error);
return new Response(JSON.stringify({ reviews: [], error: error.message || "Failed to fetch reviews" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
}

254
src/app/contact/contact.css Normal file
View File

@ -0,0 +1,254 @@
.container {
width: 90%;
max-width: 1200px;
margin: auto;
}
.contact-section {
padding: 80px 0;
background: #ffffff;
}
.contact-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 80px;
align-items: start;
min-height: 600px;
}
/* Left Column */
.contact-left {
display: flex;
flex-direction: column;
gap: 30px;
height: 100%;
}
.contact-cards {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 24px;
}
.contact-card {
background-color: #faf5f0;
/* Soft beige to match image */
border-radius: 16px;
padding: 30px 20px;
text-align: center;
transition: transform 0.2s ease;
display: flex;
flex-direction: column;
justify-content: center;
}
.contact-card:hover {
transform: translateY(-4px);
}
.card-icon {
font-size: 32px;
display: block;
margin-bottom: 12px;
color: var(--color-secondary, #d99c43);
transition: color 0.3s ease;
}
.contact-card:hover .card-icon {
color: var(--color-primary, #1b4332);
}
.contact-card h4 {
font-family: var(--font-inter), sans-serif;
font-size: 1.05rem;
font-weight: 700;
color: var(--color-primary, #1b4332);
margin: 0 0 8px;
}
.contact-card p {
font-family: var(--font-inter), sans-serif;
font-size: 0.9rem;
color: #6b7280;
margin: 0;
line-height: 1.4;
word-break: break-word;
}
.contact-map {
width: 100%;
border-radius: 12px;
overflow: hidden;
border: 1px solid #eaeaea;
flex: 1;
min-height: 320px;
}
.contact-map iframe {
width: 100%;
height: 100%;
}
/* Right Column */
.contact-right {
display: flex;
flex-direction: column;
height: 100%;
}
.contact-right h2 {
font-family: var(--font-playfair), Georgia, serif;
font-size: 2.5rem;
font-weight: 800;
color: var(--color-primary, #1b4332);
margin: 0 0 16px;
}
.contact-right h2 em {
font-style: italic;
color: #d4a017;
}
.contact-eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-inter), sans-serif;
font-size: 0.95rem;
font-weight: 700;
color: #d4a017;
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 12px;
}
.contact-subtitle {
font-family: var(--font-inter), sans-serif;
font-size: 1rem;
color: #6b7280;
line-height: 1.6;
margin: 0 0 40px;
}
.contact-form {
display: flex;
flex-direction: column;
gap: 20px;
}
.captcha-wrapper {
margin-top: 1rem;
}
.alert {
padding: 14px 18px;
border-radius: 12px;
font-family: var(--font-inter), sans-serif;
font-size: 0.95rem;
margin-bottom: 6px;
}
.alert-success {
background: #ecfdf5;
color: #166534;
border: 1px solid #d1fae5;
}
.alert-danger {
background: #fef2f2;
color: #991b1b;
border: 1px solid #fecaca;
}
.form-group {
display: flex;
flex-direction: column;
}
.form-group label {
font-family: var(--font-inter), sans-serif;
font-size: 0.9rem;
font-weight: 700;
color: var(--color-primary, #1b4332);
margin-bottom: 8px;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 16px 20px;
border: 1.5px solid #f0f0f0;
border-radius: 12px;
font-family: var(--font-inter), sans-serif;
font-size: 0.95rem;
color: #333;
outline: none;
transition: border-color 0.2s;
background-color: #ffffff;
}
.form-group input::placeholder,
.form-group textarea::placeholder {
color: #cbd5e1;
}
.form-group input:focus,
.form-group textarea:focus {
border-color: var(--color-secondary, #d99c43);
}
.submit-btn {
background-color: var(--color-secondary, #d99c43);
color: #ffffff;
border: none;
padding: 18px;
border-radius: 50px;
/* Pill shape like image */
font-family: var(--font-inter), sans-serif;
font-size: 1rem;
font-weight: 700;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
margin-top: 10px;
}
.submit-btn:hover {
background-color: var(--color-secondary-hover, #b88231);
transform: translateY(-2px);
}
.card-icon {
font-size: 32px;
display: block;
margin: 0 auto 12px;
color: var(--color-secondary, #d99c43);
transition: color 0.3s ease;
}
.contact-card:hover .card-icon {
color: var(--color-primary, #1b4332);
}
@media (max-width: 900px) {
.contact-wrapper {
grid-template-columns: 1fr;
gap: 60px;
}
.contact-cards {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 500px) {
.contact-cards {
grid-template-columns: 1fr;
}
}
@media (max-width: 375px) {
.contact-left,
.contact-right {
width: 85%;
}
}

80
src/app/contact/page.js Normal file
View File

@ -0,0 +1,80 @@
import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer";
import ScrollToTop from "@/components/ScrollToTop";
import HeroBanner from "@/components/HeroBanner";
import ContactForm from "@/components/ContactForm";
import { FaPhone, FaWhatsapp, FaEnvelope, FaMapMarkerAlt } from "react-icons/fa";
import "./contact.css";
export default function ContactPage() {
return (
<>
<Navbar />
<main>
<HeroBanner
title="Contact Us"
bgImage="/images/hero-dosa.png"
/>
<section className="contact-section">
<div className="container contact-wrapper">
{/* Left Column: Info Cards & Map */}
<div className="contact-left">
<div className="contact-cards">
<div className="contact-card">
<FaPhone className="card-icon" />
<h4>Phone</h4>
<p>+91 9876543210</p>
</div>
<div className="contact-card">
<FaWhatsapp className="card-icon" />
<h4>Whatsapp</h4>
<p>+91 9876543210</p>
</div>
<div className="contact-card">
<FaEnvelope className="card-icon" />
<h4>Email</h4>
<p>info@mydosaplace.com</p>
</div>
<div className="contact-card">
<FaMapMarkerAlt className="card-icon" />
<h4>Our Shop</h4>
<p>123 Restaurant Street,<br/>Coimbatore</p>
</div>
</div>
<div className="contact-map">
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2896.800884029152!2d-80.5796577!3d43.4438744!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x882bf755b6dfbef5%3A0x21184d424ab8bf6f!2sMy%20Dosa%20Place%20(%20South%20Indian%20Pure%20VEG)!5e0!3m2!1sen!2sin!4v1781081116425!5m2!1sen!2sin"
style={{ border: 0 }}
allowFullScreen=""
loading="lazy"
referrerPolicy="no-referrer-when-downgrade"
title="Google Maps"
></iframe>
</div>
</div>
{/* Right Column: Contact Form */}
<div className="contact-right">
<span className="contact-eyebrow">Contact Us</span>
<h2>Get In <em>Touch</em></h2>
<p className="contact-subtitle">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus,
luctus nec ullamcorper mattis, pulvinar dapibus leo.
</p>
<ContactForm />
</div>
</div>
</section>
</main>
<Footer />
<ScrollToTop />
</>
);
}

131
src/app/menu/page.js Normal file
View File

@ -0,0 +1,131 @@
import Navbar from '@/components/Navbar';
import Footer from '@/components/Footer';
import ScrollToTop from '@/components/ScrollToTop';
import HeroBanner from '@/components/HeroBanner';
import MenuSection from '@/components/menu/MenuSection';
export const menuData = [
{
category: "Breakfast",
items: [
"Idly (2pcs)",
"Mini Sambar Idly",
"Thattu Idly (2pcs)",
"Ghee Podi Thattu Idly",
"Mini Ghee Podi Idly",
"Rava Khichdi",
"Sambar Idly (2 pcs)",
"Ghee Pongal",
"Kuzhi Paniyaram (10 pcs)",
"Idly with Paya (2 pcs)",
"Rasam Idly (2 pcs)",
"South Indian Thali (Weekends Only)",
"Poori Masala (2 Nos.)",
"Rasa Vada",
"Paruppu Vada (2 Nos.)",
"Pizza Dosa",
"Thayir Vada / Dhahi Vada",
"Sambar Vada"
]
},
{
category: "Appetizers",
items: [
"Thayir (Dahi) Vada (2 pcs)",
"Rasam Vada (2 pcs)",
"Sambar Vada (2 pcs)",
"Paruppu Vada (2 pcs)",
"Medhu Vada (2 pcs)",
"Chilli Gobi",
"Gobi Manchurian",
"Chilli Idly",
"Kothu Parotta",
"Chilli Paneer",
"Gobi / Paneer 65",
"Chilli Parotta"
]
},
{
category: "Breads",
items: [
"Poori Masala",
"Parotta (2 Nos.)",
"Chappathy's (2 Nos.)"
]
},
{
category: "Speciality Dosa",
items: [
"Onion Rava Dosa",
"Rava Dosa",
"Set Masala Dosa",
"Paneer Butter Masala Dosa",
"Butter Garlic Dosa",
"Mysore Masala Dosa",
"Onion Rava Masala Dosa",
"Rava Masala Dosa",
"Cheese Dosa",
"Chettinad Masala Dosa",
"Butter Masala Dosa"
]
},
{
category: "Lunch",
items: [
"Sambar Rice",
"Tamarind Rice",
"Bagala Bath (Curd Rice)",
"Rasam",
"Mini Meals",
"Lemon Rice",
"Veg Pulav"
]
},
{
category: "Utthappams",
items: [
"Plain Utthappam",
"Onion Utthappam",
"Onion Chilli Utthappam",
"Mixed Veg Utthappam",
"Ghee Podi Utthappam"
]
},
{
category: "Beverages",
items: [
"Special Madras Coffee",
"Special Chai"
]
},
{
category: "Combos",
items: [
"1 Idly & 1 Vada",
"2 Idly & 1 Vada"
]
}
];
export const metadata = {
title: 'Our Menu | My Dosa Place Restaurant',
description:
'Explore our authentic South Indian vegetarian menu — crispy dosas, fluffy idlis, rich curries, and more. 100% vegetarian, freshly made daily.',
};
export default function MenuPage() {
return (
<>
<Navbar />
<main>
<HeroBanner
title="Our Menu"
bgImage="/images/south-indian-thali.png"
/>
<MenuSection />
</main>
<Footer />
<ScrollToTop />
</>
);
}

36
src/app/services/page.js Normal file
View File

@ -0,0 +1,36 @@
import Navbar from '@/components/Navbar';
import Footer from '@/components/Footer';
import ScrollToTop from '@/components/ScrollToTop';
import HeroBanner from '@/components/HeroBanner';
import ServiceIntro from '@/components/services/ServiceIntro';
import ServiceCards from '@/components/services/ServiceCards';
import ServicesList from '@/components/services/ServicesList';
import ServiceProcess from '@/components/services/ServiceProcess';
import ServiceCTA from '@/components/services/ServiceCTA';
export const metadata = {
title: 'Our Services | My Dosa Place Restaurant',
description:
'Dine-in, takeout, catering, and festival menus — explore the full range of services at My Dosa Place Waterloo.',
};
export default function ServicesPage() {
return (
<>
<Navbar />
<main>
<HeroBanner
title="Our Services"
bgImage="/images/crispy-masala.png"
/>
<ServiceIntro />
<ServiceCards />
{/* <ServicesList /> */}
{/* <ServiceProcess /> */}
{/* <ServiceCTA /> */}
</main>
<Footer />
<ScrollToTop />
</>
);
}

View File

@ -26,8 +26,13 @@ export default function BlogSection() {
return ( return (
<section className={styles.section}> <section className={styles.section}>
<div className={styles.container}> <div className={styles.container}>
<p className={styles.sectionTag}>📰 Culinary Journal</p> {/* <p className={styles.sectionTag}>📰 Culinary Journal</p> */}
<h2 className={styles.sectionTitle}>Latest from Our Blog</h2> <span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}>📰</span> Updates
</span>
<h2 className={styles.sectionTitle}>
Latest from Our <em>Blog</em>
</h2>
<div className={styles.blogGrid}> <div className={styles.blogGrid}>
{posts.map((post, index) => ( {posts.map((post, index) => (

View File

@ -7,6 +7,7 @@
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: 0 1.5rem; padding: 0 1.5rem;
text-align: center;
} }
.sectionTag { .sectionTag {
@ -113,8 +114,37 @@
} }
} }
@media (max-width: 767px) {
.section {
padding: 80px 0;
}
}
@media (max-width: 767px) { @media (max-width: 767px) {
.section { .section {
padding: 40px 0; padding: 40px 0;
} }
} }
.eyebrow {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin: 0 auto 14px;
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.sectionTitle em {
font-style: italic;
color: #d4a017;
}

View File

@ -0,0 +1,167 @@
"use client";
import { useState, useEffect, useRef } from "react";
import ReCAPTCHA from "react-google-recaptcha";
const initialFormData = {
name: "",
email: "",
subject: "",
message: "",
};
const initialAlert = {
show: false,
type: "",
message: "",
};
export default function ContactForm() {
const [formData, setFormData] = useState(initialFormData);
const [alert, setAlert] = useState(initialAlert);
const [captchaToken, setCaptchaToken] = useState(null);
const recaptchaRef = useRef(null);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
};
const handleCaptchaChange = (token) => {
setCaptchaToken(token);
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!formData.name || !formData.email || !formData.subject || !formData.message) {
setAlert({ show: true, type: "danger", message: "Please fill in all fields." });
return;
}
if (!captchaToken) {
setAlert({ show: true, type: "danger", message: "Please verify the CAPTCHA." });
return;
}
const emailData = {
...formData,
recaptchaToken: captchaToken,
to: "mydosaplacewaterloo@gmail.com",
senderName: "My Dosa Place Contact Form",
message: `
<b>Name:</b> ${formData.name}<br/>
<b>Email:</b> ${formData.email}<br/>
<b>Subject:</b> ${formData.subject}<br/>
<b>Message:</b><br/>${formData.message}
`,
};
try {
const response = await fetch("https://mailserver.metatronnest.com/send", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(emailData),
});
if (!response.ok) {
throw new Error("Failed to send message");
}
setAlert({ show: true, type: "success", message: "Your message has been sent successfully!" });
setFormData(initialFormData);
setCaptchaToken(null);
if (recaptchaRef.current) {
recaptchaRef.current.reset();
}
} catch (error) {
setAlert({ show: true, type: "danger", message: "Failed to send your message. Try again later." });
}
};
useEffect(() => {
if (!alert.show) return;
const timer = window.setTimeout(() => {
setAlert(initialAlert);
}, 5000);
return () => window.clearTimeout(timer);
}, [alert.show]);
return (
<form className="contact-form" onSubmit={handleSubmit}>
{alert.show && (
<div className={`alert ${alert.type === "success" ? "alert-success" : "alert-danger"}`}>
{alert.message}
</div>
)}
<div className="form-group">
<label htmlFor="name">Name</label>
<input
type="text"
id="name"
name="name"
placeholder="Your Name"
value={formData.name}
onChange={handleChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
name="email"
placeholder="message@domain.com"
value={formData.email}
onChange={handleChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
type="text"
id="subject"
name="subject"
placeholder="Title"
value={formData.subject}
onChange={handleChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="message">Message</label>
<textarea
id="message"
name="message"
rows="4"
placeholder="Type here..."
value={formData.message}
onChange={handleChange}
required
></textarea>
</div>
<div className="captcha-wrapper">
<ReCAPTCHA
ref={recaptchaRef}
sitekey="6LeaPggsAAAAAM4DHBSX0y3IC6lu9SrJIhoQKZKo"
onChange={handleCaptchaChange}
/>
</div>
<button type="submit" className="submit-btn">
Send Now
</button>
</form>
);
}

View File

@ -69,8 +69,13 @@ export default function ExperienceSection() {
{/* Right - Content */} {/* Right - Content */}
<div className={styles.contentCol}> <div className={styles.contentCol}>
<span className={styles.tag}>About Our Heritage</span> {/* <span className={styles.tag}>About Our Heritage</span> */}
<h2 className={styles.title}>Experience &amp; Expertise in Authentic Cuisine</h2> <span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}>👨🍳</span> Our Heritage
</span>
<h2 className={styles.title}>
Experience &amp; Expertise in <em>Authentic Cuisine</em>
</h2>
<p className={styles.description}> <p className={styles.description}>
Born from a deep passion for preserving South Indian culinary traditions, our restaurant brings you the true essence of heritage cooking. Every dish we serve is a testament to our decades of experience in crafting perfect, authentic flavors. Born from a deep passion for preserving South Indian culinary traditions, our restaurant brings you the true essence of heritage cooking. Every dish we serve is a testament to our decades of experience in crafting perfect, authentic flavors.
</p> </p>

View File

@ -199,23 +199,58 @@
} }
.iconWrap { .iconWrap {
width: 50px; width: 60px;
height: 50px; height: 60px;
border-radius: 50%; border-radius: 53% 47% 33% 67% / 60% 41% 59% 40%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 1.5rem; font-size: 1.8rem;
flex-shrink: 0; flex-shrink: 0;
position: relative;
z-index: 1;
}
/* Sparkle top left */
.iconWrap::before {
content: '';
position: absolute;
top: -8px;
left: -10px;
width: 20px;
height: 20px;
background-image: radial-gradient(circle 2px at 10px 2px, currentColor 100%, transparent),
radial-gradient(circle 1.5px at 2px 10px, currentColor 100%, transparent),
radial-gradient(circle 1px at 16px 16px, currentColor 100%, transparent);
z-index: -1;
opacity: 0.6;
}
/* Dotted curve right */
.iconWrap::after {
content: '';
position: absolute;
top: -4px;
right: -6px;
width: 30px;
height: 30px;
border-radius: 50%;
border: 2px dashed currentColor;
clip-path: polygon(50% 0, 100% 0, 100% 100%, 50% 100%);
transform: rotate(45deg);
z-index: -1;
opacity: 0.4;
} }
.cardSolid .iconWrap { .cardSolid .iconWrap {
background: var(--color-primary); background: var(--color-primary);
color: #fff;
box-shadow: 0 4px 10px rgba(1, 77, 51, 0.2); box-shadow: 0 4px 10px rgba(1, 77, 51, 0.2);
} }
.cardOutline .iconWrap { .cardOutline .iconWrap {
background: var(--color-bg-light); background: var(--color-bg-light);
color: var(--color-primary);
} }
.cardContent { .cardContent {
@ -257,8 +292,41 @@
} }
} }
@media (max-width: 767px) {
.section {
padding: 80px 0;
}
}
@media (max-width: 767px) { @media (max-width: 767px) {
.section { .section {
padding: 40px 0; padding: 40px 0;
} }
} }
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.title em {
font-style: italic;
color: #d4a017;
}
@media (max-width: 424px) {
.experienceBadge {
bottom: -70px;
}
}

View File

@ -33,8 +33,13 @@ export default function FavoritesSection() {
<section className={styles.section}> <section className={styles.section}>
<div className={styles.container}> <div className={styles.container}>
<div className={styles.sectionHeader}> <div className={styles.sectionHeader}>
<p className={styles.sectionTag}>Chef&apos;s Selection</p> {/* <p className={styles.sectionTag}>Chef&apos;s Selection</p> */}
<h2 className={styles.sectionTitle}>Customer Favourites</h2> <span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}></span> Popular
</span>
<h2 className={styles.sectionTitle}>
Customer <em>Favourites</em>
</h2>
</div> </div>
<div className={styles.grid}> <div className={styles.grid}>
{favorites.map((item, i) => ( {favorites.map((item, i) => (

View File

@ -137,7 +137,7 @@
@media (max-width: 767px) { @media (max-width: 767px) {
.section { .section {
padding: 40px 0; padding: 80px 0;
} }
.grid { .grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
@ -145,3 +145,31 @@
margin: 0 auto; margin: 0 auto;
} }
} }
@media (max-width: 767px) {
.section {
padding: 40px 0;
}
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.sectionTitle em {
font-style: italic;
color: #d4a017;
}

View File

@ -30,14 +30,47 @@
} }
.iconWrap { .iconWrap {
width: 64px; width: 80px;
height: 64px; height: 80px;
margin: 0 auto 1.2rem; margin: 0 auto 1.5rem;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background: var(--color-bg-light); background: #014d33; /* coral orange */
border-radius: 53% 47% 33% 67% / 60% 41% 59% 40%;
position: relative;
z-index: 1;
}
/* Sparkle top left */
.iconWrap::before {
content: '';
position: absolute;
top: -12px;
left: -15px;
width: 24px;
height: 24px;
background-image: radial-gradient(circle 2.5px at 12px 2px, #014d33 100%, transparent),
radial-gradient(circle 2px at 2px 14px, #014d33 100%, transparent),
radial-gradient(circle 1.5px at 20px 20px, #014d33 100%, transparent);
z-index: -1;
opacity: 0.8;
}
/* Dotted curve right */
.iconWrap::after {
content: '';
position: absolute;
top: -5px;
right: -10px;
width: 40px;
height: 40px;
border-radius: 50%; border-radius: 50%;
border: 2px dashed #014d33;
clip-path: polygon(50% 0, 100% 0, 100% 100%, 50% 100%);
transform: rotate(45deg);
z-index: -1;
opacity: 0.5;
} }
.icon { .icon {
@ -71,8 +104,36 @@
} }
} }
@media (max-width: 767px) {
.section {
padding: 80px 0;
}
}
@media (max-width: 767px) { @media (max-width: 767px) {
.section { .section {
padding: 40px 0; padding: 40px 0;
} }
} }
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.title em {
font-style: italic;
color: #d4a017;
}

View File

@ -35,10 +35,10 @@ export default function Footer() {
<h4 className={styles.columnTitle}>Quick Links</h4> <h4 className={styles.columnTitle}>Quick Links</h4>
<ul className={styles.linkList}> <ul className={styles.linkList}>
<li><Link href="/">Home</Link></li> <li><Link href="/">Home</Link></li>
<li><Link href="#about">About Us</Link></li> <li><Link href="/about">About Us</Link></li>
<li><Link href="#menu">Our Menu</Link></li> <li><Link href="/menu">Our Menu</Link></li>
<li><Link href="#gallery">Gallery</Link></li> <li><Link href="/services">Services</Link></li>
<li><Link href="#contact">Contact Us</Link></li> <li><Link href="/contact">Contact Us</Link></li>
</ul> </ul>
</div> </div>
@ -91,7 +91,7 @@ export default function Footer() {
{/* Bottom Bar */} {/* Bottom Bar */}
<div className={styles.bottomBar}> <div className={styles.bottomBar}>
<p className={styles.copyright}> <p className={styles.copyright}>
Copyright 2026 © mydosarestruant. Powered by <a href="https://metatroncubesolutions.com/" target="_blank" rel="noopener noreferrer" className={styles.poweredLink}>MetatronCube</a>. All Right Reserved. Copyright 2026 © My Dosa Restruant. Powered by <a href="https://metatroncubesolutions.com/" target="_blank" rel="noopener noreferrer" className={styles.poweredLink}>MetatronCube</a>. All Right Reserved.
</p> </p>
</div> </div>
</div> </div>

View File

@ -131,6 +131,8 @@
font-size: 0.9rem; font-size: 0.9rem;
color: rgba(255, 255, 255, 0.85); color: rgba(255, 255, 255, 0.85);
line-height: 1.6; line-height: 1.6;
overflow-wrap: anywhere;
word-break: break-word;
} }
/* Bottom Bar */ /* Bottom Bar */

View File

@ -14,8 +14,13 @@ export default function GallerySection() {
return ( return (
<section className={styles.section}> <section className={styles.section}>
<div className={styles.container}> <div className={styles.container}>
<p className={styles.sectionTag}>📸 Follow Our Journey</p> {/* <p className={styles.sectionTag}>📸 Follow Our Journey</p> */}
<h2 className={styles.sectionTitle}>Our Culinary Gallery</h2> <span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}>📸</span> Gallery
</span>
<h2 className={styles.sectionTitle}>
Our Culinary <em>Gallery</em>
</h2>
<div className={styles.galleryGrid}> <div className={styles.galleryGrid}>
{images.map((src, index) => ( {images.map((src, index) => (

View File

@ -7,6 +7,7 @@
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: 0 1.5rem; padding: 0 1.5rem;
text-align: center;
} }
.sectionTag { .sectionTag {
@ -116,8 +117,37 @@
} }
} }
@media (max-width: 767px) {
.section {
padding: 80px 0;
}
}
@media (max-width: 767px) { @media (max-width: 767px) {
.section { .section {
padding: 40px 0; padding: 40px 0;
} }
} }
.eyebrow {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin: 0 auto 14px;
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.sectionTitle em {
font-style: italic;
color: #d4a017;
}

View File

@ -0,0 +1,23 @@
import Link from 'next/link';
import styles from './HeroBanner.module.css';
export default function HeroBanner({ title, pageName, bgImage }) {
const currentPathName = pageName || title;
return (
<section
className={styles.hero}
style={{ backgroundImage: bgImage ? `url(${bgImage})` : 'none' }}
>
<div className={styles.overlay} />
<div className={styles.content}>
<h1 className={styles.title}>{title}</h1>
<div className={styles.breadcrumb}>
<Link href="/">Home</Link>
<span className={styles.separator}>/</span>
<span>{currentPathName}</span>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,85 @@
.hero {
position: relative;
height: 400px;
display: flex;
align-items: center;
background-color: #1b4332;
background-size: cover;
background-position: center right;
background-repeat: no-repeat;
overflow: hidden;
margin-top: 0;
}
/* Overlay that fades from dark green on the left to transparent on the right */
.overlay {
position: absolute;
inset: 0;
background: linear-gradient(90deg, #014d33 0%, rgba(1, 77, 51, 0.8) 40%, transparent 100%);
z-index: 1;
}
.content {
position: relative;
z-index: 2;
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 24px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin-top: 80px; /* Accounts for navbar */
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(2.5rem, 5vw, 3.5rem);
font-weight: 700;
color: #ffffff;
line-height: 1.15;
margin: 0 0 10px;
text-transform: capitalize;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 10px;
font-family: var(--font-inter), sans-serif;
font-size: 1rem;
color: rgba(255, 255, 255, 0.7);
}
.breadcrumb a {
color: #ffffff;
text-decoration: none;
transition: color 0.3s;
}
.breadcrumb a:hover {
color: var(--color-secondary, #d4a017);
}
.separator {
color: rgba(255, 255, 255, 0.5);
}
.breadcrumb span:last-child {
color: var(--color-secondary, #d4a017);
font-weight: 500;
}
@media (max-width: 768px) {
.hero {
height: 300px;
}
.content {
margin-top: 60px;
padding: 0 20px;
}
.title {
font-size: 2.25rem;
}
}

View File

@ -64,19 +64,19 @@ export default function HeroSection() {
{slide.subtitle} {slide.subtitle}
</p> </p>
<div className={styles.heroActions}> <div className={styles.heroActions}>
<a href="#menu" className={styles.btnPrimary}> <a href="/menu" className={styles.btnPrimary}>
{slide.primaryBtn} {slide.primaryBtn}
</a> </a>
<a href="#contact" className={styles.btnSecondary}> <a href="/contact" className={styles.btnSecondary}>
{slide.secondaryBtn} {slide.secondaryBtn}
</a> </a>
</div> </div>
</div> </div>
))} ))}
<div className={styles.heroMeta}> {/* <div className={styles.heroMeta}>
<div className={styles.metaItem}> <div className={styles.metaItem}>
<span className={styles.metaIcon}></span> <span className={styles.metaIcon}></span>
<span>4.9 Rating</span> <span>Quality Food</span>
</div> </div>
<div className={styles.metaDivider}></div> <div className={styles.metaDivider}></div>
<div className={styles.metaItem}> <div className={styles.metaItem}>
@ -88,7 +88,7 @@ export default function HeroSection() {
<span className={styles.metaIcon}>🕐</span> <span className={styles.metaIcon}>🕐</span>
<span>Open 7 Days</span> <span>Open 7 Days</span>
</div> </div>
</div> </div> */}
{/* Slide Indicators */} {/* Slide Indicators */}
<div className={styles.indicators}> <div className={styles.indicators}>

View File

@ -1,13 +1,89 @@
"use client";
import Image from 'next/image'; import Image from 'next/image';
import { useState, useRef } from 'react';
import ReCAPTCHA from 'react-google-recaptcha';
import styles from './LocationSection.module.css'; import styles from './LocationSection.module.css';
export default function LocationSection() { export default function LocationSection() {
const [formData, setFormData] = useState({
name: '',
lastName: '',
email: '',
message: '',
});
const [captchaToken, setCaptchaToken] = useState(null);
const [formStatus, setFormStatus] = useState({ show: false, type: '', message: '' });
const recaptchaRef = useRef(null);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
};
const handleCaptchaChange = (token) => {
setCaptchaToken(token);
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!formData.name || !formData.email || !formData.message) {
setFormStatus({ show: true, type: 'danger', message: 'Please fill in all required fields.' });
return;
}
if (!captchaToken) {
setFormStatus({ show: true, type: 'danger', message: 'Please verify the CAPTCHA.' });
return;
}
const emailData = {
...formData,
subject: `Message from ${formData.name} ${formData.lastName}`,
recaptchaToken: captchaToken,
to: 'mydosaplacewaterloo@gmail.com',
senderName: 'My Dosa Place Home Contact Form',
message: `
<b>Name:</b> ${formData.name} ${formData.lastName}<br/>
<b>Email:</b> ${formData.email}<br/>
<b>Message:</b><br/>${formData.message}
`,
};
try {
const response = await fetch('https://mailserver.metatronnest.com/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(emailData),
});
if (!response.ok) {
throw new Error('Failed to send message');
}
setFormStatus({ show: true, type: 'success', message: 'Your message has been sent successfully!' });
setFormData({ name: '', lastName: '', email: '', message: '' });
setCaptchaToken(null);
if (recaptchaRef.current) {
recaptchaRef.current.reset();
}
} catch {
setFormStatus({ show: true, type: 'danger', message: 'Failed to send your message. Try again later.' });
}
};
return ( return (
<section id="contact" className={styles.section}> <section id="contact" className={styles.section}>
<div className={styles.container}> <div className={styles.container}>
<div className={styles.header}> <div className={styles.header}>
<p className={styles.sectionTag}>📍 Contact & Location</p> {/* <p className={styles.sectionTag}>📍 Contact & Location</p> */}
<h2 className={styles.sectionTitle}>Get in Touch</h2> <span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}>📍</span> Find Us
</span>
<h2 className={styles.sectionTitle}>
Visit Our <em>Restaurant</em>
</h2>
<p className={styles.sectionDesc}>We'd love to hear from you. Book a table or ask us a question.</p> <p className={styles.sectionDesc}>We'd love to hear from you. Book a table or ask us a question.</p>
</div> </div>
@ -68,27 +144,73 @@ export default function LocationSection() {
<div className={styles.formPane}> <div className={styles.formPane}>
<h3 className={styles.formTitle}>Send a Message</h3> <h3 className={styles.formTitle}>Send a Message</h3>
<p className={styles.formSubtitle}>Fill out the form below and our team will get back to you shortly.</p> <p className={styles.formSubtitle}>Fill out the form below and our team will get back to you shortly.</p>
{formStatus.show && (
<div className={`${styles.formMessage} ${formStatus.type === 'success' ? styles.success : styles.error}`}>
{formStatus.message}
</div>
)}
<form className={styles.form}> <form className={styles.form} onSubmit={handleSubmit}>
<div className={styles.formRow}> <div className={styles.formRow}>
<div className={styles.formGroup}> <div className={styles.formGroup}>
<label htmlFor="name" className={styles.label}>First Name</label> <label htmlFor="name" className={styles.label}>First Name</label>
<input type="text" id="name" className={styles.input} placeholder="John" /> <input
type="text"
id="name"
name="name"
className={styles.input}
placeholder="John"
value={formData.name}
onChange={handleChange}
/>
</div> </div>
<div className={styles.formGroup}> <div className={styles.formGroup}>
<label htmlFor="lastName" className={styles.label}>Last Name</label> <label htmlFor="lastName" className={styles.label}>Last Name</label>
<input type="text" id="lastName" className={styles.input} placeholder="Doe" /> <input
type="text"
id="lastName"
name="lastName"
className={styles.input}
placeholder="Doe"
value={formData.lastName}
onChange={handleChange}
/>
</div> </div>
</div> </div>
<div className={styles.formGroup}> <div className={styles.formGroup}>
<label htmlFor="email" className={styles.label}>Email Address</label> <label htmlFor="email" className={styles.label}>Email Address</label>
<input type="email" id="email" className={styles.input} placeholder="john@example.com" /> <input
type="email"
id="email"
name="email"
className={styles.input}
placeholder="john@example.com"
value={formData.email}
onChange={handleChange}
/>
</div> </div>
<div className={styles.formGroup}> <div className={styles.formGroup}>
<label htmlFor="message" className={styles.label}>Your Message</label> <label htmlFor="message" className={styles.label}>Your Message</label>
<textarea id="message" rows="4" className={styles.textarea} placeholder="How can we help you?"></textarea> <textarea
id="message"
name="message"
rows="4"
className={styles.textarea}
placeholder="How can we help you?"
value={formData.message}
onChange={handleChange}
></textarea>
</div>
<div className={styles.captchaWrapper}>
<ReCAPTCHA
ref={recaptchaRef}
sitekey="6LeaPggsAAAAAM4DHBSX0y3IC6lu9SrJIhoQKZKo"
onChange={handleCaptchaChange}
/>
</div> </div>
<button type="submit" className={styles.btnSubmit}> <button type="submit" className={styles.btnSubmit}>

View File

@ -118,6 +118,8 @@
font-size: 0.95rem; font-size: 0.95rem;
line-height: 1.5; line-height: 1.5;
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.9);
overflow-wrap: anywhere;
word-break: break-word;
} }
/* Right - Form */ /* Right - Form */
@ -146,6 +148,30 @@
width: 100%; width: 100%;
} }
.formMessage {
margin-bottom: 1rem;
padding: 1rem 1.25rem;
border-radius: 12px;
font-family: inherit;
font-size: 0.95rem;
}
.success {
background: #ecfdf5;
color: #166534;
border: 1px solid #d1fae5;
}
.error {
background: #fef2f2;
color: #991b1b;
border: 1px solid #fecaca;
}
.captchaWrapper {
margin-bottom: 1.25rem;
}
.formRow { .formRow {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
@ -227,8 +253,61 @@
} }
} }
@media (max-width: 767px) {
.section {
padding: 80px 0;
}
}
@media (max-width: 767px) { @media (max-width: 767px) {
.section { .section {
padding: 40px 0; padding: 40px 0;
} }
} }
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.sectionTitle em {
font-style: italic;
color: #d4a017;
}
@media (max-width: 460px) {
.glassInfo {
padding: 15px;
}
.imagePane {
padding: 10px;
}
.infoIcon {
width: 35px;
height: 35px;
}
.infoItem {
gap: 10px;
}
}
@media (max-width: 375px) {
.glassInfo,
.formPane {
width: 77%;
}
}

View File

@ -14,8 +14,13 @@ export default function MenuSection() {
return ( return (
<section id="menu" className={styles.section}> <section id="menu" className={styles.section}>
<div className={styles.container}> <div className={styles.container}>
<p className={styles.sectionTag}>🍽 Our Menu</p> {/* <p className={styles.sectionTag}>🍽️ Our Menu</p> */}
<h2 className={styles.sectionTitle}>The Tiffin Menu</h2> <span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}></span> Explore Our Menu
</span>
<h2 className={styles.sectionTitle}>
A Taste of <em>South India</em>
</h2>
{/* Menu Grid */} {/* Menu Grid */}
<div className={styles.menuGrid}> <div className={styles.menuGrid}>
@ -43,8 +48,8 @@ export default function MenuSection() {
</div> </div>
<div className={styles.center}> <div className={styles.center}>
<a href="#order" className={styles.btnViewFull}> <a href="/menu" className={styles.btnViewFull}>
📋 Explore Full Menu Explore Full Menu
</a> </a>
</div> </div>
</div> </div>

View File

@ -131,8 +131,39 @@
} }
} }
@media (max-width: 767px) {
.section {
padding: 80px 0;
}
}
@media (max-width: 767px) { @media (max-width: 767px) {
.section { .section {
padding: 40px 0; padding: 40px 0;
} }
} }
.eyebrow {
display: flex;
width: 100%;
justify-content: center;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.1rem;
color: #d99c43;
margin: 0 auto 10px;
letter-spacing: 0.03em;
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.sectionTitle em {
font-style: italic;
color: #d4a017;
}

View File

@ -1,7 +1,12 @@
"use client";
import { useState } from 'react';
import Link from 'next/link'; import Link from 'next/link';
import styles from './Navbar.module.css'; import styles from './Navbar.module.css';
export default function Navbar() { export default function Navbar() {
const [menuOpen, setMenuOpen] = useState(false);
return ( return (
<nav className={styles.navbar}> <nav className={styles.navbar}>
<div className={styles.navInner}> <div className={styles.navInner}>
@ -12,25 +17,45 @@ export default function Navbar() {
</Link> </Link>
{/* Navigation Links */} {/* Navigation Links */}
<ul className={styles.navLinks}> <ul className={`${styles.navLinks} ${menuOpen ? styles.open : ''}`}>
<li><Link href="/" className={styles.navLink}>Home</Link></li> <li>
<li><Link href="#about" className={styles.navLink}>About</Link></li> <Link href="/" className={styles.navLink} onClick={() => setMenuOpen(false)}>
<li><Link href="#menu" className={styles.navLink}>Menu</Link></li> Home
<li><Link href="#specials" className={styles.navLink}>Specials</Link></li> </Link>
<li><Link href="#catering" className={styles.navLink}>Catering</Link></li> </li>
<li><Link href="#gallery" className={styles.navLink}>Gallery</Link></li> <li>
<li><Link href="#contact" className={styles.navLink}>Contact</Link></li> <Link href="/about" className={styles.navLink} onClick={() => setMenuOpen(false)}>
About
</Link>
</li>
<li>
<Link href="/services" className={styles.navLink} onClick={() => setMenuOpen(false)}>
Services
</Link>
</li>
<li>
<Link href="/menu" className={styles.navLink} onClick={() => setMenuOpen(false)}>
Menu
</Link>
</li>
<li>
<Link href="/contact" className={styles.navLink} onClick={() => setMenuOpen(false)}>
Contact
</Link>
</li>
</ul> </ul>
{/* Right Actions */} <button
<div className={styles.navActions}> type="button"
<Link href="tel:+15191234567" className={styles.btnCall}> className={`${styles.menuToggle} ${menuOpen ? styles.menuOpen : ''}`}
📞 Call Now onClick={() => setMenuOpen((prev) => !prev)}
</Link> aria-label="Toggle navigation menu"
<Link href="#order" className={styles.btnOrder}> aria-expanded={menuOpen}
Order Online >
</Link> <span />
</div> <span />
<span />
</button>
</div> </div>
</nav> </nav>
); );

View File

@ -17,6 +17,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
height: 64px; height: 64px;
position: relative;
} }
.logo { .logo {
@ -25,6 +26,7 @@
gap: 0.5rem; gap: 0.5rem;
text-decoration: none; text-decoration: none;
color: var(--color-primary); color: var(--color-primary);
z-index: 10;
} }
.logoLeaf { .logoLeaf {
@ -41,10 +43,14 @@
.navLinks { .navLinks {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center;
gap: 1.8rem; gap: 1.8rem;
list-style: none; list-style: none;
margin: 0; margin: 0 auto;
padding: 0; padding: 0;
position: static;
transform: none;
white-space: nowrap;
} }
.navLink { .navLink {
@ -75,53 +81,89 @@
width: 100%; width: 100%;
} }
.navActions { .menuToggle {
display: flex; display: none;
width: 35px;
height: 35px;
background: transparent;
border: none;
cursor: pointer;
padding: 0;
position: relative;
z-index: 20;
justify-content: center;
align-items: center; align-items: center;
gap: 0.75rem; line-height: 0;
} }
.btnCall { .menuToggle span {
font-size: 0.85rem; display: block;
font-weight: 500; width: 100%;
color: var(--color-primary); height: 3px;
border: 1px solid var(--color-primary); background: var(--color-primary);
padding: 0.5rem 1rem; border-radius: 999px;
border-radius: var(--border-radius-full); transition: transform 0.25s ease, opacity 0.25s ease;
text-decoration: none; position: relative;
transition: all 0.25s ease;
} }
.btnCall:hover { .menuToggle span + span {
border-color: var(--color-primary); margin-top: 8px;
background: rgba(1, 77, 51, 0.06);
} }
.btnOrder { .menuToggle.menuOpen span:first-child {
font-size: 0.85rem; transform: translateY(11px) rotate(45deg);
font-weight: 600;
color: var(--color-primary);
background: var(--color-secondary);
padding: 0.55rem 1.2rem;
border-radius: var(--border-radius-full);
text-decoration: none;
transition: all 0.25s ease;
} }
.btnOrder:hover { .menuToggle.menuOpen span:nth-child(2) {
background: var(--color-secondary-hover); opacity: 0;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(217, 156, 67, 0.4);
} }
@media (max-width: 1024px) { .menuToggle.menuOpen span:last-child {
transform: translateY(-11px) rotate(-45deg);
}
@media (max-width: 768px) {
.navLinks { .navLinks {
display: none; display: none;
position: absolute;
top: 100%;
left: 0;
right: 0;
transform: none;
background: #ffffff;
flex-direction: column;
align-items: center;
gap: 15px;
border-bottom: 1px solid #e8e8e8;
padding: 1rem 0;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05);
margin: 0;
} }
}
@media (max-width: 600px) { .navLinks.open {
.btnCall { display: flex;
display: none; }
.navLink {
width: 100%;
padding: 0.95rem 1rem;
text-align: center;
color: var(--color-primary);
border-bottom: 1px solid rgba(0, 0, 0, 0.04);
}
.navLink:last-child {
border-bottom: none;
}
.navInner {
justify-content: space-between;
}
.menuToggle {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
} }
} }

View File

@ -9,11 +9,16 @@ export default function ProcessSection() {
{/* Left - Dark Green Card with Overlapping Image */} {/* Left - Dark Green Card with Overlapping Image */}
<div className={styles.leftCard}> <div className={styles.leftCard}>
<div className={styles.cardContent}> <div className={styles.cardContent}>
<h2 className={styles.cardTitle}>My Dosa Place<br/>Restaurant</h2> <span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}></span> Our Process
</span>
<h2 className={styles.cardTitle}>
How We Make It <em>Perfect</em>
</h2>
<p className={styles.cardDescription}> <p className={styles.cardDescription}>
Born from a passion for preserving the culinary heritage of South India, My Dosa Place brings authentic flavors from the kitchens of Tamil Nadu and Karnataka right to Waterloo. Born from a passion for preserving the culinary heritage of South India, My Dosa Place brings authentic flavors from the kitchens of Tamil Nadu and Karnataka right to Waterloo.
</p> </p>
<a href="#menu" className={styles.btnExplore}> <a href="/menu" className={styles.btnExplore}>
Explore Menu Explore Menu
</a> </a>
</div> </div>

View File

@ -28,7 +28,7 @@
.cardContent { .cardContent {
padding: 3.5rem 3rem; padding: 3.5rem 3rem;
width: 55%; width: 65%;
max-width: 400px; max-width: 400px;
color: #fff; color: #fff;
z-index: 2; z-index: 2;
@ -159,8 +159,36 @@
} }
} }
@media (max-width: 767px) {
.section {
padding: 80px 0;
}
}
@media (max-width: 767px) { @media (max-width: 767px) {
.section { .section {
padding: 40px 0; padding: 40px 0;
} }
} }
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.title em {
font-style: italic;
color: #d4a017;
}

View File

@ -7,7 +7,12 @@ export default function PromoSection() {
<div className={styles.container}> <div className={styles.container}>
<div className={styles.content}> <div className={styles.content}>
<div className={styles.pill}>WEEKEND SPECIAL</div> <div className={styles.pill}>WEEKEND SPECIAL</div>
<h2 className={styles.title}>Royal South Indian Thali</h2> {/* <span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}>👑</span> Special Offer
</span> */}
<h2 className={styles.title}>
Royal South Indian <em>Thali</em>
</h2>
<p className={styles.description}> <p className={styles.description}>
A limited edition 21-item feast served exclusively on Saturdays and A limited edition 21-item feast served exclusively on Saturdays and
Sundays. Experience the true diversity of Southern flavours in one Sundays. Experience the true diversity of Southern flavours in one
@ -18,7 +23,7 @@ export default function PromoSection() {
<span className={styles.detailDivider}></span> <span className={styles.detailDivider}></span>
<span className={styles.detailItem}>💵 $24.99 Per Person</span> <span className={styles.detailItem}>💵 $24.99 Per Person</span>
</div> </div>
<a href="#contact" className={styles.btnReserve}> <a href="/contact" className={styles.btnReserve}>
Reserve for This Weekend Reserve for This Weekend
</a> </a>
</div> </div>

View File

@ -126,8 +126,36 @@
} }
} }
@media (max-width: 767px) {
.section {
padding: 80px 0;
}
}
@media (max-width: 767px) { @media (max-width: 767px) {
.section { .section {
padding: 40px 0; padding: 40px 0;
} }
} }
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.title em {
font-style: italic;
color: #d4a017;
}

View File

@ -3,67 +3,97 @@ import { useState, useEffect, useCallback } from 'react';
import Image from 'next/image'; import Image from 'next/image';
import styles from './ReviewsSection.module.css'; import styles from './ReviewsSection.module.css';
const reviews = [ function getInitials(name) {
{ return name
text: "Best dosa I've ever had outside of India! The Masala Dosa was perfectly crispy and the sambar was incredibly flavorful. The weekend Thali is outstanding — so many flavors in one plate. We will definitely be back!", .split(' ')
name: 'Sarah M. & Family', .map((word) => word.charAt(0).toUpperCase())
role: 'Local Food Critic', .slice(0, 2)
initials: 'SM', .join('');
rating: '4.9', }
},
{ function parseRating(review) {
text: "My Dosa Place is an absolute gem in Waterloo. The chutneys are made fresh daily and the Rava Dosa has the most amazing crunch. It genuinely transports you to Tamil Nadu. Highly recommend the Uttappam too!", const rating = review.rating ?? review.review_rating ?? review.stars ?? 0;
name: 'Raj Patel', return Number(rating) || 0;
role: 'Food Blogger', }
initials: 'RP',
rating: '5.0', function formatReviews(rawReviews) {
}, return rawReviews
{ .filter((review) => {
text: "We visited for lunch and were blown away by the authentic South Indian experience. The Sambar was rich and perfectly spiced, and the dosas were golden crisp. The staff was warm and welcoming — felt like home!", const text = review.text || review.description || review.snippet || review.review_text || review.body || review.content || '';
name: 'Priya & Anand K.', return text && text.trim().length > 0 && parseRating(review) === 5;
role: 'Regular Customer', })
initials: 'PK', .map((review) => {
rating: '4.8', const text = review.text || review.description || review.snippet || review.review_text || review.body || review.content || '';
}, const name = review.author_name || review.user?.name || 'Happy Customer';
{ const rating = parseRating(review);
text: "I've tried many Indian restaurants across Canada and My Dosa Place stands out for its genuine authenticity. The Benne Masala Dosa is a must-try. Amazing value for the quality and portion sizes. Can't wait to return!", const role = review.relative_time_description || review.time || 'Google Reviewer';
name: 'Michael Torres',
role: 'Food Enthusiast', return {
initials: 'MT', text,
rating: '4.9', name,
}, role,
]; initials: getInitials(name),
rating,
};
});
}
export default function ReviewsSection() { export default function ReviewsSection() {
const [current, setCurrent] = useState(0); const [current, setCurrent] = useState(0);
const [animating, setAnimating] = useState(false); const [animating, setAnimating] = useState(false);
const [reviews, setReviews] = useState([]);
const [loading, setLoading] = useState(true);
const goTo = useCallback((index) => { useEffect(() => {
if (animating) return; async function loadReviews() {
setAnimating(true); try {
setTimeout(() => { const res = await fetch('/api/reviews');
setCurrent(index); if (!res.ok) throw new Error('Failed to fetch reviews');
setAnimating(false); const data = await res.json();
}, 300); console.log('API Reviews Response:', data);
}, [animating]); const formatted = formatReviews(data.reviews || []);
console.log('Formatted Reviews:', formatted);
setReviews(formatted);
} catch (error) {
console.error('Reviews fetch failed:', error);
setReviews([]);
} finally {
setLoading(false);
}
}
const prev = () => goTo((current - 1 + reviews.length) % reviews.length); loadReviews();
const next = useCallback(() => goTo((current + 1) % reviews.length), [current, goTo]); }, []);
const displayReviews = reviews;
const goTo = useCallback(
(index) => {
if (animating) return;
setAnimating(true);
setTimeout(() => {
setCurrent(index);
setAnimating(false);
}, 300);
},
[animating]
);
const prev = () => goTo((current - 1 + displayReviews.length) % displayReviews.length);
const next = useCallback(() => goTo((current + 1) % displayReviews.length), [current, displayReviews.length, goTo]);
// Auto-slide every 5 seconds
useEffect(() => { useEffect(() => {
const timer = setInterval(next, 5000); const timer = setInterval(next, 5000);
return () => clearInterval(timer); return () => clearInterval(timer);
}, [next]); }, [next]);
const review = reviews[current]; const review = displayReviews.length ? displayReviews[current % displayReviews.length] : null;
return ( return (
<section className={styles.section}> <section className={styles.section}>
<div className={styles.container}> <div className={styles.container}>
<div className={styles.contentWrap}> <div className={styles.contentWrap}>
{/* Left Stacked image collage */}
<div className={styles.imageCol}> <div className={styles.imageCol}>
<div className={styles.collage}> <div className={styles.collage}>
<div className={styles.imgLeft}> <div className={styles.imgLeft}>
@ -73,53 +103,61 @@ export default function ReviewsSection() {
<Image src="/images/sambar.png" alt="Sambar" fill className={styles.img} /> <Image src="/images/sambar.png" alt="Sambar" fill className={styles.img} />
</div> </div>
<div className={styles.ratingBadge}> <div className={styles.ratingBadge}>
<span className={styles.ratingText}>4.9</span> <span className={styles.ratingText}>My Dosa <br /> Place</span>
<div className={styles.stars}></div>
<span className={styles.ratingLabel}>500+ Reviews</span>
</div> </div>
</div> </div>
</div> </div>
{/* Right Testimonial slider */}
<div className={styles.textCol}> <div className={styles.textCol}>
<p className={styles.sectionTag}>Testimonials</p> <span className={styles.eyebrow}>
<h2 className={styles.sectionTitle}>What Our Customers Say</h2> <span className={styles.eyebrowIcon}>💬</span> Google Reviews
</span>
<h2 className={styles.sectionTitle}>
What Our Customers <em>Say</em>
</h2>
<div className={`${styles.reviewCard} ${animating ? styles.fadeOut : styles.fadeIn}`}> {loading && <p className={styles.loadingText}>Loading Google reviews...</p>}
<div className={styles.quoteMark}>&ldquo;</div> {!loading && !displayReviews.length && (
<p className={styles.reviewText}>{review.text}</p> <p className={styles.loadingText}>No Google reviews are available right now.</p>
)}
{!loading && review && (
<>
<div className={`${styles.reviewCard} ${animating ? styles.fadeOut : styles.fadeIn}`}>
<div className={styles.quoteMark}>&ldquo;</div>
<p className={styles.reviewText}>{review.text}</p>
<div className={styles.reviewer}> <div className={styles.reviewer}>
<div className={styles.avatar}>{review.initials}</div> <div className={styles.avatar}>{review.initials}</div>
<div> <div>
<p className={styles.reviewerName}>{review.name}</p> <p className={styles.reviewerName}>{review.name}</p>
<p className={styles.reviewDate}>{review.role}</p> <p className={styles.reviewDate}>{review.role}</p>
</div>
{/* <div className={styles.reviewRating}>{'★'.repeat(review.rating)}</div> */}
</div>
</div> </div>
<div className={styles.reviewRating}>{review.rating} </div>
</div>
</div>
{/* Dots + Arrows */} <div className={styles.controls}>
<div className={styles.controls}> <div className={styles.dots}>
<div className={styles.dots}> {displayReviews.map((_, i) => (
{reviews.map((_, i) => ( <button
<button key={i}
key={i} className={`${styles.dot} ${i === current ? styles.dotActive : ''}`}
className={`${styles.dot} ${i === current ? styles.dotActive : ''}`} onClick={() => goTo(i)}
onClick={() => goTo(i)} aria-label={`Go to review ${i + 1}`}
aria-label={`Go to review ${i + 1}`} />
/> ))}
))} </div>
</div> <div className={styles.navButtons}>
<div className={styles.navButtons}> <button className={styles.navBtn} onClick={prev} aria-label="Previous">
<button className={styles.navBtn} onClick={prev} aria-label="Previous"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg> </button>
</button> <button className={styles.navBtn} onClick={next} aria-label="Next">
<button className={styles.navBtn} onClick={next} aria-label="Next"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg> </button>
</button> </div>
</div> </div>
</div> </>
)}
</div> </div>
</div> </div>

View File

@ -274,7 +274,7 @@
@media (max-width: 767px) { @media (max-width: 767px) {
.section { .section {
padding: 40px 0; padding: 80px 0;
} }
.collage { .collage {
@ -285,3 +285,31 @@
font-size: 1.8rem; font-size: 1.8rem;
} }
} }
@media (max-width: 767px) {
.section {
padding: 40px 0;
}
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.sectionTitle em {
font-style: italic;
color: #d4a017;
}

View File

@ -0,0 +1,31 @@
import Link from 'next/link';
import styles from './AboutCTA.module.css';
export default function AboutCTA() {
return (
<section className={styles.section}>
<div className={styles.container}>
<div className={styles.inner}>
<div className={styles.decorLeaf} aria-hidden="true">🍃</div>
<span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}></span> Ready to Visit?
</span>
<h2 className={styles.title}>
Reserve Your <em>Table</em> Today
</h2>
<p className={styles.body}>
Reserve a table today and enjoy Waterloo's favourite South Indian meal.
</p>
<div className={styles.actions}>
<Link href="/menu" className={styles.btnPrimary}>
Explore Menu
</Link>
<Link href="/contact" className={styles.btnSecondary}>
Reserve Table
</Link>
</div>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,230 @@
.section {
padding: 80px 24px;
background: #ffffff;
}
.container {
max-width: 900px;
margin: 0 auto;
}
.inner {
background: linear-gradient(135deg, #1b4332 0%, #2d6a4f 60%, #1b4332 100%);
border-radius: 28px;
padding: 48px 64px;
text-align: center;
position: relative;
overflow: hidden;
}
/* Background dots */
.inner::before {
content: '';
position: absolute;
inset: 0;
background-image: radial-gradient(rgba(255,255,255,0.04) 1px, transparent 1px);
background-size: 28px 28px;
pointer-events: none;
}
/* Gold glow */
.inner::after {
content: '';
position: absolute;
bottom: -80px;
right: -80px;
width: 300px;
height: 300px;
background: radial-gradient(circle, rgba(212, 160, 23, 0.2) 0%, transparent 70%);
border-radius: 50%;
pointer-events: none;
}
.decorLeaf {
font-size: 2.5rem;
display: block;
margin-bottom: 16px;
opacity: 0.7;
position: relative;
z-index: 1;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
position: relative;
z-index: 1;
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(1.875rem, 3.5vw, 2.75rem);
font-weight: 700;
color: #ffffff;
line-height: 1.2;
margin: 0 0 20px;
position: relative;
z-index: 1;
}
.title em {
font-style: italic;
color: #d4a017;
}
.body {
font-family: var(--font-inter), sans-serif;
font-size: 1rem;
color: rgba(255, 255, 255, 0.78);
line-height: 1.75;
max-width: 560px;
margin: 0 auto 40px;
position: relative;
z-index: 1;
}
.actions {
display: flex;
justify-content: center;
gap: 16px;
flex-wrap: wrap;
margin-bottom: 0;
position: relative;
z-index: 1;
}
.btnPrimary {
display: inline-block;
background: #d4a017;
color: #1b4332;
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
font-weight: 700;
padding: 14px 32px;
border-radius: 50px;
text-decoration: none;
transition: background 0.2s, transform 0.2s;
letter-spacing: 0.02em;
}
.btnPrimary:hover {
background: #e8b520;
transform: translateY(-2px);
}
.btnSecondary {
display: inline-block;
background: transparent;
color: #ffffff;
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
font-weight: 600;
padding: 14px 32px;
border-radius: 50px;
text-decoration: none;
border: 2px solid rgba(255, 255, 255, 0.4);
transition: border-color 0.2s, background 0.2s, transform 0.2s;
letter-spacing: 0.02em;
}
.btnSecondary:hover {
border-color: #ffffff;
background: rgba(255, 255, 255, 0.08);
transform: translateY(-2px);
}
/* Hours strip */
.hours {
display: flex;
justify-content: center;
align-items: center;
gap: 32px;
padding-top: 40px;
border-top: 1px solid rgba(255, 255, 255, 0.15);
position: relative;
z-index: 1;
flex-wrap: wrap;
}
.hourItem {
display: flex;
align-items: center;
gap: 10px;
}
.hourIcon {
font-size: 1.25rem;
opacity: 0.7;
}
.hourItem strong {
display: block;
font-family: var(--font-inter), sans-serif;
font-size: 0.8125rem;
font-weight: 600;
color: rgba(255, 255, 255, 0.9);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.hourItem span {
font-family: var(--font-inter), sans-serif;
font-size: 0.8125rem;
color: rgba(255, 255, 255, 0.6);
}
.hourDivider {
width: 1px;
height: 36px;
background: rgba(255, 255, 255, 0.15);
}
@media (max-width: 768px) {
.inner {
padding: 48px 28px;
}
.hourDivider {
display: none;
}
.hours {
gap: 20px;
}
}
@media (max-width: 480px) {
.section {
padding: 80px 16px;
}
.actions {
flex-direction: column;
align-items: center;
}
.btnPrimary,
.btnSecondary {
width: 100%;
max-width: 280px;
text-align: center;
}
}
@media (max-width: 767px) {
.section {
padding: 40px 24px;
}
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}

View File

@ -0,0 +1,59 @@
import Image from 'next/image';
import Link from 'next/link';
import styles from './AboutCards.module.css';
const cards = [
{
image: '/images/south-indian-thali.png',
title: 'Dish Heritage',
description: 'Every dish is built on recipes passed down through generations, delivered with fresh modern flair.',
link: '/menu',
variant: 'cardVariant1',
},
{
image: '/images/crispy-masala.png',
title: 'Spices Sourced with Care',
description: 'We use hand-selected South Indian spices that balance warmth, earthiness, and bright citrus notes.',
link: '/menu',
variant: 'cardVariant2',
},
{
image: '/images/hero-dosa.png',
title: 'Daily Fresh, Always Vibrant',
description: 'Our kitchen begins before dawn to make batter, chutneys, and curries from scratch every single day.',
link: '/menu',
variant: 'cardVariant3',
},
];
export default function AboutCards() {
return (
<section className={styles.section}>
<div className={styles.container}>
<div className={styles.grid}>
{cards.map((card, i) => (
<div key={i} className={`${styles.cardWrapper} ${styles[card.variant]}`}>
<div className={styles.imageContainer}>
<Image
src={card.image}
alt={card.title}
fill
style={{ objectFit: 'cover' }}
/>
<div className={styles.cardOverlay} />
</div>
<div className={styles.contentContainer}>
<span className={styles.cardLabel}>Our Promise</span>
<h3 className={styles.title}>{card.title}</h3>
<p className={styles.description}>{card.description}</p>
<Link href={card.link} className={styles.readMore}>
Explore Menu
</Link>
</div>
</div>
))}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,167 @@
.section {
padding: 80px 0;
background: #f4fbf3;
}
.container {
max-width: 1300px;
margin: 0 auto;
padding: 0 1rem;
}
.grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 30px;
}
.cardWrapper {
position: relative;
overflow: hidden;
min-height: 420px;
border-radius: 32px;
box-shadow: 0 22px 60px rgba(15, 79, 47, 0.12);
transition: transform 0.35s ease, box-shadow 0.35s ease;
}
.cardWrapper:hover {
transform: translateY(-10px);
box-shadow: 0 28px 70px rgba(15, 79, 47, 0.18);
}
.imageContainer {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.imageContainer > span {
position: absolute !important;
inset: 0;
}
.cardOverlay {
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(15, 79, 47, 0.72), rgba(15, 79, 47, 0.5));
mix-blend-mode: multiply;
}
.contentContainer {
position: relative;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
gap: 18px;
min-height: auto;
width: min(85%, 380px);
margin: 36px auto;
padding: 48px 28px;
background: rgba(15, 79, 47, 0.72);
border-radius: 28px;
border: 1px solid rgba(255, 255, 255, 0.24);
box-shadow: 0 20px 60px rgba(15, 79, 47, 0.12);
}
.contentContainer::before {
content: '';
width: 64px;
height: 3px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.9);
display: block;
margin-bottom: 12px;
}
.cardLabel {
font-family: var(--font-inter), sans-serif;
font-size: 0.78rem;
letter-spacing: 0.18em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.85);
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(1.65rem, 2.3vw, 2.2rem);
font-weight: 700;
color: #ffffff;
margin: 0;
line-height: 1.1;
}
.description {
max-width: 320px;
font-family: var(--font-inter), sans-serif;
font-size: 1rem;
color: rgba(255, 255, 255, 0.92);
line-height: 1.8;
margin: 0;
}
.readMore {
font-family: var(--font-inter), sans-serif;
font-size: 0.95rem;
font-weight: 700;
color: rgba(255, 255, 255, 0.95);
background: rgba(27, 67, 50, 0.96);
padding: 12px 26px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.8);
text-decoration: none;
transition: transform 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.cardWrapper:hover .readMore {
background: #ffffff;
color: #1b4332;
transform: translateY(-2px);
}
.cardVariant1 .cardOverlay {
background: linear-gradient(180deg, rgba(31, 87, 49, 0.72), rgba(31, 87, 49, 0.55));
}
.cardVariant2 .cardOverlay {
background: linear-gradient(180deg, rgba(23, 82, 45, 0.76), rgba(23, 82, 45, 0.5));
}
.cardVariant3 .cardOverlay {
background: linear-gradient(180deg, rgba(15, 79, 47, 0.78), rgba(15, 79, 47, 0.52));
}
.cardVariant2 .contentContainer {
padding-top: 34px;
padding-bottom: 44px;
}
.cardVariant3 .contentContainer {
justify-content: center;
}
@media (max-width: 1100px) {
.grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 760px) {
.grid {
grid-template-columns: 1fr;
}
.cardWrapper {
min-height: 360px;
}
}
@media (max-width: 767px) {
.section {
padding: 60px 16px;
}
}

View File

@ -0,0 +1,94 @@
'use client';
import { useState } from 'react';
import Image from 'next/image';
import styles from './AboutFAQ.module.css';
const faqs = [
{
q: 'Is your restaurant completely vegetarian?',
a: 'Yes — My Dosa Place is 100% vegetarian and always has been. We serve no meat, poultry, or seafood of any kind. Our kitchen is entirely lacto-vegetarian, and we also accommodate vegan guests by preparing dishes without dairy upon request.',
},
{
q: 'Do you make your batter fresh daily?',
a: 'Every single day. Our kitchen team arrives at 5 AM to begin stone-grinding the rice and lentils that form the base of our dosa and idli batters. After grinding, the batter ferments naturally for 12 hours at room temperature before service. We never use commercial batter mixes.',
},
{
q: 'Can you accommodate food allergies and intolerances?',
a: 'We do our best to accommodate common allergies and intolerances. Please inform our staff when you arrive, and we will guide you through safe menu choices and speak with our kitchen team. While we take every precaution, please note that our kitchen handles common allergens including peanuts, tree nuts, and gluten-containing ingredients.',
},
// {
// q: 'Do you take reservations, or is it walk-in only?',
// a: 'We welcome both! Walk-ins are always welcome during opening hours, and we typically accommodate groups of four or fewer quickly. For larger parties (five or more), a reservation is strongly recommended, especially on weekends. You can reserve a table online, by phone, or by visiting us in person.',
// },
// {
// q: 'Where do your ingredients come from?',
// a: 'We source produce from local Ontario farms wherever the seasons allow. Specialty South Indian ingredients — specific lentils, tamarind, curry leaves, and select spices — are imported directly from trusted suppliers in Tamil Nadu and Kerala to maintain authenticity. Every ingredient is inspected by our head chef before use.',
// },
// {
// q: 'Do you offer catering or private dining for events?',
// a: 'Yes — we offer catering for corporate lunches, cultural celebrations, and private family events. Our Royal South Indian Thali package is especially popular for group dining. Contact us directly via phone or our contact form to discuss your event size, dietary requirements, and menu preferences.',
// },
];
export default function AboutFAQ() {
const [openIndex, setOpenIndex] = useState(0);
return (
<section className={styles.section}>
<div className={styles.container}>
{/* Left — Image */}
<div className={styles.imageCol}>
<div className={styles.imageWrapper}>
<Image
src="/images/south-indian-thali.png"
alt="South Indian Thali at My Dosa Place"
fill
style={{ objectFit: 'cover' }}
/>
<div className={styles.imageBadge}>
<span className={styles.badgeNum}>6+</span>
<span className={styles.badgeLabel}>Years of<br />Trust</span>
</div>
<div className={styles.imageAccent} />
</div>
</div>
{/* Right — Header + Accordion */}
<div className={styles.rightCol}>
<div className={styles.header}>
<span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}></span> FAQs
</span>
<h2 className={styles.title}>
Frequently Asked <em>Questions</em>
</h2>
<p className={styles.lead}>
Can't find your answer here? We're always happy to chat call us or drop by during service hours.
</p>
</div>
<div className={styles.accordion}>
{faqs.map((faq, i) => (
<div
key={i}
className={`${styles.item} ${openIndex === i ? styles.open : ''}`}
>
<button
className={styles.question}
onClick={() => setOpenIndex(openIndex === i ? -1 : i)}
aria-expanded={openIndex === i}
>
<span>{faq.q}</span>
<span className={styles.icon}>{openIndex === i ? '' : '+'}</span>
</button>
<div className={styles.answerWrapper}>
<p className={styles.answer}>{faq.a}</p>
</div>
</div>
))}
</div>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,264 @@
.section {
padding: 80px 24px;
background: #f9fafb;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 80px;
align-items: center;
}
/* ── Left Image Column ── */
.imageCol {
position: relative;
}
.imageWrapper {
position: relative;
width: 100%;
height: 580px;
border-radius: 32px;
overflow: hidden;
box-shadow: 0 30px 70px rgba(0, 0, 0, 0.15);
}
/* Decorative accent ring behind image */
.imageAccent {
position: absolute;
inset: -12px;
border-radius: 40px;
border: 2px dashed rgba(212, 160, 23, 0.35);
z-index: -1;
pointer-events: none;
}
/* Badge overlaying bottom-left of image */
.imageBadge {
position: absolute;
bottom: 28px;
left: 28px;
background: #1b4332;
border-radius: 18px;
padding: 16px 22px;
display: flex;
align-items: center;
gap: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
z-index: 10;
}
.badgeNum {
font-family: var(--font-playfair), Georgia, serif;
font-size: 2rem;
font-weight: 800;
color: #d4a017;
line-height: 1;
}
.badgeLabel {
font-family: var(--font-inter), sans-serif;
font-size: 0.78rem;
color: rgba(255, 255, 255, 0.8);
line-height: 1.4;
text-transform: uppercase;
letter-spacing: 0.04em;
}
/* ── Right Column ── */
.rightCol {
display: flex;
flex-direction: column;
gap: 40px;
}
.header {
display: flex;
flex-direction: column;
gap: 0;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 12px;
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(1.875rem, 3vw, 2.5rem);
font-weight: 700;
color: #1b4332;
margin: 0 0 16px;
line-height: 1.2;
}
.title em {
font-style: italic;
color: #d4a017;
}
.lead {
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
color: #6b7280;
line-height: 1.7;
margin: 0;
}
/* Accordion */
.accordion {
display: flex;
flex-direction: column;
gap: 12px;
}
.item {
background: #ffffff;
border-radius: 20px;
border: 1px solid transparent;
overflow: hidden;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.03);
}
.item:hover {
transform: translateX(4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.06);
}
.open {
border-color: rgba(212, 160, 23, 0.3) !important;
box-shadow: 0 10px 30px rgba(27, 67, 50, 0.08);
transform: translateX(8px);
}
.open:hover {
transform: translateX(8px);
}
.question {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
padding: 20px 28px;
background: transparent;
border: none;
cursor: pointer;
text-align: left;
transition: all 0.2s;
}
.question:hover {
background: rgba(249, 250, 251, 0.5);
}
.open .question {
background: linear-gradient(90deg, #f0faf4 0%, transparent 100%);
}
.question span:first-child {
font-family: var(--font-inter), sans-serif;
font-size: 1rem;
font-weight: 700;
color: #1b4332;
line-height: 1.4;
flex: 1;
}
.open .question span:first-child {
color: #d4a017;
}
.icon {
width: 36px;
height: 36px;
background: #f3f4f6;
border-radius: 50%;
display: flex !important;
align-items: center;
justify-content: center;
font-size: 1.4rem;
font-weight: 300;
color: #1b4332;
flex-shrink: 0;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
line-height: 1;
}
.open .icon {
background: #1b4332;
color: #ffffff;
}
/* Answer — CSS animation */
.answerWrapper {
max-height: 0;
overflow: hidden;
transition: max-height 0.35s ease;
}
.open .answerWrapper {
max-height: 400px;
}
.answer {
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
color: #4b5563;
line-height: 1.8;
padding: 0 28px 20px;
margin: 0;
}
/* ── Responsive ── */
@media (max-width: 1024px) {
.container {
grid-template-columns: 1fr;
gap: 56px;
}
.imageWrapper {
height: 400px;
}
}
@media (max-width: 640px) {
.section {
padding: 80px 20px;
}
.question {
padding: 16px 20px;
}
.answer {
padding: 0 20px 18px;
}
.question span:first-child {
font-size: 0.9375rem;
}
}
@media (max-width: 767px) {
.section {
padding: 40px 24px;
}
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}

View File

@ -0,0 +1,84 @@
import Image from 'next/image';
import Link from 'next/link';
import styles from './AboutStory.module.css';
export default function AboutStory() {
return (
<section className={styles.section}>
<div className={styles.container}>
{/* Image Column */}
<div className={styles.imageCol}>
<div className={styles.imageWrapper}>
<div className={styles.imagePrimary}>
<Image
src="/images/story-heritage.png"
alt="Our heritage"
fill
style={{ objectFit: 'cover' }}
/>
</div>
<div className={styles.imageAccent}>
<Image
src="/images/medu-vada.png"
alt="Authentic dishes"
fill
style={{ objectFit: 'cover' }}
/>
</div>
<div className={styles.statBadge}>
<span className={styles.statNumber}>25+</span>
<span className={styles.statLabel}>Years of<br />Tradition</span>
</div>
</div>
</div>
{/* Text Column */}
<div className={styles.textCol}>
<span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}></span> About Our Restaurant
</span>
<h2 className={styles.title}>
A Journey from <em>Tamil Nadu</em> to Your Table
</h2>
<p className={styles.lead}>
My Dosa Place was born from a simple promise to bring the soul of South Indian
home cooking to every guest, prepared fresh with ingredients sourced from trusted
local and imported suppliers.
</p>
<p className={styles.body}>
Our founder Priya Nair grew up watching her grandmother perfect the art of
fermentation the ancient science behind every crispy, golden dosa.
</p>
{/* <p className={styles.body}>
We are proudly 100% vegetarian and use no artificial additives. Our menu
celebrates the diversity of South Indian regional cooking from Chettinad to
Kerala, from Tamil Nadu to Karnataka served with the hospitality that makes
every meal feel like a homecoming.
</p> */}
<div className={styles.pillars}>
<div className={styles.pillar}>
<div className={styles.pillarIcon}>🌿</div>
<div>
<strong>100% Vegetarian</strong>
<span>Pure plant-based ingredients, always fresh</span>
</div>
</div>
<div className={styles.pillar}>
<div className={styles.pillarIcon}>🏆</div>
<div>
<strong>Award-Winning Recipes</strong>
<span>Recognized by the Waterloo Culinary Guild</span>
</div>
</div>
</div>
<Link href="/contact" className={styles.cta}>
Get in Touch
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</Link>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,278 @@
.section {
padding: 80px 24px;
background: #ffffff;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 80px;
align-items: center;
}
/* Image Column */
.imageCol {
position: relative;
}
.imageWrapper {
position: relative;
height: 560px;
}
.imagePrimary {
position: absolute;
top: 0;
left: 0;
width: 78%;
height: 85%;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(27, 67, 50, 0.18);
}
.imageAccent {
position: absolute;
bottom: 0;
right: 0;
width: 52%;
height: 45%;
border-radius: 16px;
overflow: hidden;
border: 5px solid #ffffff;
box-shadow: 0 12px 40px rgba(27, 67, 50, 0.15);
}
.statBadge {
position: absolute;
top: 50%;
right: 0;
transform: translate(20px, -50%);
background: #d4a017;
border-radius: 14px;
padding: 20px 22px;
text-align: center;
box-shadow: 0 8px 30px rgba(212, 160, 23, 0.35);
z-index: 3;
}
.statNumber {
display: block;
font-family: var(--font-playfair), Georgia, serif;
font-size: 2.25rem;
font-weight: 700;
color: #ffffff;
line-height: 1;
}
.statLabel {
display: block;
font-family: var(--font-inter), sans-serif;
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.9);
margin-top: 4px;
text-transform: uppercase;
letter-spacing: 0.05em;
line-height: 1.4;
}
/* Text Column */
.textCol {
display: flex;
flex-direction: column;
gap: 0;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(1.875rem, 3vw, 2.625rem);
font-weight: 700;
color: #1b4332;
line-height: 1.2;
margin: 0 0 24px;
}
.title em {
font-style: italic;
color: #d4a017;
}
.lead {
font-family: var(--font-inter), sans-serif;
font-size: 1.05rem;
color: #374151;
line-height: 1.75;
font-weight: 500;
margin: 0 0 16px;
}
.body {
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
color: #6b7280;
line-height: 1.8;
margin: 0 0 16px;
}
.pillars {
display: flex;
flex-direction: column;
gap: 16px;
/* margin-top: 32px; */
padding-top: 32px;
border-top: 1px solid #e5e7eb;
}
.pillar {
display: flex;
align-items: flex-start;
gap: 14px;
}
.pillarIcon {
width: 56px;
height: 56px;
background: #014d33;
border-radius: 53% 47% 33% 67% / 60% 41% 59% 40%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
flex-shrink: 0;
position: relative;
z-index: 1;
}
.pillarIcon::before {
content: '';
position: absolute;
top: -6px;
left: -8px;
width: 16px;
height: 16px;
background-image: radial-gradient(circle 1.5px at 8px 2px, #014d33 100%, transparent),
radial-gradient(circle 1px at 2px 8px, #014d33 100%, transparent),
radial-gradient(circle 1px at 14px 14px, #014d33 100%, transparent);
z-index: -1;
opacity: 0.8;
}
.pillarIcon::after {
content: '';
position: absolute;
top: -3px;
right: -5px;
width: 24px;
height: 24px;
border-radius: 50%;
border: 1.5px dashed #014d33;
clip-path: polygon(50% 0, 100% 0, 100% 100%, 50% 100%);
transform: rotate(45deg);
z-index: -1;
opacity: 0.5;
}
.pillar strong {
display: block;
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
font-weight: 600;
color: #1b4332;
margin-bottom: 2px;
}
.pillar span {
font-family: var(--font-inter), sans-serif;
font-size: 0.85rem;
color: #6b7280;
}
@media (max-width: 1024px) {
.container {
grid-template-columns: 1fr;
gap: 60px;
}
.imageWrapper {
height: 420px;
}
.statBadge {
transform: translate(0, -50%);
right: 24px;
}
}
@media (max-width: 640px) {
.section {
padding: 80px 20px;
}
.imageWrapper {
height: 320px;
}
.imagePrimary {
width: 75%;
height: 80%;
}
.imageAccent {
width: 55%;
height: 42%;
}
}
@media (max-width: 767px) {
.section {
padding: 40px 24px;
}
}
/* CTA */
.cta {
display: inline-flex;
align-items: center;
gap: 8px;
background: #1b4332;
color: #ffffff;
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
font-weight: 600;
padding: 14px 32px;
border-radius: 50px;
text-decoration: none;
align-self: flex-start;
transition: background 0.2s, transform 0.2s;
margin-top: 20px;
}
.cta:hover {
background: #2d6a4f;
transform: translateY(-2px);
}
.cta svg {
transition: transform 0.2s;
}
.cta:hover svg {
transform: translateX(4px);
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}

View File

@ -0,0 +1,108 @@
'use client';
import { useState, useEffect } from 'react';
import styles from './AboutTestimonial.module.css';
function getReviewText(review) {
return review.text || review.description || review.snippet || review.review_text || review.body || review.content || '';
}
function formatReviews(rawReviews) {
return rawReviews
.filter((review) => {
const text = getReviewText(review);
return text && text.trim().length > 0 && review.rating >= 4;
})
.map((review) => ({
quote: getReviewText(review),
name: review.author_name || review.user?.name || 'Customer',
role: review.relative_time_description || 'Google Review',
rating: Math.round(review.rating || 5),
}));
}
export default function AboutTestimonial() {
const [active, setActive] = useState(0);
const [reviews, setReviews] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function loadReviews() {
try {
const res = await fetch('/api/reviews');
if (!res.ok) throw new Error('Failed to fetch reviews');
const data = await res.json();
const formatted = formatReviews(data.reviews || []);
setReviews(formatted);
} catch (error) {
console.error('Failed to load reviews:', error);
setReviews([]);
} finally {
setLoading(false);
}
}
loadReviews();
}, []);
const testimonials = reviews;
useEffect(() => {
if (!testimonials.length) return;
const timer = setInterval(() => {
setActive((prev) => (prev + 1) % testimonials.length);
}, 4000);
return () => clearInterval(timer);
}, [testimonials.length]);
const t = testimonials.length ? testimonials[active % testimonials.length] : null;
return (
<section className={styles.section}>
<div className={styles.container}>
<div className={styles.header}>
<span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}>💬</span> What Guests Say
</span>
<h2 className={styles.title}>
Stories from Our <em>Table</em>
</h2>
</div>
<div className={styles.testimonialArea}>
{loading && <p className={styles.loadingText}>Loading Google reviews...</p>}
{!loading && !testimonials.length && (
<p className={styles.loadingText}>No Google reviews available right now.</p>
)}
{!loading && t && (
<>
<div className={styles.quoteBox} key={active}>
<div className={styles.quoteIcon}>&ldquo;</div>
<blockquote className={styles.quote}>{t.quote}</blockquote>
<div className={styles.author}>
<div className={styles.authorAvatar}>{t.name.charAt(0)}</div>
<div className={styles.authorInfo}>
<strong>{t.name}</strong>
<span>{t.role}</span>
</div>
{/* <div className={styles.stars}>{'★'.repeat(t.rating)}</div> */}
</div>
</div>
<div className={styles.dots}>
{testimonials.map((_, i) => (
<button
key={i}
className={`${styles.dot} ${i === active ? styles.dotActive : ''}`}
onClick={() => setActive(i)}
aria-label={`Go to testimonial ${i + 1}`}
/>
))}
</div>
</>
)}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,222 @@
.section {
padding: 80px 24px;
background: #1b4332;
position: relative;
overflow: hidden;
}
.section::before {
content: '';
position: absolute;
top: -100px;
right: -100px;
width: 500px;
height: 500px;
border-radius: 50%;
background: radial-gradient(circle, rgba(212, 160, 23, 0.1) 0%, transparent 70%);
pointer-events: none;
}
.container {
max-width: 1100px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 56px;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 12px;
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(1.875rem, 3vw, 2.5rem);
font-weight: 700;
color: #ffffff;
margin: 0;
line-height: 1.2;
}
.title em {
font-style: italic;
color: #d4a017;
}
/* Layout */
.testimonialArea {
display: flex;
flex-direction: column;
gap: 40px;
align-items: center;
position: relative;
z-index: 2;
}
/* Quote Box (Glassmorphism) — fade-in on each slide */
.quoteBox {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 30px;
padding: 60px 80px;
position: relative;
max-width: 900px;
width: 100%;
text-align: center;
box-shadow: 0 30px 60px rgba(0, 0, 0, 0.3);
animation: fadeSlide 0.5s ease forwards;
}
@keyframes fadeSlide {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.quoteBox:hover {
transform: translateY(-5px);
box-shadow: 0 40px 80px rgba(0, 0, 0, 0.4);
}
.quoteIcon {
font-family: Georgia, serif;
font-size: 8rem;
line-height: 0.5;
color: #d4a017;
opacity: 0.3;
margin-bottom: 30px;
display: inline-block;
background: linear-gradient(135deg, #d4a017 0%, #ffdf73 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.quote {
font-family: var(--font-playfair), Georgia, serif;
font-size: 1.4rem;
font-style: italic;
color: #ffffff;
line-height: 1.8;
margin: 0 0 40px;
}
.author {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
}
.authorAvatar {
width: 56px;
height: 56px;
background: linear-gradient(135deg, #d4a017 0%, #e8b520 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-playfair), Georgia, serif;
font-size: 1.5rem;
font-weight: 700;
color: #1b4332;
flex-shrink: 0;
box-shadow: 0 10px 20px rgba(212, 160, 23, 0.3);
}
.authorInfo {
text-align: left;
}
.authorInfo strong {
display: block;
font-family: var(--font-inter), sans-serif;
font-size: 1.05rem;
font-weight: 700;
color: #ffffff;
}
.authorInfo span {
font-family: var(--font-inter), sans-serif;
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.6);
}
.stars {
font-size: 1.1rem;
color: #d4a017;
letter-spacing: 3px;
margin-top: 8px;
}
/* Dot Indicators */
.dots {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
border: none;
background: rgba(255, 255, 255, 0.25);
cursor: pointer;
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
padding: 0;
}
.dot:hover {
background: rgba(212, 160, 23, 0.6);
transform: scale(1.2);
}
.dotActive {
background: #d4a017 !important;
width: 28px;
border-radius: 5px;
transform: scale(1) !important;
}
@media (max-width: 640px) {
.section {
padding: 80px 20px;
}
.quoteBox {
padding: 32px 24px;
}
.quote {
font-size: 1rem;
}
}
@media (max-width: 767px) {
.section {
padding: 40px 24px;
}
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}

View File

@ -0,0 +1,70 @@
import styles from "./WhyChooseUs.module.css";
const features = [
{
icon: "🌿",
title: "Fresh Ingredients",
description:
"We use only premium quality vegetables, spices, and fresh ingredients every day."
},
{
icon: "🍽️",
title: "Authentic Recipes",
description:
"Traditional South Indian recipes prepared with care and authentic techniques."
},
{
icon: "⭐",
title: "Customer Satisfaction",
description:
"Serving delicious food with excellent hospitality and memorable dining experiences."
}
];
export default function WhyChooseUs() {
return (
<section className={styles.section}>
<div className={styles.container}>
<div className={styles.left}>
<span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}>🌟</span> Why Choose Us
</span>
<h2 className={styles.title}>
Authentic South Indian <em>Experience</em>
</h2>
<p className={styles.description}>
From traditional dosas to flavourful meals, we bring the rich taste
of South India to your table using fresh ingredients and time-tested recipes.
</p>
<div className={styles.features}>
{features.map((item, index) => (
<div key={index} className={styles.feature}>
<div className={styles.iconBox}>
{item.icon}
</div>
<div>
<h4>{item.title}</h4>
<p>{item.description}</p>
</div>
</div>
))}
</div>
</div>
<div className={styles.right}>
<img
src="/images/hero-dosa.png"
alt="Restaurant"
/>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,146 @@
.section {
padding: 80px 0;
background: #fff;
}
.container {
width: 90%;
max-width: 1300px;
margin: auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 60px;
align-items: center;
}
.subtitle {
color: #d4a017;
font-size: 18px;
font-weight: 600;
margin-bottom: 10px;
display: block;
}
.title {
font-size: 48px;
line-height: 1.2;
color: #014d33;
margin-bottom: 20px;
}
.description {
color: #666;
line-height: 1.8;
margin-bottom: 40px;
}
.features {
display: flex;
flex-direction: column;
gap: 25px;
}
.feature {
display: flex;
gap: 18px;
align-items: flex-start;
}
.iconBox {
width: 60px;
height: 60px;
background: #014d33;
color: white;
border-radius: 53% 47% 33% 67% / 60% 41% 59% 40%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
flex-shrink: 0;
position: relative;
z-index: 1;
}
.iconBox::before {
content: '';
position: absolute;
top: -8px;
left: -10px;
width: 20px;
height: 20px;
background-image: radial-gradient(circle 2px at 10px 2px, #014d33 100%, transparent),
radial-gradient(circle 1.5px at 2px 10px, #014d33 100%, transparent),
radial-gradient(circle 1px at 16px 16px, #014d33 100%, transparent);
z-index: -1;
opacity: 0.8;
}
.iconBox::after {
content: '';
position: absolute;
top: -4px;
right: -6px;
width: 30px;
height: 30px;
border-radius: 50%;
border: 2px dashed #014d33;
clip-path: polygon(50% 0, 100% 0, 100% 100%, 50% 100%);
transform: rotate(45deg);
z-index: -1;
opacity: 0.5;
}
.feature h4 {
margin-bottom: 8px;
color: #111;
font-size: 20px;
}
.feature p {
color: #666;
line-height: 1.7;
}
.right img {
width: 100%;
border-radius: 20px;
display: block;
}
@media (max-width: 991px) {
.container {
grid-template-columns: 1fr;
}
.title {
font-size: 36px;
}
}
@media (max-width: 767px) {
.section {
padding: 40px 0;
}
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.title em {
font-style: italic;
color: #d4a017;
}

View File

@ -0,0 +1,139 @@
.hero {
position: relative;
min-height: 500px;
display: flex;
align-items: center;
background: linear-gradient(135deg, #1b4332 0%, #2d6a4f 55%, #1b4332 100%);
overflow: hidden;
}
.hero::before {
content: '';
position: absolute;
inset: 0;
background-image: url('/images/services-hero-bg.jpg');
background-size: cover;
background-position: center;
opacity: 0.15;
}
/* decorative circle glow */
.hero::after {
content: '';
position: absolute;
right: 10%;
top: 50%;
transform: translateY(-50%);
width: 520px;
height: 520px;
border-radius: 50%;
background: radial-gradient(circle, rgba(212,160,23,0.13) 0%, transparent 68%);
pointer-events: none;
}
.overlay {
position: absolute;
inset: 0;
background: linear-gradient(100deg, rgba(27,67,50,0.88) 0%, rgba(27,67,50,0.38) 100%);
}
.content {
position: relative;
z-index: 2;
max-width: 1200px;
margin: 0 auto;
padding: 110px 24px 88px;
}
.eyebrow {
display: inline-block;
font-family: var(--font-cursive), cursive;
font-size: 1.25rem;
color: #d4a017;
margin-bottom: 16px;
letter-spacing: 0.02em;
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(2.5rem, 5vw, 3.75rem);
font-weight: 700;
color: #ffffff;
line-height: 1.15;
margin: 0 0 20px;
max-width: 580px;
}
.title em {
font-style: italic;
color: #d4a017;
}
.subtitle {
font-family: var(--font-inter), sans-serif;
font-size: 1.05rem;
color: rgba(255,255,255,0.8);
line-height: 1.75;
max-width: 540px;
margin: 0 0 36px;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 10px;
font-family: var(--font-inter), sans-serif;
font-size: 0.875rem;
color: rgba(255,255,255,0.55);
}
.breadcrumb a {
color: rgba(255,255,255,0.7);
text-decoration: none;
transition: color 0.2s;
}
.breadcrumb a:hover { color: #d4a017; }
.breadcrumb span:last-child { color: #d4a017; }
/* animated scroll indicator */
.scrollHint {
position: absolute;
bottom: 28px;
left: 50%;
transform: translateX(-50%);
z-index: 2;
}
.scrollHint span {
display: block;
width: 24px;
height: 38px;
border: 2px solid rgba(255,255,255,0.3);
border-radius: 12px;
position: relative;
}
.scrollHint span::before {
content: '';
position: absolute;
top: 5px;
left: 50%;
transform: translateX(-50%);
width: 4px;
height: 8px;
background: #d4a017;
border-radius: 2px;
animation: scrollBounce 1.6s ease-in-out infinite;
}
@keyframes scrollBounce {
0%, 100% { transform: translateX(-50%) translateY(0); opacity: 1; }
60% { transform: translateX(-50%) translateY(10px); opacity: 0; }
}
@media (max-width: 768px) {
.content { padding: 100px 20px 72px; }
.title { font-size: 2.25rem; }
.scrollHint { display: none; }
}

View File

@ -0,0 +1,267 @@
'use client';
import { useState } from 'react';
import styles from './MenuSection.module.css';
const categories = [
{
id: 'breakfast',
label: 'Breakfast',
emoji: '🌅',
tagline: 'Start your morning right',
color: '#e8f5e9',
accent: '#014d33',
items: [
'Idly (2pcs)',
'Mini Sambar Idly',
'Thattu Idly (2pcs)',
'Ghee Podi Thattu Idly',
'Mini Ghee Podi Idly',
'Rava Khichdi',
'Sambar Idly (2 pcs)',
'Ghee Pongal',
'Kuzhi Paniyaram (10 pcs)',
'Idly with Paya (2 pcs)',
'Rasam Idly (2 pcs)',
'South Indian Thali (Weekends Only)',
'Poori Masala (2 Nos.)',
'Rasa Vada',
'Paruppu Vada (2 Nos.)',
'Pizza Dosa',
'Thayir Vada / Dhahi Vada',
'Sambar Vada',
],
},
{
id: 'appetizers',
label: 'Appetizers',
emoji: '🍢',
tagline: 'Crispy bites to begin',
color: '#fff8e1',
accent: '#d99c43',
items: [
'Thayir (Dahi) Vada (2 pcs)',
'Rasam Vada (2 pcs)',
'Sambar Vada (2 pcs)',
'Paruppu Vada (2 pcs)',
'Medhu Vada (2 pcs)',
'Chilli Gobi',
'Gobi Manchurian',
'Chilli Idly',
'Kothu Parotta',
'Chilli Paneer',
'Gobi / Paneer 65',
'Chilli Parotta',
],
},
{
id: 'breads',
label: 'Breads',
emoji: '🫓',
tagline: 'Freshly made every day',
color: '#fce4ec',
accent: '#c2185b',
items: [
'Poori Masala',
'Parotta (2 Nos.)',
"Chappathy's (2 Nos.)",
],
},
{
id: 'speciality-dosa',
label: 'Speciality Dosa',
emoji: '🥞',
tagline: 'Crispy, golden, legendary',
color: '#e8f5e9',
accent: '#014d33',
items: [
'Onion Rava Dosa',
'Rava Dosa',
'Set Masala Dosa',
'Paneer Butter Masala Dosa',
'Butter Garlic Dosa',
'Mysore Masala Dosa',
'Onion Rava Masala Dosa',
'Rava Masala Dosa',
'Cheese Dosa',
'Chettinad Masala Dosa',
'Butter Masala Dosa',
],
},
{
id: 'lunch',
label: 'Lunch',
emoji: '🍛',
tagline: 'Hearty midday meals',
color: '#fff3e0',
accent: '#e65100',
items: [
'Sambar Rice',
'Tamarind Rice',
'Bagala Bath (Curd Rice)',
'Rasam',
'Mini Meals',
'Lemon Rice',
'Veg Pulav',
],
},
{
id: 'utthappams',
label: 'Utthappams',
emoji: '🫔',
tagline: 'Thick, soft & flavourful',
color: '#e3f2fd',
accent: '#1565c0',
items: [
'Plain Utthappam',
'Onion Utthappam',
'Onion Chilli Utthappam',
'Mixed Veg Utthappam',
'Ghee Podi Utthappam',
],
},
{
id: 'beverages',
label: 'Beverages',
emoji: '☕',
tagline: 'Cool, warm, refreshing',
color: '#f3e5f5',
accent: '#6a1b9a',
items: [
'Special Madras Coffee',
'Special Chai',
],
},
{
id: 'combos',
label: 'Combos',
emoji: '🎁',
tagline: 'Best value pairings',
color: '#e8f5e9',
accent: '#2e7d32',
items: [
'1 Idly & 1 Vada',
'2 Idly & 1 Vada',
],
},
];
export default function MenuSection() {
const [activeTab, setActiveTab] = useState('breakfast');
const [animating, setAnimating] = useState(false);
const [displayedTab, setDisplayedTab] = useState('breakfast');
const switchTab = (id) => {
if (id === activeTab || animating) return;
setAnimating(true);
setTimeout(() => {
setDisplayedTab(id);
setActiveTab(id);
setAnimating(false);
}, 200);
};
const currentCat = categories.find((c) => c.id === displayedTab);
return (
<section className={styles.section}>
{/* Page header */}
<div className={styles.pageHeader}>
<span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}></span> Explore Our Menu
</span>
<h1 className={styles.pageTitle}>
A Taste of <em>South India</em>
</h1>
<p className={styles.pageSubtitle}>
100% vegetarian · Stone-ground batter · Freshly made daily
</p>
</div>
{/* Mobile tab strip */}
<div className={styles.mobileTabStrip}>
{categories.map((cat) => (
<button
key={cat.id}
className={`${styles.mobileTab} ${activeTab === cat.id ? styles.mobileTabActive : ''}`}
onClick={() => switchTab(cat.id)}
style={activeTab === cat.id ? { borderColor: cat.accent, color: cat.accent } : {}}
>
<span>{cat.emoji}</span>
<span>{cat.label}</span>
</button>
))}
</div>
{/* Main layout */}
<div className={styles.layout}>
{/* Sidebar */}
<aside className={styles.sidebar}>
<p className={styles.sidebarLabel}>Categories</p>
<nav className={styles.tabNav}>
{categories.map((cat) => (
<button
key={cat.id}
className={`${styles.tabBtn} ${activeTab === cat.id ? styles.tabActive : ''}`}
onClick={() => switchTab(cat.id)}
style={activeTab === cat.id ? { background: cat.accent } : {}}
>
<span className={styles.tabEmoji}>{cat.emoji}</span>
<span className={styles.tabText}>
<span className={styles.tabLabel}>{cat.label}</span>
<span className={styles.tabTagline}>{cat.tagline}</span>
</span>
<span className={styles.tabCount}>{cat.items.length}</span>
</button>
))}
</nav>
<div className={styles.sidebarDeco} aria-hidden="true">🍃</div>
</aside>
{/* Content */}
<div className={styles.content}>
{/* Category header */}
<div
className={`${styles.catHeader} ${animating ? styles.fadeOut : styles.fadeIn}`}
style={{ background: currentCat.color, borderLeft: `4px solid ${currentCat.accent}` }}
>
<span className={styles.catEmoji}>{currentCat.emoji}</span>
<div>
<h2 className={styles.catTitle} style={{ color: currentCat.accent }}>
{currentCat.label}
</h2>
<p className={styles.catTagline}>{currentCat.tagline}</p>
</div>
<span className={styles.catCount} style={{ background: currentCat.accent }}>
{currentCat.items.length} items
</span>
</div>
{/* Items grid */}
<div className={`${styles.itemsGrid} ${animating ? styles.fadeOut : styles.fadeIn}`}>
{currentCat.items.map((item, i) => (
<div
key={item}
className={styles.itemCard}
style={{ animationDelay: `${i * 40}ms` }}
>
<div className={styles.itemImageWrapper}>
<img src="/images/south-indian-thali.png" alt={item} className={styles.itemImage} />
</div>
<div className={styles.itemContent}>
<h3 className={styles.itemName}>{item}</h3>
<p className={styles.itemDescription}>Authentic South Indian flavor.</p>
</div>
</div>
))}
</div>
{/* Veg badge */}
{/* <div className={styles.vegBadge}>
<span className={styles.vegDot} />
<span>100% Pure Vegetarian</span>
</div> */}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,414 @@
/* ── Section wrapper ── */
.section {
padding: 80px 20px;
background: #f9faf8;
min-height: 100vh;
}
/* ── Page header ── */
.pageHeader {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
width: 100%;
margin-bottom: 56px;
}
.eyebrow {
display: flex;
width: 100%;
justify-content: center;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.1rem;
color: #d99c43;
margin: 0 auto 10px;
letter-spacing: 0.03em;
}
.pageTitle {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(2rem, 4vw, 3rem);
font-weight: 700;
color: #014d33;
line-height: 1.15;
margin: 0 0 14px;
}
.pageTitle em {
font-style: italic;
color: #d99c43;
}
.pageSubtitle {
font-family: var(--font-inter), sans-serif;
font-size: 1rem;
color: #666;
margin: 0;
letter-spacing: 0.04em;
}
/* ── Mobile tab strip ── */
.mobileTabStrip {
display: none;
overflow-x: auto;
gap: 10px;
padding: 0 4px 16px;
margin-bottom: 24px;
scrollbar-width: none;
}
.mobileTabStrip::-webkit-scrollbar { display: none; }
.mobileTab {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 10px 16px;
border-radius: 14px;
border: 2px solid #e0e0e0;
background: #fff;
font-size: 0.78rem;
font-weight: 600;
color: #666;
white-space: nowrap;
flex-shrink: 0;
transition: all 0.2s ease;
}
.mobileTab span:first-child { font-size: 1.3rem; }
.mobileTabActive {
border-color: #014d33;
background: #014d33;
color: #fff;
}
/* ── Main layout ── */
.layout {
display: grid;
grid-template-columns: 280px 1fr;
gap: 32px;
max-width: 1200px;
margin: 0 auto;
}
/* ── Sidebar ── */
.sidebar {
position: sticky;
top: 100px;
align-self: start;
background: #fff;
border-radius: 24px;
padding: 24px 16px;
box-shadow: 0 8px 30px rgba(0,0,0,0.07);
}
.sidebarLabel {
font-family: var(--font-inter), sans-serif;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: #999;
margin: 0 0 14px 8px;
}
.tabNav {
display: flex;
flex-direction: column;
gap: 6px;
}
.tabBtn {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
padding: 12px 14px;
border-radius: 14px;
border: none;
background: transparent;
text-align: left;
cursor: pointer;
transition: all 0.22s ease;
color: #444;
}
.tabBtn:hover {
background: #f0f7f4;
}
.tabActive {
background: #014d33 !important;
color: #fff;
box-shadow: 0 6px 20px rgba(1,77,51,0.25);
}
.tabEmoji {
font-size: 1.4rem;
flex-shrink: 0;
}
.tabText {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
}
.tabLabel {
font-family: var(--font-inter), sans-serif;
font-size: 0.88rem;
font-weight: 700;
line-height: 1.2;
}
.tabTagline {
font-family: var(--font-inter), sans-serif;
font-size: 0.72rem;
opacity: 0.7;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tabCount {
font-family: var(--font-inter), sans-serif;
font-size: 0.72rem;
font-weight: 700;
background: rgba(255,255,255,0.2);
border-radius: 20px;
padding: 2px 8px;
flex-shrink: 0;
}
.tabBtn:not(.tabActive) .tabCount {
background: #f0f0f0;
color: #666;
}
.sidebarDeco {
text-align: center;
font-size: 2rem;
opacity: 0.3;
margin-top: 20px;
}
/* ── Content area ── */
.content {
display: flex;
flex-direction: column;
gap: 24px;
}
/* ── Category header bar ── */
.catHeader {
display: flex;
align-items: center;
gap: 18px;
padding: 20px 28px;
border-radius: 18px;
border-left: 4px solid #014d33;
}
.catEmoji {
font-size: 2.4rem;
flex-shrink: 0;
}
.catTitle {
font-family: var(--font-playfair), Georgia, serif;
font-size: 1.6rem;
font-weight: 700;
color: #014d33;
margin: 0 0 4px;
line-height: 1.2;
}
.catTagline {
font-family: var(--font-inter), sans-serif;
font-size: 0.88rem;
color: #777;
margin: 0;
}
.catCount {
margin-left: auto;
background: #014d33;
color: #fff;
font-family: var(--font-inter), sans-serif;
font-size: 0.8rem;
font-weight: 700;
padding: 6px 16px;
border-radius: 20px;
flex-shrink: 0;
letter-spacing: 0.04em;
}
/* ── Items grid ── */
.itemsGrid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 65px;
row-gap: 40px;
padding-left: 20px; /* space for overlapping images */
}
.itemCard {
position: relative;
display: flex;
align-items: center;
background: #198754;
border-radius: 16px;
padding: 20px 20px 20px 80px;
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
/* border: 1px solid #3a3a3a; */
transition: all 0.3s ease;
animation: slideUp 0.35s ease both;
min-height: 100px;
}
.itemCard:hover {
transform: translateY(-4px);
box-shadow: 0 12px 28px rgba(0,0,0,0.3);
border-color: #555;
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: translateY(0); }
}
.itemImageWrapper {
position: absolute;
left: -35px;
top: -15px;
width: 100px;
height: 100px;
border-radius: 50%;
border: 4px solid #fff;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
background: #fff;
}
.itemImage {
width: 100%;
height: 100%;
object-fit: cover;
}
.itemContent {
display: flex;
flex-direction: column;
gap: 6px;
width: 100%;
}
.itemName {
font-family: var(--font-inter), sans-serif;
font-size: 1.1rem;
font-weight: 700;
color: #fff;
margin: 0;
line-height: 1.2;
}
.itemDescription {
font-family: var(--font-inter), sans-serif;
font-size: 0.85rem;
color: #fff;
margin: 0;
line-height: 1.4;
}
/* ── Veg badge ── */
.vegBadge {
display: flex;
align-items: center;
gap: 10px;
font-family: var(--font-inter), sans-serif;
font-size: 0.85rem;
font-weight: 600;
color: #014d33;
background: #e8f5e9;
border: 1.5px solid #014d33;
border-radius: 30px;
padding: 10px 20px;
width: fit-content;
}
.vegDot {
width: 14px;
height: 14px;
border-radius: 50%;
border: 2px solid #014d33;
background: #4caf50;
flex-shrink: 0;
}
/* ── Fade animations ── */
.fadeIn {
animation: fadeIn 0.25s ease forwards;
}
.fadeOut {
animation: fadeOut 0.2s ease forwards;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeOut {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(-8px); }
}
/* ── Responsive ── */
@media (max-width: 900px) {
.layout {
grid-template-columns: 1fr;
}
.sidebar {
position: static;
display: none;
}
.mobileTabStrip {
display: flex;
}
}
@media (max-width: 600px) {
.itemsGrid {
grid-template-columns: 1fr;
}
.section {
padding: 80px 16px;
}
.catHeader {
padding: 16px 18px;
flex-wrap: wrap;
gap: 10px;
}
.catCount {
margin-left: 0;
}
}
@media (max-width: 767px) {
.section {
padding: 40px 20px;
}
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}
.title em {
font-style: italic;
color: #d4a017;
}

View File

@ -0,0 +1,65 @@
import Link from 'next/link';
import styles from './ServiceCTA.module.css';
export default function ServiceCTA() {
return (
<section className={styles.section}>
<div className={styles.container}>
<div className={styles.left}>
<span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}></span> Ready to Experience It?
</span>
<h2 className={styles.title}>
Let's Make Your Event <em>Special</em>
</h2>
<p className={styles.body}>
Whether you're craving a crispy masala dosa today or planning a feast for
200 guests next month, we're here. Reach out and we'll take care of the rest.
</p>
<div className={styles.actions}>
<Link href="/contact" className={styles.btnPrimary}>
Contact Us
</Link>
<Link href="/menu" className={styles.btnSecondary}>
View Full Menu
</Link>
</div>
</div>
<div className={styles.right}>
<div className={styles.card}>
<h3 className={styles.cardTitle}>Quick Links</h3>
<ul className={styles.links}>
<li>
<a href="#dine-in">
<span>🍽</span> Dine-In Experience
</a>
</li>
<li>
<a href="#takeout">
<span>🚗</span> Takeout & Online Orders
</a>
</li>
<li>
<a href="#catering">
<span>🎉</span> Catering for Events
</a>
</li>
<li>
<a href="#festival">
<span>🪔</span> Festival & Special Menus
</a>
</li>
</ul>
</div>
<div className={styles.contactCard}>
<p className={styles.contactLabel}>Speak to us directly</p>
<a href="tel:+15191234567" className={styles.phone}>+1 (519) 123-4567</a>
<p className={styles.contactSub}>Mon Sun · 10 AM to 9 PM</p>
</div>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,213 @@
.section {
padding: 80px 24px;
background: #f9fafb;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 420px;
gap: 72px;
align-items: center;
}
/* Left */
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(1.875rem, 2.8vw, 2.625rem);
font-weight: 700;
color: #1b4332;
line-height: 1.2;
margin: 0 0 20px;
}
.title em {
font-style: italic;
color: #2d6a4f;
}
.body {
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
color: #6b7280;
line-height: 1.8;
margin: 0 0 36px;
max-width: 460px;
}
.actions {
display: flex;
gap: 14px;
flex-wrap: wrap;
}
.btnPrimary {
display: inline-block;
background: #1b4332;
color: #ffffff;
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
font-weight: 600;
padding: 13px 30px;
border-radius: 50px;
text-decoration: none;
transition: background 0.2s, transform 0.2s;
}
.btnPrimary:hover {
background: #2d6a4f;
transform: translateY(-2px);
}
.btnSecondary {
display: inline-block;
background: transparent;
color: #1b4332;
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
font-weight: 600;
padding: 13px 30px;
border-radius: 50px;
text-decoration: none;
border: 2px solid #1b4332;
transition: background 0.2s, color 0.2s, transform 0.2s;
}
.btnSecondary:hover {
background: #1b4332;
color: #ffffff;
transform: translateY(-2px);
}
/* Right column */
.right {
display: flex;
flex-direction: column;
gap: 20px;
}
/* Quick links card */
.card {
background: #ffffff;
border: 1.5px solid #e5e7eb;
border-radius: 18px;
padding: 28px 32px;
}
.cardTitle {
font-family: var(--font-inter), sans-serif;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #9ca3af;
margin: 0 0 18px;
}
.links {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.links li a {
display: flex;
align-items: center;
gap: 10px;
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
font-weight: 500;
color: #1b4332;
text-decoration: none;
padding: 10px 12px;
border-radius: 10px;
transition: background 0.15s, color 0.15s;
}
.links li a:hover {
background: #f0faf4;
color: #2d6a4f;
}
.links li a span {
font-size: 1.125rem;
width: 28px;
text-align: center;
}
/* Contact card */
.contactCard {
background: #1b4332;
border-radius: 18px;
padding: 24px 32px;
text-align: center;
}
.contactLabel {
font-family: var(--font-inter), sans-serif;
font-size: 0.8125rem;
color: rgba(255,255,255,0.6);
margin: 0 0 8px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.phone {
display: block;
font-family: var(--font-playfair), Georgia, serif;
font-size: 1.625rem;
font-weight: 700;
color: #d4a017;
text-decoration: none;
margin-bottom: 6px;
transition: color 0.2s;
}
.phone:hover { color: #e8b520; }
.contactSub {
font-family: var(--font-inter), sans-serif;
font-size: 0.8125rem;
color: rgba(255,255,255,0.5);
margin: 0;
}
@media (max-width: 1024px) {
.container {
grid-template-columns: 1fr;
gap: 48px;
}
}
@media (max-width: 640px) {
.section { padding: 80px 20px; }
.actions { flex-direction: column; }
.btnPrimary, .btnSecondary { text-align: center; }
}
@media (max-width: 767px) {
.section {
padding: 40px 24px;
}
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}

View File

@ -0,0 +1,69 @@
import Image from 'next/image';
import Link from 'next/link';
import styles from './ServiceCards.module.css';
const cards = [
{
image: '/images/crispy-masala.png',
title: 'Dine-In Experience',
description: 'Enjoy our cozy, family-friendly atmosphere with freshly prepared South Indian cuisine served to your table.',
link: '/contact',
},
{
image: '/images/south-indian-thali.png',
title: 'Takeout & Online Orders',
description: 'Order your favourite dosas and idlis online — ready for pickup in 1520 minutes, always hot and fresh.',
link: '/menu',
},
{
image: '/images/sambar.png',
title: 'Catering for Events',
description: 'From intimate family gatherings to corporate lunches for hundreds — authentic South Indian flavour, delivered.',
link: '/contact',
},
{
image: '/images/medu-vada.png',
title: 'Festival Special Menus',
description: 'Celebrate Pongal, Onam, Ugadi and more with exclusive seasonal dishes you won\'t find anywhere else.',
link: '/contact',
},
];
export default function ServiceCards() {
return (
<section className={styles.section}>
<div className={styles.container}>
<div className={styles.header}>
<span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}>🍽</span> What We Offer
</span>
<h2 className={styles.title}>
Services Crafted with <em>Care</em>
</h2>
</div>
<div className={styles.grid}>
{cards.map((card, i) => (
<div key={i} className={styles.cardWrapper}>
<div className={styles.imageContainer}>
<Image
src={card.image}
alt={card.title}
fill
style={{ objectFit: 'cover' }}
/>
</div>
<div className={styles.contentContainer}>
<h3 className={styles.cardTitle}>{card.title}</h3>
<p className={styles.description}>{card.description}</p>
<Link href={card.link} className={styles.readMore}>
Read More
</Link>
</div>
</div>
))}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,175 @@
/* ── Section ── */
.section {
padding: 80px 24px;
background: #f9fafb;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
/* Header */
.header {
text-align: center;
margin-bottom: 60px;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 12px;
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(1.875rem, 3vw, 2.5rem);
font-weight: 700;
color: #1b4332;
margin: 0;
line-height: 1.2;
}
.title em {
font-style: italic;
color: #d4a017;
}
/* ── Grid — 2 per row ── */
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 40px;
}
/* ── Card — overlapping image + content ── */
.cardWrapper {
display: flex;
align-items: center;
position: relative;
transition: transform 0.35s ease;
}
.cardWrapper:hover {
transform: translateY(-6px);
}
.imageContainer {
width: 260px;
height: 260px;
border-radius: 36px;
overflow: hidden;
position: relative;
z-index: 2;
flex-shrink: 0;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.12);
transition: box-shadow 0.35s ease;
}
.cardWrapper:hover .imageContainer {
box-shadow: 0 28px 60px rgba(0, 0, 0, 0.18);
}
.contentContainer {
background: #ffffff;
border-radius: 36px;
padding: 44px 36px 44px 64px;
margin-left: -44px;
z-index: 1;
flex-grow: 1;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.06);
min-height: 220px;
display: flex;
flex-direction: column;
justify-content: center;
transition: box-shadow 0.35s ease;
}
.cardWrapper:hover .contentContainer {
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.1);
}
/* Card title */
.cardTitle {
font-family: var(--font-playfair), Georgia, serif;
font-size: 1.4rem;
font-weight: 700;
color: #1b4332;
line-height: 1.3;
margin: 0 0 14px;
}
.description {
font-family: var(--font-inter), sans-serif;
font-size: 0.9rem;
color: #6b7280;
line-height: 1.65;
margin: 0 0 22px;
}
.readMore {
font-family: var(--font-inter), sans-serif;
font-size: 0.9rem;
font-weight: 600;
color: #d4a017;
text-decoration: none;
border-bottom: 1.5px solid #d4a017;
align-self: flex-start;
transition: opacity 0.2s, color 0.2s;
}
.readMore:hover {
color: #1b4332;
border-color: #1b4332;
}
/* ── Responsive ── */
@media (max-width: 1024px) {
.grid {
grid-template-columns: 1fr;
max-width: 640px;
margin: 0 auto;
gap: 48px;
}
}
@media (max-width: 640px) {
.section {
padding: 80px 20px;
}
.cardWrapper {
flex-direction: column;
align-items: stretch;
}
.imageContainer {
width: 100%;
height: 260px;
border-radius: 28px;
margin-bottom: -36px;
}
.contentContainer {
margin-left: 0;
padding: 52px 28px 36px;
border-radius: 28px;
}
}
@media (max-width: 767px) {
.section {
padding: 40px 24px;
}
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}

View File

@ -0,0 +1,139 @@
.hero {
position: relative;
min-height: 500px;
display: flex;
align-items: center;
background: linear-gradient(135deg, #1b4332 0%, #2d6a4f 55%, #1b4332 100%);
overflow: hidden;
}
.hero::before {
content: '';
position: absolute;
inset: 0;
background-image: url('/images/services-hero-bg.jpg');
background-size: cover;
background-position: center;
opacity: 0.15;
}
/* decorative circle glow */
.hero::after {
content: '';
position: absolute;
right: 10%;
top: 50%;
transform: translateY(-50%);
width: 520px;
height: 520px;
border-radius: 50%;
background: radial-gradient(circle, rgba(212,160,23,0.13) 0%, transparent 68%);
pointer-events: none;
}
.overlay {
position: absolute;
inset: 0;
background: linear-gradient(100deg, rgba(27,67,50,0.88) 0%, rgba(27,67,50,0.38) 100%);
}
.content {
position: relative;
z-index: 2;
max-width: 1200px;
margin: 0 auto;
padding: 110px 24px 88px;
}
.eyebrow {
display: inline-block;
font-family: var(--font-cursive), cursive;
font-size: 1.25rem;
color: #d4a017;
margin-bottom: 16px;
letter-spacing: 0.02em;
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(2.5rem, 5vw, 3.75rem);
font-weight: 700;
color: #ffffff;
line-height: 1.15;
margin: 0 0 20px;
max-width: 580px;
}
.title em {
font-style: italic;
color: #d4a017;
}
.subtitle {
font-family: var(--font-inter), sans-serif;
font-size: 1.05rem;
color: rgba(255,255,255,0.8);
line-height: 1.75;
max-width: 540px;
margin: 0 0 36px;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 10px;
font-family: var(--font-inter), sans-serif;
font-size: 0.875rem;
color: rgba(255,255,255,0.55);
}
.breadcrumb a {
color: rgba(255,255,255,0.7);
text-decoration: none;
transition: color 0.2s;
}
.breadcrumb a:hover { color: #d4a017; }
.breadcrumb span:last-child { color: #d4a017; }
/* animated scroll indicator */
.scrollHint {
position: absolute;
bottom: 28px;
left: 50%;
transform: translateX(-50%);
z-index: 2;
}
.scrollHint span {
display: block;
width: 24px;
height: 38px;
border: 2px solid rgba(255,255,255,0.3);
border-radius: 12px;
position: relative;
}
.scrollHint span::before {
content: '';
position: absolute;
top: 5px;
left: 50%;
transform: translateX(-50%);
width: 4px;
height: 8px;
background: #d4a017;
border-radius: 2px;
animation: scrollBounce 1.6s ease-in-out infinite;
}
@keyframes scrollBounce {
0%, 100% { transform: translateX(-50%) translateY(0); opacity: 1; }
60% { transform: translateX(-50%) translateY(10px); opacity: 0; }
}
@media (max-width: 768px) {
.content { padding: 100px 20px 72px; }
.title { font-size: 2.25rem; }
.scrollHint { display: none; }
}

View File

@ -0,0 +1,94 @@
import Image from 'next/image';
import Link from 'next/link';
import styles from './ServiceIntro.module.css';
export default function ServiceIntro() {
return (
<section className={styles.section}>
<div className={styles.container}>
{/* Left — Image Grid */}
<div className={styles.imageCol}>
<div className={styles.imageGrid}>
<div className={styles.gridImg1}>
<Image
src="/images/story-heritage.png"
alt="Authentic South Indian experience at My Dosa Place"
fill
style={{ objectFit: 'cover' }}
/>
</div>
<div className={styles.gridImg2}>
<Image
src="/images/south-indian-thali.png"
alt="Freshly made medu vada and thali"
fill
style={{ objectFit: 'cover' }}
/>
</div>
{/* Floating badge */}
<div className={styles.badge}>
<span className={styles.badgeNum}>25+</span>
<span className={styles.badgeLabel}>Years of<br />Tradition</span>
</div>
{/* Floating Element */}
<div className={styles.floatingElement}>
<div className={styles.dots}></div>
</div>
</div>
</div>
{/* Right — Content */}
<div className={styles.contentCol}>
<span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}></span> Our Services
</span>
<h2 className={styles.title}>
Every Visit, an <em>Authentic</em> South Indian Experience
</h2>
<p className={styles.lead}>
From a quiet weekday lunch to a grand celebration catered at your venue,
My Dosa Place brings the warmth and authenticity of South Indian hospitality
to every occasion.
</p>
<p className={styles.body}>
We are proudly 100% vegetarian every batter stone-ground daily, every
sambar simmered for hours, every chutney prepared fresh each morning.
</p>
<div className={styles.pillars}>
<div className={styles.pillar}>
<div className={styles.pillarIcon}>🍽</div>
<div>
<strong>Dine-In &amp; Takeout</strong>
<span>Fresh food, your way, every day</span>
</div>
</div>
<div className={styles.pillar}>
<div className={styles.pillarIcon}>🎉</div>
<div>
<strong>Catering &amp; Events</strong>
<span>From 10 to 500 guests, we deliver</span>
</div>
</div>
<div className={styles.pillar}>
<div className={styles.pillarIcon}>🪔</div>
<div>
<strong>Festival Menus</strong>
<span>Seasonal specials you won't find anywhere else</span>
</div>
</div>
</div>
{/* <Link href="/contact" className={styles.cta}>
Get in Touch
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</Link> */}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,308 @@
/* ── Section ── */
.section {
padding: 80px 24px;
background: #ffffff;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 80px;
align-items: center;
}
/* ── Left — Image Grid ── */
.imageCol {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.imageGrid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
position: relative;
width: 100%;
}
.gridImg1 {
position: relative;
width: 100%;
height: 440px;
border-radius: 40px 0 40px 40px;
overflow: hidden;
box-shadow: 0 15px 35px rgba(0,0,0,0.12);
transform: translateY(-20px);
}
.gridImg2 {
position: relative;
width: 100%;
height: 380px;
border-radius: 0 40px 40px 40px;
overflow: hidden;
box-shadow: 0 15px 35px rgba(0,0,0,0.12);
transform: translateY(40px);
}
/* Floating stat badge — bottom left */
.badge {
position: absolute;
bottom: 10px;
left: -30px;
z-index: 10;
background: #1b4332;
border-radius: 20px;
padding: 18px 24px;
display: flex;
align-items: center;
gap: 14px;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
}
.badgeNum {
font-family: var(--font-playfair), Georgia, serif;
font-size: 2rem;
font-weight: 800;
color: #d4a017;
line-height: 1;
}
.badgeLabel {
font-family: var(--font-inter), sans-serif;
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.8);
line-height: 1.4;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Decorative dot grid — top right, overlapping */
.floatingElement {
position: absolute;
top: -20px;
right: -20px;
width: 120px;
height: 120px;
z-index: -1;
background-image: radial-gradient(#1b4332 20%, transparent 20%);
background-size: 20px 20px;
opacity: 0.15;
}
/* ── Right — Content Column ── */
.contentCol {
display: flex;
flex-direction: column;
gap: 0;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 14px;
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(1.875rem, 2.8vw, 2.6rem);
font-weight: 700;
color: #1b4332;
line-height: 1.2;
margin: 0 0 20px;
}
.title em {
font-style: italic;
color: #d4a017;
}
.lead {
font-family: var(--font-inter), sans-serif;
font-size: 1rem;
color: #374151;
line-height: 1.8;
margin: 0 0 16px;
font-weight: 500;
}
.body {
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
color: #6b7280;
line-height: 1.8;
margin: 0 0 36px;
}
/* Pillars */
.pillars {
display: flex;
flex-direction: column;
gap: 16px;
margin-bottom: 40px;
}
.pillar {
display: flex;
align-items: center;
gap: 16px;
}
.pillarIcon {
width: 56px;
height: 56px;
background: #014d33;
border-radius: 53% 47% 33% 67% / 60% 41% 59% 40%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
flex-shrink: 0;
position: relative;
z-index: 1;
}
.pillarIcon::before {
content: '';
position: absolute;
top: -6px;
left: -8px;
width: 16px;
height: 16px;
background-image: radial-gradient(circle 1.5px at 8px 2px, #014d33 100%, transparent),
radial-gradient(circle 1px at 2px 8px, #014d33 100%, transparent),
radial-gradient(circle 1px at 14px 14px, #014d33 100%, transparent);
z-index: -1;
opacity: 0.8;
}
.pillarIcon::after {
content: '';
position: absolute;
top: -3px;
right: -5px;
width: 24px;
height: 24px;
border-radius: 50%;
border: 1.5px dashed #014d33;
clip-path: polygon(50% 0, 100% 0, 100% 100%, 50% 100%);
transform: rotate(45deg);
z-index: -1;
opacity: 0.5;
}
.pillar strong {
display: block;
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
font-weight: 700;
color: #1b4332;
}
.pillar span {
font-family: var(--font-inter), sans-serif;
font-size: 0.8125rem;
color: #9ca3af;
}
/* CTA */
.cta {
display: inline-flex;
align-items: center;
gap: 8px;
background: #1b4332;
color: #ffffff;
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
font-weight: 600;
padding: 14px 32px;
border-radius: 50px;
text-decoration: none;
align-self: flex-start;
transition: background 0.2s, transform 0.2s;
}
.cta:hover {
background: #2d6a4f;
transform: translateY(-2px);
}
.cta svg {
transition: transform 0.2s;
}
.cta:hover svg {
transform: translateX(4px);
}
/* ── Responsive ── */
@media (max-width: 1100px) {
.container {
grid-template-columns: 1fr;
gap: 56px;
}
.gridImg1 {
height: 380px;
transform: translateY(0);
}
.gridImg2 {
height: 380px;
transform: translateY(0);
}
.badge {
left: 16px;
bottom: -20px;
}
.floatingElement {
right: 16px;
}
}
@media (max-width: 640px) {
.section {
padding: 80px 20px;
}
.imageGrid {
grid-template-columns: 1fr;
gap: 16px;
}
.gridImg1, .gridImg2 {
height: 300px;
border-radius: 24px;
transform: translateY(0);
}
.floatingElement {
width: 80px;
height: 80px;
top: -10px;
right: 0;
}
}
@media (max-width: 767px) {
.section {
padding: 40px 24px;
}
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}

View File

@ -0,0 +1,63 @@
import styles from './ServiceProcess.module.css';
const steps = [
{
step: '01',
title: 'Choose Your Service',
desc: 'Browse our dine-in, takeout, catering, or festival options and pick what fits your occasion best.',
icon: '🔍',
},
{
step: '02',
title: 'Get in Touch',
desc: 'For catering and events, reach out via our contact form or call us. For dine-in and takeout, just show up or order online.',
icon: '📞',
},
{
step: '03',
title: 'We Plan Together',
desc: 'Our team works with you on menu selection, portions, timing, and any dietary requirements — nothing is too much trouble.',
icon: '📋',
},
{
step: '04',
title: 'Enjoy the Experience',
desc: 'Whether at our table or yours, sit back and enjoy genuinely authentic South Indian food made with care.',
icon: '🎉',
},
];
export default function ServiceProcess() {
return (
<section className={styles.section}>
<div className={styles.container}>
<div className={styles.header}>
<span className={styles.eyebrow}>
<span className={styles.eyebrowIcon}></span> How It Works
</span>
<h2 className={styles.title}>
Simple Steps to <em>Great Food</em>
</h2>
<p className={styles.lead}>
Whether you're booking a table for two or catering a 200-person wedding, here's how
working with My Dosa Place looks.
</p>
</div>
<div className={styles.steps}>
{steps.map((s, i) => (
<div key={i} className={styles.step}>
<div className={styles.stepTop}>
<div className={styles.stepIcon}>{s.icon}</div>
{i < steps.length - 1 && <div className={styles.connector} />}
</div>
<span className={styles.stepNum}>{s.step}</span>
<h3 className={styles.stepTitle}>{s.title}</h3>
<p className={styles.stepDesc}>{s.desc}</p>
</div>
))}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,201 @@
.section {
padding: 80px 24px;
background: #1b4332;
position: relative;
overflow: hidden;
}
.section::before {
content: '';
position: absolute;
inset: 0;
background-image: radial-gradient(rgba(255,255,255,0.03) 1px, transparent 1px);
background-size: 30px 30px;
pointer-events: none;
}
.section::after {
content: '';
position: absolute;
left: -120px;
bottom: -120px;
width: 480px;
height: 480px;
border-radius: 50%;
background: radial-gradient(circle, rgba(212,160,23,0.1) 0%, transparent 70%);
pointer-events: none;
}
.container {
max-width: 1200px;
margin: 0 auto;
position: relative;
z-index: 1;
}
.header {
text-align: center;
margin-bottom: 64px;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-cursive), cursive;
font-size: 1.15rem;
color: #d4a017;
margin-bottom: 12px;
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(1.875rem, 3vw, 2.5rem);
font-weight: 700;
color: #ffffff;
margin: 0 0 16px;
line-height: 1.2;
}
.title em {
font-style: italic;
color: #d4a017;
}
.lead {
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
color: rgba(255,255,255,0.72);
line-height: 1.75;
max-width: 500px;
margin: 0 auto;
}
/* Steps row */
.steps {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0;
}
.step {
padding: 0 28px;
border-right: 1px solid rgba(255,255,255,0.08);
}
.step:last-child {
border-right: none;
}
.step:first-child {
padding-left: 0;
}
/* Icon + horizontal connector */
.stepTop {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.stepIcon {
width: 56px;
height: 56px;
background: rgba(212,160,23,0.15);
border: 1.5px solid rgba(212,160,23,0.35);
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
flex-shrink: 0;
}
.connector {
flex: 1;
height: 1px;
background: repeating-linear-gradient(
90deg,
rgba(212,160,23,0.4) 0px,
rgba(212,160,23,0.4) 6px,
transparent 6px,
transparent 12px
);
margin-left: 16px;
}
.stepNum {
display: block;
font-family: var(--font-playfair), Georgia, serif;
font-size: 2.5rem;
font-weight: 700;
color: rgba(255,255,255,0.08);
line-height: 1;
margin-bottom: 10px;
}
.stepTitle {
font-family: var(--font-inter), sans-serif;
font-size: 1rem;
font-weight: 700;
color: #ffffff;
margin: 0 0 10px;
line-height: 1.3;
}
.stepDesc {
font-family: var(--font-inter), sans-serif;
font-size: 0.875rem;
color: rgba(255,255,255,0.62);
line-height: 1.75;
margin: 0;
}
@media (max-width: 1024px) {
.steps {
grid-template-columns: repeat(2, 1fr);
gap: 40px;
}
.step {
padding: 0;
border-right: none;
border-bottom: 1px solid rgba(255,255,255,0.08);
padding-bottom: 40px;
}
.step:nth-child(3),
.step:nth-child(4) {
border-bottom: none;
padding-bottom: 0;
}
.connector { display: none; }
}
@media (max-width: 640px) {
.section { padding: 80px 20px; }
.steps {
grid-template-columns: 1fr;
gap: 36px;
}
.step:nth-child(3) {
border-bottom: 1px solid rgba(255,255,255,0.08);
padding-bottom: 36px;
}
}
@media (max-width: 767px) {
.section {
padding: 40px 24px;
}
}
.eyebrowIcon {
font-size: 1.2rem;
font-style: normal;
font-family: var(--font-inter), sans-serif;
}

View File

@ -0,0 +1,23 @@
import styles from './ServiceStats.module.css';
const stats = [
{ value: '8K+', label: 'Happy Guests Served' },
{ value: '100%', label: 'Vegetarian Always' },
{ value: '40+', label: 'Menu Items' },
{ value: '5★', label: 'Average Google Rating' },
];
export default function ServiceStats() {
return (
<section className={styles.section}>
<div className={styles.container}>
{stats.map((s, i) => (
<div key={i} className={styles.item}>
<span className={styles.value}>{s.value}</span>
<span className={styles.label}>{s.label}</span>
</div>
))}
</div>
</section>
);
}

View File

@ -0,0 +1,64 @@
.section {
background: #f9fafb;
border-bottom: 1px solid #e5e7eb;
padding: 80px 24px;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(4, 1fr);
divide-x: 1px solid #e5e7eb;
}
.item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 36px 20px;
gap: 6px;
border-right: 1px solid #e5e7eb;
transition: background 0.2s;
}
.item:last-child { border-right: none; }
.item:hover { background: #f0faf4; }
.value {
font-family: var(--font-playfair), Georgia, serif;
font-size: 2.25rem;
font-weight: 700;
color: #1b4332;
line-height: 1;
}
.label {
font-family: var(--font-inter), sans-serif;
font-size: 0.8125rem;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.06em;
text-align: center;
}
@media (max-width: 768px) {
.container {
grid-template-columns: repeat(2, 1fr);
}
.item:nth-child(2) { border-right: none; }
.item:nth-child(3) { border-top: 1px solid #e5e7eb; }
.item:nth-child(4) { border-top: 1px solid #e5e7eb; border-right: none; }
}
@media (max-width: 480px) {
.value { font-size: 1.875rem; }
}
@media (max-width: 767px) {
.section {
padding: 40px 24px;
}
}

View File

@ -0,0 +1,128 @@
import Image from 'next/image';
import Link from 'next/link';
import styles from './ServicesList.module.css';
const services = [
{
id: 'dine-in',
tag: 'Service 01',
title: 'Dine-In Experience',
description:
'Step into our cozy, family-friendly restaurant and experience authentic South Indian dining right here in Waterloo. Whether it\'s a quiet weekday lunch or a lively weekend family meal, our warm atmosphere and attentive team ensure every visit feels special.',
highlights: [
{ icon: '🍽️', text: 'Comfortable seating for families and groups' },
{ icon: '🌿', text: '100% vegetarian menu, freshly prepared daily' },
{ icon: '💁', text: 'Friendly staff and quick, attentive service' },
{ icon: '🔓', text: 'Open 7 days — MonFri 11AM to 9PM, Weekends 10AM to 10PM' },
],
cta: { label: 'Reserve a Table', href: '/contact' },
image: '/images/service-dine-in.jpg',
imageAlt: 'Guests enjoying dosas in our Waterloo restaurant',
flip: false,
accent: '#2d6a4f',
},
{
id: 'takeout',
tag: 'Service 02',
title: 'Takeout & Online Orders',
description:
'No time to dine in? Order your favourite dosas, idlis, and vadas online and enjoy them hot and fresh at home or on the go. Our takeout packaging is designed to keep your food crispy and aromatic until the last bite.',
highlights: [
{ icon: '🚗', text: 'Fast pickup — ready in 1520 minutes' },
{ icon: '📦', text: 'Fresh eco-friendly packaging that preserves crunch' },
{ icon: '💻', text: 'Easy online ordering — browse, tap, pick up' },
{ icon: '⏱️', text: 'No minimum order, no fuss' },
],
cta: { label: 'Order Online', href: '/menu' },
image: '/images/service-takeout.jpg',
imageAlt: 'Freshly packed takeout dosa boxes',
flip: true,
accent: '#d4a017',
},
{
id: 'catering',
tag: 'Service 03',
title: 'Catering for Events & Parties',
description:
'Make your next event truly flavorful with authentic South Indian catering. We cater for birthdays, weddings, corporate lunches, housewarmings, and community gatherings — bringing the full My Dosa Place experience directly to your venue.',
highlights: [
{ icon: '🎉', text: 'Tailored 100% vegetarian menus for any occasion' },
{ icon: '🍛', text: 'Live dosa counters available on request' },
{ icon: '👨‍🍳', text: 'Experienced catering team — setup to cleanup' },
{ icon: '📋', text: 'Custom menu planning and dietary accommodation' },
],
cta: { label: 'Get a Catering Quote', href: '/contact' },
image: '/images/service-catering.jpg',
imageAlt: 'South Indian catering spread for a large event',
flip: false,
accent: '#2d6a4f',
},
{
id: 'festival',
tag: 'Service 04',
title: 'Festival & Special Menus',
description:
'Celebrate South Indian festivals with us! We offer limited-time festival menus during Pongal, Onam, Ugadi, and other special occasions — filled with traditional dishes and festive combos that honour the culture and season.',
highlights: [
{ icon: '🪔', text: 'Special menus for Pongal, Onam, Ugadi & more' },
{ icon: '🍚', text: 'Traditional dishes not on our regular menu' },
{ icon: '🎊', text: 'Festive combos for the whole family' },
{ icon: '📅', text: 'Seasonal availability — follow us for updates' },
],
cta: { label: 'See Upcoming Events', href: '/contact' },
image: '/images/service-festival.jpg',
imageAlt: 'Festive South Indian Pongal spread',
flip: true,
accent: '#d4a017',
},
];
export default function ServicesList() {
return (
<section className={styles.section}>
{services.map((svc, i) => (
<div key={svc.id} id={svc.id} className={`${styles.service} ${svc.flip ? styles.flip : ''}`}>
<div className={styles.serviceContainer}>
{/* Image side */}
<div className={styles.imageCol}>
<div className={styles.imageFrame}>
<Image
src={svc.image}
alt={svc.imageAlt}
fill
style={{ objectFit: 'cover' }}
/>
<div className={styles.imageTag}>
<span>{svc.tag}</span>
</div>
</div>
</div>
{/* Content side */}
<div className={styles.contentCol}>
<span className={styles.tag}>{svc.tag}</span>
<h2 className={styles.title}>{svc.title}</h2>
<p className={styles.desc}>{svc.description}</p>
<ul className={styles.highlights}>
{svc.highlights.map((h, j) => (
<li key={j} className={styles.highlight}>
<span className={styles.highlightIcon}>{h.icon}</span>
<span>{h.text}</span>
</li>
))}
</ul>
<Link href={svc.cta.href} className={styles.cta}>
{svc.cta.label}
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</Link>
</div>
</div>
</div>
))}
</section>
);
}

View File

@ -0,0 +1,208 @@
.section {
background: #ffffff;
}
/* Each service block */
.service {
padding: 88px 24px;
border-bottom: 1px solid #f3f4f6;
}
.service:nth-child(even) {
background: #f9fafb;
}
.service:last-child {
border-bottom: none;
}
.serviceContainer {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 72px;
align-items: center;
}
/* Flipped variant */
.flip .serviceContainer {
direction: rtl;
}
.flip .imageCol,
.flip .contentCol {
direction: ltr;
}
/* Image column */
.imageCol {
position: relative;
}
.imageFrame {
position: relative;
width: 100%;
padding-bottom: 72%;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 24px 64px rgba(27, 67, 50, 0.14);
}
/* gold corner accent */
.imageFrame::before {
content: '';
position: absolute;
bottom: -12px;
right: -12px;
width: 140px;
height: 140px;
border: 3px solid #d4a017;
border-radius: 14px;
z-index: 0;
pointer-events: none;
}
.imageTag {
position: absolute;
top: 20px;
left: 20px;
z-index: 2;
background: rgba(27, 67, 50, 0.85);
backdrop-filter: blur(6px);
border-radius: 50px;
padding: 6px 16px;
}
.imageTag span {
font-family: var(--font-inter), sans-serif;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.07em;
color: #d4a017;
}
/* Content column */
.contentCol {
display: flex;
flex-direction: column;
gap: 0;
}
.tag {
display: inline-block;
font-family: var(--font-inter), sans-serif;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #d4a017;
margin-bottom: 12px;
}
.title {
font-family: var(--font-playfair), Georgia, serif;
font-size: clamp(1.75rem, 2.5vw, 2.375rem);
font-weight: 700;
color: #1b4332;
line-height: 1.2;
margin: 0 0 18px;
}
.desc {
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
color: #4b5563;
line-height: 1.8;
margin: 0 0 28px;
}
/* Highlights */
.highlights {
list-style: none;
padding: 0;
margin: 0 0 36px;
display: flex;
flex-direction: column;
gap: 12px;
}
.highlight {
display: flex;
align-items: flex-start;
gap: 12px;
font-family: var(--font-inter), sans-serif;
font-size: 0.9rem;
color: #374151;
line-height: 1.5;
}
.highlightIcon {
width: 36px;
height: 36px;
background: #f0faf4;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
flex-shrink: 0;
}
/* CTA button */
.cta {
display: inline-flex;
align-items: center;
gap: 8px;
background: #1b4332;
color: #ffffff;
font-family: var(--font-inter), sans-serif;
font-size: 0.9375rem;
font-weight: 600;
padding: 13px 28px;
border-radius: 50px;
text-decoration: none;
transition: background 0.2s, transform 0.2s;
align-self: flex-start;
}
.cta:hover {
background: #2d6a4f;
transform: translateY(-2px);
}
.cta svg {
transition: transform 0.2s;
}
.cta:hover svg {
transform: translateX(4px);
}
/* Tablet */
@media (max-width: 1024px) {
.serviceContainer {
grid-template-columns: 1fr;
gap: 48px;
}
.flip .serviceContainer {
direction: ltr;
}
.service {
padding: 64px 24px;
}
}
/* Mobile */
@media (max-width: 640px) {
.service {
padding: 56px 20px;
}
.imageFrame::before {
display: none;
}
}