diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..bd13c0c --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,34 @@ +name: CodeQL + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + schedule: + - cron: "30 1 * * 0" + +jobs: + analyze: + name: Analyze (javascript-typescript) + runs-on: ubuntu-latest + permissions: + security-events: write + packages: read + actions: read + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: javascript-typescript + build-mode: none + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:javascript-typescript" diff --git a/src/app/App.tsx b/src/app/App.tsx index 0ac2a57..8a13fc9 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -1,5 +1,5 @@ import { useEffect, useLayoutEffect, useRef } from "react"; -import { Routes, Route, BrowserRouter } from "react-router-dom"; +import { Routes, Route, useLocation, BrowserRouter } from "react-router-dom"; import { AnimatePresence, motion } from "motion/react"; import { Navbar } from "./components/Navbar"; import { Hero } from "./components/Hero"; @@ -15,6 +15,8 @@ import { FAQ } from "./components/FAQ"; import { FinalCTA } from "./components/FinalCTA"; import { Footer } from "./components/Footer"; import NotFound from "./components/NotFound"; +import { ServiceDetailPage } from "./components/ServiceDetailPage"; +import { ThemeProvider } from "./hooks/useTheme"; import { SplashScreen } from "./components/SplashScreen"; import { AuthProvider } from "../Firebase/AuthContext"; import { SignIn } from "../Firebase/SignIn"; @@ -34,6 +36,12 @@ import { PricingConfig } from "../dashboard/pages/PricingConfig"; const REVEAL_EASE: [number, number, number, number] = [0.4, 0, 0.2, 1]; +function ScrollToTop() { + const { pathname } = useLocation(); + useEffect(() => { window.scrollTo(0, 0); }, [pathname]); + return null; +} + function LandingPage() { return ( <> @@ -110,11 +118,14 @@ function LandingShell() { aria-hidden={!loading.isReady || undefined} > + + } /> } /> } /> + } /> } /> + diff --git a/src/app/components/FAQ.tsx b/src/app/components/FAQ.tsx index fb4083c..b2eca6e 100644 --- a/src/app/components/FAQ.tsx +++ b/src/app/components/FAQ.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; import { motion, AnimatePresence, useReducedMotion } from "motion/react"; +import { TypingText } from "./TypingText"; import { ChevronDown } from "lucide-react"; const faqs = [ @@ -49,7 +50,7 @@ export function FAQ() {

Frequently Asked{" "} - Questions +

diff --git a/src/app/components/Hero.tsx b/src/app/components/Hero.tsx index 4c77a0b..b6902de 100644 --- a/src/app/components/Hero.tsx +++ b/src/app/components/Hero.tsx @@ -1,6 +1,7 @@ import { motion, useReducedMotion } from 'motion/react'; import { Smartphone, Zap, TrendingUp, Sparkles } from 'lucide-react'; import { useState, useRef } from 'react'; +import { TypingText } from './TypingText'; const heroImage = "https://images.unsplash.com/photo-1551288049-bebda4e38f71?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtb2Rlcm4lMjB3ZWJzaXRlJTIwZGFzaGJvYXJkJTIwZGVzaWdufGVufDF8fHx8MTc4MTcwMjY1OXww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"; @@ -63,7 +64,7 @@ export function Hero() {

Your Business Deserves a Website That{' '} - Converts +

diff --git a/src/app/components/Navbar.tsx b/src/app/components/Navbar.tsx index bbb8211..694ff2b 100644 --- a/src/app/components/Navbar.tsx +++ b/src/app/components/Navbar.tsx @@ -1,15 +1,16 @@ -import { useState, useEffect } from 'react'; +import { useState } from 'react'; import { Link, useNavigate, useLocation } from 'react-router-dom'; import { Menu, X, Moon, Sun } from 'lucide-react'; import { motion, AnimatePresence, useReducedMotion } from 'motion/react'; import { useThrottledScroll } from '../hooks/useThrottledScroll'; +import { useTheme } from '../hooks/useTheme'; import { useAuth } from '../../Firebase/useAuth'; import { auth } from '../../Firebase/firebase'; export function Navbar() { const [isScrolled, setIsScrolled] = useState(false); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); - const [isDarkMode, setIsDarkMode] = useState(false); + const { isDarkMode, toggleTheme } = useTheme(); const { currentUser } = useAuth(); const location = useLocation(); const navigate = useNavigate(); @@ -20,33 +21,6 @@ export function Navbar() { setIsScrolled(scrollY > 20); }, 150); - useEffect(() => { - // Check for saved theme preference or system preference - const savedTheme = localStorage.getItem('theme'); - const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; - const isDark = savedTheme ? savedTheme === 'dark' : prefersDark; - - setIsDarkMode(isDark); - if (isDark) { - document.documentElement.classList.add('dark'); - } else { - document.documentElement.classList.remove('dark'); - } - }, []); - - const toggleTheme = () => { - const newDarkMode = !isDarkMode; - setIsDarkMode(newDarkMode); - - if (newDarkMode) { - document.documentElement.classList.add('dark'); - localStorage.setItem('theme', 'dark'); - } else { - document.documentElement.classList.remove('dark'); - localStorage.setItem('theme', 'light'); - } - }; - const handleSignOut = async () => { try { await auth.signOut(); @@ -143,13 +117,16 @@ export function Navbar() { )} {currentUser ? ( -
- - Welcome, {currentUser.displayName || currentUser.email} - +
+ + Dashboard + @@ -233,12 +210,21 @@ export function Navbar() { Contact {currentUser ? ( - +
+ setIsMobileMenuOpen(false)} + className="block w-full text-center px-6 py-2.5 bg-gradient-to-r from-indigo-600 to-purple-600 text-white rounded-lg hover:shadow-lg hover:shadow-indigo-500/50 transition-all duration-300 font-medium" + > + Dashboard + + +
) : ( Simple,{' '} - Transparent Pricing +

diff --git a/src/app/components/Process.tsx b/src/app/components/Process.tsx index 29712be..f55c970 100644 --- a/src/app/components/Process.tsx +++ b/src/app/components/Process.tsx @@ -1,4 +1,5 @@ import { motion, useReducedMotion } from 'motion/react'; +import { TypingText } from './TypingText'; import { ClipboardList, Palette, Code2, Rocket } from 'lucide-react'; const steps = [ @@ -47,7 +48,7 @@ export function Process() {

How We{' '} - Work +

diff --git a/src/app/components/ServiceDetailPage.tsx b/src/app/components/ServiceDetailPage.tsx new file mode 100644 index 0000000..0d89516 --- /dev/null +++ b/src/app/components/ServiceDetailPage.tsx @@ -0,0 +1,428 @@ +import { useState } from 'react'; +import { useParams, Link, Navigate } from 'react-router-dom'; +import { motion, useReducedMotion } from 'motion/react'; +import { ArrowLeft, Check, ChevronDown, ArrowRight } from 'lucide-react'; +import { services } from '../data/servicesData'; +import { Navbar } from './Navbar'; +import { Footer } from './Footer'; + +function FAQItem({ question, answer, index }: { question: string; answer: string; index: number }) { + const [open, setOpen] = useState(false); + const reduce = useReducedMotion(); + + return ( + + + +

{answer}

+ + + ); +} + +export function ServiceDetailPage() { + const { slug } = useParams<{ slug: string }>(); + const reduce = useReducedMotion(); + const service = services.find((s) => s.slug === slug); + + if (!service) return ; + + const Icon = service.icon; + + return ( +
+ + +
+ {/* ── Hero ── */} +
+ {/* Aurora blobs */} +
+
+
+ +
+ {/* Back link */} + + + + All Services + + + + {/* Icon chip */} + +
+
+
+ + +
+
+ + + {/* Heading */} + + + Our Services + +

+ {service.title} +

+

+ {service.tagline} +

+

+ {service.whatIsIt} +

+
+ + {/* CTA */} + + + Get a Free Quote + + + + View All Services + + +
+
+ + {/* ── What's included ── */} +
+
+ +

+ What's{' '} + + included + +

+

+ Everything delivered in your package — no hidden extras. +

+
+ +
+ {service.includes.map((item, i) => ( + + + + + {item} + + ))} +
+
+
+ + {/* ── Ideal for ── */} +
+
+ +

+ Who it's{' '} + + for + +

+

+ This service is a great fit if you are… +

+
+ +
+ {service.idealFor.map((item, i) => ( + + + {item} + + ))} +
+
+
+ + {/* ── Process ── */} +
+
+ +

+ How we{' '} + + work + +

+

+ A clear process from first conversation to launch day. +

+
+ +
+ {service.process.map((step, i) => ( + + {/* Connector line (hidden on last) */} + {i < service.process.length - 1 && ( +
+ )} + +
+
+ {step.step} +
+

{step.title}

+

{step.description}

+
+ + ))} +
+
+
+ + {/* ── Highlights ── */} +
+
+ +

+ Why it{' '} + + stands out + +

+

+ The details that make the difference. +

+
+ +
+ {service.highlights.map((h, i) => ( + +
+
+ +
+

{h.title}

+

{h.description}

+ + ))} +
+
+
+ + {/* ── FAQs ── */} +
+
+ +

+ Common{' '} + + questions + +

+

+ Everything you need to know before getting started. +

+
+ +
+ {service.faqs.map((faq, i) => ( + + ))} +
+
+
+ + {/* ── CTA banner ── */} +
+
+ + {/* Gradient blobs inside card */} +
+
+ +
+ + Ready to start? + +

+ Let's build your{' '} + + {service.title.toLowerCase()} + +

+

+ Tell us about your project and we'll get back to you with a tailored quote — usually within 24 hours. +

+
+ + Get a Free Quote + + + + Explore Other Services + +
+
+ +
+
+
+ +
+
+ ); +} diff --git a/src/app/components/Services.tsx b/src/app/components/Services.tsx index fd67c32..1fdc38b 100644 --- a/src/app/components/Services.tsx +++ b/src/app/components/Services.tsx @@ -1,45 +1,8 @@ import React, { useRef } from 'react'; +import { Link } from 'react-router-dom'; import { motion, useScroll, useTransform, useReducedMotion } from 'motion/react'; -import { FileText, Globe, Briefcase, ShoppingCart, Code, Settings } from 'lucide-react'; - -const services = [ - { - icon: FileText, - title: 'Landing Pages', - description: 'High-converting landing pages designed to capture leads and drive conversions.', - color: 'from-indigo-500 to-blue-500', - }, - { - icon: Globe, - title: 'Business Websites', - description: 'Professional multi-page websites that establish your online presence.', - color: 'from-purple-500 to-pink-500', - }, - { - icon: Briefcase, - title: 'Portfolio Websites', - description: 'Showcase your work with stunning portfolio designs that impress clients.', - color: 'from-cyan-500 to-teal-500', - }, - { - icon: ShoppingCart, - title: 'E-Commerce Stores', - description: 'Full-featured online stores with secure payment processing and inventory management.', - color: 'from-orange-500 to-red-500', - }, - { - icon: Code, - title: 'Custom Web Applications', - description: 'Tailored web applications built to solve your unique business challenges.', - color: 'from-green-500 to-emerald-500', - }, - { - icon: Settings, - title: 'Website Maintenance', - description: 'Ongoing support and updates to keep your website running smoothly.', - color: 'from-violet-500 to-purple-500', - }, -]; +import { services } from '../data/servicesData'; +import { TypingText } from './TypingText'; // Fine fractal-noise grain to kill blur banding on the light backdrop. Fully // percent-encoded so the data URI survives any bundler / CSS parser. @@ -78,6 +41,7 @@ function ServiceCard({ }; return ( +
+ ); } @@ -232,7 +197,7 @@ export function Services() { animate={reduce ? undefined : { backgroundPositionX: ['0%', '200%'] }} transition={{ duration: 6, repeat: Infinity, ease: 'linear' }} > - Succeed Online +

diff --git a/src/app/components/Testimonials.tsx b/src/app/components/Testimonials.tsx index 838feab..83a8819 100644 --- a/src/app/components/Testimonials.tsx +++ b/src/app/components/Testimonials.tsx @@ -1,5 +1,6 @@ import { motion, useReducedMotion } from "motion/react"; import { Quote, Star } from "lucide-react"; +import { TypingText } from "./TypingText"; import priya from "../../assets/testimonials/priya.jpg"; import arjun from "../../assets/testimonials/arjun.jpg"; import ananya from "../../assets/testimonials/ananya.jpg"; @@ -220,7 +221,7 @@ export function Testimonials() {

What Our{" "} - Clients Say +

diff --git a/src/app/components/TypingText.tsx b/src/app/components/TypingText.tsx new file mode 100644 index 0000000..719b4d4 --- /dev/null +++ b/src/app/components/TypingText.tsx @@ -0,0 +1,95 @@ +import { useState, useEffect, useRef } from 'react'; +import { useReducedMotion, useInView } from 'motion/react'; + +interface TypingTextProps { + text: string; + /** Milliseconds to wait before typing starts */ + delay?: number; + /** Milliseconds per character */ + speed?: number; + /** Wait for the element to enter the viewport before starting */ + triggerOnView?: boolean; + /** Show blinking cursor while typing */ + showCursor?: boolean; + /** Tailwind bg-* class for the cursor bar colour */ + cursorColor?: string; +} + +/** + * Renders text character-by-character with a blinking cursor. + * Stops animating once all characters are displayed. + * Respects prefers-reduced-motion — shows the full string instantly when set. + * Screen readers receive the complete text via aria-label at all times. + */ +export function TypingText({ + text, + delay = 0, + speed = 55, + triggerOnView = true, + showCursor = true, + cursorColor = 'bg-indigo-500', +}: TypingTextProps) { + const reduce = useReducedMotion(); + const ref = useRef(null); + const isInView = useInView(ref, { once: true, margin: '-5% 0px' }); + + const [displayed, setDisplayed] = useState(reduce ? text : ''); + const [done, setDone] = useState(!!reduce); + const [blink, setBlink] = useState(true); + + const shouldStart = triggerOnView ? isInView : true; + + useEffect(() => { + if (reduce) return; + if (!shouldStart) return; + + setDisplayed(''); + setDone(false); + + let cancelled = false; + let intervalId: ReturnType | undefined; + + const timeoutId = setTimeout(() => { + let i = 0; + intervalId = setInterval(() => { + if (cancelled) { clearInterval(intervalId); return; } + i++; + setDisplayed(text.slice(0, i)); + if (i >= text.length) { + clearInterval(intervalId); + if (!cancelled) setDone(true); + } + }, speed); + }, delay); + + return () => { + cancelled = true; + clearTimeout(timeoutId); + if (intervalId) clearInterval(intervalId); + }; + }, [shouldStart, text, delay, speed, reduce]); + + useEffect(() => { + if (done || reduce) { setBlink(false); return; } + const id = setInterval(() => setBlink((v) => !v), 530); + return () => clearInterval(id); + }, [done, reduce]); + + if (reduce) { + return {text}; + } + + return ( + + + {showCursor && !done && ( + + ); +} diff --git a/src/app/components/ui/skeleton.tsx b/src/app/components/ui/skeleton.tsx index e5a04d6..166d0ef 100644 --- a/src/app/components/ui/skeleton.tsx +++ b/src/app/components/ui/skeleton.tsx @@ -4,7 +4,7 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) { return (

); diff --git a/src/app/data/servicesData.ts b/src/app/data/servicesData.ts new file mode 100644 index 0000000..e265d19 --- /dev/null +++ b/src/app/data/servicesData.ts @@ -0,0 +1,296 @@ +import { FileText, Globe, Briefcase, ShoppingCart, Code, Settings, type LucideIcon } from 'lucide-react'; + +export interface ServiceData { + slug: string; + icon: LucideIcon; + title: string; + tagline: string; + description: string; + color: string; + heroGradient: string; + whatIsIt: string; + includes: string[]; + idealFor: string[]; + process: { step: number; title: string; description: string }[]; + highlights: { title: string; description: string }[]; + faqs: { question: string; answer: string }[]; +} + +export const services: ServiceData[] = [ + { + slug: 'landing-pages', + icon: FileText, + title: 'Landing Pages', + tagline: 'Convert visitors into customers from the first click.', + description: 'High-converting landing pages designed to capture leads and drive conversions.', + color: 'from-indigo-500 to-blue-500', + heroGradient: 'from-indigo-600 to-blue-600', + whatIsIt: + 'A landing page is a standalone web page built with a single focused goal — capturing email signups, selling a product, or registering event attendees. Unlike a full website, every element is purpose-built to guide the visitor toward one action.', + includes: [ + 'Custom hero section with compelling headline & CTA', + 'Social proof — testimonials, trust badges, logos', + 'Feature / benefit sections with icons or illustrations', + 'Lead capture form with email integration', + 'Mobile-first responsive design', + 'SEO meta tags & Open Graph setup', + 'Analytics integration (Google Analytics / Pixel)', + 'A/B testing–ready structure', + ], + idealFor: [ + 'Product launches & pre-launch waitlists', + 'Ad campaign destinations (Google, Meta, LinkedIn)', + 'Event registrations & webinars', + 'SaaS free trial or demo signups', + 'Local business lead generation', + ], + process: [ + { step: 1, title: 'Discovery Call', description: 'We learn your goal, audience, and offer to shape the page strategy.' }, + { step: 2, title: 'Wireframe & Copy', description: 'We draft the page structure and persuasive copy before any design begins.' }, + { step: 3, title: 'Design & Build', description: 'Pixel-perfect design is coded into a fast, accessible, production-ready page.' }, + { step: 4, title: 'Review & Launch', description: 'You review, we refine, then go live with full QA across devices and browsers.' }, + ], + highlights: [ + { title: 'Conversion-First Design', description: 'Every layout decision is informed by CRO principles, not just aesthetics.' }, + { title: 'Sub-2s Load Time', description: 'Optimised assets, lazy loading, and CDN delivery keep bounce rates low.' }, + { title: 'Integrates Everywhere', description: 'Works with Mailchimp, HubSpot, ActiveCampaign, Zapier, and any CRM you already use.' }, + { title: 'GDPR Ready', description: 'Cookie consent, privacy-compliant forms, and secure data handling out of the box.' }, + ], + faqs: [ + { question: 'How long does it take to build a landing page?', answer: 'Typically 5–10 business days from approved copy/wireframe to launch-ready page.' }, + { question: 'Can I update the page myself after launch?', answer: 'Yes — we deliver with a CMS option or simple editable sections so you stay in control.' }, + { question: 'Do you write the copy?', answer: 'Yes, copywriting is included. We research your audience and offer before writing.' }, + { question: 'Will it work with my existing domain?', answer: 'Absolutely. We deploy to any subdomain or path on your existing domain.' }, + ], + }, + { + slug: 'business-websites', + icon: Globe, + title: 'Business Websites', + tagline: 'Your brand online — professional, fast, and built to grow.', + description: 'Professional multi-page websites that establish your online presence.', + color: 'from-purple-500 to-pink-500', + heroGradient: 'from-purple-600 to-pink-600', + whatIsIt: + 'A business website is your 24/7 digital storefront. It communicates who you are, what you offer, and why clients should choose you — then makes it easy for them to act. We build multi-page sites that balance brand storytelling with clear conversion paths.', + includes: [ + 'Home, About, Services, Contact pages (+ more)', + 'Custom brand-aligned design system', + 'Blog / news section with CMS', + 'Contact forms with email routing', + 'Google Maps integration', + 'On-page SEO foundation — meta, schema, sitemap', + 'Social media link integration', + 'HTTPS, domain, and hosting setup guidance', + ], + idealFor: [ + 'Small to medium-sized businesses going online', + 'Service providers — agencies, consultants, law firms', + 'Local businesses wanting to rank in their city', + 'Startups building their first credibility hub', + 'Established brands refreshing an outdated site', + ], + process: [ + { step: 1, title: 'Brand Intake', description: 'Colors, fonts, tone of voice, competitors — we gather everything that shapes your identity.' }, + { step: 2, title: 'Sitemap & Architecture', description: 'We plan the page hierarchy and user journeys before writing a line of code.' }, + { step: 3, title: 'Design System', description: 'A reusable component library is built so every page looks cohesive and scales easily.' }, + { step: 4, title: 'Build, Test & Launch', description: 'Full-stack build with cross-browser QA, accessibility audit, and performance checks before going live.' }, + ], + highlights: [ + { title: 'Brand-True Design', description: 'We design around your existing visual identity — or create one from scratch if you need it.' }, + { title: 'SEO Foundation', description: 'Semantic HTML, structured data, page speed, and a clean sitemap so Google can find and rank you.' }, + { title: 'CMS-Powered', description: 'Update your own content — blog posts, team bios, service pages — without touching code.' }, + { title: 'Scales With You', description: 'Architecture planned for future pages, languages, or features without costly rewrites.' }, + ], + faqs: [ + { question: 'How many pages are included?', answer: 'Standard packages include 5–8 pages. Additional pages are available at a flat rate per page.' }, + { question: 'Do you handle hosting?', answer: 'We can deploy to your preferred host or recommend and set up a managed host for you.' }, + { question: 'What CMS do you use?', answer: "We typically use Sanity, Contentful, or headless WordPress — chosen based on your team's comfort level." }, + { question: 'Can I see examples of your work?', answer: 'Yes — check our Portfolio section for recent business website projects.' }, + ], + }, + { + slug: 'portfolio-websites', + icon: Briefcase, + title: 'Portfolio Websites', + tagline: 'Make your work impossible to ignore.', + description: 'Showcase your work with stunning portfolio designs that impress clients.', + color: 'from-cyan-500 to-teal-500', + heroGradient: 'from-cyan-600 to-teal-600', + whatIsIt: + "A portfolio website is your personal brand in digital form. It's the first thing prospective clients, employers, or collaborators look at — and it needs to communicate your skill, taste, and personality at a glance. We craft portfolios that feel as impressive as the work inside them.", + includes: [ + 'Curated project gallery with case study pages', + 'Animated hero / personal introduction', + 'Skills, services, or expertise section', + 'Filterable project categories', + 'Downloadable CV / resume integration', + 'Contact form with calendar booking option', + 'Dark / light mode toggle', + 'Open Graph cards for social sharing', + ], + idealFor: [ + 'Designers, illustrators, and photographers', + 'Developers and engineers', + 'Architects and interior designers', + 'Writers, journalists, and content creators', + 'Filmmakers and motion designers', + ], + process: [ + { step: 1, title: 'Content Audit', description: 'We review your existing work and select pieces that best represent your range and depth.' }, + { step: 2, title: 'Personal Brand Direction', description: 'Typography, color palette, and voice are chosen to reflect your style authentically.' }, + { step: 3, title: 'Gallery & Case Study Design', description: 'Each project gets a micro-page with context, process, and outcome — not just a screenshot.' }, + { step: 4, title: 'Launch & Optimise', description: 'We optimise for page speed, social sharing, and discoverability before handing over the keys.' }, + ], + highlights: [ + { title: 'First-Impression Animation', description: 'Tasteful motion on load and scroll that feels premium without slowing the page down.' }, + { title: 'Case Study Depth', description: 'Individual project pages that show your thinking process, not just the final deliverable.' }, + { title: 'Easy Self-Updating', description: 'Add new projects yourself without a developer — drag, upload, publish.' }, + { title: 'Recruiter & Client Tested', description: 'Layouts informed by what hiring managers and buyers actually look for when evaluating portfolios.' }, + ], + faqs: [ + { question: 'How many projects can I showcase?', answer: "There's no hard cap. We typically design for 6–12 featured projects with room to grow." }, + { question: 'Can I use my own domain?', answer: 'Yes. We help you connect your custom domain (e.g. yourname.com) and set up email forwarding.' }, + { question: 'What if my work is under NDA?', answer: 'We can create password-protected case study pages viewable only by invited visitors.' }, + { question: 'Do you help with writing project descriptions?', answer: 'Yes — copywriting for project summaries and the About page is included in all portfolio packages.' }, + ], + }, + { + slug: 'e-commerce-stores', + icon: ShoppingCart, + title: 'E-Commerce Stores', + tagline: 'Sell online with confidence. From first click to checkout.', + description: 'Full-featured online stores with secure payment processing and inventory management.', + color: 'from-orange-500 to-red-500', + heroGradient: 'from-orange-600 to-red-600', + whatIsIt: + 'An e-commerce store is a fully operational online shop — product listings, shopping cart, secure payments, order management, and customer accounts, all in one place. We build stores that are fast, trustworthy, and optimised to turn browsers into buyers.', + includes: [ + 'Product catalog with categories & filters', + 'Shopping cart & wishlist functionality', + 'Secure checkout — Stripe / PayPal / Razorpay', + 'Customer accounts & order history', + 'Inventory & stock management dashboard', + 'Discount codes & promotional engine', + 'Abandoned cart email recovery', + 'Order fulfilment & shipping integrations', + ], + idealFor: [ + 'Brands launching direct-to-consumer (DTC)', + 'Artisans, creators, and small batch makers', + 'B2B businesses wanting an online order portal', + 'Brick-and-mortar stores expanding online', + 'Digital product sellers — courses, templates, files', + ], + process: [ + { step: 1, title: 'Store Strategy', description: 'We map your catalog, pricing model, payment providers, and shipping rules before touching design.' }, + { step: 2, title: 'UX & Catalog Design', description: 'Product pages, collection pages, and checkout are designed to minimise friction and build trust.' }, + { step: 3, title: 'Integration & Testing', description: 'Payment gateways, inventory systems, and email automations are wired and stress-tested.' }, + { step: 4, title: 'Launch & Handover', description: 'Full store walkthrough, staff training, and a post-launch support window.' }, + ], + highlights: [ + { title: 'Conversion-Optimised Checkout', description: 'Guest checkout, one-click upsells, and trust signals at every friction point to maximise completed orders.' }, + { title: 'Mobile-First Shopping', description: 'Over 60% of purchases happen on mobile — every interaction is thumb-friendly and fast.' }, + { title: 'Inventory Intelligence', description: 'Real-time stock tracking, low-stock alerts, and multi-location inventory management.' }, + { title: 'Marketing-Ready', description: 'Built-in SEO, product schema, social shopping feeds (Meta, Google), and email list capture.' }, + ], + faqs: [ + { question: 'Which payment gateways do you support?', answer: 'Stripe, PayPal, Razorpay, Square, and most regional gateways. We set up and test the integration for you.' }, + { question: 'Can I manage products myself?', answer: 'Yes — you get a full admin dashboard to add, edit, and remove products, run promotions, and process orders.' }, + { question: 'Do you build on Shopify or custom?', answer: 'Both. We recommend Shopify for most retail scenarios, and custom builds for complex B2B or subscription models.' }, + { question: 'Is the store PCI-compliant?', answer: "Yes. Payment data never touches our servers — all card processing is handled by certified payment processors." }, + ], + }, + { + slug: 'custom-web-applications', + icon: Code, + title: 'Custom Web Applications', + tagline: 'Software that fits your workflow, not the other way around.', + description: 'Tailored web applications built to solve your unique business challenges.', + color: 'from-green-500 to-emerald-500', + heroGradient: 'from-green-600 to-emerald-600', + whatIsIt: + "A custom web application is purpose-built software that runs in the browser and solves a specific business problem — a client portal, an internal tool, a SaaS dashboard, an automated workflow. Unlike off-the-shelf software, it does exactly what your business needs and nothing it doesn't.", + includes: [ + 'Requirements discovery & technical architecture', + 'User authentication & role-based access control', + 'Custom dashboard & data visualisation', + 'REST or GraphQL API design & development', + 'Third-party API & webhook integrations', + 'Database design — relational or document', + 'Real-time features — live updates, notifications', + 'Automated testing suite & CI/CD pipeline', + ], + idealFor: [ + 'Businesses replacing manual spreadsheet workflows', + 'SaaS founders building their MVP', + 'Teams needing an internal operations tool', + 'Companies automating client-facing processes', + 'Organisations requiring custom data reporting', + ], + process: [ + { step: 1, title: 'Requirements Workshop', description: 'We map every user role, workflow, and edge case before committing to an architecture.' }, + { step: 2, title: 'Technical Architecture', description: 'Stack selection, data models, and API contracts are documented and reviewed with your team.' }, + { step: 3, title: 'Agile Build', description: 'Development in 2-week sprints with working software delivered and demoed after each sprint.' }, + { step: 4, title: 'QA, Deploy & Support', description: 'Automated tests, load testing, staged rollout, and a 90-day post-launch support window.' }, + ], + highlights: [ + { title: 'Scalable by Design', description: "Architecture decisions are made with your 10× growth scenario in mind, not just today's load." }, + { title: 'Security First', description: 'OWASP-compliant, with input validation, rate limiting, encrypted storage, and audit logging built in.' }, + { title: 'Integrates with Your Stack', description: 'We connect to your existing ERP, CRM, accounting software, or any API-enabled tool.' }, + { title: 'Full Source Ownership', description: 'You own the code. No vendor lock-in, no per-seat licensing — yours from day one.' }, + ], + faqs: [ + { question: 'How long does a custom app take to build?', answer: 'MVPs typically take 6–12 weeks. Full-featured applications range from 3–9 months depending on complexity.' }, + { question: 'What tech stack do you use?', answer: 'React + TypeScript on the frontend, Node.js or Python on the backend, PostgreSQL or MongoDB for data — chosen for your specific needs.' }, + { question: 'Can you take over an existing codebase?', answer: "Yes. We'll audit the existing code, document what's there, and take responsibility for ongoing development." }, + { question: 'Do you offer ongoing maintenance?', answer: 'Yes — monthly retainer plans cover bug fixes, dependency updates, feature additions, and priority support.' }, + ], + }, + { + slug: 'website-maintenance', + icon: Settings, + title: 'Website Maintenance', + tagline: 'Your website, always healthy. You focus on your business.', + description: 'Ongoing support and updates to keep your website running smoothly.', + color: 'from-violet-500 to-purple-500', + heroGradient: 'from-violet-600 to-purple-600', + whatIsIt: + "Website maintenance is the ongoing care that keeps your site secure, fast, and up to date. Without it, outdated plugins create security holes, broken links erode trust, and slow load times cost you visitors. Our maintenance plans handle all of it so you don't have to.", + includes: [ + 'CMS, plugin & dependency updates', + 'Daily automated backups — 30-day retention', + 'Uptime monitoring with instant alerts', + 'Monthly performance & Core Web Vitals report', + 'Security scanning & malware removal', + 'Broken link & 404 error fixes', + 'Content updates — text, images, blog posts', + 'Priority support — response within 4 hours', + ], + idealFor: [ + 'Business owners without in-house tech teams', + 'Agencies needing white-label maintenance for clients', + 'Sites running WordPress, Webflow, or custom CMS', + 'E-commerce stores where downtime = lost revenue', + 'Any business that launched a site and needs ongoing care', + ], + process: [ + { step: 1, title: 'Site Audit', description: 'We review your current site health — speed, security, plugins, errors — and baseline everything.' }, + { step: 2, title: 'Onboarding', description: 'Access handover, backup system setup, and monitoring alerts configured within 24 hours.' }, + { step: 3, title: 'Monthly Maintenance', description: 'Scheduled updates, backups, and a monthly report lands in your inbox every month.' }, + { step: 4, title: 'On-Demand Requests', description: 'Content updates, small fixes, and feature tweaks handled within your monthly hours.' }, + ], + highlights: [ + { title: '99.9% Uptime Target', description: 'Proactive monitoring catches issues before your visitors do — often before you even know about them.' }, + { title: 'Zero Surprise Bills', description: 'Flat monthly pricing. No per-update fees, no emergency call-out charges.' }, + { title: 'Monthly Transparency Report', description: "A plain-English report every month showing what was done, your site's speed, and any issues resolved." }, + { title: 'Instant Security Response', description: 'If your site is ever compromised, we respond and restore within hours — not days.' }, + ], + faqs: [ + { question: 'What\'s included in the "content updates" allowance?', answer: 'Text edits, image swaps, blog publishing, and minor layout tweaks — up to 2 hours per month on standard plans.' }, + { question: 'Can I cancel anytime?', answer: 'Yes — plans are month-to-month. Cancel with 30 days notice, no penalties.' }, + { question: "Do you maintain sites you didn't build?", answer: 'Absolutely. We onboard any existing site after a technical audit to make sure we understand it fully.' }, + { question: 'What happens if something breaks?', answer: 'We fix it, no questions asked. Emergency fixes are included in all plans with a 4-hour response SLA.' }, + ], + }, +]; diff --git a/src/app/hooks/useTheme.ts b/src/app/hooks/useTheme.ts new file mode 100644 index 0000000..a0308cd --- /dev/null +++ b/src/app/hooks/useTheme.ts @@ -0,0 +1,49 @@ +import { + createElement, + createContext, + useCallback, + useContext, + useEffect, + useState, + type ReactNode, +} from 'react'; + +interface ThemeContextValue { + isDarkMode: boolean; + toggleTheme: () => void; +} + +const ThemeContext = createContext(null); + +function getInitialTheme(): boolean { + try { + const saved = localStorage.getItem('theme'); + if (saved) return saved === 'dark'; + return window.matchMedia('(prefers-color-scheme: dark)').matches; + } catch { + return false; + } +} + +export function ThemeProvider({ children }: { children: ReactNode }) { + // Lazy initializer reads localStorage synchronously — no FOUC on first render. + const [isDarkMode, setIsDarkMode] = useState(getInitialTheme); + + // Sole owner of the `dark` class and localStorage value. + useEffect(() => { + document.documentElement.classList.toggle('dark', isDarkMode); + localStorage.setItem('theme', isDarkMode ? 'dark' : 'light'); + }, [isDarkMode]); + + const toggleTheme = useCallback(() => { + setIsDarkMode((prev) => !prev); + }, []); + + return createElement(ThemeContext.Provider, { value: { isDarkMode, toggleTheme } }, children); +} + +export function useTheme(): ThemeContextValue { + const ctx = useContext(ThemeContext); + if (!ctx) throw new Error('useTheme must be used inside '); + return ctx; +} diff --git a/src/dashboard/components/DashboardLayout.tsx b/src/dashboard/components/DashboardLayout.tsx index 96add0a..15522ca 100644 --- a/src/dashboard/components/DashboardLayout.tsx +++ b/src/dashboard/components/DashboardLayout.tsx @@ -4,6 +4,7 @@ import { motion, AnimatePresence } from "motion/react"; import { signOut } from "firebase/auth"; import { auth } from "../../Firebase/firebase"; import { useAuth } from "../../Firebase/useAuth"; +import { useTheme } from "../../app/hooks/useTheme"; import { LayoutDashboard, GitBranch, @@ -15,6 +16,8 @@ import { Menu, X, Home, + Moon, + Sun, Sparkles, Settings2, } from "lucide-react"; @@ -67,6 +70,7 @@ export function DashboardLayout() { const location = useLocation(); const navigate = useNavigate(); const { currentUser } = useAuth(); + const { isDarkMode, toggleTheme } = useTheme(); const handleSignOut = async () => { try { @@ -121,6 +125,18 @@ export function DashboardLayout() {
+ {/* Top bar (mobile) */} -
+
+
+ + + Servio Dashboard + +
- - Servio Dashboard -
diff --git a/src/main.tsx b/src/main.tsx index 26b06f7..7f8e888 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,6 +2,8 @@ import { createRoot } from "react-dom/client"; import App from "./app/App"; import "./styles/index.css"; -createRoot(document.getElementById("root")!).render( - -); \ No newline at end of file +const root = document.getElementById("root"); + +if (root) { + createRoot(root).render(); +} diff --git a/tsconfig.json b/tsconfig.json index 3d3df96..f968d23 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,6 @@ "resolveJsonModule": true, /* Path alias — mirrors the `@` alias in vite.config.ts */ - "baseUrl": ".", "paths": { "@/*": ["./src/*"] },