2 cases built from this template
Same prompt, different [VARIABLES]. Click one to preview it above.About this template
Larder is a three-page website template for a small restaurant whose cooking follows the season. It has a home page, a menu page with prices and a visit page with opening hours and a drawn street map. It suits small-plates dining rooms, wine bars, bakeries, cafés and any place whose menu changes often and whose bookings come in by phone or email rather than through a booking widget.
The look is editorial. Oversized Fraunces headlines sit on a warm paper background with ink-dark text and one terracotta accent, paired with small uppercase labels in Instrument Sans. Layouts follow an asymmetric twelve-column grid. Text blocks and illustrations sit offset from each other, with plenty of empty space and thin hairline rules between them. Full-width image strips break up the pages. Each one is built from layered SVG illustrations (a laid table, a field at harvest, market crates) whose layers move at different speeds as you scroll. You don't need any photography. Every image is drawn in code, so the template stays light and is easy to recolor.
The menu page reads like a printed card. Dishes are grouped by course, each with a short description, dotted leaders running to the price and dietary marks, and a sticky course index tracks where you are. The visit page pairs an hours table that highlights today with an invented street map whose walking route draws itself when it scrolls into view. Booking uses tel: and mailto: links, which appear in the header, in a dark reservations band and in the footer.
To adapt it, change the brand, concept, neighbourhood, season, phone number and email. All copy, dishes, prices and hours live in one content file. The motion is limited to fade-and-rise reveals, line-by-line headline entrances and parallax, and all of it turns off when reduced motion is requested.
The template prompt
The live demo was built from exactly this text — nothing added. The other cases only change the values of the [VARIABLES].
# Editorial Restaurant Website
Build this website exactly as described below. Replace the [VARIABLES] with your own values; keep everything else.
## Brief
Larder: a three-page editorial website (home, menu, visit) for [BRAND], a 32-seat restaurant serving [CONCEPT] in [NEIGHBOURHOOD]. It has oversized serif type on warm paper, an asymmetric 12-column grid, full-bleed parallax strips made of layered code-drawn SVG illustrations, a typographic priced menu for [SEASON], an hours table that highlights today and an invented hand-drawn SVG street map with a pin and a walking route. Booking is by phone ([PHONE]) or email ([EMAIL]) only, with no backend.
- Audience: Diners deciding where to eat this week. That means locals checking the current menu and prices on their phone, and first-time visitors looking for hours, directions and how to book.
- Goal: Make the food and the room feel considered, show this season's menu with prices at a glance, and turn interest into a phone call or an email to book.
- Tone: Calm, warm and literate, like a printed menu card or a food magazine. Short plain sentences in the first-person plural ('we'), with concrete ingredients, places and times. No exclamation marks, superlatives or marketing clichés.
## Stack
- Framework: Vite 5 + vanilla TypeScript
- Styling: Tailwind CSS 3.4
- Libraries: lenis (MIT), lucide (ISC), @fontsource-variable/fraunces (OFL-1.1), @fontsource-variable/instrument-sans (OFL-1.1), tailwindcss (MIT), postcss (MIT), autoprefixer (MIT), typescript (Apache-2.0), vite (MIT)
- Build: `npm run build` → static files in `dist/`. No backend, no server code, no external APIs.
- Setup:
- Scaffold with npm create vite@5 using the vanilla-ts template. Keep three real HTML files at the project root: index.html, menu.html and visit.html.
- Install the runtime packages: npm i lenis lucide @fontsource-variable/fraunces @fontsource-variable/instrument-sans. Install the dev packages: npm i -D [email protected] postcss autoprefixer. Then run npx tailwindcss init -p.
- package.json scripts: dev = 'vite', build = 'tsc && vite build', preview = 'vite preview'.
- vite.config.ts: base './', build.sourcemap false, build.rollupOptions.input = { index: resolve(__dirname,'index.html'), menu: resolve(__dirname,'menu.html'), visit: resolve(__dirname,'visit.html') }.
- tailwind.config.js: content ['./*.html','./src/**/*.ts']. theme.extend.colors: paper #F4EEE3, paper-deep #EAE1D1, ink #1E1B17, ink-soft #5B544B, hairline #CFC4B2, terracotta #A8502F, moss #5F6B47, ochre #C8963E, beet #7B3343, cream #FBF8F2. fontFamily: display ['"Fraunces Variable"','Georgia','serif'], sans ['"Instrument Sans Variable"','system-ui','sans-serif']. maxWidth.container '1360px'.
- At the top of src/main.ts, import '@fontsource-variable/fraunces/full.css', '@fontsource-variable/fraunces/full-italic.css', '@fontsource-variable/instrument-sans', 'lenis/dist/lenis.css' and './styles.css'.
- src/styles.css holds @tailwind base/components/utilities. It also defines the component classes .label, .display-xl, .display-l, .statement, .dish-row, .leader, .diet-tag, .link-underline and .grain, the [data-reveal] states, @keyframes pulse-ring, and one @media (prefers-reduced-motion: reduce) block that disables every animation and transition.
- src/content.ts holds every piece of visible copy. That covers the site object (brand, concept, neighbourhood, season, phone, phoneHref with + and digits only, email, address lines), nav, the hours array (day, times, closed flag), the menu courses with dishes (name, price, description, tags), the home/menu/visit section copy, captions, the Market strip price-tag labels and the map labels. Pages render from it with template strings. Nothing visible is hard-coded anywhere else.
- When the variable values differ from the defaults, rewrite all copy in src/content.ts to fit the new brand, concept, place and season: headlines, dish names, prices, descriptions, hours, address, captions, price tags and map street labels. Keep line counts and string lengths close to the originals so the layout holds.
- src/art/ contains plate.ts, stillLife.ts, strips.ts (tableStrip, fieldStrip and marketStrip, each returning { back, mid, front } SVG strings) and map.ts. Every function returns inline SVG markup strings, and colors come from one shared palette object.
- src/main.ts exports initSite(page: 'home' | 'menu' | 'visit'). It renders the header, reservation band and footer into #header, #reserve and #footer, then swaps html.no-js for html.js. It starts Lenis (unless reduced motion is requested), the reveal observer, the parallax loop, the count-ups and the today-highlighting for hours, and finally calls lucide createIcons({ icons: { Phone, Mail, MapPin, Clock, ArrowRight, ArrowUpRight, Menu, X, Footprints, Bus, Accessibility } }).
- src/pages/home.ts, menu.ts and visit.ts each render their page sections into #main from content.ts and then call initSite(page).
- Each HTML file contains <html lang='en' class='no-js'>, a <title> ('[BRAND] — [CONCEPT]' for index, 'Menu — [BRAND]', 'Visit — [BRAND]'), <link rel='icon' type='image/svg+xml' href='/favicon.svg'> pointing to public/favicon.svg, and a body with class 'bg-paper text-ink font-sans antialiased' containing <div id='header'></div><main id='main'></main><div id='reserve'></div><div id='footer'></div><div class='grain' aria-hidden='true'></div> and <script type='module' src='/src/pages/<page>.ts'></script>.
- Write ASSETS.md at the project root listing the fonts and icon set with source URL, author and license, and state that every illustration is drawn in code and no third-party images are used.
## Variables
- [BRAND] — Restaurant name (default: "Wrenfold"). Invented name. Used in the header wordmark, giant footer wordmark, titles and map label.
- [CONCEPT] — What you serve (default: "Seasonal small plates & natural wine"). Short phrase for the hero label, footer line and home title.
- [NEIGHBOURHOOD] — Location (street or yard, district) (default: "Cooper's Yard, Eastmere"). Invented place. Drives the address, directions copy and map street labels.
- [SEASON] — Current season (default: "Autumn"). Shown in menu titles and labels. Dishes and produce should match it.
- [PHONE] — Booking phone (default: "+44 20 7946 0321"). Shown exactly as written. The tel: link uses + and digits only. Use a fictional number.
- [EMAIL] — Booking email (default: "[email protected]"). Used for mailto: links. Keep a .example domain for demos.
- [CREDIT] — keep a small "Template by VibeFather" link to https://vibefather.ai/t/editorial-restaurant-website in the footer (default: yes; delete this line to remove it)
The copy under Pages & Sections is written for the default values. If your values differ, rewrite every headline, paragraph, label and image for them — same length, tone and structure. Layout, colors, type and motion stay as described.
Keep all copy, image paths and links in one content file (for example `src/content.ts`), keyed by section, so the content can be swapped later without touching components.
## Design
### Colors
- Paper `#F4EEE3` — Page background, header and course-index background, text on the dark band
- Paper deep `#EAE1D1` — Alternate section band (home menu preview), illustration panels, map ground
- Ink `#1E1B17` — All headings and body text, primary button fill, reservation band background
- Ink soft `#5B544B` — Secondary text, descriptions, captions, dotted leaders (at 50% opacity)
- Hairline `#CFC4B2` — 1px rules, borders, table lines, plate rims
- Terracotta `#A8502F` — The only UI accent: italic headline words, today dot, active nav dot, course-index underline, map pin and route, hover states
- Cream `#FBF8F2` — Plates, cloths and price tags inside illustrations
- Moss `#5F6B47` — Illustration only: leaves, hills, park hatch
- Ochre `#C8963E` — Illustration only: squash, pears, wheat, sky tint
- Beet `#7B3343` — Illustration only: beetroot, small food details
### Typography
- Display: h1, h2, statements, dish names, wordmarks, large contact links. Use upright 300 for large sizes, italic 300 for accent words, 400 for dish names and the header wordmark. Turn font-optical-sizing on.: Fraunces (OFL), weights 300/400
- Text and labels: body 17px/1.6 at 400, prices 500 with tabular-nums, labels 12px at 600 uppercase with letter-spacing 0.18em, buttons 12px at 600 uppercase with letter-spacing 0.16em: Instrument Sans (OFL), weights 400/500/600
### Layout & motion
- Spacing: 12-column grid inside a container with max-width 1360px, 48px side padding on desktop and 24px on mobile, and 24px column gaps. Sections have 128px vertical padding on desktop and 80px on mobile. Use 24px between a label and its heading and 32px between a heading and its body text. Rules are 1px #CFC4B2 hairlines. Type scale: .display-xl clamp(3.5rem, 10.5vw, 10.5rem) at weight 300, line-height 0.92, tracking -0.035em; .display-l clamp(2.5rem, 6vw, 6rem) at 300, line-height 0.98, tracking -0.03em; .statement clamp(1.75rem, 3.2vw, 3rem) at 300, line-height 1.15, tracking -0.015em. Breakpoints: mobile below 768px, single column; the desktop grid starts at 1024px; tablet (768–1023px) uses the mobile stacking with wider padding.
- Radius: 0px everywhere: buttons, chips, tags, figures, cards and the map frame. Only intrinsically circular things are round: the map pin head, the today dot, the nav dot, the pulse ring and the plate drawings.
- Motion: Smooth scroll: new Lenis({ duration: 1.1, easing: t => Math.min(1, 1.001 - Math.pow(2, -10 * t)) }) driven by one requestAnimationFrame loop that also runs the parallax. Anchor links call lenis.scrollTo(target, { offset: -140 }).
- Motion: Headline entrance on load: every h1 line is wrapped in an overflow-hidden block span. The inner span rises from translateY(105%) to 0 over 1000ms with cubic-bezier(0.22,1,0.36,1), 90ms stagger, starting at 150ms. The label row fades in over 400ms first.
- Motion: Scroll reveals: [data-reveal] elements start at opacity 0 and translateY(24px) (only under html.js) and move to opacity 1 and 0 over 900ms with cubic-bezier(0.22,1,0.36,1). They are triggered once by an IntersectionObserver with threshold 0.15 and rootMargin '0px 0px -8% 0px'. Items in a group stagger 80ms through a --d CSS variable.
- Motion: Parallax strips: each strip holds absolutely positioned layers (top -20%, height 140%) with data-speed (back 0.08, mid 0.18, front 0.32, overlay text 0.45). Per frame, for strips flagged in view by an IntersectionObserver, compute p = clamp((innerHeight - rect.top) / (innerHeight + rect.height), 0, 1) and set each layer to transform: translate3d(0, (0.5 - p) * speed * rect.height px, 0) with will-change: transform.
- Motion: Hero plate: the plate group inside the hero figure rotates from 0deg to 10deg, linked to scroll progress through the hero.
- Motion: Count-up: the home stats count from 0 to their value over 1200ms with easeOutCubic the first time they enter view.
- Motion: Links: nav and text links use a 1px currentColor underline drawn with background-size, growing from 0% to 100% left to right over 400ms on hover. Persistent CTA text links keep the underline and it turns terracotta on hover.
- Motion: Dish rows (hover-capable pointers only): the name shifts 4px right and the dotted leader turns terracotta over 300ms.
- Motion: Reservation links: on hover the text shifts 8px right, the arrow-up-right icon rotates 45deg and the row's top border turns terracotta, all over 300ms.
- Motion: Map: when 30% of the map is in view, the dashed route draws once through an SVG mask whose solid path animates stroke-dashoffset from its length to 0 over 1800ms ease-in-out. The pin then drops from translateY(-24px) with opacity 0 to rest over 500ms with cubic-bezier(0.34,1.56,0.64,1), the label card fades in over 300ms, and a pulse ring under the pin loops (r 10 to 34, opacity 0.5 to 0, 2400ms ease-out, infinite).
- Motion: Mobile nav overlay: clip-path moves from inset(0 0 100% 0) to inset(0) over 600ms with cubic-bezier(0.22,1,0.36,1). Links rise 24px and fade in with a 70ms stagger. Lenis stops while the overlay is open.
- Motion: Footer giant wordmark: letters rise 40% and fade in with a 40ms stagger when the footer enters view.
- Motion: prefers-reduced-motion: reduce: Lenis is never created, parallax transforms are not applied, all [data-reveal] content and headline lines show immediately, count-ups show final values, the hero plate does not rotate, the map route is drawn statically with the pin in place and no pulse, and all CSS transitions and animations are set to 0.01ms.
- Imagery: There are no photographs. All imagery is flat, slightly textured illustration drawn as inline SVG strings in src/art/, using only the palette (cream, paper, paper-deep, ink, ink-soft, hairline, terracotta, moss, ochre, beet). Shapes are simple and geometric with a hand-drawn wobble from one shared SVG filter (feTurbulence type 'fractalNoise' baseFrequency 0.015 numOctaves 2 seed 4, then feDisplacementMap scale 3) applied to shape groups and never to text. A global grain overlay (.grain) is fixed at inset 0 with pointer-events none, z-index 60, opacity 0.07 and mix-blend-mode multiply; its background is a 200px tiled SVG data URI of feTurbulence fractalNoise at baseFrequency 0.8 with 2 octaves. HERO PLATE (viewBox 0 0 400 500): a paper-deep background; a cream plate at (200,250), r 160, with a 1.5px hairline rim and a 1px inner ring at r 120; five ochre squash crescents with lighter #E2B86A inner arcs fanned at 72deg intervals around the centre; six moss sage leaves with cream midribs; a wavy brown-butter drizzle (stroke #9A6A2F, 3px, 70% opacity); fourteen small ink seed ellipses; and the corner of a terracotta linen napkin at the bottom-left, rotated -8deg, with three cream stripe lines. STILL LIFE (viewBox 0 0 360 480): a paper-deep background, a cream cloth as a wavy polygon over the bottom third, two ochre pears with ink stems, one lumpy #D9B04C quince, three ink-soft walnuts with crease lines, and soft ink shadow ellipses at 8% opacity. STRIPS (every layer uses viewBox 0 0 1600 900 with preserveAspectRatio 'xMidYMid slice'). TABLE: the back layer is #E6DCC9 linen with a 6px crosshatch of ink lines at 8% opacity and a 220px terracotta table runner across the middle with three thin cream stripes. The mid layer has seven cream plates (r 90 to 170) with hairline rims, each holding a different simple dish: ochre squash crescents, beet discs with a cream swirl, a moss leaf cluster, ink seed dots, or a terracotta tartare oval with an ochre yolk circle. The front layer has a knife and fork as long rounded ink-soft shapes and two wine-glass rings (terracotta 4px stroke at 35% opacity). FIELD: the back layer is a sky gradient from #EBCB9C at the top to #F4EEE3 at the horizon, with a terracotta sun disc of r 90 at (1150,300). The mid layer has two rolling hill bands (#8A9470 behind, #5F6B47 in front) crossed by twelve parallel curved furrow strokes in cream at 25% opacity. The front layer is a dark #2B2A22 hedgerow of overlapping bumps along the bottom 30%, with a few ochre wheat-stalk strokes. MARKET: the back layer is an ink background with horizontal ink-soft crate slats (40px tall, 14px gaps). The mid layer has three crates seen straight on, packed with produce: terracotta tomatoes with moss star calyxes, ochre pears with ink stems, moss cabbages made of concentric leaf arcs, and beet roots with leaf stalks. The front layer has four cream price tags on ink strings, lettered in Fraunces italic with labels from content.ts ('Quince 3', 'Pears 2', 'Chard 4', 'Squash 5'). FAVICON (public/favicon.svg, 32x32): a terracotta circle with a cream inner ring, like a plate seen from above.
## Pages & Sections
### Home (`index.html`)
1. **Header (shared on all pages)** — Left: the wordmark '[BRAND]' (Fraunces 400, 26px, tracking -0.02em) linking to ./index.html. Centre (desktop, 1024px and up): nav links 'Home', 'Menu' and 'Visit' to ./index.html, ./menu.html and ./visit.html, styled as 12px uppercase labels. The current page has a 6px terracotta dot before its label. Right: a 40px square icon button with the Lucide mail icon (aria-label 'Email [BRAND]') linking to mailto:[EMAIL], followed by a 'Book a table' button with the Lucide phone icon linking to tel:[PHONE] (+ and digits only). Mobile (below 1024px): the wordmark, a compact 'Book' tel: button and a Lucide menu toggle. The toggle opens a full-screen paper overlay that lists Home / Menu / Visit in Fraunces 300 at 56px, with [PHONE], [EMAIL] and today's hours at the bottom. A Lucide x button closes it.
Layout: position sticky, top 0, z-50, height 72px (64px on mobile), full width, paper background. The inner container is 1360px wide with 48px padding (24px on mobile), flex justify-between items-center. The Book button is 40px tall with 18px horizontal padding, a 1px ink border, radius 0 and a 12px uppercase label at 600 with tracking 0.16em. On hover it fills ink with paper text. After scrollY > 24 a 1px hairline bottom border fades in.
Motion: The border fades in over 300ms. The overlay opens with a clip-path reveal and staggered links, as described in Design motion.
2. **Hero** — Label row: '[CONCEPT] · [NEIGHBOURHOOD]' on the left and 'Open Wed–Sun' on the right. h1 in three lines: 'Small plates,' / 'cooked to the' / 'week's harvest.' The third line is Fraunces italic 300 in terracotta. Intro (20px Instrument Sans, ink-soft, max 34ch): 'Wrenfold is a thirty-two seat dining room in Cooper's Yard, Eastmere. Nine growers, one wood grill and a menu we rewrite every Tuesday. Order a few plates, share everything, stay for another glass.' CTAs: the primary button 'Book a table' (ink fill, paper text, phone icon, 52px tall, tel:[PHONE]) and a secondary text link 'See the autumn menu' with arrow-right to ./menu.html. Hours line: 'Wed–Fri 17:30–late · Sat 12:00–late · Sun 12:00–16:00'. Figure: the HERO PLATE illustration with the caption 'Fig. 1 — Delicata squash, brown butter, sage.'
Layout: Container with the 12-column grid, 64px top padding (40px mobile) and 128px bottom padding (80px mobile). The label row spans all 12 columns with a 1px hairline top border, 16px top padding and 48px bottom margin. The h1 (.display-xl) spans columns 1–11, one block per line. Below it, the figure takes columns 1–5 at aspect 4/5 with a 64px top margin, and its caption is 12px ink-soft with a 12px top margin. The text column takes columns 7–11 with a 160px top margin and holds the intro, then the CTA row (gap 32px, 40px top margin), then the hours line (32px top margin, hairline top border, 16px top padding, 13px ink-soft). Mobile order: label row, h1, intro, CTAs, hours, then the full-width figure.
Motion: The h1 line entrance runs on load. The intro, CTAs and hours fade and rise starting at 500ms with an 80ms stagger. The figure reveals on scroll, and the plate rotates with scroll.
3. **Strip I — The table** — TABLE parallax strip. The caption chip at the bottom-left reads 'Plate I — The pass, Tuesday service'.
Layout: Full viewport width, height clamp(320px, 62vh, 640px), overflow hidden, position relative. Three absolutely positioned layers (back, mid, front), each spanning the full width with top -20% and height 140%, each holding one SVG. The caption chip has a paper background, a 1px hairline border, an 11px uppercase label at 600 with tracking 0.18em and 8px 12px padding. It sits 48px from the left and 32px from the bottom (24px and 24px on mobile).
Motion: Layer speeds are 0.08, 0.18 and 0.32 (see Design motion).
4. **01 — The kitchen** — Label: '01 — The kitchen'. Statement: 'We cook what our growers bring to the back door on Tuesday morning. If the frost takes the chard, the chard comes off the menu. It is a small way of eating, and we think it is the best one.' The words 'on Tuesday morning' are set in italic terracotta. There are three stats: '32' with 'Seats in one room, no second sitting.'; '9' with 'Growers and makers within 60 km.'; and '52' with 'Menus a year, one every Tuesday.'
Layout: 128px vertical padding. The label sits in columns 1–3 and is sticky at top 120px on desktop. The statement (.statement, max 26ch) sits in columns 5–12. The stats row sits in columns 5–12 with a 96px top margin, split into three equal columns, each with a 1px hairline left border and 24px left padding. Each number is Fraunces 300 at clamp(4rem, 7vw, 6.5rem) with line-height 1, and its caption is 15px ink-soft (max 18ch, 12px top margin). On mobile everything stacks, and each stat gets a hairline top border instead of a left border.
Motion: The statement reveals, then the stats reveal with an 80ms stagger and count up.
5. **02 — On the menu now** — Label: '02 — On the menu · Autumn'. h2: 'A few plates' / 'from this week.' with the second line in italic terracotta. A list of five dish rows: 'Roasted delicata squash, brown butter, sage' 11; 'Beetroot baked in salt, whipped goat's curd' 10; 'Charred leeks, egg-yolk dressing' 10; 'Hand-cut beef tartare, smoked oyster mayonnaise' 14; 'Mussels, cider & lovage' 13. Each row has its description and diet tags exactly as on the Menu page. After the list comes the text link 'Read the full menu' with arrow-right to ./menu.html. The figure shows the STILL LIFE illustration with the caption 'Fig. 2 — Quince, pear, walnut.'
Layout: A full-width paper-deep band with 128px vertical padding. The h2 (.display-l) spans columns 1–8 with a 64px bottom margin. The dish list (the shared .dish-row component described on the Menu page) takes columns 1–6, with the link 40px below it. The figure takes columns 8–12 at aspect 3/4, top-aligned with the dish list and offset 48px down, with its caption below. On mobile the figure follows the link at full width.
Motion: The h2 lines reveal, the dish rows reveal with an 80ms stagger, and the figure reveals.
6. **Strip II — The field** — FIELD parallax strip with overlay text 'Eat in season.' and no caption chip.
Layout: The same structure as Strip I, with height clamp(360px, 72vh, 720px). A fourth layer holds the overlay headline in Fraunces italic 300 at clamp(3rem, 9vw, 8.75rem), paper-colored, positioned 48px from the left (24px on mobile) and 18% from the bottom so that it sits over the dark hills and hedgerow.
Motion: Layer speeds are 0.08, 0.18 and 0.32, and the overlay text moves at 0.45.
7. **03 — Visit** — Label: '03 — Visit'. h2: 'Down the cobbles,' / 'past the old cooperage.' Paragraph: 'We're at the far end of Cooper's Yard, six minutes on foot from Eastmere Station. The bar is kept for walk-ins every night, and the kitchen takes last orders an hour before close.' Text link: 'Hours, map & directions' with arrow-right to ./visit.html. Compact hours: 'Mon–Tue Closed', 'Wed–Thu 17:30–23:00', 'Fri 17:30–23:30', 'Sat 12:00–15:00, 17:30–23:30', 'Sun 12:00–16:00'. Address: '14 Cooper's Yard, Eastmere EM2 4QT' with the Lucide map-pin icon.
Layout: 128px vertical padding. The left side takes columns 1–6: the label, the h2 (.display-l), the paragraph (max 42ch, 32px top margin, ink-soft) and the link (32px top margin). The right side takes columns 8–12 with a 96px top margin. Its hours rows are 15px with a 1px hairline bottom border and 14px vertical padding, flex justify-between, with the day at weight 500 and the times in tabular-nums. The row matching today (from new Date().getDay()) gets a 6px terracotta dot before the day and a 10px uppercase terracotta 'Today' tag. The address sits 24px below the rows. On mobile everything stacks.
Motion: Reveals with an 80ms stagger.
8. **Reservation band (shared on all pages)** — Label: 'Reservations'. h2: 'Book by phone,' / 'or write to us.' with the second line italic. Two large link rows: '[PHONE]' with the phone icon linking to tel:, and '[EMAIL]' with the mail icon linking to mailto:. Each ends in an arrow-up-right icon. Note: 'Tables are released four weeks ahead. The bar is kept for walk-ins every night. For seven or more, please email.'
Layout: A full-bleed ink background with paper text and 128px vertical padding (96px on mobile). The label sits in columns 1–3 and the h2 (.display-l) in columns 4–12. The link rows stack in columns 4–12 with a 64px top margin. Each row is full width with a 1px top border in rgba(244,238,227,0.2), 24px vertical padding and flex justify-between items-center, and the link text is Fraunces 300 at clamp(1.75rem, 4vw, 3.5rem). The note sits in columns 4–9 with a 40px top margin, at 15px in paper at 70% opacity.
Motion: Reveals. The link hover effect is described in Design motion.
9. **Footer (shared on all pages)** — Four columns. '[BRAND]' with 'Seasonal small plates & natural wine in Cooper's Yard, Eastmere.'; 'Address' with '14 Cooper's Yard', 'Eastmere EM2 4QT'; 'Hours' with 'Wed–Fri 17:30–late', 'Sat 12:00–late', 'Sun 12:00–16:00', 'Mon–Tue closed'; and 'Contact' with the [PHONE] tel: link, the [EMAIL] mailto: link and the page links Home / Menu / Visit (./index.html, ./menu.html, ./visit.html). Below them sits the giant wordmark '[BRAND]'. The bottom bar has '© 2026 [BRAND]. Illustrations drawn in code.' on the left and the optional [CREDIT] link on the right.
Layout: Paper background with 96px top padding. The columns span 1–4, 5–6, 7–9 and 10–12. Each column heading is a 12px uppercase label with a 16px bottom margin, and the text is 15px with line-height 1.7. The giant wordmark is Fraunces 300 at 19vw with line-height 0.8 and tracking -0.05em, placed 96px below the columns. It sits in an overflow-hidden wrapper and is translated 10% down so its baseline crops against the bottom bar. The bottom bar has a 1px hairline top border, 20px vertical padding and 12px ink-soft text, flex justify-between. On mobile the columns form a 2x2 grid and the wordmark is 22vw.
Motion: The wordmark letters rise in on view.
### Menu (`menu.html`)
1. **Header (shared)** — Identical to the home header. 'Menu' is the active page.
Layout: As on the home page.
2. **Menu hero** — Label row: 'Menu · Autumn 2026' on the left and 'Printed every Tuesday' on the right. h1: 'The autumn' / 'menu.' with 'menu.' in italic terracotta. Note: 'Plates are made to share. Three or four per person is about right; the kitchen sends them as they're ready.' Legend: 'V vegetarian · VG vegan · GF gluten-free · Prices in £'.
Layout: 64px top padding and 96px bottom padding. The label row is styled exactly as in the home hero. The h1 (.display-xl) spans columns 1–8. Columns 9–12 are bottom-aligned with the h1 (self-end) and hold the note (17px ink-soft, max 30ch) and then the legend (12px uppercase label, 24px top margin, with each diet code shown as a .diet-tag). On mobile they stack.
Motion: h1 line entrance on load, then the note fades and rises.
3. **Course index** — Anchor links: 'Snacks', 'Small plates', 'Larger plates', 'Cheese', 'Sweet', 'To drink' pointing to #snacks, #small-plates, #larger-plates, #cheese, #sweet and #to-drink.
Layout: position sticky, top 72px (64px on mobile), z-40, paper background, 1px hairline top and bottom borders, height 52px. Inside the container there is a flex row with 32px gaps and overflow-x auto with a hidden scrollbar. Links are 12px uppercase at 600 with tracking 0.16em in ink-soft. The active link turns ink and gets a 2px terracotta underline offset 6px.
Motion: An IntersectionObserver with rootMargin '-40% 0px -55% 0px' sets the active course. Clicking scrolls with lenis.scrollTo(target, { offset: -140 }). On mobile the active link scrolls into view horizontally.
4. **Courses** — Six course sections with Roman numerals, titles and notes. I. Snacks, 'While you decide.' — 'Warm sourdough & cultured butter' 5, 'Rye-and-spelt loaf, baked at noon.' V; 'Smoked curd on rye crisp, pickled walnut' 6, 'Curd from a small dairy twenty minutes north.' V; 'Fried sage, brown-butter salt' 5, 'A bowl to pick at.' V GF; 'Cured mackerel, apple, horseradish' 8, 'Cured overnight, sliced thin.' GF. II. Small plates, 'Three or four between two.' — 'Roasted delicata squash, brown butter, sage' 11, 'Toasted seeds, a little chilli.' V GF; 'Beetroot baked in salt, whipped goat's curd' 10, 'Hazelnuts and the beet leaves, wilted.' V GF; 'Roasted carrots, carrot-top salsa verde' 9, 'Pumpkin seeds, cider vinegar.' VG GF; 'Charred leeks, egg-yolk dressing' 10, 'Crisp shallots and chives.' V; 'Hand-cut beef tartare, smoked oyster mayonnaise' 14, 'Potato crisps on the side.' GF; 'Grilled mushrooms on toast, aged cheddar' 12, 'Thyme and a spoon of mushroom ketchup.' V; 'Mussels, cider & lovage' 13, 'Grilled bread for the broth.' III. Larger plates, 'To share, or not.' — 'Slow-cooked pork shoulder, quince' 22, 'Mustard greens, cooking juices.' GF; 'Hake, celeriac purée, mussel butter' 24, 'From the day boats.' GF; 'Pumpkin dumplings, sage butter' 19, 'Aged sheep's cheese, toasted pumpkin seeds.' V; 'Roast partridge, bread sauce, blackberries' 28, 'A whole bird; allow twenty-five minutes.' IV. Cheese, 'Cut to order.' — 'Three cheeses, oatcakes, pear chutney' 14, 'Chosen weekly from small makers.' V. V. Sweet, 'Worth the room.' — 'Brown-butter cake, poached pear' 9, 'Crème fraîche, a little honey.' V; 'Baked apple custard, oat crumble' 8, 'Served warm.' V; 'Dark chocolate, rye, sea salt' 8, 'A small, rich slice.' V; 'Sorbet of the week' 6, 'Ask what the growers sent.' VG GF. VI. To drink, 'Ask about the wine.' — 'Pressed apple & sorrel soda' 5, 'Made in house, lightly sparkling.'; 'Quince & verjus spritz' 11, 'Low alcohol, bitter-sweet.'; 'Orange wine by the glass' 9, 'One of twelve; ask for the bottle list.'; 'Filter coffee' 4, 'From a roaster across town.'
Layout: Each course is a <section> with its id, 80px vertical padding and a 1px hairline top border. Columns 1–4 hold the Roman numeral as a 12px label (e.g. 'I.'), the course title in Fraunces italic 300 at clamp(2.25rem, 4vw, 3.5rem), and the note at 15px ink-soft (max 24ch, 12px top margin). This block is sticky at top 152px on desktop. Columns 6–12 hold the dish list. .dish-row is an <li> with 20px vertical padding and a 1px hairline bottom border (none on the last row). Line 1 is a flex row, items-baseline, gap 12px: the name in Fraunces 400 at clamp(1.25rem, 1.8vw, 1.5rem) with line-height 1.25; the .leader, which is flex-1 with min-width 24px, a 2px dotted ink-soft/50 border-bottom and translateY(-0.3em); and the price in Instrument Sans 500 at 17px with tabular-nums and no currency symbol. Line 2 is the description at 15px ink-soft (max 52ch, 6px top margin), followed inline by the .diet-tag chips (10px uppercase at 600, 1px hairline border, 1px 5px padding, 8px left margin). On mobile the course header stacks above the list, the name wraps, and the leader stays on the name's last line.
Motion: Course headers and dish rows reveal with an 80ms stagger. The dish-row hover effect is described in Design motion.
5. **Strip III — The market** — MARKET parallax strip placed between course III (Larger plates) and course IV (Cheese). The caption chip reads 'Plate II — Tuesday market crates'.
Layout: The same structure as the home Strip I, with height clamp(320px, 62vh, 640px).
Motion: Layer speeds are 0.08, 0.18 and 0.32.
6. **Chef's choice** — Label: 'For the table'. Title: 'Let the kitchen cook for you.' Text: 'Eight plates chosen by the kitchen for the whole table, sent as they're ready. £48 per person; please book ahead.' Text link: 'Book by phone' with arrow-right to tel:[PHONE].
Layout: Placed after course VI in columns 6–12 with a 64px top margin. A box with a 1px ink border, 40px padding (24px on mobile) and radius 0. The title is Fraunces 300 at clamp(1.75rem, 3vw, 2.5rem) with a 16px top margin. The text is 17px with a 16px top margin, and the link has a 24px top margin.
Motion: Reveals.
7. **Allergen note** — 'Please tell us about allergies when you book. A discretionary 12.5% service charge is added to the bill. The menu changes weekly and dishes may sell out.'
Layout: Columns 6–12, 40px top margin and 128px bottom padding, 13px ink-soft, max 60ch.
Motion: Reveals.
8. **Reservation band (shared)** — As on the home page.
Layout: As on the home page.
9. **Footer (shared)** — As on the home page.
Layout: As on the home page.
### Visit (`visit.html`)
1. **Header (shared)** — Identical to the home header. 'Visit' is the active page.
Layout: As on the home page.
2. **Visit hero** — Label row: 'Visit · [NEIGHBOURHOOD]' on the left and 'Open Wed–Sun' on the right. h1: 'Come hungry,' / 'stay late.' with the second line in italic terracotta. Note: 'Thirty-two seats, a six-seat counter facing the grill and a bar we keep for walk-ins.'
Layout: Same structure as the menu hero: the h1 (.display-xl) spans columns 1–8, and the note sits in columns 9–12, bottom-aligned, at 17px ink-soft, max 30ch.
Motion: h1 line entrance on load.
3. **Info grid** — Address: '[BRAND]', '14 Cooper's Yard', 'Eastmere EM2 4QT'. Hours, as a full table: Monday Closed; Tuesday Closed; Wednesday 17:30–23:00; Thursday 17:30–23:00; Friday 17:30–23:30; Saturday 12:00–15:00 & 17:30–23:30; Sunday 12:00–16:00. Under the table: 'The kitchen closes one hour before the room.' Contact: the [PHONE] tel: link and the [EMAIL] mailto: link, each with its Lucide icon, then 'Bookings by phone 10:00–17:00, Tuesday to Saturday. Emails answered within a day.' and 'Private hire: the whole room seats 32 — email us.'
Layout: 96px bottom padding. Three columns on desktop: Address in columns 1–3, Hours in columns 4–8 and Contact in columns 9–12. Each column has a 1px hairline left border (not the first), 24px left padding and starts with a 12px label heading (with Lucide map-pin, clock or phone at 14px). The address is Fraunces 300 at 28px with line-height 1.2. Hours rows are 15px with hairline bottom borders and 12px vertical padding, flex justify-between, in tabular-nums, and closed days are ink-soft. Today's row has a terracotta dot and a 'Today' tag, as on the home page. Contact links are 17px with .link-underline. On mobile the columns stack, each with a hairline top border.
Motion: Columns reveal with an 80ms stagger.
4. **Map** — An invented hand-drawn SVG street map (viewBox 0 0 1200 750) inside a figure with the caption 'Not to scale. Drawn by us.' Ground: paper-deep. Canal: a 56px #C9CDB8 stroke curving through (0,560), (420,520), (800,610) and (1200,540), with 1px ink-soft edge lines and the label 'Eastmere Canal'. Streets are 22px paper strokes with 1px hairline outlines: 'Tanner Row' runs horizontally at y 200; 'Ropemaker Walk' runs horizontally at y 470; 'Cask Lane' runs vertically at x 520 from y 200 to 470; 'Cooper's Yard' runs north from Ropemaker Walk at x 760 to a small square ending at y 340; 'Station Approach' runs from (250,640) through a footbridge over the canal at x 300 up to (330,470). About fourteen building blocks (#DDD2BE fill, hairline stroke) fill the gaps. The park 'Tanner's Green' is a polygon at x 840–1160, y 40–170, filled with a moss diagonal hatch at 35% opacity. 'Eastmere Station' is a rectangle at (90,640) measuring 200x70 with a track-hatch line. A bus stop is a small ink circle on Tanner Row at (600,200) labelled '38 · 41', and an arch marker is a small semicircle at (760,455). Route: a terracotta dashed path (3px stroke, dasharray '2 10', round caps) from (250,640) via (300,560) and (330,470), east along y 470 to (760,470), then north to (760,360), labelled '6 min walk' in Fraunces italic terracotta at (520,452). Pin: a 28x38 terracotta teardrop at (760,350) with a cream centre dot and a pulse ring. Next to the pin is a label card (paper fill, hairline border) with '[BRAND]' in Fraunces italic at 22px and '14 Cooper's Yard' in 11px uppercase. A compass circle with an 'N' arrow sits at the top left, and a scale bar labelled '100 m' sits at the bottom right. Street labels are Instrument Sans 11px uppercase with tracking 0.2em in ink-soft and are rotated along vertical streets. All labels come from content.ts.
Layout: Inside the container, spanning all 12 columns. The figure has a 1px hairline border and radius 0, with aspect-ratio 16/10 on desktop and 4/5 on mobile (the SVG uses preserveAspectRatio 'xMidYMid slice' and is centred on the pin). The wobble filter applies to the streets, blocks, canal and park groups but not to the text, pin or route. The caption is 12px ink-soft with a 12px top margin. The svg has role='img' and an aria-label describing the route.
Motion: Route draw, pin drop, label fade and pulse as described in Design motion.
5. **Getting here** — Three items, each with a Lucide icon. Footprints, 'On foot': 'Six minutes from Eastmere Station: up Station Approach, over the footbridge, right onto Ropemaker Walk, then left through the arch into Cooper's Yard. Bike racks in the yard.' Bus, 'By bus': 'Routes 38 and 41 stop on Tanner Row, two minutes away down Cask Lane.' Accessibility, 'Access': 'Step-free entrance from the yard, an accessible WC and space for two wheelchairs at the low tables. Call ahead and we will set one aside.'
Layout: 96px top padding and 128px bottom padding, three equal columns across 12 (columns 1–4, 5–8, 9–12), stacking on mobile. Each item has a 20px icon in a 40px square with a 1px hairline border, the title in Fraunces 400 at 24px (16px top margin) and the text at 15px ink-soft (max 36ch, 8px top margin).
Motion: Items reveal with an 80ms stagger.
6. **Strip IV — The room** — TABLE parallax strip (reused) with the caption chip 'Plate III — Thirty-two seats'.
Layout: The same structure as the home Strip I.
Motion: Layer speeds are 0.08, 0.18 and 0.32.
7. **Reservation band (shared)** — As on the home page.
Layout: As on the home page.
8. **Footer (shared)** — As on the home page.
Layout: As on the home page.
## Assets
- font: Fraunces Variable, upright and italic, from the @fontsource-variable/fraunces npm package (OFL-1.1). Import full.css and full-italic.css. Self-hosted and bundled by Vite.
- font: Instrument Sans Variable from the @fontsource-variable/instrument-sans npm package (OFL-1.1). Self-hosted and bundled by Vite.
- icon: Lucide icons from the lucide npm package (ISC), rendered with createIcons and limited to phone, mail, map-pin, clock, arrow-right, arrow-up-right, menu, x, footprints, bus and accessibility. All are 1.5px stroke in currentColor at 16–20px.
- image: Every image is drawn in code as inline SVG strings in src/art/: the hero plate, the still life, the TABLE, FIELD and MARKET strip layers, and the street map. The grain overlay is an SVG feTurbulence data URI in styles.css. Nothing is downloaded and there are no bitmaps.
- icon: Favicon: public/favicon.svg, hand-written as a 32x32 terracotta circle with a cream inner ring.
## Don'ts
- No backend of any kind: no server code, API routes, databases, auth or forms that submit anywhere. Booking happens only through tel: and mailto: links.
- No online booking form, reservation widget or third-party embed or API of any kind.
- No Google Maps, Mapbox, OpenStreetMap tiles, map libraries or real map data. The map is an invented hand-drawn SVG with invented street names and landmarks only.
- No real restaurant's name, logo, wordmark or slogan, and no copied copy, dish names or menu text from any real restaurant. All copy is invented for [BRAND].
- No photos from Unsplash, Pexels, Pixabay or other stock sites, no Google Images and no placeholder-image services. All imagery is code-drawn SVG.
- No people's faces as hero imagery, and no brand logos or brand icons (no Simple Icons).
- No commercial or custom fonts. Fontsource OFL fonts only, self-hosted. Never load anything from fonts.googleapis.com, fonts.gstatic.com, Adobe Fonts or any font CDN.
- No CDN <script> or <link> tags, no hotlinked files and no analytics. Every font, icon and script is bundled locally.
- No source maps in the production build, no src/ copied into dist/, and output must be minified. Keep dist/ under 20 MB.
- Internal links always point to file names (./index.html, ./menu.html, ./visit.html), never to '/' or a bare directory. Asset paths are relative (Vite base './').
- Do not change the stack: no React, Vue or other frameworks, no UI kits, and no Aceternity UI or Tailwind Plus/Tailwind UI source.
- No rounded corners (except the circular elements named in Design), no drop shadows, no gradients on UI elements and no emoji.
- Use only one UI accent color (terracotta). Moss, ochre and beet appear only inside illustrations.
- Under prefers-reduced-motion there must be no smooth scroll, parallax, reveals, headline entrances, count-ups, plate rotation, route drawing or pin pulse. Show all content in its final state.
- Zero console errors at 1440x900 and 390x844, and no horizontal page scroll on mobile (only the course index scrolls sideways).
- No SEO extras: no meta descriptions, Open Graph tags, JSON-LD or sitemaps. Use a plain <title> and the favicon only.
- Use no real phone numbers or domains. Keep the fictional defaults or the user's own values.
- Do not mention VibeFather anywhere in the site except the optional [CREDIT] footer link.
---
— Prompt by VibeFather · https://vibefather.ai/t/editorial-restaurant-website · CC BY-NC 4.0 (non-commercial; commercial use needs a VibeFather Pro license) · Request removal: https://vibefather.ai/takedown
Free for personal projects under CC BY-NC 4.0. Commercial use comes with Pro.
DESIGN.md
The design system on its own — colors, type, spacing and motion. Apply this look to a project you already have.
---
name: "Editorial Restaurant Website"
colors:
paper: "#F4EEE3"
paper_deep: "#EAE1D1"
ink: "#1E1B17"
ink_soft: "#5B544B"
hairline: "#CFC4B2"
terracotta: "#A8502F"
cream: "#FBF8F2"
moss: "#5F6B47"
ochre: "#C8963E"
beet: "#7B3343"
typography:
display_h1_h2_statements_dish_names_wordmarks_large_contact_links_use_upright_300_for_large_sizes_italic_300_for_accent_words_400_for_dish_names_and_the_header_wordmark_turn_font_optical_sizing_on: { family: "Fraunces", weights: [300, 400] }
text_and_labels_body_17px_1_6_at_400_prices_500_with_tabular_nums_labels_12px_at_600_uppercase_with_letter_spacing_0_18em_buttons_12px_at_600_uppercase_with_letter_spacing_0_16em: { family: "Instrument Sans", weights: [400, 500, 600] }
spacing: "12-column grid inside a container with max-width 1360px, 48px side padding on desktop and 24px on mobile, and 24px column gaps. Sections have 128px vertical padding on desktop and 80px on mobile. Use 24px between a label and its heading and 32px between a heading and its body text. Rules are 1px #CFC4B2 hairlines. Type scale: .display-xl clamp(3.5rem, 10.5vw, 10.5rem) at weight 300, line-height 0.92, tracking -0.035em; .display-l clamp(2.5rem, 6vw, 6rem) at 300, line-height 0.98, tracking -0.03em; .statement clamp(1.75rem, 3.2vw, 3rem) at 300, line-height 1.15, tracking -0.015em. Breakpoints: mobile below 768px, single column; the desktop grid starts at 1024px; tablet (768–1023px) uses the mobile stacking with wider padding."
radius: "0px everywhere: buttons, chips, tags, figures, cards and the map frame. Only intrinsically circular things are round: the map pin head, the today dot, the nav dot, the pulse ring and the plate drawings."
---
# Editorial Restaurant Website — Design System
Apply this design system to an existing project. It describes the look and feel only; for the full site, use prompt.md.
## Colors
- **Paper** `#F4EEE3` — Page background, header and course-index background, text on the dark band
- **Paper deep** `#EAE1D1` — Alternate section band (home menu preview), illustration panels, map ground
- **Ink** `#1E1B17` — All headings and body text, primary button fill, reservation band background
- **Ink soft** `#5B544B` — Secondary text, descriptions, captions, dotted leaders (at 50% opacity)
- **Hairline** `#CFC4B2` — 1px rules, borders, table lines, plate rims
- **Terracotta** `#A8502F` — The only UI accent: italic headline words, today dot, active nav dot, course-index underline, map pin and route, hover states
- **Cream** `#FBF8F2` — Plates, cloths and price tags inside illustrations
- **Moss** `#5F6B47` — Illustration only: leaves, hills, park hatch
- **Ochre** `#C8963E` — Illustration only: squash, pears, wheat, sky tint
- **Beet** `#7B3343` — Illustration only: beetroot, small food details
## Typography
- **Display: h1, h2, statements, dish names, wordmarks, large contact links. Use upright 300 for large sizes, italic 300 for accent words, 400 for dish names and the header wordmark. Turn font-optical-sizing on.** — Fraunces (OFL), weights 300/400. Self-host the font files.
- **Text and labels: body 17px/1.6 at 400, prices 500 with tabular-nums, labels 12px at 600 uppercase with letter-spacing 0.18em, buttons 12px at 600 uppercase with letter-spacing 0.16em** — Instrument Sans (OFL), weights 400/500/600. Self-host the font files.
## Spacing & shape
- Spacing: 12-column grid inside a container with max-width 1360px, 48px side padding on desktop and 24px on mobile, and 24px column gaps. Sections have 128px vertical padding on desktop and 80px on mobile. Use 24px between a label and its heading and 32px between a heading and its body text. Rules are 1px #CFC4B2 hairlines. Type scale: .display-xl clamp(3.5rem, 10.5vw, 10.5rem) at weight 300, line-height 0.92, tracking -0.035em; .display-l clamp(2.5rem, 6vw, 6rem) at 300, line-height 0.98, tracking -0.03em; .statement clamp(1.75rem, 3.2vw, 3rem) at 300, line-height 1.15, tracking -0.015em. Breakpoints: mobile below 768px, single column; the desktop grid starts at 1024px; tablet (768–1023px) uses the mobile stacking with wider padding.
- Radius: 0px everywhere: buttons, chips, tags, figures, cards and the map frame. Only intrinsically circular things are round: the map pin head, the today dot, the nav dot, the pulse ring and the plate drawings.
## Motion
- Smooth scroll: new Lenis({ duration: 1.1, easing: t => Math.min(1, 1.001 - Math.pow(2, -10 * t)) }) driven by one requestAnimationFrame loop that also runs the parallax. Anchor links call lenis.scrollTo(target, { offset: -140 }).
- Headline entrance on load: every h1 line is wrapped in an overflow-hidden block span. The inner span rises from translateY(105%) to 0 over 1000ms with cubic-bezier(0.22,1,0.36,1), 90ms stagger, starting at 150ms. The label row fades in over 400ms first.
- Scroll reveals: [data-reveal] elements start at opacity 0 and translateY(24px) (only under html.js) and move to opacity 1 and 0 over 900ms with cubic-bezier(0.22,1,0.36,1). They are triggered once by an IntersectionObserver with threshold 0.15 and rootMargin '0px 0px -8% 0px'. Items in a group stagger 80ms through a --d CSS variable.
- Parallax strips: each strip holds absolutely positioned layers (top -20%, height 140%) with data-speed (back 0.08, mid 0.18, front 0.32, overlay text 0.45). Per frame, for strips flagged in view by an IntersectionObserver, compute p = clamp((innerHeight - rect.top) / (innerHeight + rect.height), 0, 1) and set each layer to transform: translate3d(0, (0.5 - p) * speed * rect.height px, 0) with will-change: transform.
- Hero plate: the plate group inside the hero figure rotates from 0deg to 10deg, linked to scroll progress through the hero.
- Count-up: the home stats count from 0 to their value over 1200ms with easeOutCubic the first time they enter view.
- Links: nav and text links use a 1px currentColor underline drawn with background-size, growing from 0% to 100% left to right over 400ms on hover. Persistent CTA text links keep the underline and it turns terracotta on hover.
- Dish rows (hover-capable pointers only): the name shifts 4px right and the dotted leader turns terracotta over 300ms.
- Reservation links: on hover the text shifts 8px right, the arrow-up-right icon rotates 45deg and the row's top border turns terracotta, all over 300ms.
- Map: when 30% of the map is in view, the dashed route draws once through an SVG mask whose solid path animates stroke-dashoffset from its length to 0 over 1800ms ease-in-out. The pin then drops from translateY(-24px) with opacity 0 to rest over 500ms with cubic-bezier(0.34,1.56,0.64,1), the label card fades in over 300ms, and a pulse ring under the pin loops (r 10 to 34, opacity 0.5 to 0, 2400ms ease-out, infinite).
- Mobile nav overlay: clip-path moves from inset(0 0 100% 0) to inset(0) over 600ms with cubic-bezier(0.22,1,0.36,1). Links rise 24px and fade in with a 70ms stagger. Lenis stops while the overlay is open.
- Footer giant wordmark: letters rise 40% and fade in with a 40ms stagger when the footer enters view.
- prefers-reduced-motion: reduce: Lenis is never created, parallax transforms are not applied, all [data-reveal] content and headline lines show immediately, count-ups show final values, the hero plate does not rotate, the map route is drawn statically with the pin in place and no pulse, and all CSS transitions and animations are set to 0.01ms.
## Imagery
There are no photographs. All imagery is flat, slightly textured illustration drawn as inline SVG strings in src/art/, using only the palette (cream, paper, paper-deep, ink, ink-soft, hairline, terracotta, moss, ochre, beet). Shapes are simple and geometric with a hand-drawn wobble from one shared SVG filter (feTurbulence type 'fractalNoise' baseFrequency 0.015 numOctaves 2 seed 4, then feDisplacementMap scale 3) applied to shape groups and never to text. A global grain overlay (.grain) is fixed at inset 0 with pointer-events none, z-index 60, opacity 0.07 and mix-blend-mode multiply; its background is a 200px tiled SVG data URI of feTurbulence fractalNoise at baseFrequency 0.8 with 2 octaves. HERO PLATE (viewBox 0 0 400 500): a paper-deep background; a cream plate at (200,250), r 160, with a 1.5px hairline rim and a 1px inner ring at r 120; five ochre squash crescents with lighter #E2B86A inner arcs fanned at 72deg intervals around the centre; six moss sage leaves with cream midribs; a wavy brown-butter drizzle (stroke #9A6A2F, 3px, 70% opacity); fourteen small ink seed ellipses; and the corner of a terracotta linen napkin at the bottom-left, rotated -8deg, with three cream stripe lines. STILL LIFE (viewBox 0 0 360 480): a paper-deep background, a cream cloth as a wavy polygon over the bottom third, two ochre pears with ink stems, one lumpy #D9B04C quince, three ink-soft walnuts with crease lines, and soft ink shadow ellipses at 8% opacity. STRIPS (every layer uses viewBox 0 0 1600 900 with preserveAspectRatio 'xMidYMid slice'). TABLE: the back layer is #E6DCC9 linen with a 6px crosshatch of ink lines at 8% opacity and a 220px terracotta table runner across the middle with three thin cream stripes. The mid layer has seven cream plates (r 90 to 170) with hairline rims, each holding a different simple dish: ochre squash crescents, beet discs with a cream swirl, a moss leaf cluster, ink seed dots, or a terracotta tartare oval with an ochre yolk circle. The front layer has a knife and fork as long rounded ink-soft shapes and two wine-glass rings (terracotta 4px stroke at 35% opacity). FIELD: the back layer is a sky gradient from #EBCB9C at the top to #F4EEE3 at the horizon, with a terracotta sun disc of r 90 at (1150,300). The mid layer has two rolling hill bands (#8A9470 behind, #5F6B47 in front) crossed by twelve parallel curved furrow strokes in cream at 25% opacity. The front layer is a dark #2B2A22 hedgerow of overlapping bumps along the bottom 30%, with a few ochre wheat-stalk strokes. MARKET: the back layer is an ink background with horizontal ink-soft crate slats (40px tall, 14px gaps). The mid layer has three crates seen straight on, packed with produce: terracotta tomatoes with moss star calyxes, ochre pears with ink stems, moss cabbages made of concentric leaf arcs, and beet roots with leaf stalks. The front layer has four cream price tags on ink strings, lettered in Fraunces italic with labels from content.ts ('Quince 3', 'Pears 2', 'Chard 4', 'Squash 5'). FAVICON (public/favicon.svg, 32x32): a terracotta circle with a cream inner ring, like a plate seen from above.
## Tone
Calm, warm and literate, like a printed menu card or a food magazine. Short plain sentences in the first-person plural ('we'), with concrete ingredients, places and times. No exclamation marks, superlatives or marketing clichés.
---
— Prompt by VibeFather · https://vibefather.ai/t/editorial-restaurant-website · CC BY-NC 4.0 (non-commercial; commercial use needs a VibeFather Pro license) · Request removal: https://vibefather.ai/takedown