# Build a CubeSites website with JSON

CubeSites is a visual website builder for UK businesses. This guide and the JSON Schema are generated from the renderer, which defines the site's blocks and allowed values. Give this guide and the schema to your AI assistant, ask it for a complete version 2 JSON file, then validate the file before importing it.

## File shape

A file has `format` set to `cubesites-site`, `version` set to `2`, a name, optional theme/globals/settings/SEO, pages, and optional posts, redirects, service areas and assets. Each page has a title, a slug (empty string for the home page), an optional language code (empty for the main language), and sections. Section props and style keys come from the block catalogue below. Use only keys defined by the schema.

```json
{
  "format": "cubesites-site",
  "version": 2,
  "name": "Your Company",
  "theme": {
    "palette": "charcoal",
    "fontPair": "warm-friendly",
    "styles": [
      {
        "id": "gbrand1",
        "name": "Brand button",
        "kind": "button",
        "props": {}
      }
    ]
  },
  "globals": {
    "nav": {
      "logoText": "Your Company"
    }
  },
  "pages": [
    {
      "title": "Home",
      "slug": "",
      "sections": [
        {
          "type": "hero",
          "layout": {
            "preset": "centred"
          },
          "props": {},
          "style": {
            "texture": "grain",
            "padding": "lg"
          }
        }
      ]
    },
    {
      "title": "Services",
      "slug": "services",
      "sections": [
        {
          "type": "slideshow",
          "props": {},
          "style": {
            "padding": "md"
          }
        }
      ]
    },
    {
      "title": "Contact",
      "slug": "contact",
      "sections": [
        {
          "type": "contact",
          "props": {},
          "style": {
            "padding": "md"
          }
        }
      ]
    }
  ]
}
```

## Blocks

- **hero**: The first thing visitors see: a big heading, a line of supporting text, buttons and an optional image.. Fields: eyebrow (text, max 60); heading (text, max 160); subheading (textarea, max 400); primary (link); secondary (link); image (image); video (video); points (list, max 6, up to 6 items); fields: icon, text; pointsStyle (select); values: stacked, inline, band; textPosition (select); values: centre, left, band; shade (select); values: none, light, medium, strong, darkest; imageShape (select); values: rounded, square, arch, circle, leaf; imageFrame (select); values: none, block, outline, dots; frameColour (select); values: accent, primary, secondary, surface, white; layout (select); values: centred, split-right, split-left; imageSide (select); values: right, left; height (select); values: auto, short, tall, screen; layout presets: centred (Centred), split-right (Image right), split-left (Image left), media-top (Image above), media-background (Image behind), stacked-left (Image below), split-bleed (The words in the content column and the picture filling the other half of the screen, edge to edge and full height); elements: eyebrow, heading, text, buttons, media.
- **slideshow**: Full-width pictures that take turns, each with a heading, a line or two of text and a link. Good for showing a few offers or services at the top of the home page.. Fields: slides (list, max 8, up to 8 items); fields: image, heading, text, link; caption (select); values: band, centre, left; height (select); values: short, medium, tall; autoplay (toggle); interval (number, max 15).
- **features**: A grid of short points, each with an icon, a title and a sentence. Good for benefits, reasons to choose you or what is included.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); columns (select); values: 2, 3, 4; style (select); values: cards, plain, rows; iconStyle (select); values: badge, circle, outline, large; items (list, max 12, up to 12 items); fields: icon, title, text, link; headLayout (select); values: stacked, beside; layout presets: cards (Cards), plain (Plain), rows (Icon beside text); elements: eyebrow, heading, intro, items.
- **media-text**: An image beside a block of text, with optional ticked points and a button. Good for "about us" and explaining one idea in depth.. Fields: eyebrow (text, max 60); heading (text, max 160); body (richtext); bullets (list, max 8, up to 8 items); fields: text; primary (link); image (image); imageSide (select); values: left, right; imageBleed (toggle); imageShape (select); values: rounded, square, arch, circle, leaf; imageFrame (select); values: none, block, outline, dots; frameColour (select); values: accent, primary, secondary, surface, white; layout presets: image-left (Image left), image-right (Image right), image-top (Image above); elements: eyebrow, heading, body, bullets, button, media.
- **services**: Cards for each service you offer, with an optional image, a guide price and a link to more detail.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); columns (select); values: 2, 3, 4; showFilters (toggle); fillRow (toggle); imageFit (select); values: cover, contain; imageEdge (toggle); linkStyle (select); values: link, button; items (list, max 12, up to 12 items); fields: icon, image, title, text, price, tags, link; headLayout (select); values: stacked, beside; layout presets: cards (Cards), plain (Plain), overlay (Text on the photo); elements: eyebrow, heading, intro, items.
- **places-map**: A world map with a pin for each place, linking to its page, and a list of the places for everyone who cannot see the map. Shown as a dotted illustration or as a street map you can zoom, with buttons to switch between groups of places.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); display (select); values: dotted, street; height (select); values: sm, md, lg; width (select); values: container, full; mapStyle (select); values: light, bright, liberty, mapbox-streets, mapbox-outdoors, mapbox-light, mapbox-dark, mapbox-navigation-day, mapbox-navigation-night, mapbox-satellite, mapbox-satellite-plain, mapbox-custom; mapboxStyle (text, max 200); pinStyle (select); values: label, dot, drop; controls (toggle); scrollZoom (toggle); pinColour (select); values: auto, accent, primary, secondary, dark, custom; pinCustom (color); mapColour (select); values: natural, muted, greyscale, brand; zoom (number, max 18); pitch (number, max 60); bearing (number, max 180); region (select); values: fit, world, europe, northern-europe, mediterranean, caribbean, north-america, south-america, africa, asia, oceania, polar-south; showNames (toggle); showList (toggle); places (list, max 60, up to 60 items); fields: name, lat, lng, note, group, image, marker, link; headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, map, list.
- **breadcrumbs**: A trail of links to the pages above this one, worked out from its address.. Fields: showHome (toggle).
- **page-list**: Cards for the pages under an address, such as every destination under /destinations, using each page's search description and share image.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); parent (text, max 191); columns (select); values: 2, 3, 4; showImages (toggle); showDescriptions (toggle); cardStyle (select); values: cards, overlay; linkLabel (text, max 40); headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, items.
- **stats**: A row of figures with labels. Only for real figures the business gives you; never invent numbers.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); animate (toggle); look (select); values: plain, divided, cards; items (list, max 6, up to 6 items); fields: value, label; headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, items.
- **testimonials**: Quotes from customers with their name and role. Only use real quotes the business supplies; leave placeholders otherwise.. Fields: eyebrow (text, max 60); heading (text, max 160); layout (select); values: grid, single; items (list, max 9, up to 9 items); fields: quote, name, role, rating, photo; layout presets: grid (Grid), single (One large quote), featured-plus-grid (Featured and grid); elements: eyebrow, heading, items.
- **team**: Photos, names and roles of the people on the Team page. People are managed in Team, or synced from CubeHR when it is connected.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); columns (select); values: 3, 4; department (text, max 80); showBio (toggle); showSocial (toggle); limit (number, max 100); headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, items.
- **gallery**: A grid of photos that open larger when clicked.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); layout (select); values: grid, masonry; columns (select); values: 2, 3, 4; lightbox (toggle); showFilters (toggle); items (list, max 48, up to 48 items); fields: image, caption, category; headLayout (select); values: stacked, beside; layout presets: grid (Even grid), masonry (Masonry); elements: eyebrow, heading, intro, filters, items.
- **image**: One image with an optional caption.. Fields: image (image); caption (text, max 200); href (text, max 255).
- **video**: A YouTube or Vimeo video, played without tracking cookies until the visitor presses play.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); url (text, max 255); caption (text, max 200); headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, player.
- **map**: A map of an address or postcode with a link to directions.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); query (text, max 200); height (select); values: sm, md, lg; provider (select); values: google, street; lat (number, max 90); lng (number, max 180); placeName (text, max 60); mapStyle (select); values: light, bright, liberty, mapbox-streets, mapbox-outdoors, mapbox-light, mapbox-dark, mapbox-navigation-day, mapbox-navigation-night, mapbox-satellite, mapbox-satellite-plain, mapbox-custom; mapboxStyle (text, max 200); pinStyle (select); values: label, dot; pinColour (select); values: auto, accent, primary, secondary, dark, custom; pinCustom (color); pinImage (image); mapColour (select); values: natural, muted, greyscale, brand; zoom (number, max 18); pitch (number, max 60); bearing (number, max 180); controls (toggle); scrollZoom (toggle); headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, map, directions.
- **pricing**: Packages or plans side by side with what each includes. Only real prices the business gives you.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); items (list, max 4, up to 4 items); fields: name, price, annualPrice, period, description, features, cta, featured, badge; billingToggle (toggle); monthlyLabel (text, max 30); annualLabel (text, max 30); note (text, max 200); headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, items, note.
- **faq**: Frequently asked questions that open and close. Also tells search engines about the questions.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); items (list, max 30, up to 30 items); fields: question, answer; headLayout (select); values: stacked, beside; layout presets: list (One list), two-columns (Two columns), intro-aside (Introduction beside); elements: eyebrow, heading, intro, items.
- **contact**: A contact form that emails the business, with spam protection, plus contact details alongside.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); showPhone (toggle); showCompany (toggle); showMessage (toggle); messageLabel (text, max 60); questions (list, max 15, up to 15 items); fields: label, kind, options, dates, required; formName (text, max 80); submitLabel (text, max 40); successMessage (text, max 200); consentText (textarea, max 400); consentLink (link); acknowledge (toggle); consentRequired (toggle); formColumns (select); values: 1, 2; showDetails (toggle); image (image); email (text, max 255); phone (text, max 40); address (textarea, max 300); hours (textarea, max 300); createTask (toggle); taskProject (connect-project); headLayout (select); values: stacked, beside; layout presets: aside-right (Details on the right), aside-left (Details on the left), stacked (Details below); elements: eyebrow, heading, intro, body.
- **cta**: A short, bold prompt to take the next step, with one or two buttons. Often the last section on a page.. Fields: heading (text, max 160); text (textarea, max 400); primary (link); secondary (link); style (select); values: card, band, split; layout presets: card (Card), band (Centred band), split (Buttons beside); elements: heading, text, buttons.
- **logo-bar**: A row of client, partner or accreditation logos.. Fields: heading (text, max 120); mono (toggle); scroll (toggle); style (select); values: row, tiles; tileColumns (select); values: 4, 5, 6; fade (toggle); link (link); items (list, max 48, up to 48 items); fields: image, name, href; elements: heading, items.
- **steps**: A numbered process: how working with the business goes, from first contact to finish.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); items (list, max 6, up to 6 items); fields: title, text; headLayout (select); values: stacked, beside; layout presets: columns (Side by side), list (One under another); elements: eyebrow, heading, intro, items.
- **page-title**: The page’s name as a plain heading, with an optional line under it. For pages that open without a picture.. Fields: heading (text, max 160); intro (textarea, max 400).
- **rich-text**: Free-form text with headings, lists, quotes and links. Use for policies, longer explanations and anything that does not fit another block.. Fields: content (richtext); docStyle (select); values: plain, card, contents, numbered, accordion.
- **blog-latest**: The newest posts from the site blog (or news page), updating by itself as posts are published.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); count (number, max 12); showExcerpt (toggle); linkLabel (text, max 60); headLayout (select); values: stacked, beside; layout presets: grid (Grid), featured (Newest beside the rest); elements: eyebrow, heading, intro, items, all.
- **countdown**: A live countdown to a launch, sale or event.. Fields: heading (text, max 160); text (textarea, max 300); target (datetime); finishedText (text, max 160).
- **quote**: One large quotation with who said it. Good for a founder statement or a standout line from a customer.. Fields: quote (textarea, max 600); name (text, max 80); role (text, max 100); photo (image); size (select); values: medium, large; elements: quote, person.
- **timeline**: Moments in order, each with a date, a title and a sentence. Good for your history or how a project runs.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); items (list, max 16, up to 16 items); fields: date, title, text; headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, items.
- **hours**: Your opening times day by day, with a note for holidays and optional address and phone number.. Fields: heading (text, max 160); items (list, max 14, up to 14 items); fields: day, hours; note (textarea, max 300); address (textarea, max 300); phone (text, max 40); liveStatus (toggle); elements: card, details.
- **timetable**: A weekly schedule of sessions, such as classes, clinics, surgery hours or course times, by day with times, filters by category and today marked.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); layout (select); values: grid, list, agenda; showFilters (toggle); highlightToday (toggle); sessions (list, max 80, up to 80 items); fields: day, start, end, title, detail, category, link; note (textarea, max 300); headLayout (select); values: stacked, beside; layout presets: grid (Days side by side as columns on wide screens), list (Each day a row, its sessions listed beside it), agenda (A short line per day with each session as a time and a name); elements: eyebrow, heading, intro, filters, items, note.
- **price-list**: Items with a short description and a price, like a menu or a list of treatments. Prices come from the business, never from AI.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); columns (select); values: 1, 2; items (list, max 40, up to 40 items); fields: name, description, price, tag; headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, items.
- **comparison**: Features down the side and two or three options across the top, with ticks, crosses or short text.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); colA (text, max 40); colB (text, max 40); colC (text, max 40); highlight (select); values: none, a, b, c; rows (list, max 30, up to 30 items); fields: feature, a, b, c; headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, items.
- **checklist**: A tidy list of ticked points in one or two columns. Good for what is included or who you work with.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); columns (select); values: 1, 2, 3; items (list, max 30, up to 30 items); fields: text; headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, items.
- **notice**: An important message in a coloured box: a closure, a new service or what to do in an emergency.. Fields: tone (select); values: info, success, warning, urgent; icon (icon); heading (text, max 160); text (textarea, max 600); link (link); layout (select); values: card, band; dismissible (toggle); showFrom (datetime); showUntil (datetime); layout presets: card (Box), band (Band across the page); elements: icon, heading, text, link.
- **banner**: A slim strip with one line of text and a button. Good for an offer, an event or news.. Fields: text (text, max 200); primary (link); icon (icon); layout presets: inline (In a row), centred (Centred), stacked (Stacked); elements: icon, text, button.
- **social**: Buttons to your social profiles, so visitors can follow you.. Fields: heading (text, max 160); intro (textarea, max 600); items (list, max 10, up to 10 items); fields: network, url; headLayout (select); values: stacked, beside; elements: heading, intro, items.
- **signup**: A short form that collects email addresses for your mailing list, with spam protection. Sign-ups are kept under Subscribers, ready to export.. Fields: heading (text, max 160); intro (textarea, max 600); askName (toggle); formName (text, max 80); submitLabel (text, max 40); successMessage (text, max 200); consentText (textarea, max 400); consentLink (link); layout presets: stacked (Form below), split (Form beside); elements: heading, intro, form.
- **tabs**: Tabbed panels, each with a heading, text, ticked points, a picture and a button. Good for kinds of customer, treatment areas or levels of service side by side.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); style (select); values: underline, pills, vertical; items (list, max 8, up to 8 items); fields: label, icon, heading, body, points, image, link; headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, items.
- **calculator**: A price estimator: visitors choose options, see a running total and send it with an enquiry. Only real prices the business gives you.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); baseLabel (text, max 80); baseAmount (number, max 1000000); period (select); values: one-off, month, year; groups (list, max 8, up to 8 items); fields: label, kind, options, required; vatNote (text, max 120); disclaimer (textarea, max 300); cta (link); headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, items.
- **bento**: A grid of mixed-size cards: words, pictures, a big figure or an icon. Good for an overview of what you offer at a glance.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); gap (select); values: sm, md, lg; items (list, max 12, up to 12 items); fields: size, kind, tone, icon, title, text, image, link; headLayout (select); values: stacked, beside; elements: eyebrow, heading, intro, items.
- **marquee**: A slowly scrolling strip of short words or phrases, such as what a studio offers.. Fields: items (list, max 16, up to 16 items); fields: text, icon; size (select); values: sm, md, lg, xl; speed (select); values: slow, medium, fast; direction (select); values: left, right; separator (select); values: dot, star, slash, icon, none; pauseOnHover (toggle).
- **downloads**: Documents visitors can download, such as a price list, a brochure or a form, each with its type and size.. Fields: heading (text, max 160); intro (textarea, max 600); layout (select); values: list, cards; showDetails (toggle); buttonLabel (text, max 40); items (list, max 30, up to 30 items); fields: file, label, description; headLayout (select); values: stacked, beside; elements: heading, intro, items.
- **custom-code**: Your own HTML, such as a booking widget or a third-party embed. It runs in a sealed frame so it cannot reach the rest of the page.. Fields: title (text, max 120); html (code, max 50000); height (number, max 3000).
- **spacer**: Extra vertical space between sections.. Fields: size (select); values: sm, md, lg.
- **divider**: A line to separate content.. Fields: style (select); values: line, thick, dots.
- **columns**: Build your own section: one to four columns, each with headings, text, buttons, images and more, in any order.. Fields: ratio (select); values: equal, 2-1, 1-2, 1-1-2, 2-1-1, 1-2-1; gap (select); values: sm, md, lg; valign (select); values: top, center, bottom; stack (toggle); phoneOrder (select); values: normal, reverse; columns (list, max 4, up to 4 items); fields: items; elements: columns.
- **shop-basket**: A link to the shop basket showing how many items are in it. With the slide-in basket it opens it. Needs your own shop.. Fields: label (text, max 30); showCount (toggle).
- **product-grid**: Products from your catalogue: featured, newest, on offer, from a category or a collection, or ones you choose. With prices, and a quick add to basket when the shop is on.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); source (select); values: featured, category, tag, offers, newest, chosen; category (catalogue-category); tag (catalogue-tag); items (catalogue-items, max 24); count (number, max 24); columns (select); values: 2, 3, 4; layout (select); values: grid, carousel; quickAdd (toggle); link (link); headLayout (select); values: stacked, beside.
- **product-spotlight**: One product, large: its picture beside your words, its price and a button to buy it or see it.. Fields: item (catalogue-item); layout (select); values: image-left, image-right; eyebrow (text, max 60); heading (text, max 160); text (textarea, max 600); showFeatures (toggle); buttonLabel (text, max 30).
- **category-tiles**: Your product categories as picture tiles, cards or circles, each linking to its page.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); source (select); values: top, children, chosen; parent (catalogue-category); categories (catalogue-categories, max 24); style (select); values: tiles, cards, circles; columns (select); values: 2, 3, 4, 5, 6; showCounts (toggle); headLayout (select); values: stacked, beside.
- **shop-search**: A search box for your products, with suggestions as people type.. Fields: heading (text, max 160); intro (textarea, max 600); placeholder (text, max 80); headLayout (select); values: stacked, beside.
- **products**: Products and prices from CubeERP, through CubeConnect. For your own shop, use the Shop blocks.. Fields: eyebrow (text, max 60); heading (text, max 160); intro (textarea, max 600); columns (select); values: 2, 3, 4; count (number, max 24); category (shop-category, max 80); buttonLabel (text, max 30); headLayout (select); values: stacked, beside.
- **client-portal**: A sign-in panel for your clients to raise and follow support requests in CubeMSP.. Fields: heading (text, max 160); text (textarea, max 300); buttonLabel (text, max 40).

## Design values

Palette IDs: charcoal, harbour, forest, terracotta, indigo, plum, ocean, sand, midnight. Font-pair IDs: warm-friendly, modern-clean, editorial, quiet-professional, creative-studio, clear-humanist. Font IDs: system, inter, space-grotesk, dm-sans, manrope, plus-jakarta-sans, outfit, sora, work-sans, figtree, ibm-plex-sans, archivo, nunito, nunito-sans, playfair-display, fraunces, lora, source-serif-4, dm-serif-display, jetbrains-mono, open-sans, lato, montserrat, poppins, raleway, rubik, josefin-sans, cormorant-garamond, eb-garamond, libre-baskerville, merriweather, dancing-script, great-vibes, caveat, pacifico. Theme colour keys are primary, secondary, accent, background, surface, text, muted, border, dark and values are six-digit hex colours.

Textures: grain, paper, dots, grid, diagonal, crosshatch, waves, rings, confetti. Gradients: none, linear, radial, mesh. Edges: wave, slant, curve, arc, zigzag, torn. Surfaces: outlined, flat, raised, glass, tinted, hard. Section style groups: tone=default/muted/brand/dark; padding=none/sm/md/lg/xl; width=narrow/normal/wide/full; align=left/center; texture=none/grain/paper/dots/grid/diagonal/crosshatch/waves/rings/confetti; textureColour=auto/primary/secondary/accent/text/background/dark; textureOpacity=number; textureScale=sm/md/lg; gradient=none/linear/radial/mesh; gradientFrom=primary/secondary/accent/surface/dark/background; gradientTo=primary/secondary/accent/surface/dark/background; gradientVia=primary/secondary/accent/surface/dark/background; gradientDirection=down/right/diagonal/diagonal-up; gradientStrength=soft/full; dividerTop=none/wave/slant/curve/arc/zigzag/torn; dividerTopFlip=toggle; dividerTopHeight=sm/md/lg; dividerBottom=none/wave/slant/curve/arc/zigzag/torn; dividerBottomFlip=toggle; dividerBottomHeight=sm/md/lg; dividerColour=auto/primary/secondary/accent/surface/dark/background; surface=theme/outlined/flat/raised/glass/tinted/hard; animate=none/fade-up/fade-in/slide-left/slide-right/zoom/unveil; animateItems=toggle; animateSpeed=quick/normal/slow; imageMotion=none/slow-zoom/parallax/hover-zoom. Element styles: align=default/left/center/right; size=default/sm/md/lg/xl/xxl; tone=default/muted/primary/accent; spaceAfter=default/none/sm/md/lg; hideOn=none/mobile/desktop/tablet/wide/mobile-tablet/mobile-wide; font=default/heading/body/accent.

Style groups are stored in `theme.styles` with an ID, name, kind (`section`, `card`, `heading`, `button`) and props. Sections and layout elements may refer to a group by ID. Maximum style groups: 40. Layout presets, element keys and element styles are listed on each block above.

## Content and safety

- Write in British English. Never invent prices, statistics, reviews, testimonials or people. Use explicit placeholders such as `[Add a real customer quote]` and `[Add a real figure and what it measures]`.
- Do not put HTML or scripts in text. Rich text is a Tiptap JSON document using the schema's allowed nodes and marks; plain strings are also accepted as Markdown-like text and converted to paragraphs.
- Links should be site paths such as `/contact` or HTTPS URLs. Do not use remote image URLs: server-side fetching would create an SSRF risk.
- An image can use an `assetId` already in this workspace, inline base64 `data` with a filename and alt text, or `placeholder: true` with alt text.
- Respect the limits in the schema: up to 40 pages, 60 sections per page, 500 posts, 2000 redirects and 500 service areas. Strings and nested lists have per-field maximums.
- A redirect only answers an address no page uses. `status` is 301 (the default), 302 or 410; a 410 marks an old address as gone and has no `to`. `matchType` `PREFIX` also answers every address under `from` (all sent to the same `to`); `EXACT`, the default, answers `from` only.
- Synced sections are workspace-specific and are not part of this portable format. Do not include integration credentials, form submissions, analytics records, billing or custom domains.

## Import and management

Use the CubeSites app's site import dialog, or the authenticated API. First call `POST /api/v1/workspaces/{workspaceId}/sites/validate` with the JSON body. Then call `POST /api/v1/workspaces/{workspaceId}/sites/import` to create a site, or `PUT /api/v1/workspaces/{workspaceId}/sites/{siteId}/json` to update a draft. Appending `?dryRun=1` to import checks without writing. Pages are matched by slug; missing pages are kept unless `?prune=1` is explicit. JSON updates the draft only. Publish separately after review.

Schema: [cubesites-site.schema.json](/spec/cubesites-site.schema.json). API guide: [site-guide.md](/spec/site-guide.md).