Shop integration
This commit is contained in:
112
Pottery-website/components/BlogPostLayout.tsx
Normal file
112
Pottery-website/components/BlogPostLayout.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useStore } from '../src/context/StoreContext';
|
||||
|
||||
interface BlogPostLayoutProps {
|
||||
title: string;
|
||||
category: string;
|
||||
date: string;
|
||||
image: string;
|
||||
imageAlt: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const BlogPostLayout: React.FC<BlogPostLayoutProps> = ({
|
||||
title,
|
||||
category,
|
||||
date,
|
||||
image,
|
||||
imageAlt,
|
||||
children,
|
||||
}) => {
|
||||
const { articles } = useStore();
|
||||
// Scroll to top on mount
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
const nextArticles = articles.filter(post => post.title !== title).slice(0, 2);
|
||||
|
||||
return (
|
||||
<div className="bg-stone-50 dark:bg-black min-h-screen font-body transition-colors duration-500">
|
||||
|
||||
<main className="pt-32 pb-24">
|
||||
{/* Article Header */}
|
||||
<article className="max-w-4xl mx-auto px-6 md:px-12">
|
||||
<div className="flex items-center space-x-4 mb-8 justify-center">
|
||||
<span className="text-xs uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-3 py-1 rounded-full">{category}</span>
|
||||
<span className="text-xs uppercase tracking-[0.2em] text-text-muted">{date}</span>
|
||||
</div>
|
||||
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="font-display text-5xl md:text-6xl lg:text-7xl text-center text-text-main dark:text-white mb-16 leading-tight"
|
||||
>
|
||||
{title}
|
||||
</motion.h1>
|
||||
|
||||
{/* Hero Image */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
className="w-full h-[40vh] md:h-[50vh] relative mb-16 overflow-hidden shadow-xl rounded-sm"
|
||||
>
|
||||
<img
|
||||
src={image}
|
||||
alt={imageAlt}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="prose prose-stone dark:prose-invert max-w-none mx-auto prose-headings:font-display prose-headings:font-light prose-p:font-light prose-p:leading-loose prose-a:text-terracotta hover:prose-a:text-terracotta-dark prose-img:rounded-sm">
|
||||
{children}
|
||||
</div>
|
||||
|
||||
{/* Read Next Section */}
|
||||
{nextArticles.length > 0 && (
|
||||
<div className="mt-24 pt-16 border-t border-stone-200 dark:border-stone-800">
|
||||
<h3 className="font-display text-3xl text-center mb-12 text-text-main dark:text-white">Read Next</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{nextArticles.map((post) => (
|
||||
<Link key={post.id} to={`/editorial/${post.slug}`} className="group block">
|
||||
<div className="aspect-[3/2] overflow-hidden bg-stone-100 mb-4">
|
||||
<img
|
||||
src={post.image}
|
||||
alt={post.title}
|
||||
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
<h4 className="font-display text-2xl text-text-main dark:text-white group-hover:underline decoration-1 underline-offset-4 mb-2">
|
||||
{post.title}
|
||||
</h4>
|
||||
<div className="flex items-center space-x-2 text-sm text-stone-500 uppercase tracking-widest">
|
||||
<span>{post.category}</span>
|
||||
<span>—</span>
|
||||
<span>{post.date}</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Back Link */}
|
||||
<div className="mt-20 text-center">
|
||||
<Link to="/editorial" className="inline-block border-b border-black dark:border-white pb-1 text-sm uppercase tracking-widest hover:text-stone-500 transition-colors">
|
||||
Back to Editorial
|
||||
</Link>
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BlogPostLayout;
|
||||
131
Pottery-website/components/Cart.tsx
Normal file
131
Pottery-website/components/Cart.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import React from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useStore } from '../src/context/StoreContext';
|
||||
|
||||
const Cart: React.FC = () => {
|
||||
const { cart, isCartOpen, setCartOpen, removeFromCart, updateQuantity } = useStore();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const subtotal = cart.reduce((total, item) => total + (item.price * item.quantity), 0);
|
||||
|
||||
const handleCheckout = () => {
|
||||
setCartOpen(false);
|
||||
navigate('/checkout');
|
||||
};
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isCartOpen && (
|
||||
<>
|
||||
{/* Backdrop */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
onClick={() => setCartOpen(false)}
|
||||
className="fixed inset-0 bg-black/30 backdrop-blur-sm z-[60]"
|
||||
/>
|
||||
|
||||
{/* Drawer */}
|
||||
<motion.div
|
||||
initial={{ x: '100%' }}
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: '100%' }}
|
||||
transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
|
||||
className="fixed top-0 right-0 h-full w-full max-w-md bg-white dark:bg-stone-950 z-[70] shadow-2xl flex flex-col"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="p-8 border-b border-stone-100 dark:border-stone-900 flex justify-between items-center">
|
||||
<h2 className="font-display text-2xl uppercase tracking-widest text-text-main dark:text-white">Your Bag</h2>
|
||||
<button
|
||||
onClick={() => setCartOpen(false)}
|
||||
className="p-2 hover:bg-stone-50 dark:hover:bg-stone-900 rounded-full transition-colors"
|
||||
>
|
||||
<span className="material-symbols-outlined text-stone-500">close</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Items List */}
|
||||
<div className="flex-1 overflow-y-auto p-8 space-y-8">
|
||||
{cart.length === 0 ? (
|
||||
<div className="h-full flex flex-col items-center justify-center text-stone-400 space-y-4">
|
||||
<span className="material-symbols-outlined text-4xl font-light">shopping_bag</span>
|
||||
<p className="font-light tracking-wide uppercase text-xs">Your bag is empty</p>
|
||||
<button
|
||||
onClick={() => setCartOpen(false)}
|
||||
className="text-text-main dark:text-white underline underline-offset-4 text-xs uppercase tracking-widest pt-4"
|
||||
>
|
||||
Start Shopping
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
cart.map((item) => (
|
||||
<div key={item.id} className="flex gap-6 group">
|
||||
<div className="w-24 aspect-[4/5] bg-stone-100 dark:bg-stone-900 overflow-hidden rounded-sm flex-shrink-0">
|
||||
<img src={item.image} alt={item.title} className="w-full h-full object-cover" />
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col justify-between py-1">
|
||||
<div className="flex justify-between items-start">
|
||||
<div>
|
||||
<h3 className="font-display text-lg text-text-main dark:text-white">{item.title}</h3>
|
||||
<p className="text-xs text-stone-500 uppercase tracking-widest mt-1">Ref. {item.number}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => removeFromCart(item.id)}
|
||||
className="text-stone-300 hover:text-red-400 transition-colors"
|
||||
>
|
||||
<span className="material-symbols-outlined text-sm">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex justify-between items-end">
|
||||
<div className="flex items-center border border-stone-100 dark:border-stone-800 rounded-sm">
|
||||
<button
|
||||
onClick={() => updateQuantity(item.id, item.quantity - 1)}
|
||||
className="px-2 py-1 hover:bg-stone-50 dark:hover:bg-stone-900 transition-colors"
|
||||
>
|
||||
<span className="material-symbols-outlined text-xs">remove</span>
|
||||
</button>
|
||||
<span className="px-3 text-xs font-medium w-8 text-center">{item.quantity}</span>
|
||||
<button
|
||||
onClick={() => updateQuantity(item.id, item.quantity + 1)}
|
||||
className="px-2 py-1 hover:bg-stone-50 dark:hover:bg-stone-900 transition-colors"
|
||||
>
|
||||
<span className="material-symbols-outlined text-xs">add</span>
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-sm font-light text-text-main dark:text-white">${(item.price * item.quantity).toFixed(2)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
{cart.length > 0 && (
|
||||
<div className="p-8 border-t border-stone-100 dark:border-stone-900 space-y-6">
|
||||
<div className="flex justify-between items-center bg-stone-50 dark:bg-stone-900/50 p-6 rounded-sm">
|
||||
<span className="text-xs uppercase tracking-widest text-stone-500">Subtotal</span>
|
||||
<span className="text-xl font-display text-text-main dark:text-white">${subtotal.toFixed(2)}</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleCheckout}
|
||||
type="button"
|
||||
className="w-full bg-black dark:bg-white text-white dark:text-black py-5 uppercase tracking-[0.3em] text-xs font-bold hover:opacity-90 transition-opacity shadow-xl"
|
||||
>
|
||||
Proceed to Checkout
|
||||
</button>
|
||||
<p className="text-[10px] text-center text-stone-400 uppercase tracking-widest italic">
|
||||
Shipping & taxes calculated at checkout
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
};
|
||||
|
||||
export default Cart;
|
||||
92
Pottery-website/components/FAQ.tsx
Normal file
92
Pottery-website/components/FAQ.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { useScrollFadeIn } from '../hooks/useScrollAnimations';
|
||||
|
||||
interface FAQItemProps {
|
||||
question: string;
|
||||
answer: string;
|
||||
}
|
||||
|
||||
const FAQItem: React.FC<FAQItemProps> = ({ question, answer }) => {
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
|
||||
return (
|
||||
<div className="border-b border-stone-200 dark:border-stone-700">
|
||||
<button
|
||||
className="w-full py-6 flex justify-between items-center text-left focus:outline-none"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
aria-expanded={isOpen}
|
||||
>
|
||||
<h3 className="text-lg font-display text-text-main dark:text-white">{question}</h3>
|
||||
<span className={`transform transition-transform duration-300 ${isOpen ? 'rotate-45' : ''}`}>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
className={`overflow-hidden transition-all duration-500 ease-in-out ${isOpen ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'}`}
|
||||
>
|
||||
<p className="pb-6 text-text-muted dark:text-gray-400 font-body leading-relaxed max-w-2xl">
|
||||
{answer}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const FAQ: React.FC = () => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
useScrollFadeIn(containerRef);
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
question: "Do you ship your ceramics internationally?",
|
||||
answer: "Currently, we ship our handmade pottery mainly within Texas and the United States. We occasionally open international shipping spots for specific drops. Sign up for our newsletter to be notified."
|
||||
},
|
||||
{
|
||||
question: "Are your pieces dishwasher and microwave safe?",
|
||||
answer: "Yes! Our functional stoneware, including mugs, plates, and bowls, is fired to cone 6 oxidation, making it durable for daily use. However, hand washing is always recommended to prolong the life of your unique handmade ceramics."
|
||||
},
|
||||
{
|
||||
question: "Where is your studio located?",
|
||||
answer: "Our studio is based in the heart of Corpus Christi, Texas. We take inspiration from the Gulf Coast landscape. We offer local pickup for our Corpus Christi neighbors!"
|
||||
},
|
||||
{
|
||||
question: "Do you offer pottery classes in Corpus Christi?",
|
||||
answer: "We are working on bringing intimate wheel-throwing workshops to our Corpus Christi studio soon. Check our 'Atelier' page or follow us on Instagram for announcements."
|
||||
},
|
||||
{
|
||||
question: "Do you take custom orders or commissions?",
|
||||
answer: "We accept a limited number of custom dinnerware commissions each year. If you are looking for a bespoke set for your home or restaurant, please contact us directly."
|
||||
},
|
||||
{
|
||||
question: "How often do you restock the shop?",
|
||||
answer: "We work in small batches and typically release a new 'Sandstone' or 'Seafoam' collection every 4-6 weeks. Join our email list to get early access to the next kiln opening."
|
||||
},
|
||||
{
|
||||
question: "What clay bodies and glazes do you use?",
|
||||
answer: "We use a proprietary blend of stoneware clay that mimics the texture of Texas limestone. Our glazes are formulated in-house to reflect colors of the sea and sand."
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section ref={containerRef} className="py-24 px-6 md:px-20 bg-stone-50 dark:bg-stone-900/50">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<span className="block font-body text-xs uppercase tracking-[0.2em] text-text-muted mb-8">
|
||||
Common Questions
|
||||
</span>
|
||||
<h2 className="font-display text-4xl md:text-5xl text-text-main dark:text-white mb-16">
|
||||
Studio FAQ
|
||||
</h2>
|
||||
<div className="flex flex-col">
|
||||
{faqs.map((faq, index) => (
|
||||
<FAQItem key={index} question={faq.question} answer={faq.answer} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default FAQ;
|
||||
@@ -10,7 +10,7 @@ const Footer: React.FC = () => {
|
||||
<div className="lg:col-span-5 flex flex-col justify-between h-full">
|
||||
<div>
|
||||
<h2 className="font-display text-6xl md:text-8xl leading-none tracking-tighter mb-8 bg-gradient-to-br from-white to-stone-400 bg-clip-text text-transparent">
|
||||
HOTCHPOTSH
|
||||
HOTSCHPOTSH
|
||||
</h2>
|
||||
<p className="font-body text-lg font-light text-stone-400 leading-relaxed max-w-md">
|
||||
Handcrafted ceramics for the modern home. Created with intention, fired with patience, and delivered with care.
|
||||
@@ -78,11 +78,12 @@ const Footer: React.FC = () => {
|
||||
|
||||
{/* Bottom Bar */}
|
||||
<div className="border-t border-white/10 pt-12 flex flex-col md:flex-row justify-between items-center text-xs text-stone-500 tracking-widest uppercase font-light">
|
||||
<p>© 2024 HOTCHPOTSH Ceramics. All rights reserved.</p>
|
||||
<p>© 2024 HOTSCHPOTSH Ceramics. All rights reserved.</p>
|
||||
<div className="flex space-x-8 mt-6 md:mt-0">
|
||||
<a className="hover:text-white transition-colors" href="#">Privacy</a>
|
||||
<a className="hover:text-white transition-colors" href="#">Terms</a>
|
||||
<a className="hover:text-white transition-colors" href="#">Cookies</a>
|
||||
<a className="hover:text-white transition-colors" href="/admin">Admin</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,8 +2,10 @@ import React, { useState, useEffect } from 'react';
|
||||
import { NAV_ITEMS } from '../constants';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useStore } from '../src/context/StoreContext';
|
||||
|
||||
const Header: React.FC = () => {
|
||||
const { cart, setCartOpen } = useStore();
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
|
||||
@@ -18,8 +20,8 @@ const Header: React.FC = () => {
|
||||
return (
|
||||
<header
|
||||
className={`fixed top-0 w-full z-50 transition-all duration-500 ${scrolled
|
||||
? 'bg-white/80 dark:bg-black/80 backdrop-blur-xl py-2 border-b border-stone-200/50 dark:border-stone-800/50'
|
||||
: 'bg-transparent py-6'
|
||||
? 'bg-white/80 dark:bg-black/80 backdrop-blur-xl py-2 border-b border-stone-200/50 dark:border-stone-800/50'
|
||||
: 'bg-transparent py-6'
|
||||
}`}
|
||||
>
|
||||
<div className="max-w-[1920px] mx-auto px-6 md:px-12">
|
||||
@@ -38,7 +40,7 @@ const Header: React.FC = () => {
|
||||
{/* Logo */}
|
||||
<div className="flex-shrink-0 relative group cursor-pointer">
|
||||
<Link className="font-display text-4xl md:text-5xl font-light tracking-widest uppercase text-text-main dark:text-white" to="/">
|
||||
HOTCHPOTSH
|
||||
HOTSCHPOTSH
|
||||
</Link>
|
||||
{/* Subtle glow effect on hover */}
|
||||
<div className="absolute -inset-4 bg-white/20 blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-full" />
|
||||
@@ -64,10 +66,18 @@ const Header: React.FC = () => {
|
||||
<button className="hover:scale-110 transition-transform duration-300 hidden sm:block p-2">
|
||||
<span className="material-symbols-outlined text-xl font-light">search</span>
|
||||
</button>
|
||||
<a className="hover:scale-110 transition-transform duration-300 relative group p-2" href="#">
|
||||
<button
|
||||
onClick={() => setCartOpen(true)}
|
||||
className="hover:scale-110 transition-transform duration-300 relative group p-2"
|
||||
type="button"
|
||||
>
|
||||
<span className="material-symbols-outlined text-xl font-light">shopping_bag</span>
|
||||
<span className="absolute top-0 right-0 bg-black dark:bg-white text-white dark:text-black text-[9px] w-4 h-4 flex items-center justify-center rounded-full opacity-0 scale-50 group-hover:opacity-100 group-hover:scale-100 transition-all duration-300">2</span>
|
||||
</a>
|
||||
{cart.length > 0 && (
|
||||
<span className="absolute top-0 right-0 bg-black dark:bg-white text-white dark:text-black text-[9px] w-4 h-4 flex items-center justify-center rounded-full opacity-100 scale-100 transition-all duration-300">
|
||||
{cart.reduce((total, item) => total + item.quantity, 0)}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,10 +8,10 @@ const Hero: React.FC = () => {
|
||||
New Collection 2024
|
||||
</span>
|
||||
<h1 className="font-display text-6xl md:text-7xl lg:text-8xl xl:text-9xl text-text-main dark:text-white font-thin leading-[0.9] mb-10">
|
||||
Form <br /><span className="italic pl-12 md:pl-20 text-text-muted">of</span> Earth
|
||||
Earth <br /><span className="italic pl-12 md:pl-20 text-text-muted">of</span> Ocean
|
||||
</h1>
|
||||
<p className="font-body text-text-muted dark:text-gray-400 text-sm md:text-base font-light mb-12 max-w-sm leading-loose ml-1">
|
||||
Discover the imperfect perfection of hand-thrown stoneware. Pieces that bring silence and intention to your daily rituals.
|
||||
Handcrafted ceramics from the Texas Coast. Functional art inspired by the raw textures and colors of Corpus Christi. Small batch, slow-made.
|
||||
</p>
|
||||
<div className="ml-1">
|
||||
<a className="inline-block border-b border-text-main dark:border-white pb-1 text-text-main dark:text-white font-body text-xs uppercase tracking-[0.2em] hover:text-text-muted transition-colors duration-300" href="#">
|
||||
|
||||
@@ -1,45 +1,48 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { JOURNAL_ENTRIES } from '../constants';
|
||||
|
||||
const JournalSection: React.FC = () => {
|
||||
return (
|
||||
<section className="relative py-32 bg-terracotta-soft dark:bg-black overflow-hidden transition-colors duration-500">
|
||||
<div className="absolute inset-0 z-0 mix-blend-multiply opacity-30">
|
||||
<img
|
||||
alt="Atmospheric studio background"
|
||||
className="w-full h-full object-cover blur-3xl scale-110 grayscale"
|
||||
src="https://lh3.googleusercontent.com/aida-public/AB6AXuAipMlYLTcRT_hdc3VePfFIlrA56VzZ5G2y3gcRfmIZMERwGFKq2N19Gqo6mw7uZowXmjl2eJ89TI3Mcud2OyOfadO3mPVF_v0sI0OHupqM49WEFcWzH-Wbu3DL6bQ46F2Y8SIAk-NUQy8psjcIdBKRrM8fqdn4eOPANYTXpVxkLMAm4R0Axy4aEKNdmj917ZKKTxvXB-J8nGlITJkJ-ua7XcZOwGnfK5ttzyWW35A0oOSffCf972gmpV27wrVQgYJNLS7UyDdyQIQ"
|
||||
<img
|
||||
alt="Atmospheric studio background"
|
||||
className="w-full h-full object-cover blur-3xl scale-110 grayscale"
|
||||
src="https://lh3.googleusercontent.com/aida-public/AB6AXuAipMlYLTcRT_hdc3VePfFIlrA56VzZ5G2y3gcRfmIZMERwGFKq2N19Gqo6mw7uZowXmjl2eJ89TI3Mcud2OyOfadO3mPVF_v0sI0OHupqM49WEFcWzH-Wbu3DL6bQ46F2Y8SIAk-NUQy8psjcIdBKRrM8fqdn4eOPANYTXpVxkLMAm4R0Axy4aEKNdmj917ZKKTxvXB-J8nGlITJkJ-ua7XcZOwGnfK5ttzyWW35A0oOSffCf972gmpV27wrVQgYJNLS7UyDdyQIQ"
|
||||
/>
|
||||
</div>
|
||||
<div className="max-w-[1920px] mx-auto px-6 md:px-12 relative z-10">
|
||||
<div className="flex justify-between items-baseline mb-20 border-b border-text-main/20 dark:border-gray-800 pb-6">
|
||||
<h2 className="font-display text-6xl font-thin text-text-main dark:text-white">The <span className="italic">Journal</span></h2>
|
||||
<a className="text-xs uppercase tracking-[0.2em] text-text-main dark:text-white hover:text-text-muted transition-colors" href="#">View Archive</a>
|
||||
<h2 className="font-display text-6xl font-thin text-text-main dark:text-white">Editorial</h2>
|
||||
<Link className="text-xs uppercase tracking-[0.2em] text-text-main dark:text-white hover:text-text-muted transition-colors" to="/editorial">View Archive</Link>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12">
|
||||
{JOURNAL_ENTRIES.map((entry) => (
|
||||
<article key={entry.id} className={`group cursor-pointer ${entry.marginTop ? 'lg:mt-20' : ''}`}>
|
||||
<div className="relative h-[500px] overflow-hidden mb-8 shadow-md">
|
||||
<img
|
||||
alt={entry.title}
|
||||
className="w-full h-full object-cover transition-transform duration-[1.5s] group-hover:scale-105"
|
||||
src={entry.image}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/10 group-hover:bg-black/0 transition-colors duration-500"></div>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex items-center space-x-4 mb-4">
|
||||
<span className="text-[10px] uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-2 py-1 rounded-full">{entry.category}</span>
|
||||
<span className="text-[10px] uppercase tracking-[0.2em] text-text-muted">{entry.date}</span>
|
||||
<Link key={entry.id} to={entry.slug} className={`group cursor-pointer block ${entry.marginTop ? 'lg:mt-20' : ''}`}>
|
||||
<article>
|
||||
<div className="relative h-[500px] overflow-hidden mb-8 shadow-md">
|
||||
<img
|
||||
alt={entry.title}
|
||||
className="w-full h-full object-cover transition-transform duration-[1.5s] group-hover:scale-105"
|
||||
src={entry.image}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/10 group-hover:bg-black/0 transition-colors duration-500"></div>
|
||||
</div>
|
||||
<h3 className="font-display text-3xl text-text-main dark:text-white mb-4 leading-tight group-hover:underline decoration-1 underline-offset-4">
|
||||
{entry.title}
|
||||
</h3>
|
||||
<p className="text-sm font-light text-text-muted dark:text-gray-400 leading-loose max-w-sm">
|
||||
{entry.description}
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex items-center space-x-4 mb-4">
|
||||
<span className="text-[10px] uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-2 py-1 rounded-full">{entry.category}</span>
|
||||
<span className="text-[10px] uppercase tracking-[0.2em] text-text-muted">{entry.date}</span>
|
||||
</div>
|
||||
<h3 className="font-display text-3xl text-text-main dark:text-white mb-4 leading-tight group-hover:underline decoration-1 underline-offset-4">
|
||||
{entry.title}
|
||||
</h3>
|
||||
<p className="text-sm font-light text-text-muted dark:text-gray-400 leading-loose max-w-sm">
|
||||
{entry.description}
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user