Final
48
Pottery-website/.gitignore
vendored
@@ -1,24 +1,24 @@
|
|||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
pnpm-debug.log*
|
pnpm-debug.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
.idea
|
.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.suo
|
*.suo
|
||||||
*.ntvs*
|
*.ntvs*
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|||||||
@@ -1,66 +1,68 @@
|
|||||||
import React, { Suspense, lazy } from 'react';
|
import React, { Suspense, lazy } from 'react';
|
||||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
||||||
import Header from './components/Header';
|
import Header from './components/Header';
|
||||||
import Footer from './components/Footer';
|
import Footer from './components/Footer';
|
||||||
import Cart from './components/Cart';
|
import Cart from './components/Cart';
|
||||||
import ScrollToTop from './components/ScrollToTop';
|
import ScrollToTop from './components/ScrollToTop';
|
||||||
import RouteTransition from './components/RouteTransition';
|
import RouteTransition from './components/RouteTransition';
|
||||||
import { StoreProvider } from './src/context/StoreContext';
|
import { StoreProvider } from './src/context/StoreContext';
|
||||||
|
|
||||||
// Lazy load pages for better performance
|
// Lazy load pages for better performance
|
||||||
const Home = lazy(() => import('./pages/Home'));
|
const Home = lazy(() => import('./pages/Home'));
|
||||||
const Collections = lazy(() => import('./pages/Collections'));
|
const Collections = lazy(() => import('./pages/Collections'));
|
||||||
const Atelier = lazy(() => import('./pages/Atelier'));
|
const Atelier = lazy(() => import('./pages/Atelier'));
|
||||||
const Editorial = lazy(() => import('./pages/Editorial'));
|
const Editorial = lazy(() => import('./pages/Editorial'));
|
||||||
const ProductPhotography = lazy(() => import('./pages/Journal/ProductPhotography'));
|
const ProductPhotography = lazy(() => import('./pages/Journal/ProductPhotography'));
|
||||||
const PackagingGuide = lazy(() => import('./pages/Journal/PackagingGuide'));
|
const PackagingGuide = lazy(() => import('./pages/Journal/PackagingGuide'));
|
||||||
const MotivationInClay = lazy(() => import('./pages/Journal/MotivationInClay'));
|
const MotivationInClay = lazy(() => import('./pages/Journal/MotivationInClay'));
|
||||||
const ProductDetail = lazy(() => import('./pages/ProductDetail'));
|
const ProductDetail = lazy(() => import('./pages/ProductDetail'));
|
||||||
const ArticleDetail = lazy(() => import('./pages/ArticleDetail'));
|
const ArticleDetail = lazy(() => import('./pages/ArticleDetail'));
|
||||||
const Checkout = lazy(() => import('./pages/Checkout'));
|
const Checkout = lazy(() => import('./pages/Checkout'));
|
||||||
const MockPayment = lazy(() => import('./pages/MockPayment'));
|
const MockPayment = lazy(() => import('./pages/MockPayment'));
|
||||||
const Success = lazy(() => import('./pages/Success'));
|
const Success = lazy(() => import('./pages/Success'));
|
||||||
const Admin = lazy(() => import('./pages/Admin'));
|
const FAQ = lazy(() => import('./pages/FAQ'));
|
||||||
const FAQ = lazy(() => import('./pages/FAQ'));
|
const Shipping = lazy(() => import('./pages/Shipping'));
|
||||||
const Shipping = lazy(() => import('./pages/Shipping'));
|
const Returns = lazy(() => import('./pages/Returns'));
|
||||||
const Returns = lazy(() => import('./pages/Returns'));
|
const Contact = lazy(() => import('./pages/Contact'));
|
||||||
const Contact = lazy(() => import('./pages/Contact'));
|
const Privacy = lazy(() => import('./pages/Privacy'));
|
||||||
const Privacy = lazy(() => import('./pages/Privacy'));
|
const Cookies = lazy(() => import('./pages/Cookies'));
|
||||||
const Cookies = lazy(() => import('./pages/Cookies'));
|
|
||||||
|
function App() {
|
||||||
function App() {
|
return (
|
||||||
return (
|
<Router>
|
||||||
<Router>
|
<StoreProvider>
|
||||||
<StoreProvider>
|
<ScrollToTop />
|
||||||
<ScrollToTop />
|
<Header />
|
||||||
<Header />
|
<Cart />
|
||||||
<Cart />
|
<RouteTransition>
|
||||||
<RouteTransition>
|
<Suspense fallback={null}>
|
||||||
<Suspense fallback={null}>
|
<Routes>
|
||||||
<Routes>
|
<Route path="/" element={<Home />} />
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/collections" element={<Collections />} />
|
||||||
<Route path="/collections" element={<Collections />} />
|
|
||||||
<Route path="/collections/:slug" element={<ProductDetail />} />
|
<Route path="/collections/:slug" element={<ProductDetail />} />
|
||||||
<Route path="/atelier" element={<Atelier />} />
|
<Route path="/atelier" element={<Atelier />} />
|
||||||
<Route path="/editorial" element={<Editorial />} />
|
<Route path="/editorial" element={<Editorial />} />
|
||||||
|
<Route path="/editorial/product-photography-for-small-businesses" element={<ProductPhotography />} />
|
||||||
|
<Route path="/editorial/how-to-care-for-handmade-ceramics" element={<PackagingGuide />} />
|
||||||
|
<Route path="/editorial/finding-motivation-in-clay" element={<MotivationInClay />} />
|
||||||
<Route path="/editorial/:slug" element={<ArticleDetail />} />
|
<Route path="/editorial/:slug" element={<ArticleDetail />} />
|
||||||
<Route path="/checkout" element={<Checkout />} />
|
<Route path="/checkout" element={<Checkout />} />
|
||||||
<Route path="/mock-payment" element={<MockPayment />} />
|
<Route path="/mock-payment" element={<MockPayment />} />
|
||||||
<Route path="/success" element={<Success />} />
|
<Route path="/success" element={<Success />} />
|
||||||
<Route path="/admin" element={<Admin />} />
|
<Route path="/admin" element={<Navigate to="/" replace />} />
|
||||||
<Route path="/faq" element={<FAQ />} />
|
<Route path="/faq" element={<FAQ />} />
|
||||||
<Route path="/shipping" element={<Shipping />} />
|
<Route path="/shipping" element={<Shipping />} />
|
||||||
<Route path="/returns" element={<Returns />} />
|
<Route path="/returns" element={<Returns />} />
|
||||||
<Route path="/contact" element={<Contact />} />
|
<Route path="/contact" element={<Contact />} />
|
||||||
<Route path="/privacy" element={<Privacy />} />
|
<Route path="/privacy" element={<Privacy />} />
|
||||||
<Route path="/cookies" element={<Cookies />} />
|
<Route path="/cookies" element={<Cookies />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</RouteTransition>
|
</RouteTransition>
|
||||||
<Footer />
|
<Footer />
|
||||||
</StoreProvider>
|
</StoreProvider>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
@@ -1,118 +1,118 @@
|
|||||||
# KNUTH Ceramics — Handmade Pottery Atelier
|
# KNUTH Ceramics — Handmade Pottery Atelier
|
||||||
|
|
||||||
Welcome to the official repository for **KNUTH Ceramics**, a premium handmade pottery e-commerce platform and editorial site. This project features a React-based frontend, a Node.js/Express backend, and a PostgreSQL database for full content and order management.
|
Welcome to the official repository for **KNUTH Ceramics**, a premium handmade pottery e-commerce platform and editorial site. This project features a React-based frontend, a Node.js/Express backend, and a PostgreSQL database for full content and order management.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🏛️ Project Architecture
|
## 🏛️ Project Architecture
|
||||||
|
|
||||||
The application is split into two main parts:
|
The application is split into two main parts:
|
||||||
- **Frontend**: Built with React, Vite, Framer Motion (animations), and Vanilla CSS.
|
- **Frontend**: Built with React, Vite, Framer Motion (animations), and Vanilla CSS.
|
||||||
- **Backend**: Node.js/Express server providing a RESTful API for products, articles, and orders.
|
- **Backend**: Node.js/Express server providing a RESTful API for products, articles, and orders.
|
||||||
- **Database**: PostgreSQL with custom schema and migration scripts.
|
- **Database**: PostgreSQL with custom schema and migration scripts.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚡ Main Features
|
## ⚡ Main Features
|
||||||
|
|
||||||
- **🛒 Shopping Experience**: Persistent shopping bag (localStorage), quantity management, and a dedicated checkout flow.
|
- **🛒 Shopping Experience**: Persistent shopping bag (localStorage), quantity management, and a dedicated checkout flow.
|
||||||
- **💳 Mock Payment Integration**: A simulated payment system that validates the checkout process and records orders without requiring live Stripe keys.
|
- **💳 Mock Payment Integration**: A simulated payment system that validates the checkout process and records orders without requiring live Stripe keys.
|
||||||
- **📰 Editorial Section**: A "Journal" with rich storytelling, featuring articles with dynamic sections and featured post placement.
|
- **📰 Editorial Section**: A "Journal" with rich storytelling, featuring articles with dynamic sections and featured post placement.
|
||||||
- **🛠️ Admin Dashboard**: A secure management interface to:
|
- **🛠️ Admin Dashboard**: A secure management interface to:
|
||||||
- Create, update, and delete products (images, prices, descriptions).
|
- Create, update, and delete products (images, prices, descriptions).
|
||||||
- Manage articles (rich content editor, featuring toggle).
|
- Manage articles (rich content editor, featuring toggle).
|
||||||
- **Fulfill Orders**: View customer details and update shipping status (Pending → Shipped → Delivered).
|
- **Fulfill Orders**: View customer details and update shipping status (Pending → Shipped → Delivered).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Getting Started
|
## 🚀 Getting Started
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
- **Node.js** (v18+ recommended)
|
- **Node.js** (v18+ recommended)
|
||||||
- **PostgreSQL** instance (Local or Remote)
|
- **PostgreSQL** instance (Local or Remote)
|
||||||
- **npm** or **yarn**
|
- **npm** or **yarn**
|
||||||
|
|
||||||
### 1. Database Setup
|
### 1. Database Setup
|
||||||
1. Create a new PostgreSQL database (e.g., `pottery_db`).
|
1. Create a new PostgreSQL database (e.g., `pottery_db`).
|
||||||
2. Navigate to the `server` directory:
|
2. Navigate to the `server` directory:
|
||||||
```bash
|
```bash
|
||||||
cd server
|
cd server
|
||||||
```
|
```
|
||||||
3. Initialize the schema:
|
3. Initialize the schema:
|
||||||
```bash
|
```bash
|
||||||
# Option A: Run scripts manually using schema.sql
|
# Option A: Run scripts manually using schema.sql
|
||||||
# Option B: Run the setup script (requires DB credentials in .env)
|
# Option B: Run the setup script (requires DB credentials in .env)
|
||||||
node db_setup.js
|
node db_setup.js
|
||||||
```
|
```
|
||||||
4. Run migrations for the latest features (like the `orders` table):
|
4. Run migrations for the latest features (like the `orders` table):
|
||||||
```bash
|
```bash
|
||||||
node migrate.js
|
node migrate.js
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Backend Configuration
|
### 2. Backend Configuration
|
||||||
1. In the `server` directory, create a `.env` file based on the following template:
|
1. In the `server` directory, create a `.env` file based on the following template:
|
||||||
```env
|
```env
|
||||||
PORT=5000
|
PORT=5000
|
||||||
DB_USER=your_username
|
DB_USER=your_username
|
||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_NAME=pottery_db
|
DB_NAME=pottery_db
|
||||||
DB_PASSWORD=your_password
|
DB_PASSWORD=your_password
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
```
|
```
|
||||||
2. Install dependencies:
|
2. Install dependencies:
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
3. Start the server:
|
3. Start the server:
|
||||||
```bash
|
```bash
|
||||||
# Development (with nodemon)
|
# Development (with nodemon)
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
# Production
|
# Production
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Frontend Configuration
|
### 3. Frontend Configuration
|
||||||
1. Return to the project root:
|
1. Return to the project root:
|
||||||
```bash
|
```bash
|
||||||
cd ..
|
cd ..
|
||||||
```
|
```
|
||||||
2. Install dependencies:
|
2. Install dependencies:
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
3. Start the development server:
|
3. Start the development server:
|
||||||
```bash
|
```bash
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
4. Open your browser at `http://localhost:5173`.
|
4. Open your browser at `http://localhost:5173`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛡️ Admin Dashboard Usage
|
## 🛡️ Admin Dashboard Usage
|
||||||
|
|
||||||
Access the dashboard by navigating to `/admin`.
|
Access the dashboard by navigating to `/admin`.
|
||||||
- **Products**: Manage your shop inventory.
|
- **Products**: Manage your shop inventory.
|
||||||
- **Editorial**: Write and manage blog posts.
|
- **Editorial**: Write and manage blog posts.
|
||||||
- **Orders**: Monitor incoming sales. Use the "Visibility" icon to view customer shipment details and update their fulfillment status.
|
- **Orders**: Monitor incoming sales. Use the "Visibility" icon to view customer shipment details and update their fulfillment status.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚠️ Important Considerations
|
## ⚠️ Important Considerations
|
||||||
|
|
||||||
- **Storage Limits**: The shopping cart uses `localStorage`. To avoid the `QuotaExceededError`, only item IDs and quantities are stored. The application automatically hydrates this data from the backend.
|
- **Storage Limits**: The shopping cart uses `localStorage`. To avoid the `QuotaExceededError`, only item IDs and quantities are stored. The application automatically hydrates this data from the backend.
|
||||||
- **Large Images**: When uploading high-resolution images via the Admin UI, ensure they are optimized. The system supports Base64 encoding for simplicity, but large files may impact performance.
|
- **Large Images**: When uploading high-resolution images via the Admin UI, ensure they are optimized. The system supports Base64 encoding for simplicity, but large files may impact performance.
|
||||||
- **Mock Checkout**: The "Mock Payment" page is designed for testing. In a production environment, this should be replaced with a live Stripe `CheckoutSession` or equivalent provider.
|
- **Mock Checkout**: The "Mock Payment" page is designed for testing. In a production environment, this should be replaced with a live Stripe `CheckoutSession` or equivalent provider.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 Directory Structure
|
## 📦 Directory Structure
|
||||||
|
|
||||||
- `/components`: Reusable UI elements (Cart, Header, Footer, etc.).
|
- `/components`: Reusable UI elements (Cart, Header, Footer, etc.).
|
||||||
- `/pages`: Main view routes (Home, Collections, Admin, Checkout).
|
- `/pages`: Main view routes (Home, Collections, Admin, Checkout).
|
||||||
- `/server`: Node Express backend and database logic.
|
- `/server`: Node Express backend and database logic.
|
||||||
- `/src/context`: Global state management via `StoreContext`.
|
- `/src/context`: Global state management via `StoreContext`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Crafted with care by Antigravity.**
|
**Crafted with care by Antigravity.**
|
||||||
|
|||||||
@@ -1,112 +1,110 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useStore } from '../src/context/StoreContext';
|
import { useStore } from '../src/context/StoreContext';
|
||||||
|
|
||||||
interface BlogPostLayoutProps {
|
interface BlogPostLayoutProps {
|
||||||
title: string;
|
title: string;
|
||||||
category: string;
|
category: string;
|
||||||
date: string;
|
date: string;
|
||||||
image: string;
|
image: string;
|
||||||
imageAlt: string;
|
imageAlt: string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
author?: string;
|
||||||
|
}
|
||||||
const BlogPostLayout: React.FC<BlogPostLayoutProps> = ({
|
|
||||||
title,
|
const BlogPostLayout: React.FC<BlogPostLayoutProps> = ({
|
||||||
category,
|
title,
|
||||||
date,
|
category,
|
||||||
image,
|
date,
|
||||||
imageAlt,
|
image,
|
||||||
children,
|
imageAlt,
|
||||||
}) => {
|
children,
|
||||||
const { articles } = useStore();
|
author = 'Claudia Knuth',
|
||||||
// Scroll to top on mount
|
}) => {
|
||||||
useEffect(() => {
|
const { articles } = useStore();
|
||||||
window.scrollTo(0, 0);
|
const getArticleHref = (slug: string) => (
|
||||||
}, []);
|
slug.startsWith('/editorial/') ? slug : `/editorial/${slug}`
|
||||||
|
);
|
||||||
const nextArticles = articles.filter(post => post.title !== title).slice(0, 2);
|
|
||||||
|
useEffect(() => {
|
||||||
return (
|
window.scrollTo(0, 0);
|
||||||
<div className="bg-stone-50 dark:bg-black min-h-screen font-body transition-colors duration-500">
|
}, []);
|
||||||
|
|
||||||
<main className="pt-32 pb-24">
|
const nextArticles = articles.filter(post => post.title !== title).slice(0, 2);
|
||||||
{/* Article Header */}
|
|
||||||
<article className="max-w-4xl mx-auto px-6 md:px-12">
|
return (
|
||||||
<div className="flex items-center space-x-4 mb-8 justify-center">
|
<div className="bg-stone-50 dark:bg-black min-h-screen font-body transition-colors duration-500">
|
||||||
<span className="text-xs uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-3 py-1 rounded-full">{category}</span>
|
<main className="pt-32 pb-24">
|
||||||
<span className="text-xs uppercase tracking-[0.2em] text-text-muted">{date}</span>
|
<article className="max-w-4xl mx-auto px-6 md:px-12">
|
||||||
</div>
|
<div className="flex items-center space-x-4 mb-8 justify-center flex-wrap gap-y-2">
|
||||||
|
<span className="text-xs uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-3 py-1 rounded-full">{category}</span>
|
||||||
<motion.h1
|
<span className="text-xs uppercase tracking-[0.2em] text-text-muted">{date}</span>
|
||||||
initial={{ opacity: 0, y: 20 }}
|
<span className="text-xs text-text-muted">by <span className="font-medium" itemProp="author">{author}</span></span>
|
||||||
animate={{ opacity: 1, y: 0 }}
|
</div>
|
||||||
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"
|
<motion.h1
|
||||||
>
|
initial={{ opacity: 0, y: 20 }}
|
||||||
{title}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
</motion.h1>
|
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"
|
||||||
{/* Hero Image */}
|
>
|
||||||
<motion.div
|
{title}
|
||||||
initial={{ opacity: 0, scale: 0.95 }}
|
</motion.h1>
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
|
||||||
transition={{ duration: 0.8, delay: 0.2 }}
|
<motion.div
|
||||||
className="w-full h-[40vh] md:h-[50vh] relative mb-16 overflow-hidden shadow-xl rounded-sm"
|
initial={{ opacity: 0, scale: 0.95 }}
|
||||||
>
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
<img
|
transition={{ duration: 0.8, delay: 0.2 }}
|
||||||
src={image}
|
className="w-full h-[40vh] md:h-[50vh] relative mb-16 overflow-hidden shadow-xl rounded-sm"
|
||||||
alt={imageAlt}
|
>
|
||||||
className="w-full h-full object-cover"
|
<img
|
||||||
/>
|
src={image}
|
||||||
</motion.div>
|
alt={imageAlt}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
{/* 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">
|
</motion.div>
|
||||||
{children}
|
|
||||||
</div>
|
<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}
|
||||||
{/* Read Next Section */}
|
</div>
|
||||||
{nextArticles.length > 0 && (
|
|
||||||
<div className="mt-24 pt-16 border-t border-stone-200 dark:border-stone-800">
|
{nextArticles.length > 0 && (
|
||||||
<h3 className="font-display text-3xl text-center mb-12 text-text-main dark:text-white">Read Next</h3>
|
<div className="mt-24 pt-16 border-t border-stone-200 dark:border-stone-800">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
<h3 className="font-display text-3xl text-center mb-12 text-text-main dark:text-white">Read Next</h3>
|
||||||
{nextArticles.map((post) => (
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||||
<Link key={post.id} to={`/editorial/${post.slug}`} className="group block">
|
{nextArticles.map((post) => (
|
||||||
<div className="aspect-[3/2] overflow-hidden bg-stone-100 mb-4">
|
<Link key={post.id} to={getArticleHref(post.slug)} className="group block">
|
||||||
<img
|
<div className="aspect-[3/2] overflow-hidden bg-stone-100 mb-4">
|
||||||
src={post.image}
|
<img
|
||||||
alt={post.title}
|
src={post.image}
|
||||||
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
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">
|
</div>
|
||||||
{post.title}
|
<h4 className="font-display text-2xl text-text-main dark:text-white group-hover:underline decoration-1 underline-offset-4 mb-2">
|
||||||
</h4>
|
{post.title}
|
||||||
<div className="flex items-center space-x-2 text-sm text-stone-500 uppercase tracking-widest">
|
</h4>
|
||||||
<span>{post.category}</span>
|
<div className="flex items-center space-x-2 text-sm text-stone-500 uppercase tracking-widest">
|
||||||
<span>—</span>
|
<span>{post.category}</span>
|
||||||
<span>{post.date}</span>
|
<span>-</span>
|
||||||
</div>
|
<span>{post.date}</span>
|
||||||
</Link>
|
</div>
|
||||||
))}
|
</Link>
|
||||||
</div>
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
{/* Back Link */}
|
|
||||||
<div className="mt-20 text-center">
|
<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">
|
<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
|
Back to Editorial
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
</div>
|
};
|
||||||
);
|
|
||||||
};
|
export default BlogPostLayout;
|
||||||
|
|
||||||
export default BlogPostLayout;
|
|
||||||
|
|||||||
@@ -1,118 +1,106 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { COLLECTIONS } from '../constants';
|
import { COLLECTIONS } from '../constants';
|
||||||
import { CollectionItem } from '../types';
|
import { CollectionItem } from '../types';
|
||||||
|
|
||||||
const cardVariants = {
|
const cardVariants = {
|
||||||
hidden: {
|
hidden: {
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
y: 80,
|
y: 80,
|
||||||
rotateX: 15,
|
rotateX: 15,
|
||||||
},
|
},
|
||||||
visible: (index: number) => ({
|
visible: (index: number) => ({
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
y: 0,
|
y: 0,
|
||||||
rotateX: 0,
|
rotateX: 0,
|
||||||
transition: {
|
transition: {
|
||||||
delay: index * 0.15,
|
delay: index * 0.15,
|
||||||
duration: 0.8,
|
duration: 0.8,
|
||||||
ease: [0.25, 0.46, 0.45, 0.94],
|
ease: [0.25, 0.46, 0.45, 0.94],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const Collections: React.FC = () => {
|
const Collections: React.FC = () => {
|
||||||
const col1 = [COLLECTIONS[0], COLLECTIONS[1]];
|
const col1 = COLLECTIONS.filter((_, i) => i % 4 === 0);
|
||||||
const col2 = [COLLECTIONS[2], COLLECTIONS[3]];
|
const col2 = COLLECTIONS.filter((_, i) => i % 4 === 1);
|
||||||
const col3 = [COLLECTIONS[4], COLLECTIONS[5]];
|
const col3 = COLLECTIONS.filter((_, i) => i % 4 === 2);
|
||||||
|
const col4 = COLLECTIONS.filter((_, i) => i % 4 === 3);
|
||||||
const renderCard = (item: CollectionItem, index: number) => (
|
|
||||||
<motion.a
|
const renderCard = (item: CollectionItem, index: number) => (
|
||||||
key={item.id}
|
<motion.a
|
||||||
className="group block cursor-pointer"
|
key={item.id}
|
||||||
href="#"
|
className="group block cursor-pointer"
|
||||||
variants={cardVariants}
|
href="#"
|
||||||
initial="hidden"
|
variants={cardVariants}
|
||||||
whileInView="visible"
|
initial="hidden"
|
||||||
viewport={{ once: true, margin: "-100px" }}
|
whileInView="visible"
|
||||||
custom={index}
|
viewport={{ once: true, margin: "-100px" }}
|
||||||
>
|
custom={index}
|
||||||
<div className={`relative overflow-hidden ${item.aspectRatio} mb-6`}>
|
>
|
||||||
{/* Image with clean hover effect */}
|
<div className={`relative overflow-hidden ${item.aspectRatio} mb-6`}>
|
||||||
<motion.img
|
{/* Image with clean hover effect */}
|
||||||
alt={`${item.title} collection`}
|
<motion.img
|
||||||
className="w-full h-full object-cover"
|
alt={`${item.title} collection`}
|
||||||
src={item.image}
|
className="w-full h-full object-cover"
|
||||||
whileHover={{ scale: 1.05 }}
|
src={item.image}
|
||||||
transition={{ duration: 0.6, ease: [0.25, 0.46, 0.45, 0.94] }}
|
whileHover={{ scale: 1.05 }}
|
||||||
/>
|
transition={{ duration: 0.6, ease: [0.25, 0.46, 0.45, 0.94] }}
|
||||||
{/* Subtle overlay that fades out on hover */}
|
/>
|
||||||
<motion.div
|
{/* Subtle overlay that fades out on hover */}
|
||||||
className="absolute inset-0 bg-black/5"
|
<motion.div
|
||||||
initial={{ opacity: 1 }}
|
className="absolute inset-0 bg-black/5"
|
||||||
whileHover={{ opacity: 0 }}
|
initial={{ opacity: 1 }}
|
||||||
transition={{ duration: 0.4 }}
|
whileHover={{ opacity: 0 }}
|
||||||
/>
|
transition={{ duration: 0.4 }}
|
||||||
{/* Clean reveal line effect on hover */}
|
/>
|
||||||
<motion.div
|
{/* Clean reveal line effect on hover */}
|
||||||
className="absolute bottom-0 left-0 right-0 h-1 bg-white/80"
|
<motion.div
|
||||||
initial={{ scaleX: 0 }}
|
className="absolute bottom-0 left-0 right-0 h-1 bg-white/80"
|
||||||
whileHover={{ scaleX: 1 }}
|
initial={{ scaleX: 0 }}
|
||||||
transition={{ duration: 0.5, ease: "easeOut" }}
|
whileHover={{ scaleX: 1 }}
|
||||||
style={{ transformOrigin: "left" }}
|
transition={{ duration: 0.5, ease: "easeOut" }}
|
||||||
/>
|
style={{ transformOrigin: "left" }}
|
||||||
</div>
|
/>
|
||||||
<motion.div
|
</div>
|
||||||
className="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4"
|
</motion.a>
|
||||||
initial={{ opacity: 0.8 }}
|
);
|
||||||
whileHover={{ opacity: 1 }}
|
|
||||||
>
|
return (
|
||||||
<h3 className="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all duration-300">
|
<section className="py-32 bg-warm-grey dark:bg-[#141210] transition-colors duration-500">
|
||||||
{item.title}
|
<div className="max-w-[1920px] mx-auto px-6 md:px-12">
|
||||||
</h3>
|
<motion.div
|
||||||
<motion.span
|
className="flex flex-col md:flex-row justify-between items-end mb-20 md:mb-32 px-4"
|
||||||
className="text-xs uppercase tracking-widest text-text-muted"
|
initial={{ opacity: 0, y: 40 }}
|
||||||
whileHover={{ x: 5 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.3 }}
|
viewport={{ once: true }}
|
||||||
>
|
transition={{ duration: 0.8, ease: "easeOut" }}
|
||||||
{item.number}
|
>
|
||||||
</motion.span>
|
<h2 className="font-display text-5xl md:text-7xl font-thin text-text-main dark:text-white">
|
||||||
</motion.div>
|
Curated <span className="italic text-text-muted">Editions</span>
|
||||||
</motion.a>
|
</h2>
|
||||||
);
|
<p className="hidden md:block font-body text-sm text-text-muted max-w-xs leading-relaxed text-right">
|
||||||
|
Explore our seasonal collections, fired in small batches.
|
||||||
return (
|
</p>
|
||||||
<section className="py-32 bg-warm-grey dark:bg-[#141210] transition-colors duration-500">
|
</motion.div>
|
||||||
<div className="max-w-[1920px] mx-auto px-6 md:px-12">
|
|
||||||
<motion.div
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 md:gap-8 lg:gap-12">
|
||||||
className="flex flex-col md:flex-row justify-between items-end mb-20 md:mb-32 px-4"
|
<div className="flex flex-col space-y-8 md:space-y-16">
|
||||||
initial={{ opacity: 0, y: 40 }}
|
{col1.map((item, idx) => renderCard(item, idx))}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
</div>
|
||||||
viewport={{ once: true }}
|
<div className="flex flex-col space-y-8 md:space-y-16 pt-16 md:pt-24">
|
||||||
transition={{ duration: 0.8, ease: "easeOut" }}
|
{col2.map((item, idx) => renderCard(item, idx + 2))}
|
||||||
>
|
</div>
|
||||||
<h2 className="font-display text-5xl md:text-7xl font-thin text-text-main dark:text-white">
|
<div className="flex flex-col space-y-8 md:space-y-16 pt-8 md:pt-12 lg:pt-0">
|
||||||
Curated <span className="italic text-text-muted">Editions</span>
|
{col3.map((item, idx) => renderCard(item, idx + 4))}
|
||||||
</h2>
|
</div>
|
||||||
<p className="hidden md:block font-body text-sm text-text-muted max-w-xs leading-relaxed text-right">
|
<div className="flex flex-col space-y-8 md:space-y-16 pt-24 md:pt-32 lg:pt-24">
|
||||||
Explore our seasonal collections, fired in small batches.
|
{col4.map((item, idx) => renderCard(item, idx + 6))}
|
||||||
</p>
|
</div>
|
||||||
</motion.div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 lg:gap-16">
|
</section>
|
||||||
<div className="flex flex-col space-y-16 md:space-y-32">
|
);
|
||||||
{col1.map((item, idx) => renderCard(item, idx))}
|
};
|
||||||
</div>
|
|
||||||
<div className="flex flex-col space-y-16 md:space-y-32 md:pt-32">
|
|
||||||
{col2.map((item, idx) => renderCard(item, idx + 2))}
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col space-y-16 md:space-y-32 md:pt-16 lg:pt-0">
|
|
||||||
{col3.map((item, idx) => renderCard(item, idx + 4))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Collections;
|
export default Collections;
|
||||||
@@ -41,28 +41,24 @@ const FAQ: React.FC = () => {
|
|||||||
|
|
||||||
const faqs = [
|
const faqs = [
|
||||||
{
|
{
|
||||||
question: "Do you ship your ceramics internationally?",
|
question: "Is the online shop currently open?",
|
||||||
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."
|
answer: "Our online shop is temporarily closed while we focus on new collections and studio work. Follow us on Instagram or sign up for our newsletter to be the first to know when it reopens. For commissions, reach out directly at knuth.claudia@gmail.com."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: "Are your pieces dishwasher and microwave safe?",
|
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."
|
answer: "Yes! Our functional stoneware, including mugs, plates, and bowls, is high-fire kiln fired, 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?",
|
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!"
|
answer: "Our work is rooted in Corpus Christi, Texas, inspired by the colors and textures of the Gulf Coast."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: "Do you offer pottery classes in Corpus Christi?",
|
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."
|
answer: "Pottery classes and wheel-throwing workshops are available through the Art Center of Corpus Christi. Visit the Art Center for current schedules and registration."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: "Do you take custom orders or commissions?",
|
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."
|
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, reach out directly at knuth.claudia@gmail.com."
|
||||||
},
|
|
||||||
{
|
|
||||||
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?",
|
question: "What clay bodies and glazes do you use?",
|
||||||
|
|||||||
@@ -1,89 +1,93 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { gsap } from 'gsap';
|
import { gsap } from 'gsap';
|
||||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||||
|
|
||||||
gsap.registerPlugin(ScrollTrigger);
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
const FeatureSection: React.FC = () => {
|
const FeatureSection: React.FC = () => {
|
||||||
const sectionRef = useRef<HTMLDivElement>(null);
|
const sectionRef = useRef<HTMLDivElement>(null);
|
||||||
const imageRef = useRef<HTMLDivElement>(null);
|
const imageRef = useRef<HTMLDivElement>(null);
|
||||||
const contentRef = useRef<HTMLDivElement>(null);
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const section = sectionRef.current;
|
const section = sectionRef.current;
|
||||||
const image = imageRef.current;
|
const image = imageRef.current;
|
||||||
const content = contentRef.current;
|
const content = contentRef.current;
|
||||||
|
|
||||||
if (!section || !image || !content) return;
|
if (!section || !image || !content) return;
|
||||||
|
|
||||||
// Image reveal animation
|
// Image reveal animation
|
||||||
gsap.fromTo(
|
gsap.fromTo(
|
||||||
image,
|
image,
|
||||||
{ clipPath: 'inset(100% 0 0 0)', opacity: 0 },
|
{ clipPath: 'inset(100% 0 0 0)', opacity: 0 },
|
||||||
{
|
{
|
||||||
clipPath: 'inset(0% 0 0 0)',
|
clipPath: 'inset(0% 0 0 0)',
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
duration: 1.2,
|
duration: 1.2,
|
||||||
ease: 'power3.out',
|
ease: 'power3.out',
|
||||||
scrollTrigger: {
|
scrollTrigger: {
|
||||||
trigger: section,
|
trigger: section,
|
||||||
start: 'top 60%',
|
start: 'top 60%',
|
||||||
toggleActions: 'play none none reverse',
|
toggleActions: 'play none none reverse',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Content fade in animation
|
// Content fade in animation
|
||||||
gsap.fromTo(
|
gsap.fromTo(
|
||||||
content,
|
content,
|
||||||
{ x: -60, opacity: 0 },
|
{ x: -60, opacity: 0 },
|
||||||
{
|
{
|
||||||
x: 0,
|
x: 0,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
duration: 1,
|
duration: 1,
|
||||||
ease: 'power3.out',
|
ease: 'power3.out',
|
||||||
scrollTrigger: {
|
scrollTrigger: {
|
||||||
trigger: section,
|
trigger: section,
|
||||||
start: 'top 50%',
|
start: 'top 50%',
|
||||||
toggleActions: 'play none none reverse',
|
toggleActions: 'play none none reverse',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
ScrollTrigger.getAll().forEach((trigger) => trigger.kill());
|
ScrollTrigger.getAll().forEach((trigger) => trigger.kill());
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section ref={sectionRef} className="py-32 md:py-48 bg-sage dark:bg-stone-900 overflow-hidden relative transition-colors duration-500">
|
<section ref={sectionRef} className="py-32 md:py-48 bg-sage dark:bg-stone-900 overflow-hidden relative transition-colors duration-500">
|
||||||
<div className="max-w-[1800px] mx-auto px-6">
|
<div className="max-w-[1800px] mx-auto px-6">
|
||||||
<div className="relative flex flex-col md:block">
|
<div className="relative flex flex-col md:block">
|
||||||
<div className="hidden md:block absolute -top-24 left-10 z-0 select-none opacity-[0.03] dark:opacity-[0.05] pointer-events-none">
|
<div className="hidden md:block absolute -top-24 left-10 z-0 select-none opacity-10 dark:opacity-20 pointer-events-none">
|
||||||
<span className="font-display text-[20rem] leading-none text-black dark:text-white">CRAFT</span>
|
<span className="font-display text-[20rem] leading-none text-black dark:text-white">CRAFT</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
ref={imageRef}
|
ref={imageRef}
|
||||||
className="w-full md:w-3/5 h-[600px] md:h-[800px] ml-auto relative z-10 shadow-2xl bg-center bg-cover bg-no-repeat"
|
className="w-full md:w-3/5 h-[400px] md:h-[600px] ml-auto relative z-10 shadow-2xl overflow-hidden"
|
||||||
style={{ backgroundImage: "url('/ceramic-cups.png')" }}
|
>
|
||||||
>
|
<img
|
||||||
</div>
|
src="/landingpage/2.png"
|
||||||
<div ref={contentRef} className="relative z-20 mt-[-100px] md:mt-0 md:absolute md:top-1/2 md:left-20 md:-translate-y-1/2 bg-white dark:bg-stone-900 p-12 md:p-20 shadow-xl max-w-xl mx-auto md:mx-0">
|
alt="Handcrafted stoneware ceramics by KNUTH Ceramics — slow-made pottery from Corpus Christi, Texas"
|
||||||
<span className="block w-12 h-[1px] bg-text-main mb-8"></span>
|
className="w-full h-full object-cover object-center"
|
||||||
<h2 className="font-display text-4xl md:text-5xl font-light mb-8 text-text-main dark:text-white leading-tight">
|
/>
|
||||||
The Art of <br /><i className="font-thin">Slow Living</i>
|
</div>
|
||||||
</h2>
|
<div ref={contentRef} className="relative z-20 mt-[-100px] md:mt-0 md:absolute md:top-1/2 md:left-20 md:-translate-y-1/2 bg-white dark:bg-stone-900 p-12 md:p-20 shadow-xl max-w-xl mx-auto md:mx-0">
|
||||||
<p className="font-body font-light text-text-muted dark:text-gray-400 mb-10 leading-loose text-sm">
|
<span className="block w-12 h-[1px] bg-text-main mb-8"></span>
|
||||||
We believe in the beauty of handmade objects. Our collection features a curated selection of ceramics designed to elevate the everyday. From sturdy mugs for your morning coffee to elegant vases that breathe life into a room, each piece is crafted with patience and intention.
|
<h2 className="font-display text-4xl md:text-5xl font-light mb-8 text-text-main dark:text-white leading-tight">
|
||||||
</p>
|
The Art of <br /><i className="font-thin">Slow Living</i>
|
||||||
<a className="group inline-flex items-center text-xs uppercase tracking-[0.2em] text-text-main dark:text-white font-medium" href="#">
|
</h2>
|
||||||
Read Our Story <span className="ml-2 group-hover:translate-x-1 transition-transform">→</span>
|
<p className="font-body font-light text-text-muted dark:text-gray-400 mb-10 leading-loose text-sm">
|
||||||
</a>
|
We believe in the beauty of handmade objects. Our collection features a curated selection of ceramics designed to elevate the everyday. From sturdy mugs for your morning coffee to elegant vases that breathe life into a room, each piece is crafted with patience and intention.
|
||||||
</div>
|
</p>
|
||||||
</div>
|
<a className="group inline-flex items-center text-xs uppercase tracking-[0.2em] text-text-main dark:text-white font-medium" href="#">
|
||||||
</div>
|
Read Our Story <span className="ml-2 group-hover:translate-x-1 transition-transform">→</span>
|
||||||
</section>
|
</a>
|
||||||
);
|
</div>
|
||||||
};
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default FeatureSection;
|
export default FeatureSection;
|
||||||
@@ -1,115 +1,125 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { FOOTER_LINKS } from '../constants';
|
import { FOOTER_LINKS } from '../constants';
|
||||||
|
|
||||||
const DISABLED_FOOTER_LINKS = new Set([
|
const DISABLED_FOOTER_LINKS = new Set([
|
||||||
'Best Sellers',
|
'Best Sellers',
|
||||||
'Gift Cards',
|
'Gift Cards',
|
||||||
'Sustainability',
|
'Sustainability',
|
||||||
'Careers',
|
'Careers',
|
||||||
'Press',
|
'Press',
|
||||||
]);
|
'Shipping',
|
||||||
|
'Returns',
|
||||||
const Footer: React.FC = () => {
|
]);
|
||||||
const renderFooterLink = (link: { label: string; href: string }) => {
|
|
||||||
const baseClassName = 'text-lg font-light transition-all duration-300 block';
|
const Footer: React.FC = () => {
|
||||||
|
const renderFooterLink = (link: { label: string; href: string }) => {
|
||||||
if (DISABLED_FOOTER_LINKS.has(link.label)) {
|
const baseClassName = 'text-lg font-light transition-all duration-300 block';
|
||||||
return (
|
|
||||||
<span className={`${baseClassName} text-stone-500 cursor-not-allowed`} aria-disabled="true">
|
if (DISABLED_FOOTER_LINKS.has(link.label)) {
|
||||||
{link.label}
|
return (
|
||||||
</span>
|
<span className={`${baseClassName} text-stone-500 cursor-not-allowed`} aria-disabled="true">
|
||||||
);
|
{link.label}
|
||||||
}
|
</span>
|
||||||
|
);
|
||||||
return (
|
}
|
||||||
<Link className={`${baseClassName} hover:text-stone-400 hover:pl-2`} to={link.href}>
|
|
||||||
{link.label}
|
return (
|
||||||
</Link>
|
<Link className={`${baseClassName} hover:text-stone-400 hover:pl-2`} to={link.href}>
|
||||||
);
|
{link.label}
|
||||||
};
|
</Link>
|
||||||
|
);
|
||||||
return (
|
};
|
||||||
<footer className="bg-primary dark:bg-black text-white pt-32 pb-12 px-6 md:px-12 border-t border-stone-800">
|
|
||||||
<div className="max-w-[1920px] mx-auto">
|
return (
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-16 lg:gap-24 mb-32">
|
<footer className="bg-primary dark:bg-black text-white pt-32 pb-12 px-6 md:px-12 border-t border-stone-800">
|
||||||
{/* Brand & Mission */}
|
<div className="max-w-[1920px] mx-auto">
|
||||||
<div className="lg:col-span-5 flex flex-col justify-between h-full">
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-16 lg:gap-24 mb-32">
|
||||||
<div>
|
{/* Brand & Mission */}
|
||||||
<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">
|
<div className="lg:col-span-5 flex flex-col justify-between h-full">
|
||||||
KNUTH Ceramics
|
<div>
|
||||||
</h2>
|
<h2 className="font-display text-6xl md:text-8xl leading-none tracking-tighter mb-6 bg-gradient-to-br from-white to-stone-400 bg-clip-text text-transparent">
|
||||||
<p className="font-body text-lg font-light text-stone-400 leading-relaxed max-w-md">
|
KNUTH Ceramics
|
||||||
Handcrafted ceramics for the modern home. Created with intention, fired with patience, and delivered with care.
|
</h2>
|
||||||
</p>
|
<div className="mb-8">
|
||||||
</div>
|
<span className="font-body text-[0.7rem] uppercase tracking-[0.2em] border border-stone-800 px-3 py-1.5 inline-block text-stone-300 bg-stone-900/40">
|
||||||
|
Online Shop Opening — Coming Soon
|
||||||
<div className="mt-16 lg:mt-0">
|
</span>
|
||||||
<h4 className="text-sm font-bold uppercase tracking-widest mb-6">Join our newsletter</h4>
|
</div>
|
||||||
<form className="flex flex-col sm:flex-row gap-4 max-w-md" onSubmit={(e) => e.preventDefault()}>
|
<p className="font-body text-lg font-light text-stone-400 leading-relaxed max-w-md">
|
||||||
<input
|
Handcrafted ceramics for the modern home. Created with intention, fired with patience, and delivered with care.
|
||||||
className="bg-white/5 border border-white/10 text-white placeholder-stone-500 focus:outline-none focus:border-white/30 text-sm px-6 py-4 w-full transition-colors"
|
</p>
|
||||||
placeholder="Enter your email"
|
</div>
|
||||||
type="email"
|
|
||||||
/>
|
<div className="mt-16 lg:mt-0">
|
||||||
<button
|
<h4 className="text-sm font-bold uppercase tracking-widest mb-4">Get in touch</h4>
|
||||||
className="bg-white text-black px-8 py-4 text-xs font-bold uppercase tracking-widest hover:bg-stone-200 transition-colors"
|
<a
|
||||||
type="submit"
|
href="mailto:knuth.claudia@gmail.com"
|
||||||
>
|
className="font-body text-stone-400 hover:text-white transition-colors text-base block mb-2"
|
||||||
Subscribe
|
>
|
||||||
</button>
|
knuth.claudia@gmail.com
|
||||||
</form>
|
</a>
|
||||||
</div>
|
<a
|
||||||
</div>
|
href="https://www.instagram.com/knuth.ceramics"
|
||||||
|
target="_blank"
|
||||||
{/* Links Columns */}
|
rel="noopener noreferrer"
|
||||||
<div className="lg:col-span-7 grid grid-cols-2 md:grid-cols-3 gap-12 pt-4">
|
className="font-body text-stone-400 hover:text-white transition-colors text-base block"
|
||||||
<div>
|
aria-label="Instagram"
|
||||||
<h4 className="text-xs font-bold uppercase tracking-widest mb-8 text-stone-500">{FOOTER_LINKS[0].title}</h4>
|
>
|
||||||
<ul className="space-y-6">
|
<svg viewBox="0 0 24 24" fill="currentColor" className="w-5 h-5">
|
||||||
{FOOTER_LINKS[0].links.map((link) => (
|
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/>
|
||||||
<li key={link.label}>
|
</svg>
|
||||||
{renderFooterLink(link)}
|
</a>
|
||||||
</li>
|
</div>
|
||||||
))}
|
</div>
|
||||||
</ul>
|
|
||||||
</div>
|
{/* Links Columns */}
|
||||||
<div>
|
<div className="lg:col-span-7 grid grid-cols-2 md:grid-cols-3 gap-12 pt-4">
|
||||||
<h4 className="text-xs font-bold uppercase tracking-widest mb-8 text-stone-500">{FOOTER_LINKS[1].title}</h4>
|
<div>
|
||||||
<ul className="space-y-6">
|
<h4 className="text-xs font-bold uppercase tracking-widest mb-8 text-stone-500">{FOOTER_LINKS[0].title}</h4>
|
||||||
{FOOTER_LINKS[1].links.map((link) => (
|
<ul className="space-y-6">
|
||||||
<li key={link.label}>
|
{FOOTER_LINKS[0].links.map((link) => (
|
||||||
{renderFooterLink(link)}
|
<li key={link.label}>
|
||||||
</li>
|
{renderFooterLink(link)}
|
||||||
))}
|
</li>
|
||||||
</ul>
|
))}
|
||||||
</div>
|
</ul>
|
||||||
<div>
|
</div>
|
||||||
<h4 className="text-xs font-bold uppercase tracking-widest mb-8 text-stone-500">{FOOTER_LINKS[2].title}</h4>
|
<div>
|
||||||
<ul className="space-y-6">
|
<h4 className="text-xs font-bold uppercase tracking-widest mb-8 text-stone-500">{FOOTER_LINKS[1].title}</h4>
|
||||||
{FOOTER_LINKS[2].links.map((link) => (
|
<ul className="space-y-6">
|
||||||
<li key={link.label}>
|
{FOOTER_LINKS[1].links.map((link) => (
|
||||||
{renderFooterLink(link)}
|
<li key={link.label}>
|
||||||
</li>
|
{renderFooterLink(link)}
|
||||||
))}
|
</li>
|
||||||
</ul>
|
))}
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div>
|
||||||
|
<h4 className="text-xs font-bold uppercase tracking-widest mb-8 text-stone-500">{FOOTER_LINKS[2].title}</h4>
|
||||||
{/* Bottom Bar */}
|
<ul className="space-y-6">
|
||||||
<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">
|
{FOOTER_LINKS[2].links.map((link) => (
|
||||||
<p>© 2025 KNUTH Ceramics. All rights reserved.</p>
|
<li key={link.label}>
|
||||||
<div className="flex space-x-8 mt-6 md:mt-0">
|
{renderFooterLink(link)}
|
||||||
<Link className="hover:text-white transition-colors" to="/privacy">Privacy</Link>
|
</li>
|
||||||
<Link className="hover:text-white transition-colors" to="/returns">Terms</Link>
|
))}
|
||||||
<Link className="hover:text-white transition-colors" to="/cookies">Cookies</Link>
|
</ul>
|
||||||
<Link className="hover:text-white transition-colors" to="/admin">Admin</Link>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</footer>
|
{/* 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>© 2026 KNUTH Ceramics. All rights reserved.</p>
|
||||||
|
<div className="flex space-x-8 mt-6 md:mt-0">
|
||||||
export default Footer;
|
<Link className="hover:text-white transition-colors" to="/privacy">Privacy</Link>
|
||||||
|
<Link className="hover:text-white transition-colors" to="/returns">Terms</Link>
|
||||||
|
<Link className="hover:text-white transition-colors" to="/cookies">Cookies</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Footer;
|
||||||
|
|||||||
@@ -1,179 +1,179 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { GALLERY_IMAGES } from '../constants';
|
import { GALLERY_IMAGES } from '../constants';
|
||||||
|
|
||||||
interface GalleryImage {
|
interface GalleryImage {
|
||||||
src: string;
|
src: string;
|
||||||
likes: number;
|
likes: number;
|
||||||
comments: number;
|
comments: number;
|
||||||
caption: string;
|
caption: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GallerySection: React.FC = () => {
|
const GallerySection: React.FC = () => {
|
||||||
const [selectedImage, setSelectedImage] = useState<GalleryImage | null>(null);
|
const [selectedImage, setSelectedImage] = useState<GalleryImage | null>(null);
|
||||||
|
|
||||||
// Double the images for seamless infinite scroll
|
// Double the images for seamless infinite scroll
|
||||||
const duplicatedImages = [...GALLERY_IMAGES, ...GALLERY_IMAGES] as GalleryImage[];
|
const duplicatedImages = [...GALLERY_IMAGES, ...GALLERY_IMAGES] as GalleryImage[];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className="py-20 bg-white dark:bg-background-dark overflow-hidden">
|
<section className="py-20 bg-white dark:bg-background-dark overflow-hidden">
|
||||||
<div className="max-w-[1920px] mx-auto px-4">
|
<div className="max-w-[1920px] mx-auto px-4">
|
||||||
<motion.div
|
<motion.div
|
||||||
className="flex justify-between items-center mb-8 px-2"
|
className="flex justify-between items-center mb-8 px-2"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
transition={{ duration: 0.6 }}
|
transition={{ duration: 0.6 }}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-4">
|
<a href="https://www.instagram.com/knuth.ceramics" target="_blank" rel="noopener noreferrer" className="flex items-center gap-4 group cursor-pointer">
|
||||||
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-purple-500 via-pink-500 to-orange-400 p-[2px]">
|
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-purple-500 via-pink-500 to-orange-400 p-[2px] transition-transform group-hover:scale-105 duration-300">
|
||||||
<div className="w-full h-full rounded-full bg-white dark:bg-background-dark flex items-center justify-center">
|
<div className="w-full h-full rounded-full bg-white dark:bg-background-dark flex items-center justify-center">
|
||||||
<span className="font-display text-lg">K</span>
|
<span className="font-display text-lg">K</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-display text-xl text-text-main dark:text-white">@knuth_ceramics</h4>
|
<h4 className="font-display text-xl text-text-main dark:text-white group-hover:underline">@knuth_ceramics</h4>
|
||||||
<p className="text-xs text-text-muted">24.8k followers</p>
|
<p className="text-xs text-text-muted">24.8k followers</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a>
|
||||||
<a className="px-6 py-2 border border-text-main dark:border-white text-xs uppercase tracking-widest text-text-main dark:text-white hover:bg-text-main hover:text-white dark:hover:bg-white dark:hover:text-black transition-all duration-300 rounded-full" href="#">
|
<a className="px-6 py-2 border border-text-main dark:border-white text-xs uppercase tracking-widest text-text-main dark:text-white hover:bg-text-main hover:text-white dark:hover:bg-white dark:hover:text-black transition-all duration-300 rounded-full" href="https://www.instagram.com/knuth.ceramics" target="_blank" rel="noopener noreferrer">
|
||||||
Follow
|
Follow
|
||||||
</a>
|
</a>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Infinite Carousel */}
|
{/* Infinite Carousel */}
|
||||||
<div className="relative group overflow-hidden">
|
<div className="relative group overflow-hidden">
|
||||||
<style>{`
|
<style>{`
|
||||||
@keyframes marquee {
|
@keyframes marquee {
|
||||||
0% { transform: translateX(0); }
|
0% { transform: translateX(0); }
|
||||||
100% { transform: translateX(-${GALLERY_IMAGES.length * 304}px); }
|
100% { transform: translateX(-${GALLERY_IMAGES.length * 304}px); }
|
||||||
}
|
}
|
||||||
.animate-marquee {
|
.animate-marquee {
|
||||||
animation: marquee 40s linear infinite;
|
animation: marquee 40s linear infinite;
|
||||||
}
|
}
|
||||||
.animate-marquee:hover {
|
.animate-marquee:hover {
|
||||||
animation-play-state: paused;
|
animation-play-state: paused;
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
<div className="flex gap-4 animate-marquee w-max py-4">
|
<div className="flex gap-4 animate-marquee w-max py-4">
|
||||||
{duplicatedImages.map((img, idx) => (
|
{duplicatedImages.map((img, idx) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={idx}
|
key={idx}
|
||||||
className="relative flex-shrink-0 w-72 h-72 overflow-hidden cursor-pointer rounded-lg"
|
className="relative flex-shrink-0 w-72 h-72 overflow-hidden cursor-pointer rounded-lg"
|
||||||
whileHover={{ scale: 1.02 }}
|
whileHover={{ scale: 1.02 }}
|
||||||
onClick={() => setSelectedImage(img)}
|
onClick={() => setSelectedImage(img)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt={img.caption}
|
alt={img.caption}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
src={img.src}
|
src={img.src}
|
||||||
/>
|
/>
|
||||||
{/* Instagram-style hover overlay */}
|
{/* Instagram-style hover overlay */}
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute inset-0 bg-black/50 flex items-center justify-center gap-8 text-white"
|
className="absolute inset-0 bg-black/50 flex items-center justify-center gap-8 text-white"
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
whileHover={{ opacity: 1 }}
|
whileHover={{ opacity: 1 }}
|
||||||
transition={{ duration: 0.2 }}
|
transition={{ duration: 0.2 }}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="material-symbols-outlined" style={{ fontVariationSettings: "'FILL' 1" }}>favorite</span>
|
<span className="material-symbols-outlined" style={{ fontVariationSettings: "'FILL' 1" }}>favorite</span>
|
||||||
<span className="font-bold">{img.likes.toLocaleString()}</span>
|
<span className="font-bold">{img.likes.toLocaleString()}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="material-symbols-outlined">chat_bubble</span>
|
<span className="material-symbols-outlined">chat_bubble</span>
|
||||||
<span className="font-bold">{img.comments}</span>
|
<span className="font-bold">{img.comments}</span>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Lightbox Modal */}
|
{/* Lightbox Modal */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{
|
{
|
||||||
selectedImage && (
|
selectedImage && (
|
||||||
<motion.div
|
<motion.div
|
||||||
className="fixed inset-0 bg-black/90 z-50 flex items-center justify-center p-4"
|
className="fixed inset-0 bg-black/90 z-50 flex items-center justify-center p-4"
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
exit={{ opacity: 0 }}
|
exit={{ opacity: 0 }}
|
||||||
onClick={() => setSelectedImage(null)}
|
onClick={() => setSelectedImage(null)}
|
||||||
>
|
>
|
||||||
<motion.div
|
<motion.div
|
||||||
className="relative max-w-4xl w-full bg-white dark:bg-stone-900 rounded-xl overflow-hidden flex flex-col md:flex-row"
|
className="relative max-w-4xl w-full bg-white dark:bg-stone-900 rounded-xl overflow-hidden flex flex-col md:flex-row"
|
||||||
initial={{ scale: 0.9, opacity: 0 }}
|
initial={{ scale: 0.9, opacity: 0 }}
|
||||||
animate={{ scale: 1, opacity: 1 }}
|
animate={{ scale: 1, opacity: 1 }}
|
||||||
exit={{ scale: 0.9, opacity: 0 }}
|
exit={{ scale: 0.9, opacity: 0 }}
|
||||||
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
|
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
{/* Image */}
|
{/* Image */}
|
||||||
<div className="md:w-2/3 aspect-square md:aspect-auto">
|
<div className="md:w-2/3 aspect-square md:aspect-auto">
|
||||||
<img
|
<img
|
||||||
src={selectedImage.src}
|
src={selectedImage.src}
|
||||||
alt={selectedImage.caption}
|
alt={selectedImage.caption}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Side panel */}
|
{/* Side panel */}
|
||||||
<div className="md:w-1/3 p-6 flex flex-col">
|
<div className="md:w-1/3 p-6 flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center gap-3 pb-4 border-b border-stone-200 dark:border-stone-700">
|
<div className="flex items-center gap-3 pb-4 border-b border-stone-200 dark:border-stone-700">
|
||||||
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-purple-500 via-pink-500 to-orange-400 p-[2px]">
|
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-purple-500 via-pink-500 to-orange-400 p-[2px]">
|
||||||
<div className="w-full h-full rounded-full bg-white dark:bg-stone-900 flex items-center justify-center">
|
<div className="w-full h-full rounded-full bg-white dark:bg-stone-900 flex items-center justify-center">
|
||||||
<span className="font-display text-sm">K</span>
|
<span className="font-display text-sm">K</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="font-bold text-sm text-text-main dark:text-white">knuth_ceramics</span>
|
<span className="font-bold text-sm text-text-main dark:text-white">knuth_ceramics</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Caption */}
|
{/* Caption */}
|
||||||
<div className="flex-1 py-4">
|
<div className="flex-1 py-4">
|
||||||
<p className="text-sm text-text-main dark:text-white">
|
<p className="text-sm text-text-main dark:text-white">
|
||||||
<span className="font-bold">knuth_ceramics</span> {selectedImage.caption}
|
<span className="font-bold">knuth_ceramics</span> {selectedImage.caption}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Actions */}
|
{/* Actions */}
|
||||||
<div className="border-t border-stone-200 dark:border-stone-700 pt-4">
|
<div className="border-t border-stone-200 dark:border-stone-700 pt-4">
|
||||||
<div className="flex gap-4 mb-4">
|
<div className="flex gap-4 mb-4">
|
||||||
<button className="hover:scale-110 transition-transform">
|
<button className="hover:scale-110 transition-transform">
|
||||||
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white" style={{ fontVariationSettings: "'FILL' 1" }}>favorite</span>
|
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white" style={{ fontVariationSettings: "'FILL' 1" }}>favorite</span>
|
||||||
</button>
|
</button>
|
||||||
<button className="hover:scale-110 transition-transform">
|
<button className="hover:scale-110 transition-transform">
|
||||||
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white">chat_bubble</span>
|
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white">chat_bubble</span>
|
||||||
</button>
|
</button>
|
||||||
<button className="hover:scale-110 transition-transform">
|
<button className="hover:scale-110 transition-transform">
|
||||||
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white">send</span>
|
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white">send</span>
|
||||||
</button>
|
</button>
|
||||||
<button className="hover:scale-110 transition-transform ml-auto">
|
<button className="hover:scale-110 transition-transform ml-auto">
|
||||||
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white">bookmark</span>
|
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white">bookmark</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-bold text-text-main dark:text-white">{selectedImage.likes.toLocaleString()} likes</p>
|
<p className="text-sm font-bold text-text-main dark:text-white">{selectedImage.likes.toLocaleString()} likes</p>
|
||||||
<p className="text-xs text-text-muted mt-1">{selectedImage.comments} comments</p>
|
<p className="text-xs text-text-muted mt-1">{selectedImage.comments} comments</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Close button */}
|
{/* Close button */}
|
||||||
<button
|
<button
|
||||||
className="absolute top-4 right-4 text-white bg-black/50 rounded-full p-2 hover:bg-black/70 transition-colors"
|
className="absolute top-4 right-4 text-white bg-black/50 rounded-full p-2 hover:bg-black/70 transition-colors"
|
||||||
onClick={() => setSelectedImage(null)}
|
onClick={() => setSelectedImage(null)}
|
||||||
>
|
>
|
||||||
<span className="material-symbols-outlined">close</span>
|
<span className="material-symbols-outlined">close</span>
|
||||||
</button>
|
</button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</AnimatePresence >
|
</AnimatePresence >
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default GallerySection;
|
export default GallerySection;
|
||||||
|
|||||||
@@ -1,119 +1,118 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { NAV_ITEMS } from '../constants';
|
import { NAV_ITEMS } from '../constants';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useStore } from '../src/context/StoreContext';
|
import { useStore } from '../src/context/StoreContext';
|
||||||
|
|
||||||
const Header: React.FC = () => {
|
const Header: React.FC = () => {
|
||||||
const { cart, setCartOpen } = useStore();
|
const { cart, setCartOpen } = useStore();
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
const [scrolled, setScrolled] = useState(false);
|
const [scrolled, setScrolled] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
setScrolled(window.scrollY > 50);
|
setScrolled(window.scrollY > 50);
|
||||||
};
|
};
|
||||||
window.addEventListener('scroll', handleScroll);
|
window.addEventListener('scroll', handleScroll);
|
||||||
return () => window.removeEventListener('scroll', handleScroll);
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
className={`fixed top-0 w-full z-50 transition-all duration-500 ${scrolled
|
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-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-transparent py-6'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="max-w-[1920px] mx-auto px-6 md:px-12">
|
<div className="max-w-[1920px] mx-auto px-6 md:px-12">
|
||||||
<div className="flex justify-between items-center h-20">
|
<div className="flex justify-between items-center h-20">
|
||||||
{/* Mobile Menu Button */}
|
{/* Mobile Menu Button */}
|
||||||
<div className="flex items-center md:hidden">
|
<div className="flex items-center md:hidden">
|
||||||
<button
|
<button
|
||||||
className="text-text-main dark:text-white p-2 hover:bg-stone-100 dark:hover:bg-stone-800 rounded-full transition-colors"
|
className="text-text-main dark:text-white p-2 hover:bg-stone-100 dark:hover:bg-stone-800 rounded-full transition-colors"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||||
>
|
>
|
||||||
<span className="material-symbols-outlined">menu</span>
|
<span className="material-symbols-outlined">menu</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className="flex-shrink-0 relative group cursor-pointer">
|
<div className="flex-shrink-0 relative group cursor-pointer">
|
||||||
<Link className="relative z-10 font-display text-4xl md:text-5xl font-light tracking-widest uppercase text-text-main dark:text-white" to="/">
|
<Link className="relative z-10 font-display text-4xl md:text-5xl font-light tracking-widest uppercase text-text-main dark:text-white" to="/">
|
||||||
KNUTH Ceramics
|
KNUTH Ceramics
|
||||||
</Link>
|
</Link>
|
||||||
{/* Subtle glow effect on hover */}
|
{/* 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 pointer-events-none" />
|
<div className="absolute -inset-4 bg-white/20 blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-full pointer-events-none" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Desktop Nav */}
|
{/* Desktop Nav */}
|
||||||
<nav className="hidden md:flex space-x-12">
|
<nav className="hidden md:flex space-x-12">
|
||||||
{NAV_ITEMS.map((item) => (
|
{NAV_ITEMS.map((item) => (
|
||||||
<Link
|
<Link
|
||||||
key={item.label}
|
key={item.label}
|
||||||
className="group relative text-xs uppercase tracking-[0.2em] text-text-main dark:text-white hover:text-black dark:hover:text-white transition-colors duration-300 py-2"
|
className="group relative text-xs uppercase tracking-[0.2em] text-text-main dark:text-white hover:text-black dark:hover:text-white transition-colors duration-300 py-2"
|
||||||
to={item.label === 'Collections' ? '/collections' : item.label === 'Atelier' ? '/atelier' : '/editorial'}
|
to={item.label === 'Collections' ? '/collections' : item.label === 'Atelier' ? '/atelier' : '/editorial'}
|
||||||
>
|
>
|
||||||
{item.label}
|
{item.label}
|
||||||
{/* Underline Reveal Animation */}
|
{/* Underline Reveal Animation */}
|
||||||
<span className="absolute bottom-0 left-0 w-0 h-[1px] bg-black dark:bg-white transition-all duration-300 group-hover:w-full" />
|
<span className="absolute bottom-0 left-0 w-0 h-[1px] bg-black dark:bg-white transition-all duration-300 group-hover:w-full" />
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Icons */}
|
{/* Icons */}
|
||||||
<div className="flex items-center space-x-6 text-text-main dark:text-white">
|
<div className="flex items-center space-x-6 text-text-main dark:text-white">
|
||||||
<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
|
||||||
</button>
|
onClick={() => setCartOpen(true)}
|
||||||
<button
|
className="hover:scale-110 transition-transform duration-300 relative group p-2"
|
||||||
onClick={() => setCartOpen(true)}
|
type="button"
|
||||||
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>
|
||||||
>
|
{cart.length > 0 && (
|
||||||
<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-100 scale-100 transition-all duration-300">
|
||||||
{cart.length > 0 && (
|
{cart.reduce((total, item) => total + item.quantity, 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">
|
</span>
|
||||||
{cart.reduce((total, item) => total + item.quantity, 0)}
|
)}
|
||||||
</span>
|
</button>
|
||||||
)}
|
*/}
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
{/* Mobile Menu Overlay */}
|
||||||
{/* Mobile Menu Overlay */}
|
<AnimatePresence>
|
||||||
<AnimatePresence>
|
{isMenuOpen && (
|
||||||
{isMenuOpen && (
|
<motion.div
|
||||||
<motion.div
|
initial={{ opacity: 0, height: 0 }}
|
||||||
initial={{ opacity: 0, height: 0 }}
|
animate={{ opacity: 1, height: 'auto' }}
|
||||||
animate={{ opacity: 1, height: 'auto' }}
|
exit={{ opacity: 0, height: 0 }}
|
||||||
exit={{ opacity: 0, height: 0 }}
|
className="md:hidden absolute top-full left-0 w-full bg-white/95 dark:bg-black/95 backdrop-blur-xl border-b border-stone-200 dark:border-stone-800 shadow-2xl overflow-hidden"
|
||||||
className="md:hidden absolute top-full left-0 w-full bg-white/95 dark:bg-black/95 backdrop-blur-xl border-b border-stone-200 dark:border-stone-800 shadow-2xl overflow-hidden"
|
>
|
||||||
>
|
<div className="flex flex-col p-8 space-y-6">
|
||||||
<div className="flex flex-col p-8 space-y-6">
|
{NAV_ITEMS.map((item, idx) => (
|
||||||
{NAV_ITEMS.map((item, idx) => (
|
<Link
|
||||||
<Link
|
key={item.label}
|
||||||
key={item.label}
|
to={item.label === 'Collections' ? '/collections' : item.label === 'Atelier' ? '/atelier' : '/editorial'}
|
||||||
to={item.label === 'Collections' ? '/collections' : item.label === 'Atelier' ? '/atelier' : '/editorial'}
|
className="text-lg uppercase tracking-[0.2em] text-text-main dark:text-white hover:pl-4 transition-all duration-300 border-l-2 border-transparent hover:border-black dark:hover:border-white"
|
||||||
className="text-lg uppercase tracking-[0.2em] text-text-main dark:text-white hover:pl-4 transition-all duration-300 border-l-2 border-transparent hover:border-black dark:hover:border-white"
|
onClick={() => setIsMenuOpen(false)}
|
||||||
onClick={() => setIsMenuOpen(false)}
|
>
|
||||||
>
|
<motion.span
|
||||||
<motion.span
|
initial={{ x: -20, opacity: 0 }}
|
||||||
initial={{ x: -20, opacity: 0 }}
|
animate={{ x: 0, opacity: 1 }}
|
||||||
animate={{ x: 0, opacity: 1 }}
|
transition={{ delay: idx * 0.1 }}
|
||||||
transition={{ delay: idx * 0.1 }}
|
>
|
||||||
>
|
{item.label}
|
||||||
{item.label}
|
</motion.span>
|
||||||
</motion.span>
|
</Link>
|
||||||
</Link>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
</motion.div>
|
)}
|
||||||
)}
|
</AnimatePresence>
|
||||||
</AnimatePresence>
|
</header>
|
||||||
</header>
|
);
|
||||||
);
|
};
|
||||||
};
|
|
||||||
|
export default Header;
|
||||||
export default Header;
|
|
||||||
|
|||||||
@@ -1,40 +1,44 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
const Hero: React.FC = () => {
|
|
||||||
return (
|
const Hero: React.FC = () => {
|
||||||
<section className="relative min-h-screen pt-24 w-full flex flex-col md:flex-row items-center overflow-hidden bg-background-light dark:bg-background-dark">
|
return (
|
||||||
<div className="w-full md:w-5/12 h-full flex flex-col justify-center px-6 md:pl-20 md:pr-12 py-20 z-10">
|
<section className="relative min-h-[85vh] pt-24 pb-12 w-full flex flex-col md:flex-row items-center overflow-hidden bg-background-light dark:bg-background-dark">
|
||||||
<span className="font-body text-xs uppercase tracking-[0.3em] text-text-muted mb-8 ml-1 block">
|
<div className="w-full md:w-1/2 h-full flex flex-col justify-center px-6 md:pl-20 md:pr-12 py-12 z-10">
|
||||||
New Collection 2024
|
<span className="font-body text-xs uppercase tracking-[0.3em] text-text-muted mb-12 ml-1 block">
|
||||||
</span>
|
New Collection 2026
|
||||||
<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">
|
</span>
|
||||||
Earth <br /><span className="italic pl-12 md:pl-20 text-text-muted">of</span> Ocean
|
<span className="font-body text-[0.7rem] uppercase tracking-[0.2em] border border-stone-300 dark:border-stone-700 px-3 py-1.5 inline-block text-stone-700 dark:text-stone-300 mt-4">
|
||||||
</h1>
|
Online Shop Opening — Coming Soon
|
||||||
<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">
|
</span>
|
||||||
Handcrafted ceramics from the Texas Coast. Functional art inspired by the raw textures and colors of Corpus Christi. Small batch, slow-made.
|
<h1 className="font-display text-5xl md:text-6xl lg:text-7xl xl:text-8xl text-text-main dark:text-white font-thin leading-[0.9] mb-8">
|
||||||
|
<span className="whitespace-nowrap">German Craft.</span><br /><span className="italic pl-8 md:pl-16 text-text-muted whitespace-nowrap">Coastal Soul.</span>
|
||||||
|
</h1>
|
||||||
|
<p className="font-body text-text-muted dark:text-gray-400 text-sm font-light mb-8 max-w-sm leading-relaxed ml-1">
|
||||||
|
Handcrafted ceramics from the Texas Coast. Functional art inspired by the raw textures and colors of Corpus Christi. Small batch, slow-made.
|
||||||
</p>
|
</p>
|
||||||
<div className="ml-1">
|
<div className="ml-1 mt-8">
|
||||||
<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="#">
|
<Link 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" to="/collections">
|
||||||
View The Collection
|
View The Collection
|
||||||
</a>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full md:w-7/12 h-[60vh] md:h-screen relative">
|
<div className="w-full md:w-1/2 h-[50vh] md:h-[75vh] relative mt-12 md:mt-0">
|
||||||
<div className="absolute inset-0 bg-stone-200 dark:bg-stone-800">
|
<div className="absolute inset-0 bg-stone-200 dark:bg-stone-800">
|
||||||
<img
|
<img
|
||||||
alt="Minimalist ceramic vase with single branch"
|
alt="Minimalist ceramic vase with single branch"
|
||||||
className="w-full h-full object-cover object-center brightness-95"
|
className="w-full h-full object-cover object-center brightness-95"
|
||||||
src="/pottery-studio.png"
|
src="/landingpage/1.png"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute bottom-10 left-10 md:left-auto md:right-20 bg-background-light/90 dark:bg-background-dark/90 backdrop-blur p-6 max-w-xs hidden md:block shadow-sm">
|
<div className="absolute bottom-16 left-10 md:left-auto md:right-20 bg-background-light/90 dark:bg-background-dark/90 backdrop-blur p-6 max-w-xs hidden md:block shadow-sm">
|
||||||
<p className="font-display italic text-xl text-text-main dark:text-gray-200">
|
<p className="font-display italic text-lg text-text-main dark:text-gray-200">
|
||||||
"In emptiness, there is fullness."
|
"In emptiness, there is fullness."
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Hero;
|
export default Hero;
|
||||||
|
|||||||
@@ -1,84 +1,91 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { gsap } from 'gsap';
|
import { gsap } from 'gsap';
|
||||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||||
|
|
||||||
gsap.registerPlugin(ScrollTrigger);
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
const horizontalImages = [
|
const horizontalImages = [
|
||||||
{ src: '/pottery-vase.png', title: 'Handcrafted Vases', description: 'Each vase tells a story of patience and craft' },
|
{ src: '/product_images/kitchenware.png', title: 'Coffee Cups', description: 'Wheel-thrown cups for your morning ritual — no two alike' },
|
||||||
{ src: '/pottery-bowls.png', title: 'Artisan Bowls', description: 'Organic forms inspired by nature' },
|
{ src: '/product_images/lass_das_so_202603231510.png', title: 'Bowls', description: 'Handcrafted stoneware bowls for the table and the kitchen' },
|
||||||
{ src: '/pottery-plates.png', title: 'Dinner Collection', description: 'Elevate your everyday dining experience' },
|
{ src: '/product_images/Produkt_foto_studio_202603231654 (1).png', title: 'Tableware', description: 'Small-batch dinnerware made to be used every day' },
|
||||||
{ src: '/pottery-studio.png', title: 'Our Studio', description: 'Where creativity meets tradition' },
|
{ src: '/product_images/Produkt_foto_studio_202603231744.png', title: 'Kitchenware', description: 'Functional ceramics built for the rhythms of daily life' },
|
||||||
{ src: '/ceramic-cups.png', title: 'Ceramic Cups', description: 'Handmade with love and intention' },
|
{ src: '/product_images/Produkt_foto_studio_202603231654 (2).png', title: 'Decoration', description: 'Sculptural pieces inspired by the textures of the Gulf Coast' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const HorizontalScrollSection: React.FC = () => {
|
const HorizontalScrollSection: React.FC = () => {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const container = containerRef.current;
|
const container = containerRef.current;
|
||||||
const scrollContainer = scrollRef.current;
|
const scrollContainer = scrollRef.current;
|
||||||
|
|
||||||
if (!container || !scrollContainer) return;
|
if (!container || !scrollContainer) return;
|
||||||
|
|
||||||
const scrollWidth = scrollContainer.scrollWidth - window.innerWidth;
|
const cards = Array.from(scrollContainer.children) as HTMLDivElement[];
|
||||||
|
const lastCard = cards[cards.length - 1];
|
||||||
const tween = gsap.to(scrollContainer, {
|
|
||||||
x: -scrollWidth,
|
if (!lastCard) return;
|
||||||
ease: 'none',
|
|
||||||
scrollTrigger: {
|
const lastCardRightEdge = lastCard.offsetLeft + lastCard.offsetWidth;
|
||||||
trigger: container,
|
const mobileEndInset = window.innerWidth < 768 ? 24 : 64;
|
||||||
start: 'top top',
|
const maxScroll = Math.max(lastCardRightEdge - window.innerWidth + mobileEndInset, 0);
|
||||||
end: () => `+=${scrollWidth * 0.5}`,
|
|
||||||
scrub: 1,
|
const tween = gsap.to(scrollContainer, {
|
||||||
pin: true,
|
x: -maxScroll,
|
||||||
anticipatePin: 1,
|
ease: 'none',
|
||||||
},
|
scrollTrigger: {
|
||||||
});
|
trigger: container,
|
||||||
|
start: 'top top',
|
||||||
return () => {
|
end: () => `+=${maxScroll * 0.5}`,
|
||||||
tween.scrollTrigger?.kill();
|
scrub: 1,
|
||||||
tween.kill();
|
pin: true,
|
||||||
};
|
anticipatePin: 1,
|
||||||
}, []);
|
},
|
||||||
|
});
|
||||||
return (
|
|
||||||
<section ref={containerRef} className="relative overflow-hidden bg-clay-dark">
|
return () => {
|
||||||
<div
|
tween.scrollTrigger?.kill();
|
||||||
ref={scrollRef}
|
tween.kill();
|
||||||
className="flex h-screen items-center"
|
};
|
||||||
>
|
}, []);
|
||||||
{horizontalImages.map((image, index) => (
|
|
||||||
<div
|
return (
|
||||||
key={index}
|
<section ref={containerRef} className="relative overflow-hidden bg-clay-dark h-screen w-full">
|
||||||
className="relative flex-shrink-0 w-[90vw] md:w-[75vw] h-screen flex items-center justify-center p-4 md:p-8"
|
<div
|
||||||
>
|
ref={scrollRef}
|
||||||
<div className="relative w-full h-full max-w-5xl max-h-[80vh] overflow-hidden rounded-lg shadow-2xl group">
|
className="flex h-screen items-center"
|
||||||
<img
|
>
|
||||||
src={image.src}
|
{horizontalImages.map((image, index) => (
|
||||||
alt={image.title}
|
<div
|
||||||
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
key={index}
|
||||||
/>
|
className="relative flex-shrink-0 w-[86vw] md:w-[75vw] h-screen flex items-center justify-center px-4 pr-16 md:p-8"
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-transparent to-transparent" />
|
>
|
||||||
<div className="absolute bottom-0 left-0 p-12 text-white">
|
<div className="relative w-full h-full max-w-4xl max-h-[60vh] overflow-hidden rounded-lg shadow-2xl group">
|
||||||
<h3 className="font-display text-5xl md:text-6xl font-light mb-4">{image.title}</h3>
|
<img
|
||||||
<p className="font-body text-lg font-light opacity-80 max-w-md">{image.description}</p>
|
src={image.src}
|
||||||
</div>
|
alt={image.title}
|
||||||
</div>
|
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
||||||
<div className="absolute top-1/2 right-8 -translate-y-1/2 text-white/20 font-display text-[15rem] leading-none select-none pointer-events-none">
|
/>
|
||||||
{String(index + 1).padStart(2, '0')}
|
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-transparent to-transparent" />
|
||||||
</div>
|
<div className="absolute bottom-0 left-0 p-8 md:p-12 text-white">
|
||||||
</div>
|
<h3 className="font-display text-4xl md:text-5xl font-light mb-4">{image.title}</h3>
|
||||||
))}
|
<p className="font-body text-base md:text-lg font-light opacity-80 max-w-md">{image.description}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 flex items-center gap-4 text-white/60">
|
</div>
|
||||||
<span className="material-symbols-outlined text-sm">arrow_back</span>
|
<div className="absolute top-1/2 right-3 translate-x-[24%] -translate-y-1/2 text-white/20 font-display text-[4.5rem] sm:text-[6rem] md:right-0 md:translate-x-[42%] md:text-[12rem] xl:text-[15rem] leading-none select-none pointer-events-none">
|
||||||
<span className="text-xs uppercase tracking-[0.3em] font-light">Scroll to explore</span>
|
{String(index + 1).padStart(2, '0')}
|
||||||
<span className="material-symbols-outlined text-sm">arrow_forward</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
))}
|
||||||
);
|
</div>
|
||||||
};
|
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 flex items-center gap-4 text-white/60">
|
||||||
|
<span className="material-symbols-outlined text-sm">arrow_back</span>
|
||||||
export default HorizontalScrollSection;
|
<span className="text-xs uppercase tracking-[0.3em] font-light">Scroll to explore</span>
|
||||||
|
<span className="material-symbols-outlined text-sm">arrow_forward</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HorizontalScrollSection;
|
||||||
|
|||||||
176
Pottery-website/components/InstagramFeed.tsx
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
instgrm?: {
|
||||||
|
Embeds: {
|
||||||
|
process: () => void;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const posts = [
|
||||||
|
'https://www.instagram.com/p/DSOFijljukL/',
|
||||||
|
'https://www.instagram.com/p/DSGh7rMjVes/',
|
||||||
|
'https://www.instagram.com/p/DRIIUECj4f6/',
|
||||||
|
'https://www.instagram.com/p/DJOvmvcIoo7/',
|
||||||
|
'https://www.instagram.com/p/DJOu5b7IL4t/',
|
||||||
|
'https://www.instagram.com/p/DIQnhO0oJgw/',
|
||||||
|
'https://www.instagram.com/p/DIJUVqbI4EH/',
|
||||||
|
'https://www.instagram.com/p/DHlvDNyIDRa/',
|
||||||
|
'https://www.instagram.com/p/DHlub_iojwv/',
|
||||||
|
'https://www.instagram.com/p/DJOvdVLIZpM/',
|
||||||
|
];
|
||||||
|
|
||||||
|
const InstagramFeed: React.FC = () => {
|
||||||
|
const [selectedPost, setSelectedPost] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// Double the list for seamless infinite marquee scroll
|
||||||
|
const duplicatedPosts = [...posts, ...posts];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Process Instagram embeds whenever the component mounts or the lightbox opens
|
||||||
|
if (window.instgrm) {
|
||||||
|
window.instgrm.Embeds.process();
|
||||||
|
} else {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = '//www.instagram.com/embed.js';
|
||||||
|
script.async = true;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
}
|
||||||
|
}, [selectedPost]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section className="py-24 px-6 md:px-12 bg-stone-50 dark:bg-stone-900 overflow-hidden">
|
||||||
|
<div className="max-w-[1400px] mx-auto">
|
||||||
|
<span className="block font-body text-xs uppercase tracking-[0.3em] text-stone-400 mb-6">
|
||||||
|
Follow Along
|
||||||
|
</span>
|
||||||
|
<div className="flex items-end justify-between mb-16 px-2">
|
||||||
|
<h2 className="font-display text-4xl md:text-5xl text-text-main dark:text-white leading-none">
|
||||||
|
From the Studio
|
||||||
|
</h2>
|
||||||
|
<a
|
||||||
|
href="https://www.instagram.com/knuth.ceramics"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-xs font-bold uppercase tracking-widest text-stone-500 hover:text-stone-900 dark:hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
@knuth.ceramics →
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Infinite Carousel */}
|
||||||
|
<div className="relative group overflow-hidden">
|
||||||
|
<style>{`
|
||||||
|
@keyframes marquee {
|
||||||
|
0% { transform: translateX(0); }
|
||||||
|
100% { transform: translateX(-${posts.length * 342}px); /* 326px width + 16px gap */ }
|
||||||
|
}
|
||||||
|
.animate-marquee {
|
||||||
|
animation: marquee 50s linear infinite;
|
||||||
|
}
|
||||||
|
.animate-marquee:hover {
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
<div className="flex gap-4 animate-marquee w-max py-4">
|
||||||
|
{duplicatedPosts.map((permalink, idx) => (
|
||||||
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="relative flex-shrink-0 w-[326px] overflow-hidden rounded-[8px] group/item cursor-pointer bg-white"
|
||||||
|
>
|
||||||
|
{/* Invisible Overlay to capture clicks.
|
||||||
|
Because iframes block events, we put a div above it.
|
||||||
|
On hover it reveals a subtle mask to indicate interactivity. */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 z-10 bg-black/0 group-hover/item:bg-black/50 transition-colors duration-300 flex flex-col items-center justify-center opacity-0 group-hover/item:opacity-100"
|
||||||
|
onClick={() => setSelectedPost(permalink)}
|
||||||
|
>
|
||||||
|
<p className="text-white font-display text-lg px-4 text-center font-bold drop-shadow-md">
|
||||||
|
View Post
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* The Instagram Embed itself.
|
||||||
|
By omitting data-instgrm-captioned we hide the caption/hashtags directly. */}
|
||||||
|
<div className="pointer-events-none">
|
||||||
|
<blockquote
|
||||||
|
className="instagram-media"
|
||||||
|
data-instgrm-permalink={`${permalink}?utm_source=ig_embed&utm_campaign=loading`}
|
||||||
|
data-instgrm-version="14"
|
||||||
|
style={{
|
||||||
|
background: '#FFF',
|
||||||
|
border: 0,
|
||||||
|
margin: 0,
|
||||||
|
maxWidth: '540px',
|
||||||
|
minWidth: '326px',
|
||||||
|
padding: 0,
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Lightbox Modal */}
|
||||||
|
<AnimatePresence>
|
||||||
|
{selectedPost && (
|
||||||
|
<motion.div
|
||||||
|
className="fixed inset-0 bg-black/90 z-50 flex items-center justify-center p-4 overflow-y-auto pt-[100px]"
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
onClick={() => setSelectedPost(null)}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
className="relative max-w-lg w-full bg-white dark:bg-stone-900 rounded-xl overflow-hidden my-auto"
|
||||||
|
initial={{ scale: 0.9, opacity: 0 }}
|
||||||
|
animate={{ scale: 1, opacity: 1 }}
|
||||||
|
exit={{ scale: 0.9, opacity: 0 }}
|
||||||
|
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{/* Close button inside modal container */}
|
||||||
|
<button
|
||||||
|
className="absolute top-2 right-2 text-stone-500 bg-white border border-stone-200 shadow-md rounded-full w-8 h-8 flex items-center justify-center hover:bg-stone-100 transition-colors z-[60]"
|
||||||
|
onClick={() => setSelectedPost(null)}
|
||||||
|
>
|
||||||
|
<span className="font-bold">×</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Instagram Embed WITH caption shown in the Lightbox */}
|
||||||
|
<div className="w-full bg-white mt-12 pb-4 px-2">
|
||||||
|
<blockquote
|
||||||
|
className="instagram-media"
|
||||||
|
data-instgrm-captioned
|
||||||
|
data-instgrm-permalink={`${selectedPost}?utm_source=ig_embed&utm_campaign=loading`}
|
||||||
|
data-instgrm-version="14"
|
||||||
|
style={{
|
||||||
|
background: '#FFF',
|
||||||
|
border: 0,
|
||||||
|
boxShadow: 'none',
|
||||||
|
margin: '0',
|
||||||
|
maxWidth: '540px',
|
||||||
|
minWidth: '326px',
|
||||||
|
padding: 0,
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InstagramFeed;
|
||||||
@@ -1,53 +1,57 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { JOURNAL_ENTRIES } from '../constants';
|
import { JOURNAL_ENTRIES } from '../constants';
|
||||||
|
|
||||||
const JournalSection: React.FC = () => {
|
const JournalSection: React.FC = () => {
|
||||||
return (
|
const getArticleHref = (slug: string) => (
|
||||||
<section className="relative py-32 bg-terracotta-soft dark:bg-black overflow-hidden transition-colors duration-500">
|
slug.startsWith('/editorial/') ? slug : `/editorial/${slug}`
|
||||||
<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"
|
|
||||||
/>
|
|
||||||
</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">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) => (
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
</section>
|
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
export default JournalSection;
|
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"
|
||||||
|
/>
|
||||||
|
</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">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) => (
|
||||||
|
<Link key={entry.id} to={getArticleHref(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>
|
||||||
|
<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>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default JournalSection;
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const QuoteSection: React.FC = () => {
|
const QuoteSection: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<section className="py-32 bg-clay-dark dark:bg-black border-y border-stone-800/50 transition-colors duration-500">
|
<section className="py-32 bg-clay-dark dark:bg-black border-y border-stone-800/50 transition-colors duration-500">
|
||||||
<div className="max-w-5xl mx-auto px-6 text-center">
|
<div className="max-w-5xl mx-auto px-6 text-center">
|
||||||
<span className="material-symbols-outlined text-4xl mb-8 text-stone-500 font-thin">format_quote</span>
|
<span className="material-symbols-outlined text-4xl mb-8 text-stone-500 font-thin">format_quote</span>
|
||||||
<h3 className="font-display text-3xl md:text-5xl font-thin leading-snug text-stone-100 dark:text-stone-200 mb-10 italic">
|
<h3 className="font-display text-3xl md:text-5xl font-thin leading-snug text-stone-100 dark:text-stone-200 mb-10 italic">
|
||||||
"My pottery is designed to be both beautiful and practical. From minimalist vases to durable dinner plates, each piece has its own character."
|
"My pottery is designed to be both beautiful and practical. From minimalist vases to durable dinner plates, each piece has its own character."
|
||||||
</h3>
|
</h3>
|
||||||
<p className="font-body text-xs uppercase tracking-[0.2em] text-stone-400">
|
<p className="font-body text-xs uppercase tracking-[0.2em] text-stone-400">
|
||||||
Anonymous — Verified Collector
|
Anonymous — Verified Collector
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default QuoteSection;
|
export default QuoteSection;
|
||||||
84
Pottery-website/components/SEO.tsx
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
// TODO: Update SITE_URL to your actual domain before deploying
|
||||||
|
export const SITE_URL = 'https://knuthceramics.com';
|
||||||
|
export const SITE_NAME = 'KNUTH Ceramics';
|
||||||
|
const DEFAULT_OG_IMAGE = `${SITE_URL}/landingpage/artelier.png`;
|
||||||
|
|
||||||
|
type Schema = Record<string, unknown>;
|
||||||
|
|
||||||
|
interface SEOProps {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
canonical?: string;
|
||||||
|
schema?: Schema | Schema[];
|
||||||
|
ogImage?: string;
|
||||||
|
ogType?: 'website' | 'article' | 'product';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMeta(selector: string, attr: string, value: string) {
|
||||||
|
let el = document.querySelector<HTMLMetaElement>(selector);
|
||||||
|
if (!el) {
|
||||||
|
el = document.createElement('meta');
|
||||||
|
const [attrName, attrVal] = selector.replace('meta[', '').replace(']', '').split('="');
|
||||||
|
el.setAttribute(attrName, attrVal.replace('"', ''));
|
||||||
|
document.head.appendChild(el);
|
||||||
|
}
|
||||||
|
el.setAttribute(attr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SEO: React.FC<SEOProps> = ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
canonical,
|
||||||
|
schema,
|
||||||
|
ogImage = DEFAULT_OG_IMAGE,
|
||||||
|
ogType = 'website',
|
||||||
|
}) => {
|
||||||
|
const schemaStr = schema ? JSON.stringify(schema) : null;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fullTitle = title.includes(SITE_NAME) ? title : `${title} | ${SITE_NAME}`;
|
||||||
|
document.title = fullTitle;
|
||||||
|
|
||||||
|
setMeta('meta[name="description"]', 'content', description);
|
||||||
|
setMeta('meta[property="og:title"]', 'content', fullTitle);
|
||||||
|
setMeta('meta[property="og:description"]', 'content', description);
|
||||||
|
setMeta('meta[property="og:type"]', 'content', ogType);
|
||||||
|
setMeta('meta[property="og:image"]', 'content', ogImage);
|
||||||
|
setMeta('meta[property="og:site_name"]', 'content', SITE_NAME);
|
||||||
|
setMeta('meta[name="twitter:card"]', 'content', 'summary_large_image');
|
||||||
|
setMeta('meta[name="twitter:title"]', 'content', fullTitle);
|
||||||
|
setMeta('meta[name="twitter:description"]', 'content', description);
|
||||||
|
setMeta('meta[name="twitter:image"]', 'content', ogImage);
|
||||||
|
|
||||||
|
let canonicalEl = document.querySelector<HTMLLinkElement>('link[rel="canonical"]');
|
||||||
|
if (!canonicalEl) {
|
||||||
|
canonicalEl = document.createElement('link');
|
||||||
|
canonicalEl.setAttribute('rel', 'canonical');
|
||||||
|
document.head.appendChild(canonicalEl);
|
||||||
|
}
|
||||||
|
canonicalEl.setAttribute('href', canonical ?? `${SITE_URL}${window.location.pathname}`);
|
||||||
|
|
||||||
|
// Remove previous page-level schemas, inject new ones
|
||||||
|
document.querySelectorAll('script[data-seo="page"]').forEach(el => el.remove());
|
||||||
|
if (schemaStr) {
|
||||||
|
const schemas = Array.isArray(JSON.parse(schemaStr)) ? JSON.parse(schemaStr) : [JSON.parse(schemaStr)];
|
||||||
|
schemas.forEach((s: Schema) => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.setAttribute('type', 'application/ld+json');
|
||||||
|
script.setAttribute('data-seo', 'page');
|
||||||
|
script.textContent = JSON.stringify(s);
|
||||||
|
document.head.appendChild(script);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.querySelectorAll('script[data-seo="page"]').forEach(el => el.remove());
|
||||||
|
};
|
||||||
|
}, [title, description, canonical, ogImage, ogType, schemaStr]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SEO;
|
||||||
@@ -1,148 +1,295 @@
|
|||||||
import { NavItem, CollectionItem, JournalEntry, FooterSection } from './types';
|
import { NavItem, CollectionItem, JournalEntry, FooterSection } from './types';
|
||||||
|
|
||||||
export const NAV_ITEMS: NavItem[] = [
|
export const NAV_ITEMS: NavItem[] = [
|
||||||
{ label: 'Collections', href: '#' },
|
{ label: 'Collections', href: '/collections' },
|
||||||
{ label: 'Atelier', href: '#' },
|
{ label: 'Atelier', href: '/atelier' },
|
||||||
{ label: 'Editorial', href: '#' },
|
{ label: 'Editorial', href: '/editorial' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const COLLECTIONS: CollectionItem[] = [
|
export const COLLECTIONS: CollectionItem[] = [
|
||||||
|
// --- Tableware ---
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'Coastal Grey Tableware Set',
|
||||||
|
slug: 'coastal-grey-tableware-set',
|
||||||
|
number: '01',
|
||||||
|
image: '/product_images/Lass_aussehen_produkt_202603231513 (1).png',
|
||||||
|
images: ['/product_images/Lass_aussehen_produkt_202603231513 (1).png'],
|
||||||
|
price: 140,
|
||||||
|
description: 'A wheel-thrown set of cups, bowls, and saucers finished in a speckled coastal grey glaze. Handmade in Corpus Christi, TX. Dishwasher safe.',
|
||||||
|
aspectRatio: 'aspect-[4/3]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 13,
|
||||||
|
title: 'Ceramic Place Setting',
|
||||||
|
slug: 'ceramic-place-setting',
|
||||||
|
number: '13',
|
||||||
|
image: '/product_images/Produkt_foto_studio_202603231654 (1).png',
|
||||||
|
images: ['/product_images/Produkt_foto_studio_202603231654 (1).png'],
|
||||||
|
price: 120,
|
||||||
|
description: 'A handmade ceramic plate with a small ceramic tree accent piece. A complete place setting for the table, made in small batches in Corpus Christi, TX.',
|
||||||
|
aspectRatio: 'aspect-square',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 18,
|
||||||
|
title: 'Textured Floral Plates — Set of Four',
|
||||||
|
slug: 'textured-floral-plates',
|
||||||
|
number: '18',
|
||||||
|
image: '/product_images/Produkt_foto_studio_202603231654 (6).png',
|
||||||
|
images: ['/product_images/Produkt_foto_studio_202603231654 (6).png'],
|
||||||
|
price: 210,
|
||||||
|
description: 'A set of four round stoneware plates with a hand-pressed floral texture. Each plate varies slightly in glaze — soft white and sage tones inspired by the Gulf Coast.',
|
||||||
|
aspectRatio: 'aspect-[3/4]',
|
||||||
|
},
|
||||||
|
// --- Coffee Cups ---
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: 'Set of Three Stoneware Cups',
|
||||||
|
slug: 'set-of-three-stoneware-cups',
|
||||||
|
number: '04',
|
||||||
|
image: '/product_images/lass_das_so_202603231510 (3).png',
|
||||||
|
images: ['/product_images/lass_das_so_202603231510 (3).png'],
|
||||||
|
price: 120,
|
||||||
|
description: 'Three wheel-thrown stoneware cups with a layered grey-green interior glaze and dark clay body. No two are alike. Ideal for coffee, tea, or espresso.',
|
||||||
|
aspectRatio: 'aspect-[4/5]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
title: 'Dog Mam Cup — Teal',
|
||||||
|
slug: 'dog-mam-cup-teal',
|
||||||
|
number: '05',
|
||||||
|
image: '/product_images/lass_das_so_202603231511.png',
|
||||||
|
images: ['/product_images/lass_das_so_202603231511.png'],
|
||||||
|
price: 65,
|
||||||
|
description: 'A handmade ceramic cup stamped with "Dog Mam" in the clay. Finished in a flowing orange and teal coastal glaze. A one-of-a-kind gift for dog lovers.',
|
||||||
|
aspectRatio: 'aspect-square',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
title: 'Dog Mam Cup — Blush',
|
||||||
|
slug: 'dog-mam-cup-blush',
|
||||||
|
number: '06',
|
||||||
|
image: '/product_images/lass_das_so_202603231511 (1).png',
|
||||||
|
images: ['/product_images/lass_das_so_202603231511 (1).png'],
|
||||||
|
price: 45,
|
||||||
|
description: 'A handmade ceramic cup stamped with "Dog Mam" in the clay. Finished in a warm blush and amber glaze. A one-of-a-kind gift for dog lovers.',
|
||||||
|
aspectRatio: 'aspect-[3/4]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
title: 'Dark Stoneware Cups — Amber Drip',
|
||||||
|
slug: 'dark-stoneware-cups-amber-drip',
|
||||||
|
number: '08',
|
||||||
|
image: '/product_images/lass_das_so_202603231511 (3).png',
|
||||||
|
images: ['/product_images/lass_das_so_202603231511 (3).png'],
|
||||||
|
price: 110,
|
||||||
|
description: 'Five handmade stoneware cups with a dark clay body and flowing amber drip glaze. Small-batch, wheel-thrown in Corpus Christi. Each piece is unique.',
|
||||||
|
aspectRatio: 'aspect-square',
|
||||||
|
},
|
||||||
|
// --- Bowls ---
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: 'Dark Stoneware Bowl',
|
||||||
|
slug: 'dark-stoneware-bowl',
|
||||||
|
number: '03',
|
||||||
|
image: '/product_images/lass_das_so_202603231510 (2).png',
|
||||||
|
images: ['/product_images/lass_das_so_202603231510 (2).png'],
|
||||||
|
price: 95,
|
||||||
|
description: 'A single wheel-thrown stoneware bowl with a dark exterior and rich amber interior glaze. Made in Corpus Christi, TX. Dishwasher safe.',
|
||||||
|
aspectRatio: 'aspect-[3/4]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
title: 'Pastel Bowl Collection',
|
||||||
|
slug: 'pastel-bowl-collection',
|
||||||
|
number: '07',
|
||||||
|
image: '/product_images/lass_das_so_202603231511 (2).png',
|
||||||
|
images: ['/product_images/lass_das_so_202603231511 (2).png'],
|
||||||
|
price: 150,
|
||||||
|
description: 'A collection of handmade stoneware bowls and cups in soft coastal pastels — blush pink, sky blue, buttercup yellow, and white. Each piece is unique.',
|
||||||
|
aspectRatio: 'aspect-[4/3]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
title: 'Coastal Sunset Bowl Set',
|
||||||
|
slug: 'coastal-sunset-bowl-set',
|
||||||
|
number: '10',
|
||||||
|
image: '/product_images/lass_das_so_202603231510.png',
|
||||||
|
images: ['/product_images/lass_das_so_202603231510.png'],
|
||||||
|
price: 185,
|
||||||
|
description: 'Stacked stoneware bowls glazed in warm coastal tones — amber, orange, pink, and teal — inspired by the Gulf Coast sunsets of Corpus Christi.',
|
||||||
|
aspectRatio: 'aspect-[3/4]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
title: 'Handmade Blush Bowl',
|
||||||
|
slug: 'handmade-blush-bowl',
|
||||||
|
number: '11',
|
||||||
|
image: '/product_images/lass_das_so_202603231510 (1).png',
|
||||||
|
images: ['/product_images/lass_das_so_202603231510 (1).png'],
|
||||||
|
price: 240,
|
||||||
|
description: 'A single handmade stoneware bowl with a luminous blush and rose interior glaze. Wheel-thrown and one-of-a-kind. Made in Corpus Christi, TX.',
|
||||||
|
aspectRatio: 'aspect-[4/3]',
|
||||||
|
},
|
||||||
|
// --- Kitchenware ---
|
||||||
|
{
|
||||||
|
id: 12,
|
||||||
|
title: 'Dark Stoneware Vessel',
|
||||||
|
slug: 'dark-stoneware-vessel',
|
||||||
|
number: '12',
|
||||||
|
image: '/product_images/Produkt_foto_studio_202603231654.png',
|
||||||
|
images: ['/product_images/Produkt_foto_studio_202603231654.png'],
|
||||||
|
price: 150,
|
||||||
|
description: 'A squat wheel-thrown stoneware vessel with a textured dark clay body and amber drip glaze. Functional and sculptural — use as a utensil holder or vase.',
|
||||||
|
aspectRatio: 'aspect-[3/4]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 16,
|
||||||
|
title: 'Studio Collection — Mixed Pieces',
|
||||||
|
slug: 'studio-collection-mixed-pieces',
|
||||||
|
number: '16',
|
||||||
|
image: '/product_images/Produkt_foto_studio_202603231654 (4).png',
|
||||||
|
images: ['/product_images/Produkt_foto_studio_202603231654 (4).png'],
|
||||||
|
price: 145,
|
||||||
|
description: 'A curated grouping from the studio — includes small bowls, trinket dishes, a flower frog, and miniature ceramic houses. Each piece handmade in Corpus Christi.',
|
||||||
|
aspectRatio: 'aspect-square',
|
||||||
|
},
|
||||||
|
// --- Decoration ---
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: 'Handmade Ceramic Rose',
|
||||||
|
slug: 'handmade-ceramic-rose',
|
||||||
|
number: '02',
|
||||||
|
image: '/product_images/Lass_aussehen_produkt_202603231513 (2).png',
|
||||||
|
images: ['/product_images/Lass_aussehen_produkt_202603231513 (2).png'],
|
||||||
|
price: 160,
|
||||||
|
description: 'A hand-sculpted ceramic rose in unglazed white stoneware. A one-of-a-kind decorative piece — no two petals are the same. Made in Corpus Christi, TX.',
|
||||||
|
aspectRatio: 'aspect-square',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 9,
|
||||||
|
title: 'Hope Bowl with Clay Tokens',
|
||||||
|
slug: 'hope-bowl-with-clay-tokens',
|
||||||
|
number: '09',
|
||||||
|
image: '/product_images/Lass_aussehen_produkt_202603231513.png',
|
||||||
|
images: ['/product_images/Lass_aussehen_produkt_202603231513.png'],
|
||||||
|
price: 130,
|
||||||
|
description: 'A decorative stoneware bowl filled with handmade clay word tokens — small houses and tags stamped with "Hope". An intentional piece for your home.',
|
||||||
|
aspectRatio: 'aspect-[3/4]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 14,
|
||||||
|
title: 'Ceramic House Lanterns — Set of Four',
|
||||||
|
slug: 'ceramic-house-lanterns',
|
||||||
|
number: '14',
|
||||||
|
image: '/product_images/Produkt_foto_studio_202603231654 (2).png',
|
||||||
|
images: ['/product_images/Produkt_foto_studio_202603231654 (2).png'],
|
||||||
|
price: 95,
|
||||||
|
description: 'Four handmade ceramic house lanterns with cut-out windows that glow when lit from inside. Each house has a unique facade — hearts, stars, and arched details.',
|
||||||
|
aspectRatio: 'aspect-[4/3]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 15,
|
||||||
|
title: 'Flower Frog Vase — Gulf Blue',
|
||||||
|
slug: 'flower-frog-vase-gulf-blue',
|
||||||
|
number: '15',
|
||||||
|
image: '/product_images/Produkt_foto_studio_202603231654 (3).png',
|
||||||
|
images: ['/product_images/Produkt_foto_studio_202603231654 (3).png'],
|
||||||
|
price: 180,
|
||||||
|
description: 'A dome-shaped ceramic flower frog glazed in soft Gulf blue. Holds dried or fresh flowers through pin holes on top. Handmade in Corpus Christi, TX.',
|
||||||
|
aspectRatio: 'aspect-[3/4]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 17,
|
||||||
|
title: 'Miniature Ceramic Village',
|
||||||
|
slug: 'miniature-ceramic-village',
|
||||||
|
number: '17',
|
||||||
|
image: '/product_images/Produkt_foto_studio_202603231654 (5).png',
|
||||||
|
images: ['/product_images/Produkt_foto_studio_202603231654 (5).png'],
|
||||||
|
price: 165,
|
||||||
|
description: 'Small ceramic house sculptures arranged on round clay discs — each village is unique. A handmade decorative piece with hand-carved door and window details.',
|
||||||
|
aspectRatio: 'aspect-[4/5]',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 19,
|
||||||
title: 'Tableware',
|
title: 'Stoneware Kitchen Vessel',
|
||||||
slug: 'tableware-set',
|
slug: 'stoneware-kitchen-vessel',
|
||||||
number: '01',
|
number: '19',
|
||||||
image: '/collection-tableware.png',
|
image: '/product_images/Produkt_foto_studio_202603231744.png',
|
||||||
images: ['/collection-tableware.png', '/pottery-plates.png', '/ceramic-cups.png'],
|
images: ['/product_images/Produkt_foto_studio_202603231744.png'],
|
||||||
price: 185,
|
price: 135,
|
||||||
description: 'A complete hand-thrown tableware set for four. Finished in our signature matte white glaze with raw clay rims.',
|
description: 'A versatile wheel-thrown stoneware vessel designed for kitchen utility. Minimalist form with a responsive glaze.',
|
||||||
aspectRatio: 'aspect-[3/4]',
|
aspectRatio: 'aspect-[3/4]',
|
||||||
},
|
},
|
||||||
{
|
];
|
||||||
id: 2,
|
|
||||||
title: 'Lighting',
|
export const JOURNAL_ENTRIES: JournalEntry[] = [
|
||||||
slug: 'ceramic-lighting',
|
{
|
||||||
number: '04',
|
id: 1,
|
||||||
image: '/collection-lighting.png',
|
category: 'Studio',
|
||||||
images: ['/collection-lighting.png', '/pottery-studio.png', '/collection-vases.png'],
|
date: 'Oct 03',
|
||||||
price: 240,
|
title: 'Product Photography for Small Businesses',
|
||||||
description: 'Sculptural ceramic pendant lights that bring warmth and texture to any space. Each piece is unique.',
|
slug: '/editorial/product-photography-for-small-businesses',
|
||||||
aspectRatio: 'aspect-[4/3]',
|
description: "Learning that beautiful products aren't enough on their own — you also need beautiful photos to tell the story.",
|
||||||
},
|
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuAipMlYLTcRT_hdc3VePfFIlrA56VzZ5G2y3gcRfmIZMERwGFKq2N19Gqo6mw7uZowXmjl2eJ89TI3Mcud2OyOfadO3mPVF_v0sI0OHupqM49WEFcWzH-Wbu3DL6bQ46F2Y8SIAk-NUQy8psjcIdBKRrM8fqdn4eOPANYTXpVxkLMAm4R0Axy4aEKNdmj917ZKKTxvXB-J8nGlITJkJ-ua7XcZOwGnfK5ttzyWW35A0oOSffCf972gmpV27wrVQgYJNLS7UyDdyQIQ',
|
||||||
{
|
},
|
||||||
id: 3,
|
{
|
||||||
title: 'Vases',
|
id: 2,
|
||||||
slug: 'organic-vases',
|
category: 'Guide',
|
||||||
number: '02',
|
date: 'Jul 15',
|
||||||
image: '/collection-vases.png',
|
title: 'How to Care for Handmade Ceramics',
|
||||||
images: ['/collection-vases.png', '/pottery-vase.png', '/collection-lighting.png'],
|
slug: '/editorial/how-to-care-for-handmade-ceramics',
|
||||||
price: 95,
|
description: 'How to care for handmade ceramics: a practical daily care guide for mugs, bowls, and plates, including dishwasher, microwave, crazing, and cleaning tips.',
|
||||||
description: 'Organic forms inspired by the dunes of Padre Island. Perfect for dried stems or fresh bouquets.',
|
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuAaWGnX_NYT3S_lOflL2NJZGbWge4AAkvra4ymvF8ag-c1UKsOAIB-rsLVQXW5xIlPZipDiK8-ysPyv22xdgsvzs4EOXSSCcrT4Lb2YCe0u5orxRaZEA5TgxeoKq15zaWKSlmnHyPGjPd_7yglpfO13eZmbU5KaxFJ1KGO0UAxoO9BpsyCYgbgINMoSz3epGe5ZdwBWRH-5KCzjoLuXimFTLcd5bqg9T1YofTxgy2hWBMJzKkafyEniq8dP6hMmfNCLVcCHHHx0hRU',
|
||||||
aspectRatio: 'aspect-square',
|
marginTop: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 3,
|
||||||
title: 'Serving',
|
category: 'Wellness',
|
||||||
slug: 'serving-bowls',
|
date: 'Jun 11',
|
||||||
number: '05',
|
title: 'Finding Motivation in Clay',
|
||||||
image: '/pottery-bowls.png',
|
slug: '/editorial/finding-motivation-in-clay',
|
||||||
images: ['/pottery-bowls.png', '/collection-kitchenware.png', '/pottery-plates.png'],
|
description: "10 gentle, practical tips to help potters find motivation during slow or uncertain moments in the creative process.",
|
||||||
price: 120,
|
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuB8NOE5fGfN4d87cbcB27_Sh-nrlZlqxsTlYKbCZk98SoL-gHsPSWFNuxd1DxBq0g8Qysh0RBZ_btu-_WaH68UjV8SXPUalyxREvUqao4oXmra--pWAsaooWwKvWCzReYZ8kj7G-KIYIAo5mqudzB8n9C6-HVTNPPx9QgZHr_YsojMxlmmVcQ5bqk7-Lp0KtSAiVIPD2-1UE1dMGnkVSLUXKdgA65JIh8M3TtNkaJTGONuFKoTERrYOWe7u2BILnqyukTzlNcvK7Sc',
|
||||||
description: 'Large, shallow serving bowls designed for communal gatherings. Durable and dishwasher safe.',
|
},
|
||||||
aspectRatio: 'aspect-[3/4]',
|
];
|
||||||
},
|
|
||||||
{
|
export const GALLERY_IMAGES = [
|
||||||
id: 5,
|
{ src: '/product_images/kitchenware.png', likes: 2847, comments: 124, caption: 'Morning rituals ☕' },
|
||||||
title: 'Kitchenware',
|
{ src: '/product_images/vases.png', likes: 3521, comments: 89, caption: 'Crafted with intention 🏺' },
|
||||||
slug: 'kitchen-essentials',
|
{ src: '/product_images/serving.png', likes: 1956, comments: 67, caption: 'Wabi-sabi collection' },
|
||||||
number: '03',
|
{ src: '/product_images/textiles.png', likes: 4102, comments: 156, caption: 'Ready for your table ✨' },
|
||||||
image: '/collection-kitchenware.png',
|
{ src: '/landingpage/artelier.png', likes: 5234, comments: 203, caption: 'Where the magic happens' },
|
||||||
images: ['/collection-kitchenware.png', '/ceramic-cups.png', '/pottery-bowls.png'],
|
{ src: '/product_images/tableware.png', likes: 2678, comments: 94, caption: 'Stacked with love' },
|
||||||
price: 65,
|
{ src: '/product_images/vases.png', likes: 3189, comments: 112, caption: 'Organic forms' },
|
||||||
description: 'Everyday essentials including berry bowls, spoon rests, and utensil holders. Functional beauty.',
|
{ src: '/product_images/kitchenware.png', likes: 1847, comments: 78, caption: 'Matcha time 🍵' },
|
||||||
aspectRatio: 'aspect-[3/5]',
|
];
|
||||||
},
|
|
||||||
{
|
export const FOOTER_LINKS: FooterSection[] = [
|
||||||
id: 6,
|
{
|
||||||
title: 'Textiles',
|
title: 'Shop',
|
||||||
slug: 'linen-textiles',
|
links: [
|
||||||
number: '06',
|
{ label: 'All Ceramics', href: '/collections' },
|
||||||
image: '/pottery-plates.png',
|
{ label: 'Best Sellers', href: '#' },
|
||||||
images: ['/pottery-plates.png', '/collection-tableware.png', '/collection-vases.png'],
|
{ label: 'Gift Cards', href: '#' },
|
||||||
price: 45,
|
],
|
||||||
description: 'Natural linen napkins and runners that perfectly complement our stoneware ceramics.',
|
},
|
||||||
aspectRatio: 'aspect-square',
|
{
|
||||||
},
|
title: 'Company',
|
||||||
|
links: [
|
||||||
|
{ label: 'Our Story', href: '/atelier' },
|
||||||
|
{ label: 'Sustainability', href: '#' },
|
||||||
|
{ label: 'Careers', href: '#' },
|
||||||
|
{ label: 'Press', href: '#' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Support',
|
||||||
|
links: [
|
||||||
|
{ label: 'FAQ', href: '/faq' },
|
||||||
|
{ label: 'Shipping', href: '/shipping' },
|
||||||
|
{ label: 'Returns', href: '/returns' },
|
||||||
|
{ label: 'Contact', href: '/contact' },
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const JOURNAL_ENTRIES: JournalEntry[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
category: 'Studio',
|
|
||||||
date: 'Oct 03',
|
|
||||||
title: 'Product Photography for Small Businesses',
|
|
||||||
slug: '/editorial/product-photography-for-small-businesses',
|
|
||||||
description: "Learning that beautiful products aren't enough on their own — you also need beautiful photos to tell the story.",
|
|
||||||
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuAipMlYLTcRT_hdc3VePfFIlrA56VzZ5G2y3gcRfmIZMERwGFKq2N19Gqo6mw7uZowXmjl2eJ89TI3Mcud2OyOfadO3mPVF_v0sI0OHupqM49WEFcWzH-Wbu3DL6bQ46F2Y8SIAk-NUQy8psjcIdBKRrM8fqdn4eOPANYTXpVxkLMAm4R0Axy4aEKNdmj917ZKKTxvXB-J8nGlITJkJ-ua7XcZOwGnfK5ttzyWW35A0oOSffCf972gmpV27wrVQgYJNLS7UyDdyQIQ',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
category: 'Guide',
|
|
||||||
date: 'Jul 15',
|
|
||||||
title: 'The Art of Packaging',
|
|
||||||
slug: '/editorial/the-art-of-packaging',
|
|
||||||
description: "A practical guide for potters who want to package and send their handmade ceramics with care and confidence.",
|
|
||||||
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuAaWGnX_NYT3S_lOflL2NJZGbWge4AAkvra4ymvF8ag-c1UKsOAIB-rsLVQXW5xIlPZipDiK8-ysPyv22xdgsvzs4EOXSSCcrT4Lb2YCe0u5orxRaZEA5TgxeoKq15zaWKSlmnHyPGjPd_7yglpfO13eZmbU5KaxFJ1KGO0UAxoO9BpsyCYgbgINMoSz3epGe5ZdwBWRH-5KCzjoLuXimFTLcd5bqg9T1YofTxgy2hWBMJzKkafyEniq8dP6hMmfNCLVcCHHHx0hRU',
|
|
||||||
marginTop: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
category: 'Wellness',
|
|
||||||
date: 'Jun 11',
|
|
||||||
title: 'Finding Motivation in Clay',
|
|
||||||
slug: '/editorial/finding-motivation-in-clay',
|
|
||||||
description: "10 gentle, practical tips to help potters find motivation during slow or uncertain moments in the creative process.",
|
|
||||||
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuB8NOE5fGfN4d87cbcB27_Sh-nrlZlqxsTlYKbCZk98SoL-gHsPSWFNuxd1DxBq0g8Qysh0RBZ_btu-_WaH68UjV8SXPUalyxREvUqao4oXmra--pWAsaooWwKvWCzReYZ8kj7G-KIYIAo5mqudzB8n9C6-HVTNPPx9QgZHr_YsojMxlmmVcQ5bqk7-Lp0KtSAiVIPD2-1UE1dMGnkVSLUXKdgA65JIh8M3TtNkaJTGONuFKoTERrYOWe7u2BILnqyukTzlNcvK7Sc',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const GALLERY_IMAGES = [
|
|
||||||
{ src: '/ceramic-cups.png', likes: 2847, comments: 124, caption: 'Morning rituals ☕' },
|
|
||||||
{ src: '/pottery-vase.png', likes: 3521, comments: 89, caption: 'Crafted with intention 🏺' },
|
|
||||||
{ src: '/pottery-bowls.png', likes: 1956, comments: 67, caption: 'Wabi-sabi collection' },
|
|
||||||
{ src: '/pottery-plates.png', likes: 4102, comments: 156, caption: 'Ready for your table ✨' },
|
|
||||||
{ src: '/pottery-studio.png', likes: 5234, comments: 203, caption: 'Where the magic happens' },
|
|
||||||
{ src: '/collection-tableware.png', likes: 2678, comments: 94, caption: 'Stacked with love' },
|
|
||||||
{ src: '/collection-vases.png', likes: 3189, comments: 112, caption: 'Organic forms' },
|
|
||||||
{ src: '/collection-kitchenware.png', likes: 1847, comments: 78, caption: 'Matcha time 🍵' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const FOOTER_LINKS: FooterSection[] = [
|
|
||||||
{
|
|
||||||
title: 'Shop',
|
|
||||||
links: [
|
|
||||||
{ label: 'All Ceramics', href: '#' },
|
|
||||||
{ label: 'New Arrivals', href: '#' },
|
|
||||||
{ label: 'Best Sellers', href: '#' },
|
|
||||||
{ label: 'Gift Cards', href: '#' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Company',
|
|
||||||
links: [
|
|
||||||
{ label: 'Our Story', href: '#' },
|
|
||||||
{ label: 'Sustainability', href: '#' },
|
|
||||||
{ label: 'Careers', href: '#' },
|
|
||||||
{ label: 'Press', href: '#' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Support',
|
|
||||||
links: [
|
|
||||||
{ label: 'FAQ', href: '/faq' },
|
|
||||||
{ label: 'Shipping', href: '/shipping' },
|
|
||||||
{ label: 'Returns', href: '/returns' },
|
|
||||||
{ label: 'Contact', href: '/contact' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
@@ -1,88 +1,182 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||||
<title>KNUTH Ceramics - Editorial Collection</title>
|
<title>KNUTH Ceramics — Handcrafted Pottery from Corpus Christi, Texas</title>
|
||||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
<meta name="description" content="KNUTH Ceramics crafts small-batch stoneware pottery in Corpus Christi, Texas. Shop handmade vases, bowls, tableware, and dinnerware inspired by the Gulf Coast. Custom commissions welcome."/>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400&family=Manrope:wght@200;300;400;500&display=swap" rel="stylesheet"/>
|
<meta name="robots" content="index, follow"/>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
<link rel="canonical" href="https://knuthceramics.com/"/>
|
||||||
<script>
|
|
||||||
tailwind.config = {
|
<!-- Open Graph -->
|
||||||
darkMode: "class",
|
<meta property="og:title" content="KNUTH Ceramics — Handcrafted Pottery from Corpus Christi, Texas"/>
|
||||||
theme: {
|
<meta property="og:description" content="Small-batch stoneware pottery made in Corpus Christi, TX. Handcrafted vases, bowls, and tableware inspired by the Texas Gulf Coast. Custom dinnerware commissions accepted."/>
|
||||||
extend: {
|
<meta property="og:type" content="website"/>
|
||||||
colors: {
|
<meta property="og:url" content="https://knuthceramics.com/"/>
|
||||||
primary: "#292524", // Warm Charcoal
|
<meta property="og:image" content="https://knuthceramics.com/landingpage/artelier.png"/>
|
||||||
secondary: "#78716C", // Warm Stone
|
<meta property="og:site_name" content="KNUTH Ceramics"/>
|
||||||
"background-light": "#F5F4F0", // Soft Sand / Alabaster (Hero)
|
|
||||||
"background-dark": "#1C1917", // Dark Warm Grey
|
<!-- Twitter Card -->
|
||||||
// New Sectional Colors
|
<meta name="twitter:card" content="summary_large_image"/>
|
||||||
"sage": "#D4D9D1", // Soft Sage Green
|
<meta name="twitter:title" content="KNUTH Ceramics — Handcrafted Pottery from Corpus Christi, Texas"/>
|
||||||
"warm-grey": "#DAD7D4", // Warm Grey
|
<meta name="twitter:description" content="Small-batch stoneware pottery made in Corpus Christi, TX. Handcrafted vases, bowls, and tableware inspired by the Texas Gulf Coast."/>
|
||||||
"clay-dark": "#33302D", // Deep Charcoal / Clay
|
<meta name="twitter:image" content="https://knuthceramics.com/landingpage/artelier.png"/>
|
||||||
"terracotta-soft": "#E6DDD5", // Pale Ochre / Soft Terracotta
|
|
||||||
"accent-sand": "#E7E5E4",
|
<!-- Static JSON-LD: Organization + LocalBusiness (visible to crawlers that don't execute JS) -->
|
||||||
"accent-warm": "#D6D3D1",
|
<script type="application/ld+json" data-seo="site">
|
||||||
"text-main": "#1C1917",
|
{
|
||||||
"text-muted": "#57534E",
|
"@context": "https://schema.org",
|
||||||
},
|
"@type": ["LocalBusiness", "ArtGallery"],
|
||||||
fontFamily: {
|
"name": "KNUTH Ceramics",
|
||||||
display: ["Cormorant Garamond", "serif"],
|
"alternateName": "Knuth Ceramics",
|
||||||
body: ["Manrope", "sans-serif"],
|
"description": "Handcrafted ceramics by Claudia Knuth. Small-batch stoneware pottery inspired by the raw textures and colors of the Texas Gulf Coast in Corpus Christi, TX. German craft tradition meets coastal soul.",
|
||||||
},
|
"url": "https://knuthceramics.com",
|
||||||
fontSize: {
|
"logo": "https://knuthceramics.com/landingpage/artelier.png",
|
||||||
'10xl': '10rem',
|
"image": "https://knuthceramics.com/landingpage/artelier.png",
|
||||||
},
|
"email": "knuth.claudia@gmail.com",
|
||||||
spacing: {
|
"telephone": "",
|
||||||
'128': '32rem',
|
"address": {
|
||||||
}
|
"@type": "PostalAddress",
|
||||||
},
|
"addressLocality": "Corpus Christi",
|
||||||
},
|
"addressRegion": "TX",
|
||||||
};
|
"addressCountry": "US"
|
||||||
</script>
|
},
|
||||||
<style>
|
"geo": {
|
||||||
.material-symbols-outlined {
|
"@type": "GeoCoordinates",
|
||||||
font-variation-settings:
|
"latitude": "27.8006",
|
||||||
'FILL' 0,
|
"longitude": "-97.3964"
|
||||||
'wght' 300,
|
},
|
||||||
'GRAD' 0,
|
"sameAs": [
|
||||||
'opsz' 24
|
"https://www.instagram.com/knuth.ceramics"
|
||||||
}
|
],
|
||||||
html {
|
"founder": {
|
||||||
scroll-behavior: smooth;
|
"@type": "Person",
|
||||||
}
|
"name": "Claudia Knuth",
|
||||||
.parallax-bg {
|
"email": "knuth.claudia@gmail.com",
|
||||||
background-attachment: fixed;
|
"jobTitle": "Ceramic Artist & Potter"
|
||||||
background-position: center;
|
},
|
||||||
background-repeat: no-repeat;
|
"priceRange": "$$",
|
||||||
background-size: cover;
|
"currenciesAccepted": "USD",
|
||||||
}
|
"paymentAccepted": "Credit Card",
|
||||||
::-webkit-scrollbar {
|
"areaServed": [
|
||||||
width: 6px;
|
{
|
||||||
}
|
"@type": "State",
|
||||||
::-webkit-scrollbar-track {
|
"name": "Texas"
|
||||||
background: #F5F4F0;
|
},
|
||||||
}
|
{
|
||||||
::-webkit-scrollbar-thumb {
|
"@type": "Country",
|
||||||
background: #D6D3D1;
|
"name": "United States"
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb:hover {
|
],
|
||||||
background: #78716C;
|
"hasOfferCatalog": {
|
||||||
}
|
"@type": "OfferCatalog",
|
||||||
</style>
|
"name": "Handcrafted Ceramics",
|
||||||
<script type="importmap">
|
"itemListElement": [
|
||||||
{
|
{"@type": "Offer", "itemOffered": {"@type": "Product", "name": "Handmade Ceramic Vases"}},
|
||||||
"imports": {
|
{"@type": "Offer", "itemOffered": {"@type": "Product", "name": "Handmade Ceramic Bowls"}},
|
||||||
"react-dom/": "https://esm.sh/react-dom@^19.2.3/",
|
{"@type": "Offer", "itemOffered": {"@type": "Product", "name": "Handmade Tableware & Dinnerware"}},
|
||||||
"react/": "https://esm.sh/react@^19.2.3/",
|
{"@type": "Offer", "itemOffered": {"@type": "Product", "name": "Custom Ceramic Commissions"}}
|
||||||
"react": "https://esm.sh/react@^19.2.3"
|
]
|
||||||
}
|
},
|
||||||
}
|
"knowsAbout": [
|
||||||
</script>
|
"Wheel-thrown pottery",
|
||||||
</head>
|
"Stoneware ceramics",
|
||||||
<body class="font-body bg-background-light dark:bg-background-dark text-text-main dark:text-gray-200 antialiased transition-colors duration-500 selection:bg-stone-200 selection:text-black">
|
"Handmade tableware",
|
||||||
<div id="root"></div>
|
"Coastal-inspired ceramics",
|
||||||
<script type="module" src="/index.tsx"></script>
|
"Custom dinnerware"
|
||||||
</body>
|
]
|
||||||
</html>
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- WebSite schema for sitelinks search -->
|
||||||
|
<script type="application/ld+json" data-seo="site">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebSite",
|
||||||
|
"name": "KNUTH Ceramics",
|
||||||
|
"url": "https://knuthceramics.com",
|
||||||
|
"description": "Handcrafted ceramics from Corpus Christi, Texas"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400&family=Manrope:wght@200;300;400;500&display=swap" rel="stylesheet"/>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
darkMode: "class",
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
primary: "#292524", // Warm Charcoal
|
||||||
|
secondary: "#78716C", // Warm Stone
|
||||||
|
"background-light": "#F5F4F0", // Soft Sand / Alabaster (Hero)
|
||||||
|
"background-dark": "#1C1917", // Dark Warm Grey
|
||||||
|
// New Sectional Colors
|
||||||
|
"sage": "#D4D9D1", // Soft Sage Green
|
||||||
|
"warm-grey": "#DAD7D4", // Warm Grey
|
||||||
|
"clay-dark": "#33302D", // Deep Charcoal / Clay
|
||||||
|
"terracotta-soft": "#E6DDD5", // Pale Ochre / Soft Terracotta
|
||||||
|
"accent-sand": "#E7E5E4",
|
||||||
|
"accent-warm": "#D6D3D1",
|
||||||
|
"text-main": "#1C1917",
|
||||||
|
"text-muted": "#57534E",
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
display: ["Cormorant Garamond", "serif"],
|
||||||
|
body: ["Manrope", "sans-serif"],
|
||||||
|
},
|
||||||
|
fontSize: {
|
||||||
|
'10xl': '10rem',
|
||||||
|
},
|
||||||
|
spacing: {
|
||||||
|
'128': '32rem',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.material-symbols-outlined {
|
||||||
|
font-variation-settings:
|
||||||
|
'FILL' 0,
|
||||||
|
'wght' 300,
|
||||||
|
'GRAD' 0,
|
||||||
|
'opsz' 24
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
.parallax-bg {
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: #F5F4F0;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #D6D3D1;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #78716C;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="importmap">
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"react-dom/": "https://esm.sh/react-dom@^19.2.3/",
|
||||||
|
"react/": "https://esm.sh/react@^19.2.3/",
|
||||||
|
"react": "https://esm.sh/react@^19.2.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body class="font-body bg-background-light dark:bg-background-dark text-text-main dark:text-gray-200 antialiased transition-colors duration-500 selection:bg-stone-200 selection:text-black">
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/index.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
const rootElement = document.getElementById('root');
|
const rootElement = document.getElementById('root');
|
||||||
if (!rootElement) {
|
if (!rootElement) {
|
||||||
throw new Error("Could not find root element to mount to");
|
throw new Error("Could not find root element to mount to");
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(rootElement);
|
// Use hydrateRoot when react-snap has pre-rendered HTML, createRoot for normal dev/fallback
|
||||||
root.render(
|
if (rootElement.hasChildNodes()) {
|
||||||
<React.StrictMode>
|
ReactDOM.hydrateRoot(
|
||||||
<App />
|
rootElement,
|
||||||
</React.StrictMode>
|
<React.StrictMode>
|
||||||
);
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ReactDOM.createRoot(rootElement).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "KNUTH Ceramics",
|
"name": "KNUTH Ceramics",
|
||||||
"description": "A high-fidelity recreation of the KNUTH Ceramics editorial e-commerce website featuring a minimalist design, custom typography, and responsive layout.",
|
"description": "A high-fidelity recreation of the KNUTH Ceramics editorial e-commerce website featuring a minimalist design, custom typography, and responsive layout.",
|
||||||
"requestFramePermissions": []
|
"requestFramePermissions": []
|
||||||
}
|
}
|
||||||
|
|||||||
5178
Pottery-website/package-lock.json
generated
@@ -1,24 +1,43 @@
|
|||||||
{
|
{
|
||||||
"name": "ikkai-ceramics",
|
"name": "ikkai-ceramics",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview"
|
"postbuild": "react-snap",
|
||||||
},
|
"preview": "vite preview"
|
||||||
"dependencies": {
|
},
|
||||||
"framer-motion": "^12.26.0",
|
"reactSnap": {
|
||||||
"gsap": "^3.14.2",
|
"source": "dist",
|
||||||
"react": "^19.2.3",
|
"include": [
|
||||||
"react-dom": "^19.2.3",
|
"/",
|
||||||
"react-router-dom": "^7.12.0"
|
"/collections",
|
||||||
},
|
"/atelier",
|
||||||
"devDependencies": {
|
"/faq",
|
||||||
"@types/node": "^22.14.0",
|
"/editorial",
|
||||||
"@vitejs/plugin-react": "^5.0.0",
|
"/contact",
|
||||||
"typescript": "~5.8.2",
|
"/shipping",
|
||||||
"vite": "^6.2.0"
|
"/returns"
|
||||||
}
|
],
|
||||||
}
|
"headless": true,
|
||||||
|
"waitFor": 800,
|
||||||
|
"puppeteerArgs": ["--no-sandbox", "--disable-setuid-sandbox"],
|
||||||
|
"inlineCss": false
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"framer-motion": "^12.26.0",
|
||||||
|
"gsap": "^3.14.2",
|
||||||
|
"react": "^19.2.3",
|
||||||
|
"react-dom": "^19.2.3",
|
||||||
|
"react-router-dom": "^7.12.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.14.0",
|
||||||
|
"@vitejs/plugin-react": "^5.0.0",
|
||||||
|
"react-snap": "^1.23.0",
|
||||||
|
"typescript": "~5.8.2",
|
||||||
|
"vite": "^6.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,73 +1,106 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
import InstagramFeed from '../components/InstagramFeed';
|
||||||
const Atelier: React.FC = () => {
|
import SEO, { SITE_URL } from '../components/SEO';
|
||||||
return (
|
|
||||||
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
const atelierSchema = {
|
||||||
<div className="max-w-[1920px] mx-auto px-6 md:px-12">
|
"@context": "https://schema.org",
|
||||||
{/* Intro */}
|
"@type": "AboutPage",
|
||||||
<div className="grid grid-cols-1 md:grid-cols-12 gap-12 mb-32 items-center">
|
"name": "The Studio | KNUTH Ceramics — Pottery in Corpus Christi, TX",
|
||||||
<div className="md:col-span-5 md:col-start-2">
|
"description": "Discover the KNUTH Ceramics atelier in Corpus Christi, Texas. Claudia Knuth creates wheel-thrown stoneware inspired by the raw textures and colors of the Texas Gulf Coast.",
|
||||||
<motion.span
|
"url": `${SITE_URL}/atelier`,
|
||||||
initial={{ opacity: 0 }}
|
"breadcrumb": {
|
||||||
animate={{ opacity: 1 }}
|
"@type": "BreadcrumbList",
|
||||||
transition={{ duration: 1 }}
|
"itemListElement": [
|
||||||
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
{ "@type": "ListItem", "position": 1, "name": "Home", "item": `${SITE_URL}/` },
|
||||||
>
|
{ "@type": "ListItem", "position": 2, "name": "Atelier", "item": `${SITE_URL}/atelier` }
|
||||||
The Studio
|
]
|
||||||
</motion.span>
|
},
|
||||||
<motion.h1
|
"about": {
|
||||||
initial={{ y: 30, opacity: 0 }}
|
"@type": "Person",
|
||||||
animate={{ y: 0, opacity: 1 }}
|
"name": "Claudia Knuth",
|
||||||
transition={{ delay: 0.2, duration: 0.8 }}
|
"jobTitle": "Ceramic Artist & Potter",
|
||||||
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
"email": "knuth.claudia@gmail.com",
|
||||||
>
|
"knowsAbout": ["Wheel-throwing", "Stoneware pottery", "Coastal ceramics", "Custom dinnerware"]
|
||||||
Formed by<br />Hand & Fire
|
}
|
||||||
</motion.h1>
|
};
|
||||||
<motion.p
|
|
||||||
initial={{ y: 30, opacity: 0 }}
|
const Atelier: React.FC = () => {
|
||||||
animate={{ y: 0, opacity: 1 }}
|
return (
|
||||||
transition={{ delay: 0.4, duration: 0.8 }}
|
<>
|
||||||
className="font-body text-lg font-light text-stone-500 leading-relaxed max-w-lg"
|
<SEO
|
||||||
>
|
title="The Studio — Pottery in Corpus Christi, TX | KNUTH Ceramics"
|
||||||
Our atelier is a sanctuary of slow creation. Located in the heart of Corpus Christi, we practice the ancient art of wheel-throwing, honoring the raw beauty of the Texas Coast.
|
description="The KNUTH Ceramics atelier in Corpus Christi, Texas. Claudia Knuth creates wheel-thrown stoneware inspired by the Gulf Coast — fired to cone 6, designed for daily coastal living."
|
||||||
</motion.p>
|
canonical={`${SITE_URL}/atelier`}
|
||||||
</div>
|
schema={atelierSchema}
|
||||||
<div className="md:col-span-12 lg:col-span-6 relative h-[600px] lg:h-[800px] w-full">
|
/>
|
||||||
<motion.div
|
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
||||||
initial={{ clipPath: 'inset(100% 0 0 0)' }}
|
<div className="max-w-[1920px] mx-auto px-6 md:px-12">
|
||||||
animate={{ clipPath: 'inset(0% 0 0 0)' }}
|
{/* Intro */}
|
||||||
transition={{ delay: 0.2, duration: 1.5, ease: "easeOut" }}
|
<div className="grid grid-cols-1 md:grid-cols-12 gap-12 mb-32 items-center">
|
||||||
className="h-full w-full"
|
<div className="md:col-span-5 md:col-start-2">
|
||||||
>
|
<motion.span
|
||||||
<img src="/pottery-studio.png" alt="Pottery Studio in Corpus Christi" className="w-full h-full object-cover" />
|
initial={{ opacity: 0 }}
|
||||||
</motion.div>
|
animate={{ opacity: 1 }}
|
||||||
</div>
|
transition={{ duration: 1 }}
|
||||||
</div>
|
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
||||||
|
>
|
||||||
{/* Philosophy Section */}
|
The Studio
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 border-t border-stone-200 dark:border-stone-800 pt-24">
|
</motion.span>
|
||||||
{[
|
<motion.h1
|
||||||
{ title: "Coastal Clay", text: "We work with stoneware clay bodies that reflect the sandy textures of the Gulf Coast." },
|
initial={{ y: 30, opacity: 0 }}
|
||||||
{ title: "Electric Firing", text: "Fired in oxidation to cone 6, creating durable surfaces that mimic the bleached colors of driftwood and shell." },
|
animate={{ y: 0, opacity: 1 }}
|
||||||
{ title: "Functional Art", text: "Designed to be used and loved. Our ceramics are durable, dishwasher safe, and meant for daily coastal living." }
|
transition={{ delay: 0.2, duration: 0.8 }}
|
||||||
].map((item, idx) => (
|
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
||||||
<motion.div
|
>
|
||||||
key={item.title}
|
Formed by<br />Hand & Fire
|
||||||
initial={{ y: 20, opacity: 0 }}
|
</motion.h1>
|
||||||
whileInView={{ y: 0, opacity: 1 }}
|
<motion.p
|
||||||
viewport={{ once: true }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
transition={{ delay: idx * 0.2, duration: 0.8 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
className="p-8 hover:bg-white dark:hover:bg-black transition-colors duration-500"
|
transition={{ delay: 0.4, duration: 0.8 }}
|
||||||
>
|
className="font-body text-lg font-light text-stone-500 leading-relaxed max-w-lg"
|
||||||
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">{item.title}</h3>
|
>
|
||||||
<p className="font-body font-light text-stone-500 leading-relaxed">{item.text}</p>
|
Our atelier is a sanctuary of slow creation. Located in the heart of Corpus Christi, we practice the ancient art of wheel-throwing, honoring the raw beauty of the Texas Coast.
|
||||||
</motion.div>
|
</motion.p>
|
||||||
))}
|
</div>
|
||||||
</div>
|
<div className="md:col-span-12 lg:col-span-6 relative h-[600px] lg:h-[800px] w-full">
|
||||||
</div>
|
<motion.div
|
||||||
</div>
|
initial={{ clipPath: 'inset(100% 0 0 0)' }}
|
||||||
);
|
animate={{ clipPath: 'inset(0% 0 0 0)' }}
|
||||||
};
|
transition={{ delay: 0.2, duration: 1.5, ease: "easeOut" }}
|
||||||
|
className="h-full w-full"
|
||||||
export default Atelier;
|
>
|
||||||
|
<img src="/landingpage/artelier.png" alt="Pottery Studio in Corpus Christi" className="w-full h-full object-cover" />
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Philosophy Section */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 border-t border-stone-200 dark:border-stone-800 pt-24">
|
||||||
|
{[
|
||||||
|
{ title: "Coastal Clay", text: "We work with stoneware clay bodies that reflect the sandy textures of the Gulf Coast." },
|
||||||
|
{ title: "Electric Firing", text: "Fired in an electric kiln, creating durable surfaces that mimic the bleached colors of driftwood and shell." },
|
||||||
|
{ title: "Functional Art", text: "Designed to be used and loved. Our ceramics are durable, dishwasher safe, and meant for daily coastal living." }
|
||||||
|
].map((item, idx) => (
|
||||||
|
<motion.div
|
||||||
|
key={item.title}
|
||||||
|
initial={{ y: 20, opacity: 0 }}
|
||||||
|
whileInView={{ y: 0, opacity: 1 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
transition={{ delay: idx * 0.2, duration: 0.8 }}
|
||||||
|
className="p-8 hover:bg-white dark:hover:bg-black transition-colors duration-500"
|
||||||
|
>
|
||||||
|
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">{item.title}</h3>
|
||||||
|
<p className="font-body font-light text-stone-500 leading-relaxed">{item.text}</p>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<InstagramFeed />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Atelier;
|
||||||
|
|||||||
@@ -1,73 +1,143 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import { Link } from 'react-router-dom';
|
import { useStore } from '../src/context/StoreContext';
|
||||||
import { useStore } from '../src/context/StoreContext';
|
import { CollectionItem } from '../types';
|
||||||
|
import SEO, { SITE_URL } from '../components/SEO';
|
||||||
const Collections: React.FC = () => {
|
|
||||||
const { products } = useStore();
|
const Collections: React.FC = () => {
|
||||||
return (
|
const { products } = useStore();
|
||||||
<section className="pt-32 pb-24 px-6 md:px-12 bg-stone-50 dark:bg-stone-900 min-h-screen">
|
const [selectedProduct, setSelectedProduct] = React.useState<CollectionItem | null>(null);
|
||||||
<div className="max-w-[1920px] mx-auto">
|
|
||||||
{/* Header */}
|
React.useEffect(() => {
|
||||||
<div className="mb-24 text-center">
|
if (!selectedProduct) {
|
||||||
<motion.h1
|
return undefined;
|
||||||
initial={{ y: 20, opacity: 0 }}
|
}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
|
||||||
transition={{ delay: 0.5, duration: 0.8 }}
|
const handleEscape = (event: KeyboardEvent) => {
|
||||||
className="font-display text-5xl md:text-7xl font-light mb-6 text-text-main dark:text-white"
|
if (event.key === 'Escape') {
|
||||||
>
|
setSelectedProduct(null);
|
||||||
Shop Collection
|
}
|
||||||
</motion.h1>
|
};
|
||||||
<motion.p
|
|
||||||
initial={{ y: 20, opacity: 0 }}
|
window.addEventListener('keydown', handleEscape);
|
||||||
animate={{ y: 0, opacity: 1 }}
|
return () => window.removeEventListener('keydown', handleEscape);
|
||||||
transition={{ delay: 0.7, duration: 0.8 }}
|
}, [selectedProduct]);
|
||||||
className="font-body text-stone-500 max-w-xl mx-auto text-lg font-light leading-relaxed"
|
|
||||||
>
|
const collectionsSchema = {
|
||||||
Curated series of functional objects. From our 'Sandstone' mugs to 'Seafoam' vases, each collection celebrates the palette of the Texas coast.
|
"@context": "https://schema.org",
|
||||||
</motion.p>
|
"@type": "CollectionPage",
|
||||||
</div>
|
"name": "Handcrafted Ceramic Collection | KNUTH Ceramics",
|
||||||
|
"description": "Browse KNUTH Ceramics' handcrafted stoneware collection. Each piece is wheel-thrown in Corpus Christi, Texas, inspired by the Gulf Coast. Vases, bowls, tableware, and more from $45.",
|
||||||
{/* Grid */}
|
"url": `${SITE_URL}/collections`,
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-12 gap-y-16 lg:gap-x-16 px-4">
|
"breadcrumb": {
|
||||||
{products.map((collection, index) => (
|
"@type": "BreadcrumbList",
|
||||||
<Link to={`/collections/${collection.slug}`} key={collection.id} className="block group cursor-pointer">
|
"itemListElement": [
|
||||||
<motion.div
|
{ "@type": "ListItem", "position": 1, "name": "Home", "item": `${SITE_URL}/` },
|
||||||
initial={{ opacity: 0, y: 20 }}
|
{ "@type": "ListItem", "position": 2, "name": "Collections", "item": `${SITE_URL}/collections` }
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
]
|
||||||
viewport={{ once: true }}
|
},
|
||||||
transition={{ delay: index * 0.1 }}
|
"numberOfItems": products.length,
|
||||||
>
|
"itemListElement": products.map((p, i) => ({
|
||||||
<div className="relative overflow-hidden mb-6 aspect-[4/5] bg-stone-100">
|
"@type": "ListItem",
|
||||||
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/5 transition-colors duration-500 z-10" />
|
"position": i + 1,
|
||||||
<img
|
"url": `${SITE_URL}/collections/${p.slug}`,
|
||||||
src={collection.image}
|
"name": p.title
|
||||||
alt={collection.title}
|
}))
|
||||||
className="w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-110"
|
};
|
||||||
/>
|
|
||||||
{/* Quick overlay info */}
|
return (
|
||||||
<div className="absolute bottom-6 left-6 z-20 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
<>
|
||||||
<span className="bg-white dark:bg-black px-4 py-2 text-xs uppercase tracking-widest text-text-main dark:text-white">View Item</span>
|
<SEO
|
||||||
</div>
|
title="Handcrafted Ceramic Collection | KNUTH Ceramics"
|
||||||
</div>
|
description="Browse KNUTH Ceramics' handcrafted stoneware collection. Each piece is wheel-thrown in Corpus Christi, Texas. Vases, bowls, and tableware from $45 — made in the Gulf Coast tradition."
|
||||||
|
canonical={`${SITE_URL}/collections`}
|
||||||
<div className="flex justify-between items-baseline pr-2">
|
schema={collectionsSchema}
|
||||||
<div>
|
/>
|
||||||
<h2 className="font-display text-3xl font-light text-text-main dark:text-white mb-1 group-hover:underline decoration-1 underline-offset-4">
|
<section className="pt-32 pb-24 px-6 md:px-12 bg-stone-50 dark:bg-stone-900 min-h-screen">
|
||||||
{collection.title}
|
<div className="max-w-[1920px] mx-auto">
|
||||||
</h2>
|
{/* Header */}
|
||||||
</div>
|
<div className="mb-24 text-center">
|
||||||
<span className="text-lg font-light text-text-main dark:text-white">
|
<motion.h1
|
||||||
${collection.price}
|
initial={{ y: 20, opacity: 0 }}
|
||||||
</span>
|
animate={{ y: 0, opacity: 1 }}
|
||||||
</div>
|
transition={{ delay: 0.5, duration: 0.8 }}
|
||||||
</motion.div>
|
className="font-display text-5xl md:text-7xl font-light mb-6 text-text-main dark:text-white"
|
||||||
</Link>
|
>
|
||||||
))}
|
Collection
|
||||||
</div>
|
</motion.h1>
|
||||||
</div>
|
<motion.p
|
||||||
</section>
|
initial={{ y: 20, opacity: 0 }}
|
||||||
);
|
animate={{ y: 0, opacity: 1 }}
|
||||||
};
|
transition={{ delay: 0.7, duration: 0.8 }}
|
||||||
|
className="font-body text-stone-500 max-w-xl mx-auto text-lg font-light leading-relaxed"
|
||||||
export default Collections;
|
>
|
||||||
|
A gallery of handmade objects. Select any piece to view it larger, then close when you are done.
|
||||||
|
</motion.p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Grid */}
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-x-6 gap-y-10 lg:gap-x-10 px-4">
|
||||||
|
{products.map((collection, index) => (
|
||||||
|
<motion.button
|
||||||
|
key={collection.id}
|
||||||
|
type="button"
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
transition={{ delay: index * 0.1 }}
|
||||||
|
onClick={() => setSelectedProduct(collection)}
|
||||||
|
className="group block cursor-pointer text-left"
|
||||||
|
>
|
||||||
|
<div className="relative overflow-hidden mb-6 aspect-[4/5] bg-stone-100">
|
||||||
|
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/5 transition-colors duration-500 z-10" />
|
||||||
|
<img
|
||||||
|
src={collection.image}
|
||||||
|
alt={collection.title}
|
||||||
|
className="w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</motion.button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<AnimatePresence>
|
||||||
|
{selectedProduct && (
|
||||||
|
<motion.div
|
||||||
|
className="fixed inset-0 z-[70] bg-black/80 backdrop-blur-sm flex items-center justify-center p-4 sm:p-8"
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
onClick={() => setSelectedProduct(null)}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
className="relative flex w-full max-w-6xl items-center justify-center"
|
||||||
|
initial={{ scale: 0.96, opacity: 0 }}
|
||||||
|
animate={{ scale: 1, opacity: 1 }}
|
||||||
|
exit={{ scale: 0.96, opacity: 0 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={selectedProduct.image}
|
||||||
|
alt={selectedProduct.title}
|
||||||
|
className="max-h-[92vh] max-w-full object-contain"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setSelectedProduct(null)}
|
||||||
|
className="absolute right-3 top-3 text-4xl font-normal leading-none text-red-500 transition-colors hover:text-red-400"
|
||||||
|
aria-label="Close image preview"
|
||||||
|
>
|
||||||
|
x
|
||||||
|
</button>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Collections;
|
||||||
|
|||||||
@@ -1,218 +1,212 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
const Contact: React.FC = () => {
|
const Contact: React.FC = () => {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
subject: '',
|
subject: '',
|
||||||
message: ''
|
message: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// Handle form submission
|
// Handle form submission
|
||||||
console.log('Form submitted:', formData);
|
console.log('Form submitted:', formData);
|
||||||
alert('Thank you for your message! We\'ll get back to you within 1-2 business days.');
|
alert('Thank you for your message! We\'ll get back to you within 1-2 business days.');
|
||||||
setFormData({ name: '', email: '', subject: '', message: '' });
|
setFormData({ name: '', email: '', subject: '', message: '' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
||||||
setFormData({
|
setFormData({
|
||||||
...formData,
|
...formData,
|
||||||
[e.target.name]: e.target.value
|
[e.target.name]: e.target.value
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const contactInfo = [
|
const contactInfo = [
|
||||||
{
|
{
|
||||||
icon: "📧",
|
icon: "📧",
|
||||||
title: "Email",
|
title: "Email",
|
||||||
detail: "support@knuthceramics.com",
|
detail: "knuth.claudia@gmail.com",
|
||||||
description: "Best for order inquiries or detailed questions"
|
description: "For commissions, inquiries, and custom orders. We reply within 1–2 business days."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "📞",
|
icon: "📍",
|
||||||
title: "Phone",
|
title: "Location",
|
||||||
detail: "(361) 555-1234",
|
detail: "Corpus Christi, Texas",
|
||||||
description: "Mon–Fri, 9am–5pm CST"
|
description: "Our studio is based on the Gulf Coast of South Texas."
|
||||||
},
|
}
|
||||||
{
|
];
|
||||||
icon: "📍",
|
|
||||||
title: "Workshop",
|
return (
|
||||||
detail: "123 Artisan Lane, Corpus Christi, TX 78401",
|
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
||||||
description: "Please note: this is our workshop, not a retail store"
|
<div className="max-w-[1400px] mx-auto px-6 md:px-12">
|
||||||
}
|
{/* Header */}
|
||||||
];
|
<div className="mb-24 max-w-3xl">
|
||||||
|
<motion.span
|
||||||
return (
|
initial={{ opacity: 0 }}
|
||||||
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
animate={{ opacity: 1 }}
|
||||||
<div className="max-w-[1400px] mx-auto px-6 md:px-12">
|
transition={{ duration: 1 }}
|
||||||
{/* Header */}
|
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
||||||
<div className="mb-24 max-w-3xl">
|
>
|
||||||
<motion.span
|
Get in Touch
|
||||||
initial={{ opacity: 0 }}
|
</motion.span>
|
||||||
animate={{ opacity: 1 }}
|
<motion.h1
|
||||||
transition={{ duration: 1 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
animate={{ y: 0, opacity: 1 }}
|
||||||
>
|
transition={{ delay: 0.2, duration: 0.8 }}
|
||||||
Get in Touch
|
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
||||||
</motion.span>
|
>
|
||||||
<motion.h1
|
Contact<br />Us
|
||||||
initial={{ y: 30, opacity: 0 }}
|
</motion.h1>
|
||||||
animate={{ y: 0, opacity: 1 }}
|
<motion.p
|
||||||
transition={{ delay: 0.2, duration: 0.8 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
animate={{ y: 0, opacity: 1 }}
|
||||||
>
|
transition={{ delay: 0.4, duration: 0.8 }}
|
||||||
Contact<br />Us
|
className="font-body text-lg font-light text-stone-500 leading-relaxed"
|
||||||
</motion.h1>
|
>
|
||||||
<motion.p
|
We're happy to help with any questions, custom requests, or feedback. We usually reply to emails within 1–2 business days.
|
||||||
initial={{ y: 30, opacity: 0 }}
|
</motion.p>
|
||||||
animate={{ y: 0, opacity: 1 }}
|
</div>
|
||||||
transition={{ delay: 0.4, duration: 0.8 }}
|
|
||||||
className="font-body text-lg font-light text-stone-500 leading-relaxed"
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 mb-24">
|
||||||
>
|
{/* Contact Form */}
|
||||||
We're happy to help with any questions, custom requests, or feedback. We usually reply to emails within 1–2 business days.
|
<motion.div
|
||||||
</motion.p>
|
initial={{ y: 30, opacity: 0 }}
|
||||||
</div>
|
animate={{ y: 0, opacity: 1 }}
|
||||||
|
transition={{ delay: 0.5, duration: 0.8 }}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 mb-24">
|
>
|
||||||
{/* Contact Form */}
|
<h2 className="font-display text-3xl mb-8 text-text-main dark:text-white">
|
||||||
<motion.div
|
Send us a message
|
||||||
initial={{ y: 30, opacity: 0 }}
|
</h2>
|
||||||
animate={{ y: 0, opacity: 1 }}
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
transition={{ delay: 0.5, duration: 0.8 }}
|
<div>
|
||||||
>
|
<label className="block text-xs uppercase tracking-widest text-stone-500 mb-3">
|
||||||
<h2 className="font-display text-3xl mb-8 text-text-main dark:text-white">
|
Name *
|
||||||
Send us a message
|
</label>
|
||||||
</h2>
|
<input
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
type="text"
|
||||||
<div>
|
name="name"
|
||||||
<label className="block text-xs uppercase tracking-widest text-stone-500 mb-3">
|
value={formData.name}
|
||||||
Name *
|
onChange={handleChange}
|
||||||
</label>
|
required
|
||||||
<input
|
className="w-full px-6 py-4 bg-white dark:bg-black border border-stone-200 dark:border-stone-800 focus:border-stone-400 dark:focus:border-stone-600 outline-none transition-colors text-text-main dark:text-white"
|
||||||
type="text"
|
/>
|
||||||
name="name"
|
</div>
|
||||||
value={formData.name}
|
<div>
|
||||||
onChange={handleChange}
|
<label className="block text-xs uppercase tracking-widest text-stone-500 mb-3">
|
||||||
required
|
Email *
|
||||||
className="w-full px-6 py-4 bg-white dark:bg-black border border-stone-200 dark:border-stone-800 focus:border-stone-400 dark:focus:border-stone-600 outline-none transition-colors text-text-main dark:text-white"
|
</label>
|
||||||
/>
|
<input
|
||||||
</div>
|
type="email"
|
||||||
<div>
|
name="email"
|
||||||
<label className="block text-xs uppercase tracking-widest text-stone-500 mb-3">
|
value={formData.email}
|
||||||
Email *
|
onChange={handleChange}
|
||||||
</label>
|
required
|
||||||
<input
|
className="w-full px-6 py-4 bg-white dark:bg-black border border-stone-200 dark:border-stone-800 focus:border-stone-400 dark:focus:border-stone-600 outline-none transition-colors text-text-main dark:text-white"
|
||||||
type="email"
|
/>
|
||||||
name="email"
|
</div>
|
||||||
value={formData.email}
|
<div>
|
||||||
onChange={handleChange}
|
<label className="block text-xs uppercase tracking-widest text-stone-500 mb-3">
|
||||||
required
|
Subject
|
||||||
className="w-full px-6 py-4 bg-white dark:bg-black border border-stone-200 dark:border-stone-800 focus:border-stone-400 dark:focus:border-stone-600 outline-none transition-colors text-text-main dark:text-white"
|
</label>
|
||||||
/>
|
<select
|
||||||
</div>
|
name="subject"
|
||||||
<div>
|
value={formData.subject}
|
||||||
<label className="block text-xs uppercase tracking-widest text-stone-500 mb-3">
|
onChange={handleChange}
|
||||||
Subject
|
className="w-full px-6 py-4 bg-white dark:bg-black border border-stone-200 dark:border-stone-800 focus:border-stone-400 dark:focus:border-stone-600 outline-none transition-colors text-text-main dark:text-white"
|
||||||
</label>
|
>
|
||||||
<select
|
<option value="">Select a subject</option>
|
||||||
name="subject"
|
<option value="order">Order Inquiry</option>
|
||||||
value={formData.subject}
|
<option value="custom">Custom Request</option>
|
||||||
onChange={handleChange}
|
<option value="shipping">Shipping Question</option>
|
||||||
className="w-full px-6 py-4 bg-white dark:bg-black border border-stone-200 dark:border-stone-800 focus:border-stone-400 dark:focus:border-stone-600 outline-none transition-colors text-text-main dark:text-white"
|
<option value="return">Return/Exchange</option>
|
||||||
>
|
<option value="other">Other</option>
|
||||||
<option value="">Select a subject</option>
|
</select>
|
||||||
<option value="order">Order Inquiry</option>
|
</div>
|
||||||
<option value="custom">Custom Request</option>
|
<div>
|
||||||
<option value="shipping">Shipping Question</option>
|
<label className="block text-xs uppercase tracking-widest text-stone-500 mb-3">
|
||||||
<option value="return">Return/Exchange</option>
|
Message *
|
||||||
<option value="other">Other</option>
|
</label>
|
||||||
</select>
|
<textarea
|
||||||
</div>
|
name="message"
|
||||||
<div>
|
value={formData.message}
|
||||||
<label className="block text-xs uppercase tracking-widest text-stone-500 mb-3">
|
onChange={handleChange}
|
||||||
Message *
|
required
|
||||||
</label>
|
rows={6}
|
||||||
<textarea
|
className="w-full px-6 py-4 bg-white dark:bg-black border border-stone-200 dark:border-stone-800 focus:border-stone-400 dark:focus:border-stone-600 outline-none transition-colors resize-none text-text-main dark:text-white"
|
||||||
name="message"
|
/>
|
||||||
value={formData.message}
|
</div>
|
||||||
onChange={handleChange}
|
<button
|
||||||
required
|
type="submit"
|
||||||
rows={6}
|
className="w-full bg-primary dark:bg-white text-white dark:text-black px-10 py-4 text-xs font-bold uppercase tracking-widest hover:bg-stone-800 dark:hover:bg-stone-200 transition-colors"
|
||||||
className="w-full px-6 py-4 bg-white dark:bg-black border border-stone-200 dark:border-stone-800 focus:border-stone-400 dark:focus:border-stone-600 outline-none transition-colors resize-none text-text-main dark:text-white"
|
>
|
||||||
/>
|
Send Message
|
||||||
</div>
|
</button>
|
||||||
<button
|
</form>
|
||||||
type="submit"
|
</motion.div>
|
||||||
className="w-full bg-primary dark:bg-white text-white dark:text-black px-10 py-4 text-xs font-bold uppercase tracking-widest hover:bg-stone-800 dark:hover:bg-stone-200 transition-colors"
|
|
||||||
>
|
{/* Contact Information */}
|
||||||
Send Message
|
<div className="space-y-8">
|
||||||
</button>
|
{contactInfo.map((info, index) => (
|
||||||
</form>
|
<motion.div
|
||||||
</motion.div>
|
key={info.title}
|
||||||
|
initial={{ y: 20, opacity: 0 }}
|
||||||
{/* Contact Information */}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
<div className="space-y-8">
|
transition={{ delay: 0.6 + index * 0.1, duration: 0.6 }}
|
||||||
{contactInfo.map((info, index) => (
|
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
||||||
<motion.div
|
>
|
||||||
key={info.title}
|
<div className="text-4xl mb-4">{info.icon}</div>
|
||||||
initial={{ y: 20, opacity: 0 }}
|
<h3 className="font-display text-2xl mb-2 text-text-main dark:text-white">
|
||||||
animate={{ y: 0, opacity: 1 }}
|
{info.title}
|
||||||
transition={{ delay: 0.6 + index * 0.1, duration: 0.6 }}
|
</h3>
|
||||||
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
<p className="font-body text-lg text-text-main dark:text-white mb-2">
|
||||||
>
|
{info.detail}
|
||||||
<div className="text-4xl mb-4">{info.icon}</div>
|
</p>
|
||||||
<h3 className="font-display text-2xl mb-2 text-text-main dark:text-white">
|
<p className="font-body font-light text-sm text-stone-500">
|
||||||
{info.title}
|
{info.description}
|
||||||
</h3>
|
</p>
|
||||||
<p className="font-body text-lg text-text-main dark:text-white mb-2">
|
</motion.div>
|
||||||
{info.detail}
|
))}
|
||||||
</p>
|
</div>
|
||||||
<p className="font-body font-light text-sm text-stone-500">
|
</div>
|
||||||
{info.description}
|
|
||||||
</p>
|
{/* Social Media */}
|
||||||
</motion.div>
|
<motion.div
|
||||||
))}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
</div>
|
animate={{ y: 0, opacity: 1 }}
|
||||||
</div>
|
transition={{ delay: 1, duration: 0.8 }}
|
||||||
|
className="text-center p-12 bg-stone-100 dark:bg-black border border-stone-200 dark:border-stone-800"
|
||||||
{/* Social Media */}
|
>
|
||||||
<motion.div
|
<h3 className="font-display text-3xl mb-4 text-text-main dark:text-white">
|
||||||
initial={{ y: 30, opacity: 0 }}
|
Follow Our Journey
|
||||||
animate={{ y: 0, opacity: 1 }}
|
</h3>
|
||||||
transition={{ delay: 1, duration: 0.8 }}
|
<p className="font-body font-light text-stone-500 mb-8">
|
||||||
className="text-center p-12 bg-stone-100 dark:bg-black border border-stone-200 dark:border-stone-800"
|
Connect with us on Instagram or Facebook for new releases and behind-the-scenes peeks.
|
||||||
>
|
</p>
|
||||||
<h3 className="font-display text-3xl mb-4 text-text-main dark:text-white">
|
<div className="flex justify-center gap-6">
|
||||||
Follow Our Journey
|
<a
|
||||||
</h3>
|
href="https://instagram.com/knuth.ceramics"
|
||||||
<p className="font-body font-light text-stone-500 mb-8">
|
target="_blank"
|
||||||
Connect with us on Instagram or Facebook for new releases and behind-the-scenes peeks.
|
rel="noopener noreferrer"
|
||||||
</p>
|
className="px-8 py-3 bg-white dark:bg-stone-900 text-black dark:text-white border border-stone-300 dark:border-stone-700 text-xs font-bold uppercase tracking-widest hover:bg-stone-100 dark:hover:bg-stone-800 transition-colors"
|
||||||
<div className="flex justify-center gap-6">
|
>
|
||||||
<a
|
Instagram
|
||||||
href="https://instagram.com/knuthceramics"
|
</a>
|
||||||
target="_blank"
|
<a
|
||||||
rel="noopener noreferrer"
|
href="https://facebook.com/knuthceramics"
|
||||||
className="px-8 py-3 bg-white dark:bg-stone-900 text-black dark:text-white border border-stone-300 dark:border-stone-700 text-xs font-bold uppercase tracking-widest hover:bg-stone-100 dark:hover:bg-stone-800 transition-colors"
|
target="_blank"
|
||||||
>
|
rel="noopener noreferrer"
|
||||||
Instagram
|
className="px-8 py-3 bg-white dark:bg-stone-900 text-black dark:text-white border border-stone-300 dark:border-stone-700 text-xs font-bold uppercase tracking-widest hover:bg-stone-100 dark:hover:bg-stone-800 transition-colors"
|
||||||
</a>
|
>
|
||||||
<a
|
Facebook
|
||||||
href="https://facebook.com/knuthceramics"
|
</a>
|
||||||
target="_blank"
|
</div>
|
||||||
rel="noopener noreferrer"
|
</motion.div>
|
||||||
className="px-8 py-3 bg-white dark:bg-stone-900 text-black dark:text-white border border-stone-300 dark:border-stone-700 text-xs font-bold uppercase tracking-widest hover:bg-stone-100 dark:hover:bg-stone-800 transition-colors"
|
</div>
|
||||||
>
|
</div>
|
||||||
Facebook
|
);
|
||||||
</a>
|
};
|
||||||
</div>
|
|
||||||
</motion.div>
|
export default Contact;
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Contact;
|
|
||||||
|
|||||||
@@ -1,194 +1,194 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
const Cookies: React.FC = () => {
|
const Cookies: React.FC = () => {
|
||||||
const cookieTypes = [
|
const cookieTypes = [
|
||||||
{
|
{
|
||||||
type: "Essential Cookies",
|
type: "Essential Cookies",
|
||||||
icon: "🔒",
|
icon: "🔒",
|
||||||
description: "These are necessary for basic site functions (like keeping items in your cart, secure login, etc.) and cannot be turned off without affecting the site.",
|
description: "These are necessary for basic site functions (like keeping items in your cart, secure login, etc.) and cannot be turned off without affecting the site.",
|
||||||
canDisable: false
|
canDisable: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Analytics Cookies",
|
type: "Analytics Cookies",
|
||||||
icon: "📊",
|
icon: "📊",
|
||||||
description: "We use Google Analytics to learn how customers find and use our site. These cookies help us improve content, layout, and functionality.",
|
description: "We use Google Analytics to learn how customers find and use our site. These cookies help us improve content, layout, and functionality.",
|
||||||
canDisable: true,
|
canDisable: true,
|
||||||
example: "Google Analytics"
|
example: "Google Analytics"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Marketing Cookies",
|
type: "Marketing Cookies",
|
||||||
icon: "🎯",
|
icon: "🎯",
|
||||||
description: "If you opt-in, we may use cookies to show you personalized ads or to measure ad performance (e.g. Facebook Pixel, Google AdWords). No personal information is stored – just anonymous data to track which ads work best.",
|
description: "If you opt-in, we may use cookies to show you personalized ads or to measure ad performance (e.g. Facebook Pixel, Google AdWords). No personal information is stored – just anonymous data to track which ads work best.",
|
||||||
canDisable: true,
|
canDisable: true,
|
||||||
example: "Facebook Pixel, Google AdWords"
|
example: "Facebook Pixel, Google AdWords"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-12">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-12">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-24">
|
<div className="mb-24">
|
||||||
<motion.span
|
<motion.span
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ duration: 1 }}
|
transition={{ duration: 1 }}
|
||||||
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
||||||
>
|
>
|
||||||
Legal Information
|
Legal Information
|
||||||
</motion.span>
|
</motion.span>
|
||||||
<motion.h1
|
<motion.h1
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.2, duration: 0.8 }}
|
transition={{ delay: 0.2, duration: 0.8 }}
|
||||||
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
||||||
>
|
>
|
||||||
Cookie<br />Policy
|
Cookie<br />Policy
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<motion.p
|
<motion.p
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.4, duration: 0.8 }}
|
transition={{ delay: 0.4, duration: 0.8 }}
|
||||||
className="font-body text-lg font-light text-stone-500 leading-relaxed max-w-3xl"
|
className="font-body text-lg font-light text-stone-500 leading-relaxed max-w-3xl"
|
||||||
>
|
>
|
||||||
We use cookies and similar technologies to make our website work better for you. Cookies are small text files stored on your device. Here's how we use them and how you can control them.
|
We use cookies and similar technologies to make our website work better for you. Cookies are small text files stored on your device. Here's how we use them and how you can control them.
|
||||||
</motion.p>
|
</motion.p>
|
||||||
<motion.p
|
<motion.p
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.5, duration: 0.8 }}
|
transition={{ delay: 0.5, duration: 0.8 }}
|
||||||
className="font-body text-sm font-light text-stone-400 mt-4"
|
className="font-body text-sm font-light text-stone-400 mt-4"
|
||||||
>
|
>
|
||||||
Last Updated: February 2, 2025
|
Last Updated: February 2, 2025
|
||||||
</motion.p>
|
</motion.p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* What Are Cookies */}
|
{/* What Are Cookies */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.6, duration: 0.6 }}
|
transition={{ delay: 0.6, duration: 0.6 }}
|
||||||
className="mb-16 p-8 bg-amber-50 dark:bg-amber-900/10 border-l-4 border-amber-500"
|
className="mb-16 p-8 bg-amber-50 dark:bg-amber-900/10 border-l-4 border-amber-500"
|
||||||
>
|
>
|
||||||
<h2 className="font-display text-2xl mb-4 text-text-main dark:text-white">
|
<h2 className="font-display text-2xl mb-4 text-text-main dark:text-white">
|
||||||
What Are Cookies?
|
What Are Cookies?
|
||||||
</h2>
|
</h2>
|
||||||
<p className="font-body font-light text-stone-700 dark:text-stone-300 leading-relaxed">
|
<p className="font-body font-light text-stone-700 dark:text-stone-300 leading-relaxed">
|
||||||
Cookies are small text files that websites store on your computer or mobile device when you visit them. They help the website remember information about your visit, like your preferred language, items in your shopping cart, and other settings. This makes your next visit easier and the site more useful to you.
|
Cookies are small text files that websites store on your computer or mobile device when you visit them. They help the website remember information about your visit, like your preferred language, items in your shopping cart, and other settings. This makes your next visit easier and the site more useful to you.
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Cookie Types */}
|
{/* Cookie Types */}
|
||||||
<div className="mb-24">
|
<div className="mb-24">
|
||||||
<h2 className="font-display text-4xl md:text-5xl mb-12 text-text-main dark:text-white">
|
<h2 className="font-display text-4xl md:text-5xl mb-12 text-text-main dark:text-white">
|
||||||
Types of Cookies We Use
|
Types of Cookies We Use
|
||||||
</h2>
|
</h2>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
{cookieTypes.map((cookie, index) => (
|
{cookieTypes.map((cookie, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={cookie.type}
|
key={cookie.type}
|
||||||
initial={{ y: 20, opacity: 0 }}
|
initial={{ y: 20, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.7 + index * 0.1, duration: 0.6 }}
|
transition={{ delay: 0.7 + index * 0.1, duration: 0.6 }}
|
||||||
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
||||||
>
|
>
|
||||||
<div className="text-5xl mb-6">{cookie.icon}</div>
|
<div className="text-5xl mb-6">{cookie.icon}</div>
|
||||||
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">
|
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">
|
||||||
{cookie.type}
|
{cookie.type}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed mb-4">
|
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed mb-4">
|
||||||
{cookie.description}
|
{cookie.description}
|
||||||
</p>
|
</p>
|
||||||
{cookie.example && (
|
{cookie.example && (
|
||||||
<p className="font-body text-sm text-stone-500 italic">
|
<p className="font-body text-sm text-stone-500 italic">
|
||||||
Examples: {cookie.example}
|
Examples: {cookie.example}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className="mt-6 pt-6 border-t border-stone-200 dark:border-stone-800">
|
<div className="mt-6 pt-6 border-t border-stone-200 dark:border-stone-800">
|
||||||
<span className={`inline-block px-4 py-2 text-xs font-bold uppercase tracking-widest ${cookie.canDisable ? 'bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-400' : 'bg-stone-200 dark:bg-stone-800 text-stone-600 dark:text-stone-400'}`}>
|
<span className={`inline-block px-4 py-2 text-xs font-bold uppercase tracking-widest ${cookie.canDisable ? 'bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-400' : 'bg-stone-200 dark:bg-stone-800 text-stone-600 dark:text-stone-400'}`}>
|
||||||
{cookie.canDisable ? 'Can be disabled' : 'Always active'}
|
{cookie.canDisable ? 'Can be disabled' : 'Always active'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Managing Cookies */}
|
{/* Managing Cookies */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 1, duration: 0.6 }}
|
transition={{ delay: 1, duration: 0.6 }}
|
||||||
className="mb-16 border-l-2 border-stone-300 dark:border-stone-700 pl-8"
|
className="mb-16 border-l-2 border-stone-300 dark:border-stone-700 pl-8"
|
||||||
>
|
>
|
||||||
<h2 className="font-display text-3xl md:text-4xl mb-6 text-text-main dark:text-white">
|
<h2 className="font-display text-3xl md:text-4xl mb-6 text-text-main dark:text-white">
|
||||||
Managing Your Cookie Preferences
|
Managing Your Cookie Preferences
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-4 font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
<div className="space-y-4 font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
||||||
<p>
|
<p>
|
||||||
You can control or disable cookies in your browser settings. However, disabling cookies may limit your ability to use some features (like staying logged in or completing a purchase).
|
You can control or disable cookies in your browser settings. However, disabling cookies may limit your ability to use some features (like staying logged in or completing a purchase).
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
By continuing to use our site without changing your settings, you consent to the use of cookies as described in this policy.
|
By continuing to use our site without changing your settings, you consent to the use of cookies as described in this policy.
|
||||||
</p>
|
</p>
|
||||||
<p className="font-semibold text-text-main dark:text-white">
|
<p className="font-semibold text-text-main dark:text-white">
|
||||||
Browser Cookie Settings:
|
Browser Cookie Settings:
|
||||||
</p>
|
</p>
|
||||||
<ul className="list-disc list-inside space-y-2 ml-4">
|
<ul className="list-disc list-inside space-y-2 ml-4">
|
||||||
<li><strong>Chrome:</strong> Settings → Privacy and security → Cookies and other site data</li>
|
<li><strong>Chrome:</strong> Settings → Privacy and security → Cookies and other site data</li>
|
||||||
<li><strong>Firefox:</strong> Preferences → Privacy & Security → Cookies and Site Data</li>
|
<li><strong>Firefox:</strong> Preferences → Privacy & Security → Cookies and Site Data</li>
|
||||||
<li><strong>Safari:</strong> Preferences → Privacy → Manage Website Data</li>
|
<li><strong>Safari:</strong> Preferences → Privacy → Manage Website Data</li>
|
||||||
<li><strong>Edge:</strong> Settings → Cookies and site permissions → Manage and delete cookies</li>
|
<li><strong>Edge:</strong> Settings → Cookies and site permissions → Manage and delete cookies</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Third-Party Cookies */}
|
{/* Third-Party Cookies */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 1.2, duration: 0.6 }}
|
transition={{ delay: 1.2, duration: 0.6 }}
|
||||||
className="mb-16 p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
className="mb-16 p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
||||||
>
|
>
|
||||||
<h2 className="font-display text-3xl mb-4 text-text-main dark:text-white">
|
<h2 className="font-display text-3xl mb-4 text-text-main dark:text-white">
|
||||||
Third-Party Cookies
|
Third-Party Cookies
|
||||||
</h2>
|
</h2>
|
||||||
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed mb-4">
|
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed mb-4">
|
||||||
Some cookies on our site are set by third-party services we use, such as:
|
Some cookies on our site are set by third-party services we use, such as:
|
||||||
</p>
|
</p>
|
||||||
<ul className="list-disc list-inside space-y-2 font-body font-light text-stone-600 dark:text-stone-400 ml-4">
|
<ul className="list-disc list-inside space-y-2 font-body font-light text-stone-600 dark:text-stone-400 ml-4">
|
||||||
<li><strong>Google Analytics:</strong> To understand how visitors use our site</li>
|
<li><strong>Google Analytics:</strong> To understand how visitors use our site</li>
|
||||||
<li><strong>Payment Processors:</strong> To securely process transactions</li>
|
<li><strong>Payment Processors:</strong> To securely process transactions</li>
|
||||||
<li><strong>Social Media Platforms:</strong> If you interact with embedded social media content</li>
|
<li><strong>Social Media Platforms:</strong> If you interact with embedded social media content</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed mt-4">
|
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed mt-4">
|
||||||
These third parties have their own privacy policies governing their use of your information. We recommend reviewing their policies to understand how they use cookies.
|
These third parties have their own privacy policies governing their use of your information. We recommend reviewing their policies to understand how they use cookies.
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Contact */}
|
{/* Contact */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 1.4, duration: 0.8 }}
|
transition={{ delay: 1.4, duration: 0.8 }}
|
||||||
className="p-12 bg-primary dark:bg-black text-white text-center border border-stone-800"
|
className="p-12 bg-primary dark:bg-black text-white text-center border border-stone-800"
|
||||||
>
|
>
|
||||||
<h2 className="font-display text-3xl mb-4">Questions About Cookies?</h2>
|
<h2 className="font-display text-3xl mb-4">Questions About Cookies?</h2>
|
||||||
<p className="font-body font-light text-stone-300 mb-8 max-w-2xl mx-auto">
|
<p className="font-body font-light text-stone-300 mb-8 max-w-2xl mx-auto">
|
||||||
For any questions about cookies or to opt-out of analytics tracking, please contact us at <strong>support@knuthceramics.com</strong>
|
For any questions about cookies or to opt-out of analytics tracking, please contact us at <strong>support@knuthceramics.com</strong>
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
to="/contact"
|
to="/contact"
|
||||||
className="inline-block bg-white text-black px-10 py-4 text-xs font-bold uppercase tracking-widest hover:bg-stone-200 transition-colors"
|
className="inline-block bg-white text-black px-10 py-4 text-xs font-bold uppercase tracking-widest hover:bg-stone-200 transition-colors"
|
||||||
>
|
>
|
||||||
Contact Us
|
Contact Us
|
||||||
</Link>
|
</Link>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Cookies;
|
export default Cookies;
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ import { Link } from 'react-router-dom';
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { useStore } from '../src/context/StoreContext';
|
import { useStore } from '../src/context/StoreContext';
|
||||||
|
|
||||||
const Editorial: React.FC = () => {
|
const Editorial: React.FC = () => {
|
||||||
const { articles, isLoading } = useStore();
|
const { articles, isLoading } = useStore();
|
||||||
|
const getArticleHref = (slug: string) => (
|
||||||
|
slug.startsWith('/editorial/') ? slug : `/editorial/${slug}`
|
||||||
|
);
|
||||||
|
|
||||||
if (isLoading) return <div className="min-h-screen flex items-center justify-center pt-24 font-light text-stone-400">Loading Journal...</div>;
|
if (isLoading) return <div className="min-h-screen flex items-center justify-center pt-24 font-light text-stone-400">Loading Journal...</div>;
|
||||||
if (!articles || articles.length === 0) {
|
if (!articles || articles.length === 0) {
|
||||||
@@ -25,7 +28,7 @@ const Editorial: React.FC = () => {
|
|||||||
{/* Featured Post */}
|
{/* Featured Post */}
|
||||||
<section className="px-6 mb-32">
|
<section className="px-6 mb-32">
|
||||||
<div className="max-w-[1400px] mx-auto">
|
<div className="max-w-[1400px] mx-auto">
|
||||||
<Link to={`/editorial/${featuredArticle.slug}`} className="group block">
|
<Link to={getArticleHref(featuredArticle.slug)} className="group block">
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 items-center">
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 items-center">
|
||||||
<div className="lg:col-span-8 overflow-hidden rounded-sm aspect-[16/9]">
|
<div className="lg:col-span-8 overflow-hidden rounded-sm aspect-[16/9]">
|
||||||
<motion.img
|
<motion.img
|
||||||
@@ -66,7 +69,7 @@ const Editorial: React.FC = () => {
|
|||||||
<div className="max-w-[1400px] mx-auto">
|
<div className="max-w-[1400px] mx-auto">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-16">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-16">
|
||||||
{otherArticles.map((entry, idx) => (
|
{otherArticles.map((entry, idx) => (
|
||||||
<Link key={entry.id} to={`/editorial/${entry.slug}`} className="group block">
|
<Link key={entry.id} to={getArticleHref(entry.slug)} className="group block">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 20, opacity: 0 }}
|
initial={{ y: 20, opacity: 0 }}
|
||||||
whileInView={{ y: 0, opacity: 1 }}
|
whileInView={{ y: 0, opacity: 1 }}
|
||||||
|
|||||||
@@ -1,161 +1,186 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
import SEO, { SITE_URL } from '../components/SEO';
|
||||||
interface FAQItem {
|
|
||||||
question: string;
|
interface FAQItem {
|
||||||
answer: string;
|
question: string;
|
||||||
}
|
answer: string;
|
||||||
|
}
|
||||||
const faqData: FAQItem[] = [
|
|
||||||
{
|
const faqData: FAQItem[] = [
|
||||||
question: "Are your ceramics safe for food, oven, and dishwasher use?",
|
{
|
||||||
answer: "Yes – all our pottery is glazed and food-safe. We use durable, non-toxic glazes that are oven-, microwave-, and dishwasher-safe. To extend their life, we still recommend hand-washing and avoiding harsh scrubbing. Treat your ceramicware gently (like any quality dishware) to keep the colors vibrant and the glaze strong."
|
question: "Are your ceramics safe for food, oven, and dishwasher use?",
|
||||||
},
|
answer: "Yes – all our pottery is glazed and food-safe. We use durable, non-toxic glazes that are oven-, microwave-, and dishwasher-safe. To extend their life, we still recommend hand-washing and avoiding harsh scrubbing. Treat your ceramicware gently (like any quality dishware) to keep the colors vibrant and the glaze strong."
|
||||||
{
|
},
|
||||||
question: "How unique is each piece?",
|
{
|
||||||
answer: "Every piece in our shop is designed and handcrafted in small batches. That means no two pieces are exactly alike. You will see slight variations and unique markings that make each item one-of-a-kind. We do our best to photograph products accurately, but please allow for some artisanal differences. These natural imperfections are a sign of true craftsmanship."
|
question: "How unique is each piece?",
|
||||||
},
|
answer: "Every piece we create is handcrafted in small batches at our Corpus Christi studio. No two pieces are exactly alike — you will see slight variations and unique markings that make each item one-of-a-kind. These natural imperfections are a sign of true craftsmanship and the human hand behind every piece."
|
||||||
{
|
},
|
||||||
question: "How long will it take to receive my order?",
|
{
|
||||||
answer: "Once you place an order, please allow 1–3 business days for us to carefully prepare and package your item (handmade pottery takes a bit of extra time). After that, standard shipping via USPS usually takes 3–5 business days within the U.S. You'll receive a tracking number by email as soon as your order ships."
|
question: "Is the online shop currently open?",
|
||||||
},
|
answer: "Our online shop is temporarily closed while we focus on new collections and studio work. You can still reach us directly for custom commissions or inquiries. Sign up for our newsletter or follow us on Instagram to be the first to know when the shop reopens."
|
||||||
{
|
},
|
||||||
question: "Do you ship outside the USA?",
|
{
|
||||||
answer: "Currently we ship nationwide within the U.S. only. We do not offer international shipping at this time. Orders ship from Corpus Christi, Texas. We focus on providing fast, reliable delivery to Texas and all U.S. customers."
|
question: "Do you take custom orders or commissions?",
|
||||||
},
|
answer: "Yes — we accept a limited number of custom commissions each year. Claudia's work is rooted in the Art Center of Corpus Christi community. Whether you are looking for a bespoke dinnerware set or a one-of-a-kind gift, reach out directly at knuth.claudia@gmail.com to discuss your vision, timeline, and pricing."
|
||||||
{
|
},
|
||||||
question: "What if my order arrives damaged?",
|
{
|
||||||
answer: "We take great care in packaging each piece using recyclable and padded materials. Still, accidents happen. If any item arrives broken or damaged, please contact us immediately. Take a photo of the damage (and packaging, if possible), and email us within 7 days of receipt. All orders are fully insured during transit. Once we review the details, we'll be happy to repair, replace, or refund damaged items at no additional cost to you."
|
question: "Do you offer pottery classes or workshops?",
|
||||||
},
|
answer: "Pottery classes and wheel-throwing workshops are available through the Art Center of Corpus Christi. Visit the Art Center for current class schedules and registration."
|
||||||
{
|
},
|
||||||
question: "What is your return/exchange policy?",
|
{
|
||||||
answer: "We want you to love your purchase. Because each piece is handmade and unique, all sales are final except in cases of damage or defect. If something isn't right with your order, please let us know. We handle returns or exchanges for defective items (typically within 14 days of delivery). You'll just need to return the piece unused, in its original condition and packaging. (Sorry, we cannot accept returns on change-of-mind or carelessly broken items.) See our Returns page for full details."
|
question: "How do I contact you with questions?",
|
||||||
},
|
answer: "We are here to help! You can reach us directly at knuth.claudia@gmail.com or use the contact form on our site. We aim to respond within 1–2 business days. Thank you for your interest in KNUTH Ceramics — we love hearing from you."
|
||||||
{
|
}
|
||||||
question: "How do I contact you with questions?",
|
];
|
||||||
answer: "We're here to help! Feel free to reach out anytime. You can email our customer support at support@knuthceramics.com or use the contact form on our site. We aim to respond within 1–2 business days. Thank you for choosing KNUTH Ceramics – we love answering your questions and hearing your feedback!"
|
|
||||||
}
|
const faqSchema = {
|
||||||
];
|
"@context": "https://schema.org",
|
||||||
|
"@type": "FAQPage",
|
||||||
const FAQ: React.FC = () => {
|
"name": "FAQ | KNUTH Ceramics — Handcrafted Pottery Corpus Christi",
|
||||||
const [openIndex, setOpenIndex] = useState<number | null>(null);
|
"url": `${SITE_URL}/faq`,
|
||||||
|
"breadcrumb": {
|
||||||
const toggleFAQ = (index: number) => {
|
"@type": "BreadcrumbList",
|
||||||
setOpenIndex(openIndex === index ? null : index);
|
"itemListElement": [
|
||||||
};
|
{ "@type": "ListItem", "position": 1, "name": "Home", "item": `${SITE_URL}/` },
|
||||||
|
{ "@type": "ListItem", "position": 2, "name": "FAQ", "item": `${SITE_URL}/faq` }
|
||||||
return (
|
]
|
||||||
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
},
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-12">
|
"mainEntity": faqData.map(item => ({
|
||||||
{/* Header */}
|
"@type": "Question",
|
||||||
<div className="mb-24">
|
"name": item.question,
|
||||||
<motion.span
|
"acceptedAnswer": {
|
||||||
initial={{ opacity: 0 }}
|
"@type": "Answer",
|
||||||
animate={{ opacity: 1 }}
|
"text": item.answer
|
||||||
transition={{ duration: 1 }}
|
}
|
||||||
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
}))
|
||||||
>
|
};
|
||||||
Help Center
|
|
||||||
</motion.span>
|
const FAQ: React.FC = () => {
|
||||||
<motion.h1
|
const [openIndex, setOpenIndex] = useState<number | null>(null);
|
||||||
initial={{ y: 30, opacity: 0 }}
|
|
||||||
animate={{ y: 0, opacity: 1 }}
|
const toggleFAQ = (index: number) => {
|
||||||
transition={{ delay: 0.2, duration: 0.8 }}
|
setOpenIndex(openIndex === index ? null : index);
|
||||||
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
};
|
||||||
>
|
|
||||||
Frequently Asked<br />Questions
|
return (
|
||||||
</motion.h1>
|
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
||||||
<motion.p
|
<SEO
|
||||||
initial={{ y: 30, opacity: 0 }}
|
title="FAQ — Handcrafted Ceramics & Pottery Classes | KNUTH Ceramics"
|
||||||
animate={{ y: 0, opacity: 1 }}
|
description="Answers to common questions about KNUTH Ceramics: food safety, custom commissions, pottery classes in Corpus Christi TX, clay bodies, glazes, and how to order."
|
||||||
transition={{ delay: 0.4, duration: 0.8 }}
|
canonical={`${SITE_URL}/faq`}
|
||||||
className="font-body text-lg font-light text-stone-500 leading-relaxed max-w-2xl"
|
schema={faqSchema}
|
||||||
>
|
/>
|
||||||
Find answers to common questions about our handcrafted ceramics, shipping, care instructions, and more.
|
<div className="max-w-[1200px] mx-auto px-6 md:px-12">
|
||||||
</motion.p>
|
{/* Header */}
|
||||||
</div>
|
<div className="mb-24">
|
||||||
|
<motion.span
|
||||||
{/* FAQ Items */}
|
initial={{ opacity: 0 }}
|
||||||
<div className="space-y-4">
|
animate={{ opacity: 1 }}
|
||||||
{faqData.map((item, index) => (
|
transition={{ duration: 1 }}
|
||||||
<motion.div
|
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
||||||
key={index}
|
>
|
||||||
initial={{ y: 20, opacity: 0 }}
|
Help Center
|
||||||
animate={{ y: 0, opacity: 1 }}
|
</motion.span>
|
||||||
transition={{ delay: index * 0.1, duration: 0.6 }}
|
<motion.h1
|
||||||
className="border border-stone-200 dark:border-stone-800 bg-white dark:bg-black"
|
initial={{ y: 30, opacity: 0 }}
|
||||||
>
|
animate={{ y: 0, opacity: 1 }}
|
||||||
<button
|
transition={{ delay: 0.2, duration: 0.8 }}
|
||||||
onClick={() => toggleFAQ(index)}
|
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
||||||
className="w-full px-8 py-6 flex justify-between items-center hover:bg-stone-50 dark:hover:bg-stone-900 transition-colors"
|
>
|
||||||
>
|
Frequently Asked<br />Questions
|
||||||
<h3 className="font-body text-lg md:text-xl text-left text-text-main dark:text-white pr-8">
|
</motion.h1>
|
||||||
{item.question}
|
<motion.p
|
||||||
</h3>
|
initial={{ y: 30, opacity: 0 }}
|
||||||
<motion.div
|
animate={{ y: 0, opacity: 1 }}
|
||||||
animate={{ rotate: openIndex === index ? 45 : 0 }}
|
transition={{ delay: 0.4, duration: 0.8 }}
|
||||||
transition={{ duration: 0.3 }}
|
className="font-body text-lg font-light text-stone-500 leading-relaxed max-w-2xl"
|
||||||
className="flex-shrink-0"
|
>
|
||||||
>
|
Find answers to common questions about our handcrafted ceramics, shipping, care instructions, and more.
|
||||||
<svg
|
</motion.p>
|
||||||
className="w-6 h-6 text-stone-400"
|
</div>
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
{/* FAQ Items */}
|
||||||
viewBox="0 0 24 24"
|
<div className="space-y-4">
|
||||||
>
|
{faqData.map((item, index) => (
|
||||||
<path
|
<motion.div
|
||||||
strokeLinecap="round"
|
key={index}
|
||||||
strokeLinejoin="round"
|
initial={{ y: 20, opacity: 0 }}
|
||||||
strokeWidth={1.5}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
d="M12 4v16m8-8H4"
|
transition={{ delay: index * 0.1, duration: 0.6 }}
|
||||||
/>
|
className="border border-stone-200 dark:border-stone-800 bg-white dark:bg-black"
|
||||||
</svg>
|
>
|
||||||
</motion.div>
|
<button
|
||||||
</button>
|
onClick={() => toggleFAQ(index)}
|
||||||
|
className="w-full px-8 py-6 flex justify-between items-center hover:bg-stone-50 dark:hover:bg-stone-900 transition-colors"
|
||||||
<AnimatePresence>
|
>
|
||||||
{openIndex === index && (
|
<h3 className="font-body text-lg md:text-xl text-left text-text-main dark:text-white pr-8">
|
||||||
<motion.div
|
{item.question}
|
||||||
initial={{ height: 0, opacity: 0 }}
|
</h3>
|
||||||
animate={{ height: "auto", opacity: 1 }}
|
<motion.div
|
||||||
exit={{ height: 0, opacity: 0 }}
|
animate={{ rotate: openIndex === index ? 45 : 0 }}
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
className="overflow-hidden"
|
className="flex-shrink-0"
|
||||||
>
|
>
|
||||||
<div className="px-8 pb-6 border-t border-stone-100 dark:border-stone-800 pt-6">
|
<svg
|
||||||
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
className="w-6 h-6 text-stone-400"
|
||||||
{item.answer}
|
fill="none"
|
||||||
</p>
|
stroke="currentColor"
|
||||||
</div>
|
viewBox="0 0 24 24"
|
||||||
</motion.div>
|
>
|
||||||
)}
|
<path
|
||||||
</AnimatePresence>
|
strokeLinecap="round"
|
||||||
</motion.div>
|
strokeLinejoin="round"
|
||||||
))}
|
strokeWidth={1.5}
|
||||||
</div>
|
d="M12 4v16m8-8H4"
|
||||||
|
/>
|
||||||
{/* Contact CTA */}
|
</svg>
|
||||||
<motion.div
|
</motion.div>
|
||||||
initial={{ y: 30, opacity: 0 }}
|
</button>
|
||||||
animate={{ y: 0, opacity: 1 }}
|
|
||||||
transition={{ delay: 0.8, duration: 0.8 }}
|
<AnimatePresence>
|
||||||
className="mt-24 p-12 bg-primary dark:bg-black text-white text-center border border-stone-800"
|
{openIndex === index && (
|
||||||
>
|
<motion.div
|
||||||
<h2 className="font-display text-3xl md:text-4xl mb-4">Still have questions?</h2>
|
initial={{ height: 0, opacity: 0 }}
|
||||||
<p className="font-body font-light text-stone-400 mb-8 max-w-xl mx-auto">
|
animate={{ height: "auto", opacity: 1 }}
|
||||||
We're here to help. Reach out to our customer support team.
|
exit={{ height: 0, opacity: 0 }}
|
||||||
</p>
|
transition={{ duration: 0.3 }}
|
||||||
<Link
|
className="overflow-hidden"
|
||||||
to="/contact"
|
>
|
||||||
className="inline-block bg-white text-black px-10 py-4 text-xs font-bold uppercase tracking-widest hover:bg-stone-200 transition-colors"
|
<div className="px-8 pb-6 border-t border-stone-100 dark:border-stone-800 pt-6">
|
||||||
>
|
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
||||||
Contact Us
|
{item.answer}
|
||||||
</Link>
|
</p>
|
||||||
</motion.div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
)}
|
||||||
);
|
</AnimatePresence>
|
||||||
};
|
</motion.div>
|
||||||
|
))}
|
||||||
export default FAQ;
|
</div>
|
||||||
|
|
||||||
|
{/* Contact CTA */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ y: 30, opacity: 0 }}
|
||||||
|
animate={{ y: 0, opacity: 1 }}
|
||||||
|
transition={{ delay: 0.8, duration: 0.8 }}
|
||||||
|
className="mt-24 p-12 bg-primary dark:bg-black text-white text-center border border-stone-800"
|
||||||
|
>
|
||||||
|
<h2 className="font-display text-3xl md:text-4xl mb-4">Still have questions?</h2>
|
||||||
|
<p className="font-body font-light text-stone-400 mb-8 max-w-xl mx-auto">
|
||||||
|
We're here to help. Reach out to our customer support team.
|
||||||
|
</p>
|
||||||
|
<Link
|
||||||
|
to="/contact"
|
||||||
|
className="inline-block bg-white text-black px-10 py-4 text-xs font-bold uppercase tracking-widest hover:bg-stone-200 transition-colors"
|
||||||
|
>
|
||||||
|
Contact Us
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FAQ;
|
||||||
|
|||||||
@@ -5,18 +5,93 @@ import HorizontalScrollSection from '../components/HorizontalScrollSection';
|
|||||||
import Collections from '../components/Collections';
|
import Collections from '../components/Collections';
|
||||||
import QuoteSection from '../components/QuoteSection';
|
import QuoteSection from '../components/QuoteSection';
|
||||||
import JournalSection from '../components/JournalSection';
|
import JournalSection from '../components/JournalSection';
|
||||||
import GallerySection from '../components/GallerySection';
|
import InstagramFeed from '../components/InstagramFeed';
|
||||||
|
|
||||||
import FAQ from '../components/FAQ';
|
import FAQ from '../components/FAQ';
|
||||||
|
import SEO, { SITE_URL } from '../components/SEO';
|
||||||
|
|
||||||
|
const homeSchema = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebPage",
|
||||||
|
"name": "KNUTH Ceramics — Handcrafted Pottery from Corpus Christi, Texas",
|
||||||
|
"description": "KNUTH Ceramics crafts small-batch stoneware pottery in Corpus Christi, Texas. Shop handmade vases, bowls, tableware, and dinnerware inspired by the Gulf Coast. Custom commissions welcome.",
|
||||||
|
"url": `${SITE_URL}/`,
|
||||||
|
"breadcrumb": {
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
"itemListElement": [
|
||||||
|
{ "@type": "ListItem", "position": 1, "name": "Home", "item": `${SITE_URL}/` }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const faqSchema = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "FAQPage",
|
||||||
|
"mainEntity": [
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
"name": "Is the online shop currently open?",
|
||||||
|
"acceptedAnswer": {
|
||||||
|
"@type": "Answer",
|
||||||
|
"text": "Our online shop is temporarily closed while we focus on new collections and studio work. Follow us on Instagram @knuth.ceramics or email knuth.claudia@gmail.com for commissions and updates."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
"name": "Are your ceramics dishwasher and microwave safe?",
|
||||||
|
"acceptedAnswer": {
|
||||||
|
"@type": "Answer",
|
||||||
|
"text": "Yes. Our functional stoneware — including mugs, plates, and bowls — is fired to cone 6 oxidation, making it durable for daily use. Hand washing is recommended to extend the life of your piece."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
"name": "Where is KNUTH Ceramics located?",
|
||||||
|
"acceptedAnswer": {
|
||||||
|
"@type": "Answer",
|
||||||
|
"text": "Our studio is rooted in Corpus Christi, Texas, inspired by the colors and textures of the Gulf Coast."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
"name": "Do you offer pottery classes in Corpus Christi?",
|
||||||
|
"acceptedAnswer": {
|
||||||
|
"@type": "Answer",
|
||||||
|
"text": "Pottery classes and wheel-throwing workshops are available through the Art Center of Corpus Christi. Visit the Art Center for current schedules and registration."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
"name": "Do you accept custom ceramic commissions?",
|
||||||
|
"acceptedAnswer": {
|
||||||
|
"@type": "Answer",
|
||||||
|
"text": "Yes. We accept a limited number of custom dinnerware commissions each year. Contact Claudia at knuth.claudia@gmail.com to discuss your vision, timeline, and pricing."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
"name": "What clay and glazes does KNUTH Ceramics use?",
|
||||||
|
"acceptedAnswer": {
|
||||||
|
"@type": "Answer",
|
||||||
|
"text": "We use a stoneware clay body that reflects the sandy textures of the Texas Gulf Coast. Our glazes are formulated in-house to capture the colors of the sea and sand along the Corpus Christi shoreline."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
const Home: React.FC = () => {
|
const Home: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
|
<SEO
|
||||||
|
title="Handcrafted Pottery from Corpus Christi, Texas | KNUTH Ceramics"
|
||||||
|
description="KNUTH Ceramics creates small-batch stoneware pottery in Corpus Christi, TX. Shop handmade vases, bowls, and tableware inspired by the Texas Gulf Coast. Custom commissions welcome."
|
||||||
|
canonical={`${SITE_URL}/`}
|
||||||
|
schema={[homeSchema, faqSchema]}
|
||||||
|
/>
|
||||||
<Hero />
|
<Hero />
|
||||||
<FeatureSection />
|
<FeatureSection />
|
||||||
<HorizontalScrollSection />
|
<HorizontalScrollSection />
|
||||||
<Collections />
|
<Collections />
|
||||||
<GallerySection />
|
<InstagramFeed />
|
||||||
<JournalSection />
|
<JournalSection />
|
||||||
<QuoteSection />
|
<QuoteSection />
|
||||||
<FAQ />
|
<FAQ />
|
||||||
|
|||||||
@@ -1,96 +1,143 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import BlogPostLayout from '../../components/BlogPostLayout';
|
import BlogPostLayout from '../../components/BlogPostLayout';
|
||||||
|
import SEO, { SITE_URL } from '../../components/SEO';
|
||||||
const MotivationInClay: React.FC = () => {
|
|
||||||
React.useEffect(() => {
|
const HERO_IMAGE = 'https://lh3.googleusercontent.com/aida-public/AB6AXuB8NOE5fGfN4d87cbcB27_Sh-nrlZlqxsTlYKbCZk98SoL-gHsPSWFNuxd1DxBq0g8Qysh0RBZ_btu-_WaH68UjV8SXPUalyxREvUqao4oXmra--pWAsaooWwKvWCzReYZ8kj7G-KIYIAo5mqudzB8n9C6-HVTNPPx9QgZHr_YsojMxlmmVcQ5bqk7-Lp0KtSAiVIPD2-1UE1dMGnkVSLUXKdgA65JIh8M3TtNkaJTGONuFKoTERrYOWe7u2BILnqyukTzlNcvK7Sc';
|
||||||
document.title = "Creative Block for Potters: 10 Tips for Motivation | KNUTH Ceramics";
|
const SOURCES = [
|
||||||
let meta = document.querySelector('meta[name="description"]');
|
{
|
||||||
if (!meta) {
|
label: 'IKKAI Ceramics - Pottery Motivation: 10 Tips for When You Are Feeling Stuck',
|
||||||
meta = document.createElement('meta');
|
href: 'https://ikkaiceramics.nl/blogs/welcome-to-my-journal/10-gentle-tips-for-pottery-motivation-when-youre-feeling-stuck',
|
||||||
meta.setAttribute('name', 'description');
|
},
|
||||||
document.head.appendChild(meta);
|
{
|
||||||
}
|
label: 'Ceramic Arts Network - Ceramics Monthly',
|
||||||
meta.setAttribute('content', 'Overcoming Creative Block for Potters is possible. Use these 10 gentle, practical tips to rediscover your motivation and love for clay. Read more now.');
|
href: 'https://ceramicartsnetwork.org/ceramics-monthly/',
|
||||||
}, []);
|
},
|
||||||
|
];
|
||||||
return (
|
|
||||||
<BlogPostLayout
|
const articleSchema = {
|
||||||
title="Creative Block for Potters: 10 Tips for Motivation"
|
'@context': 'https://schema.org',
|
||||||
category="Wellness"
|
'@type': 'Article',
|
||||||
date="Jun 11"
|
headline: 'Finding Motivation in Clay',
|
||||||
image="https://lh3.googleusercontent.com/aida-public/AB6AXuB8NOE5fGfN4d87cbcB27_Sh-nrlZlqxsTlYKbCZk98SoL-gHsPSWFNuxd1DxBq0g8Qysh0RBZ_btu-_WaH68UjV8SXPUalyxREvUqao4oXmra--pWAsaooWwKvWCzReYZ8kj7G-KIYIAo5mqudzB8n9C6-HVTNPPx9QgZHr_YsojMxlmmVcQ5bqk7-Lp0KtSAiVIPD2-1UE1dMGnkVSLUXKdgA65JIh8M3TtNkaJTGONuFKoTERrYOWe7u2BILnqyukTzlNcvK7Sc"
|
description: 'Finding motivation in clay during slow seasons and creative blocks. Ten gentle, practical ideas for potters who need a steadier way back into the studio.',
|
||||||
imageAlt="Creative Block for Potters tips"
|
author: { '@type': 'Person', name: 'Claudia Knuth' },
|
||||||
>
|
publisher: { '@type': 'Organization', name: 'KNUTH Ceramics', url: SITE_URL },
|
||||||
<p className="lead text-xl text-stone-600 dark:text-stone-300 italic mb-8">
|
datePublished: '2024-06-11',
|
||||||
Dealing with <strong>Creative Block for Potters</strong> (and finding new <strong>Pottery Inspiration</strong>) is a common struggle in the studio. Where the physical labor is intense and the failure rate is high, burnout is real. Whether you are facing general exhaustion or a specific artistic wall, know that this season is part of the cycle.
|
url: `${SITE_URL}/editorial/finding-motivation-in-clay`,
|
||||||
</p>
|
image: HERO_IMAGE,
|
||||||
|
mainEntityOfPage: `${SITE_URL}/editorial/finding-motivation-in-clay`,
|
||||||
<p className="mb-6">
|
keywords: 'finding motivation in clay, pottery motivation, creative block potters, ceramic artist inspiration, potter burnout',
|
||||||
Here is how to overcome <strong>Creative Block for Potters</strong> and find your flow again.
|
};
|
||||||
</p>
|
|
||||||
|
const MotivationInClay: React.FC = () => {
|
||||||
<img
|
return (
|
||||||
src="https://images.unsplash.com/photo-1459156212016-c812468e2115?q=80&w=2574&auto=format&fit=crop"
|
<>
|
||||||
alt="Creative Block for Potters guide"
|
<SEO
|
||||||
className="w-full my-12 shadow-lg"
|
title="Finding Motivation in Clay"
|
||||||
/>
|
description="Finding motivation in clay during slow seasons and creative blocks. Ten gentle, practical ideas for potters who need a steadier way back into the studio."
|
||||||
|
canonical={`${SITE_URL}/editorial/finding-motivation-in-clay`}
|
||||||
<h2 className="mt-16 mb-8 text-3xl">1. Play without Purpose</h2>
|
schema={articleSchema}
|
||||||
<p className="mb-6">
|
ogType="article"
|
||||||
Stop making <Link to="/collections">Collections</Link>. Stop thinking about what will sell. Grab a lump of clay and just <em>pinch</em>. When you remove the pressure, you often solve the <strong>Creative Block for Potters</strong> naturally.
|
ogImage={HERO_IMAGE}
|
||||||
</p>
|
/>
|
||||||
|
<BlogPostLayout
|
||||||
<h2 className="mt-16 mb-8 text-3xl">2. Switch Your Technique</h2>
|
title="Finding Motivation in Clay"
|
||||||
<p className="mb-6">
|
category="Wellness"
|
||||||
If you are a wheel thrower, try <strong>hand building</strong>. Changing your physical movements can unlock new neural pathways.
|
date="Jun 11"
|
||||||
</p>
|
image={HERO_IMAGE}
|
||||||
|
imageAlt="Finding motivation in clay - a potter's hands resting beside an unfinished ceramic piece"
|
||||||
<h2 className="mt-16 mb-8 text-3xl">3. The "100 Pattern" Challenge</h2>
|
>
|
||||||
<p className="mb-6">
|
<p className="lead text-xl text-stone-600 dark:text-stone-300 italic mb-8">
|
||||||
Commit to making 100 small test tiles. Constraints actually breed creativity.
|
<strong>Finding motivation in clay</strong> is part of the work. Every potter knows the feeling of walking into the studio, seeing the wheel ready, and still thinking: maybe tomorrow.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 className="mt-16 mb-8 text-3xl">4. Clean Your Studio (Reset)</h2>
|
<p className="mb-6">
|
||||||
<p className="mb-6">
|
Sometimes the block is exhaustion. Sometimes it is perfectionism. Sometimes it is the quieter anxiety of not knowing what to make next. The tips below are not a cure-all. They are gentler than that. They are ways back in.
|
||||||
A cluttered space leads to a cluttered mind. Spend a day organizing your <Link to="/atelier">Atelier</Link>. A fresh, clean bat on the wheel is an invitation.
|
</p>
|
||||||
</p>
|
|
||||||
|
<img
|
||||||
<h2 className="mt-16 mb-8 text-3xl">5. Look Outside of Pottery</h2>
|
src="/product_images/motivation_pottery_mid_v4.png"
|
||||||
<p className="mb-6">
|
alt="Finding motivation in clay - hands working with wet clay at a pottery wheel"
|
||||||
Don't look at other potters on Instagram. That leads to comparison. instead, look at:
|
className="w-full my-12 shadow-lg rounded-sm"
|
||||||
</p>
|
/>
|
||||||
<ul className="mb-12 space-y-4">
|
<p className="text-sm text-center text-stone-500 -mt-8 mb-12 italic">
|
||||||
<li><strong>Architecture</strong>: for structural shapes.</li>
|
Sometimes the most useful goal is simply getting your hands back into clay.
|
||||||
<li><strong>Nature</strong>: for textures (tree bark, river stones).</li>
|
</p>
|
||||||
</ul>
|
|
||||||
|
<h2 className="mt-16 mb-4 text-3xl">1. Make something just because</h2>
|
||||||
<h2 className="mt-16 mb-8 text-3xl">6. Take a Class</h2>
|
<p className="mb-8">
|
||||||
<p className="mb-6">
|
Stop trying to make the saleable version of yourself for an hour. Make a crooked cup. Make something too small. Make something experimental. The pressure to be good can freeze the hands; curiosity usually unfreezes them.
|
||||||
Even masters are students. Taking a workshop puts you back in the "beginner's mind," which is a fertile place for ideas.
|
</p>
|
||||||
</p>
|
|
||||||
|
<h2 className="mt-16 mb-4 text-3xl">2. Choose quantity over perfection</h2>
|
||||||
<h2 className="mt-16 mb-8 text-3xl">7. Revisit Your "Why"</h2>
|
<p className="mb-8">
|
||||||
<p className="mb-6">
|
Potters learn by volume. Ten quick cylinders can teach more than one overworked perfect mug. On difficult days, make the goal output rather than excellence. The body often remembers how to keep going before the mind does.
|
||||||
Look at the very first pot you ever kept. Reconnecting with your origin story can fuel your current practice.
|
</p>
|
||||||
</p>
|
|
||||||
|
<h2 className="mt-16 mb-4 text-3xl">3. Separate practice from production</h2>
|
||||||
<h2 className="mt-16 mb-8 text-3xl">8. Limit Your Time</h2>
|
<p className="mb-8">
|
||||||
<p className="mb-6">
|
Not every studio session needs to produce something worth selling. Protect a few sessions as research days. Use them to test forms, glaze ideas, trimming decisions, or scale. Failure feels different when failure is part of the brief.
|
||||||
Tell yourself, "I will only work for 20 minutes." Often, the hardest part is just starting.
|
</p>
|
||||||
</p>
|
|
||||||
|
<h2 className="mt-16 mb-4 text-3xl">4. Notice your studio self-talk</h2>
|
||||||
<h2 className="mt-16 mb-8 text-3xl">9. Embrace functionality</h2>
|
<p className="mb-8">
|
||||||
<p className="mb-6">
|
Replace "I am terrible at this" with "this part is still new for me." Replace "that failed" with "that taught me something." Motivation rarely grows in a studio ruled by contempt.
|
||||||
Make something you <em>need</em>. A spoon rest. A soap dish. Solving a simple, functional problem is a great way to handle <strong>Creative Block for Potters</strong>.
|
</p>
|
||||||
</p>
|
|
||||||
|
<h2 className="mt-16 mb-4 text-3xl">5. Tend to the space before the work</h2>
|
||||||
<h2 className="mt-16 mb-8 text-3xl">10. Rest</h2>
|
<p className="mb-8">
|
||||||
<p className="mb-6">
|
Wipe the table. Sweep the floor. Put on music. Bring something in from outside. The studio does not need to be perfect, but it helps when it feels intentional. We pay attention to that on the <Link to="/atelier">atelier page</Link> because atmosphere changes how the body arrives to work.
|
||||||
Sometimes, the block isn't mental; it's physical. Take a week off. The clay will be there when you get back.
|
</p>
|
||||||
</p>
|
|
||||||
</BlogPostLayout>
|
<h2 className="mt-16 mb-4 text-3xl">6. Let clay be grounding, not only productive</h2>
|
||||||
);
|
<p className="mb-8">
|
||||||
};
|
Clay asks for presence. Your hands are wet. Your phone is useless. Your attention has to come back to pressure, speed, breath, and touch. On low-motivation days, that may be enough. The point does not have to be output. The point can be contact.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-4 text-3xl">7. Go outward before you force ideas</h2>
|
||||||
|
<p className="mb-8">
|
||||||
|
Visit a gallery. Walk near water. Look at bark, shells, stone, weathered wood, old handles, old bowls. Texture and form have to go in before they can come back out through your hands. Along the coast here, those cues are everywhere, and they show up in our <Link to="/collections">glazes and forms</Link> whether we mean them to or not.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-4 text-3xl">8. Let community carry some of the weight</h2>
|
||||||
|
<p className="mb-8">
|
||||||
|
Pottery can be solitary, but it does not have to be isolating. A class, guild, open studio, or workshop can reset your energy quickly. Being around other people making things changes the room inside your head.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-4 text-3xl">9. Protect creative time from business time</h2>
|
||||||
|
<p className="mb-8">
|
||||||
|
Pricing, shipping, social media, and orders can quietly consume the same mental space that used to belong to making. Sometimes motivation returns not because you found it, but because you protected it structurally. Put business tasks somewhere else in the week.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-4 text-3xl">10. Set one small goal and let it count</h2>
|
||||||
|
<p className="mb-8">
|
||||||
|
Pull one taller wall. Trim one foot cleanly. Test one glaze combination. Small goals create traction because they can actually be finished. And when you finish them, name that. Momentum grows faster when it is noticed.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-6 text-3xl">A note on the clay itself</h2>
|
||||||
|
<p className="mb-6">
|
||||||
|
What runs through all of this is simpler than any technique. Working with clay is one of the most human forms of grounding we have. The hand pressing into soft material and receiving a response is not incidental to the art. It is the art.
|
||||||
|
</p>
|
||||||
|
<p className="mb-12">
|
||||||
|
When motivation feels far away, come back to the smallest version of the practice. Wedge. Pinch. Press your thumb into a ball of clay. Start there.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="mt-20 pt-12 border-t border-stone-200 dark:border-stone-800">
|
||||||
|
<h3 className="font-display text-xl mb-6 text-stone-500 dark:text-stone-400 uppercase tracking-widest text-sm">Sources & Further Reading</h3>
|
||||||
|
<ul className="space-y-2 text-sm text-stone-500 dark:text-stone-400 font-light">
|
||||||
|
{SOURCES.map((source) => (
|
||||||
|
<li key={source.href}>
|
||||||
|
<a href={source.href} target="_blank" rel="noreferrer" className="underline decoration-stone-300 underline-offset-4 hover:text-stone-700 dark:hover:text-stone-200">
|
||||||
|
{source.label}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
<li>Bayles & Orland - <em>Art and Fear</em></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</BlogPostLayout>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default MotivationInClay;
|
export default MotivationInClay;
|
||||||
|
|||||||
@@ -1,90 +1,158 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import BlogPostLayout from '../../components/BlogPostLayout';
|
import BlogPostLayout from '../../components/BlogPostLayout';
|
||||||
|
import SEO, { SITE_URL } from '../../components/SEO';
|
||||||
const PackagingGuide: React.FC = () => {
|
|
||||||
React.useEffect(() => {
|
const HERO_IMAGE = 'https://lh3.googleusercontent.com/aida-public/AB6AXuAaWGnX_NYT3S_lOflL2NJZGbWge4AAkvra4ymvF8ag-c1UKsOAIB-rsLVQXW5xIlPZipDiK8-ysPyv22xdgsvzs4EOXSSCcrT4Lb2YCe0u5orxRaZEA5TgxeoKq15zaWKSlmnHyPGjPd_7yglpfO13eZmbU5KaxFJ1KGO0UAxoO9BpsyCYgbgINMoSz3epGe5ZdwBWRH-5KCzjoLuXimFTLcd5bqg9T1YofTxgy2hWBMJzKkafyEniq8dP6hMmfNCLVcCHHHx0hRU';
|
||||||
document.title = "How to Package Pottery for Shipping: A Safe Guide | KNUTH Ceramics";
|
|
||||||
let meta = document.querySelector('meta[name="description"]');
|
const SOURCES = [
|
||||||
if (!meta) {
|
{
|
||||||
meta = document.createElement('meta');
|
label: 'The Pottery Wheel - Is Pottery Dishwasher Safe? Washing Handmade Ceramics',
|
||||||
meta.setAttribute('name', 'description');
|
href: 'https://thepotterywheel.com/is-pottery-dishwasher-safe/',
|
||||||
document.head.appendChild(meta);
|
},
|
||||||
}
|
{
|
||||||
meta.setAttribute('content', 'Learn how to package pottery for shipping safely. Use our double-box method and sustainable tips to ensure your handmade ceramics arrive intact. Read now.');
|
label: 'Mayco Colors - Dinnerware and Food Safety',
|
||||||
}, []);
|
href: 'https://www.maycocolors.com/resources/dinnerware-food-safety/',
|
||||||
|
},
|
||||||
return (
|
{
|
||||||
<BlogPostLayout
|
label: 'Mayco Colors - Stoneware Bisque',
|
||||||
title="How to Package Pottery for Shipping"
|
href: 'https://www.maycocolors.com/forms/stoneware-bisque',
|
||||||
category="Guide"
|
},
|
||||||
date="Jul 15"
|
{
|
||||||
image="https://lh3.googleusercontent.com/aida-public/AB6AXuAaWGnX_NYT3S_lOflL2NJZGbWge4AAkvra4ymvF8ag-c1UKsOAIB-rsLVQXW5xIlPZipDiK8-ysPyv22xdgsvzs4EOXSSCcrT4Lb2YCe0u5orxRaZEA5TgxeoKq15zaWKSlmnHyPGjPd_7yglpfO13eZmbU5KaxFJ1KGO0UAxoO9BpsyCYgbgINMoSz3epGe5ZdwBWRH-5KCzjoLuXimFTLcd5bqg9T1YofTxgy2hWBMJzKkafyEniq8dP6hMmfNCLVcCHHHx0hRU"
|
label: 'FDA - Questions and Answers on Lead-Glazed Traditional Pottery',
|
||||||
imageAlt="How to Package Pottery for Shipping Safely guide"
|
href: 'https://www.fda.gov/food/environmental-contaminants-food/questions-and-answers-lead-glazed-traditional-pottery',
|
||||||
>
|
},
|
||||||
<p className="lead text-xl text-stone-600 dark:text-stone-300 italic mb-8">
|
];
|
||||||
<strong>How to Package Pottery for Shipping</strong> safely is the most important skill for a small business owner. There is nothing more heartbreaking than a shattered creation, so mastering this art form ensures your hard work survives the journey.
|
|
||||||
</p>
|
const articleSchema = {
|
||||||
|
'@context': 'https://schema.org',
|
||||||
<p className="mb-6">
|
'@type': 'Article',
|
||||||
Here is your comprehensive guide on shipping handmade art so it arrives safely every single time.
|
headline: 'How to Care for Handmade Ceramics',
|
||||||
</p>
|
description: 'How to care for handmade ceramics: a practical daily care guide for mugs, bowls, and plates, including dishwasher, microwave, crazing, and cleaning tips.',
|
||||||
|
author: { '@type': 'Person', name: 'Claudia Knuth' },
|
||||||
|
publisher: { '@type': 'Organization', name: 'KNUTH Ceramics', url: SITE_URL },
|
||||||
|
datePublished: '2024-07-15',
|
||||||
<h2 className="mt-16 mb-8 text-3xl">1. The Double-Box Method (The Golden Rule)</h2>
|
url: `${SITE_URL}/editorial/how-to-care-for-handmade-ceramics`,
|
||||||
<p className="mb-6">
|
image: HERO_IMAGE,
|
||||||
When considering safe delivery—especially for large items—the <strong>double-box method</strong> is the industry standard.
|
mainEntityOfPage: `${SITE_URL}/editorial/how-to-care-for-handmade-ceramics`,
|
||||||
</p>
|
keywords: 'how to care for handmade ceramics, pottery care, handmade pottery care, ceramic care guide',
|
||||||
<ul className="mb-12 space-y-4">
|
};
|
||||||
<li><strong>Inner Box</strong>: Wrap your <Link to="/collections">Collections</Link> piece and place it in a small box. It should fit snugly.</li>
|
|
||||||
<li><strong>Outer Box</strong>: Place the small box inside a larger shipping box, with at least 2 inches of padding on all sides.</li>
|
const PackagingGuide: React.FC = () => {
|
||||||
<li><em>Why?</em> The outer box absorbs the shock, keeping your art safe.</li>
|
return (
|
||||||
</ul>
|
<>
|
||||||
|
<SEO
|
||||||
<h2 className="mt-16 mb-8 text-3xl">2. Wrapping Materials: Layers Matter</h2>
|
title="How to Care for Handmade Ceramics | Daily Care Guide"
|
||||||
<p className="mb-6">
|
description="How to care for handmade ceramics: a practical daily care guide for mugs, bowls, and plates, including dishwasher, microwave, crazing, and cleaning tips."
|
||||||
Don't rely on just one material when you plan your packing strategy.
|
canonical={`${SITE_URL}/editorial/how-to-care-for-handmade-ceramics`}
|
||||||
</p>
|
schema={articleSchema}
|
||||||
<ul className="mb-12 space-y-4">
|
ogType="article"
|
||||||
<li><strong>Layer 1: Tissue Paper</strong>: Protects the glaze.</li>
|
ogImage={HERO_IMAGE}
|
||||||
<li><strong>Layer 2: Bubble Wrap</strong>: The workhorse. Wrap the piece <em>tightly</em> in small-bubble wrap.</li>
|
/>
|
||||||
<li><strong>The Shake Test</strong>: Shake the box hard. If you hear movement, add tougher filler.</li>
|
<BlogPostLayout
|
||||||
</ul>
|
title="How to Care for Handmade Ceramics"
|
||||||
|
category="Guide"
|
||||||
<div className="my-16">
|
date="Jul 15"
|
||||||
<img
|
image={HERO_IMAGE}
|
||||||
src="/assets/images/packaging_guide.png"
|
imageAlt="how to care for handmade ceramics - handmade pottery drying after washing"
|
||||||
alt="Sustainable pottery packaging materials including honeycomb paper and packing peanuts"
|
>
|
||||||
className="w-full shadow-lg rounded-sm"
|
<p className="lead text-xl text-stone-600 dark:text-stone-300 italic mb-8">
|
||||||
/>
|
<strong>How to care for handmade ceramics</strong> is one of the most important questions to answer when pottery becomes part of daily life. Handmade mugs, bowls, and plates are meant to be used and loved often, but they do last better when they are treated with a little attention.
|
||||||
<p className="text-sm text-center text-stone-500 mt-4 italic">Eco-friendly packaging materials ready for use.</p>
|
</p>
|
||||||
</div>
|
|
||||||
|
<p className="mb-6">
|
||||||
<h2 className="mt-16 mb-8 text-3xl">3. Sustainable Packaging Alternatives</h2>
|
There is something different about living with handmade work. A mug becomes part of a morning ritual. A bowl begins to hold fruit, soup, or sea salt on the kitchen counter. A plate starts to feel less like an object and more like part of the rhythm of the home. That is exactly why learning how to care for handmade ceramics matters.
|
||||||
<p className="mb-6">
|
</p>
|
||||||
Many customers value sustainability in our <Link to="/atelier">Atelier</Link>.
|
|
||||||
</p>
|
<p className="mb-6">
|
||||||
<ul>
|
Good pottery does not need fussy care. It needs thoughtful care. If you understand a few basics about cleaning, temperature changes, glaze surfaces, and daily use, your favorite pieces can stay beautiful and functional for years.
|
||||||
<li><strong>Honeycomb Paper</strong>: A biodegradable alternative.</li>
|
</p>
|
||||||
<li><strong>Corn Starch Peanuts</strong>: Dissolve in water.</li>
|
|
||||||
<li><strong>Cardboard Scraps</strong>: Excellent dense filler.</li>
|
<h2 className="mt-16 mb-6 text-3xl">Start with the maker's care instructions</h2>
|
||||||
</ul>
|
<p className="mb-6">
|
||||||
|
The first rule in how to care for handmade ceramics is simple: trust the maker first. Whether a piece is dishwasher-safe, microwave-safe, or oven-safe depends on the clay body, the glaze fit, and the firing temperature. Properly fired stoneware and porcelain are often more durable than earthenware, but not every handmade ceramic piece should be treated the same way.
|
||||||
<h2 className="mt-16 mb-8 text-3xl">4. Branding Your Unboxing Experience</h2>
|
</p>
|
||||||
<ul className="mb-12 space-y-4">
|
<p className="mb-6">
|
||||||
<li><strong>The "Thank You" Note</strong>: Builds a connection.</li>
|
If the potter recommends hand washing, hand wash it. If the potter says a piece is decorative only, keep it out of food use. That guidance matters more than assumptions.
|
||||||
<li><strong>Care Instructions</strong>: Explain microwave/dishwasher safety.</li>
|
</p>
|
||||||
<li><strong>Stickers</strong>: Build anticipation.</li>
|
|
||||||
</ul>
|
<h2 className="mt-16 mb-6 text-3xl">Dishwasher-safe does not always mean best in the dishwasher</h2>
|
||||||
|
<p className="mb-6">
|
||||||
<h2 className="mt-16 mb-8 text-3xl">5. Insurance and labeling</h2>
|
A big part of how to care for handmade ceramics is understanding the difference between safe and ideal. Some handmade pottery can go in the dishwasher, especially vitrified stoneware or porcelain, but repeated dishwasher cycles can still dull glossy glazes, discolor unglazed foot rings, and expose pieces to small knocks from other dishes.
|
||||||
<ul className="mb-12 space-y-4">
|
</p>
|
||||||
<li><strong>Fragile Stickers</strong>: Helpful, but not a guarantee.</li>
|
<p className="mb-6">
|
||||||
<li><strong>Shipping Insurance</strong>: Always pay the extra few dollars for peace of mind.</li>
|
For everyday care, hand washing is often the gentler choice. Warm water, mild soap, and a soft sponge are usually enough. If you want a favorite mug or bowl to age well, hand washing is usually the safer long-term habit.
|
||||||
</ul>
|
</p>
|
||||||
</BlogPostLayout>
|
|
||||||
);
|
<img
|
||||||
};
|
src="/product_images/care_guide_mid_v2.png"
|
||||||
|
alt="how to care for handmade ceramics - handmade pottery drying after washing"
|
||||||
|
className="w-full my-12 shadow-lg rounded-sm"
|
||||||
|
/>
|
||||||
|
<p className="text-sm text-center text-stone-500 -mt-8 mb-12 italic">
|
||||||
|
Handmade ceramics last best when daily care is gentle, consistent, and suited to the way they were made.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-6 text-3xl">Watch for crazing, cracks, and chips</h2>
|
||||||
|
<p className="mb-6">
|
||||||
|
Another important part of how to care for handmade ceramics is paying attention when a piece changes. Fine crack lines in the glaze, called crazing, may affect how easy a surface is to clean. Chips on rims or cracks that catch your fingernail are signs that a functional piece may no longer be ideal for food use.
|
||||||
|
</p>
|
||||||
|
<p className="mb-6">
|
||||||
|
That does not mean the piece has to be discarded. A cracked mug can become a pencil cup. A crazed bowl can hold keys, shells, or dried flowers. But damaged foodware is often better retired from the table.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-6 text-3xl">Be careful with sudden temperature changes</h2>
|
||||||
|
<p className="mb-6">
|
||||||
|
If you are learning how to care for handmade ceramics, thermal shock is worth remembering. Ceramics do not like abrupt changes in temperature. A cold bowl moved straight into a hot oven, or a hot mug rinsed immediately under cold water, can crack from stress.
|
||||||
|
</p>
|
||||||
|
<p className="mb-6">
|
||||||
|
It is always safer to warm pottery gradually and let it cool naturally. Even if a piece is microwave- or oven-friendly, gentle handling helps it last longer.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-6 text-3xl">Know what should and should not touch food</h2>
|
||||||
|
<p className="mb-6">
|
||||||
|
Food safety is part of how to care for handmade ceramics too. Handmade pottery from a reliable studio is very different from older decorative ware or uncertain imports. FDA guidance continues to warn that some traditional pottery may contain unsafe levels of lead, especially when the source and intended use are unclear.
|
||||||
|
</p>
|
||||||
|
<p className="mb-6">
|
||||||
|
For that reason, it is always best to buy functional pottery from makers who understand food-safe surfaces and can clearly tell you how the piece is intended to be used.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-6 text-3xl">A few everyday habits help a lot</h2>
|
||||||
|
<ul className="mb-8 space-y-4 list-none pl-0">
|
||||||
|
<li><strong>Store pieces fully dry.</strong> Moisture trapped in stacked pottery can lead to marks and odor.</li>
|
||||||
|
<li><strong>Avoid metal scouring pads.</strong> Soft sponges are safer for glaze surfaces.</li>
|
||||||
|
<li><strong>Stack with care.</strong> Handmade rims and feet do not love rough contact.</li>
|
||||||
|
<li><strong>Lift larger mugs by the body when possible.</strong> It is simply gentler over time.</li>
|
||||||
|
<li><strong>Pay attention to unusual heat in the microwave.</strong> If a piece gets very hot quickly, stop using it that way.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p className="mb-6">
|
||||||
|
If you want to see how our functional work is made and presented, browse the <Link to="/collections">collection</Link>, visit the <Link to="/atelier">atelier</Link>, or read more through the <Link to="/editorial">editorial archive</Link>. For practical buying questions, the <Link to="/faq">FAQ</Link> is a useful next stop too.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-6 text-3xl">Handmade pottery is meant to be used</h2>
|
||||||
|
<p className="mb-6">
|
||||||
|
The heart of how to care for handmade ceramics is not perfection. It is attention. Use your pottery often. Wash it kindly. Store it dry. Notice when a piece needs a softer kind of use.
|
||||||
|
</p>
|
||||||
|
<p className="mb-12">
|
||||||
|
The best handmade ceramics are not the ones hidden away in a cabinet. They are the ones that become part of daily life. Knowing how to care for handmade ceramics simply helps those pieces stay with you longer.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="mt-20 pt-12 border-t border-stone-200 dark:border-stone-800">
|
||||||
|
<h3 className="font-display text-xl mb-6 text-stone-500 dark:text-stone-400 uppercase tracking-widest text-sm">Sources & Further Reading</h3>
|
||||||
|
<ul className="space-y-2 text-sm text-stone-500 dark:text-stone-400 font-light">
|
||||||
|
{SOURCES.map((source) => (
|
||||||
|
<li key={source.href}>
|
||||||
|
<a href={source.href} target="_blank" rel="noreferrer" className="underline decoration-stone-300 underline-offset-4 hover:text-stone-700 dark:hover:text-stone-200">
|
||||||
|
{source.label}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</BlogPostLayout>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default PackagingGuide;
|
export default PackagingGuide;
|
||||||
|
|||||||
@@ -1,93 +1,155 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import BlogPostLayout from '../../components/BlogPostLayout';
|
import BlogPostLayout from '../../components/BlogPostLayout';
|
||||||
// Wait, I don't know if react-helmet is installed. Checking package.json... it was not.
|
import SEO, { SITE_URL } from '../../components/SEO';
|
||||||
// I will adhere to the "no new dependencies" rule unless necessary. I'll just render the meta tags usually, but without Helmet they won't lift to head.
|
|
||||||
// The user asked for "Meta Title" and "Meta Description" implementation. I will add a helper to update document.title.
|
const HERO_IMAGE = 'https://lh3.googleusercontent.com/aida-public/AB6AXuAipMlYLTcRT_hdc3VePfFIlrA56VzZ5G2y3gcRfmIZMERwGFKq2N19Gqo6mw7uZowXmjl2eJ89TI3Mcud2OyOfadO3mPVF_v0sI0OHupqM49WEFcWzH-Wbu3DL6bQ46F2Y8SIAk-NUQy8psjcIdBKRrM8fqdn4eOPANYTXpVxkLMAm4R0Axy4aEKNdmj917ZKKTxvXB-J8nGlITJkJ-ua7XcZOwGnfK5ttzyWW35A0oOSffCf972gmpV27wrVQgYJNLS7UyDdyQIQ';
|
||||||
|
const SOURCES = [
|
||||||
const ProductPhotography: React.FC = () => {
|
{
|
||||||
React.useEffect(() => {
|
label: 'Ceramic Arts Network - A Guide to Pottery Photography That Will Make Your Work Pop',
|
||||||
document.title = "Product Photography for Pottery: Tips for Sales | KNUTH Ceramics";
|
href: 'https://ceramicartsnetwork.org/daily/article/A-Guide-to-Pottery-Photography-That-Will-Make-Your-Work-Pop',
|
||||||
// Simple meta description update for basic SPA
|
},
|
||||||
let meta = document.querySelector('meta[name="description"]');
|
{
|
||||||
if (!meta) {
|
label: "Etsy Seller Handbook - The Ultimate Guide to Telling Your Etsy Shop's Visual Story",
|
||||||
meta = document.createElement('meta');
|
href: 'https://www.etsy.com/seller-handbook/article/the-ultimate-guide-to-telling-your-etsy/22722480541',
|
||||||
meta.setAttribute('name', 'description');
|
},
|
||||||
document.head.appendChild(meta);
|
{
|
||||||
}
|
label: 'ClayShare - Photographing Your Pottery',
|
||||||
meta.setAttribute('content', 'Master Product Photography for Pottery with our DIY guide. Learn lighting and styling tips to boost your handmade ceramic sales online. Read more now.');
|
href: 'https://www.clayshare.com/photographing-your-pottery',
|
||||||
}, []);
|
},
|
||||||
|
];
|
||||||
return (
|
|
||||||
<BlogPostLayout
|
const articleSchema = {
|
||||||
title="Product Photography for Pottery"
|
'@context': 'https://schema.org',
|
||||||
category="Studio"
|
'@type': 'Article',
|
||||||
date="Oct 03"
|
headline: 'Product Photography for Small Businesses',
|
||||||
image="https://lh3.googleusercontent.com/aida-public/AB6AXuAipMlYLTcRT_hdc3VePfFIlrA56VzZ5G2y3gcRfmIZMERwGFKq2N19Gqo6mw7uZowXmjl2eJ89TI3Mcud2OyOfadO3mPVF_v0sI0OHupqM49WEFcWzH-Wbu3DL6bQ46F2Y8SIAk-NUQy8psjcIdBKRrM8fqdn4eOPANYTXpVxkLMAm4R0Axy4aEKNdmj917ZKKTxvXB-J8nGlITJkJ-ua7XcZOwGnfK5ttzyWW35A0oOSffCf972gmpV27wrVQgYJNLS7UyDdyQIQ"
|
description: 'Product photography for small businesses selling handmade ceramics. Learn light, angles, styling, and listing-photo essentials that help convert browsers into buyers.',
|
||||||
imageAlt="DIY Product Photography for Pottery setup with natural light"
|
author: { '@type': 'Person', name: 'Claudia Knuth' },
|
||||||
>
|
publisher: { '@type': 'Organization', name: 'KNUTH Ceramics', url: SITE_URL },
|
||||||
<p className="lead text-xl text-stone-600 dark:text-stone-300 italic mb-8">
|
datePublished: '2024-10-03',
|
||||||
Mastering <strong>Product Photography for Pottery</strong> is essential because in the world of handmade business, your work is only as good as the photo that represents it. Since customers can't touch your mugs online, your photos must bridge the gap between browsing and buying.
|
url: `${SITE_URL}/editorial/product-photography-for-small-businesses`,
|
||||||
</p>
|
image: HERO_IMAGE,
|
||||||
|
mainEntityOfPage: `${SITE_URL}/editorial/product-photography-for-small-businesses`,
|
||||||
<p>
|
keywords: 'product photography for small businesses, pottery photography, ceramic product photos, handmade business photography',
|
||||||
Here is how to elevate your <strong>Product Photography for Pottery</strong> without expensive gear.
|
};
|
||||||
</p>
|
|
||||||
|
const ProductPhotography: React.FC = () => {
|
||||||
<img
|
return (
|
||||||
src="https://images.unsplash.com/photo-1516483638261-f4dbaf036963?q=80&w=2574&auto=format&fit=crop"
|
<>
|
||||||
alt="Product Photography for Pottery setup"
|
<SEO
|
||||||
title="DIY Setup"
|
title="Product Photography for Small Businesses"
|
||||||
className="w-full my-12 shadow-lg"
|
description="Product photography for small businesses selling handmade ceramics. Learn light, angles, styling, and listing-photo essentials that help convert browsers into buyers."
|
||||||
/>
|
canonical={`${SITE_URL}/editorial/product-photography-for-small-businesses`}
|
||||||
|
schema={articleSchema}
|
||||||
<h2 className="mt-16 mb-8 text-3xl">1. Treasure the Natural Light</h2>
|
ogType="article"
|
||||||
<p className="mb-6">
|
ogImage={HERO_IMAGE}
|
||||||
Lighting is the single most critical element of successful <strong>Product Photography for Pottery</strong>. Avoid the harsh, yellow glow of indoor lamps. Instead, set up your "studio" next to a large North or South-facing window, similar to the natural light in our <Link to="/atelier">Atelier</Link>.
|
/>
|
||||||
</p>
|
<BlogPostLayout
|
||||||
<ul className="mb-12 space-y-4">
|
title="Product Photography for Small Businesses"
|
||||||
<li><strong>Diffused Light is Best</strong>: If the sun is beaming directly in, tape a sheet of white parchment paper over the window. This creates soft shadows that highlight the curves of your <strong>ceramic vessels</strong> without blinding glare.</li>
|
category="Studio"
|
||||||
<li><strong>The Golden Hour</strong>: For lifestyle shots, try shooting during the hour after sunrise or before sunset for a warm, magical glow.</li>
|
date="Oct 03"
|
||||||
</ul>
|
image={HERO_IMAGE}
|
||||||
|
imageAlt="Product photography for small businesses - handmade pottery styled near a window with natural light"
|
||||||
<h2 className="mt-16 mb-8 text-3xl">2. Master the "Hero Shot"</h2>
|
>
|
||||||
<p className="mb-6">
|
<p className="lead text-xl text-stone-600 dark:text-stone-300 italic mb-8">
|
||||||
Every listing needs a clear shot. When mastering <strong>Product Photography for Pottery</strong>, the "Hero Shot" usually requires a clean background for your <Link to="/collections">Collections</Link>.
|
<strong>Product photography for small businesses</strong> is one of the most powerful tools a maker has, and one of the most overlooked. Beautiful pots are not enough on their own. You also need beautiful photos to tell the story.
|
||||||
</p>
|
</p>
|
||||||
<ul className="mb-12 space-y-4">
|
|
||||||
<li><strong>The Infinite Curve</strong>: Use a large sheet of white poster board. Tape one end to the wall and let it curve gently down onto the table. This seamless background eliminates distracting horizon lines.</li>
|
<p className="mb-6">
|
||||||
<li><strong>Tripod Stability</strong>: Blurry photos are a dealbreaker. If you don't have a tripod, prop your phone up against a stack of books.</li>
|
I remember a specific moment clearly. I had just finished a batch of Gulf-glazed mugs - soft seafoam bleeding into sandy cream, exactly the way the water looks near the shore on a quiet morning. I listed them on my shop within the hour, took a quick photo on my kitchen counter, and waited. Nothing.
|
||||||
</ul>
|
</p>
|
||||||
|
<p className="mb-6">
|
||||||
<h2 className="mt-16 mb-8 text-3xl">3. Tell a Story with Props</h2>
|
A few weeks later I was scrolling through another potter's shop. The work was technically similar to mine, but her photos stopped me mid-scroll. Soft light across the throwing lines. A hand around a mug. A top-down shot that showed glaze pooling at the center of a bowl. I bought something without even meaning to.
|
||||||
<p className="mb-6">
|
</p>
|
||||||
While a clean background shows the details, lifestyle **Product Photography for Pottery** sells the <em>dream</em>.
|
<p className="mb-6">
|
||||||
</p>
|
That was the lesson: I was not selling bad pots. I was telling a bad story.
|
||||||
<ul className="mb-12 space-y-4">
|
</p>
|
||||||
<li><strong>Context matters</strong>: Don't just show a mug; show it steaming with coffee next to a half-read book.</li>
|
|
||||||
<li><strong>Keep it subtle</strong>: Your props should never compete with your work. Neutral linens complement the vibrant <strong>glaze colors</strong> of your <Link to="/collections">Collections</Link>.</li>
|
<h2 className="mt-16 mb-6 text-3xl">Why product photography matters so much</h2>
|
||||||
</ul>
|
<p className="mb-6">
|
||||||
|
The strongest seller education on Etsy, Ceramic Arts Network, and ClayShare keeps circling the same truth: online buyers decide visually, and they decide quickly. For handmade ceramics, photos carry even more weight because customers cannot feel the clay body, read the glaze surface, or judge scale with their hands. Your images have to do that work for them.
|
||||||
<h2 className="mt-16 mb-8 text-3xl">4. Angles & Details</h2>
|
</p>
|
||||||
<p className="mb-6">
|
<p className="mb-6">
|
||||||
Don't stop at one angle. Online buyers need to see everything.
|
Your job is to give the customer's hands something to imagine.
|
||||||
</p>
|
</p>
|
||||||
<ul className="mb-12 space-y-4">
|
|
||||||
<li><strong>The Eye-Level Shot</strong>: Perfect for showing the profile of a vase.</li>
|
<h2 className="mt-16 mb-6 text-3xl">1. Start with light, not your camera</h2>
|
||||||
<li><strong>The Top-Down Shot</strong>: Ideal for plates and bowls.</li>
|
<p className="mb-6">
|
||||||
<li><strong>The Detail Macro</strong>: Get close. Show the texture of the raw clay body.</li>
|
The camera matters less than the light. The most effective setup I know costs almost nothing: a large window, an overcast day, and a sheet of white foam board placed opposite the light source to lift the shadows.
|
||||||
</ul>
|
</p>
|
||||||
|
<p className="mb-6">
|
||||||
<h2 className="mt-16 mb-8 text-3xl">5. Editing: Less is More</h2>
|
What you want to avoid is direct sunlight. It creates harsh hotspots, flattens matte glazes, and blows out glossy surfaces. Soft directional light does the opposite. It reveals form, texture, and the subtle transitions that make handmade work feel alive.
|
||||||
<p className="mb-6">
|
</p>
|
||||||
You don't need Photoshop. Free apps like <strong>Snapseed</strong> or <strong>Lightroom Mobile</strong> are powerful tools for editing <strong>Product Photography for Pottery</strong>.
|
<p className="mb-6">
|
||||||
</p>
|
If you need consistency year-round, two daylight-balanced LED lights with diffusion are enough for a small studio setup. But if you are just beginning, use the window first. Learn to see light before you spend money.
|
||||||
<ul className="mb-12 space-y-4">
|
</p>
|
||||||
<li><strong>Correction, not Alteration</strong>: Adjust brightness, contrast, and white balance.</li>
|
|
||||||
<li><strong>True-to-Life Color</strong>: Be very careful not to over-saturate.</li>
|
<img
|
||||||
</ul>
|
src="/product_images/product_photography_mid_v2.png"
|
||||||
</BlogPostLayout>
|
alt="Product photography for small businesses - ceramics styled near a window with soft natural light"
|
||||||
);
|
className="w-full my-12 shadow-lg rounded-sm"
|
||||||
};
|
/>
|
||||||
|
<p className="text-sm text-center text-stone-500 -mt-8 mb-12 italic">
|
||||||
|
Natural window light is still the easiest and most flattering starting point for pottery photography.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-6 text-3xl">2. The six photos every listing needs</h2>
|
||||||
|
<p className="mb-6">
|
||||||
|
You do not need twenty angles. You need the right six:
|
||||||
|
</p>
|
||||||
|
<ul className="mb-8 space-y-4 list-none pl-0">
|
||||||
|
<li><strong>The hero shot.</strong> Slightly elevated, clean background, the image that carries the listing.</li>
|
||||||
|
<li><strong>The front profile.</strong> Shows silhouette, proportion, and handle placement.</li>
|
||||||
|
<li><strong>The top-down.</strong> Essential for bowls and plates because the interior matters.</li>
|
||||||
|
<li><strong>The detail shot.</strong> Throwing lines, glaze breaks, clay texture. This is where handmade work wins.</li>
|
||||||
|
<li><strong>The scale shot.</strong> A hand, spoon, or book nearby so size is unmistakable.</li>
|
||||||
|
<li><strong>The in-use shot.</strong> A mug with coffee, a bowl with fruit, a vase with something from outside. This is where people start seeing the piece in their own life.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-6 text-3xl">3. Backgrounds and props should support the pot</h2>
|
||||||
|
<p className="mb-6">
|
||||||
|
For listing images, stay neutral: white paper, linen, stone, raw wood. For lifestyle images, use props that feel like they come from the same world as the clay. In my studio here in Corpus Christi, that often means weathered wood, natural fiber cloth, and something gathered from outside.
|
||||||
|
</p>
|
||||||
|
<p className="mb-6">
|
||||||
|
The rule I keep coming back to is simple: props support the pot. They do not perform alongside it. If someone remembers the eucalyptus stem more than the bowl, pull the eucalyptus.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-6 text-3xl">4. A few phone-camera habits matter more than gear</h2>
|
||||||
|
<ul className="mb-8 space-y-4 list-none pl-0">
|
||||||
|
<li><strong>Lock focus and exposure.</strong> Let the composition stay stable from shot to shot.</li>
|
||||||
|
<li><strong>Do not use digital zoom.</strong> Move closer instead.</li>
|
||||||
|
<li><strong>Use a tripod.</strong> Even a small tabletop phone tripod makes a visible difference.</li>
|
||||||
|
<li><strong>Keep white balance consistent.</strong> One glaze should not look like three different colors across one listing.</li>
|
||||||
|
<li><strong>Photograph details intentionally.</strong> Texture is part of the value of handmade work.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p className="mb-6">
|
||||||
|
You can browse our <Link to="/collections">current collection</Link> to see how we approach consistency across listings, and our <Link to="/atelier">atelier page</Link> for the studio atmosphere those images grow out of.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mt-16 mb-6 text-3xl">This is craft, too</h2>
|
||||||
|
<p className="mb-6">
|
||||||
|
Product photography for small businesses is a craft skill the same way trimming or pulling a handle is a craft skill. It takes repetition. It takes attention. And it gets better when you treat it as part of the work rather than something separate from it.
|
||||||
|
</p>
|
||||||
|
<p className="mb-12">
|
||||||
|
Beautiful pots deserve an audience. Good photographs are often how they find one.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="mt-20 pt-12 border-t border-stone-200 dark:border-stone-800">
|
||||||
|
<h3 className="font-display text-xl mb-6 text-stone-500 dark:text-stone-400 uppercase tracking-widest text-sm">Sources & Further Reading</h3>
|
||||||
|
<ul className="space-y-2 text-sm text-stone-500 dark:text-stone-400 font-light">
|
||||||
|
{SOURCES.map((source) => (
|
||||||
|
<li key={source.href}>
|
||||||
|
<a href={source.href} target="_blank" rel="noreferrer" className="underline decoration-stone-300 underline-offset-4 hover:text-stone-700 dark:hover:text-stone-200">
|
||||||
|
{source.label}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</BlogPostLayout>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default ProductPhotography;
|
export default ProductPhotography;
|
||||||
|
|||||||
@@ -1,206 +1,206 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
const Privacy: React.FC = () => {
|
const Privacy: React.FC = () => {
|
||||||
const sections = [
|
const sections = [
|
||||||
{
|
{
|
||||||
title: "Information We Collect",
|
title: "Information We Collect",
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
subtitle: "Account & Order Information",
|
subtitle: "Account & Order Information",
|
||||||
text: "When you make a purchase or create an account, we collect your name, billing and shipping address, email, phone number, and payment information (handled securely by our payment processor)."
|
text: "When you make a purchase or create an account, we collect your name, billing and shipping address, email, phone number, and payment information (handled securely by our payment processor)."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subtitle: "Site Usage",
|
subtitle: "Site Usage",
|
||||||
text: "We may collect data about how you use our website (such as pages visited, items viewed) through cookies and analytics tools. This is non-identifying information to help us improve our site."
|
text: "We may collect data about how you use our website (such as pages visited, items viewed) through cookies and analytics tools. This is non-identifying information to help us improve our site."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subtitle: "Communications",
|
subtitle: "Communications",
|
||||||
text: "If you contact us by email or fill out our contact form, we collect your message and contact details to respond to you."
|
text: "If you contact us by email or fill out our contact form, we collect your message and contact details to respond to you."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "How We Use Information",
|
title: "How We Use Information",
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
subtitle: "To fulfill orders",
|
subtitle: "To fulfill orders",
|
||||||
text: "Processing payments, shipping purchases, and sending order confirmations and updates."
|
text: "Processing payments, shipping purchases, and sending order confirmations and updates."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subtitle: "Customer service",
|
subtitle: "Customer service",
|
||||||
text: "Answering your inquiries, providing support, and improving our products and services."
|
text: "Answering your inquiries, providing support, and improving our products and services."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subtitle: "Marketing (with consent)",
|
subtitle: "Marketing (with consent)",
|
||||||
text: "Sending you newsletters or promotional offers if you opt-in. You can unsubscribe at any time."
|
text: "Sending you newsletters or promotional offers if you opt-in. You can unsubscribe at any time."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subtitle: "Site improvement",
|
subtitle: "Site improvement",
|
||||||
text: "Analyzing site usage trends to enhance our website and customer experience."
|
text: "Analyzing site usage trends to enhance our website and customer experience."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Sharing & Third Parties",
|
title: "Sharing & Third Parties",
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
subtitle: "We do not sell your personal information",
|
subtitle: "We do not sell your personal information",
|
||||||
text: "However, we share limited data with trusted third parties as needed:"
|
text: "However, we share limited data with trusted third parties as needed:"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subtitle: "Shipping and Fulfillment",
|
subtitle: "Shipping and Fulfillment",
|
||||||
text: "We provide your address and order details to USPS or other carriers to deliver your items."
|
text: "We provide your address and order details to USPS or other carriers to deliver your items."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subtitle: "Payment Processors",
|
subtitle: "Payment Processors",
|
||||||
text: "Secure payment details (e.g. credit card) are handled by Stripe/PayPal (your full card number is not stored on our site)."
|
text: "Secure payment details (e.g. credit card) are handled by Stripe/PayPal (your full card number is not stored on our site)."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subtitle: "Marketing Services",
|
subtitle: "Marketing Services",
|
||||||
text: "If you subscribe to our newsletter, we use email marketing platforms (e.g. Mailchimp) to send updates."
|
text: "If you subscribe to our newsletter, we use email marketing platforms (e.g. Mailchimp) to send updates."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subtitle: "Analytics",
|
subtitle: "Analytics",
|
||||||
text: "We use Google Analytics to understand website traffic. This service may set cookies and collect usage data, but it does not identify you personally."
|
text: "We use Google Analytics to understand website traffic. This service may set cookies and collect usage data, but it does not identify you personally."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Your Rights",
|
title: "Your Rights",
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
subtitle: "Data Access & Control",
|
subtitle: "Data Access & Control",
|
||||||
text: "Depending on where you live (such as California), you may have certain rights regarding your data. Generally, you can: Access or correct the personal information we hold about you; Request deletion of your data (for example, if you delete your account or unsubscribe from communications); Opt-out of certain uses of your data, like marketing emails."
|
text: "Depending on where you live (such as California), you may have certain rights regarding your data. Generally, you can: Access or correct the personal information we hold about you; Request deletion of your data (for example, if you delete your account or unsubscribe from communications); Opt-out of certain uses of your data, like marketing emails."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
subtitle: "Exercise Your Rights",
|
subtitle: "Exercise Your Rights",
|
||||||
text: "To exercise any of these rights or with questions about your data, contact us at privacy@knuthceramics.com. We will respond within 30 days."
|
text: "To exercise any of these rights or with questions about your data, contact us at privacy@knuthceramics.com. We will respond within 30 days."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Data Security",
|
title: "Data Security",
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
subtitle: "Protection Measures",
|
subtitle: "Protection Measures",
|
||||||
text: "We take data security seriously. We use HTTPS/SSL for secure data transmission and follow industry-standard practices to protect your data. Only authorized staff have access to order details, and we never store sensitive payment data on our servers. If a data breach occurs, we will notify affected users in accordance with applicable laws."
|
text: "We take data security seriously. We use HTTPS/SSL for secure data transmission and follow industry-standard practices to protect your data. Only authorized staff have access to order details, and we never store sensitive payment data on our servers. If a data breach occurs, we will notify affected users in accordance with applicable laws."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Children's Privacy",
|
title: "Children's Privacy",
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
subtitle: "Age Restrictions",
|
subtitle: "Age Restrictions",
|
||||||
text: "Our site and products are not intended for children under 13. We do not knowingly collect information from children under 13. If we learn that we have collected such data, we will promptly delete it. If you are a parent or guardian and believe we have information about your child, please contact us."
|
text: "Our site and products are not intended for children under 13. We do not knowingly collect information from children under 13. If we learn that we have collected such data, we will promptly delete it. If you are a parent or guardian and believe we have information about your child, please contact us."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-12">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-12">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-24">
|
<div className="mb-24">
|
||||||
<motion.span
|
<motion.span
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ duration: 1 }}
|
transition={{ duration: 1 }}
|
||||||
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
||||||
>
|
>
|
||||||
Legal Information
|
Legal Information
|
||||||
</motion.span>
|
</motion.span>
|
||||||
<motion.h1
|
<motion.h1
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.2, duration: 0.8 }}
|
transition={{ delay: 0.2, duration: 0.8 }}
|
||||||
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
||||||
>
|
>
|
||||||
Privacy<br />Policy
|
Privacy<br />Policy
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<motion.p
|
<motion.p
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.4, duration: 0.8 }}
|
transition={{ delay: 0.4, duration: 0.8 }}
|
||||||
className="font-body text-lg font-light text-stone-500 leading-relaxed max-w-3xl"
|
className="font-body text-lg font-light text-stone-500 leading-relaxed max-w-3xl"
|
||||||
>
|
>
|
||||||
At KNUTH Ceramics, your privacy and trust are important to us. We collect and handle your information responsibly, because 60% of people are more likely to trust brands that protect their data. This policy explains what data we collect, how we use it, and your rights.
|
At KNUTH Ceramics, your privacy and trust are important to us. We collect and handle your information responsibly, because 60% of people are more likely to trust brands that protect their data. This policy explains what data we collect, how we use it, and your rights.
|
||||||
</motion.p>
|
</motion.p>
|
||||||
<motion.p
|
<motion.p
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.5, duration: 0.8 }}
|
transition={{ delay: 0.5, duration: 0.8 }}
|
||||||
className="font-body text-sm font-light text-stone-400 mt-4"
|
className="font-body text-sm font-light text-stone-400 mt-4"
|
||||||
>
|
>
|
||||||
Last Updated: February 2, 2025
|
Last Updated: February 2, 2025
|
||||||
</motion.p>
|
</motion.p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Policy Sections */}
|
{/* Policy Sections */}
|
||||||
<div className="space-y-16">
|
<div className="space-y-16">
|
||||||
{sections.map((section, index) => (
|
{sections.map((section, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={section.title}
|
key={section.title}
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.6 + index * 0.1, duration: 0.6 }}
|
transition={{ delay: 0.6 + index * 0.1, duration: 0.6 }}
|
||||||
className="border-l-2 border-stone-300 dark:border-stone-700 pl-8"
|
className="border-l-2 border-stone-300 dark:border-stone-700 pl-8"
|
||||||
>
|
>
|
||||||
<h2 className="font-display text-3xl md:text-4xl mb-8 text-text-main dark:text-white">
|
<h2 className="font-display text-3xl md:text-4xl mb-8 text-text-main dark:text-white">
|
||||||
{section.title}
|
{section.title}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{section.content.map((item, idx) => (
|
{section.content.map((item, idx) => (
|
||||||
<div key={idx}>
|
<div key={idx}>
|
||||||
<h3 className="font-body font-semibold text-lg text-text-main dark:text-white mb-2">
|
<h3 className="font-body font-semibold text-lg text-text-main dark:text-white mb-2">
|
||||||
{item.subtitle}
|
{item.subtitle}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
||||||
{item.text}
|
{item.text}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Contact & Updates */}
|
{/* Contact & Updates */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 1.2, duration: 0.8 }}
|
transition={{ delay: 1.2, duration: 0.8 }}
|
||||||
className="mt-24 p-12 bg-primary dark:bg-black text-white border border-stone-800"
|
className="mt-24 p-12 bg-primary dark:bg-black text-white border border-stone-800"
|
||||||
>
|
>
|
||||||
<h2 className="font-display text-3xl mb-4">Contact & Updates</h2>
|
<h2 className="font-display text-3xl mb-4">Contact & Updates</h2>
|
||||||
<p className="font-body font-light text-stone-300 leading-relaxed mb-6 max-w-3xl">
|
<p className="font-body font-light text-stone-300 leading-relaxed mb-6 max-w-3xl">
|
||||||
If you have any privacy questions, or if you want to request or update your personal data, please contact us at <strong>privacy@knuthceramics.com</strong> or use our contact page. We may update this Privacy Policy occasionally to reflect changes in our practices; the latest version will always be posted here with an updated date.
|
If you have any privacy questions, or if you want to request or update your personal data, please contact us at <strong>privacy@knuthceramics.com</strong> or use our contact page. We may update this Privacy Policy occasionally to reflect changes in our practices; the latest version will always be posted here with an updated date.
|
||||||
</p>
|
</p>
|
||||||
<p className="font-body font-light text-stone-400 text-sm">
|
<p className="font-body font-light text-stone-400 text-sm">
|
||||||
By shopping with us, you acknowledge you have read and agree to this Privacy Policy.
|
By shopping with us, you acknowledge you have read and agree to this Privacy Policy.
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Additional Contact */}
|
{/* Additional Contact */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 1.4, duration: 0.8 }}
|
transition={{ delay: 1.4, duration: 0.8 }}
|
||||||
className="mt-12 text-center"
|
className="mt-12 text-center"
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
to="/contact"
|
to="/contact"
|
||||||
className="inline-block bg-white dark:bg-stone-800 text-black dark:text-white px-10 py-4 text-xs font-bold uppercase tracking-widest border border-stone-300 dark:border-stone-700 hover:bg-stone-100 dark:hover:bg-stone-700 transition-colors"
|
className="inline-block bg-white dark:bg-stone-800 text-black dark:text-white px-10 py-4 text-xs font-bold uppercase tracking-widest border border-stone-300 dark:border-stone-700 hover:bg-stone-100 dark:hover:bg-stone-700 transition-colors"
|
||||||
>
|
>
|
||||||
Contact Us
|
Contact Us
|
||||||
</Link>
|
</Link>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Privacy;
|
export default Privacy;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import { useParams, Link } from 'react-router-dom';
|
import { useParams, Link } from 'react-router-dom';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { useStore } from '../src/context/StoreContext';
|
import { useStore } from '../src/context/StoreContext';
|
||||||
|
import SEO, { SITE_URL } from '../components/SEO';
|
||||||
|
|
||||||
const ProductDetail: React.FC = () => {
|
const ProductDetail: React.FC = () => {
|
||||||
const { slug } = useParams<{ slug: string }>();
|
const { slug } = useParams<{ slug: string }>();
|
||||||
@@ -17,8 +18,56 @@ const ProductDetail: React.FC = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const productSchema = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Product",
|
||||||
|
"name": product.title,
|
||||||
|
"description": `${product.description} Handcrafted in Corpus Christi, TX by Claudia Knuth. Made in small batches using stoneware clay inspired by the Texas Gulf Coast.`,
|
||||||
|
"image": `${SITE_URL}${product.image}`,
|
||||||
|
"url": `${SITE_URL}/collections/${product.slug}`,
|
||||||
|
"brand": {
|
||||||
|
"@type": "Brand",
|
||||||
|
"name": "KNUTH Ceramics"
|
||||||
|
},
|
||||||
|
"manufacturer": {
|
||||||
|
"@type": "LocalBusiness",
|
||||||
|
"name": "KNUTH Ceramics",
|
||||||
|
"address": {
|
||||||
|
"@type": "PostalAddress",
|
||||||
|
"addressLocality": "Corpus Christi",
|
||||||
|
"addressRegion": "TX",
|
||||||
|
"addressCountry": "US"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"offers": {
|
||||||
|
"@type": "Offer",
|
||||||
|
"availability": "https://schema.org/InStock",
|
||||||
|
"priceCurrency": "USD",
|
||||||
|
"seller": {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "KNUTH Ceramics"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"breadcrumb": {
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
"itemListElement": [
|
||||||
|
{ "@type": "ListItem", "position": 1, "name": "Home", "item": `${SITE_URL}/` },
|
||||||
|
{ "@type": "ListItem", "position": 2, "name": "Collections", "item": `${SITE_URL}/collections` },
|
||||||
|
{ "@type": "ListItem", "position": 3, "name": product.title, "item": `${SITE_URL}/collections/${product.slug}` }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white dark:bg-stone-950 min-h-screen pt-32 pb-24">
|
<div className="bg-white dark:bg-stone-950 min-h-screen pt-32 pb-24">
|
||||||
|
<SEO
|
||||||
|
title={`${product.title} — Handmade Ceramic | KNUTH Ceramics`}
|
||||||
|
description={`${product.description} Handcrafted in Corpus Christi, TX. $${product.price} — free shipping on orders over $150.`}
|
||||||
|
canonical={`${SITE_URL}/collections/${product.slug}`}
|
||||||
|
ogImage={`${SITE_URL}${product.image}`}
|
||||||
|
ogType="product"
|
||||||
|
schema={productSchema}
|
||||||
|
/>
|
||||||
<div className="max-w-[1920px] mx-auto px-6 md:px-12">
|
<div className="max-w-[1920px] mx-auto px-6 md:px-12">
|
||||||
{/* Breadcrumb */}
|
{/* Breadcrumb */}
|
||||||
<div className="mb-12 text-sm uppercase tracking-widest text-stone-500">
|
<div className="mb-12 text-sm uppercase tracking-widest text-stone-500">
|
||||||
@@ -91,6 +140,7 @@ const ProductDetail: React.FC = () => {
|
|||||||
</ul>
|
</ul>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
|
{/*
|
||||||
<motion.button
|
<motion.button
|
||||||
initial={{ opacity: 0, y: 10 }}
|
initial={{ opacity: 0, y: 10 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
@@ -101,6 +151,7 @@ const ProductDetail: React.FC = () => {
|
|||||||
>
|
>
|
||||||
Add to Cart
|
Add to Cart
|
||||||
</motion.button>
|
</motion.button>
|
||||||
|
*/}
|
||||||
|
|
||||||
<div className="mt-12 pt-12 border-t border-stone-200 dark:border-stone-800 text-sm text-stone-500 font-light space-y-2">
|
<div className="mt-12 pt-12 border-t border-stone-200 dark:border-stone-800 text-sm text-stone-500 font-light space-y-2">
|
||||||
<p>Free shipping on orders over $150</p>
|
<p>Free shipping on orders over $150</p>
|
||||||
|
|||||||
@@ -1,164 +1,164 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
const Returns: React.FC = () => {
|
const Returns: React.FC = () => {
|
||||||
const returnSteps = [
|
const returnSteps = [
|
||||||
{
|
{
|
||||||
step: "01",
|
step: "01",
|
||||||
title: "Contact Us",
|
title: "Contact Us",
|
||||||
description: "Email support@knuthceramics.com within 14 days of delivery with your order number and photos of the item and packaging."
|
description: "Email support@knuthceramics.com within 14 days of delivery with your order number and photos of the item and packaging."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
step: "02",
|
step: "02",
|
||||||
title: "Receive Instructions",
|
title: "Receive Instructions",
|
||||||
description: "Our team will review your request and provide detailed return instructions and a return authorization if approved."
|
description: "Our team will review your request and provide detailed return instructions and a return authorization if approved."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
step: "03",
|
step: "03",
|
||||||
title: "Ship Back",
|
title: "Ship Back",
|
||||||
description: "Carefully package the item with at least 1\" of padding around each piece. Use insured shipping and retain your tracking number."
|
description: "Carefully package the item with at least 1\" of padding around each piece. Use insured shipping and retain your tracking number."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
step: "04",
|
step: "04",
|
||||||
title: "Get Refund",
|
title: "Get Refund",
|
||||||
description: "Once we receive and inspect the returned item, we'll process your exchange or refund within 5–7 business days."
|
description: "Once we receive and inspect the returned item, we'll process your exchange or refund within 5–7 business days."
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
||||||
<div className="max-w-[1400px] mx-auto px-6 md:px-12">
|
<div className="max-w-[1400px] mx-auto px-6 md:px-12">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-24 max-w-3xl">
|
<div className="mb-24 max-w-3xl">
|
||||||
<motion.span
|
<motion.span
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ duration: 1 }}
|
transition={{ duration: 1 }}
|
||||||
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
||||||
>
|
>
|
||||||
Customer Care
|
Customer Care
|
||||||
</motion.span>
|
</motion.span>
|
||||||
<motion.h1
|
<motion.h1
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.2, duration: 0.8 }}
|
transition={{ delay: 0.2, duration: 0.8 }}
|
||||||
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
||||||
>
|
>
|
||||||
Returns &<br />Exchanges
|
Returns &<br />Exchanges
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<motion.p
|
<motion.p
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.4, duration: 0.8 }}
|
transition={{ delay: 0.4, duration: 0.8 }}
|
||||||
className="font-body text-lg font-light text-stone-500 leading-relaxed"
|
className="font-body text-lg font-light text-stone-500 leading-relaxed"
|
||||||
>
|
>
|
||||||
We understand that buying handmade ceramics is different than buying mass-produced goods. Because each piece is made just for you, returns are limited. Please review our policy carefully.
|
We understand that buying handmade ceramics is different than buying mass-produced goods. Because each piece is made just for you, returns are limited. Please review our policy carefully.
|
||||||
</motion.p>
|
</motion.p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Policy Details */}
|
{/* Policy Details */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-24">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-24">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 20, opacity: 0 }}
|
initial={{ y: 20, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.5, duration: 0.6 }}
|
transition={{ delay: 0.5, duration: 0.6 }}
|
||||||
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
||||||
>
|
>
|
||||||
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">Eligibility</h3>
|
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">Eligibility</h3>
|
||||||
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
||||||
We only accept returns for items that are <strong>defective, damaged, or incorrect</strong>. If your item arrived damaged, see the Damaged Items section below. For a refund or exchange, contact us within 14 days of delivery.
|
We only accept returns for items that are <strong>defective, damaged, or incorrect</strong>. If your item arrived damaged, see the Damaged Items section below. For a refund or exchange, contact us within 14 days of delivery.
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 20, opacity: 0 }}
|
initial={{ y: 20, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.6, duration: 0.6 }}
|
transition={{ delay: 0.6, duration: 0.6 }}
|
||||||
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
||||||
>
|
>
|
||||||
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">Condition</h3>
|
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">Condition</h3>
|
||||||
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
||||||
Returned items must be <strong>unused and in their original packaging</strong> (minus the damage, if any). We reserve the right to refuse returns for pieces showing signs of use or abuse.
|
Returned items must be <strong>unused and in their original packaging</strong> (minus the damage, if any). We reserve the right to refuse returns for pieces showing signs of use or abuse.
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 20, opacity: 0 }}
|
initial={{ y: 20, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.7, duration: 0.6 }}
|
transition={{ delay: 0.7, duration: 0.6 }}
|
||||||
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
||||||
>
|
>
|
||||||
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">Return Shipping</h3>
|
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">Return Shipping</h3>
|
||||||
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
||||||
The customer is responsible for return shipping costs, <strong>except in the case of our error or damage</strong>. We strongly recommend insured shipping and adequate packaging.
|
The customer is responsible for return shipping costs, <strong>except in the case of our error or damage</strong>. We strongly recommend insured shipping and adequate packaging.
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 20, opacity: 0 }}
|
initial={{ y: 20, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.8, duration: 0.6 }}
|
transition={{ delay: 0.8, duration: 0.6 }}
|
||||||
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800"
|
||||||
>
|
>
|
||||||
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">Exchanges & Refunds</h3>
|
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">Exchanges & Refunds</h3>
|
||||||
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
||||||
Once we receive and inspect the returned item, we will process an exchange or refund promptly. Refunds are issued to the original payment method. Please allow 5–7 business days for the credit to appear.
|
Once we receive and inspect the returned item, we will process an exchange or refund promptly. Refunds are issued to the original payment method. Please allow 5–7 business days for the credit to appear.
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Return Process */}
|
{/* Return Process */}
|
||||||
<div className="mb-24">
|
<div className="mb-24">
|
||||||
<h2 className="font-display text-4xl md:text-5xl mb-12 text-text-main dark:text-white">
|
<h2 className="font-display text-4xl md:text-5xl mb-12 text-text-main dark:text-white">
|
||||||
Return Process
|
Return Process
|
||||||
</h2>
|
</h2>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
{returnSteps.map((item, index) => (
|
{returnSteps.map((item, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={item.step}
|
key={item.step}
|
||||||
initial={{ y: 20, opacity: 0 }}
|
initial={{ y: 20, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.9 + index * 0.1, duration: 0.6 }}
|
transition={{ delay: 0.9 + index * 0.1, duration: 0.6 }}
|
||||||
className="relative"
|
className="relative"
|
||||||
>
|
>
|
||||||
<div className="absolute top-0 left-0 font-display text-6xl text-stone-200 dark:text-stone-800 -z-10">
|
<div className="absolute top-0 left-0 font-display text-6xl text-stone-200 dark:text-stone-800 -z-10">
|
||||||
{item.step}
|
{item.step}
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-16">
|
<div className="pt-16">
|
||||||
<h4 className="font-display text-xl mb-3 text-text-main dark:text-white">
|
<h4 className="font-display text-xl mb-3 text-text-main dark:text-white">
|
||||||
{item.title}
|
{item.title}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed text-sm">
|
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed text-sm">
|
||||||
{item.description}
|
{item.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Damaged Items */}
|
{/* Damaged Items */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 1.3, duration: 0.8 }}
|
transition={{ delay: 1.3, duration: 0.8 }}
|
||||||
className="p-8 md:p-12 bg-primary dark:bg-black text-white border border-stone-800"
|
className="p-8 md:p-12 bg-primary dark:bg-black text-white border border-stone-800"
|
||||||
>
|
>
|
||||||
<h3 className="font-display text-3xl mb-4">Damaged Items</h3>
|
<h3 className="font-display text-3xl mb-4">Damaged Items</h3>
|
||||||
<p className="font-body font-light text-stone-300 leading-relaxed max-w-3xl mb-6">
|
<p className="font-body font-light text-stone-300 leading-relaxed max-w-3xl mb-6">
|
||||||
If your item arrived broken, please contact us immediately. Take a photo of the damage (and packaging, if possible), and email us within 7 days of receipt. All orders are fully insured during transit. We will cover shipping costs to replace or refund damaged goods.
|
If your item arrived broken, please contact us immediately. Take a photo of the damage (and packaging, if possible), and email us within 7 days of receipt. All orders are fully insured during transit. We will cover shipping costs to replace or refund damaged goods.
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
to="/contact"
|
to="/contact"
|
||||||
className="inline-block bg-white text-black px-8 py-3 text-xs font-bold uppercase tracking-widest hover:bg-stone-200 transition-colors"
|
className="inline-block bg-white text-black px-8 py-3 text-xs font-bold uppercase tracking-widest hover:bg-stone-200 transition-colors"
|
||||||
>
|
>
|
||||||
Report Damage
|
Report Damage
|
||||||
</Link>
|
</Link>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Returns;
|
export default Returns;
|
||||||
|
|||||||
@@ -1,131 +1,131 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
const Shipping: React.FC = () => {
|
const Shipping: React.FC = () => {
|
||||||
const shippingDetails = [
|
const shippingDetails = [
|
||||||
{
|
{
|
||||||
title: "Order Processing",
|
title: "Order Processing",
|
||||||
description: "Each item is handcrafted and inspected with care. Please allow up to 3 business days for us to prepare and pack your order.",
|
description: "Each item is handcrafted and inspected with care. Please allow up to 3 business days for us to prepare and pack your order.",
|
||||||
icon: "📦"
|
icon: "📦"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Shipping Methods",
|
title: "Shipping Methods",
|
||||||
description: "We offer standard USPS shipping for most orders. For faster delivery, expedited shipping (such as USPS Priority or UPS) is available at checkout. Some oversized or multiple-item orders may ship via UPS Ground.",
|
description: "We offer standard USPS shipping for most orders. For faster delivery, expedited shipping (such as USPS Priority or UPS) is available at checkout. Some oversized or multiple-item orders may ship via UPS Ground.",
|
||||||
icon: "🚚"
|
icon: "🚚"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Delivery Time",
|
title: "Delivery Time",
|
||||||
description: "After processing, expect 3–5 business days transit time for standard domestic delivery. Expedited options can arrive in 1–2 days. Your order confirmation email will include a tracking number.",
|
description: "After processing, expect 3–5 business days transit time for standard domestic delivery. Expedited options can arrive in 1–2 days. Your order confirmation email will include a tracking number.",
|
||||||
icon: "⏱️"
|
icon: "⏱️"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Free Shipping",
|
title: "Free Shipping",
|
||||||
description: "We offer flat-rate shipping on orders under $75. Free standard shipping is available on all orders over $75. These promotions are subject to change, so check the cart for current offers.",
|
description: "We offer flat-rate shipping on orders under $75. Free standard shipping is available on all orders over $75. These promotions are subject to change, so check the cart for current offers.",
|
||||||
icon: "✨"
|
icon: "✨"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Packaging",
|
title: "Packaging",
|
||||||
description: "We reuse packing materials and eco-friendly fillers whenever possible to protect your ceramics and reduce waste. Every package is insured for its full value during transit.",
|
description: "We reuse packing materials and eco-friendly fillers whenever possible to protect your ceramics and reduce waste. Every package is insured for its full value during transit.",
|
||||||
icon: "♻️"
|
icon: "♻️"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Shipping Restrictions",
|
title: "Shipping Restrictions",
|
||||||
description: "We do not ship to P.O. boxes. Unfortunately, we cannot ship internationally at this time (U.S. addresses only). If you need a rush order, please contact us – we'll do our best to accommodate.",
|
description: "We do not ship to P.O. boxes. Unfortunately, we cannot ship internationally at this time (U.S. addresses only). If you need a rush order, please contact us – we'll do our best to accommodate.",
|
||||||
icon: "🇺🇸"
|
icon: "🇺🇸"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
<div className="bg-stone-50 dark:bg-stone-900 min-h-screen pt-32 pb-24">
|
||||||
<div className="max-w-[1400px] mx-auto px-6 md:px-12">
|
<div className="max-w-[1400px] mx-auto px-6 md:px-12">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-24 max-w-3xl">
|
<div className="mb-24 max-w-3xl">
|
||||||
<motion.span
|
<motion.span
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ duration: 1 }}
|
transition={{ duration: 1 }}
|
||||||
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
className="block text-xs uppercase tracking-[0.3em] text-stone-400 mb-6"
|
||||||
>
|
>
|
||||||
Delivery Information
|
Delivery Information
|
||||||
</motion.span>
|
</motion.span>
|
||||||
<motion.h1
|
<motion.h1
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.2, duration: 0.8 }}
|
transition={{ delay: 0.2, duration: 0.8 }}
|
||||||
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
className="font-display text-5xl md:text-7xl lg:text-8xl leading-none text-text-main dark:text-white mb-8"
|
||||||
>
|
>
|
||||||
Shipping<br />Policy
|
Shipping<br />Policy
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<motion.p
|
<motion.p
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.4, duration: 0.8 }}
|
transition={{ delay: 0.4, duration: 0.8 }}
|
||||||
className="font-body text-lg font-light text-stone-500 leading-relaxed"
|
className="font-body text-lg font-light text-stone-500 leading-relaxed"
|
||||||
>
|
>
|
||||||
KNUTH Ceramics proudly ships nationwide from our Texas studio. We strive to make delivery smooth and transparent for you.
|
KNUTH Ceramics proudly ships nationwide from our Texas studio. We strive to make delivery smooth and transparent for you.
|
||||||
</motion.p>
|
</motion.p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Shipping Details Grid */}
|
{/* Shipping Details Grid */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-24">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-24">
|
||||||
{shippingDetails.map((detail, index) => (
|
{shippingDetails.map((detail, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={detail.title}
|
key={detail.title}
|
||||||
initial={{ y: 20, opacity: 0 }}
|
initial={{ y: 20, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: index * 0.1, duration: 0.6 }}
|
transition={{ delay: index * 0.1, duration: 0.6 }}
|
||||||
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800 hover:border-stone-400 dark:hover:border-stone-600 transition-colors"
|
className="p-8 bg-white dark:bg-black border border-stone-200 dark:border-stone-800 hover:border-stone-400 dark:hover:border-stone-600 transition-colors"
|
||||||
>
|
>
|
||||||
<div className="text-4xl mb-6">{detail.icon}</div>
|
<div className="text-4xl mb-6">{detail.icon}</div>
|
||||||
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">
|
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">
|
||||||
{detail.title}
|
{detail.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
<p className="font-body font-light text-stone-600 dark:text-stone-400 leading-relaxed">
|
||||||
{detail.description}
|
{detail.description}
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Important Notice */}
|
{/* Important Notice */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 0.8, duration: 0.8 }}
|
transition={{ delay: 0.8, duration: 0.8 }}
|
||||||
className="p-8 md:p-12 bg-amber-50 dark:bg-amber-900/10 border-l-4 border-amber-500"
|
className="p-8 md:p-12 bg-amber-50 dark:bg-amber-900/10 border-l-4 border-amber-500"
|
||||||
>
|
>
|
||||||
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">
|
<h3 className="font-display text-2xl mb-4 text-text-main dark:text-white">
|
||||||
Peak Season Notice
|
Peak Season Notice
|
||||||
</h3>
|
</h3>
|
||||||
<p className="font-body font-light text-stone-700 dark:text-stone-300 leading-relaxed">
|
<p className="font-body font-light text-stone-700 dark:text-stone-300 leading-relaxed">
|
||||||
Peak seasons and holidays may add 1–2 extra days to processing. We'll notify you of any anticipated delays. By having a clear, upfront shipping policy, we aim to boost your confidence and reduce checkout surprises.
|
Peak seasons and holidays may add 1–2 extra days to processing. We'll notify you of any anticipated delays. By having a clear, upfront shipping policy, we aim to boost your confidence and reduce checkout surprises.
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* CTA Section */}
|
{/* CTA Section */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ delay: 1, duration: 0.8 }}
|
transition={{ delay: 1, duration: 0.8 }}
|
||||||
className="mt-24 text-center"
|
className="mt-24 text-center"
|
||||||
>
|
>
|
||||||
<h2 className="font-display text-3xl md:text-4xl mb-6 text-text-main dark:text-white">
|
<h2 className="font-display text-3xl md:text-4xl mb-6 text-text-main dark:text-white">
|
||||||
Questions about shipping?
|
Questions about shipping?
|
||||||
</h2>
|
</h2>
|
||||||
<p className="font-body font-light text-stone-500 mb-8 max-w-xl mx-auto">
|
<p className="font-body font-light text-stone-500 mb-8 max-w-xl mx-auto">
|
||||||
Contact our team for specific shipping inquiries or rush orders.
|
Contact our team for specific shipping inquiries or rush orders.
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
to="/contact"
|
to="/contact"
|
||||||
className="inline-block bg-primary dark:bg-white text-white dark:text-black px-10 py-4 text-xs font-bold uppercase tracking-widest hover:bg-stone-800 dark:hover:bg-stone-200 transition-colors"
|
className="inline-block bg-primary dark:bg-white text-white dark:text-black px-10 py-4 text-xs font-bold uppercase tracking-widest hover:bg-stone-800 dark:hover:bg-stone-200 transition-colors"
|
||||||
>
|
>
|
||||||
Get in Touch
|
Get in Touch
|
||||||
</Link>
|
</Link>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Shipping;
|
export default Shipping;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 622 KiB |
|
Before Width: | Height: | Size: 566 KiB |
|
Before Width: | Height: | Size: 794 KiB |
|
Before Width: | Height: | Size: 808 KiB |
|
Before Width: | Height: | Size: 631 KiB |
BIN
Pottery-website/public/landingpage/1.png
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
BIN
Pottery-website/public/landingpage/2.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
Pottery-website/public/landingpage/3.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
Pottery-website/public/landingpage/4.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
Pottery-website/public/landingpage/artelier.png
Normal file
|
After Width: | Height: | Size: 3.0 MiB |
|
Before Width: | Height: | Size: 695 KiB |
|
Before Width: | Height: | Size: 696 KiB |
|
Before Width: | Height: | Size: 879 KiB |
|
Before Width: | Height: | Size: 701 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 2.3 MiB |
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
BIN
Pottery-website/public/product_images/care_guide_mid_v2.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
Pottery-website/public/product_images/kitchenware.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
BIN
Pottery-website/public/product_images/lighting.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
Pottery-website/public/product_images/motivation_pottery_mid.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
BIN
Pottery-website/public/product_images/packaging_guide_mid.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 50 KiB |
BIN
Pottery-website/public/product_images/serving.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
Pottery-website/public/product_images/tableware.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
Pottery-website/public/product_images/textiles.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
Pottery-website/public/product_images/vases.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
37
Pottery-website/public/robots.txt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# KNUTH Ceramics — robots.txt
|
||||||
|
# https://knuthceramics.com
|
||||||
|
|
||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
Disallow: /checkout
|
||||||
|
Disallow: /mock-payment
|
||||||
|
Disallow: /success
|
||||||
|
Disallow: /admin
|
||||||
|
|
||||||
|
# AI Search Crawlers — explicitly allowed for citation visibility
|
||||||
|
User-agent: GPTBot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: ChatGPT-User
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: PerplexityBot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: ClaudeBot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: anthropic-ai
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: Google-Extended
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: Bingbot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
# Training-only crawlers — blocked (prevents scraping without citation benefit)
|
||||||
|
User-agent: CCBot
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
Sitemap: https://knuthceramics.com/sitemap.xml
|
||||||
91
Pottery-website/public/sitemap.xml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
|
||||||
|
<!-- Core pages -->
|
||||||
|
<url>
|
||||||
|
<loc>https://knuthceramics.com/</loc>
|
||||||
|
<changefreq>weekly</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://knuthceramics.com/collections</loc>
|
||||||
|
<changefreq>weekly</changefreq>
|
||||||
|
<priority>0.9</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://knuthceramics.com/atelier</loc>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://knuthceramics.com/editorial</loc>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.7</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://knuthceramics.com/faq</loc>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.7</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://knuthceramics.com/contact</loc>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://knuthceramics.com/shipping</loc>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.5</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://knuthceramics.com/returns</loc>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.5</priority>
|
||||||
|
</url>
|
||||||
|
|
||||||
|
<!-- Product pages — Tableware -->
|
||||||
|
<url><loc>https://knuthceramics.com/collections/coastal-grey-tableware-set</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/ceramic-place-setting</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/textured-floral-plates</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
|
||||||
|
<!-- Product pages — Coffee Cups -->
|
||||||
|
<url><loc>https://knuthceramics.com/collections/set-of-three-stoneware-cups</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/dog-mam-cup-teal</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/dog-mam-cup-blush</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/dark-stoneware-cups-amber-drip</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
|
||||||
|
<!-- Product pages — Bowls -->
|
||||||
|
<url><loc>https://knuthceramics.com/collections/dark-stoneware-bowl</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/pastel-bowl-collection</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/coastal-sunset-bowl-set</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/handmade-blush-bowl</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
|
||||||
|
<!-- Product pages — Kitchenware -->
|
||||||
|
<url><loc>https://knuthceramics.com/collections/dark-stoneware-vessel</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/studio-collection-mixed-pieces</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
|
||||||
|
<!-- Product pages — Decoration -->
|
||||||
|
<url><loc>https://knuthceramics.com/collections/handmade-ceramic-rose</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/hope-bowl-with-clay-tokens</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/ceramic-house-lanterns</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/flower-frog-vase-gulf-blue</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://knuthceramics.com/collections/miniature-ceramic-village</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||||
|
|
||||||
|
<!-- Journal / Editorial -->
|
||||||
|
<url>
|
||||||
|
<loc>https://knuthceramics.com/editorial/product-photography-for-small-businesses</loc>
|
||||||
|
<changefreq>yearly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://knuthceramics.com/editorial/the-art-of-packaging</loc>
|
||||||
|
<changefreq>yearly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://knuthceramics.com/editorial/finding-motivation-in-clay</loc>
|
||||||
|
<changefreq>yearly</changefreq>
|
||||||
|
<priority>0.6</priority>
|
||||||
|
</url>
|
||||||
|
|
||||||
|
</urlset>
|
||||||
@@ -54,9 +54,9 @@ async function setup() {
|
|||||||
{
|
{
|
||||||
title: 'Tableware',
|
title: 'Tableware',
|
||||||
price: 185,
|
price: 185,
|
||||||
image: '/collection-tableware.png',
|
image: '/product_images/tableware.png',
|
||||||
description: 'A complete hand-thrown tableware set for four. Finished in our signature matte white glaze with raw clay rims.',
|
description: 'A complete hand-thrown tableware set for four. Finished in our signature matte white glaze with raw clay rims.',
|
||||||
gallery: ['/collection-tableware.png', '/pottery-plates.png', '/ceramic-cups.png'],
|
gallery: ['/product_images/tableware.png', '/product_images/textiles.png', '/product_images/kitchenware.png'],
|
||||||
slug: 'tableware-set',
|
slug: 'tableware-set',
|
||||||
number: '01',
|
number: '01',
|
||||||
aspect_ratio: 'aspect-[3/4]'
|
aspect_ratio: 'aspect-[3/4]'
|
||||||
@@ -64,9 +64,9 @@ async function setup() {
|
|||||||
{
|
{
|
||||||
title: 'Lighting',
|
title: 'Lighting',
|
||||||
price: 240,
|
price: 240,
|
||||||
image: '/collection-lighting.png',
|
image: '/product_images/lighting.png',
|
||||||
description: 'Sculptural ceramic pendant lights that bring warmth and texture to any space. Each piece is unique.',
|
description: 'Sculptural ceramic pendant lights that bring warmth and texture to any space. Each piece is unique.',
|
||||||
gallery: ['/collection-lighting.png', '/pottery-studio.png', '/collection-vases.png'],
|
gallery: ['/product_images/lighting.png', '/landingpage/artelier.png', '/product_images/vases.png'],
|
||||||
slug: 'ceramic-lighting',
|
slug: 'ceramic-lighting',
|
||||||
number: '04',
|
number: '04',
|
||||||
aspect_ratio: 'aspect-[4/3]'
|
aspect_ratio: 'aspect-[4/3]'
|
||||||
@@ -74,9 +74,9 @@ async function setup() {
|
|||||||
{
|
{
|
||||||
title: 'Vases',
|
title: 'Vases',
|
||||||
price: 95,
|
price: 95,
|
||||||
image: '/collection-vases.png',
|
image: '/product_images/vases.png',
|
||||||
description: 'Organic forms inspired by the dunes of Padre Island. Perfect for dried stems or fresh bouquets.',
|
description: 'Organic forms inspired by the dunes of Padre Island. Perfect for dried stems or fresh bouquets.',
|
||||||
gallery: ['/collection-vases.png', '/pottery-vase.png', '/collection-lighting.png'],
|
gallery: ['/product_images/vases.png', '/product_images/vases.png', '/product_images/lighting.png'],
|
||||||
slug: 'organic-vases',
|
slug: 'organic-vases',
|
||||||
number: '02',
|
number: '02',
|
||||||
aspect_ratio: 'aspect-square'
|
aspect_ratio: 'aspect-square'
|
||||||
|
|||||||
2722
Pottery-website/server/package-lock.json
generated
@@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index.js",
|
"start": "node index.js",
|
||||||
"dev": "nodemon index.js",
|
"dev": "nodemon index.js",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"pg": "^8.16.3"
|
"pg": "^8.16.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^3.1.11"
|
"nodemon": "^3.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,10 +35,11 @@ interface StoreContextType {
|
|||||||
addProduct: (product: CollectionItem) => void;
|
addProduct: (product: CollectionItem) => void;
|
||||||
updateProduct: (product: CollectionItem) => void;
|
updateProduct: (product: CollectionItem) => void;
|
||||||
deleteProduct: (id: number) => void;
|
deleteProduct: (id: number) => void;
|
||||||
addArticle: (article: JournalEntry) => void;
|
addArticle: (article: JournalEntry) => void;
|
||||||
updateArticle: (article: JournalEntry) => void;
|
updateArticle: (article: JournalEntry) => void;
|
||||||
deleteArticle: (id: number) => void;
|
deleteArticle: (id: number) => void;
|
||||||
}
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const StoreContext = createContext<StoreContextType | undefined>(undefined);
|
const StoreContext = createContext<StoreContextType | undefined>(undefined);
|
||||||
|
|
||||||
@@ -264,10 +265,10 @@ export const StoreProvider: React.FC<{ children: ReactNode }> = ({ children }) =
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StoreContext.Provider value={{
|
<StoreContext.Provider value={{
|
||||||
products,
|
products,
|
||||||
articles,
|
articles,
|
||||||
cart,
|
cart,
|
||||||
orders,
|
orders,
|
||||||
isCartOpen,
|
isCartOpen,
|
||||||
setCartOpen,
|
setCartOpen,
|
||||||
@@ -280,15 +281,16 @@ export const StoreProvider: React.FC<{ children: ReactNode }> = ({ children }) =
|
|||||||
updateOrderStatus,
|
updateOrderStatus,
|
||||||
addProduct,
|
addProduct,
|
||||||
updateProduct,
|
updateProduct,
|
||||||
deleteProduct,
|
deleteProduct,
|
||||||
addArticle,
|
addArticle,
|
||||||
updateArticle,
|
updateArticle,
|
||||||
deleteArticle
|
deleteArticle,
|
||||||
}}>
|
isLoading
|
||||||
{isLoading ? <div className="fixed inset-0 bg-white z-50 flex items-center justify-center font-display text-xl animate-pulse">Loading Store...</div> : children}
|
}}>
|
||||||
</StoreContext.Provider>
|
{children}
|
||||||
);
|
</StoreContext.Provider>
|
||||||
};
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const useStore = () => {
|
export const useStore = () => {
|
||||||
const context = useContext(StoreContext);
|
const context = useContext(StoreContext);
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"useDefineForClassFields": false,
|
"useDefineForClassFields": false,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"lib": [
|
"lib": [
|
||||||
"ES2022",
|
"ES2022",
|
||||||
"DOM",
|
"DOM",
|
||||||
"DOM.Iterable"
|
"DOM.Iterable"
|
||||||
],
|
],
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"types": [
|
"types": [
|
||||||
"node"
|
"node"
|
||||||
],
|
],
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": [
|
"@/*": [
|
||||||
"./*"
|
"./*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"noEmit": true
|
"noEmit": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,38 @@
|
|||||||
export interface NavItem {
|
export interface NavItem {
|
||||||
label: string;
|
label: string;
|
||||||
href: string;
|
href: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CollectionItem {
|
export interface CollectionItem {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
price: number;
|
price: number;
|
||||||
image: string;
|
image: string;
|
||||||
images: string[];
|
images: string[];
|
||||||
description?: string;
|
description?: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
number: string;
|
number: string;
|
||||||
aspectRatio: string;
|
aspectRatio: string;
|
||||||
details?: string[];
|
details?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JournalEntry {
|
export interface JournalEntry {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
date: string;
|
date: string;
|
||||||
image: string;
|
image: string;
|
||||||
sections: {
|
sections: {
|
||||||
id: string;
|
id: string;
|
||||||
type: 'text' | 'image';
|
type: 'text' | 'image';
|
||||||
content: string;
|
content: string;
|
||||||
}[];
|
}[];
|
||||||
slug: string;
|
slug: string;
|
||||||
category?: string;
|
category?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
isFeatured?: boolean;
|
isFeatured?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FooterSection {
|
export interface FooterSection {
|
||||||
title: string;
|
title: string;
|
||||||
links: NavItem[];
|
links: NavItem[];
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { defineConfig, loadEnv } from 'vite';
|
import { defineConfig, loadEnv } from 'vite';
|
||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
|
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
const env = loadEnv(mode, '.', '');
|
const env = loadEnv(mode, '.', '');
|
||||||
return {
|
return {
|
||||||
server: {
|
server: {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
},
|
},
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
define: {
|
define: {
|
||||||
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
||||||
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
|
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': path.resolve(__dirname, '.'),
|
'@': path.resolve(__dirname, '.'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
756
code.html
@@ -1,379 +1,379 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en"><head>
|
<html lang="en"><head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||||
<title>IKKAI Ceramics - Editorial Collection</title>
|
<title>IKKAI Ceramics - Editorial Collection</title>
|
||||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400&family=Manrope:wght@200;300;400;500&display=swap" rel="stylesheet"/>
|
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400&family=Manrope:wght@200;300;400;500&display=swap" rel="stylesheet"/>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||||
<script>
|
<script>
|
||||||
tailwind.config = {
|
tailwind.config = {
|
||||||
darkMode: "class",
|
darkMode: "class",
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
primary: "#292524", // Warm Charcoal
|
primary: "#292524", // Warm Charcoal
|
||||||
secondary: "#78716C", // Warm Stone
|
secondary: "#78716C", // Warm Stone
|
||||||
"background-light": "#F5F4F0", // Soft Sand / Alabaster (Hero)
|
"background-light": "#F5F4F0", // Soft Sand / Alabaster (Hero)
|
||||||
"background-dark": "#1C1917", // Dark Warm Grey
|
"background-dark": "#1C1917", // Dark Warm Grey
|
||||||
// New Sectional Colors
|
// New Sectional Colors
|
||||||
"sage": "#D4D9D1", // Soft Sage Green
|
"sage": "#D4D9D1", // Soft Sage Green
|
||||||
"warm-grey": "#DAD7D4", // Warm Grey
|
"warm-grey": "#DAD7D4", // Warm Grey
|
||||||
"clay-dark": "#33302D", // Deep Charcoal / Clay
|
"clay-dark": "#33302D", // Deep Charcoal / Clay
|
||||||
"terracotta-soft": "#E6DDD5", // Pale Ochre / Soft Terracotta
|
"terracotta-soft": "#E6DDD5", // Pale Ochre / Soft Terracotta
|
||||||
"accent-sand": "#E7E5E4",
|
"accent-sand": "#E7E5E4",
|
||||||
"accent-warm": "#D6D3D1",
|
"accent-warm": "#D6D3D1",
|
||||||
"text-main": "#1C1917",
|
"text-main": "#1C1917",
|
||||||
"text-muted": "#57534E",
|
"text-muted": "#57534E",
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
display: ["Cormorant Garamond", "serif"],
|
display: ["Cormorant Garamond", "serif"],
|
||||||
body: ["Manrope", "sans-serif"],
|
body: ["Manrope", "sans-serif"],
|
||||||
},
|
},
|
||||||
fontSize: {
|
fontSize: {
|
||||||
'10xl': '10rem',
|
'10xl': '10rem',
|
||||||
},
|
},
|
||||||
spacing: {
|
spacing: {
|
||||||
'128': '32rem',
|
'128': '32rem',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.material-symbols-outlined {
|
.material-symbols-outlined {
|
||||||
font-variation-settings:
|
font-variation-settings:
|
||||||
'FILL' 0,
|
'FILL' 0,
|
||||||
'wght' 300,
|
'wght' 300,
|
||||||
'GRAD' 0,
|
'GRAD' 0,
|
||||||
'opsz' 24
|
'opsz' 24
|
||||||
}
|
}
|
||||||
html {
|
html {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
.parallax-bg {
|
.parallax-bg {
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}::-webkit-scrollbar {
|
}::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: #F5F4F0;
|
background: #F5F4F0;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: #D6D3D1;
|
background: #D6D3D1;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: #78716C;
|
background: #78716C;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="font-body bg-background-light dark:bg-background-dark text-text-main dark:text-gray-200 antialiased transition-colors duration-500 selection:bg-stone-200 selection:text-black">
|
<body class="font-body bg-background-light dark:bg-background-dark text-text-main dark:text-gray-200 antialiased transition-colors duration-500 selection:bg-stone-200 selection:text-black">
|
||||||
<div class="bg-primary dark:bg-black text-white text-[10px] tracking-[0.2em] text-center py-3 uppercase font-light">
|
<div class="bg-primary dark:bg-black text-white text-[10px] tracking-[0.2em] text-center py-3 uppercase font-light">
|
||||||
Complimentary shipping on orders over €200
|
Complimentary shipping on orders over €200
|
||||||
</div>
|
</div>
|
||||||
<header class="fixed top-0 w-full z-50 bg-background-light/80 dark:bg-background-dark/80 backdrop-blur-md border-b border-stone-200/50 dark:border-stone-800/50 transition-all duration-300">
|
<header class="fixed top-0 w-full z-50 bg-background-light/80 dark:bg-background-dark/80 backdrop-blur-md border-b border-stone-200/50 dark:border-stone-800/50 transition-all duration-300">
|
||||||
<div class="max-w-[1920px] mx-auto px-6 md:px-12">
|
<div class="max-w-[1920px] mx-auto px-6 md:px-12">
|
||||||
<div class="flex justify-between items-center h-24">
|
<div class="flex justify-between items-center h-24">
|
||||||
<div class="flex items-center md:hidden">
|
<div class="flex items-center md:hidden">
|
||||||
<button class="text-text-main dark:text-white p-2" type="button">
|
<button class="text-text-main dark:text-white p-2" type="button">
|
||||||
<span class="material-symbols-outlined">menu</span>
|
<span class="material-symbols-outlined">menu</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<a class="font-display text-4xl md:text-5xl font-light tracking-widest uppercase text-text-main dark:text-white" href="#">
|
<a class="font-display text-4xl md:text-5xl font-light tracking-widest uppercase text-text-main dark:text-white" href="#">
|
||||||
IKKAI
|
IKKAI
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<nav class="hidden md:flex space-x-16">
|
<nav class="hidden md:flex space-x-16">
|
||||||
<a class="text-xs uppercase tracking-[0.15em] text-text-muted dark:text-gray-400 hover:text-primary dark:hover:text-white transition-colors duration-300" href="#">Collections</a>
|
<a class="text-xs uppercase tracking-[0.15em] text-text-muted dark:text-gray-400 hover:text-primary dark:hover:text-white transition-colors duration-300" href="#">Collections</a>
|
||||||
<a class="text-xs uppercase tracking-[0.15em] text-text-muted dark:text-gray-400 hover:text-primary dark:hover:text-white transition-colors duration-300" href="#">Atelier</a>
|
<a class="text-xs uppercase tracking-[0.15em] text-text-muted dark:text-gray-400 hover:text-primary dark:hover:text-white transition-colors duration-300" href="#">Atelier</a>
|
||||||
<a class="text-xs uppercase tracking-[0.15em] text-text-muted dark:text-gray-400 hover:text-primary dark:hover:text-white transition-colors duration-300" href="#">Editorial</a>
|
<a class="text-xs uppercase tracking-[0.15em] text-text-muted dark:text-gray-400 hover:text-primary dark:hover:text-white transition-colors duration-300" href="#">Editorial</a>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="flex items-center space-x-6 text-text-main dark:text-white">
|
<div class="flex items-center space-x-6 text-text-main dark:text-white">
|
||||||
<button class="hover:text-text-muted transition-colors hidden sm:block">
|
<button class="hover:text-text-muted transition-colors hidden sm:block">
|
||||||
<span class="material-symbols-outlined text-xl font-light">search</span>
|
<span class="material-symbols-outlined text-xl font-light">search</span>
|
||||||
</button>
|
</button>
|
||||||
<a class="hover:text-text-muted transition-colors relative group" href="#">
|
<a class="hover:text-text-muted transition-colors relative group" href="#">
|
||||||
<span class="material-symbols-outlined text-xl font-light">shopping_bag</span>
|
<span class="material-symbols-outlined text-xl font-light">shopping_bag</span>
|
||||||
<span class="absolute -top-1 -right-2 bg-text-main dark:bg-white text-white dark:text-black text-[9px] w-4 h-4 flex items-center justify-center rounded-full opacity-0 group-hover:opacity-100 transition-opacity">2</span>
|
<span class="absolute -top-1 -right-2 bg-text-main dark:bg-white text-white dark:text-black text-[9px] w-4 h-4 flex items-center justify-center rounded-full opacity-0 group-hover:opacity-100 transition-opacity">2</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<section class="relative min-h-screen pt-24 w-full flex flex-col md:flex-row items-center overflow-hidden bg-background-light dark:bg-background-dark">
|
<section class="relative min-h-screen pt-24 w-full flex flex-col md:flex-row items-center overflow-hidden bg-background-light dark:bg-background-dark">
|
||||||
<div class="w-full md:w-5/12 h-full flex flex-col justify-center px-6 md:pl-20 md:pr-12 py-20 z-10">
|
<div class="w-full md:w-5/12 h-full flex flex-col justify-center px-6 md:pl-20 md:pr-12 py-20 z-10">
|
||||||
<span class="font-body text-xs uppercase tracking-[0.3em] text-text-muted mb-8 ml-1 block">New Collection 2024</span>
|
<span class="font-body text-xs uppercase tracking-[0.3em] text-text-muted mb-8 ml-1 block">New Collection 2024</span>
|
||||||
<h1 class="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">
|
<h1 class="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 class="italic pl-12 md:pl-20 text-text-muted">of</span> Earth
|
Form <br/><span class="italic pl-12 md:pl-20 text-text-muted">of</span> Earth
|
||||||
</h1>
|
</h1>
|
||||||
<p class="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">
|
<p class="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.
|
Discover the imperfect perfection of hand-thrown stoneware. Pieces that bring silence and intention to your daily rituals.
|
||||||
</p>
|
</p>
|
||||||
<div class="ml-1">
|
<div class="ml-1">
|
||||||
<a class="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="#">
|
<a class="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="#">
|
||||||
View The Collection
|
View The Collection
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full md:w-7/12 h-[60vh] md:h-screen relative">
|
<div class="w-full md:w-7/12 h-[60vh] md:h-screen relative">
|
||||||
<div class="absolute inset-0 bg-stone-200 dark:bg-stone-800">
|
<div class="absolute inset-0 bg-stone-200 dark:bg-stone-800">
|
||||||
<img alt="Minimalist ceramic vase with single branch" class="w-full h-full object-cover object-center brightness-95" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBvZAnP7XY-eGn5eL8LHgtIhpGoP7m9sPeESpuqYuRRomwq-VbkWnk3og2Zz3Y008nJOg3m1_HX59c-oDrJrtdXltWuBceYV0538LLAwbtslwnO_7BOuBw5y4v-4m9JtFou3lwflr2jbi_6zW8EZaxmGL6_EqVOkYct5HiXbw0JYTYhxPegtBET_-AeTOqJHvuDJGSzRAImHVh74ucDQgnl6QzlQZ17IKZU8o-1SdfLMvL8EvTb-jAeb7wv-wHpLSPbHK4XwYiVszk"/>
|
<img alt="Minimalist ceramic vase with single branch" class="w-full h-full object-cover object-center brightness-95" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBvZAnP7XY-eGn5eL8LHgtIhpGoP7m9sPeESpuqYuRRomwq-VbkWnk3og2Zz3Y008nJOg3m1_HX59c-oDrJrtdXltWuBceYV0538LLAwbtslwnO_7BOuBw5y4v-4m9JtFou3lwflr2jbi_6zW8EZaxmGL6_EqVOkYct5HiXbw0JYTYhxPegtBET_-AeTOqJHvuDJGSzRAImHVh74ucDQgnl6QzlQZ17IKZU8o-1SdfLMvL8EvTb-jAeb7wv-wHpLSPbHK4XwYiVszk"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute bottom-10 left-10 md:left-auto md:right-20 bg-background-light/90 dark:bg-background-dark/90 backdrop-blur p-6 max-w-xs hidden md:block shadow-sm">
|
<div class="absolute bottom-10 left-10 md:left-auto md:right-20 bg-background-light/90 dark:bg-background-dark/90 backdrop-blur p-6 max-w-xs hidden md:block shadow-sm">
|
||||||
<p class="font-display italic text-xl text-text-main dark:text-gray-200">"In emptiness, there is fullness."</p>
|
<p class="font-display italic text-xl text-text-main dark:text-gray-200">"In emptiness, there is fullness."</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="py-32 md:py-48 bg-sage dark:bg-stone-900 overflow-hidden relative transition-colors duration-500">
|
<section class="py-32 md:py-48 bg-sage dark:bg-stone-900 overflow-hidden relative transition-colors duration-500">
|
||||||
<div class="max-w-[1800px] mx-auto px-6">
|
<div class="max-w-[1800px] mx-auto px-6">
|
||||||
<div class="relative flex flex-col md:block">
|
<div class="relative flex flex-col md:block">
|
||||||
<div class="hidden md:block absolute -top-24 left-10 z-0 select-none opacity-[0.03] dark:opacity-[0.05] pointer-events-none">
|
<div class="hidden md:block absolute -top-24 left-10 z-0 select-none opacity-[0.03] dark:opacity-[0.05] pointer-events-none">
|
||||||
<span class="font-display text-[20rem] leading-none text-black dark:text-white">CRAFT</span>
|
<span class="font-display text-[20rem] leading-none text-black dark:text-white">CRAFT</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full md:w-3/5 h-[600px] md:h-[800px] ml-auto relative z-10 parallax-bg shadow-2xl" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAwcT6-AV8qpFzjZnVg9E60XgAEjN8kSfvJhBviAQySkGqDm950ofKMSXpKUvN44YobZIMvBeV-QcLz_xE7hQKdYdSIjoPasavbYAMbtqN4XySDFYqxgVq34e2R0BJJqX0WAzSFTcTd1WbnDjhlb8Vr8NyVtoB-09ArCsO6ZwnpvplPNHWwqzA0pebI6c32n8BPTMwvL5MuqUV8T5-tEw6MiNVyXJKGX-EIAxboK60MBn0tdYRBneueLDgcjvJ-s7R6yVBe1H4j1kc');">
|
<div class="w-full md:w-3/5 h-[600px] md:h-[800px] ml-auto relative z-10 parallax-bg shadow-2xl" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAwcT6-AV8qpFzjZnVg9E60XgAEjN8kSfvJhBviAQySkGqDm950ofKMSXpKUvN44YobZIMvBeV-QcLz_xE7hQKdYdSIjoPasavbYAMbtqN4XySDFYqxgVq34e2R0BJJqX0WAzSFTcTd1WbnDjhlb8Vr8NyVtoB-09ArCsO6ZwnpvplPNHWwqzA0pebI6c32n8BPTMwvL5MuqUV8T5-tEw6MiNVyXJKGX-EIAxboK60MBn0tdYRBneueLDgcjvJ-s7R6yVBe1H4j1kc');">
|
||||||
</div>
|
</div>
|
||||||
<div class="relative z-20 mt-[-100px] md:mt-0 md:absolute md:top-1/2 md:left-20 md:-translate-y-1/2 bg-white dark:bg-stone-900 p-12 md:p-20 shadow-xl max-w-xl mx-auto md:mx-0">
|
<div class="relative z-20 mt-[-100px] md:mt-0 md:absolute md:top-1/2 md:left-20 md:-translate-y-1/2 bg-white dark:bg-stone-900 p-12 md:p-20 shadow-xl max-w-xl mx-auto md:mx-0">
|
||||||
<span class="block w-12 h-[1px] bg-text-main mb-8"></span>
|
<span class="block w-12 h-[1px] bg-text-main mb-8"></span>
|
||||||
<h2 class="font-display text-4xl md:text-5xl font-light mb-8 text-text-main dark:text-white leading-tight">
|
<h2 class="font-display text-4xl md:text-5xl font-light mb-8 text-text-main dark:text-white leading-tight">
|
||||||
The Art of <br/><i class="font-thin">Slow Living</i>
|
The Art of <br/><i class="font-thin">Slow Living</i>
|
||||||
</h2>
|
</h2>
|
||||||
<p class="font-body font-light text-text-muted dark:text-gray-400 mb-10 leading-loose text-sm">
|
<p class="font-body font-light text-text-muted dark:text-gray-400 mb-10 leading-loose text-sm">
|
||||||
We believe in the beauty of handmade objects. Our collection features a curated selection of ceramics designed to elevate the everyday. From sturdy mugs for your morning coffee to elegant vases that breathe life into a room, each piece is crafted with patience and intention.
|
We believe in the beauty of handmade objects. Our collection features a curated selection of ceramics designed to elevate the everyday. From sturdy mugs for your morning coffee to elegant vases that breathe life into a room, each piece is crafted with patience and intention.
|
||||||
</p>
|
</p>
|
||||||
<a class="group inline-flex items-center text-xs uppercase tracking-[0.2em] text-text-main dark:text-white font-medium" href="#">
|
<a class="group inline-flex items-center text-xs uppercase tracking-[0.2em] text-text-main dark:text-white font-medium" href="#">
|
||||||
Read Our Story <span class="ml-2 group-hover:translate-x-1 transition-transform">→</span>
|
Read Our Story <span class="ml-2 group-hover:translate-x-1 transition-transform">→</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="py-32 bg-warm-grey dark:bg-[#141210] transition-colors duration-500">
|
<section class="py-32 bg-warm-grey dark:bg-[#141210] transition-colors duration-500">
|
||||||
<div class="max-w-[1920px] mx-auto px-6 md:px-12">
|
<div class="max-w-[1920px] mx-auto px-6 md:px-12">
|
||||||
<div class="flex flex-col md:flex-row justify-between items-end mb-20 md:mb-32 px-4">
|
<div class="flex flex-col md:flex-row justify-between items-end mb-20 md:mb-32 px-4">
|
||||||
<h2 class="font-display text-5xl md:text-7xl font-thin text-text-main dark:text-white">Curated <span class="italic text-text-muted">Editions</span></h2>
|
<h2 class="font-display text-5xl md:text-7xl font-thin text-text-main dark:text-white">Curated <span class="italic text-text-muted">Editions</span></h2>
|
||||||
<p class="hidden md:block font-body text-sm text-text-muted max-w-xs leading-relaxed text-right">
|
<p class="hidden md:block font-body text-sm text-text-muted max-w-xs leading-relaxed text-right">
|
||||||
Explore our seasonal collections, fired in small batches.
|
Explore our seasonal collections, fired in small batches.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 lg:gap-16">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 lg:gap-16">
|
||||||
<div class="flex flex-col space-y-16 md:space-y-32">
|
<div class="flex flex-col space-y-16 md:space-y-32">
|
||||||
<a class="group block cursor-pointer" href="#">
|
<a class="group block cursor-pointer" href="#">
|
||||||
<div class="relative overflow-hidden aspect-[3/4] mb-6">
|
<div class="relative overflow-hidden aspect-[3/4] mb-6">
|
||||||
<img alt="Tableware collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDEMG0U2yN-srAgpA4aSXewbFyRyFWnm181AquAJCRzLwgPHNHbs-fxFKQ8DMbozvyRU-s0LUPRKoZtht1-Lp3RDOfKE3jCrAD_A4tl9BXwHGUcAPWj0jBq3C9plosFkHIzYUDBtbq_Azg3RK2csufB9tH_tIJhMW--_IIfZeAltM9sgTD5wAPRPIUyV-0iemF2eWLZnx0IfTLZSkN930lHZ6aHxWChqHqoVMUTdYxqHPt0tpUW3C082em7_4fuoRpWdf4_flYYoO4"/>
|
<img alt="Tableware collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDEMG0U2yN-srAgpA4aSXewbFyRyFWnm181AquAJCRzLwgPHNHbs-fxFKQ8DMbozvyRU-s0LUPRKoZtht1-Lp3RDOfKE3jCrAD_A4tl9BXwHGUcAPWj0jBq3C9plosFkHIzYUDBtbq_Azg3RK2csufB9tH_tIJhMW--_IIfZeAltM9sgTD5wAPRPIUyV-0iemF2eWLZnx0IfTLZSkN930lHZ6aHxWChqHqoVMUTdYxqHPt0tpUW3C082em7_4fuoRpWdf4_flYYoO4"/>
|
||||||
<div class="absolute inset-0 bg-black/10 group-hover:bg-transparent transition-colors duration-500"></div>
|
<div class="absolute inset-0 bg-black/10 group-hover:bg-transparent transition-colors duration-500"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
||||||
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Tableware</h3>
|
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Tableware</h3>
|
||||||
<span class="text-xs uppercase tracking-widest text-text-muted">01</span>
|
<span class="text-xs uppercase tracking-widest text-text-muted">01</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="group block cursor-pointer" href="#">
|
<a class="group block cursor-pointer" href="#">
|
||||||
<div class="relative overflow-hidden aspect-[4/3] mb-6">
|
<div class="relative overflow-hidden aspect-[4/3] mb-6">
|
||||||
<img alt="Lamp Shades collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAz5MOY7i5TxXxrGVaW7nItrMPEhwnNz5VkQ7BwzWHUBMfV3j8A42PekcfAMOXu7nP2pX7m-Trx0lBWwFq4RuDfJMghT-DwyJAP4nT2sTCgX_WosvcMQfj5koFU-CLX7CMboAxAPXWUWe3Q8xU4Zl0kysFKLG34fR_GaRlN0diovvLg1SQ6fLq2dMRg2o523onwafjD0f6XBDxbtWBsnfIp_2U1_0zFahOkW2JyyJhIZFVCTiP61CY2rkwqtmupBjzzY7iKcMtszhE"/>
|
<img alt="Lamp Shades collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAz5MOY7i5TxXxrGVaW7nItrMPEhwnNz5VkQ7BwzWHUBMfV3j8A42PekcfAMOXu7nP2pX7m-Trx0lBWwFq4RuDfJMghT-DwyJAP4nT2sTCgX_WosvcMQfj5koFU-CLX7CMboAxAPXWUWe3Q8xU4Zl0kysFKLG34fR_GaRlN0diovvLg1SQ6fLq2dMRg2o523onwafjD0f6XBDxbtWBsnfIp_2U1_0zFahOkW2JyyJhIZFVCTiP61CY2rkwqtmupBjzzY7iKcMtszhE"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
||||||
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Lighting</h3>
|
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Lighting</h3>
|
||||||
<span class="text-xs uppercase tracking-widest text-text-muted">04</span>
|
<span class="text-xs uppercase tracking-widest text-text-muted">04</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col space-y-16 md:space-y-32 md:pt-32">
|
<div class="flex flex-col space-y-16 md:space-y-32 md:pt-32">
|
||||||
<a class="group block cursor-pointer" href="#">
|
<a class="group block cursor-pointer" href="#">
|
||||||
<div class="relative overflow-hidden aspect-square mb-6">
|
<div class="relative overflow-hidden aspect-square mb-6">
|
||||||
<img alt="Home Decor collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBV8jgjafGxXCrSmiIUCN5Zwqv0cl-Ivw5hjQoa5ejFBx3a0C7zgeI_pywBpee7f0scHB_zrYQbI0zryX0P_F2w_xefVbl_8vkvSRMPhsqrs_z9u16FlDVgmXX9_PxhC8oRWZmGbtHsvXhfDEtvAi94tBJeQYTdG2a-XJ7gB0F8GLyvVl7_NHu9iB_TyVhbKIOv354VUmcNAehnGfuK0fTtAjQr0qxaHt8CD9pLJvfTeVJZF2VPRgToY5dN4eqRTRJrQPuLIW2aP9k"/>
|
<img alt="Home Decor collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBV8jgjafGxXCrSmiIUCN5Zwqv0cl-Ivw5hjQoa5ejFBx3a0C7zgeI_pywBpee7f0scHB_zrYQbI0zryX0P_F2w_xefVbl_8vkvSRMPhsqrs_z9u16FlDVgmXX9_PxhC8oRWZmGbtHsvXhfDEtvAi94tBJeQYTdG2a-XJ7gB0F8GLyvVl7_NHu9iB_TyVhbKIOv354VUmcNAehnGfuK0fTtAjQr0qxaHt8CD9pLJvfTeVJZF2VPRgToY5dN4eqRTRJrQPuLIW2aP9k"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
||||||
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Vases</h3>
|
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Vases</h3>
|
||||||
<span class="text-xs uppercase tracking-widest text-text-muted">02</span>
|
<span class="text-xs uppercase tracking-widest text-text-muted">02</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="group block cursor-pointer" href="#">
|
<a class="group block cursor-pointer" href="#">
|
||||||
<div class="relative overflow-hidden aspect-[3/4] mb-6">
|
<div class="relative overflow-hidden aspect-[3/4] mb-6">
|
||||||
<img alt="Accessories collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDlTM_GLiQWReZzrfgwPZXJ2OjRaelPwrF2URWBLiqQjOdyxy0onUB9FoX4kcIzTnQSRIfRZsg6Dt5BS6j5bE6SYhdkZ60HAOZJNybnBvZqfICwldKNMiTg9-fm4X1otiHO8vO_Hr-DuwsaE818YSDiW2vyVH947T8peRurHz-sYZu9gJgq9R4D3BtLrdbf9R6MaYmqGZ47NAwHV1BHicOSMFGxfhK-p5exDM963E8qBTwl3PEXcRdnAq6-B-ada0XJ3jz8iA4Cavo"/>
|
<img alt="Accessories collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDlTM_GLiQWReZzrfgwPZXJ2OjRaelPwrF2URWBLiqQjOdyxy0onUB9FoX4kcIzTnQSRIfRZsg6Dt5BS6j5bE6SYhdkZ60HAOZJNybnBvZqfICwldKNMiTg9-fm4X1otiHO8vO_Hr-DuwsaE818YSDiW2vyVH947T8peRurHz-sYZu9gJgq9R4D3BtLrdbf9R6MaYmqGZ47NAwHV1BHicOSMFGxfhK-p5exDM963E8qBTwl3PEXcRdnAq6-B-ada0XJ3jz8iA4Cavo"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
||||||
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Serving</h3>
|
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Serving</h3>
|
||||||
<span class="text-xs uppercase tracking-widest text-text-muted">05</span>
|
<span class="text-xs uppercase tracking-widest text-text-muted">05</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col space-y-16 md:space-y-32 md:pt-16 lg:pt-0">
|
<div class="flex flex-col space-y-16 md:space-y-32 md:pt-16 lg:pt-0">
|
||||||
<a class="group block cursor-pointer" href="#">
|
<a class="group block cursor-pointer" href="#">
|
||||||
<div class="relative overflow-hidden aspect-[3/5] mb-6">
|
<div class="relative overflow-hidden aspect-[3/5] mb-6">
|
||||||
<img alt="Matcha Bowls collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuD_jVXnz1bLom_g2aBEGMY2fmAe2vuv4pwP_NdhGGqtKhcIr36eQ-BQ5d3cMXXjIDURtvBt9juNVX8kJG1T404125GGwEoQmMCD4IawdGY7hDBeByI8PG9Z8Ioc-skCG9X5bdU9-O4PS6KBglPV8fnkyG6FjPkN0RdGvHWMZQ6iInrJhOqiqX3r-6YvmIpGSi5FoXyFnmcfLnf1faHq8kWIMuv0WgLHSlOFlB5MeIJAQwvuk5Gbk4drXCt2heYy5WRWIdutVdiQOa4"/>
|
<img alt="Matcha Bowls collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuD_jVXnz1bLom_g2aBEGMY2fmAe2vuv4pwP_NdhGGqtKhcIr36eQ-BQ5d3cMXXjIDURtvBt9juNVX8kJG1T404125GGwEoQmMCD4IawdGY7hDBeByI8PG9Z8Ioc-skCG9X5bdU9-O4PS6KBglPV8fnkyG6FjPkN0RdGvHWMZQ6iInrJhOqiqX3r-6YvmIpGSi5FoXyFnmcfLnf1faHq8kWIMuv0WgLHSlOFlB5MeIJAQwvuk5Gbk4drXCt2heYy5WRWIdutVdiQOa4"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
||||||
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Kitchenware</h3>
|
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Kitchenware</h3>
|
||||||
<span class="text-xs uppercase tracking-widest text-text-muted">03</span>
|
<span class="text-xs uppercase tracking-widest text-text-muted">03</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="group block cursor-pointer" href="#">
|
<a class="group block cursor-pointer" href="#">
|
||||||
<div class="relative overflow-hidden aspect-square mb-6">
|
<div class="relative overflow-hidden aspect-square mb-6">
|
||||||
<img alt="Furoshiki collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAI1p48nya5N3xLUE8fx6a0Cwniu_QuS5yo-wq6PE1W77n0orf57QyF1g4426uqGtiv02HHHXd40Sq81usdXnpOqvLiviW_gSGdtlorcOpaSl6R8k23cG_I-5v4pPVJiaTPqrhK1U3VtxLX5Bpj8x7NOtZT4K1jtI4NHt-S1A0GvBjM7jCfH_0y8Xw8L_R5br8I8_KmdbC7ACaNd4OAZUpJdt4UUANVy664jG4m9dZshHpa8Og4aFzZ1CRxmQExSVEzc0CKZ9GSLB0"/>
|
<img alt="Furoshiki collection" class="w-full h-full object-cover transition-transform duration-[1.5s] ease-out group-hover:scale-110" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAI1p48nya5N3xLUE8fx6a0Cwniu_QuS5yo-wq6PE1W77n0orf57QyF1g4426uqGtiv02HHHXd40Sq81usdXnpOqvLiviW_gSGdtlorcOpaSl6R8k23cG_I-5v4pPVJiaTPqrhK1U3VtxLX5Bpj8x7NOtZT4K1jtI4NHt-S1A0GvBjM7jCfH_0y8Xw8L_R5br8I8_KmdbC7ACaNd4OAZUpJdt4UUANVy664jG4m9dZshHpa8Og4aFzZ1CRxmQExSVEzc0CKZ9GSLB0"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
<div class="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4">
|
||||||
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Textiles</h3>
|
<h3 class="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all">Textiles</h3>
|
||||||
<span class="text-xs uppercase tracking-widest text-text-muted">06</span>
|
<span class="text-xs uppercase tracking-widest text-text-muted">06</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="py-32 bg-clay-dark dark:bg-black border-y border-stone-800/50 transition-colors duration-500">
|
<section class="py-32 bg-clay-dark dark:bg-black border-y border-stone-800/50 transition-colors duration-500">
|
||||||
<div class="max-w-5xl mx-auto px-6 text-center">
|
<div class="max-w-5xl mx-auto px-6 text-center">
|
||||||
<span class="material-symbols-outlined text-4xl mb-8 text-stone-500 font-thin">format_quote</span>
|
<span class="material-symbols-outlined text-4xl mb-8 text-stone-500 font-thin">format_quote</span>
|
||||||
<h3 class="font-display text-3xl md:text-5xl font-thin leading-snug text-stone-100 dark:text-stone-200 mb-10 italic">
|
<h3 class="font-display text-3xl md:text-5xl font-thin leading-snug text-stone-100 dark:text-stone-200 mb-10 italic">
|
||||||
"My pottery is designed to be both beautiful and practical. From minimalist vases to durable dinner plates, each piece has its own character."
|
"My pottery is designed to be both beautiful and practical. From minimalist vases to durable dinner plates, each piece has its own character."
|
||||||
</h3>
|
</h3>
|
||||||
<p class="font-body text-xs uppercase tracking-[0.2em] text-stone-400">
|
<p class="font-body text-xs uppercase tracking-[0.2em] text-stone-400">
|
||||||
Anonymous — Verified Collector
|
Anonymous — Verified Collector
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="relative py-32 bg-terracotta-soft dark:bg-black overflow-hidden transition-colors duration-500">
|
<section class="relative py-32 bg-terracotta-soft dark:bg-black overflow-hidden transition-colors duration-500">
|
||||||
<div class="absolute inset-0 z-0 mix-blend-multiply opacity-30">
|
<div class="absolute inset-0 z-0 mix-blend-multiply opacity-30">
|
||||||
<img alt="Atmospheric studio background" class="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" class="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>
|
||||||
<div class="max-w-[1920px] mx-auto px-6 md:px-12 relative z-10">
|
<div class="max-w-[1920px] mx-auto px-6 md:px-12 relative z-10">
|
||||||
<div class="flex justify-between items-baseline mb-20 border-b border-text-main/20 dark:border-gray-800 pb-6">
|
<div class="flex justify-between items-baseline mb-20 border-b border-text-main/20 dark:border-gray-800 pb-6">
|
||||||
<h2 class="font-display text-6xl font-thin text-text-main dark:text-white">The <span class="italic">Journal</span></h2>
|
<h2 class="font-display text-6xl font-thin text-text-main dark:text-white">The <span class="italic">Journal</span></h2>
|
||||||
<a class="text-xs uppercase tracking-[0.2em] text-text-main dark:text-white hover:text-text-muted transition-colors" href="#">View Archive</a>
|
<a class="text-xs uppercase tracking-[0.2em] text-text-main dark:text-white hover:text-text-muted transition-colors" href="#">View Archive</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-12">
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-12">
|
||||||
<article class="group cursor-pointer">
|
<article class="group cursor-pointer">
|
||||||
<div class="relative h-[500px] overflow-hidden mb-8 shadow-md">
|
<div class="relative h-[500px] overflow-hidden mb-8 shadow-md">
|
||||||
<img alt="Pottery workshop" class="w-full h-full object-cover transition-transform duration-[1.5s] group-hover:scale-105" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAipMlYLTcRT_hdc3VePfFIlrA56VzZ5G2y3gcRfmIZMERwGFKq2N19Gqo6mw7uZowXmjl2eJ89TI3Mcud2OyOfadO3mPVF_v0sI0OHupqM49WEFcWzH-Wbu3DL6bQ46F2Y8SIAk-NUQy8psjcIdBKRrM8fqdn4eOPANYTXpVxkLMAm4R0Axy4aEKNdmj917ZKKTxvXB-J8nGlITJkJ-ua7XcZOwGnfK5ttzyWW35A0oOSffCf972gmpV27wrVQgYJNLS7UyDdyQIQ"/>
|
<img alt="Pottery workshop" class="w-full h-full object-cover transition-transform duration-[1.5s] group-hover:scale-105" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAipMlYLTcRT_hdc3VePfFIlrA56VzZ5G2y3gcRfmIZMERwGFKq2N19Gqo6mw7uZowXmjl2eJ89TI3Mcud2OyOfadO3mPVF_v0sI0OHupqM49WEFcWzH-Wbu3DL6bQ46F2Y8SIAk-NUQy8psjcIdBKRrM8fqdn4eOPANYTXpVxkLMAm4R0Axy4aEKNdmj917ZKKTxvXB-J8nGlITJkJ-ua7XcZOwGnfK5ttzyWW35A0oOSffCf972gmpV27wrVQgYJNLS7UyDdyQIQ"/>
|
||||||
<div class="absolute inset-0 bg-black/10 group-hover:bg-black/0 transition-colors duration-500"></div>
|
<div class="absolute inset-0 bg-black/10 group-hover:bg-black/0 transition-colors duration-500"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex items-center space-x-4 mb-4">
|
<div class="flex items-center space-x-4 mb-4">
|
||||||
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-2 py-1 rounded-full">Studio</span>
|
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-2 py-1 rounded-full">Studio</span>
|
||||||
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted">Oct 03</span>
|
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted">Oct 03</span>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="font-display text-3xl text-text-main dark:text-white mb-4 leading-tight group-hover:underline decoration-1 underline-offset-4">Product Photography for Small Businesses</h3>
|
<h3 class="font-display text-3xl text-text-main dark:text-white mb-4 leading-tight group-hover:underline decoration-1 underline-offset-4">Product Photography for Small Businesses</h3>
|
||||||
<p class="text-sm font-light text-text-muted dark:text-gray-400 leading-loose max-w-sm">
|
<p class="text-sm font-light text-text-muted dark:text-gray-400 leading-loose max-w-sm">
|
||||||
Learning that beautiful products aren't enough on their own — you also need beautiful photos to tell the story.
|
Learning that beautiful products aren't enough on their own — you also need beautiful photos to tell the story.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<article class="group cursor-pointer lg:mt-20">
|
<article class="group cursor-pointer lg:mt-20">
|
||||||
<div class="relative h-[500px] overflow-hidden mb-8 shadow-md">
|
<div class="relative h-[500px] overflow-hidden mb-8 shadow-md">
|
||||||
<img alt="Packing ceramics" class="w-full h-full object-cover transition-transform duration-[1.5s] group-hover:scale-105" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAaWGnX_NYT3S_lOflL2NJZGbWge4AAkvra4ymvF8ag-c1UKsOAIB-rsLVQXW5xIlPZipDiK8-ysPyv22xdgsvzs4EOXSSCcrT4Lb2YCe0u5orxRaZEA5TgxeoKq15zaWKSlmnHyPGjPd_7yglpfO13eZmbU5KaxFJ1KGO0UAxoO9BpsyCYgbgINMoSz3epGe5ZdwBWRH-5KCzjoLuXimFTLcd5bqg9T1YofTxgy2hWBMJzKkafyEniq8dP6hMmfNCLVcCHHHx0hRU"/>
|
<img alt="Packing ceramics" class="w-full h-full object-cover transition-transform duration-[1.5s] group-hover:scale-105" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAaWGnX_NYT3S_lOflL2NJZGbWge4AAkvra4ymvF8ag-c1UKsOAIB-rsLVQXW5xIlPZipDiK8-ysPyv22xdgsvzs4EOXSSCcrT4Lb2YCe0u5orxRaZEA5TgxeoKq15zaWKSlmnHyPGjPd_7yglpfO13eZmbU5KaxFJ1KGO0UAxoO9BpsyCYgbgINMoSz3epGe5ZdwBWRH-5KCzjoLuXimFTLcd5bqg9T1YofTxgy2hWBMJzKkafyEniq8dP6hMmfNCLVcCHHHx0hRU"/>
|
||||||
<div class="absolute inset-0 bg-black/10 group-hover:bg-black/0 transition-colors duration-500"></div>
|
<div class="absolute inset-0 bg-black/10 group-hover:bg-black/0 transition-colors duration-500"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex items-center space-x-4 mb-4">
|
<div class="flex items-center space-x-4 mb-4">
|
||||||
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-2 py-1 rounded-full">Guide</span>
|
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-2 py-1 rounded-full">Guide</span>
|
||||||
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted">Jul 15</span>
|
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted">Jul 15</span>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="font-display text-3xl text-text-main dark:text-white mb-4 leading-tight group-hover:underline decoration-1 underline-offset-4">The Art of Packaging</h3>
|
<h3 class="font-display text-3xl text-text-main dark:text-white mb-4 leading-tight group-hover:underline decoration-1 underline-offset-4">The Art of Packaging</h3>
|
||||||
<p class="text-sm font-light text-text-muted dark:text-gray-400 leading-loose max-w-sm">
|
<p class="text-sm font-light text-text-muted dark:text-gray-400 leading-loose max-w-sm">
|
||||||
A practical guide for potters who want to package and send their handmade ceramics with care and confidence.
|
A practical guide for potters who want to package and send their handmade ceramics with care and confidence.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<article class="group cursor-pointer">
|
<article class="group cursor-pointer">
|
||||||
<div class="relative h-[500px] overflow-hidden mb-8 shadow-md">
|
<div class="relative h-[500px] overflow-hidden mb-8 shadow-md">
|
||||||
<img alt="Pottery tools" class="w-full h-full object-cover transition-transform duration-[1.5s] group-hover:scale-105" src="https://lh3.googleusercontent.com/aida-public/AB6AXuB8NOE5fGfN4d87cbcB27_Sh-nrlZlqxsTlYKbCZk98SoL-gHsPSWFNuxd1DxBq0g8Qysh0RBZ_btu-_WaH68UjV8SXPUalyxREvUqao4oXmra--pWAsaooWwKvWCzReYZ8kj7G-KIYIAo5mqudzB8n9C6-HVTNPPx9QgZHr_YsojMxlmmVcQ5bqk7-Lp0KtSAiVIPD2-1UE1dMGnkVSLUXKdgA65JIh8M3TtNkaJTGONuFKoTERrYOWe7u2BILnqyukTzlNcvK7Sc"/>
|
<img alt="Pottery tools" class="w-full h-full object-cover transition-transform duration-[1.5s] group-hover:scale-105" src="https://lh3.googleusercontent.com/aida-public/AB6AXuB8NOE5fGfN4d87cbcB27_Sh-nrlZlqxsTlYKbCZk98SoL-gHsPSWFNuxd1DxBq0g8Qysh0RBZ_btu-_WaH68UjV8SXPUalyxREvUqao4oXmra--pWAsaooWwKvWCzReYZ8kj7G-KIYIAo5mqudzB8n9C6-HVTNPPx9QgZHr_YsojMxlmmVcQ5bqk7-Lp0KtSAiVIPD2-1UE1dMGnkVSLUXKdgA65JIh8M3TtNkaJTGONuFKoTERrYOWe7u2BILnqyukTzlNcvK7Sc"/>
|
||||||
<div class="absolute inset-0 bg-black/10 group-hover:bg-black/0 transition-colors duration-500"></div>
|
<div class="absolute inset-0 bg-black/10 group-hover:bg-black/0 transition-colors duration-500"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex items-center space-x-4 mb-4">
|
<div class="flex items-center space-x-4 mb-4">
|
||||||
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-2 py-1 rounded-full">Wellness</span>
|
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-2 py-1 rounded-full">Wellness</span>
|
||||||
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted">Jun 11</span>
|
<span class="text-[10px] uppercase tracking-[0.2em] text-text-muted">Jun 11</span>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="font-display text-3xl text-text-main dark:text-white mb-4 leading-tight group-hover:underline decoration-1 underline-offset-4">Finding Motivation in Clay</h3>
|
<h3 class="font-display text-3xl text-text-main dark:text-white mb-4 leading-tight group-hover:underline decoration-1 underline-offset-4">Finding Motivation in Clay</h3>
|
||||||
<p class="text-sm font-light text-text-muted dark:text-gray-400 leading-loose max-w-sm">
|
<p class="text-sm font-light text-text-muted dark:text-gray-400 leading-loose max-w-sm">
|
||||||
10 gentle, practical tips to help potters find motivation during slow or uncertain moments in the creative process.
|
10 gentle, practical tips to help potters find motivation during slow or uncertain moments in the creative process.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="py-20 bg-white dark:bg-background-dark">
|
<section class="py-20 bg-white dark:bg-background-dark">
|
||||||
<div class="max-w-[1920px] mx-auto px-4">
|
<div class="max-w-[1920px] mx-auto px-4">
|
||||||
<div class="flex justify-between items-center mb-8 px-2">
|
<div class="flex justify-between items-center mb-8 px-2">
|
||||||
<h4 class="font-display text-2xl text-text-main dark:text-white">@ikkai_ceramics</h4>
|
<h4 class="font-display text-2xl text-text-main dark:text-white">@ikkai_ceramics</h4>
|
||||||
<a class="text-xs uppercase tracking-widest text-text-muted hover:text-primary transition-colors" href="#">Follow</a>
|
<a class="text-xs uppercase tracking-widest text-text-muted hover:text-primary transition-colors" href="#">Follow</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 md:grid-cols-6 gap-px bg-stone-100 dark:bg-stone-800 border border-stone-100 dark:border-stone-800">
|
<div class="grid grid-cols-2 md:grid-cols-6 gap-px bg-stone-100 dark:bg-stone-800 border border-stone-100 dark:border-stone-800">
|
||||||
<div class="aspect-square relative group overflow-hidden">
|
<div class="aspect-square relative group overflow-hidden">
|
||||||
<img alt="Gallery image 1" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDGY1PXPxlhIz9qU-XbDrdJjrRnX1pFo8YpH3HM1Crq9C6iVApx-qFkpjTj_MDOXXrX4jprk69hA0fmwR2EdURQyKaBLDAdkIE3vLKCyTRMhgyGerlpsy6_KZkZs-9hiaoWZPBFzvBIGWZ0i7sfbbtkQdBGJfK30yftDOPjI1NJfzBtsKNMbYOnXfmm-6u7uiovrM54rtRNWzsxmcvhRKQebZIUrERvGGYsUvUVARYEzSs4thyJnMYROk0LmoCrJ03_QjDvLzy_zjw"/>
|
<img alt="Gallery image 1" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDGY1PXPxlhIz9qU-XbDrdJjrRnX1pFo8YpH3HM1Crq9C6iVApx-qFkpjTj_MDOXXrX4jprk69hA0fmwR2EdURQyKaBLDAdkIE3vLKCyTRMhgyGerlpsy6_KZkZs-9hiaoWZPBFzvBIGWZ0i7sfbbtkQdBGJfK30yftDOPjI1NJfzBtsKNMbYOnXfmm-6u7uiovrM54rtRNWzsxmcvhRKQebZIUrERvGGYsUvUVARYEzSs4thyJnMYROk0LmoCrJ03_QjDvLzy_zjw"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="aspect-square relative group overflow-hidden">
|
<div class="aspect-square relative group overflow-hidden">
|
||||||
<img alt="Gallery image 2" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBLafDd9RRuru_JkGfpxms6q-dFwzNrjKIazUT33dREB4eWcGSGtNYEKzTCgaRJYIGNvhE1z1tYSrr3ZMrMSQs_3oIJz2hrlYYq49EJaI8VD2YrM9akd50BsF3voaGW1yZFHM5S36ZbrCx3A8Id2wkDnlJ7TnUYdo76-TErMa6h94HxQYBSwLQESFBrPfDEi5Qf6MDfE-6i3HJNIYGS79zemso4U8mMRi-HA17y4ilifDoI2B4vc3ROE3HFbTVP6JxJxjklnlbMt28"/>
|
<img alt="Gallery image 2" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBLafDd9RRuru_JkGfpxms6q-dFwzNrjKIazUT33dREB4eWcGSGtNYEKzTCgaRJYIGNvhE1z1tYSrr3ZMrMSQs_3oIJz2hrlYYq49EJaI8VD2YrM9akd50BsF3voaGW1yZFHM5S36ZbrCx3A8Id2wkDnlJ7TnUYdo76-TErMa6h94HxQYBSwLQESFBrPfDEi5Qf6MDfE-6i3HJNIYGS79zemso4U8mMRi-HA17y4ilifDoI2B4vc3ROE3HFbTVP6JxJxjklnlbMt28"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="aspect-square relative group overflow-hidden">
|
<div class="aspect-square relative group overflow-hidden">
|
||||||
<img alt="Gallery image 3" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDGyeyp1BDBjdasLDmyEWUfA0eeSJ8qMmrlPS0IgsyNdW_-0NEbFZhO72z3kox8ys3T2PZOprxzbnDwBQfjdwLiISmT3eHil4LQgRNt35AucCh1b-BFmUjXB9vuQ-JAFY122ABF9AmGWIhKCH7HHJj-Pibiz4NlcEGf_-59GAtt_y4g6OzzZpBKzfZAXd2_h-2I3ZbMaNDYEVK9dZSwVSrUNxQEFRFLUoqoNm8_VqgfpwpXmGwSmEZfy-lfjnXDBF1AS3ipD7JR04E"/>
|
<img alt="Gallery image 3" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDGyeyp1BDBjdasLDmyEWUfA0eeSJ8qMmrlPS0IgsyNdW_-0NEbFZhO72z3kox8ys3T2PZOprxzbnDwBQfjdwLiISmT3eHil4LQgRNt35AucCh1b-BFmUjXB9vuQ-JAFY122ABF9AmGWIhKCH7HHJj-Pibiz4NlcEGf_-59GAtt_y4g6OzzZpBKzfZAXd2_h-2I3ZbMaNDYEVK9dZSwVSrUNxQEFRFLUoqoNm8_VqgfpwpXmGwSmEZfy-lfjnXDBF1AS3ipD7JR04E"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="aspect-square relative group overflow-hidden">
|
<div class="aspect-square relative group overflow-hidden">
|
||||||
<img alt="Gallery image 4" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBxM-yqwsAyWN-4sNS6FCNyry1QP6_yKpP7av1uPQTEArxL29_Ir1vfmlNJ50UMMYBRXHwSYUS9dwgO9hsccM8QKk0DybH5Hsa0k1oA1PSD2fIButt6JbICrrLhqC51S37PtN7vpPxtlqFPXQyaGEQl8r8eZbbROIqtUCGdWks_prak9UNTbeph0gHDa0Xlw8HtXRSgQCqONjRuWVWC9ynnqpqXXgLIeCvCVBUiQuXxCnhLsMDWOcw8sjaVSFKo1tRi9IUFRaXEGu0"/>
|
<img alt="Gallery image 4" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBxM-yqwsAyWN-4sNS6FCNyry1QP6_yKpP7av1uPQTEArxL29_Ir1vfmlNJ50UMMYBRXHwSYUS9dwgO9hsccM8QKk0DybH5Hsa0k1oA1PSD2fIButt6JbICrrLhqC51S37PtN7vpPxtlqFPXQyaGEQl8r8eZbbROIqtUCGdWks_prak9UNTbeph0gHDa0Xlw8HtXRSgQCqONjRuWVWC9ynnqpqXXgLIeCvCVBUiQuXxCnhLsMDWOcw8sjaVSFKo1tRi9IUFRaXEGu0"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="aspect-square relative group overflow-hidden">
|
<div class="aspect-square relative group overflow-hidden">
|
||||||
<img alt="Gallery image 5" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDD63SnScOyJcWSHLRucaOZ46wQpGV0c-ljb4DvnaURof8aTmuMD75dT1UUKXzDwWBUKGqu3nNlUUOXnEfjLE8pwAdvRKHhunOunZW5qbw35eY1vH14HXGqQSe90m7RUxku70QRlVS338tKQEAJ9TasOSte56oSEmKzUC0q9VF9P8GTl-0R8CcmvQ9hfwRIe34s2QUEwE96LYTREHdWZI6RRZojG8MTeV1qGFgFgjwEqnYbIGCFdW5TFMyTvkuPd1R0IBNfWZzhJkM"/>
|
<img alt="Gallery image 5" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDD63SnScOyJcWSHLRucaOZ46wQpGV0c-ljb4DvnaURof8aTmuMD75dT1UUKXzDwWBUKGqu3nNlUUOXnEfjLE8pwAdvRKHhunOunZW5qbw35eY1vH14HXGqQSe90m7RUxku70QRlVS338tKQEAJ9TasOSte56oSEmKzUC0q9VF9P8GTl-0R8CcmvQ9hfwRIe34s2QUEwE96LYTREHdWZI6RRZojG8MTeV1qGFgFgjwEqnYbIGCFdW5TFMyTvkuPd1R0IBNfWZzhJkM"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="aspect-square relative group overflow-hidden">
|
<div class="aspect-square relative group overflow-hidden">
|
||||||
<img alt="Gallery image 6" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBQAOyWAgHl3Uf9zP9-cmp1ZPrAw5wi7GP1div8GHtarOvO68Kn8069PqiCFYs91kLD1YWcb9tk3y9Fm12AmMfRpIIvNTvAWxkZ7xL0BWM_UZ5BPmvSVuRDXKcvg5_qQVXJOy5ub3Yu3oBqKhR617MhwY4F_Am0cNClmSgPaYHALRi-CB3_hlLdgXQhI0dP5j7yNqlrTxHHv34vRQWvg2_Htkum0XcSQHuK9-A89-Cgcz5-V-FzCjxKPzAROoN0OKL9YxjRXHkwQjk"/>
|
<img alt="Gallery image 6" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 grayscale hover:grayscale-0" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBQAOyWAgHl3Uf9zP9-cmp1ZPrAw5wi7GP1div8GHtarOvO68Kn8069PqiCFYs91kLD1YWcb9tk3y9Fm12AmMfRpIIvNTvAWxkZ7xL0BWM_UZ5BPmvSVuRDXKcvg5_qQVXJOy5ub3Yu3oBqKhR617MhwY4F_Am0cNClmSgPaYHALRi-CB3_hlLdgXQhI0dP5j7yNqlrTxHHv34vRQWvg2_Htkum0XcSQHuK9-A89-Cgcz5-V-FzCjxKPzAROoN0OKL9YxjRXHkwQjk"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<footer class="bg-primary dark:bg-black text-stone-400 py-24 px-6 md:px-12">
|
<footer class="bg-primary dark:bg-black text-stone-400 py-24 px-6 md:px-12">
|
||||||
<div class="max-w-[1920px] mx-auto">
|
<div class="max-w-[1920px] mx-auto">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-12 gap-12 border-b border-stone-700 pb-20 mb-12">
|
<div class="grid grid-cols-1 md:grid-cols-12 gap-12 border-b border-stone-700 pb-20 mb-12">
|
||||||
<div class="md:col-span-4">
|
<div class="md:col-span-4">
|
||||||
<a class="font-display text-4xl text-white block mb-8" href="#">IKKAI</a>
|
<a class="font-display text-4xl text-white block mb-8" href="#">IKKAI</a>
|
||||||
<p class="font-body text-sm font-light leading-loose max-w-sm mb-8">
|
<p class="font-body text-sm font-light leading-loose max-w-sm mb-8">
|
||||||
Handcrafted ceramics for the modern home. Created with intention, fired with patience, and delivered with care.
|
Handcrafted ceramics for the modern home. Created with intention, fired with patience, and delivered with care.
|
||||||
</p>
|
</p>
|
||||||
<form class="flex border-b border-stone-600 pb-2 max-w-xs">
|
<form class="flex border-b border-stone-600 pb-2 max-w-xs">
|
||||||
<input class="bg-transparent border-none text-white placeholder-stone-500 focus:ring-0 text-sm w-full p-0" placeholder="Join our newsletter" type="email"/>
|
<input class="bg-transparent border-none text-white placeholder-stone-500 focus:ring-0 text-sm w-full p-0" placeholder="Join our newsletter" type="email"/>
|
||||||
<button class="text-xs uppercase tracking-widest text-white hover:text-stone-300" type="submit">Subscribe</button>
|
<button class="text-xs uppercase tracking-widest text-white hover:text-stone-300" type="submit">Subscribe</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="md:col-span-2 md:col-start-7">
|
<div class="md:col-span-2 md:col-start-7">
|
||||||
<h4 class="text-white text-xs font-bold uppercase tracking-widest mb-8">Shop</h4>
|
<h4 class="text-white text-xs font-bold uppercase tracking-widest mb-8">Shop</h4>
|
||||||
<ul class="space-y-4 text-xs font-light tracking-widest uppercase">
|
<ul class="space-y-4 text-xs font-light tracking-widest uppercase">
|
||||||
<li><a class="hover:text-white transition-colors" href="#">All Ceramics</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">All Ceramics</a></li>
|
||||||
<li><a class="hover:text-white transition-colors" href="#">New Arrivals</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">New Arrivals</a></li>
|
||||||
<li><a class="hover:text-white transition-colors" href="#">Best Sellers</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">Best Sellers</a></li>
|
||||||
<li><a class="hover:text-white transition-colors" href="#">Gift Cards</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">Gift Cards</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
<h4 class="text-white text-xs font-bold uppercase tracking-widest mb-8">Company</h4>
|
<h4 class="text-white text-xs font-bold uppercase tracking-widest mb-8">Company</h4>
|
||||||
<ul class="space-y-4 text-xs font-light tracking-widest uppercase">
|
<ul class="space-y-4 text-xs font-light tracking-widest uppercase">
|
||||||
<li><a class="hover:text-white transition-colors" href="#">Our Story</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">Our Story</a></li>
|
||||||
<li><a class="hover:text-white transition-colors" href="#">Sustainability</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">Sustainability</a></li>
|
||||||
<li><a class="hover:text-white transition-colors" href="#">Careers</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">Careers</a></li>
|
||||||
<li><a class="hover:text-white transition-colors" href="#">Press</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">Press</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
<h4 class="text-white text-xs font-bold uppercase tracking-widest mb-8">Support</h4>
|
<h4 class="text-white text-xs font-bold uppercase tracking-widest mb-8">Support</h4>
|
||||||
<ul class="space-y-4 text-xs font-light tracking-widest uppercase">
|
<ul class="space-y-4 text-xs font-light tracking-widest uppercase">
|
||||||
<li><a class="hover:text-white transition-colors" href="#">FAQ</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">FAQ</a></li>
|
||||||
<li><a class="hover:text-white transition-colors" href="#">Shipping</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">Shipping</a></li>
|
||||||
<li><a class="hover:text-white transition-colors" href="#">Returns</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">Returns</a></li>
|
||||||
<li><a class="hover:text-white transition-colors" href="#">Contact</a></li>
|
<li><a class="hover:text-white transition-colors" href="#">Contact</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col md:flex-row justify-between items-center text-[10px] tracking-widest uppercase">
|
<div class="flex flex-col md:flex-row justify-between items-center text-[10px] tracking-widest uppercase">
|
||||||
<p>© 2024 IKKAI Ceramics. All rights reserved.</p>
|
<p>© 2024 IKKAI Ceramics. All rights reserved.</p>
|
||||||
<div class="flex space-x-6 mt-4 md:mt-0">
|
<div class="flex space-x-6 mt-4 md:mt-0">
|
||||||
<a class="hover:text-white" href="#">Privacy</a>
|
<a class="hover:text-white" href="#">Privacy</a>
|
||||||
<a class="hover:text-white" href="#">Terms</a>
|
<a class="hover:text-white" href="#">Terms</a>
|
||||||
<a class="hover:text-white" href="#">Cookies</a>
|
<a class="hover:text-white" href="#">Cookies</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body></html>
|
</body></html>
|
||||||
6
package-lock.json
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "hotschpotsh",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||