This file tells Claude how to work with the Vinebound codebase. Read it before making any content changes.
Note for Claude: These guidelines are intentionally brief for now and will be expanded. Always apply what's here — and when in doubt, err on the side of warm, knowledgeable, and unpretentious.
People who love wine but aren't snobs about it. Visitors planning a day out, a weekend away, or a dog walk with a glass at the end. They want to know what a vineyard is actually like — not just what it grows.
[[SECTION:...]] headings should be descriptive and natural — they appear in the on-page TOC and as anchor links[[SECTION:...]]) should summarise the page helpfully — they're the most important text for both readers and search.This section will be expanded with examples, specific guide templates, and imagery guidelines.
All site content lives in Google Sheets. Claude uses a local CLI to read and write them directly.
# List all tabs in a spreadsheet
node scripts/sheets.js tabs <spreadsheetId>
# Read all rows as JSON
node scripts/sheets.js read <spreadsheetId> <tabName>
# Append a new row
node scripts/sheets.js append <spreadsheetId> <tabName> '<json>'
# Update an existing row (rowIndex is 1-based, after the header row)
node scripts/sheets.js update <spreadsheetId> <sheetName> <rowIndex> '<json>'
# Clear a row
node scripts/sheets.js clear <spreadsheetId> <sheetName> <rowIndex>
All commands must be run from /Users/stephenpritchard/Desktop/vinebound-site.
| Spreadsheet ID | 11QNw1yUXW322XVOLyc9Mq-cacLx8YDbj8WnUAEtPnkE |
| Tab name | blog-content |
Columns:
| Column | Required | Notes |
|---|---|---|
status |
✅ | published or draft. Draft posts are hidden from the site. |
title |
✅ | Post title |
slug |
✅ | URL slug e.g. my-post-title. Must be unique. Lowercase, hyphens only. |
date |
✅ | Preferred format: dd/MM/yyyy (e.g. 30/03/2026). Also accepts yyyy-MM-dd. |
excerpt |
✅ | Short summary shown on listing pages |
cover |
— | Image path e.g. /assets/images/vineyards/ridgeview-main.JPG |
body_html |
✅ | Full post body as HTML. Supports <h2>, <h3>, <p>, <ul>, <a>, etc. |
Example — add a new draft post:
node scripts/sheets.js append 11QNw1yUXW322XVOLyc9Mq-cacLx8YDbj8WnUAEtPnkE blog-content '{"status":"draft","title":"Spring Vineyard Events 2026","slug":"spring-vineyard-events-2026","date":"30/03/2026","excerpt":"The best vineyard events this spring across England and Wales.","body_html":"<p>Spring is here and the vineyards are busy...</p>"}'
Note: Posts can also be created as .njk or .md files in grapevine/posts/. Both methods work. The sheet method is preferred for bulk content; file method is preferred for long-form articles needing custom templates.
| Spreadsheet ID | 1bEM9fpwuQyHO50ScZ5pLZfs4mpYVAGJbxvhat1mr7Is |
| Tab name (main) | Published |
| Tab name (editorial copy) | vineyards_copy |
Key columns in Published:
| Column | Notes |
|---|---|
published |
TRUE or FALSE. Only TRUE rows appear on the site. |
name |
Vineyard display name |
slug |
URL slug — must be unique, lowercase, hyphens only |
lat / lon |
Decimal coordinates for the map pin |
county / state |
e.g. Kent, England |
website |
Full URL with UTM params appended |
published |
Must be TRUE to appear on site |
Various has*Extract fields |
e.g. dogFriendlyExtract, hasPublicToursExtract — set to Yes to enable filter |
Key columns in vineyards_copy:
| Column | Notes |
|---|---|
slug |
Must match the slug in Published tab |
shortDescription |
1–2 sentence teaser shown on listing cards |
longDescription |
Full editorial HTML content shown on vineyard detail page |
⚠️ Do not edit vineyards without being asked — the dataset is large and updates propagate to map pins, filters, and detail pages.
| Spreadsheet ID | 1_6i3krVo6ML6grp-fgJhLsV1yS07fQ_7_oGmjIwIqLE |
| Tab name | Make.com Data Entry |
Columns:
| Column | Required | Notes |
|---|---|---|
moderation_status |
✅ | approved to appear on site |
vineyard_name |
✅ | Display name of the vineyard |
vineyard_slug |
✅ | Must match a slug in the vineyards sheet |
event_title |
✅ | |
event_type |
✅ | e.g. Tour & Tasting, Seasonal, Festival |
start_date |
✅ | Format: yyyy-MM-dd |
end_date |
— | Format: yyyy-MM-dd. Leave blank if single day. |
county |
✅ | |
postcode |
✅ | |
description_short |
✅ | 1–2 sentences |
booking_url |
— | Direct booking link |
status |
✅ | scheduled |
| Spreadsheet ID | 1NQQHzbuRzrKUEbdncADbNt7l8klHptwv4mtXAkvVePA |
| Tab name | Published |
Key columns: slug, title, type (theme or region), region, county, intro, full_text (HTML), published (TRUE/FALSE), vineyard_slugs (comma-separated).
| Spreadsheet ID | 1kgGwk2hZ8MfcHJfcq6mQrgEiHgtc0-H_ptKLx77HXF8 |
| Tab name | Published |
Key columns: slug, title, level (1/2/3), type (region/county/town), region, county, town, summary (HTML), full_text (HTML), published (TRUE/FALSE).
| Spreadsheet ID | 1yTjBNgpJS12gDCyDbSbLAtuHwvQdeghGG-BKFKh0LLg |
| Tab name | filter_config |
This drives the vineyard map filters. Do not edit without being asked.
node scripts/sheets.js append 11QNw1yUXW322XVOLyc9Mq-cacLx8YDbj8WnUAEtPnkE blog-content \
'{"status":"draft","title":"TITLE","slug":"slug-here","date":"30/03/2026","excerpt":"EXCERPT","body_html":"<p>BODY</p>"}'
First, find its row number:
node scripts/sheets.js read 11QNw1yUXW322XVOLyc9Mq-cacLx8YDbj8WnUAEtPnkE blog-content
Rows are 1-indexed starting after the header. Then:
node scripts/sheets.js update 11QNw1yUXW322XVOLyc9Mq-cacLx8YDbj8WnUAEtPnkE blog-content <rowNumber> '{"status":"published"}'
node scripts/sheets.js read 11QNw1yUXW322XVOLyc9Mq-cacLx8YDbj8WnUAEtPnkE blog-content
node scripts/sheets.js append 1_6i3krVo6ML6grp-fgJhLsV1yS07fQ_7_oGmjIwIqLE "Make.com Data Entry" \
'{"moderation_status":"approved","vineyard_name":"...","vineyard_slug":"...","event_title":"...","event_type":"Tour & Tasting","start_date":"2026-04-15","county":"Kent","postcode":"TN1 1AB","description_short":"...","status":"scheduled"}'
node scripts/sheets.js tabs <id> to check if unsure.dd/MM/yyyy (e.g. 30/03/2026). ISO yyyy-MM-dd also works. Never use US format MM/dd/yyyy.yyyy-MM-dd.status: "draft" hides a Grapevine post from the site. Use this for work-in-progress.published: "FALSE" hides a vineyard. moderation_status controls event visibility.clear only if explicitly asked. Prefer updating status to draft / published: FALSE instead.npm start.The full_text and longDescription fields support special markers that inject dynamic content or structure the page into collapsible sections. Use these in guide and vineyard copy written into the Google Sheets.
[[SECTION:Heading Text]]Splits the content into a collapsible accordion section with the given heading. Everything between one [[SECTION:...]] marker and the next becomes the body of that section. Any content written before the first [[SECTION:...]] marker becomes an intro/overview that sits above the accordion and is always visible.
A "On this page" table of contents is automatically generated from all section headings when there are 2 or more sections.
Example:
<p>This is the intro — always visible, not in a section.</p>
[[SECTION:Getting There]]
<p>Take the A272 towards Petworth...</p>
[[SECTION:What to Expect]]
<p>Tours run on the hour from 11am...</p>
[[SECTION:The Wine]]
<p>They're best known for their Bacchus...</p>
Rules:
SECTION is required: [[SECTION:Title]] ✅ — [[SECTION Title]] ❌[[section:Title]] and [[Section:Title]] are both normalised automatically#getting-there) — keep them short and descriptivefull_text (guides), full_text (area-guides), longDescription (vineyards_copy)[[FEATURED_VINEYARDS]]Inserts a grid of featured vineyard cards at that exact position in the content. Which vineyards appear is controlled by the vineyard_slugs column of the guide row (comma-separated slugs).
Example in full_text:
<p>Here are some of the best vineyards in Kent for a weekend visit.</p>
[[FEATURED_VINEYARDS]]
<p>All of the above offer public tours — booking ahead is recommended.</p>
Rules:
[[FEATURED_VINEYARDS]] marker per guide — additional ones are ignoredvineyard.njk strips it automatically)vineyard_slugs in the sheet row, not by the marker position[[MAP]]Inserts the interactive vineyard map at that position, filtered to show only vineyards relevant to the current guide (by county, region, or explicit slug list).
Example:
[[SECTION:Explore the Map]]
Use the map below to find vineyards near you.
[[MAP]]
[[SECTION:Planning Your Visit]]
<p>Most vineyards in this area are open April–October...</p>
Rules:
[[SECTION:...]] block or in the intro[[FAQ]]Renders a structured FAQ block from the guide's faq_html or faq_json column. Place the marker where you want the FAQ to appear in the content.
[[SECTION:Frequently Asked Questions]]
[[FAQ]]
Grapevine posts can also live as files in grapevine/posts/. Use this method for posts that need custom layouts or are very long. File posts take priority and are merged with sheet posts at build time.
File naming: grapevine/posts/my-slug.njk
Frontmatter:
---
title: My Post Title
date: 2026-03-30
excerpt: Short description
cover: /assets/images/vineyards/example.jpg
layout: layouts/grapevine-post.njk
---
Then write the post body in HTML or Markdown below the frontmatter.