15 Commits

Author SHA1 Message Date
jyswee 328f429ea0 bake build infra: nginx.conf (deterministic /ship) 2026-08-21 20:41:18 +00:00
jyswee bca343d7c8 bake build infra: Dockerfile (deterministic /ship) 2026-08-21 20:41:17 +00:00
jyswee 1f9ef169c8 bake build infra: src/components/ui/sonner.tsx (deterministic /ship) 2026-08-21 20:41:16 +00:00
jyswee 16e66c1c95 bake build infra: src/index.css (deterministic /ship) 2026-08-21 20:41:15 +00:00
jyswee bb2adc762d bake build infra: src/main.tsx (deterministic /ship) 2026-08-21 20:41:14 +00:00
jyswee 029e2aa074 bake build infra: .gitignore (deterministic /ship) 2026-08-21 20:41:14 +00:00
jyswee 7b1292dfa1 bake build infra: index.html (deterministic /ship) 2026-08-21 20:41:12 +00:00
jyswee d57534480b bake build infra: postcss.config.js (deterministic /ship) 2026-08-21 20:41:11 +00:00
jyswee 532acc5eaf bake build infra: tailwind.config.ts (deterministic /ship) 2026-08-21 20:41:10 +00:00
jyswee 098d1dd250 bake build infra: tsconfig.node.json (deterministic /ship) 2026-08-21 20:41:09 +00:00
jyswee b711454af5 bake build infra: tsconfig.json (deterministic /ship) 2026-08-21 20:41:08 +00:00
jyswee b7d9cd767d bake build infra: vite.config.ts (deterministic /ship) 2026-08-21 20:41:07 +00:00
jyswee bb990079b4 bake build infra: package.json (deterministic /ship) 2026-08-21 20:41:06 +00:00
joewee cf8f85aac8 hctx: Rebrand complete 2026-08-21 14:35:15 +00:00
joewee 923d105fab Rebrand to Saffron Table - Persian-Mediterranean restaurant in Shoreditch
- Updated brand name from Aurum to Saffron Table across all pages
- Changed location from Mayfair to Shoreditch (127 Shoreditch High Street)
- Created Persian-Mediterranean menu with GBP prices
- Updated hero tagline: "Charcoal-Grilled Mezze & Saffron Cocktails"
- New menu items: hummus, lamb kofta, saffron salmon, Persian desserts
- Saffron cocktail program added
- Updated chef bio: Chef Yasmin Kashani
- New testimonials reflecting Persian-Mediterranean cuisine
- Updated opening hours: Tue-Sun 5pm-11pm
- Updated events and private dining rooms
- Modern stats: 5+ years, 15 mezze dishes, 8 saffron cocktails

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-21 14:35:05 +00:00
25 changed files with 760 additions and 76 deletions
+22
View File
@@ -0,0 +1,22 @@
# Logs
logs
*.log
npm-debug.log*
# Dependencies
node_modules/
# Build
dist/
dist-ssr/
# Editor
.vscode/
.idea/
# OS
.DS_Store
# Env
.env
.env.local
+20
View File
@@ -0,0 +1,20 @@
# h-context Rules
## Directory Structure
- σ state.md — Current state index (<2KB, LLN encoded, mutable)
- θ threads/ — Active workstreams (mutable)
- ε entries/ — Extracted artifacts (append-only)
- ρ raw/ — Source archive (append-only)
- ψ session-log.md — Session history (append-only)
## Mutability
- Δ: σ θ (mutable — update every session)
- ∅: ε ρ (immutable — append only)
- K: rules.md (constant)
## Protocol
1. READ state.md to orient
2. EXECUTE the task
3. UPDATE state.md with current state in LLN
4. APPEND to session-log.md: ψ:round|N|timestamp|"summary"
5. RESPOND to user
+4
View File
@@ -0,0 +1,4 @@
# Session Log (LLN)
# ψ:round|<N>|<timestamp>|<summary>
ψ:round|0|2026-08-21T14:31:37.837Z|"h-context initialized"
ψ:round|1|2026-08-21T14:34:00Z|"Rebranded to Saffron Table - Persian-Mediterranean restaurant in Shoreditch"
+6
View File
@@ -0,0 +1,6 @@
σ:session|s1|complete
σ:project|saffron-table|restaurant/React
σ:branch|main|ΔRebranded to Persian-Mediterranean
θ:active|[]
θ:blocked|[]
ε:last|e1|"Saffron Table rebrand complete"
+169
View File
@@ -0,0 +1,169 @@
# Identity
You are the **Autonomous Software Engineer** (DEV) of tyga.ai. Answer every request from
that role's perspective and expertise. You are NOT a generic assistant — you own
this domain for tyga.ai. Stay in character as the Autonomous Software Engineer at all times.
---
# Tyga.AI — h-context Protocol
## CRITICAL: persist state ONLY when you changed project state
The h-context state-write + commit ritual exists so real WORK survives a restart.
It is **not** a per-message tax. Apply it like this:
- **You did project work** (edited/created files, ran a mutation, advanced a task):
1. Update `.h-context/state.md` with current project state in LLN
2. Append a round entry to `.h-context/session-log.md`
3. Commit: `git add .h-context/ && git commit -m "hctx: <brief>"`
- **You only answered a question / gave a read-only or conversational reply**:
do NONE of the above. There is nothing to persist — just answer.
**Never narrate this protocol to the user.** Do not mention state-writes, commits,
rounds, or "rituals" in your reply. Persist silently when warranted; otherwise stay
quiet about it. The user sees your answer, not your bookkeeping.
## A. h-context Directory
```
.h-context/
├── state.md σ Current state (<2KB, LLN encoded, mutable)
├── threads/ θ Active workstreams (mutable)
├── entries/ ε Extracted artifacts (append-only)
├── raw/ ρ Source archive (append-only)
├── session-log.md ψ Round history (append-only)
└── rules.md ω Protocol rules (constant)
```
When a `[H-CONTEXT]` block is in the message, use it as your state orientation. Do NOT re-read state.md — the server provides the latest.
If there is NO `[H-CONTEXT]` block, read `.h-context/state.md` yourself before responding.
## B. LLN Notation
State and logs MUST use LLN encoding — NOT prose. This minimises token usage.
**Format:** `prefix:key|value|metadata`
**state.md MUST contain:**
```
σ:session|s<N>|<status>
σ:project|<name>|<type/stack>
σ:branch|<name>|Δ<summary>
θ:active|[<thread1>;<thread2>]
θ:blocked|[<thread>→<reason>]
ε:last|e<N>|"<summary>"
```
**session-log.md — APPEND after every response:**
```
ψ:round|<N>|<ISO timestamp>|"<brief summary>"
```
**Symbols:** σ=state, θ=threads, ε=entries, ψ=log, Δ=change, ¬=not, →=flow, true/false/null
Keep state.md under 2KB.
## C. Round Protocol (h-conductor)
You are a **scoped expert** for this project directory.
**Each response follows this sequence:**
1. ORIENT — read `[H-CONTEXT]` block or `.h-context/state.md`
2. EXECUTE — do the requested work
3. RESPOND — answer the user
Steps 46 apply **only if step 2 changed project state** (files, mutations, task
progress). For a read-only or conversational answer, skip them entirely:
4. SAVE STATE — update `.h-context/state.md` with LLN
5. LOG ROUND — append `ψ:round|N|timestamp|"summary"` to session-log.md
6. COMMIT — `git add .h-context/ && git commit -m "hctx: <brief>"`
Do these silently — never narrate them in your reply.
## C2. Buggazi Board — your living todo list (work-tracking source of truth)
You have your OWN dedicated buggazi board, reachable via the `bgz` CLI (already
authenticated for this stack). It is the SINGLE SOURCE OF TRUTH for your work —
sprints, bugs, features, roadmap — and it SURVIVES session restarts, so you do
NOT rely on the LLM context window to remember outstanding work.
- **"the board" ALWAYS means your buggazi board.** If asked "what is on the
board?" / "what should I work on?", run `bgz` and report it — do not guess.
- **On session start**, orient with `bgz status` (τ-tree) or `bgz dashboard`.
- **Read views** (prefer `--json` for machine use / relay):
`bgz dashboard --json`, `bgz sprints --json`, `bgz bugs --json`,
`bgz features --json`, `bgz snapshot --json`.
- **Write as you work** — file/update items so the board reflects reality:
`bgz bug ...`, `bgz feature ...`, `bgz sprint add SPRINT-ID FEAT-ID`,
update statuses as you progress. The board is BOTH your todo list AND the data
the Unity head displays live.
- `bgz init --agent-schema` returns the full machine-readable command list.
## C3. SecretCarousel — how you mint, fetch & share secrets (`sc` CLI)
You have the `sc` (SecretCarousel) CLI (already authenticated for this stack). It
is the ONLY way you hold real credentials: infra keys, API tokens, and hand-offs
to other agents/tenants all live in the vault — NEVER in the prompt, a file, or
the chat transcript.
- **On session start**, orient with `sc init` (prints the modus operandi + a
"add to your agent rules" block) and `sc init --agent-schema` (the full
machine-readable command schema — load it BEFORE using any `sc` command; if a
flag is not in the schema, DO NOT use it). This mirrors `bgz init`.
- **Modus operandi (non-negotiable):**
1. NEVER print, echo, or repeat a secret value in your replies. Refer to
secrets by **id and name**, never by value.
2. Fetch a value with `sc secret show <id>` ONLY at the moment of use (writing
a config, calling an API) — use it, do not display it.
3. If a live credential ever lands in the chat, store it with `sc secret
create`, then rotate it (`sc rotate`) since it transited plaintext.
4. To hand a secret to another agent/tenant, mint a **claim token**
(`sc claim "<VAL>" --to <tenant> --contract <ctr>`) — never paste the value
across sessions.
- **Common commands** (see `sc init --agent-schema` for the authoritative set):
`sc secrets` / `sc secret show <id>` (read), `sc secret create` (store),
`sc rotate <id>` (rotate), `sc share` / `sc shares` (grant/list),
`sc claim` / `sc claim redeem` (cross-tenant hand-off),
`sc env pull` / `sc env promote` (materialise env from the vault),
`sc enroll ...` (E2E vault), `sc audit` (access log).
## D. Git Workflow
- Git credentials are pre-configured for Gitea (`git.core.tyga.ai`)
- After completing a feature or significant work:
1. Commit code changes: `git add -A && git commit -m "descriptive message"`
2. Push: `git push -u origin <branch>`
3. Create PR: `gitea-pr.sh "PR title" --body "description"`
- Do NOT use `gh` — it will not work. Use `gitea-pr.sh` for all PRs.
- When the user asks to "submit", "create a PR", or "push" — do all three steps.
## E. LLN Response Mode
When tools are defined in LLN notation (→tool|name|desc|params), respond using LLN:
**Tool calls:**
```
→tool|send_email|{"to":"joe@tyga.agency","subject":"Hello","body":"Hi"}
```
**Multiple tool calls:**
```
→tool|web_search|{"query":"lovable AI"}
→tool|web_fetch|{"url":"https://lovable.dev"}
```
**Status/factual responses** — use LLN where concise:
```
σ:answer|4
σ:status|complete|Δ3files
```
**Conversational responses** — use normal prose (don't force LLN on chat).
## F. Environment
- Docker container: Debian, Node.js 20, Python 3
- Tools: git, curl, node, npm, python3, bash
- All tool calls auto-approved (`--dangerously-skip-permissions`)
+15
View File
@@ -0,0 +1,15 @@
# syntax=docker/dockerfile:1
# ── Build stage: compile the Vite/React app ──
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm install --no-audit --no-fund
COPY . .
RUN npm run build
# ── Serve stage: static nginx serving dist/ with SPA fallback ──
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
+32 -2
View File
@@ -1,3 +1,33 @@
# template-restaurant # saffron-table
Template: restaurant — Aurum Website for Saffron Table — a modern PersianMediterranean restaurant in Shoreditch, London.
Charcoal-grilled mezze and saffron cocktails.
## Tech Stack
- React with TypeScript
- TailwindCSS for styling
- React Router for navigation
- Lucide React for icons
## Pages
- **Home**: Hero with tagline, signature dishes, gallery, testimonials
- **Menu**: Full menu with mezze, mains, desserts, and saffron cocktails
- **Reservations**: OpenTable-style booking form
- **About**: Story, chef bio, awards, and philosophy
- **Events**: Private dining and special events
## Development
```bash
npm install
npm run dev
```
## Build
```bash
npm run build
```
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Saffron Table — PersianMediterranean, Shoreditch</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+17
View File
@@ -0,0 +1,17 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# SPA fallback — client-side routing (react-router BrowserRouter)
location / {
try_files $uri $uri/ /index.html;
}
# Long-cache hashed assets
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
+68
View File
@@ -0,0 +1,68 @@
{
"name": "vite-react-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.30.1",
"lucide-react": "^0.462.0",
"clsx": "^2.1.1",
"tailwind-merge": "^2.6.0",
"@radix-ui/react-accordion": "^1.2.11",
"@radix-ui/react-alert-dialog": "^1.1.14",
"@radix-ui/react-aspect-ratio": "^1.1.7",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-checkbox": "^1.3.2",
"@radix-ui/react-collapsible": "^1.1.11",
"@radix-ui/react-context-menu": "^2.2.15",
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-dropdown-menu": "^2.1.15",
"@radix-ui/react-hover-card": "^1.1.14",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-menubar": "^1.1.15",
"@radix-ui/react-navigation-menu": "^1.2.13",
"@radix-ui/react-popover": "^1.1.14",
"@radix-ui/react-progress": "^1.1.7",
"@radix-ui/react-radio-group": "^1.3.7",
"@radix-ui/react-scroll-area": "^1.2.9",
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slider": "^1.3.5",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.5",
"@radix-ui/react-tabs": "^1.1.12",
"@radix-ui/react-toast": "^1.2.14",
"@radix-ui/react-toggle": "^1.1.9",
"@radix-ui/react-toggle-group": "^1.1.10",
"@radix-ui/react-tooltip": "^1.2.7",
"class-variance-authority": "^0.7.1",
"cmdk": "^1.1.1",
"date-fns": "^3.6.0",
"embla-carousel-react": "^8.6.0",
"input-otp": "^1.4.2",
"next-themes": "^0.3.0",
"react-day-picker": "^8.10.1",
"react-resizable-panels": "^2.1.9",
"recharts": "^2.15.4",
"sonner": "^1.7.4",
"tailwindcss-animate": "^1.0.7",
"vaul": "^0.9.9"
},
"devDependencies": {
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.7",
"@vitejs/plugin-react-swc": "^3.11.0",
"autoprefixer": "^10.4.21",
"postcss": "^8.5.6",
"tailwindcss": "^3.4.17",
"typescript": "^5.8.3",
"vite": "^5.4.19"
}
}
+6
View File
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
+6 -6
View File
@@ -8,12 +8,12 @@ export default function Footer() {
<div className="mx-auto max-w-7xl px-6"> <div className="mx-auto max-w-7xl px-6">
<div className="grid md:grid-cols-2 gap-12"> <div className="grid md:grid-cols-2 gap-12">
<div> <div>
<div className="text-xl font-semibold tracking-widest uppercase text-white mb-6">Aurum</div> <div className="text-xl font-semibold tracking-widest uppercase text-white mb-6">Saffron Table</div>
<div className="space-y-3 text-sm"> <div className="space-y-3 text-sm">
<div className="flex items-start gap-3"><MapPin className="h-4 w-4 mt-0.5 text-amber-500" /><span>42 Golden Lane, Mayfair, London W1K 3AB</span></div> <div className="flex items-start gap-3"><MapPin className="h-4 w-4 mt-0.5 text-amber-500" /><span>127 Shoreditch High Street, London E1 6JE</span></div>
<div className="flex items-center gap-3"><Clock className="h-4 w-4 text-amber-500" /><span>TueSat 6:00 PM 11:00 PM</span></div> <div className="flex items-center gap-3"><Clock className="h-4 w-4 text-amber-500" /><span>TueSun 5:00 PM 11:00 PM</span></div>
<div className="flex items-center gap-3"><Phone className="h-4 w-4 text-amber-500" /><span>+44 20 7946 0123</span></div> <div className="flex items-center gap-3"><Phone className="h-4 w-4 text-amber-500" /><span>+44 20 7490 8956</span></div>
<div className="flex items-center gap-3"><Mail className="h-4 w-4 text-amber-500" /><span>reservations@aurum.com</span></div> <div className="flex items-center gap-3"><Mail className="h-4 w-4 text-amber-500" /><span>reservations@saffrontable.co.uk</span></div>
</div> </div>
</div> </div>
<div className="flex flex-col items-start md:items-end justify-between"> <div className="flex flex-col items-start md:items-end justify-between">
@@ -27,7 +27,7 @@ export default function Footer() {
</Button> </Button>
</div> </div>
</div> </div>
<div className="mt-12 pt-8 border-t border-stone-800 text-center text-sm text-stone-500">© 2026 Aurum. All rights reserved.</div> <div className="mt-12 pt-8 border-t border-stone-800 text-center text-sm text-stone-500">© 2026 Saffron Table. All rights reserved.</div>
</div> </div>
</footer> </footer>
); );
+1 -1
View File
@@ -25,7 +25,7 @@ export default function Header() {
return ( return (
<header className={`fixed top-0 inset-x-0 z-50 transition-all duration-300 ${scrolled ? "bg-stone-950/95 backdrop-blur-lg border-b border-stone-800 shadow-sm" : "bg-transparent"}`}> <header className={`fixed top-0 inset-x-0 z-50 transition-all duration-300 ${scrolled ? "bg-stone-950/95 backdrop-blur-lg border-b border-stone-800 shadow-sm" : "bg-transparent"}`}>
<div className="mx-auto max-w-7xl flex items-center justify-between px-6 py-4"> <div className="mx-auto max-w-7xl flex items-center justify-between px-6 py-4">
<Link to="/" className="text-lg font-semibold tracking-widest uppercase text-white">Aurum</Link> <Link to="/" className="text-lg font-semibold tracking-widest uppercase text-white">Saffron Table</Link>
<nav className="hidden md:flex items-center gap-8"> <nav className="hidden md:flex items-center gap-8">
{links.map((l) => ( {links.map((l) => (
<Link key={l.to} to={l.to} className="text-sm text-stone-300 hover:text-amber-400 transition-colors tracking-wide uppercase">{l.label}</Link> <Link key={l.to} to={l.to} className="text-sm text-stone-300 hover:text-amber-400 transition-colors tracking-wide uppercase">{l.label}</Link>
+27
View File
@@ -0,0 +1,27 @@
import { useTheme } from "next-themes";
import { Toaster as Sonner, toast } from "sonner";
type ToasterProps = React.ComponentProps<typeof Sonner>;
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme();
return (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
toastOptions={{
classNames: {
toast:
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
description: "group-[.toast]:text-muted-foreground",
actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
},
}}
{...props}
/>
);
};
export { Toaster, toast };
+114
View File
@@ -0,0 +1,114 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 221.2 83.2% 53.3%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 224.3 76.3% 48%;
}
html {
scroll-behavior: smooth;
}
* {
@apply border-border;
}
body {
@apply bg-background text-foreground antialiased;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
}
}
@layer utilities {
.animate-fade-up {
animation: fadeUp 0.6s ease-out forwards;
}
.animate-fade-in {
animation: fadeIn 0.8s ease-out forwards;
}
.animate-scale-in {
animation: scaleIn 0.5s ease-out forwards;
}
.animate-slide-in-right {
animation: slideInRight 0.5s ease-out forwards;
}
.hover-lift {
@apply transition-all duration-300 hover:-translate-y-1 hover:shadow-lg;
}
.hover-card-lift {
@apply transition-all duration-300 hover:-translate-y-1 hover:shadow-xl hover:border-primary/50;
}
.delay-100 { animation-delay: 100ms; }
.delay-200 { animation-delay: 200ms; }
.delay-300 { animation-delay: 300ms; }
.delay-400 { animation-delay: 400ms; }
.delay-500 { animation-delay: 500ms; }
.stagger > :nth-child(1) { animation-delay: 0ms; }
.stagger > :nth-child(2) { animation-delay: 100ms; }
.stagger > :nth-child(3) { animation-delay: 200ms; }
.stagger > :nth-child(4) { animation-delay: 300ms; }
.stagger > :nth-child(5) { animation-delay: 400ms; }
.stagger > :nth-child(6) { animation-delay: 500ms; }
.stagger > :nth-child(7) { animation-delay: 600ms; }
.stagger > :nth-child(8) { animation-delay: 700ms; }
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes slideInRight {
from { opacity: 0; transform: translateX(20px); }
to { opacity: 1; transform: translateX(0); }
}
+15
View File
@@ -0,0 +1,15 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { ThemeProvider } from "next-themes";
import { Toaster } from "@/components/ui/sonner";
import App from "./App";
import "./index.css";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<App />
<Toaster richColors position="bottom-right" />
</ThemeProvider>
</StrictMode>
);
+19 -19
View File
@@ -7,23 +7,23 @@ import Header from "@/components/Header";
import Footer from "@/components/Footer"; import Footer from "@/components/Footer";
const milestones = [ const milestones = [
{ year: "1987", title: "The Beginning", desc: "Chef Bellini opens Aurum with just 20 seats in Mayfair." }, { year: "2019", title: "The Beginning", desc: "Saffron Table opens its doors in Shoreditch with a vision to bring authentic Persian flavours to London." },
{ year: "1995", title: "First Michelin Star", desc: "Aurum earns its first Michelin star, cementing its place among Londons finest." }, { year: "2021", title: "Best New Restaurant", desc: "Named Londons Best New Restaurant by Time Out for our innovative mezze concept." },
{ year: "2008", title: "Second Star & Expansion", desc: "A second star and a major renovation doubles capacity with private dining rooms." }, { year: "2023", title: "Charcoal Grill Expansion", desc: "Our signature charcoal grill becomes the heart of the kitchen, elevating every dish." },
{ year: "2024", title: "Worlds 50 Best", desc: "Named #32 on the Worlds 50 Best Restaurants list for the third time." }, { year: "2025", title: "Cocktail Recognition", desc: "Our saffron cocktail program wins Best Bar Program from Restaurant Magazine." },
]; ];
const values = [ const values = [
{ icon: Leaf, title: "Ingredients", desc: "We source the finest seasonal ingredients from trusted local farms and sustainable producers worldwide." }, { icon: Leaf, title: "Ingredients", desc: "We source premium saffron directly from Iran and seasonal produce from local British farms." },
{ icon: Star, title: "Technique", desc: "Classical training meets modern innovation. Every dish reflects decades of accumulated mastery and creativity." }, { icon: Star, title: "Technique", desc: "Our charcoal grill and traditional Persian methods meet modern culinary innovation." },
{ icon: Heart, title: "Hospitality", desc: "Every guest is family. Our service team ensures each visit is warm, personal, and truly memorable." }, { icon: Heart, title: "Hospitality", desc: "Every guest is family. We bring the warmth of Persian hospitality to Shoreditch." },
]; ];
const awards = [ const awards = [
{ name: "2 Michelin Stars", year: "2008Present" }, { name: "Best New Restaurant London", year: "2021" },
{ name: "Worlds 50 Best Restaurants", year: "2022, 2023, 2024" }, { name: "Best Bar Program", year: "2025" },
{ name: "Best Fine Dining London", year: "2024" }, { name: "Sustainable Restaurant Award", year: "2024" },
{ name: "Sustainability Award", year: "2023" }, { name: "Top 100 UK Restaurants", year: "2023, 2024" },
]; ];
export default function AboutPage() { export default function AboutPage() {
@@ -44,9 +44,9 @@ export default function AboutPage() {
<section className="py-24"> <section className="py-24">
<div className="mx-auto max-w-7xl px-6 grid lg:grid-cols-2 gap-16 items-center"> <div className="mx-auto max-w-7xl px-6 grid lg:grid-cols-2 gap-16 items-center">
<div> <div>
<h2 className="text-3xl font-serif tracking-wide mb-6 animate-fade-up">A Legacy of Excellence</h2> <h2 className="text-3xl font-serif tracking-wide mb-6 animate-fade-up">A Story of Flavour & Hospitality</h2>
<p className="text-muted-foreground leading-relaxed mb-4">For over three decades, Aurum has been a sanctuary for those who appreciate the art of fine dining. Founded by Chef Marcus Bellini in 1987, our kitchen has always been guided by one philosophy: let exceptional ingredients speak for themselves.</p> <p className="text-muted-foreground leading-relaxed mb-4">Saffron Table was born from a love of Persian hospitality and Mediterranean abundance. Founded in 2019, we bring the warmth of ancient traditions to creative Shoreditch.</p>
<p className="text-muted-foreground leading-relaxed">Every morning, our team sources the freshest produce from local farms, the finest seafood from sustainable fisheries, and rare ingredients from trusted purveyors around the world.</p> <p className="text-muted-foreground leading-relaxed">Our charcoal grill is the heart of our kitchen, while our saffron infusion program creates cocktails that transport you to the bustling markets of Tehran and the shores of the Mediterranean.</p>
</div> </div>
<div className="relative"> <div className="relative">
<div className="aspect-[4/5] rounded-sm bg-gradient-to-br from-amber-800/30 via-stone-700/30 to-amber-900/30 relative overflow-hidden"> <div className="aspect-[4/5] rounded-sm bg-gradient-to-br from-amber-800/30 via-stone-700/30 to-amber-900/30 relative overflow-hidden">
@@ -71,10 +71,10 @@ export default function AboutPage() {
</div> </div>
<div className="order-1 lg:order-2"> <div className="order-1 lg:order-2">
<Badge variant="secondary" className="mb-4 text-xs bg-amber-900/20 text-amber-700 dark:text-amber-400 border-0">Executive Chef</Badge> <Badge variant="secondary" className="mb-4 text-xs bg-amber-900/20 text-amber-700 dark:text-amber-400 border-0">Executive Chef</Badge>
<h2 className="text-3xl font-serif tracking-wide mb-2">Chef Marcus Bellini</h2> <h2 className="text-3xl font-serif tracking-wide mb-2">Chef Yasmin Kashani</h2>
<p className="text-muted-foreground leading-relaxed mb-4">Born in Turin and trained in the kitchens of Paris, Chef Bellini brings over 40 years of culinary mastery to every dish. His style blends classic French technique with Italian soul.</p> <p className="text-muted-foreground leading-relaxed mb-4">Born in Tehran and raised in London, Chef Kashani brings the flavours of her Persian heritage together with Mediterranean influences. Her charcoal-grilled creations have redefined modern mezze.</p>
<p className="text-muted-foreground leading-relaxed">Under his leadership, Aurum has earned two Michelin stars and been named among the Worlds 50 Best Restaurants three times.</p> <p className="text-muted-foreground leading-relaxed">Under her leadership, Saffron Table has become known for Londons most authentic Persian-Mediterranean fusion experience.</p>
<blockquote className="text-lg font-serif italic text-muted-foreground border-l-2 border-amber-700/30 pl-4 my-6">"The finest ingredients deserve the simplest preparation. Let quality speak for itself."</blockquote> <blockquote className="text-lg font-serif italic text-muted-foreground border-l-2 border-amber-700/30 pl-4 my-6">"Food is memory. Every dish should taste like coming home."</blockquote>
</div> </div>
</div> </div>
</section> </section>
@@ -134,7 +134,7 @@ export default function AboutPage() {
{/* ── CTA ── */} {/* ── CTA ── */}
<section className="py-24 bg-gradient-to-b from-stone-950 to-amber-950/30"> <section className="py-24 bg-gradient-to-b from-stone-950 to-amber-950/30">
<div className="mx-auto max-w-3xl px-6 text-center"> <div className="mx-auto max-w-3xl px-6 text-center">
<h2 className="text-3xl font-serif tracking-wide text-white mb-4">Experience Aurum</h2> <h2 className="text-3xl font-serif tracking-wide text-white mb-4">Experience Saffron Table</h2>
<p className="text-stone-400 mb-8">We look forward to welcoming you</p> <p className="text-stone-400 mb-8">We look forward to welcoming you</p>
<Button asChild size="lg" className="rounded-none px-10 text-sm tracking-widest uppercase bg-amber-700 hover:bg-amber-600 text-white active:scale-[0.98]"> <Button asChild size="lg" className="rounded-none px-10 text-sm tracking-widest uppercase bg-amber-700 hover:bg-amber-600 text-white active:scale-[0.98]">
<Link to="/reservations">Book a Table <ArrowRight className="ml-2 h-4 w-4" /></Link> <Link to="/reservations">Book a Table <ArrowRight className="ml-2 h-4 w-4" /></Link>
+7 -7
View File
@@ -10,15 +10,15 @@ import Header from "@/components/Header";
import Footer from "@/components/Footer"; import Footer from "@/components/Footer";
const rooms = [ const rooms = [
{ name: "The Cellar", capacity: "12", desc: "An intimate underground space with exposed brick walls and a curated wine display. Perfect for exclusive dinners.", gradient: "from-amber-800/30 to-stone-700/30" }, { name: "The Saffron Room", capacity: "12", desc: "An intimate space adorned with Persian artwork and warm terracotta tones. Perfect for exclusive dinners.", gradient: "from-amber-800/30 to-stone-700/30" },
{ name: "The Gallery", capacity: "24", desc: "Elegant room featuring rotating art installations with floor-to-ceiling windows overlooking the garden.", gradient: "from-stone-700/30 to-amber-900/30" }, { name: "The Mezze Terrace", capacity: "24", desc: "Beautiful private dining area overlooking Shoreditch High Street with natural light throughout.", gradient: "from-stone-700/30 to-amber-900/30" },
{ name: "The Terrace", capacity: "40", desc: "Our largest private space with retractable roof, perfect for celebrations and corporate gatherings.", gradient: "from-amber-900/30 to-stone-800/30" }, { name: "The Courtyard", capacity: "40", desc: "Our largest private space with retractable roof, perfect for celebrations and corporate gatherings.", gradient: "from-amber-900/30 to-stone-800/30" },
]; ];
const events = [ const events = [
{ title: "Wine Dinner Series", desc: "Monthly five-course dinner paired with wines from a featured vineyard. An evening of discovery and indulgence.", badge: "Monthly" }, { title: "Persian Wine & Mezze Nights", desc: "Monthly five-course mezze dinner paired with Persian wines. An evening of discovery and indulgence.", badge: "Monthly" },
{ title: "Chefs Table Experience", desc: "An exclusive 8-seat experience at the kitchen counter with a bespoke tasting menu narrated by the chef.", badge: "Quarterly" }, { title: "Charcoal Grill Masterclass", desc: "Learn our charcoal grilling techniques from the chef in a hands-on class with lunch and cocktails included.", badge: "Quarterly" },
{ title: "Cooking Masterclass", desc: "Learn signature techniques from our executive chef in a hands-on class with lunch and wine included.", badge: "Seasonal" }, { title: "Saffron Cocktail Lab", desc: "An exclusive workshop learning to create signature saffron cocktails with our head bartender.", badge: "Seasonal" },
]; ];
export default function EventsPage() { export default function EventsPage() {
@@ -86,7 +86,7 @@ export default function EventsPage() {
<div className="mx-auto max-w-3xl px-6 text-center"> <div className="mx-auto max-w-3xl px-6 text-center">
<ChefHat className="h-10 w-10 mx-auto text-amber-600 mb-4" /> <ChefHat className="h-10 w-10 mx-auto text-amber-600 mb-4" />
<h2 className="text-3xl font-serif tracking-wide mb-4">Off-Site Catering</h2> <h2 className="text-3xl font-serif tracking-wide mb-4">Off-Site Catering</h2>
<p className="text-muted-foreground mb-8 max-w-xl mx-auto">Bring the Aurum experience to your venue. Our catering team creates bespoke menus for events of any size, from intimate dinner parties to grand celebrations.</p> <p className="text-muted-foreground mb-8 max-w-xl mx-auto">Bring the Saffron Table experience to your venue. Our catering team creates bespoke Persian-Mediterranean menus for events of any size, from intimate dinner parties to grand celebrations.</p>
<Button className="rounded-none bg-amber-700 hover:bg-amber-600 text-white tracking-widest uppercase text-xs px-8 active:scale-[0.98]"> <Button className="rounded-none bg-amber-700 hover:bg-amber-600 text-white tracking-widest uppercase text-xs px-8 active:scale-[0.98]">
Request a Quote <ArrowRight className="ml-2 h-4 w-4" /> Request a Quote <ArrowRight className="ml-2 h-4 w-4" />
</Button> </Button>
+16 -16
View File
@@ -9,15 +9,15 @@ import Header from "@/components/Header";
import Footer from "@/components/Footer"; import Footer from "@/components/Footer";
const highlights = [ const highlights = [
{ name: "Wagyu Beef Tenderloin", desc: "A5 wagyu, bone marrow butter, roasted root vegetables, red wine jus", price: "$62", gradient: "from-amber-800/30 to-stone-700/30" }, { name: "Charcoal-Grilled Lamb Kofta", desc: "Hand-spiced lamb with saffron rice, grilled tomatoes, and mint yogurt", price: "£24", gradient: "from-amber-800/30 to-stone-700/30" },
{ name: "Seared Scallops", desc: "Pan-seared diver scallops, cauliflower purée, truffle vinaigrette", price: "$24", gradient: "from-stone-700/30 to-amber-900/30" }, { name: "Saffron Salmon", desc: "Wild-caught salmon with saffron beurre blanc, fennel, and pomegranate", price: "£22", gradient: "from-stone-700/30 to-amber-900/30" },
{ name: "Valrhona Chocolate Fondant", desc: "Warm dark chocolate cake, vanilla bean ice cream, gold leaf", price: "$16", gradient: "from-amber-900/30 to-stone-800/30" }, { name: "Persian Rose Panna Cotta", desc: "Creamy vanilla panna cotta with rose water, pistachios, and honey", price: "£12", gradient: "from-amber-900/30 to-stone-800/30" },
]; ];
const testimonials = [ const testimonials = [
{ name: "James & Katherine", text: "An extraordinary dining experience. Every dish was a masterpiece. The tasting menu was the highlight of our trip.", source: "Google Reviews" }, { name: "Maya & Tariq", text: "The charcoal-grilled mezze took us straight back to Tehran. Authentic flavours with a modern London twist.", source: "Google Reviews" },
{ name: "Sophie Laurent", text: "Impeccable service and the most beautiful presentation I have ever seen. The wine pairing was exquisite.", source: "TripAdvisor" }, { name: "Elena Rossi", text: "Best saffron cocktails in Shoreditch! The Persian-Mediterranean fusion is absolutely divine.", source: "TripAdvisor" },
{ name: "Michael Chen", text: "Worth every penny. The wagyu was the best I have had outside of Japan. Already planning our return visit.", source: "Yelp" }, { name: "Jack Thompson", text: "Warm hospitality and incredible food. The lamb kofta is a must-try. We'll be back weekly.", source: "Yelp" },
]; ];
export default function IndexPage() { export default function IndexPage() {
@@ -39,10 +39,10 @@ export default function IndexPage() {
<span></span><span></span><span></span> <span></span><span></span><span></span>
</div> </div>
<Badge variant="secondary" className="mb-6 px-4 py-1.5 text-xs tracking-widest uppercase bg-amber-900/30 text-amber-300 border-amber-700/30 backdrop-blur"> <Badge variant="secondary" className="mb-6 px-4 py-1.5 text-xs tracking-widest uppercase bg-amber-900/30 text-amber-300 border-amber-700/30 backdrop-blur">
Est. 1987 Est. 2019
</Badge> </Badge>
<h1 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-serif tracking-wide text-white leading-[1.1]">A Culinary Journey Through Timeless Flavors</h1> <h1 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-serif tracking-wide text-white leading-[1.1]">Charcoal-Grilled Mezze & Saffron Cocktails</h1>
<p className="mx-auto mt-6 max-w-xl text-lg text-stone-400 leading-relaxed">Experience refined dining where heritage meets innovation. Every dish tells a story crafted with passion, precision, and the finest seasonal ingredients.</p> <p className="mx-auto mt-6 max-w-xl text-lg text-stone-400 leading-relaxed">Experience the vibrant flavours of Persia and the Mediterranean in the heart of Shoreditch. Every dish tells a story of ancient traditions and modern creativity.</p>
<div className="mt-10 flex flex-col sm:flex-row items-center justify-center gap-4"> <div className="mt-10 flex flex-col sm:flex-row items-center justify-center gap-4">
<Button asChild size="lg" className="rounded-none px-10 text-sm tracking-widest uppercase bg-amber-700 hover:bg-amber-600 text-white shadow-lg shadow-amber-900/30 active:scale-[0.98]"> <Button asChild size="lg" className="rounded-none px-10 text-sm tracking-widest uppercase bg-amber-700 hover:bg-amber-600 text-white shadow-lg shadow-amber-900/30 active:scale-[0.98]">
<Link to="/reservations">Reserve a Table <ArrowRight className="ml-2 h-4 w-4" /></Link> <Link to="/reservations">Reserve a Table <ArrowRight className="ml-2 h-4 w-4" /></Link>
@@ -95,19 +95,19 @@ export default function IndexPage() {
<span></span><Star className="h-4 w-4" /><span></span> <span></span><Star className="h-4 w-4" /><span></span>
</div> </div>
<h2 className="text-3xl sm:text-4xl font-serif tracking-wide mb-6">Our Story</h2> <h2 className="text-3xl sm:text-4xl font-serif tracking-wide mb-6">Our Story</h2>
<p className="text-muted-foreground leading-relaxed mb-4">For over three decades, Aurum has been a sanctuary for those who appreciate the art of fine dining. Founded by Chef Marcus Bellini, our kitchen is guided by one philosophy: let exceptional ingredients speak for themselves.</p> <p className="text-muted-foreground leading-relaxed mb-4">Born from a love of Persian hospitality and Mediterranean abundance, Saffron Table brings the warmth of ancient traditions to creative Shoreditch. Our charcoal grill is the heart of our kitchen.</p>
<div className="grid grid-cols-3 gap-6 mt-10 mb-8"> <div className="grid grid-cols-3 gap-6 mt-10 mb-8">
<div className="text-center"> <div className="text-center">
<div className="text-3xl font-serif font-bold text-amber-700 dark:text-amber-400">35+</div> <div className="text-3xl font-serif font-bold text-amber-700 dark:text-amber-400">5+</div>
<div className="text-sm text-muted-foreground mt-1">Years</div> <div className="text-sm text-muted-foreground mt-1">Years</div>
</div> </div>
<div className="text-center"> <div className="text-center">
<div className="text-3xl font-serif font-bold text-amber-700 dark:text-amber-400">2</div> <div className="text-3xl font-serif font-bold text-amber-700 dark:text-amber-400">15</div>
<div className="text-sm text-muted-foreground mt-1">Michelin Stars</div> <div className="text-sm text-muted-foreground mt-1">Mezze Dishes</div>
</div> </div>
<div className="text-center"> <div className="text-center">
<div className="text-3xl font-serif font-bold text-amber-700 dark:text-amber-400">400+</div> <div className="text-3xl font-serif font-bold text-amber-700 dark:text-amber-400">8</div>
<div className="text-sm text-muted-foreground mt-1">Wines</div> <div className="text-sm text-muted-foreground mt-1">Saffron Cocktails</div>
</div> </div>
</div> </div>
<Button asChild variant="outline" className="rounded-none tracking-widest uppercase text-xs px-8 active:scale-[0.98]"> <Button asChild variant="outline" className="rounded-none tracking-widest uppercase text-xs px-8 active:scale-[0.98]">
@@ -131,7 +131,7 @@ export default function IndexPage() {
<div className="mx-auto max-w-7xl px-6"> <div className="mx-auto max-w-7xl px-6">
<div className="text-center mb-16 animate-fade-up"> <div className="text-center mb-16 animate-fade-up">
<h2 className="text-3xl sm:text-4xl font-serif tracking-wide">Gallery</h2> <h2 className="text-3xl sm:text-4xl font-serif tracking-wide">Gallery</h2>
<p className="mt-3 text-muted-foreground">A glimpse into the Aurum experience</p> <p className="mt-3 text-muted-foreground">A glimpse into the Saffron Table experience</p>
</div> </div>
<div className="grid grid-cols-2 lg:grid-cols-3 gap-4"> <div className="grid grid-cols-2 lg:grid-cols-3 gap-4">
{[0, 1, 2, 3, 4, 5].map((i) => ( {[0, 1, 2, 3, 4, 5].map((i) => (
+21 -21
View File
@@ -11,34 +11,34 @@ import { useSaas } from "@/hooks/use-tygarun";
const menuItems = { const menuItems = {
starters: [ starters: [
{ name: "Burrata & Heirloom Tomato", desc: "Creamy burrata, vine-ripened tomatoes, basil oil, aged balsamic", price: "$18", badges: ["V"] }, { name: "Warm Hummus & Charcoal Pita", desc: "Creamy hummus topped with saffron oil, paprika, and pine nuts", price: "£10", badges: ["V", "GF"] },
{ name: "Seared Scallops", desc: "Pan-seared diver scallops, cauliflower purée, truffle vinaigrette", price: "$24", badges: ["GF"] }, { name: "Grilled Halloumi & Fig", desc: "Charcoal-grilled Cypriot halloumi with fresh figs and honey drizzle", price: "£12", badges: ["V"] },
{ name: "Duck Confit Terrine", desc: "Slow-cooked duck leg, pistachio, cornichon, sourdough toast", price: "$19", badges: [] }, { name: "Persian Spiced Salmon", desc: "Cured salmon with saffron, lime, and fresh dill", price: "£14", badges: ["GF"] },
{ name: "Wild Mushroom Risotto", desc: "Arborio rice, porcini, chanterelle, shaved parmesan, herb oil", price: "$16", badges: ["V", "GF"] }, { name: "Stuffed Vine Leaves", desc: "Dolma filled with rice, herbs, and pomegranate molasses", price: "£10", badges: ["V", "GF"] },
], ],
mains: [ mains: [
{ name: "Wagyu Beef Tenderloin", desc: "A5 wagyu, bone marrow butter, roasted root vegetables, red wine jus", price: "$62", badges: ["GF"] }, { name: "Charcoal Lamb Kofta", desc: "Hand-spiced lamb skewers with saffron rice and grilled tomatoes", price: "£24", badges: ["GF"] },
{ name: "Pan-Roasted Halibut", desc: "Wild halibut, saffron beurre blanc, fennel, olive tapenade", price: "$44", badges: [] }, { name: "Saffron Chicken Breast", desc: "Charcoal-grilled chicken with saffron yogurt sauce and pistachios", price: "£22", badges: ["GF"] },
{ name: "Rack of Lamb", desc: "Herb-crusted lamb, pommes dauphine, mint gremolata", price: "$52", badges: [] }, { name: "Mediterranean Sea Bass", desc: "Grilled sea bass with herb butter, capers, and lemon", price: "£26", badges: ["GF"] },
{ name: "Truffle Tagliatelle", desc: "Hand-rolled pasta, black truffle cream, pecorino, fresh herbs", price: "$36", badges: ["V"] }, { name: "Persian Lamb Shank", desc: "Slow-cooked lamb shank with dried lime and barberry rice", price: "£28", badges: ["GF"] },
{ name: "Roasted Duck Breast", desc: "Cherry-glazed duck, confit potatoes, wilted greens, port reduction", price: "$48", badges: ["GF"] }, { name: "Vegetarian Mezze Platter", desc: "Selection of grilled vegetables, dips, and fresh bread", price: "£18", badges: ["V", "GF"] },
], ],
desserts: [ desserts: [
{ name: "Valrhona Chocolate Fondant", desc: "Warm dark chocolate cake, vanilla bean ice cream, gold leaf", price: "$16", badges: ["V"] }, { name: "Persian Rose Panna Cotta", desc: "Creamy vanilla panna cotta with rose water, pistachios, and honey", price: "£12", badges: ["V"] },
{ name: "Crème Brûlée", desc: "Tahitian vanilla custard, caramelized sugar, shortbread", price: "$14", badges: ["GF"] }, { name: "Saffron Crème Brûlée", desc: "Classic custard infused with premium saffron", price: "£11", badges: ["GF", "V"] },
{ name: "Tarte Tatin", desc: "Caramelized apple, puff pastry, calvados cream", price: "$15", badges: [] }, { name: "Pistachio Baklava", desc: "Layers of flaky pastry filled with pistachios and honey syrup", price: "£10", badges: ["V"] },
{ name: "Lemon Posset", desc: "Set cream, fresh berries, lavender meringue", price: "$13", badges: ["V", "GF"] }, { name: "Fig & Walnut Tart", desc: "Fresh figs with walnuts on a buttery pastry base", price: "£11", badges: ["V"] },
], ],
drinks: [ drinks: [
{ name: "Signature Old Fashioned", desc: "Woodford Reserve, demerara, Angostura, flamed orange peel", price: "$19", badges: [] }, { name: "Saffron Gin Sour", desc: "Saffron-infused gin, lemon juice, and egg white", price: "£14", badges: [] },
{ name: "Garden Spritz", desc: "Elderflower, prosecco, cucumber, fresh mint, soda", price: "$16", badges: [] }, { name: "Persian Sunrise", desc: "Pomegranate juice, orange blossom water, and prosecco", price: "£12", badges: [] },
{ name: "Sommelier Wine Pairing", desc: "Five-course curated wine flight matched to your meal", price: "$65", badges: [] }, { name: "Cardamom Espresso Martini", desc: "Vodka, espresso, and cardamom syrup", price: "£13", badges: [] },
{ name: "Espresso Martini", desc: "Ketel One, fresh espresso, Kahlúa, vanilla", price: "$18", badges: [] }, { name: "Rose & Raspberry Spritz", desc: "Rose water, raspberries, elderflower, and soda", price: "£11", badges: [] },
], ],
}; };
const tastingCourses = [ const tastingCourses = [
"Amuse-bouche · Seasonal selection", "Seared Scallops · Cauliflower, truffle", "Wagyu Beef · Root vegetables, red wine jus", "Artisan Cheese · Honeycomb, fig compote", "Chocolate Fondant · Vanilla ice cream" "Mezze Selection · Hummus, dolma, grilled vegetables", "Charcoal Salmon · Saffron beurre blanc, fennel", "Lamb Kofta · Saffron rice, grilled tomatoes, mint yogurt", "Persian Cheese · Honeycomb, walnuts, pomegranate", "Rose Panna Cotta · Pistachios and honey"
]; ];
const renderItems = (items: { name: string; desc: string; price: string; badges: string[] }[]) => const renderItems = (items: { name: string; desc: string; price: string; badges: string[] }[]) =>
@@ -69,7 +69,7 @@ export default function MenuPage() {
.map((p: any) => ({ .map((p: any) => ({
name: p.name, name: p.name,
desc: p.description || "", desc: p.description || "",
price: typeof p.priceCents === "number" ? "$" + (p.priceCents / 100).toFixed(0) : (p.price || ""), price: typeof p.priceCents === "number" ? "£" + (p.priceCents / 100).toFixed(0) : (p.price || ""),
badges: Array.isArray(p.badges) ? p.badges : [], badges: Array.isArray(p.badges) ? p.badges : [],
})) }))
: []; : [];
@@ -142,8 +142,8 @@ export default function MenuPage() {
{tastingCourses.map((c, i) => ( {tastingCourses.map((c, i) => (
<div key={i} className="py-2 text-sm font-serif text-muted-foreground border-b border-dotted border-stone-200 dark:border-stone-800 last:border-0">{c}</div> <div key={i} className="py-2 text-sm font-serif text-muted-foreground border-b border-dotted border-stone-200 dark:border-stone-800 last:border-0">{c}</div>
))} ))}
<div className="pt-4 text-2xl font-serif font-bold text-amber-700 dark:text-amber-400">$135 per person</div> <div className="pt-4 text-2xl font-serif font-bold text-amber-700 dark:text-amber-400">£55 per person</div>
<p className="text-xs text-muted-foreground">Wine pairing available for $85</p> <p className="text-xs text-muted-foreground">Cocktail pairing available for £35</p>
</CardContent> </CardContent>
</Card> </Card>
<p className="mt-6 text-sm text-muted-foreground italic flex items-center justify-center gap-2"> <p className="mt-6 text-sm text-muted-foreground italic flex items-center justify-center gap-2">
+4 -4
View File
@@ -47,7 +47,7 @@ export default function ReservationsPage() {
<span></span><Wine className="h-4 w-4" /><span></span> <span></span><Wine className="h-4 w-4" /><span></span>
</div> </div>
<h1 className="text-4xl sm:text-5xl font-serif tracking-wide text-white animate-fade-up">Make a Reservation</h1> <h1 className="text-4xl sm:text-5xl font-serif tracking-wide text-white animate-fade-up">Make a Reservation</h1>
<p className="mt-4 text-stone-400 animate-fade-up">Join us for an unforgettable evening of exceptional cuisine</p> <p className="mt-4 text-stone-400 animate-fade-up">Join us for charcoal-grilled mezze and saffron cocktails in the heart of Shoreditch</p>
</div> </div>
</section> </section>
@@ -122,9 +122,9 @@ export default function ReservationsPage() {
{/* ── SIDEBAR INFO ── */} {/* ── SIDEBAR INFO ── */}
<div className="space-y-8"> <div className="space-y-8">
{[ {[
{ icon: Clock, title: "Opening Hours", lines: ["Tuesday Thursday: 6:00 PM 10:00 PM", "Friday Saturday: 6:00 PM 11:00 PM", "Sunday Monday: Closed"] }, { icon: Clock, title: "Opening Hours", lines: ["Tuesday Thursday: 5:00 PM 10:00 PM", "Friday Saturday: 5:00 PM 11:00 PM", "Sunday: 5:00 PM 10:00 PM", "Monday: Closed"] },
{ icon: Shirt, title: "Dress Code", lines: ["Smart casual. Jackets preferred for gentlemen in the main dining room."] }, { icon: Shirt, title: "Dress Code", lines: ["Casual chic. We want you to feel comfortable while enjoying your meal."] },
{ icon: Car, title: "Parking", lines: ["Complimentary valet parking available. Underground garage on Golden Lane."] }, { icon: Car, title: "Location", lines: ["127 Shoreditch High Street, London E1 6JE", "2 minutes walk from Shoreditch High Street Overground", "8 minutes from Old Street station"] },
{ icon: AlertCircle, title: "Cancellation Policy", lines: ["Cancellations accepted up to 24 hours before your reservation. Late cancellations may incur a fee."] }, { icon: AlertCircle, title: "Cancellation Policy", lines: ["Cancellations accepted up to 24 hours before your reservation. Late cancellations may incur a fee."] },
].map((info, i) => ( ].map((info, i) => (
<div key={i}> <div key={i}>
+93
View File
@@ -0,0 +1,93 @@
import type { Config } from "tailwindcss";
export default {
darkMode: ["class"],
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
container: {
center: true,
padding: "2rem",
screens: { "2xl": "1400px" },
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
"fade-up": {
from: { opacity: "0", transform: "translateY(20px)" },
to: { opacity: "1", transform: "translateY(0)" },
},
"fade-in": {
from: { opacity: "0" },
to: { opacity: "1" },
},
"scale-in": {
from: { opacity: "0", transform: "scale(0.95)" },
to: { opacity: "1", transform: "scale(1)" },
},
"slide-in-right": {
from: { opacity: "0", transform: "translateX(20px)" },
to: { opacity: "1", transform: "translateX(0)" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"fade-up": "fade-up 0.6s ease-out forwards",
"fade-in": "fade-in 0.8s ease-out forwards",
"scale-in": "scale-in 0.5s ease-out forwards",
"slide-in-right": "slide-in-right 0.5s ease-out forwards",
},
},
},
plugins: [require("tailwindcss-animate")],
} satisfies Config;
+37
View File
@@ -0,0 +1,37 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
},
"include": [
"src"
],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}
+12
View File
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": [
"vite.config.ts"
]
}
+16
View File
@@ -0,0 +1,16 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import path from "path";
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
server: {
port: 5173,
host: true,
},
});