{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://schemas.zeropress.dev/preview-data/v0.7/schema.json",
  "title": "ZeroPress Preview Data v0.7",
  "description": "Canonical site data consumed by ZeroPress Build Core and ZeroPress theme preview tooling.",
  "markdownDescription": "Canonical site data consumed by ZeroPress Build Core and ZeroPress theme preview tooling.",
  "type": "object",
  "additionalProperties": false,
  "required": ["version", "generator", "generated_at", "site", "content"],
  "properties": {
    "$schema": {
      "type": "string",
      "description": "Optional JSON Schema URI or path used by editors and tooling. ZeroPress core does not interpret this value.",
      "markdownDescription": "Optional JSON Schema URI or path used by editors and tooling. ZeroPress core does not interpret this value."
    },
    "version": {
      "const": "0.7",
      "description": "Preview-data contract version.",
      "markdownDescription": "Preview-data contract version."
    },
    "generator": {
      "type": "string",
      "minLength": 1,
      "description": "Identifier of the generator that produced this payload.",
      "markdownDescription": "Identifier of the generator that produced this payload.",
      "pattern": "\\S"
    },
    "generated_at": {
      "$ref": "#/$defs/dateTime",
      "description": "RFC 3339 timestamp with Z or a numeric UTC offset indicating when the payload was generated.",
      "markdownDescription": "RFC 3339 timestamp with Z or a numeric UTC offset indicating when the payload was generated."
    },
    "site": {
      "$ref": "#/$defs/site"
    },
    "content": {
      "$ref": "#/$defs/content"
    },
    "menus": {
      "$ref": "#/$defs/menus"
    },
    "widgets": {
      "$ref": "#/$defs/widgets"
    },
    "collections": {
      "$ref": "#/$defs/collections"
    },
    "custom_css": {
      "$ref": "#/$defs/customCss"
    },
    "custom_html": {
      "$ref": "#/$defs/customHtml"
    }
  },
  "$defs": {
    "slugSegment": {
      "type": "string",
      "minLength": 1,
      "description": "A slug segment containing only Unicode letters, combining marks, decimal digits, isolated internal periods, hyphen, and underscore, with at least one letter or digit. Periods may not lead, trail, or appear consecutively. Decomposed input is accepted; canonical results and length are evaluated after NFC normalization.",
      "markdownDescription": "A slug segment containing only Unicode letters, combining marks, decimal digits, isolated internal periods, hyphen, and underscore, with at least one letter or digit. Periods may not lead, trail, or appear consecutively. Decomposed input is accepted; canonical results and length are evaluated after NFC normalization.",
      "pattern": "^(?=.*[\\p{L}\\p{Nd}])(?!\\.)(?!.*\\.\\.)(?!.*\\.$)[\\p{L}\\p{M}\\p{Nd}._-]+$",
      "x-zeropress-nfc-max-code-points": 200
    },
    "dateTime": {
      "type": "string",
      "format": "date-time",
      "description": "RFC 3339 date-time. JSON Schema format and the structural pattern aid editors; runtime validation is authoritative for real calendar dates and offset ranges.",
      "markdownDescription": "RFC 3339 date-time. JSON Schema format and the structural pattern aid editors; runtime validation is authoritative for real calendar dates and offset ranges.",
      "pattern": "^(\\d{4})-(\\d{2})-(\\d{2})[Tt](\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?([Zz]|([+-])(\\d{2}):(\\d{2}))$"
    },
    "absoluteWebUrl": {
      "type": "string",
      "format": "uri",
      "description": "Credential-free absolute HTTP(S) URL. Query strings and fragments are allowed; unsafe characters, malformed percent encoding, and dot path segments are rejected by runtime validation.",
      "markdownDescription": "Credential-free absolute HTTP(S) URL. Query strings and fragments are allowed; unsafe characters, malformed percent encoding, and dot path segments are rejected by runtime validation.",
      "pattern": "^[Hh][Tt][Tt][Pp][Ss]?://(?!.*%(?![0-9A-Fa-f]{2}))(?!.*[\\s\\\\\\p{Cc}])(?![^?#]*\\/\\.{1,2}(?:\\/|[?#]|$))(?:\\[[0-9A-Fa-f:.]+\\]|[^/?#:@]+)(?::[0-9]+)?(?:[/?#].*)?$"
    },
    "siteOrigin": {
      "description": "Empty string or an HTTP(S) origin without credentials, path, query, or fragment. A trailing root slash is accepted; canonical producers emit URL.origin.",
      "markdownDescription": "Empty string or an HTTP(S) origin without credentials, path, query, or fragment. A trailing root slash is accepted; canonical producers emit URL.origin.",
      "anyOf": [
        { "const": "" },
        {
          "type": "string",
          "format": "uri",
          "pattern": "^[Hh][Tt][Tt][Pp][Ss]?://(?!.*%(?![0-9A-Fa-f]{2}))(?!.*[\\s\\\\\\p{Cc}])(?:\\[[0-9A-Fa-f:.]+\\]|[^/?#:@]+)(?::[0-9]+)?/?$"
        }
      ]
    },
    "mediaOrigin": {
      "description": "Empty string or an HTTP(S) origin without credentials, path, query, or fragment. A trailing root slash is accepted; canonical producers omit it.",
      "markdownDescription": "Empty string or an HTTP(S) origin without credentials, path, query, or fragment. A trailing root slash is accepted; canonical producers omit it.",
      "anyOf": [
        { "const": "" },
        {
          "type": "string",
          "format": "uri",
          "pattern": "^[Hh][Tt][Tt][Pp][Ss]?://(?!.*%(?![0-9A-Fa-f]{2}))(?!.*[\\s\\\\\\p{Cc}])(?:\\[[0-9A-Fa-f:.]+\\]|[^/?#:@]+)(?::[0-9]+)?/?$"
        }
      ]
    },
    "navigationUrl": {
      "description": "Credential-free absolute HTTP(S) URL or safe single-slash root-relative URL. The root path / is allowed. Bare and dot-relative paths are rejected.",
      "markdownDescription": "Credential-free absolute HTTP(S) URL or safe single-slash root-relative URL. The root path / is allowed. Bare and dot-relative paths are rejected.",
      "anyOf": [
        { "$ref": "#/$defs/absoluteWebUrl" },
        {
          "type": "string",
          "pattern": "^/(?!/)(?!.*%(?![0-9A-Fa-f]{2}))(?!.*[\\s\\\\\\p{Cc}])(?![^?#]*\\/\\.{1,2}(?:\\/|[?#]|$)).*$"
        }
      ]
    },
    "mediaUrl": {
      "description": "Credential-free absolute HTTP(S) URL or safe single-slash root-relative media URL with a non-root path. Bare and dot-relative paths are rejected.",
      "markdownDescription": "Credential-free absolute HTTP(S) URL or safe single-slash root-relative media URL with a non-root path. Bare and dot-relative paths are rejected.",
      "anyOf": [
        {
          "allOf": [
            { "$ref": "#/$defs/absoluteWebUrl" },
            { "pattern": "^[Hh][Tt][Tt][Pp][Ss]?://[^/?#]+/(?=[^?#]*[^/?#])" }
          ]
        },
        {
          "type": "string",
          "pattern": "^/(?!/)(?=[^?#]*[^/?#])(?!.*%(?![0-9A-Fa-f]{2}))(?!.*[\\s\\\\\\p{Cc}])(?![^?#]*\\/\\.{1,2}(?:\\/|[?#]|$)).*$"
        }
      ]
    },
    "commentsApiBaseUrl": {
      "description": "An absolute http(s) URL with a hostname or a same-host root-relative path. Credentials, query strings, fragments, protocol-relative URLs, unsafe characters, and malformed percent encoding are not allowed.",
      "markdownDescription": "An absolute `http(s)` URL with a hostname or a same-host root-relative path. Credentials, query strings, fragments, protocol-relative URLs, unsafe characters, and malformed percent encoding are not allowed.",
      "anyOf": [
        {
          "allOf": [
            { "$ref": "#/$defs/absoluteWebUrl" },
            {
              "not": { "anyOf": [{ "pattern": "^[Hh][Tt][Tt][Pp][Ss]?://[^/?#]*@" }, { "pattern": "[?#]" }] }
            }
          ]
        },
        {
          "type": "string",
          "pattern": "^/(?!/)(?!.*%(?![0-9A-Fa-f]{2}))(?!.*[\\s\\\\\\p{Cc}])(?![^?#]*\\/\\.{1,2}(?:\\/|[?#]|$))(?!.*[?#]).*$"
        }
      ]
    },
    "previewMeta": {
      "type": "object",
      "additionalProperties": {
        "type": ["string", "number", "boolean", "null"]
      },
      "description": "Generator-defined optional metadata for site/theme conventions. ZeroPress core does not interpret metadata keys.",
      "markdownDescription": "Generator-defined optional metadata for site/theme conventions. ZeroPress core does not interpret metadata keys."
    },
    "structuredData": {
      "type": "object",
      "maxProperties": 64,
      "additionalProperties": {
        "$ref": "#/$defs/structuredDataValue1"
      },
      "description": "Page/post scoped structured data for theme-facing repeated UI blocks.",
      "markdownDescription": "Page/post scoped structured data for theme-facing repeated UI blocks.",
      "propertyNames": {
        "$ref": "#/$defs/structuredDataKey"
      }
    },
    "structuredDataKey": {
      "type": "string",
      "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*(?:-[a-zA-Z0-9_]+)*$"
    },
    "structuredDataScalar": {
      "type": ["string", "number", "boolean", "null"]
    },
    "structuredDataObject1": {
      "type": "object",
      "maxProperties": 64,
      "additionalProperties": {
        "$ref": "#/$defs/structuredDataValue2"
      },
      "propertyNames": {
        "$ref": "#/$defs/structuredDataKey"
      }
    },
    "structuredDataArray1": {
      "type": "array",
      "maxItems": 256,
      "items": {
        "$ref": "#/$defs/structuredDataValue2"
      }
    },
    "structuredDataValue1": {
      "anyOf": [
        { "$ref": "#/$defs/structuredDataScalar" },
        { "$ref": "#/$defs/structuredDataObject1" },
        { "$ref": "#/$defs/structuredDataArray1" }
      ]
    },
    "structuredDataObject2": {
      "type": "object",
      "maxProperties": 64,
      "additionalProperties": {
        "$ref": "#/$defs/structuredDataValue3"
      },
      "propertyNames": {
        "$ref": "#/$defs/structuredDataKey"
      }
    },
    "structuredDataArray2": {
      "type": "array",
      "maxItems": 256,
      "items": {
        "$ref": "#/$defs/structuredDataValue3"
      }
    },
    "structuredDataValue2": {
      "anyOf": [
        { "$ref": "#/$defs/structuredDataScalar" },
        { "$ref": "#/$defs/structuredDataObject2" },
        { "$ref": "#/$defs/structuredDataArray2" }
      ]
    },
    "structuredDataObject3": {
      "type": "object",
      "maxProperties": 64,
      "additionalProperties": {
        "$ref": "#/$defs/structuredDataValue4"
      },
      "propertyNames": {
        "$ref": "#/$defs/structuredDataKey"
      }
    },
    "structuredDataArray3": {
      "type": "array",
      "maxItems": 256,
      "items": {
        "$ref": "#/$defs/structuredDataValue4"
      }
    },
    "structuredDataValue3": {
      "anyOf": [
        { "$ref": "#/$defs/structuredDataScalar" },
        { "$ref": "#/$defs/structuredDataObject3" },
        { "$ref": "#/$defs/structuredDataArray3" }
      ]
    },
    "structuredDataObject4": {
      "type": "object",
      "maxProperties": 64,
      "additionalProperties": {
        "$ref": "#/$defs/structuredDataScalar"
      },
      "propertyNames": {
        "$ref": "#/$defs/structuredDataKey"
      }
    },
    "structuredDataArray4": {
      "type": "array",
      "maxItems": 256,
      "items": {
        "$ref": "#/$defs/structuredDataScalar"
      }
    },
    "structuredDataValue4": {
      "anyOf": [
        { "$ref": "#/$defs/structuredDataScalar" },
        { "$ref": "#/$defs/structuredDataObject4" },
        { "$ref": "#/$defs/structuredDataArray4" }
      ]
    },
    "permalinks": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional site permalink policy. Missing fields resolve to directory output, /posts/:slug/, /:slug/, /categories/:slug/, and /tags/:slug/ without mutating Preview Data.",
      "markdownDescription": "Optional site permalink policy. Missing fields resolve to directory output, /posts/:slug/, /:slug/, /categories/:slug/, and /tags/:slug/ without mutating Preview Data.",
      "properties": {
        "output_style": {
          "type": "string",
          "enum": ["directory", "html-extension"],
          "description": "Controls whether generated public URLs and output files use directory index paths or .html extension paths. Allowed values: directory, html-extension.",
          "markdownDescription": "Controls whether generated public URLs and output files use directory index paths or `.html` extension paths. Allowed values: `directory`, `html-extension`."
        },
        "posts": {
          "$ref": "#/$defs/postPermalinkPattern"
        },
        "pages": {
          "$ref": "#/$defs/slugPermalinkPattern"
        },
        "categories": {
          "$ref": "#/$defs/slugPermalinkPattern"
        },
        "tags": {
          "$ref": "#/$defs/slugPermalinkPattern"
        }
      }
    },
    "permalinkPattern": {
      "type": "string",
      "minLength": 1,
      "description": "Absolute URL path pattern beginning with /. Tokens must occupy full path segments, such as :slug or :public_id. Query strings, fragments, percent-encoding, and literal .html suffixes are not allowed. Allowed tokens depend on the permalink field.",
      "markdownDescription": "Absolute URL path pattern beginning with `/`. Tokens must occupy full path segments, such as `:slug` or `:public_id`. Query strings, fragments, percent-encoding, and literal `.html` suffixes are not allowed. Allowed tokens depend on the permalink field.",
      "pattern": "\\S",
      "x-zeropress-literal-segment-nfc-max-code-points": 200
    },
    "postPermalinkPattern": {
      "allOf": [
        { "$ref": "#/$defs/permalinkPattern" },
        {
          "type": "string",
          "pattern": "^(?!.*\\.html(?:/|$))(?=.*(?:/(?::slug|:public_id))(?:/|$))/(?:(?=[\\p{L}\\p{M}\\p{Nd}._-]*[\\p{L}\\p{Nd}])(?!\\.)(?![\\p{L}\\p{M}\\p{Nd}._-]*\\.\\.)[\\p{L}\\p{M}\\p{Nd}_-](?:[\\p{L}\\p{M}\\p{Nd}_-]|\\.(?=[\\p{L}\\p{M}\\p{Nd}_-]))*|:slug|:public_id|:year|:month|:day)(?:/(?:(?=[\\p{L}\\p{M}\\p{Nd}._-]*[\\p{L}\\p{Nd}])(?!\\.)(?![\\p{L}\\p{M}\\p{Nd}._-]*\\.\\.)[\\p{L}\\p{M}\\p{Nd}_-](?:[\\p{L}\\p{M}\\p{Nd}_-]|\\.(?=[\\p{L}\\p{M}\\p{Nd}_-]))*|:slug|:public_id|:year|:month|:day))*/?$",
          "x-zeropress-literal-segment-nfc-max-code-points": 200
        }
      ]
    },
    "slugPermalinkPattern": {
      "allOf": [
        { "$ref": "#/$defs/permalinkPattern" },
        {
          "type": "string",
          "pattern": "^(?!.*\\.html(?:/|$))(?=.*(?:/:slug)(?:/|$))/(?:(?=[\\p{L}\\p{M}\\p{Nd}._-]*[\\p{L}\\p{Nd}])(?!\\.)(?![\\p{L}\\p{M}\\p{Nd}._-]*\\.\\.)[\\p{L}\\p{M}\\p{Nd}_-](?:[\\p{L}\\p{M}\\p{Nd}_-]|\\.(?=[\\p{L}\\p{M}\\p{Nd}_-]))*|:slug)(?:/(?:(?=[\\p{L}\\p{M}\\p{Nd}._-]*[\\p{L}\\p{Nd}])(?!\\.)(?![\\p{L}\\p{M}\\p{Nd}._-]*\\.\\.)[\\p{L}\\p{M}\\p{Nd}_-](?:[\\p{L}\\p{M}\\p{Nd}_-]|\\.(?=[\\p{L}\\p{M}\\p{Nd}_-]))*|:slug))*/?$",
          "x-zeropress-literal-segment-nfc-max-code-points": 200
        }
      ]
    },
    "frontPage": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional root front page selection policy expressed as a closed discriminated union. Missing means theme_index.",
      "markdownDescription": "Optional root front page selection policy expressed as a closed discriminated union. Missing means theme_index.",
      "required": ["type"],
      "properties": {
        "type": {
          "type": "string",
          "enum": ["theme_index", "page", "standalone_html"],
          "description": "Front page source type.",
          "markdownDescription": "Front page source type."
        },
        "page_path": {
          "$ref": "#/$defs/pagePath",
          "description": "Effective relative Page route path used when type is page.",
          "markdownDescription": "Effective relative Page route path used when `type` is `page`.",
          "x-zeropress-references": "content.pages.effective_path"
        },
        "html": {
          "type": "string",
          "minLength": 1,
          "description": "Trusted full HTML used when type is standalone_html.",
          "markdownDescription": "Trusted full HTML used when `type` is `standalone_html`.",
          "pattern": "\\S"
        }
      },
      "oneOf": [
        {
          "required": ["type"],
          "properties": { "type": { "const": "theme_index" } },
          "not": { "anyOf": [{ "required": ["page_path"] }, { "required": ["html"] }] }
        },
        {
          "required": ["type", "page_path"],
          "properties": { "type": { "const": "page" } },
          "not": { "required": ["html"] }
        },
        {
          "required": ["type", "html"],
          "properties": { "type": { "const": "standalone_html" } },
          "not": { "required": ["page_path"] }
        }
      ]
    },
    "postIndex": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional post index generation policy. Missing fields use Build Core defaults: enabled true, path /, and paginate true.",
      "markdownDescription": "Optional post index generation policy. Missing fields use Build Core defaults: enabled true, path /, and paginate true.",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "Whether the site requests a post index route.",
          "markdownDescription": "Whether the site requests a post index route."
        },
        "path": {
          "$ref": "#/$defs/postIndexPath"
        },
        "paginate": {
          "type": "boolean",
          "description": "Whether the post index should generate page 2+ routes.",
          "markdownDescription": "Whether the post index should generate page 2+ routes."
        }
      }
    },
    "postIndexPath": {
      "type": "string",
      "description": "Absolute public path for the post index route. Use / for the site root.",
      "markdownDescription": "Absolute public path for the post index route. Use `/` for the site root.",
      "pattern": "^(?!.*\\.html(?:/|$))/(?:(?=[\\p{L}\\p{M}\\p{Nd}._-]*[\\p{L}\\p{Nd}])(?!\\.)(?![\\p{L}\\p{M}\\p{Nd}._-]*\\.\\.)[\\p{L}\\p{M}\\p{Nd}_-](?:[\\p{L}\\p{M}\\p{Nd}_-]|\\.(?=[\\p{L}\\p{M}\\p{Nd}_-]))*(?:/(?=[\\p{L}\\p{M}\\p{Nd}._-]*[\\p{L}\\p{Nd}])(?!\\.)(?![\\p{L}\\p{M}\\p{Nd}._-]*\\.\\.)[\\p{L}\\p{M}\\p{Nd}_-](?:[\\p{L}\\p{M}\\p{Nd}_-]|\\.(?=[\\p{L}\\p{M}\\p{Nd}_-]))*)*/?)?$",
      "x-zeropress-literal-segment-nfc-max-code-points": 200
    },
    "pagePath": {
      "type": "string",
      "description": "Effective relative Page route path. It has no leading or trailing slash, each segment follows the slug policy, and identity is compared after NFC normalization.",
      "markdownDescription": "Effective relative Page route path. It has no leading or trailing slash, each segment follows the slug policy, and identity is compared after NFC normalization.",
      "pattern": "^(?!.*\\.html(?:/|$))(?=[\\p{L}\\p{M}\\p{Nd}._-]*[\\p{L}\\p{Nd}])(?!\\.)(?![\\p{L}\\p{M}\\p{Nd}._-]*\\.\\.)[\\p{L}\\p{M}\\p{Nd}_-](?:[\\p{L}\\p{M}\\p{Nd}_-]|\\.(?=[\\p{L}\\p{M}\\p{Nd}_-]))*(?:/(?=[\\p{L}\\p{M}\\p{Nd}._-]*[\\p{L}\\p{Nd}])(?!\\.)(?![\\p{L}\\p{M}\\p{Nd}._-]*\\.\\.)[\\p{L}\\p{M}\\p{Nd}_-](?:[\\p{L}\\p{M}\\p{Nd}_-]|\\.(?=[\\p{L}\\p{M}\\p{Nd}_-]))*)*$",
      "x-zeropress-literal-segment-nfc-max-code-points": 200,
      "x-zeropress-identity": "effective-page-path"
    },
    "site": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "title",
        "description",
        "url",
        "media_origin",
        "locale",
        "posts_per_page",
        "date_style",
        "time_style",
        "timezone"
      ],
      "properties": {
        "title": {
          "type": "string",
          "minLength": 1,
          "pattern": "\\S"
        },
        "description": {
          "type": "string"
        },
        "url": {
          "$ref": "#/$defs/siteOrigin",
          "description": "Required canonical site origin. Use an empty string when the public origin is unknown.",
          "markdownDescription": "Required canonical site origin. Use an empty string when the public origin is unknown."
        },
        "media_origin": {
          "$ref": "#/$defs/mediaOrigin",
          "description": "Required media origin used to resolve relative media URLs. Use an empty string to preserve relative values. Non-empty values must be an HTTP(S) origin with an optional port and without credentials, path, query, or fragment. A trailing root slash is accepted, while canonical payloads omit it.",
          "markdownDescription": "Required media origin used to resolve relative media URLs. Use an empty string to preserve relative values. Non-empty values must be an HTTP(S) origin with an optional port and without credentials, path, query, or fragment. A trailing root slash is accepted, while canonical payloads omit it."
        },
        "media_delivery_mode": {
          "type": "string",
          "enum": ["none", "media_domain"],
          "default": "none",
          "description": "Optional media delivery capability. Missing means none. media_domain requires a non-empty site.media_origin and means media URLs under that origin support ZeroPress image variant query parameters.",
          "markdownDescription": "Optional media delivery capability. Missing means `none`. `media_domain` requires a non-empty `site.media_origin` and means media URLs under that origin support ZeroPress image variant query parameters."
        },
        "favicon": {
          "$ref": "#/$defs/siteFavicon"
        },
        "logo": {
          "$ref": "#/$defs/siteLogo"
        },
        "newsletter": {
          "$ref": "#/$defs/siteNewsletter"
        },
        "comments": {
          "$ref": "#/$defs/siteComments",
          "description": "Optional comment-provider configuration and site-level requested state. When omitted, the comment runtime is unavailable.",
          "markdownDescription": "Optional comment-provider configuration and site-level requested state. When omitted, the comment runtime is unavailable."
        },
        "expose_generator": {
          "type": "boolean",
          "default": true,
          "description": "Whether generated HTML pages should include the ZeroPress generator meta tag. Missing or true exposes the generator; false omits it.",
          "markdownDescription": "Whether generated HTML pages should include `<meta name=\"generator\" content=\"ZeroPress\">`. Missing or `true` exposes the generator; `false` omits it."
        },
        "search": {
          "$ref": "#/$defs/siteFeatureState",
          "default": {
            "enabled": true
          },
          "description": "Site-level requested state for native static search. Missing means enabled; Build Core combines the request with theme capability.",
          "markdownDescription": "Site-level requested state for native static search. Missing means enabled; Build Core combines the request with theme capability."
        },
        "feed": {
          "$ref": "#/$defs/siteFeatureState",
          "default": {
            "enabled": true
          },
          "description": "Site-level requested state for RSS feed generation. Missing means enabled; Build Core also requires a canonical site URL and feed generation to be enabled.",
          "markdownDescription": "Site-level requested state for RSS feed generation. Missing means enabled; Build Core also requires a canonical `site.url` and feed generation to be enabled."
        },
        "archive": {
          "$ref": "#/$defs/siteFeatureState",
          "default": {
            "enabled": true
          },
          "description": "Site-level requested state for chronological archive generation. Missing means enabled; Build Core also requires an archive template.",
          "markdownDescription": "Site-level requested state for chronological archive generation. Missing means enabled; Build Core also requires `archive.html`."
        },
        "locale": {
          "$ref": "#/$defs/locale"
        },
        "posts_per_page": {
          "type": "integer",
          "minimum": 1
        },
        "date_style": {
          "type": "string",
          "enum": ["none", "short", "medium", "long", "full"],
          "description": "Date style preset based on Intl.DateTimeFormat dateStyle. Use none to omit the date portion.",
          "markdownDescription": "Date style preset based on `Intl.DateTimeFormat` `dateStyle`. Use `none` to omit the date portion."
        },
        "time_style": {
          "type": "string",
          "enum": ["none", "short", "medium", "long", "full"],
          "description": "Time style preset based on Intl.DateTimeFormat timeStyle. Use none to omit the time portion.",
          "markdownDescription": "Time style preset based on `Intl.DateTimeFormat` `timeStyle`. Use `none` to omit the time portion."
        },
        "timezone": {
          "$ref": "#/$defs/timezone"
        },
        "robots": {
          "$ref": "#/$defs/siteRobots",
          "default": {
            "allow_indexing": true
          },
          "description": "Optional robots policy. Missing means allow_indexing true.",
          "markdownDescription": "Optional robots policy. Missing means `allow_indexing: true`."
        },
        "permalinks": {
          "$ref": "#/$defs/permalinks"
        },
        "front_page": {
          "$ref": "#/$defs/frontPage"
        },
        "post_index": {
          "$ref": "#/$defs/postIndex"
        },
        "footer": {
          "$ref": "#/$defs/siteFooter"
        },
        "meta": {
          "$ref": "#/$defs/previewMeta"
        }
      },
      "allOf": [
        {
          "if": {
            "required": ["media_delivery_mode"],
            "properties": { "media_delivery_mode": { "const": "media_domain" } }
          },
          "then": { "properties": { "media_origin": { "minLength": 1 } } }
        }
      ]
    },
    "locale": {
      "type": "string",
      "minLength": 2,
      "description": "Canonical BCP 47 locale. JSON Schema provides structural guidance; runtime validation is authoritative.",
      "markdownDescription": "Canonical BCP 47 locale. JSON Schema provides structural guidance; runtime validation is authoritative.",
      "pattern": "^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$",
      "x-zeropress-runtime-validation": "canonical-bcp47"
    },
    "timezone": {
      "type": "string",
      "minLength": 1,
      "description": "UTC, a canonical IANA time-zone identifier, or a canonical fixed offset within ±14:00. Zero offset is represented as UTC. Runtime validation is authoritative.",
      "markdownDescription": "UTC, a canonical IANA time-zone identifier, or a canonical fixed offset within ±14:00. Zero offset is represented as UTC. Runtime validation is authoritative.",
      "pattern": "^(?:UTC|[+-](?:0\\d|1[0-4]):[0-5]\\d|[A-Za-z_+-]+(?:/[A-Za-z0-9_+-]+)+)$",
      "x-zeropress-runtime-validation": "canonical-iana-or-fixed-offset"
    },
    "siteRobots": {
      "type": "object",
      "additionalProperties": false,
      "description": "Closed site robots policy used by Build Core and exposed to themes.",
      "markdownDescription": "Closed site robots policy used by Build Core and exposed to themes.",
      "required": ["allow_indexing"],
      "properties": {
        "allow_indexing": {
          "type": "boolean",
          "description": "Whether generated fallback robots.txt permits indexing.",
          "markdownDescription": "Whether generated fallback robots.txt permits indexing."
        }
      }
    },
    "siteFavicon": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional site favicon links for HTML head output.",
      "markdownDescription": "Optional site favicon links for HTML `<head>` output.",
      "properties": {
        "icon": {
          "$ref": "#/$defs/mediaUrl",
          "description": "ICO or generic favicon URL/path.",
          "markdownDescription": "ICO or generic favicon URL/path."
        },
        "icon_dark": {
          "$ref": "#/$defs/mediaUrl",
          "description": "Dark color-scheme ICO or generic favicon URL/path.",
          "markdownDescription": "Dark color-scheme ICO or generic favicon URL/path."
        },
        "svg": {
          "$ref": "#/$defs/mediaUrl",
          "description": "SVG favicon URL/path.",
          "markdownDescription": "SVG favicon URL/path."
        },
        "png": {
          "$ref": "#/$defs/mediaUrl",
          "description": "PNG favicon URL/path.",
          "markdownDescription": "PNG favicon URL/path."
        },
        "apple_touch_icon": {
          "$ref": "#/$defs/mediaUrl",
          "description": "Apple touch icon URL/path.",
          "markdownDescription": "Apple touch icon URL/path."
        }
      },
      "anyOf": [
        { "required": ["icon"] },
        { "required": ["icon_dark"] },
        { "required": ["svg"] },
        { "required": ["png"] },
        { "required": ["apple_touch_icon"] }
      ]
    },
    "siteLogo": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional site logo data for theme rendering.",
      "markdownDescription": "Optional site logo data for theme rendering.",
      "required": ["src"],
      "properties": {
        "src": {
          "$ref": "#/$defs/mediaUrl",
          "description": "Logo URL for theme rendering. Use a safe single-slash root-relative media path or a credential-free HTTP(S) URL with a path. Bare and dot-relative paths are not supported.",
          "markdownDescription": "Logo URL for theme rendering. Use a safe single-slash root-relative media path or a credential-free HTTP(S) URL with a path. Bare and dot-relative paths are not supported."
        },
        "alt": {
          "type": "string",
          "description": "Optional logo alternative text. Themes may fall back to site.title when omitted.",
          "markdownDescription": "Optional logo alternative text. Themes may fall back to `site.title` when omitted."
        }
      }
    },
    "siteNewsletter": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional newsletter CTA/island data for theme rendering. ZeroPress does not implement provider submit behavior.",
      "markdownDescription": "Optional newsletter CTA/island data for theme rendering. ZeroPress does not implement provider submit behavior.",
      "required": ["enabled"],
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "Whether themes should show the newsletter CTA or island.",
          "markdownDescription": "Whether themes should show the newsletter CTA or island."
        },
        "title": {
          "type": "string",
          "description": "Optional theme-facing newsletter title. Themes own fallback copy.",
          "markdownDescription": "Optional theme-facing newsletter title. Themes own fallback copy."
        },
        "description": {
          "type": "string",
          "description": "Optional theme-facing newsletter description. Themes own fallback copy.",
          "markdownDescription": "Optional theme-facing newsletter description. Themes own fallback copy."
        },
        "button_label": {
          "type": "string",
          "description": "Optional theme-facing CTA label. Themes own fallback copy.",
          "markdownDescription": "Optional theme-facing CTA label. Themes own fallback copy."
        },
        "signup_url": {
          "$ref": "#/$defs/navigationUrl",
          "description": "Optional external signup URL or same-host root-relative signup path.",
          "markdownDescription": "Optional external signup URL or same-host root-relative signup path."
        },
        "embed_url": {
          "$ref": "#/$defs/navigationUrl",
          "description": "Optional iframe embed URL or same-host root-relative embed path.",
          "markdownDescription": "Optional iframe embed URL or same-host root-relative embed path."
        }
      },
      "allOf": [
        {
          "if": {
            "required": ["enabled"],
            "properties": { "enabled": { "const": true } }
          },
          "then": { "anyOf": [{ "required": ["signup_url"] }, { "required": ["embed_url"] }] }
        }
      ]
    },
    "siteFeatureState": {
      "type": "object",
      "additionalProperties": false,
      "description": "Closed requested-state object used by optional site search, feed, and archive controls.",
      "markdownDescription": "Closed requested-state object used by optional site search, feed, and archive controls.",
      "required": ["enabled"],
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "Whether the site requests this feature. This field is required whenever the containing feature object exists.",
          "markdownDescription": "Whether the site requests this feature. This field is required whenever the containing feature object exists."
        }
      }
    },
    "siteComments": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional site-level comment-provider configuration and requested state. Consumer defaults are applied without mutating preview-data.",
      "markdownDescription": "Optional site-level comment-provider configuration and requested state. Consumer defaults are applied without mutating preview-data.",
      "required": ["enabled", "api_base_url"],
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "Whether the site requests comments when the configured provider and active theme make the runtime available.",
          "markdownDescription": "Whether the site requests comments when the configured provider and active theme make the runtime available."
        },
        "api_base_url": {
          "$ref": "#/$defs/commentsApiBaseUrl",
          "description": "Comment service base URL or same-host root-relative path. Provider-specific consumers derive content endpoints from this value.",
          "markdownDescription": "Comment service base URL or same-host root-relative path. Provider-specific consumers derive content endpoints from this value."
        },
        "provider": {
          "type": "string",
          "enum": ["zeropress", "wordpress"],
          "default": "zeropress",
          "description": "Comment API provider. Missing means zeropress.",
          "markdownDescription": "Comment API provider. Missing means `zeropress`."
        },
        "per_page": {
          "type": "integer",
          "default": 50,
          "minimum": 1,
          "maximum": 100,
          "description": "Preferred comment page size from 1 through 100. Missing means 50.",
          "markdownDescription": "Preferred comment page size from `1` through `100`. Missing means `50`."
        },
        "order": {
          "type": "string",
          "enum": ["asc", "desc"],
          "default": "desc",
          "description": "Preferred comment order. Missing means desc.",
          "markdownDescription": "Preferred comment order. Missing means `desc`."
        },
        "threading": {
          "$ref": "#/$defs/siteCommentsThreading",
          "description": "Optional threaded-comment display policy.",
          "markdownDescription": "Optional threaded-comment display policy."
        }
      }
    },
    "siteCommentsThreading": {
      "type": "object",
      "additionalProperties": false,
      "description": "Threaded-comment display policy resolved by consumers.",
      "markdownDescription": "Threaded-comment display policy resolved by consumers.",
      "properties": {
        "enabled": {
          "type": "boolean",
          "default": true,
          "description": "Whether threaded comment display is enabled. Missing means true.",
          "markdownDescription": "Whether threaded comment display is enabled. Missing means `true`."
        },
        "max_depth": {
          "type": "integer",
          "default": 2,
          "minimum": 2,
          "maximum": 10,
          "description": "Maximum threaded comment display depth from 2 through 10. Missing means 2.",
          "markdownDescription": "Maximum threaded comment display depth from `2` through `10`. Missing means `2`."
        }
      }
    },
    "contentComments": {
      "type": "object",
      "additionalProperties": false,
      "description": "ZeroPress request metadata for a content item. This object is required when ZeroPress comments are effective for that item; otherwise it is optional and ignored.",
      "markdownDescription": "ZeroPress request metadata for a content item. This object is required when ZeroPress comments are effective for that item; otherwise it is optional and ignored.",
      "required": ["request_token"],
      "properties": {
        "request_token": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Opaque non-blank request token, up to 512 Unicode code points, used by the ZeroPress provider to authorize comment API requests for this content item.",
          "markdownDescription": "Opaque non-blank request token, up to `512` Unicode code points, used by the ZeroPress provider to authorize comment API requests for this content item.",
          "pattern": "\\S"
        }
      }
    },
    "siteFooter": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional site footer display data for themes.",
      "markdownDescription": "Optional site footer display data for themes.",
      "properties": {
        "copyright_text": {
          "type": "string",
          "minLength": 1,
          "description": "Theme-facing footer copyright or legal text. ZeroPress does not add a copyright symbol automatically.",
          "markdownDescription": "Theme-facing footer copyright or legal text. ZeroPress does not add a copyright symbol automatically.",
          "pattern": "\\S"
        },
        "attribution": {
          "type": "boolean",
          "description": "When false, themes that support ZeroPress attribution should hide it. Missing or true means attribution may be shown.",
          "markdownDescription": "When `false`, themes that support ZeroPress attribution should hide it. Missing or `true` means attribution may be shown."
        }
      }
    },
    "author": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "display_name"],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1,
          "pattern": "\\S"
        },
        "display_name": {
          "type": "string",
          "minLength": 1,
          "pattern": "\\S"
        },
        "avatar": {
          "$ref": "#/$defs/mediaUrl"
        }
      }
    },
    "media": {
      "type": "object",
      "additionalProperties": false,
      "required": ["src", "width", "height"],
      "properties": {
        "src": {
          "$ref": "#/$defs/mediaUrl",
          "description": "Managed media source URL or path. Matches existing media fields such as featured_image or avatar after renderer normalization.",
          "markdownDescription": "Managed media source URL or path. Matches existing media fields such as `featured_image` or `avatar` after renderer normalization."
        },
        "width": {
          "type": "integer",
          "minimum": 1
        },
        "height": {
          "type": "integer",
          "minimum": 1
        },
        "alt": {
          "type": "string"
        }
      }
    },
    "post": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "public_id",
        "title",
        "slug",
        "content",
        "document_type",
        "excerpt",
        "published_at_iso",
        "updated_at_iso",
        "author_id",
        "status",
        "allow_comments",
        "category_slugs",
        "tag_slugs"
      ],
      "properties": {
        "public_id": {
          "type": "integer",
          "minimum": 1
        },
        "title": {
          "type": "string",
          "minLength": 1,
          "pattern": "\\S"
        },
        "slug": {
          "$ref": "#/$defs/slugSegment",
          "description": "Post route identity. Values must be unique after NFC normalization within content.posts.",
          "markdownDescription": "Post route identity. Values must be unique after NFC normalization within content.posts.",
          "x-zeropress-unique-after-normalization": "NFC"
        },
        "content": {
          "type": "string"
        },
        "document_type": {
          "type": "string",
          "enum": ["plaintext", "markdown", "html"]
        },
        "excerpt": {
          "type": "string"
        },
        "published_at_iso": {
          "$ref": "#/$defs/dateTime"
        },
        "updated_at_iso": {
          "$ref": "#/$defs/dateTime"
        },
        "author_id": {
          "type": "string",
          "minLength": 1,
          "pattern": "\\S"
        },
        "featured_image": {
          "$ref": "#/$defs/mediaUrl"
        },
        "meta": {
          "$ref": "#/$defs/previewMeta"
        },
        "data": {
          "$ref": "#/$defs/structuredData"
        },
        "status": {
          "type": "string",
          "enum": ["published", "draft"]
        },
        "discoverability": {
          "type": "string",
          "enum": ["default", "noindex", "delist"],
          "description": "Optional document discoverability policy. default leaves generated discovery outputs unchanged; noindex adds HTML robots noindex; delist removes the document from automatic discovery outputs while still rendering its route.",
          "markdownDescription": "Optional document discoverability policy. `default` leaves generated discovery outputs unchanged; `noindex` adds HTML robots `noindex`; `delist` removes the document from automatic discovery outputs while still rendering its route."
        },
        "allow_comments": {
          "type": "boolean"
        },
        "comments": {
          "$ref": "#/$defs/contentComments",
          "description": "ZeroPress request metadata for this post. Required when ZeroPress comments are effective for the post; otherwise optional and ignored.",
          "markdownDescription": "ZeroPress request metadata for this post. Required when ZeroPress comments are effective for the post; otherwise optional and ignored."
        },
        "category_slugs": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/slugSegment"
          }
        },
        "tag_slugs": {
          "type": "array",
          "description": "Ordered tag slug references for this post, unique after NFC normalization. Array order is the theme-facing display order; the first entry is display-first and does not imply a primary or SEO tag.",
          "markdownDescription": "Ordered tag slug references for this post, unique after NFC normalization. Array order is the theme-facing display order; the first entry is display-first and does not imply a primary or SEO tag.",
          "items": {
            "$ref": "#/$defs/slugSegment"
          },
          "uniqueItems": true,
          "x-zeropress-unique-after-normalization": "NFC"
        }
      }
    },
    "page": {
      "type": "object",
      "additionalProperties": false,
      "required": ["title", "slug", "content", "document_type", "status"],
      "properties": {
        "public_id": {
          "type": "integer",
          "minimum": 1,
          "description": "Optional positive public identifier used by comment providers. Required when page.allow_comments is true.",
          "markdownDescription": "Optional positive public identifier used by comment providers. Required when `page.allow_comments` is `true`."
        },
        "title": {
          "type": "string",
          "minLength": 1,
          "pattern": "\\S"
        },
        "slug": {
          "$ref": "#/$defs/slugSegment",
          "description": "Page leaf slug. Leaf slugs may repeat when their effective Page paths are distinct.",
          "markdownDescription": "Page leaf slug. Leaf slugs may repeat when their effective Page paths are distinct."
        },
        "path": {
          "$ref": "#/$defs/pagePath",
          "description": "Optional explicit effective Page route path. When omitted, consumers apply the effective pages permalink to the Page slug.",
          "markdownDescription": "Optional explicit effective Page route path. When omitted, consumers apply the effective pages permalink to the Page slug."
        },
        "content": {
          "type": "string"
        },
        "document_type": {
          "type": "string",
          "enum": ["plaintext", "markdown", "html"]
        },
        "excerpt": {
          "type": "string"
        },
        "featured_image": {
          "$ref": "#/$defs/mediaUrl"
        },
        "updated_at_iso": {
          "$ref": "#/$defs/dateTime",
          "description": "Optional machine-readable page update timestamp.",
          "markdownDescription": "Optional machine-readable page update timestamp."
        },
        "meta": {
          "$ref": "#/$defs/previewMeta"
        },
        "data": {
          "$ref": "#/$defs/structuredData"
        },
        "status": {
          "type": "string",
          "enum": ["published", "draft"]
        },
        "discoverability": {
          "type": "string",
          "enum": ["default", "noindex", "delist"],
          "description": "Optional document discoverability policy. default leaves generated discovery outputs unchanged; noindex adds HTML robots noindex; delist removes the document from automatic discovery outputs while still rendering its route.",
          "markdownDescription": "Optional document discoverability policy. `default` leaves generated discovery outputs unchanged; `noindex` adds HTML robots `noindex`; `delist` removes the document from automatic discovery outputs while still rendering its route."
        },
        "allow_comments": {
          "type": "boolean",
          "default": false,
          "description": "Optional per-page comment policy. Missing means false.",
          "markdownDescription": "Optional per-page comment policy. Missing means `false`."
        },
        "comments": {
          "$ref": "#/$defs/contentComments",
          "description": "ZeroPress request metadata for this page. Required when ZeroPress comments are effective for the page; otherwise optional and ignored.",
          "markdownDescription": "ZeroPress request metadata for this page. Required when ZeroPress comments are effective for the page; otherwise optional and ignored."
        }
      },
      "allOf": [
        {
          "if": {
            "required": ["allow_comments"],
            "properties": { "allow_comments": { "const": true } }
          },
          "then": { "required": ["public_id"] }
        }
      ]
    },
    "category": {
      "type": "object",
      "additionalProperties": false,
      "required": ["name", "slug"],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "pattern": "\\S"
        },
        "slug": {
          "$ref": "#/$defs/slugSegment"
        },
        "description": {
          "type": "string"
        }
      }
    },
    "tag": {
      "type": "object",
      "additionalProperties": false,
      "required": ["name", "slug"],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "pattern": "\\S"
        },
        "slug": {
          "$ref": "#/$defs/slugSegment"
        },
        "description": {
          "type": "string"
        }
      }
    },
    "menuItem": {
      "type": "object",
      "additionalProperties": false,
      "description": "Theme-facing navigation item. Children may be nested to any depth.",
      "markdownDescription": "Theme-facing navigation item. `children` may be nested to any depth.",
      "required": ["title", "url", "target", "children"],
      "properties": {
        "title": {
          "type": "string",
          "minLength": 1,
          "description": "Display title shown by the theme.",
          "markdownDescription": "Display title shown by the theme.",
          "pattern": "\\S"
        },
        "url": {
          "$ref": "#/$defs/navigationUrl",
          "description": "Resolved absolute http(s) URL or safe relative web path.",
          "markdownDescription": "Resolved absolute `http(s)` URL or safe relative web path."
        },
        "target": {
          "type": "string",
          "enum": ["_self", "_blank"],
          "description": "Link target semantics for the theme.",
          "markdownDescription": "Link target semantics for the theme."
        },
        "meta": {
          "$ref": "#/$defs/previewMeta",
          "description": "Optional scalar display metadata such as icon, badge, or accent.",
          "markdownDescription": "Optional scalar display metadata such as `icon`, `badge`, or `accent`."
        },
        "children": {
          "type": "array",
          "description": "Nested child menu items.",
          "markdownDescription": "Nested child menu items.",
          "items": {
            "$ref": "#/$defs/menuItem"
          }
        }
      }
    },
    "menu": {
      "type": "object",
      "additionalProperties": false,
      "description": "A named enabled menu exported to themes under its menu_id key.",
      "markdownDescription": "A named enabled menu exported to themes under its `menu_id` key.",
      "required": ["name", "items"],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Human-readable menu name for development and diagnostics.",
          "markdownDescription": "Human-readable menu name for development and diagnostics.",
          "pattern": "\\S"
        },
        "items": {
          "type": "array",
          "description": "Resolved menu tree after omission of missing references.",
          "markdownDescription": "Resolved menu tree after omission of missing references.",
          "items": {
            "$ref": "#/$defs/menuItem"
          }
        }
      }
    },
    "menus": {
      "type": "object",
      "default": {},
      "additionalProperties": {
        "$ref": "#/$defs/menu"
      },
      "description": "Enabled menus keyed by menu_id. Disabled menus and absent menu_ids are omitted.",
      "markdownDescription": "Enabled menus keyed by `menu_id`. Disabled menus and absent `menu_id`s are omitted.",
      "propertyNames": {
        "type": "string",
        "description": "Stable external menu identifier.",
        "markdownDescription": "Stable external `menu_id` identifier.",
        "pattern": "^[a-z][a-z0-9_-]{0,63}$"
      }
    },
    "widgetItem": {
      "type": "object",
      "additionalProperties": false,
      "description": "Theme-facing widget item shell. Widget-type-specific settings are intentionally left open.",
      "markdownDescription": "Theme-facing widget item shell. Widget-type-specific `settings` are intentionally left open.",
      "required": ["type", "title"],
      "properties": {
        "type": {
          "type": "string",
          "minLength": 1,
          "description": "Stable widget item type identifier.",
          "markdownDescription": "Stable widget item type identifier.",
          "pattern": "\\S"
        },
        "title": {
          "type": "string",
          "description": "Required display title string. An empty or whitespace-only value means the widget has no display title after normalization.",
          "markdownDescription": "Required display title string. An empty or whitespace-only value means the widget has no display title after normalization."
        },
        "settings": {
          "type": "object",
          "additionalProperties": true,
          "description": "Widget-type-specific settings payload. This object is intentionally schema-light.",
          "markdownDescription": "Widget-type-specific settings payload. This object is intentionally schema-light."
        }
      }
    },
    "widgetArea": {
      "type": "object",
      "additionalProperties": false,
      "description": "A named enabled widget area exported to themes under its widget_area_id key.",
      "markdownDescription": "A named enabled widget area exported to themes under its `widget_area_id` key.",
      "required": ["name", "items"],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Human-readable widget area name for development and diagnostics.",
          "markdownDescription": "Human-readable widget area name for development and diagnostics.",
          "pattern": "\\S"
        },
        "items": {
          "type": "array",
          "description": "Resolved widget item list after omission of disabled or missing-reference items.",
          "markdownDescription": "Resolved widget item list after omission of disabled or missing-reference items.",
          "items": {
            "$ref": "#/$defs/widgetItem"
          }
        }
      }
    },
    "widgets": {
      "type": "object",
      "default": {},
      "additionalProperties": {
        "$ref": "#/$defs/widgetArea"
      },
      "description": "Enabled widget areas keyed by widget_area_id. Disabled widget areas are omitted.",
      "markdownDescription": "Enabled widget areas keyed by `widget_area_id`. Disabled widget areas are omitted.",
      "propertyNames": {
        "type": "string",
        "description": "Stable external widget area identifier.",
        "markdownDescription": "Stable external `widget_area_id` identifier.",
        "pattern": "^[a-z][a-z0-9_-]{0,63}$"
      }
    },
    "collectionItem": {
      "type": "object",
      "additionalProperties": false,
      "description": "A reference to a page or post included in a named collection.",
      "markdownDescription": "A reference to a page or post included in a named collection.",
      "required": ["type"],
      "properties": {
        "type": {
          "type": "string",
          "enum": ["post", "page"],
          "description": "Referenced content type.",
          "markdownDescription": "Referenced content type."
        },
        "slug": {
          "$ref": "#/$defs/slugSegment",
          "description": "Post slug used only when type is post.",
          "markdownDescription": "Post slug used only when `type` is `post`.",
          "x-zeropress-references": "content.posts.slug"
        },
        "path": {
          "$ref": "#/$defs/pagePath",
          "description": "Effective Page route path used only when type is page.",
          "markdownDescription": "Effective Page route path used only when `type` is `page`.",
          "x-zeropress-references": "content.pages.effective_path"
        }
      },
      "oneOf": [
        {
          "required": ["type", "slug"],
          "properties": { "type": { "const": "post" } },
          "not": { "required": ["path"] }
        },
        {
          "required": ["type", "path"],
          "properties": { "type": { "const": "page" } },
          "not": { "required": ["slug"] }
        }
      ]
    },
    "collection": {
      "type": "object",
      "additionalProperties": false,
      "description": "A named curated content list exported to themes under its collection id.",
      "markdownDescription": "A named curated content list exported to themes under its collection id.",
      "required": ["items"],
      "properties": {
        "title": {
          "type": "string",
          "minLength": 1,
          "description": "Optional display title for the collection.",
          "markdownDescription": "Optional display title for the collection.",
          "pattern": "\\S"
        },
        "description": {
          "type": "string",
          "description": "Optional helper text for the collection.",
          "markdownDescription": "Optional helper text for the collection."
        },
        "items": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/collectionItem"
          },
          "uniqueItems": true
        }
      }
    },
    "collections": {
      "type": "object",
      "default": {},
      "additionalProperties": {
        "$ref": "#/$defs/collection"
      },
      "description": "Named curated content lists keyed by collection id.",
      "markdownDescription": "Named curated content lists keyed by collection id.",
      "propertyNames": {
        "type": "string",
        "description": "Stable collection identifier.",
        "markdownDescription": "Stable collection identifier.",
        "pattern": "^[a-z][a-z0-9_-]{0,63}$"
      }
    },
    "customCss": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional site-level custom stylesheet input.",
      "markdownDescription": "Optional site-level custom stylesheet input.",
      "required": ["content"],
      "properties": {
        "content": {
          "type": "string",
          "minLength": 1,
          "description": "Raw CSS source to be consumed by build tooling.",
          "markdownDescription": "Raw CSS source to be consumed by build tooling.",
          "pattern": "\\S"
        }
      }
    },
    "customHtml": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional trusted site-level HTML injection slots.",
      "markdownDescription": "Optional trusted site-level HTML injection slots. ZeroPress does not sanitize or interpret the HTML content.",
      "properties": {
        "head_end": {
          "type": "string",
          "minLength": 1,
          "maxLength": 65536,
          "description": "Trusted raw HTML inserted before the closing head tag. The string must be non-blank and may contain up to 65,536 Unicode code points.",
          "markdownDescription": "Trusted raw HTML inserted before the closing `</head>` tag. The string must be non-blank and may contain up to `65,536` Unicode code points.",
          "pattern": "\\S"
        },
        "body_end": {
          "type": "string",
          "minLength": 1,
          "maxLength": 65536,
          "description": "Trusted raw HTML inserted before the closing body tag. The string must be non-blank and may contain up to 65,536 Unicode code points.",
          "markdownDescription": "Trusted raw HTML inserted before the closing `</body>` tag. The string must be non-blank and may contain up to `65,536` Unicode code points.",
          "pattern": "\\S"
        }
      },
      "anyOf": [
        { "required": ["head_end"] },
        { "required": ["body_end"] }
      ]
    },
    "zeroPressCommentsItemPolicy": {
      "if": {
        "required": ["allow_comments"],
        "properties": { "allow_comments": { "const": true } }
      },
      "then": { "required": ["comments"] }
    },
    "contentWithZeroPressCommentsPolicy": {
      "type": "object",
      "properties": {
        "posts": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/zeroPressCommentsItemPolicy"
          }
        },
        "pages": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/zeroPressCommentsItemPolicy"
          }
        }
      }
    },
    "content": {
      "type": "object",
      "additionalProperties": false,
      "required": ["authors", "posts", "pages", "categories", "tags"],
      "properties": {
        "authors": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/author"
          }
        },
        "posts": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/post"
          }
        },
        "pages": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/page"
          }
        },
        "categories": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/category"
          }
        },
        "tags": {
          "type": "array",
          "description": "Global tag definitions. Array order has no semantic meaning; producers should emit a stable ascending order by name and then slug.",
          "markdownDescription": "Global tag definitions. Array order has no semantic meaning; producers should emit a stable ascending order by `name` and then `slug`.",
          "items": {
            "$ref": "#/$defs/tag"
          }
        },
        "media": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/media"
          }
        }
      }
    }
  },
  "allOf": [
    {
      "if": {
        "required": ["site"],
        "properties": {
          "site": {
            "type": "object",
            "required": ["comments"],
            "properties": {
              "comments": {
                "type": "object",
                "required": ["enabled"],
                "properties": { "enabled": { "const": true } },
                "anyOf": [
                  { "not": { "required": ["provider"] } },
                  {
                    "required": ["provider"],
                    "properties": { "provider": { "const": "zeropress" } }
                  }
                ]
              }
            }
          }
        }
      },
      "then": { "properties": { "content": { "$ref": "#/$defs/contentWithZeroPressCommentsPolicy" } } }
    }
  ],
  "examples": [
    {
      "$schema": "https://schemas.zeropress.dev/preview-data/v0.7/schema.json",
      "version": "0.7",
      "generator": "example-generator",
      "generated_at": "2026-05-09T00:00:00Z",
      "site": {
        "title": "Example Magazine",
        "description": "A ZeroPress example site.",
        "url": "https://example.com",
        "media_origin": "https://media.example.com",
        "media_delivery_mode": "media_domain",
        "favicon": {
          "icon": "/favicon.ico",
          "icon_dark": "/favicon.dark.ico",
          "svg": "/favicon.svg",
          "png": "/favicon.png",
          "apple_touch_icon": "/apple-touch-icon.png"
        },
        "logo": {
          "src": "/logo.svg",
          "alt": "Example Magazine"
        },
        "comments": {
          "enabled": true,
          "api_base_url": "https://comments.example.com",
          "provider": "zeropress",
          "per_page": 50,
          "order": "desc",
          "threading": {
            "enabled": true,
            "max_depth": 2
          }
        },
        "expose_generator": true,
        "search": {
          "enabled": true
        },
        "feed": {
          "enabled": true
        },
        "archive": {
          "enabled": true
        },
        "locale": "en-US",
        "posts_per_page": 10,
        "date_style": "medium",
        "time_style": "none",
        "timezone": "UTC",
        "robots": {
          "allow_indexing": true
        },
        "front_page": {
          "type": "theme_index"
        },
        "post_index": {
          "enabled": true,
          "path": "/",
          "paginate": true
        },
        "footer": {
          "copyright_text": "Example Magazine",
          "attribution": true
        },
        "meta": {
          "issue": "Spring 2026",
          "show_sponsor_banner": false
        }
      },
      "content": {
        "authors": [
          {
            "id": "editor",
            "display_name": "Example Editor",
            "avatar": "/avatars/editor.jpg"
          }
        ],
        "posts": [
          {
            "public_id": 1,
            "title": "Hello ZeroPress",
            "slug": "hello-zeropress",
            "content": "# Hello ZeroPress\n\nWelcome to the example post.",
            "document_type": "markdown",
            "excerpt": "Welcome to the example post.",
            "published_at_iso": "2026-05-09T00:00:00Z",
            "updated_at_iso": "2026-05-09T00:00:00Z",
            "author_id": "editor",
            "featured_image": "/images/hello.jpg",
            "meta": {
              "badge": "Feature"
            },
            "data": {
              "facts": [
                { "label": "Role", "value": "Writing" },
                { "label": "Year", "value": "2026" }
              ],
              "stack": ["ZeroPress", "Cloudflare"]
            },
            "status": "published",
            "allow_comments": true,
            "comments": {
              "request_token": "example-post-request-token"
            },
            "category_slugs": ["news"],
            "tag_slugs": ["intro"]
          }
        ],
        "pages": [
          {
            "public_id": 2,
            "title": "About",
            "slug": "about",
            "content": "# About\n\nAbout this site.",
            "document_type": "markdown",
            "excerpt": "About this site.",
            "updated_at_iso": "2026-05-09T00:00:00Z",
            "meta": {
              "nav_label": "About"
            },
            "data": {
              "swatches": [
                { "name": "Ink", "value": "#111111" },
                { "name": "Paper", "value": "#ffffff" }
              ]
            },
            "status": "published",
            "allow_comments": true,
            "comments": {
              "request_token": "example-page-request-token"
            }
          }
        ],
        "categories": [
          {
            "name": "News",
            "slug": "news",
            "description": "Site news and updates."
          }
        ],
        "tags": [
          {
            "name": "Intro",
            "slug": "intro",
            "description": "Introductory content."
          }
        ],
        "media": [
          {
            "src": "/avatars/editor.jpg",
            "width": 512,
            "height": 512,
            "alt": "Example Editor"
          },
          {
            "src": "/images/hello.jpg",
            "width": 1600,
            "height": 900,
            "alt": "Hello ZeroPress cover image"
          }
        ]
      },
      "menus": {
        "primary": {
          "name": "Primary Menu",
          "items": [
            {
              "title": "Home",
              "url": "/",
              "target": "_self",
              "meta": {
                "icon": "home"
              },
              "children": []
            },
            {
              "title": "About",
              "url": "/about/",
              "target": "_self",
              "meta": {
                "badge": "New"
              },
              "children": []
            }
          ]
        }
      },
      "widgets": {
        "sidebar": {
          "name": "Sidebar",
          "items": [
            {
              "type": "text",
              "title": "About This Site",
              "settings": {
                "body": "A short reusable widget example."
              }
            }
          ]
        }
      },
      "collections": {
        "cover-story": {
          "title": "Cover Story",
          "description": "Curated content for a featured area.",
          "items": [
            { "type": "post", "slug": "hello-zeropress" },
            { "type": "page", "path": "about" }
          ]
        }
      },
      "custom_html": {
        "head_end": "<meta name=\"site-verification\" content=\"example\">"
      }
    }
  ]
}
