Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 328f429ea0 | |||
| bca343d7c8 | |||
| 1f9ef169c8 | |||
| 16e66c1c95 | |||
| bb2adc762d | |||
| 029e2aa074 | |||
| 7b1292dfa1 | |||
| d57534480b | |||
| 532acc5eaf | |||
| 098d1dd250 | |||
| b711454af5 | |||
| b7d9cd767d | |||
| bb990079b4 | |||
| cf8f85aac8 | |||
| 923d105fab |
+22
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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 4–6 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
@@ -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;"]
|
||||
@@ -1,3 +1,33 @@
|
||||
# template-restaurant
|
||||
# saffron-table
|
||||
|
||||
Template: restaurant — Aurum
|
||||
Website for Saffron Table — a modern Persian–Mediterranean 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
@@ -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 — Persian–Mediterranean, Shoreditch</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
+17
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
@@ -8,12 +8,12 @@ export default function Footer() {
|
||||
<div className="mx-auto max-w-7xl px-6">
|
||||
<div className="grid md:grid-cols-2 gap-12">
|
||||
<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="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-center gap-3"><Clock className="h-4 w-4 text-amber-500" /><span>Tue–Sat 6: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"><Mail className="h-4 w-4 text-amber-500" /><span>reservations@aurum.com</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>Tue–Sun 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 7490 8956</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 className="flex flex-col items-start md:items-end justify-between">
|
||||
@@ -27,7 +27,7 @@ export default function Footer() {
|
||||
</Button>
|
||||
</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>
|
||||
</footer>
|
||||
);
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function Header() {
|
||||
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"}`}>
|
||||
<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">
|
||||
{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>
|
||||
|
||||
@@ -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
@@ -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); }
|
||||
}
|
||||
@@ -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
@@ -7,23 +7,23 @@ import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
|
||||
const milestones = [
|
||||
{ year: "1987", title: "The Beginning", desc: "Chef Bellini opens Aurum with just 20 seats in Mayfair." },
|
||||
{ year: "1995", title: "First Michelin Star", desc: "Aurum earns its first Michelin star, cementing its place among London’s finest." },
|
||||
{ year: "2008", title: "Second Star & Expansion", desc: "A second star and a major renovation doubles capacity with private dining rooms." },
|
||||
{ year: "2024", title: "World’s 50 Best", desc: "Named #32 on the World’s 50 Best Restaurants list for the third time." },
|
||||
{ year: "2019", title: "The Beginning", desc: "Saffron Table opens its doors in Shoreditch with a vision to bring authentic Persian flavours to London." },
|
||||
{ year: "2021", title: "Best New Restaurant", desc: "Named London’s Best New Restaurant by Time Out for our innovative mezze concept." },
|
||||
{ year: "2023", title: "Charcoal Grill Expansion", desc: "Our signature charcoal grill becomes the heart of the kitchen, elevating every dish." },
|
||||
{ year: "2025", title: "Cocktail Recognition", desc: "Our saffron cocktail program wins Best Bar Program from Restaurant Magazine." },
|
||||
];
|
||||
|
||||
const values = [
|
||||
{ icon: Leaf, title: "Ingredients", desc: "We source the finest seasonal ingredients from trusted local farms and sustainable producers worldwide." },
|
||||
{ icon: Star, title: "Technique", desc: "Classical training meets modern innovation. Every dish reflects decades of accumulated mastery and creativity." },
|
||||
{ icon: Heart, title: "Hospitality", desc: "Every guest is family. Our service team ensures each visit is warm, personal, and truly memorable." },
|
||||
{ icon: Leaf, title: "Ingredients", desc: "We source premium saffron directly from Iran and seasonal produce from local British farms." },
|
||||
{ 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. We bring the warmth of Persian hospitality to Shoreditch." },
|
||||
];
|
||||
|
||||
const awards = [
|
||||
{ name: "2 Michelin Stars", year: "2008–Present" },
|
||||
{ name: "World’s 50 Best Restaurants", year: "2022, 2023, 2024" },
|
||||
{ name: "Best Fine Dining London", year: "2024" },
|
||||
{ name: "Sustainability Award", year: "2023" },
|
||||
{ name: "Best New Restaurant London", year: "2021" },
|
||||
{ name: "Best Bar Program", year: "2025" },
|
||||
{ name: "Sustainable Restaurant Award", year: "2024" },
|
||||
{ name: "Top 100 UK Restaurants", year: "2023, 2024" },
|
||||
];
|
||||
|
||||
export default function AboutPage() {
|
||||
@@ -44,9 +44,9 @@ export default function AboutPage() {
|
||||
<section className="py-24">
|
||||
<div className="mx-auto max-w-7xl px-6 grid lg:grid-cols-2 gap-16 items-center">
|
||||
<div>
|
||||
<h2 className="text-3xl font-serif tracking-wide mb-6 animate-fade-up">A Legacy of Excellence</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">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>
|
||||
<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">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">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 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">
|
||||
@@ -71,10 +71,10 @@ export default function AboutPage() {
|
||||
</div>
|
||||
<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>
|
||||
<h2 className="text-3xl font-serif tracking-wide mb-2">Chef Marcus Bellini</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">Under his leadership, Aurum has earned two Michelin stars and been named among the World’s 50 Best Restaurants three times.</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>
|
||||
<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 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 her leadership, Saffron Table has become known for London’s 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">"Food is memory. Every dish should taste like coming home."</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -134,7 +134,7 @@ export default function AboutPage() {
|
||||
{/* ── CTA ── */}
|
||||
<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">
|
||||
<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>
|
||||
<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>
|
||||
|
||||
@@ -10,15 +10,15 @@ import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
|
||||
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 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 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 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 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 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 = [
|
||||
{ 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: "Chef’s Table Experience", desc: "An exclusive 8-seat experience at the kitchen counter with a bespoke tasting menu narrated by the chef.", 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: "Persian Wine & Mezze Nights", desc: "Monthly five-course mezze dinner paired with Persian wines. An evening of discovery and indulgence.", badge: "Monthly" },
|
||||
{ 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: "Saffron Cocktail Lab", desc: "An exclusive workshop learning to create signature saffron cocktails with our head bartender.", badge: "Seasonal" },
|
||||
];
|
||||
|
||||
export default function EventsPage() {
|
||||
@@ -86,7 +86,7 @@ export default function EventsPage() {
|
||||
<div className="mx-auto max-w-3xl px-6 text-center">
|
||||
<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>
|
||||
<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]">
|
||||
Request a Quote <ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
+16
-16
@@ -9,15 +9,15 @@ import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
|
||||
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: "Seared Scallops", desc: "Pan-seared diver scallops, cauliflower purée, truffle vinaigrette", price: "$24", 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: "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: "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: "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 = [
|
||||
{ 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: "Sophie Laurent", text: "Impeccable service and the most beautiful presentation I have ever seen. The wine pairing was exquisite.", 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: "Maya & Tariq", text: "The charcoal-grilled mezze took us straight back to Tehran. Authentic flavours with a modern London twist.", source: "Google Reviews" },
|
||||
{ name: "Elena Rossi", text: "Best saffron cocktails in Shoreditch! The Persian-Mediterranean fusion is absolutely divine.", source: "TripAdvisor" },
|
||||
{ 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() {
|
||||
@@ -39,10 +39,10 @@ export default function IndexPage() {
|
||||
<span>—</span><span>✦</span><span>—</span>
|
||||
</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">
|
||||
Est. 1987
|
||||
Est. 2019
|
||||
</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>
|
||||
<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>
|
||||
<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 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">
|
||||
<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>
|
||||
@@ -95,19 +95,19 @@ export default function IndexPage() {
|
||||
<span>—</span><Star className="h-4 w-4" /><span>—</span>
|
||||
</div>
|
||||
<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="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>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl font-serif font-bold text-amber-700 dark:text-amber-400">2</div>
|
||||
<div className="text-sm text-muted-foreground mt-1">Michelin Stars</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">Mezze Dishes</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl font-serif font-bold text-amber-700 dark:text-amber-400">400+</div>
|
||||
<div className="text-sm text-muted-foreground mt-1">Wines</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">Saffron Cocktails</div>
|
||||
</div>
|
||||
</div>
|
||||
<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="text-center mb-16 animate-fade-up">
|
||||
<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 className="grid grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{[0, 1, 2, 3, 4, 5].map((i) => (
|
||||
|
||||
+21
-21
@@ -11,34 +11,34 @@ import { useSaas } from "@/hooks/use-tygarun";
|
||||
|
||||
const menuItems = {
|
||||
starters: [
|
||||
{ name: "Burrata & Heirloom Tomato", desc: "Creamy burrata, vine-ripened tomatoes, basil oil, aged balsamic", price: "$18", badges: ["V"] },
|
||||
{ name: "Seared Scallops", desc: "Pan-seared diver scallops, cauliflower purée, truffle vinaigrette", price: "$24", badges: ["GF"] },
|
||||
{ name: "Duck Confit Terrine", desc: "Slow-cooked duck leg, pistachio, cornichon, sourdough toast", price: "$19", badges: [] },
|
||||
{ name: "Wild Mushroom Risotto", desc: "Arborio rice, porcini, chanterelle, shaved parmesan, herb oil", price: "$16", badges: ["V", "GF"] },
|
||||
{ name: "Warm Hummus & Charcoal Pita", desc: "Creamy hummus topped with saffron oil, paprika, and pine nuts", price: "£10", badges: ["V", "GF"] },
|
||||
{ name: "Grilled Halloumi & Fig", desc: "Charcoal-grilled Cypriot halloumi with fresh figs and honey drizzle", price: "£12", badges: ["V"] },
|
||||
{ name: "Persian Spiced Salmon", desc: "Cured salmon with saffron, lime, and fresh dill", price: "£14", badges: ["GF"] },
|
||||
{ name: "Stuffed Vine Leaves", desc: "Dolma filled with rice, herbs, and pomegranate molasses", price: "£10", badges: ["V", "GF"] },
|
||||
],
|
||||
mains: [
|
||||
{ name: "Wagyu Beef Tenderloin", desc: "A5 wagyu, bone marrow butter, roasted root vegetables, red wine jus", price: "$62", badges: ["GF"] },
|
||||
{ name: "Pan-Roasted Halibut", desc: "Wild halibut, saffron beurre blanc, fennel, olive tapenade", price: "$44", badges: [] },
|
||||
{ name: "Rack of Lamb", desc: "Herb-crusted lamb, pommes dauphine, mint gremolata", price: "$52", badges: [] },
|
||||
{ name: "Truffle Tagliatelle", desc: "Hand-rolled pasta, black truffle cream, pecorino, fresh herbs", price: "$36", badges: ["V"] },
|
||||
{ name: "Roasted Duck Breast", desc: "Cherry-glazed duck, confit potatoes, wilted greens, port reduction", price: "$48", badges: ["GF"] },
|
||||
{ name: "Charcoal Lamb Kofta", desc: "Hand-spiced lamb skewers with saffron rice and grilled tomatoes", price: "£24", badges: ["GF"] },
|
||||
{ name: "Saffron Chicken Breast", desc: "Charcoal-grilled chicken with saffron yogurt sauce and pistachios", price: "£22", badges: ["GF"] },
|
||||
{ name: "Mediterranean Sea Bass", desc: "Grilled sea bass with herb butter, capers, and lemon", price: "£26", badges: ["GF"] },
|
||||
{ name: "Persian Lamb Shank", desc: "Slow-cooked lamb shank with dried lime and barberry rice", price: "£28", badges: ["GF"] },
|
||||
{ name: "Vegetarian Mezze Platter", desc: "Selection of grilled vegetables, dips, and fresh bread", price: "£18", badges: ["V", "GF"] },
|
||||
],
|
||||
desserts: [
|
||||
{ name: "Valrhona Chocolate Fondant", desc: "Warm dark chocolate cake, vanilla bean ice cream, gold leaf", price: "$16", badges: ["V"] },
|
||||
{ name: "Crème Brûlée", desc: "Tahitian vanilla custard, caramelized sugar, shortbread", price: "$14", badges: ["GF"] },
|
||||
{ name: "Tarte Tatin", desc: "Caramelized apple, puff pastry, calvados cream", price: "$15", badges: [] },
|
||||
{ name: "Lemon Posset", desc: "Set cream, fresh berries, lavender meringue", price: "$13", badges: ["V", "GF"] },
|
||||
{ name: "Persian Rose Panna Cotta", desc: "Creamy vanilla panna cotta with rose water, pistachios, and honey", price: "£12", badges: ["V"] },
|
||||
{ name: "Saffron Crème Brûlée", desc: "Classic custard infused with premium saffron", price: "£11", badges: ["GF", "V"] },
|
||||
{ name: "Pistachio Baklava", desc: "Layers of flaky pastry filled with pistachios and honey syrup", price: "£10", badges: ["V"] },
|
||||
{ name: "Fig & Walnut Tart", desc: "Fresh figs with walnuts on a buttery pastry base", price: "£11", badges: ["V"] },
|
||||
],
|
||||
drinks: [
|
||||
{ name: "Signature Old Fashioned", desc: "Woodford Reserve, demerara, Angostura, flamed orange peel", price: "$19", badges: [] },
|
||||
{ name: "Garden Spritz", desc: "Elderflower, prosecco, cucumber, fresh mint, soda", price: "$16", badges: [] },
|
||||
{ name: "Sommelier Wine Pairing", desc: "Five-course curated wine flight matched to your meal", price: "$65", badges: [] },
|
||||
{ name: "Espresso Martini", desc: "Ketel One, fresh espresso, Kahlúa, vanilla", price: "$18", badges: [] },
|
||||
{ name: "Saffron Gin Sour", desc: "Saffron-infused gin, lemon juice, and egg white", price: "£14", badges: [] },
|
||||
{ name: "Persian Sunrise", desc: "Pomegranate juice, orange blossom water, and prosecco", price: "£12", badges: [] },
|
||||
{ name: "Cardamom Espresso Martini", desc: "Vodka, espresso, and cardamom syrup", price: "£13", badges: [] },
|
||||
{ name: "Rose & Raspberry Spritz", desc: "Rose water, raspberries, elderflower, and soda", price: "£11", badges: [] },
|
||||
],
|
||||
};
|
||||
|
||||
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[] }[]) =>
|
||||
@@ -69,7 +69,7 @@ export default function MenuPage() {
|
||||
.map((p: any) => ({
|
||||
name: p.name,
|
||||
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 : [],
|
||||
}))
|
||||
: [];
|
||||
@@ -142,8 +142,8 @@ export default function MenuPage() {
|
||||
{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 className="pt-4 text-2xl font-serif font-bold text-amber-700 dark:text-amber-400">$135 per person</div>
|
||||
<p className="text-xs text-muted-foreground">Wine pairing available for $85</p>
|
||||
<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">Cocktail pairing available for £35</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<p className="mt-6 text-sm text-muted-foreground italic flex items-center justify-center gap-2">
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function ReservationsPage() {
|
||||
<span>—</span><Wine className="h-4 w-4" /><span>—</span>
|
||||
</div>
|
||||
<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>
|
||||
</section>
|
||||
|
||||
@@ -122,9 +122,9 @@ export default function ReservationsPage() {
|
||||
{/* ── SIDEBAR INFO ── */}
|
||||
<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: Shirt, title: "Dress Code", lines: ["Smart casual. Jackets preferred for gentlemen in the main dining room."] },
|
||||
{ icon: Car, title: "Parking", lines: ["Complimentary valet parking available. Underground garage on Golden Lane."] },
|
||||
{ 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: ["Casual chic. We want you to feel comfortable while enjoying your meal."] },
|
||||
{ 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."] },
|
||||
].map((info, i) => (
|
||||
<div key={i}>
|
||||
|
||||
@@ -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;
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts"
|
||||
]
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user