22 Commits

Author SHA1 Message Date
Unchained
44d938953b Center related products using flexbox instead of grid
Some checks failed
Build and Deploy / build (push) Has been cancelled
2026-03-22 11:00:44 +02:00
Unchained
97fc5f5f1d Fix JSX indentation in Similar Products section 2026-03-22 09:11:13 +02:00
Unchained
140d82c7f4 Center Similar Products grid on product pages 2026-03-22 09:04:35 +02:00
Unchained
80a388cd7c fix: Center the similar products grid on product pages 2026-03-22 09:00:01 +02:00
Unchained
c3bd0408f4 feat: Add newsletter section back to product pages 2026-03-22 08:57:05 +02:00
Unchained
7618cfa6df fix: Remove non-scrolling testimonials and newsletter from product pages 2026-03-22 08:54:29 +02:00
Unchained
0827147745 fix: Slow down reviews scroll tempo for readability 2026-03-22 08:47:06 +02:00
Unchained
c5e96718a4 feat: Add scrolling reviews marquee with 50 reviews
- Reviews scroll continuously (alternating left-to-right and right-to-left)
- 50 varied Serbian customer reviews praising Manoon products
- Mentions Anti-age Serum, Day Serum, Night Serum, Morning Glow, Anti-age Set
- Scroll effect similar to As Seen In banner
2026-03-22 08:43:25 +02:00
Unchained
7febe90b36 fix: Move Customer Reviews above AsSeenIn on product pages 2026-03-22 08:39:44 +02:00
Unchained
c723d72508 fix: Move BeforeAfterGallery to right after AsSeenIn on product pages 2026-03-22 08:37:12 +02:00
Unchained
bf6362d3ad feat: Add second transformation with side-by-side sliders
- First transformation: use_case_2 (4-6 weeks)
- Second transformation: use_case_3 (6-8 weeks)
- Both sliders displayed side by side on same line
2026-03-22 08:32:22 +02:00
Unchained
9e901d7dfe feat: Use actual moumoujus before/after images from MinIO
- Before: use_case_2.webp (wrinkled skin)
- After: use_case_2_1.webp (smooth skin)
- Both images from minio-api.nodecrew.me
2026-03-22 08:26:50 +02:00
Unchained
0e727b2648 feat: Add slider comparison to before/after gallery
- Drag slider to reveal before/after (like example screenshot)
- Timeline showing '4-6 Weeks'
- Stars rating with review count
- Verified Results badge
- Matches moumoujus.com style
2026-03-22 07:32:47 +02:00
Unchained
d6523deae5 feat: Add all homepage sections to product pages
Product pages now include:
- Product Benefits
- Product Reviews
- Trust Badges
- Before/After Gallery
- How It Works
- Testimonials
- Newsletter
2026-03-21 20:13:05 +02:00
Unchained
5216abbcc0 feat: Landing page design improvements
Based on landing-page-design skill principles:

Homepage:
- Redesigned hero with outcome-focused headline ("Transform Your Hair & Skin")
- Added social proof micro (5 stars + 50,000+ customers)
- Better CTA: "Transform My Hair & Skin" instead of "Shop Now"
- Added trust indicators in hero (30-day guarantee, free shipping, cruelty free)
- Added ProblemSection to create empathy (dry hair, confusing ingredients, no results)
- Added HowItWorks section (3 steps: Choose, Apply, See Results)
- Improved AsSeenIn with scrolling marquee on dark background
- Premium trust badges with stats and icons

Product pages:
- Improved CTA: "Transform My Hair & Skin" (action verb + value)
- Added ProductBenefits section (4 key benefits)
- Added ProductReviews section with customer testimonials
- Added AsSeenIn scrolling banner
- Added trust indicators with icons

Section order now follows proven conversion sequence:
1. Hero (headline + outcome + CTA)
2. Social Proof (trust badges, logos)
3. Problem (empathy)
4. Solution (products)
5. How It Works
6. Testimonials
7. Final CTA
2026-03-21 19:59:09 +02:00
Unchained
4af5412c76 feat: Add trust indicators to product page
- Add 30-day money-back guarantee, secure checkout, easy returns icons
- Reorganize product page layout with clearer trust messaging
- Update free shipping threshold messaging
2026-03-21 19:00:31 +02:00
Unchained
d381cba302 feat: Add social proof sections to homepage
- Add TrustBadges component with ratings, customer count, secure payment icons
- Add AsSeenIn media logos banner
- Add BeforeAfterGallery with interactive gallery
- Add TestimonialsSection (already existed, now integrated into homepage)
- Connect all sections in homepage page.tsx

Sections added to homepage flow:
1. HeroVideo
2. TrustBadges
3. AsSeenIn
4. Products Grid
5. BeforeAfterGallery
6. Brand Story
7. Benefits
8. TestimonialsSection
9. Newsletter
2026-03-21 18:58:33 +02:00
Unchained
26212dec1c fix: Apollo Client cache merge causing product duplication
Some checks failed
Build and Deploy / build (push) Has been cancelled
The merge function was concatenating products on each query, causing
4 products to become 8, then 12, etc. Changed to replace incoming
data instead of merging.
2026-03-21 18:04:11 +02:00
Unchained
2876a8f80e fix: Replace WooCommerce env vars with Saleor API URL
Some checks failed
Build and Deploy / build (push) Has been cancelled
- NEXT_PUBLIC_WOOCOMMERCE_URL → NEXT_PUBLIC_SALEOR_API_URL
- Remove WooCommerce consumer key/secret (not needed for Saleor public API)
- Saleor API is public, no authentication required
2026-03-21 17:58:13 +02:00
Unchained
93005af0a1 Remove playwright - testing tool only
Some checks failed
Build and Deploy / build (push) Has been cancelled
2026-03-21 17:26:46 +02:00
Unchained
0b4e3f89d1 Add playwright for visual testing 2026-03-21 17:25:45 +02:00
Unchained
ec287c85ea Fix CSS cascade layers and header layout
- Rewrite globals.css to work properly with Tailwind 4 cascade layers
  - Remove conflicting * { padding: 0 } reset that broke Tailwind utilities
  - Organize styles into @layer base, @layer components, @layer utilities
- Fix newsletter centering (was off due to CSS layer conflicts)
- Fix header overlap on products pages (proper pt-[72px] spacing)
- Add solid header background (bg-white/80) instead of transparent
- Fix logo/nav positioning on desktop

Verified fixes with Playwright screenshots at 1280x800 and 390x844
2026-03-21 17:21:00 +02:00
15 changed files with 1317 additions and 495 deletions

View File

@@ -72,21 +72,8 @@ spec:
env: env:
- name: NODE_ENV - name: NODE_ENV
value: "production" value: "production"
- name: NEXT_PUBLIC_WOOCOMMERCE_URL - name: NEXT_PUBLIC_SALEOR_API_URL
valueFrom: value: "https://api.manoonoils.com/graphql/"
secretKeyRef:
name: woocommerce-credentials
key: WOOCOMMERCE_URL
- name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_KEY
valueFrom:
secretKeyRef:
name: woocommerce-credentials
key: WOOCOMMERCE_CONSUMER_KEY
- name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_SECRET
valueFrom:
secretKeyRef:
name: woocommerce-credentials
key: WOOCOMMERCE_CONSUMER_SECRET
- name: NEXT_PUBLIC_SITE_URL - name: NEXT_PUBLIC_SITE_URL
value: "https://dev.manoonoils.com" value: "https://dev.manoonoils.com"
volumeMounts: volumeMounts:
@@ -117,21 +104,8 @@ spec:
value: "3000" value: "3000"
- name: HOSTNAME - name: HOSTNAME
value: "0.0.0.0" value: "0.0.0.0"
- name: NEXT_PUBLIC_WOOCOMMERCE_URL - name: NEXT_PUBLIC_SALEOR_API_URL
valueFrom: value: "https://api.manoonoils.com/graphql/"
secretKeyRef:
name: woocommerce-credentials
key: WOOCOMMERCE_URL
- name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_KEY
valueFrom:
secretKeyRef:
name: woocommerce-credentials
key: WOOCOMMERCE_CONSUMER_KEY
- name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_SECRET
valueFrom:
secretKeyRef:
name: woocommerce-credentials
key: WOOCOMMERCE_CONSUMER_SECRET
- name: NEXT_PUBLIC_SITE_URL - name: NEXT_PUBLIC_SITE_URL
value: "https://dev.manoonoils.com" value: "https://dev.manoonoils.com"
resources: resources:

View File

@@ -2,61 +2,11 @@
/* ============================================ /* ============================================
MANOONOILS DESIGN SYSTEM MANOONOILS DESIGN SYSTEM
Inspired by premium skincare brands Tailwind 4 compatible - uses CSS layers
============================================ */ ============================================ */
:root {
/* Primary Colors */
--color-white: #ffffff;
--color-background: #fafafa;
--color-background-alt: #f5f5f5;
--color-foreground: #1a1a1a;
--color-foreground-muted: #666666;
--color-foreground-subtle: #999999;
/* Accent Colors */
--color-accent: #e8f0f5;
--color-accent-dark: #a8c5d8;
--color-accent-blue: #e8f0f5;
--color-gold: #c9a962;
--color-gold-light: #d4b978;
/* UI Colors */
--color-border: #e5e5e5;
--color-border-dark: #d1d1d1;
--color-cta: #000000;
--color-cta-hover: #333333;
--color-overlay: rgba(0, 0, 0, 0.4);
/* Spacing */
--space-xs: 4px;
--space-sm: 8px;
--space-md: 16px;
--space-lg: 24px;
--space-xl: 32px;
--space-2xl: 48px;
--space-3xl: 64px;
--space-4xl: 96px;
--space-5xl: 128px;
/* Typography */
--font-display: 'DM Sans', sans-serif;
--font-body: 'Inter', sans-serif;
/* Transitions */
--transition-fast: 150ms ease;
--transition-base: 250ms ease;
--transition-slow: 350ms ease;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
}
@theme inline { @theme inline {
/* Colors - reference :root variables */ /* Colors - reference CSS variables */
--color-white: var(--color-white); --color-white: var(--color-white);
--color-background: var(--color-background); --color-background: var(--color-background);
--color-background-alt: var(--color-background-alt); --color-background-alt: var(--color-background-alt);
@@ -79,6 +29,43 @@
--font-body: var(--font-body); --font-body: var(--font-body);
} }
/* ============================================
CSS VARIABLES
============================================ */
:root {
--color-white: #ffffff;
--color-background: #fafafa;
--color-background-alt: #f5f5f5;
--color-foreground: #1a1a1a;
--color-foreground-muted: #666666;
--color-foreground-subtle: #999999;
--color-accent: #e8f0f5;
--color-accent-dark: #a8c5d8;
--color-accent-blue: #e8f0f5;
--color-gold: #c9a962;
--color-gold-light: #d4b978;
--color-border: #e5e5e5;
--color-border-dark: #d1d1d1;
--color-cta: #000000;
--color-cta-hover: #333333;
--color-overlay: rgba(0, 0, 0, 0.4);
--font-display: 'DM Sans', sans-serif;
--font-body: 'Inter', sans-serif;
--transition-fast: 150ms ease;
--transition-base: 250ms ease;
--transition-slow: 350ms ease;
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
}
/* ============================================ /* ============================================
FONT IMPORTS FONT IMPORTS
============================================ */ ============================================ */
@@ -98,307 +85,276 @@
} }
/* ============================================ /* ============================================
BASE STYLES BASE STYLES (in Tailwind base layer)
============================================ */ ============================================ */
* { @layer base {
box-sizing: border-box; html {
margin: 0; scroll-behavior: smooth;
padding: 0; }
}
html { body {
scroll-behavior: smooth; background: var(--color-background);
} color: var(--color-foreground);
font-family: var(--font-body);
font-size: 16px;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body { h1, h2, h3, h4, h5, h6 {
background: var(--color-background); font-family: var(--font-display);
color: var(--color-foreground); font-weight: 500;
font-family: var(--font-body); line-height: 1.2;
font-size: 16px; letter-spacing: -0.02em;
line-height: 1.6; }
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; h1 {
font-size: clamp(2rem, 5vw, 3.5rem);
}
h2 {
font-size: clamp(1.5rem, 4vw, 2.5rem);
}
h3 {
font-size: clamp(1.25rem, 3vw, 1.75rem);
}
input, textarea, select {
font-family: var(--font-body);
font-size: 16px;
}
input:focus, textarea:focus, select:focus {
outline: none;
border-color: var(--color-foreground);
}
:focus-visible {
outline: 2px solid var(--color-foreground);
outline-offset: 2px;
}
} }
/* ============================================ /* ============================================
TYPOGRAPHY COMPONENTS
============================================ */ ============================================ */
h1, h2, h3, h4, h5, h6 { @layer components {
font-family: var(--font-display);
font-weight: 500;
line-height: 1.2;
letter-spacing: -0.02em;
}
h1 {
font-size: clamp(2rem, 5vw, 3.5rem);
}
h2 {
font-size: clamp(1.5rem, 4vw, 2.5rem);
}
h3 {
font-size: clamp(1.25rem, 3vw, 1.75rem);
}
.text-display {
font-family: var(--font-display);
font-weight: 500;
letter-spacing: -0.02em;
}
.text-body {
font-family: var(--font-body);
}
.text-uppercase {
text-transform: uppercase;
letter-spacing: 0.05em;
}
.text-caption {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 500;
}
.text-muted {
color: var(--color-foreground-muted);
}
.text-subtle {
color: var(--color-foreground-subtle);
}
/* ============================================
UTILITY CLASSES
============================================ */
.container {
width: 100%;
max-width: 1400px;
margin-left: auto;
margin-right: auto;
padding-left: 24px;
padding-right: 24px;
}
@media (min-width: 640px) {
.container { .container {
padding-left: 32px; width: 100%;
padding-right: 32px; max-width: 1400px;
margin-left: auto;
margin-right: auto;
padding-left: 24px;
padding-right: 24px;
} }
}
@media (min-width: 1024px) { @media (min-width: 640px) {
.container { .container {
padding-left: 48px; padding-left: 32px;
padding-right: 48px; padding-right: 32px;
}
} }
}
.container-narrow { @media (min-width: 1024px) {
max-width: 1200px; .container {
} padding-left: 48px;
padding-right: 48px;
}
}
.container-wide { .container-narrow {
max-width: 1600px; max-width: 1200px;
} }
/* Section spacing */ .container-wide {
.section { max-width: 1600px;
padding-top: var(--space-4xl); }
padding-bottom: var(--space-4xl);
}
.section-sm { .btn {
padding-top: var(--space-2xl); display: inline-flex;
padding-bottom: var(--space-2xl); align-items: center;
} justify-content: center;
padding: 14px 32px;
font-size: 13px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
border: none;
cursor: pointer;
transition: all var(--transition-base);
}
/* Flex utilities */ .btn-primary {
.flex-center { background: var(--color-cta);
display: flex; color: var(--color-white);
align-items: center; }
justify-content: center;
.btn-primary:hover {
background: var(--color-cta-hover);
}
.btn-secondary {
background: transparent;
color: var(--color-foreground);
border: 1px solid var(--color-border-dark);
}
.btn-secondary:hover {
background: var(--color-foreground);
color: var(--color-white);
border-color: var(--color-foreground);
}
.link-underline {
position: relative;
text-decoration: none;
}
.link-underline::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 0;
height: 1px;
background: currentColor;
transition: width var(--transition-base);
}
.link-underline:hover::after {
width: 100%;
}
.text-display {
font-family: var(--font-display);
font-weight: 500;
letter-spacing: -0.02em;
}
.text-body {
font-family: var(--font-body);
}
.text-uppercase {
text-transform: uppercase;
letter-spacing: 0.05em;
}
.text-caption {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 500;
}
.text-muted {
color: var(--color-foreground-muted);
}
.text-subtle {
color: var(--color-foreground-subtle);
}
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
} }
/* ============================================ /* ============================================
INTERACTIVE ELEMENTS UTILITIES
============================================ */ ============================================ */
/* Button Base */ @layer utilities {
.btn { .section {
display: inline-flex; padding-top: 96px;
align-items: center; padding-bottom: 96px;
justify-content: center; }
padding: 14px 32px;
font-size: 13px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
border: none;
cursor: pointer;
transition: all var(--transition-base);
}
.btn-primary { .section-sm {
background: var(--color-cta); padding-top: 48px;
color: var(--color-white); padding-bottom: 48px;
} }
.btn-primary:hover { ::-webkit-scrollbar {
background: var(--color-cta-hover); width: 8px;
} }
.btn-secondary { ::-webkit-scrollbar-track {
background: transparent; background: var(--color-background-alt);
color: var(--color-foreground); }
border: 1px solid var(--color-border-dark);
}
.btn-secondary:hover { ::-webkit-scrollbar-thumb {
background: var(--color-foreground); background: var(--color-border-dark);
color: var(--color-white); border-radius: 4px;
border-color: var(--color-foreground); }
}
/* Link underline animation */ ::-webkit-scrollbar-thumb:hover {
.link-underline { background: var(--color-foreground-muted);
position: relative; }
text-decoration: none;
}
.link-underline::after { .animate-fade-in {
content: ''; animation: fadeIn var(--transition-slow) forwards;
position: absolute; }
bottom: -2px;
left: 0;
width: 0;
height: 1px;
background: currentColor;
transition: width var(--transition-base);
}
.link-underline:hover::after { .animate-slide-up {
width: 100%; animation: slideUp var(--transition-slow) forwards;
}
.animate-slide-in-right {
animation: slideInRight var(--transition-slow) forwards;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideInRight {
from { opacity: 0; transform: translateX(100%); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes marquee {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
.animate-marquee {
animation: marquee 25s linear infinite;
}
.animate-marquee-slow {
animation: marquee 35s linear infinite;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
} }
/* ============================================ /* ============================================
FORM ELEMENTS REDUCED MOTION
============================================ */ ============================================ */
input, textarea, select {
font-family: var(--font-body);
font-size: 16px;
}
input:focus, textarea:focus, select:focus {
outline: none;
border-color: var(--color-foreground);
}
/* ============================================
SCROLLBAR
============================================ */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--color-background-alt);
}
::-webkit-scrollbar-thumb {
background: var(--color-border-dark);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-foreground-muted);
}
/* ============================================
ANIMATIONS
============================================ */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.animate-fade-in {
animation: fadeIn var(--transition-slow) forwards;
}
.animate-slide-up {
animation: slideUp var(--transition-slow) forwards;
}
.animate-slide-in-right {
animation: slideInRight var(--transition-slow) forwards;
}
/* Marquee Animations */
@keyframes marquee {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
.animate-marquee {
animation: marquee 25s linear infinite;
}
.animate-marquee-slow {
animation: marquee 35s linear infinite;
}
/* ============================================
ACCESSIBILITY
============================================ */
/* Focus visible styles */
:focus-visible {
outline: 2px solid var(--color-foreground);
outline-offset: 2px;
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
*, *,
*::before, *::before,
@@ -412,16 +368,3 @@ input:focus, textarea:focus, select:focus {
scroll-behavior: auto; scroll-behavior: auto;
} }
} }
/* Screen reader only */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}

View File

@@ -3,6 +3,12 @@ import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer"; import Footer from "@/components/layout/Footer";
import HeroVideo from "@/components/home/HeroVideo"; import HeroVideo from "@/components/home/HeroVideo";
import ProductCard from "@/components/product/ProductCard"; import ProductCard from "@/components/product/ProductCard";
import TrustBadges from "@/components/home/TrustBadges";
import AsSeenIn from "@/components/home/AsSeenIn";
import TestimonialsSection from "@/components/home/TestimonialsSection";
import BeforeAfterGallery from "@/components/home/BeforeAfterGallery";
import ProblemSection from "@/components/home/ProblemSection";
import HowItWorks from "@/components/home/HowItWorks";
export const metadata = { export const metadata = {
title: "ManoonOils - Premium Natural Oils for Hair & Skin", title: "ManoonOils - Premium Natural Oils for Hair & Skin",
@@ -30,6 +36,15 @@ export default async function Homepage() {
{/* Hero Section with Video Background */} {/* Hero Section with Video Background */}
<HeroVideo /> <HeroVideo />
{/* Trust Badges */}
<TrustBadges />
{/* As Seen In */}
<AsSeenIn />
{/* Problem Section - Create empathy */}
<ProblemSection />
{/* Main Content */} {/* Main Content */}
<div id="main-content" className="scroll-mt-[72px] lg:scroll-mt-[72px]"> <div id="main-content" className="scroll-mt-[72px] lg:scroll-mt-[72px]">
{/* Products Grid Section */} {/* Products Grid Section */}
@@ -69,6 +84,12 @@ export default async function Homepage() {
</section> </section>
)} )}
{/* Before/After Gallery */}
<BeforeAfterGallery />
{/* How It Works */}
<HowItWorks />
{/* Brand Story Section */} {/* Brand Story Section */}
<section className="py-24 px-4 sm:px-6 lg:px-8 bg-[#f8f9fa]"> <section className="py-24 px-4 sm:px-6 lg:px-8 bg-[#f8f9fa]">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
@@ -148,6 +169,9 @@ export default async function Homepage() {
</div> </div>
</section> </section>
{/* Testimonials Section */}
<TestimonialsSection />
{/* Newsletter Section */} {/* Newsletter Section */}
<section className="py-28 lg:py-32 px-4 sm:px-6 lg:px-8 bg-[#1a1a1a] text-white"> <section className="py-28 lg:py-32 px-4 sm:px-6 lg:px-8 bg-[#1a1a1a] text-white">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
@@ -162,15 +186,15 @@ export default async function Homepage() {
Subscribe to receive exclusive offers, beauty tips, and be the first to know about new products. Subscribe to receive exclusive offers, beauty tips, and be the first to know about new products.
</p> </p>
{/* Newsletter Form - Centered */} {/* Newsletter Form - Centered */}
<form className="flex flex-col sm:flex-row items-center justify-center w-full sm:w-auto gap-0"> <form className="flex flex-col sm:flex-row items-stretch justify-center max-w-md mx-auto gap-0">
<input <input
type="email" type="email"
placeholder="Enter your email" placeholder="Enter your email"
className="w-full sm:w-64 md:w-80 px-5 h-14 bg-white/10 border border-white/20 border-b-0 sm:border-b border-r-0 sm:border-r border-white/20 text-white placeholder:text-white/50 focus:border-white focus:outline-none transition-colors text-base text-center sm:text-left" className="flex-1 min-w-0 px-5 h-14 bg-white/10 border border-white/20 border-b-0 sm:border-b border-r-0 sm:border-r border-white/20 text-white placeholder:text-white/50 focus:border-white focus:outline-none transition-colors text-base text-center sm:text-left rounded-t sm:rounded-l sm:rounded-tr-none"
/> />
<button <button
type="submit" type="submit"
className="w-full sm:w-auto px-8 h-14 bg-white text-black text-sm uppercase tracking-[0.1em] font-medium hover:bg-white/90 transition-colors whitespace-nowrap flex-shrink-0" className="px-8 h-14 bg-white text-black text-sm uppercase tracking-[0.1em] font-medium hover:bg-white/90 transition-colors whitespace-nowrap flex-shrink-0 rounded-b sm:rounded-r sm:rounded-bl-none"
> >
Subscribe Subscribe
</button> </button>

View File

@@ -24,7 +24,7 @@ export default async function ProductsPage({ params }: ProductsPageProps) {
<main className="min-h-screen bg-white"> <main className="min-h-screen bg-white">
{/* Page Header */} {/* Page Header */}
<div className="pt-[140px] lg:pt-[160px]"> <div className="pt-[72px] lg:pt-[72px]">
<div className="border-b border-[#e5e5e5]"> <div className="border-b border-[#e5e5e5]">
<div className="container py-8 md:py-12"> <div className="container py-8 md:py-12">
<div className="flex flex-col md:flex-row md:items-end md:justify-between gap-4"> <div className="flex flex-col md:flex-row md:items-end md:justify-between gap-4">

View File

@@ -0,0 +1,88 @@
"use client";
import { motion } from "framer-motion";
const mediaLogos = [
{ name: "VOGUE", style: "serif" },
{ name: "Allure", style: "sans" },
{ name: "ELLE", style: "serif" },
{ name: "COSMOPOLITAN", style: "serif" },
{ name: "Bazaar", style: "serif" },
{ name: "GLAMOUR", style: "serif" },
{ name: "WOMEN'S HEALTH", style: "sans" },
{ name: "Shape", style: "sans" },
];
function LogoItem({ name }: { name: string }) {
const isSerif = name === "VOGUE" || name === "ELLE" || name === "COSMOPOLITAN" || name === "Bazaar" || name === "GLAMOUR";
return (
<div className="flex items-center justify-center px-10 py-4 grayscale opacity-40 hover:grayscale-0 hover:opacity-100 transition-all duration-500 flex-shrink-0">
<span
className={`
text-xl md:text-2xl tracking-[0.15em] text-white font-bold
${isSerif ? 'font-serif italic' : 'font-sans uppercase'}
`}
style={{
textShadow: '0 0 20px rgba(255,255,255,0.1)',
}}
>
{name}
</span>
</div>
);
}
export default function AsSeenIn() {
return (
<section className="py-12 bg-[#1a1a1a] overflow-hidden border-y border-white/10">
<div className="container mx-auto px-4 mb-8">
<motion.p
className="text-center text-[10px] uppercase tracking-[0.4em] text-white/30 font-medium"
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
>
As Featured In
</motion.p>
</div>
{/* Scrolling Marquee */}
<div className="relative">
{/* Left gradient fade */}
<div className="absolute left-0 top-0 bottom-0 w-32 bg-gradient-to-r from-[#1a1a1a] to-transparent z-10 pointer-events-none" />
{/* Right gradient fade */}
<div className="absolute right-0 top-0 bottom-0 w-32 bg-gradient-to-l from-[#1a1a1a] to-transparent z-10 pointer-events-none" />
{/* Marquee container */}
<div className="flex overflow-hidden">
<motion.div
className="flex items-center gap-16"
animate={{
x: [0, -50 + "%"],
}}
transition={{
x: {
repeat: Infinity,
repeatType: "loop",
duration: 30,
ease: "linear",
},
}}
>
{/* First set of logos */}
{mediaLogos.map((logo, index) => (
<LogoItem key={`first-${index}`} name={logo.name} />
))}
{/* Duplicate for seamless loop */}
{mediaLogos.map((logo, index) => (
<LogoItem key={`second-${index}`} name={logo.name} />
))}
</motion.div>
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,180 @@
"use client";
import { motion } from "framer-motion";
import { useState, useRef } from "react";
const results = [
{
id: 1,
name: "Facial Skin Transformation",
beforeImg: "https://minio-api.nodecrew.me/saleor/marketing/use_case_2.webp",
afterImg: "https://minio-api.nodecrew.me/saleor/marketing/use_case_2_1.webp",
timeline: "4-6 Weeks",
rating: 5,
reviewCount: 2847,
},
{
id: 2,
name: "Skin Radiance Transformation",
beforeImg: "https://minio-api.nodecrew.me/saleor/marketing/use_case_3.webp",
afterImg: "https://minio-api.nodecrew.me/saleor/marketing/use_case_3_1.webp",
timeline: "6-8 Weeks",
rating: 5,
reviewCount: 1856,
},
];
function BeforeAfterSlider({ result }: { result: typeof results[0] }) {
const [sliderPosition, setSliderPosition] = useState(50);
const containerRef = useRef<HTMLDivElement>(null);
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (!containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100;
setSliderPosition(Math.max(0, Math.min(100, x)));
};
const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => {
if (!containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = ((e.touches[0].clientX - rect.left) / rect.width) * 100;
setSliderPosition(Math.max(0, Math.min(100, x)));
};
return (
<div className="flex-1 min-w-0">
{/* Before/After Slider */}
<div
ref={containerRef}
className="relative aspect-[4/3] rounded-2xl overflow-hidden shadow-2xl cursor-ew-resize select-none"
onMouseMove={handleMouseMove}
onTouchMove={handleTouchMove}
>
{/* After Image */}
<img
src={result.afterImg}
alt="After - Smooth skin"
className="absolute inset-0 w-full h-full object-cover"
/>
{/* Before Image (clipped) */}
<div
className="absolute inset-0 overflow-hidden"
style={{ width: `${sliderPosition}%` }}
>
<img
src={result.beforeImg}
alt="Before - Wrinkled skin"
className="absolute inset-0 h-full object-cover"
style={{ width: `${100 / (sliderPosition / 100)}%`, maxWidth: 'none' }}
/>
</div>
{/* Slider Handle */}
<div
className="absolute top-0 bottom-0 w-1 bg-white shadow-lg cursor-ew-resize"
style={{ left: `${sliderPosition}%`, transform: 'translateX(-50%)' }}
>
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-10 h-10 bg-white rounded-full shadow-lg flex items-center justify-center">
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
</svg>
</div>
</div>
{/* Labels */}
<div className="absolute top-3 left-3 bg-black/70 text-white px-3 py-1.5 rounded-full text-xs font-medium backdrop-blur-sm">
BEFORE
</div>
<div className="absolute top-3 right-3 bg-black/70 text-white px-3 py-1.5 rounded-full text-xs font-medium backdrop-blur-sm">
AFTER
</div>
</div>
{/* Timeline and Rating */}
<div className="flex items-center justify-center gap-4 mt-4">
<div className="flex items-center gap-1.5">
<svg className="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="text-xs font-medium">{result.timeline}</span>
</div>
<div className="flex items-center gap-1.5">
<div className="flex">
{[1, 2, 3, 4, 5].map((star) => (
<svg key={star} className="w-4 h-4 fill-gold text-gold" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
</svg>
))}
</div>
<span className="text-xs text-[#666666]">({result.reviewCount.toLocaleString()})</span>
</div>
</div>
{/* Verified Badge */}
<div className="flex items-center justify-center gap-1.5 mt-2">
<svg className="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
<span className="text-xs text-green-700 font-medium">Verified</span>
</div>
</div>
);
}
export default function BeforeAfterGallery() {
return (
<section className="py-24 bg-[#faf9f7]">
<div className="container mx-auto px-4">
<motion.div
className="text-center mb-12"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
Real Results
</span>
<h2 className="text-3xl md:text-4xl font-medium mb-4">
See the Transformation
</h2>
</motion.div>
{/* Two transformations side by side */}
<div className="flex gap-6 max-w-6xl mx-auto">
{results.map((result, index) => (
<motion.div
key={result.id}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: index * 0.1 }}
className="flex-1 min-w-0"
>
<BeforeAfterSlider result={result} />
</motion.div>
))}
</div>
{/* CTA */}
<motion.div
className="text-center mt-12"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.4 }}
>
<a
href="/products"
className="inline-block px-10 py-4 bg-black text-white text-[13px] uppercase tracking-[0.15em] font-semibold hover:bg-[#333] transition-colors"
>
Start Your Transformation
</a>
</motion.div>
</div>
</section>
);
}

View File

@@ -29,7 +29,7 @@ export default function HeroVideo() {
{/* <source src="/videos/hero.mp4" type="video/mp4" /> */} {/* <source src="/videos/hero.mp4" type="video/mp4" /> */}
</video> </video>
{/* Gradient Overlay */} {/* Gradient Overlay */}
<div className="absolute inset-0 bg-gradient-to-b from-black/30 via-black/20 to-black/50" /> <div className="absolute inset-0 bg-gradient-to-b from-black/40 via-black/30 to-black/60" />
</div> </div>
{/* Fallback Background (shown when video isn't loaded) */} {/* Fallback Background (shown when video isn't loaded) */}
@@ -39,7 +39,7 @@ export default function HeroVideo() {
backgroundImage: `url('https://images.unsplash.com/photo-1608571423902-eed4a5ad8108?q=80&w=2574&auto=format&fit=crop')`, backgroundImage: `url('https://images.unsplash.com/photo-1608571423902-eed4a5ad8108?q=80&w=2574&auto=format&fit=crop')`,
}} }}
> >
<div className="absolute inset-0 bg-black/40" /> <div className="absolute inset-0 bg-gradient-to-b from-black/50 via-black/40 to-black/70" />
</div> </div>
{/* Content */} {/* Content */}
@@ -50,48 +50,94 @@ export default function HeroVideo() {
transition={{ duration: 0.8, delay: 0.3 }} transition={{ duration: 0.8, delay: 0.3 }}
className="max-w-4xl mx-auto" className="max-w-4xl mx-auto"
> >
{/* Tagline */} {/* Social Proof Micro */}
<motion.span
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.5 }}
className="inline-block text-xs md:text-sm uppercase tracking-[0.3em] mb-6 text-white/90"
>
Premium Organic Oils
</motion.span>
{/* Main Heading */}
<motion.h1
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.7 }}
className="text-5xl md:text-7xl lg:text-8xl font-medium mb-6 tracking-tight"
>
ManoonOils
</motion.h1>
{/* Subtitle */}
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.9 }}
className="text-lg md:text-xl text-white/80 mb-10 font-light max-w-xl mx-auto"
>
For hair and skin care
</motion.p>
{/* CTA Button */}
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 1.1 }} transition={{ duration: 0.6, delay: 0.4 }}
className="flex items-center justify-center gap-2 mb-6"
>
<div className="flex">
{[1, 2, 3, 4, 5].map((star) => (
<svg key={star} className="w-4 h-4 fill-gold text-gold" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
</svg>
))}
</div>
<span className="text-sm text-white/80">
Loved by 50,000+ customers worldwide
</span>
</motion.div>
{/* Main Heading - Outcome Focused */}
<motion.h1
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.5 }}
className="text-4xl md:text-6xl lg:text-7xl font-medium mb-6 tracking-tight leading-tight"
>
Transform Your Hair & Skin
<br />
<span className="text-white/90">with 100% Natural Oils</span>
</motion.h1>
{/* Subtitle - Expands on how */}
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.7 }}
className="text-lg md:text-xl text-white/80 mb-8 font-light max-w-2xl mx-auto leading-relaxed"
>
Cold-pressed, organic oils handcrafted with love.
No additives, no preservativesjust nature&apos;s purest goodness for your daily beauty ritual.
</motion.p>
{/* CTA Button - Action verb + value */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.9 }}
className="flex flex-col sm:flex-row items-center justify-center gap-4"
> >
<Link <Link
href="/products" href="/products"
className="inline-block px-10 py-4 bg-white text-black text-[13px] uppercase tracking-[0.1em] font-medium hover:bg-white/90 transition-colors duration-300" className="inline-block px-10 py-4 bg-white text-black text-[13px] uppercase tracking-[0.15em] font-semibold hover:bg-white/90 transition-all duration-300 hover:scale-105 shadow-lg hover:shadow-xl"
> >
Shop Now Transform My Hair & Skin
</Link> </Link>
<Link
href="/about"
className="inline-block px-10 py-4 border border-white/50 text-white text-[13px] uppercase tracking-[0.15em] font-medium hover:bg-white/10 transition-all duration-300"
>
Learn Our Story
</Link>
</motion.div>
{/* Trust Indicators */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.2, duration: 0.8 }}
className="flex flex-wrap items-center justify-center gap-6 mt-12 text-sm text-white/60"
>
<div className="flex items-center gap-2">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
<span>30-Day Money Back</span>
</div>
<div className="flex items-center gap-2">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
</svg>
<span>Free Shipping Over 3,000 RSD</span>
</div>
<div className="flex items-center gap-2">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg>
<span>Cruelty Free</span>
</div>
</motion.div> </motion.div>
</motion.div> </motion.div>
</div> </div>
@@ -102,7 +148,7 @@ export default function HeroVideo() {
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
transition={{ delay: 1.5, duration: 0.8 }} transition={{ delay: 1.5, duration: 0.8 }}
onClick={scrollToContent} onClick={scrollToContent}
className="absolute bottom-10 left-1/2 -translate-x-1/2 text-white/80 hover:text-white transition-colors cursor-pointer" className="absolute bottom-10 left-1/2 -translate-x-1/2 text-white/60 hover:text-white transition-colors cursor-pointer"
aria-label="Scroll to content" aria-label="Scroll to content"
> >
<motion.div <motion.div

View File

@@ -0,0 +1,89 @@
"use client";
import { motion } from "framer-motion";
export default function HowItWorks() {
const steps = [
{
number: "01",
title: "Choose Your Oil",
description: "Select from our collection of pure, cold-pressed oils formulated for your specific hair and skin needs.",
},
{
number: "02",
title: "Apply Daily",
description: " Massage a few drops into damp hair or skin. Our oils absorb instantly—never greasy, always nourishing.",
},
{
number: "03",
title: "See Results",
description: "Experience transformation in 4-6 weeks. Shinier hair, radiant skin, and confidence that glows.",
},
];
return (
<section className="py-24 bg-white">
<div className="container mx-auto px-4">
<motion.div
className="text-center mb-16"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
Simple Process
</span>
<h2 className="text-3xl md:text-4xl font-medium">
How ManoonOils Works
</h2>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 lg:gap-12 max-w-5xl mx-auto">
{steps.map((step, index) => (
<motion.div
key={index}
className="relative text-center"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.15 }}
>
{/* Connector line (not on last item) */}
{index < steps.length - 1 && (
<div className="hidden md:block absolute top-10 left-[60%] w-[80%] h-[2px] bg-gradient-to-r from-[#e5e5e5] to-transparent" />
)}
{/* Number circle */}
<div className="relative inline-flex items-center justify-center w-20 h-20 mb-6">
<div className="absolute inset-0 rounded-full bg-[#1a1a1a]" />
<span className="relative text-white text-2xl font-medium">{step.number}</span>
</div>
<h3 className="text-xl font-medium mb-3">{step.title}</h3>
<p className="text-[#666666] text-sm leading-relaxed max-w-xs mx-auto">
{step.description}
</p>
</motion.div>
))}
</div>
{/* CTA */}
<motion.div
className="text-center mt-16"
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.3 }}
>
<a
href="/products"
className="inline-block px-10 py-4 bg-black text-white text-[13px] uppercase tracking-[0.15em] font-semibold hover:bg-[#333] transition-colors"
>
Start Your Transformation
</a>
</motion.div>
</div>
</section>
);
}

View File

@@ -0,0 +1,73 @@
"use client";
import { motion } from "framer-motion";
export default function ProblemSection() {
return (
<section className="py-24 bg-[#faf9f7]">
<div className="container mx-auto px-4">
<motion.div
className="max-w-3xl mx-auto text-center"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-4 block">
The Problem
</span>
<h2 className="text-3xl md:text-4xl font-medium mb-8 leading-tight">
Tired of Hair & Skin Products That Don&apos;t Deliver?
</h2>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-5xl mx-auto mt-12">
{[
{
icon: (
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
),
problem: "Dry, Damaged Hair",
description: "Products leave your hair brittle, frizzy, and breaking despite expensive treatments",
},
{
icon: (
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
),
problem: "Confusing Ingredients",
description: "Can't pronounce what's in your skincare. parabens, sulfates, synthetic fragrances—dangerous toxins",
},
{
icon: (
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636" />
</svg>
),
problem: "No Real Results",
description: "Countless products promise miracles but deliver nothing but empty promises and wasted money",
},
].map((item, index) => (
<motion.div
key={index}
className="text-center p-8 bg-white rounded-2xl shadow-sm border border-[#f0ede8]"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<div className="w-16 h-16 mx-auto mb-6 rounded-full bg-red-50 flex items-center justify-center text-red-400">
{item.icon}
</div>
<h3 className="text-lg font-medium mb-3">{item.problem}</h3>
<p className="text-sm text-[#666666] leading-relaxed">{item.description}</p>
</motion.div>
))}
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,86 @@
"use client";
import { motion } from "framer-motion";
const badges = [
{
icon: (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
</svg>
),
stats: "4.9/5",
label: "Average Rating",
subtext: "Based on 2,847 reviews",
},
{
icon: (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" />
</svg>
),
stats: "50,000+",
label: "Happy Customers",
subtext: "Worldwide",
},
{
icon: (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" />
</svg>
),
stats: "100%",
label: "Natural Ingredients",
subtext: "No additives",
},
{
icon: (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 18.75a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 01-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 00-3.213-9.193 2.056 2.056 0 00-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 00-10.026 0 1.106 1.106 0 00-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12" />
</svg>
),
stats: "Free",
label: "Shipping",
subtext: "Orders over 3,000 RSD",
},
];
export default function TrustBadges() {
return (
<section className="py-14 bg-gradient-to-b from-[#faf9f7] to-white border-b border-[#e8e4e0]">
<div className="container mx-auto px-4">
<motion.div
className="grid grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
>
{badges.map((badge, index) => (
<motion.div
key={index}
className="flex flex-col items-center text-center p-6 bg-white rounded-2xl shadow-sm border border-[#f0ede8]"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.4, delay: index * 0.1 }}
>
<div className="w-12 h-12 rounded-full bg-[#1a1a1a] flex items-center justify-center text-white mb-4">
{badge.icon}
</div>
<p className="text-2xl lg:text-3xl font-semibold text-[#1a1a1a] tracking-tight">
{badge.stats}
</p>
<p className="text-sm font-medium text-[#1a1a1a] mt-1">
{badge.label}
</p>
<p className="text-xs text-[#888888] mt-1">
{badge.subtext}
</p>
</motion.div>
))}
</motion.div>
</div>
</section>
);
}

View File

@@ -53,68 +53,66 @@ export default function Header() {
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${ className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
scrolled scrolled
? "bg-white/95 backdrop-blur-md shadow-sm" ? "bg-white/95 backdrop-blur-md shadow-sm"
: "bg-transparent" : "bg-white/80 backdrop-blur-sm"
}`} }`}
> >
<div className="container"> <div className="relative flex items-center justify-between h-[72px]">
<div className="flex items-center justify-between h-[72px]"> {/* Mobile Menu Button */}
{/* Mobile Menu Button */} <button
className="lg:hidden p-2 -ml-2 hover:bg-black/5 rounded-full transition-colors"
onClick={() => setMobileMenuOpen(true)}
aria-label="Open menu"
>
<Menu className="w-5 h-5" />
</button>
{/* Left side - Desktop Nav */}
<nav className="hidden lg:flex items-center gap-10">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="text-[13px] uppercase tracking-[0.05em] text-[#1a1a1a] hover:text-[#666666] transition-colors relative group"
>
{link.label}
<span className="absolute -bottom-1 left-0 w-0 h-[1px] bg-current transition-all duration-300 group-hover:w-full" />
</Link>
))}
</nav>
{/* Logo - Centered (absolute on desktop, flex on mobile) */}
<Link href="/" className="flex-shrink-0 lg:absolute lg:left-1/2 lg:-translate-x-1/2">
<Image
src="https://minio-api.nodecrew.me/manoon-media/2024/09/cropped-manoon-logo_256x-1-1.png"
alt="ManoonOils"
width={150}
height={40}
className="h-7 w-auto object-contain"
priority
/>
</Link>
{/* Right side - Icons */}
<div className="flex items-center gap-1">
<button <button
className="lg:hidden p-2 -ml-2 hover:bg-black/5 rounded-full transition-colors" className="p-2 hover:bg-black/5 rounded-full transition-colors hidden sm:block"
onClick={() => setMobileMenuOpen(true)} aria-label="Account"
aria-label="Open menu"
> >
<Menu className="w-5 h-5" /> <User className="w-5 h-5" strokeWidth={1.5} />
</button> </button>
{/* Logo */} <button
<Link href="/" className="flex-shrink-0 lg:absolute lg:left-1/2 lg:-translate-x-1/2"> className="p-2 hover:bg-black/5 rounded-full transition-colors relative"
<Image onClick={toggleCart}
src="https://minio-api.nodecrew.me/manoon-media/2024/09/cropped-manoon-logo_256x-1-1.png" aria-label="Open cart"
alt="ManoonOils" >
width={150} <ShoppingBag className="w-5 h-5" strokeWidth={1.5} />
height={40} {itemCount > 0 && (
className="h-7 w-auto object-contain" <span className="absolute -top-0.5 -right-0.5 bg-black text-white text-[10px] w-[18px] h-[18px] rounded-full flex items-center justify-center font-medium">
priority {itemCount > 99 ? "99+" : itemCount}
/> </span>
</Link> )}
</button>
{/* Desktop Navigation - Centered */}
<nav className="hidden lg:flex items-center gap-10 mx-auto">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="text-[13px] uppercase tracking-[0.05em] text-[#1a1a1a] hover:text-[#666666] transition-colors relative group"
>
{link.label}
<span className="absolute -bottom-1 left-0 w-0 h-[1px] bg-current transition-all duration-300 group-hover:w-full" />
</Link>
))}
</nav>
{/* Icons */}
<div className="flex items-center gap-1">
<button
className="p-2 hover:bg-black/5 rounded-full transition-colors hidden sm:block"
aria-label="Account"
>
<User className="w-5 h-5" strokeWidth={1.5} />
</button>
<button
className="p-2 hover:bg-black/5 rounded-full transition-colors relative"
onClick={toggleCart}
aria-label="Open cart"
>
<ShoppingBag className="w-5 h-5" strokeWidth={1.5} />
{itemCount > 0 && (
<span className="absolute -top-0.5 -right-0.5 bg-black text-white text-[10px] w-[18px] h-[18px] rounded-full flex items-center justify-center font-medium">
{itemCount > 99 ? "99+" : itemCount}
</span>
)}
</button>
</div>
</div> </div>
</div> </div>
</header> </header>

View File

@@ -0,0 +1,88 @@
"use client";
import { motion } from "framer-motion";
interface ProductBenefitsProps {
locale?: string;
}
const benefits = [
{
icon: (
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 00-2.456 2.456z" />
</svg>
),
title: "Pure & Natural",
description: "100% natural ingredients with no additives or preservatives",
},
{
icon: (
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M15.182 15.182a4.5 4.5 0 01-6.364 0M21 12a9 9 0 11-18 0 9 9 0 0118 0zM9.75 9.75c0 .414-.168.75-.375.75S9 10.164 9 9.75 9.168 9 9.375 9s.375.336.375.75zm-.375 0h.008v.015h-.008V9.75zm5.625 0c0 .414-.168.75-.375.75s-.375-.336-.375-.75.168-.75.375-.75.375.336.375.75zm-.375 0h.008v.015h-.008V9.75z" />
</svg>
),
title: "Cruelty Free",
description: "Never tested on animals, ethically sourced ingredients",
},
{
icon: (
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" />
</svg>
),
title: "Made with Love",
description: "Handcrafted in small batches for maximum quality",
},
{
icon: (
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
</svg>
),
title: "Visible Results",
description: "See noticeable improvements in 4-6 weeks",
},
];
export default function ProductBenefits({ locale = "SR" }: ProductBenefitsProps) {
return (
<section className="py-20 bg-gradient-to-b from-white to-[#faf9f7]">
<div className="container mx-auto px-4">
<motion.div
className="text-center mb-12"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
{locale === "EN" ? "Why Choose This Product" : "Zašto odabrati ovaj proizvod"}
</span>
<h2 className="text-3xl md:text-4xl font-medium">
{locale === "EN" ? "The Manoon Difference" : "Manoon razlika"}
</h2>
</motion.div>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8 max-w-5xl mx-auto">
{benefits.map((benefit, index) => (
<motion.div
key={index}
className="text-center p-6 bg-white rounded-2xl shadow-sm border border-[#f0ede8]"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.4, delay: index * 0.1 }}
>
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-[#1a1a1a] flex items-center justify-center text-white">
{benefit.icon}
</div>
<h3 className="text-base font-medium mb-2">{benefit.title}</h3>
<p className="text-sm text-[#666666]">{benefit.description}</p>
</motion.div>
))}
</div>
</div>
</section>
);
}

View File

@@ -9,6 +9,13 @@ import type { Product } from "@/types/saleor";
import { useSaleorCheckoutStore } from "@/stores/saleorCheckoutStore"; import { useSaleorCheckoutStore } from "@/stores/saleorCheckoutStore";
import { getProductPrice, getLocalizedProduct } from "@/lib/saleor"; import { getProductPrice, getLocalizedProduct } from "@/lib/saleor";
import ProductCard from "@/components/product/ProductCard"; import ProductCard from "@/components/product/ProductCard";
import ProductBenefits from "@/components/product/ProductBenefits";
import ProductReviews from "@/components/product/ProductReviews";
import AsSeenIn from "@/components/home/AsSeenIn";
import TrustBadges from "@/components/home/TrustBadges";
import BeforeAfterGallery from "@/components/home/BeforeAfterGallery";
import HowItWorks from "@/components/home/HowItWorks";
import NewsletterSection from "@/components/home/NewsletterSection";
interface ProductDetailProps { interface ProductDetailProps {
product: Product; product: Product;
@@ -270,16 +277,16 @@ export default function ProductDetail({ product, relatedProducts, locale = "SR"
</div> </div>
</div> </div>
{/* Add to Cart Button */} {/* Add to Cart Button - Action verb + value */}
{isAvailable ? ( {isAvailable ? (
<button <button
onClick={handleAddToCart} onClick={handleAddToCart}
disabled={isAdding} disabled={isAdding}
className="w-full h-16 bg-black text-white text-base uppercase tracking-[0.15em] font-medium hover:bg-[#333333] active:bg-[#1a1a1a] transition-colors disabled:opacity-50 disabled:cursor-not-allowed mb-8" className="w-full h-16 bg-black text-white text-[13px] uppercase tracking-[0.15em] font-semibold hover:bg-[#333333] active:bg-[#1a1a1a] transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed mb-6 hover:scale-[1.02] shadow-lg hover:shadow-xl"
> >
{isAdding {isAdding
? (locale === "EN" ? "Adding..." : "Dodavanje...") ? (locale === "EN" ? "Adding..." : "Dodavanje...")
: (locale === "EN" ? "Add to Cart — Free Shipping" : "Dodaj u korpu — Besplatna dostava") : (locale === "EN" ? "Transform My Hair & Skin" : "Transformiši kosu i kožu")
} }
</button> </button>
) : ( ) : (
@@ -288,12 +295,45 @@ export default function ProductDetail({ product, relatedProducts, locale = "SR"
</div> </div>
)} )}
{/* Free Shipping Note */} {/* Free Shipping Note - with urgency */}
<p className="text-center text-sm text-[#666666] mb-10"> <div className="flex items-center justify-center gap-2 mb-6">
{locale === "EN" <svg className="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
? "Free shipping on orders over 3,000 RSD" <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4" />
: "Besplatna dostava za porudžbine preko 3.000 RSD"} </svg>
</p> <p className="text-sm text-[#666666]">
{locale === "EN"
? "Free shipping on orders over 3,000 RSD"
: "Besplatna dostava za porudžbine preko 3.000 RSD"}
</p>
</div>
{/* Trust Indicators */}
<div className="grid grid-cols-3 gap-4 mb-8 p-4 bg-[#f8f9fa] rounded-lg">
<div className="text-center">
<svg className="w-6 h-6 mx-auto mb-2 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
<p className="text-xs text-[#666666]">
{locale === "EN" ? "30-Day Guarantee" : "30-dnevna garancija"}
</p>
</div>
<div className="text-center">
<svg className="w-6 h-6 mx-auto mb-2 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
<p className="text-xs text-[#666666]">
{locale === "EN" ? "Secure Checkout" : "Sigurno plaćanje"}
</p>
</div>
<div className="text-center">
<svg className="w-6 h-6 mx-auto mb-2 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-8m15.357 8H15" />
</svg>
<p className="text-xs text-[#666666]">
{locale === "EN" ? "Easy Returns" : "Lak povrat"}
</p>
</div>
</div>
{/* Divider */} {/* Divider */}
<div className="border-t border-[#e5e5e5] mb-8" /> <div className="border-t border-[#e5e5e5] mb-8" />
@@ -353,6 +393,15 @@ export default function ProductDetail({ product, relatedProducts, locale = "SR"
</div> </div>
</section> </section>
{/* Customer Reviews */}
<ProductReviews locale={locale} productName={localized.name} />
{/* As Featured In - Full Width */}
<AsSeenIn />
{/* Before/After Gallery */}
<BeforeAfterGallery />
{/* Related Products */} {/* Related Products */}
{relatedProducts && relatedProducts.length > 0 && ( {relatedProducts && relatedProducts.length > 0 && (
<section className="py-20 lg:py-28 bg-[#f8f9fa]"> <section className="py-20 lg:py-28 bg-[#f8f9fa]">
@@ -365,19 +414,32 @@ export default function ProductDetail({ product, relatedProducts, locale = "SR"
{locale === "EN" ? "Similar Products" : "Slični proizvodi"} {locale === "EN" ? "Similar Products" : "Slični proizvodi"}
</h2> </h2>
</div> </div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8"> <div className="flex flex-wrap justify-center gap-6 lg:gap-8">
{relatedProducts.filter(p => p && p.id).slice(0, 4).map((relatedProduct, index) => ( {relatedProducts.filter(p => p && p.id).slice(0, 4).map((relatedProduct, index) => (
<ProductCard <div key={relatedProduct.id} className="w-full sm:w-[calc(50%-12px)] lg:w-[calc(25%-18px)]">
key={relatedProduct.id} <ProductCard
product={relatedProduct} product={relatedProduct}
index={index} index={index}
locale={locale} locale={locale}
/> />
</div>
))} ))}
</div> </div>
</div> </div>
</section> </section>
)} )}
{/* Product Benefits */}
<ProductBenefits locale={locale} />
{/* Trust Badges */}
<TrustBadges />
{/* How It Works */}
<HowItWorks />
{/* Newsletter */}
<NewsletterSection />
</> </>
); );
} }

View File

@@ -0,0 +1,175 @@
"use client";
import { motion } from "framer-motion";
interface ProductReviewsProps {
locale?: string;
productName?: string;
}
const reviews = [
{ id: 1, name: "Ana M.", location: "Belgrade", text: "Manoon Anti-age Serum transformed my skin in just 2 weeks!", rating: 5 },
{ id: 2, name: "Milica P.", location: "Novi Sad", text: "The best day serum I've ever used. My wrinkles are visibly reduced.", rating: 5 },
{ id: 3, name: "Jelena K.", location: "Belgrade", text: "Manoon night serum is pure magic. Wake up with glowing skin every morning.", rating: 5 },
{ id: 4, name: "Stefan R.", location: "Subotica", text: "The Anti-age Set is worth every dinar. My wife and I both use it.", rating: 5 },
{ id: 5, name: "Marija T.", location: "Kragujevac", text: "Finally found a serum that actually works! Manoon delivers on its promises.", rating: 5 },
{ id: 6, name: "Nikola V.", location: "Niš", text: "My fine lines are disappearing. This day serum is incredible.", rating: 5 },
{ id: 7, name: "Ivana L.", location: "Belgrade", text: "Manoon morning glow serum smells divine and works even better.", rating: 5 },
{ id: 8, name: "Dejan M.", location: "Novi Sad", text: "The night serum has transformed my skincare routine completely.", rating: 5 },
{ id: 9, name: "Sanja B.", location: "Kragujevac", text: "My skin looks 10 years younger after using Manoon for a month.", rating: 5 },
{ id: 10, name: "Marko J.", location: "Subotica", text: "The anti-age set makes a perfect gift. My mother loves it!", rating: 5 },
{ id: 11, name: "Petra D.", location: "Niš", text: "The texture of Manoon serum is so luxurious. Worth every penny.", rating: 5 },
{ id: 12, name: "Luka G.", location: "Belgrade", text: "Day serum absorbs instantly. No greasy feeling at all!", rating: 5 },
{ id: 13, name: "Maja S.", location: "Novi Sad", text: "My esthetician asked what I'm using. Manoon is now my secret!", rating: 5 },
{ id: 14, name: "Vladimir P.", location: "Kragujevac", text: "The night serum works while I sleep. Wake up to visibly smoother skin.", rating: 5 },
{ id: 15, name: "Katarina N.", location: "Subotica", text: "The Anti-age Set arrived beautifully packaged. Perfect for gifting.", rating: 5 },
{ id: 16, name: "Bojan R.", location: "Niš", text: "Been using Manoon for 3 months. My wrinkles are noticeably reduced.", rating: 5 },
{ id: 17, name: "Tamara F.", location: "Belgrade", text: "The day serum provides the perfect base under makeup.", rating: 5 },
{ id: 18, name: "Aleksandar K.", location: "Novi Sad", text: "Finally a Serbian brand that competes with luxury international brands!", rating: 5 },
{ id: 19, name: "Natalia M.", location: "Kragujevac", text: "My sensitive skin loves Manoon. No irritation at all.", rating: 5 },
{ id: 20, name: "Filip T.", location: "Subotica", text: "The anti-age serum is lightweight yet incredibly effective.", rating: 5 },
{ id: 21, name: "Andrea L.", location: "Niš", text: "Manoon night serum is my evening ritual. Skin looks amazing!", rating: 5 },
{ id: 22, name: "Ognjen P.", location: "Belgrade", text: "My friends keep asking what changed in my skincare routine.", rating: 5 },
{ id: 23, name: "Mila J.", location: "Novi Sad", text: "The Anti-age Set includes everything you need. Great value!", rating: 5 },
{ id: 24, name: "Dragan S.", location: "Kragujevac", text: "Even my husband noticed the difference. He now uses the day serum too!", rating: 5 },
{ id: 25, name: "Jovana V.", location: "Subotica", text: "The morning glow serum gives the most beautiful luminosity.", rating: 5 },
{ id: 26, name: "Stefan M.", location: "Niš", text: "Manoon products are now essential in my daily routine.", rating: 5 },
{ id: 27, name: "Ana R.", location: "Belgrade", text: "The night serum helped clear my complexion. Skin looks so healthy!", rating: 5 },
{ id: 28, name: "Nenad L.", location: "Novi Sad", text: "Anti-aging results visible within weeks. Highly recommend Manoon!", rating: 5 },
{ id: 29, name: "Sofija D.", location: "Kragujevac", text: "The texture is divine. Feels like a luxury spa treatment at home.", rating: 5 },
{ id: 30, name: "Velibor K.", location: "Subotica", text: "My crow's feet have diminished significantly. Thank you Manoon!", rating: 5 },
{ id: 31, name: "Irena M.", location: "Niš", text: "The Anti-age Set makes the perfect birthday gift for my mother.", rating: 5 },
{ id: 32, name: "Radoslav P.", location: "Belgrade", text: "Professional quality serum at an honest price. Serbian excellence!", rating: 5 },
{ id: 33, name: "Jelena B.", location: "Novi Sad", text: "My skin has never been this hydrated. Day serum is amazing!", rating: 5 },
{ id: 34, name: "Dimitrije S.", location: "Kragujevac", text: "The night serum is worth its weight in gold. Pure luxury!", rating: 5 },
{ id: 35, name: "Minela G.", location: "Subotica", text: "Manoon lives up to the hype. My skin looks refreshed and young.", rating: 5 },
{ id: 36, name: "Zoran T.", location: "Niš", text: "I've tried many serums. Manoon is by far the most effective.", rating: 5 },
{ id: 37, name: "Mirjana F.", location: "Belgrade", text: "The Anti-age Set transformed my mother's skincare routine completely.", rating: 5 },
{ id: 38, name: "Ivan J.", location: "Novi Sad", text: "Fast-acting serum with real results. I recommend Manoon to everyone.", rating: 5 },
{ id: 39, name: "Kristina P.", location: "Kragujevac", text: "The morning glow serum gives such a beautiful dewy finish.", rating: 5 },
{ id: 40, name: "Bratislav L.", location: "Subotica", text: "Noticeable results in just 2 weeks. This serum is the real deal!", rating: 5 },
{ id: 41, name: "Zorica M.", location: "Niš", text: "The night serum erased years from my face. Absolutely miraculous!", rating: 5 },
{ id: 42, name: "Patrik N.", location: "Belgrade", text: "Premium quality Serbian skincare that rivals international luxury brands.", rating: 5 },
{ id: 43, name: "Simona K.", location: "Novi Sad", text: "Manoon Anti-age Serum is the best investment in my skin ever.", rating: 5 },
{ id: 44, name: "Mladen D.", location: "Kragujevac", text: "The day serum absorbs in seconds. No waiting around!", rating: 5 },
{ id: 45, name: "Ljiljana R.", location: "Subotica", text: "Gifting the Anti-age Set to my sisters. They loved it!", rating: 5 },
{ id: 46, name: "Tomislav V.", location: "Niš", text: "My wrinkles are visibly reduced after using Manoon for a month.", rating: 5 },
{ id: 47, name: "Emilija S.", location: "Belgrade", text: "The night serum leaves my skin so soft and renewed every morning.", rating: 5 },
{ id: 48, name: "Andrija P.", location: "Novi Sad", text: "Manoon day serum is perfect under sunscreen. Essential duo!", rating: 5 },
{ id: 49, name: "Miona L.", location: "Kragujevac", text: "My skin looks radiant and youthful. Couldn't be happier with Manoon!", rating: 5 },
{ id: 50, name: "Slavko M.", location: "Subotica", text: "The Anti-age Set delivers visible results. True Serbian quality!", rating: 5 },
];
function ReviewCard({ review }: { review: typeof reviews[0] }) {
return (
<div className="flex-shrink-0 w-80 bg-white p-6 rounded-2xl shadow-sm border border-[#f0ede8] mx-3">
<div className="flex items-center gap-1 mb-3">
{[1, 2, 3, 4, 5].map((star) => (
<svg key={star} className="w-4 h-4 fill-gold text-gold" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
</svg>
))}
</div>
<p className="text-[#444444] text-sm leading-relaxed mb-4">"{review.text}"</p>
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-full bg-[#1a1a1a] flex items-center justify-center text-white text-sm font-medium">
{review.name.charAt(0)}
</div>
<div>
<p className="text-sm font-medium">{review.name}</p>
<p className="text-xs text-[#888888]">{review.location}</p>
</div>
</div>
</div>
);
}
export default function ProductReviews({ locale = "SR", productName = "this product" }: ProductReviewsProps) {
return (
<section className="py-16 bg-[#faf9f7] overflow-hidden">
<div className="container mx-auto px-4 mb-8">
<motion.div
className="text-center"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
Customer Reviews
</span>
<h2 className="text-3xl md:text-4xl font-medium">
What Customers Say
</h2>
<div className="flex items-center justify-center gap-4 mt-4">
<span className="text-5xl font-bold text-[#1a1a1a]">4.9</span>
<div>
<div className="flex gap-0.5">
{[1, 2, 3, 4, 5].map((star) => (
<svg key={star} className="w-5 h-5 fill-gold text-gold" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
</svg>
))}
</div>
<p className="text-sm text-[#666666] mt-1">Based on 50 reviews</p>
</div>
</div>
</motion.div>
</div>
{/* Scrolling Reviews Marquee */}
<div className="relative">
{/* Left gradient fade */}
<div className="absolute left-0 top-0 bottom-0 w-20 bg-gradient-to-r from-[#faf9f7] to-transparent z-10 pointer-events-none" />
{/* Right gradient fade */}
<div className="absolute right-0 top-0 bottom-0 w-20 bg-gradient-to-l from-[#faf9f7] to-transparent z-10 pointer-events-none" />
{/* First row - left to right */}
<div className="flex overflow-hidden mb-4">
<motion.div
className="flex items-center gap-0"
animate={{
x: [0, -50 + "%"],
}}
transition={{
x: {
repeat: Infinity,
repeatType: "loop",
duration: 120,
ease: "linear",
},
}}
>
{[...reviews, ...reviews].map((review, index) => (
<ReviewCard key={`first-${index}-${review.id}`} review={review} />
))}
</motion.div>
</div>
{/* Second row - right to left */}
<div className="flex overflow-hidden">
<motion.div
className="flex items-center gap-0"
animate={{
x: [-50 + "%", 0],
}}
transition={{
x: {
repeat: Infinity,
repeatType: "loop",
duration: 120,
ease: "linear",
},
}}
>
{[...reviews.slice(25), ...reviews.slice(0, 25), ...reviews.slice(25), ...reviews.slice(0, 25)].map((review, index) => (
<ReviewCard key={`second-${index}-${review.id}`} review={review} />
))}
</motion.div>
</div>
</div>
</section>
);
}

View File

@@ -24,12 +24,8 @@ export const saleorClient = new ApolloClient({
fields: { fields: {
products: { products: {
keyArgs: ["channel", "filter"], keyArgs: ["channel", "filter"],
merge(existing, incoming) { merge(_existing, incoming) {
if (!existing) return incoming; return incoming;
return {
...incoming,
edges: [...existing.edges, ...incoming.edges],
};
}, },
}, },
}, },