{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://sankey-chart-generator.vercel.app/diagram.schema.json",
  "title": "Sankey Chart Generator diagram file",
  "description": "Portable, client-only Sankey diagram format. Cycle and balance checks are performed by the application after schema validation.",
  "type": "object",
  "additionalProperties": false,
  "required": ["title", "unit", "nodeColors", "layout", "flows"],
  "properties": {
    "title": { "type": "string" },
    "unit": { "type": "string", "description": "Complete subtitle rendered verbatim; no prefix is added." },
    "provenance": { "$ref": "#/$defs/provenance" },
    "nodeColors": {
      "type": "object",
      "propertyNames": { "minLength": 1 },
      "additionalProperties": { "$ref": "#/$defs/colour" }
    },
    "nodeLabelColors": {
      "type": "object",
      "propertyNames": { "minLength": 1 },
      "additionalProperties": { "$ref": "#/$defs/colour" }
    },
    "branding": { "$ref": "#/$defs/branding" },
    "layout": { "$ref": "#/$defs/layout" },
    "flows": {
      "type": "array",
      "items": { "$ref": "#/$defs/flow" }
    }
  },
  "$defs": {
    "colour": {
      "type": "string",
      "pattern": "^#[0-9a-fA-F]{6}$"
    },
    "flow": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "source", "target", "value", "color"],
      "properties": {
        "id": { "type": "string", "minLength": 1 },
        "source": { "type": "string", "minLength": 1 },
        "target": { "type": "string", "minLength": 1 },
        "value": { "type": "number", "exclusiveMinimum": 0 },
        "color": { "$ref": "#/$defs/colour" }
      }
    },
    "layout": {
      "type": "object",
      "additionalProperties": false,
      "required": ["labelSize", "positions"],
      "properties": {
        "labelSize": { "type": "number", "minimum": 12, "maximum": 28 },
        "titleColor": { "$ref": "#/$defs/colour" },
        "subtitleColor": { "$ref": "#/$defs/colour" },
        "numberFormat": { "$ref": "#/$defs/numberFormat" },
        "showValues": { "type": "boolean" },
        "showPercentages": { "type": "boolean" },
        "percentageBasis": { "enum": ["parent", "first-main"] },
        "percentageDecimalPlaces": { "type": "integer", "minimum": 0, "maximum": 6 },
        "positions": {
          "type": "object",
          "propertyNames": { "minLength": 1 },
          "additionalProperties": { "$ref": "#/$defs/position" }
        }
      }
    },
    "numberFormat": {
      "type": "object",
      "additionalProperties": false,
      "required": ["thousandsSeparator", "decimalSeparator", "decimalPlaces"],
      "properties": {
        "thousandsSeparator": { "enum": ["comma", "dot", "space"] },
        "decimalSeparator": { "enum": ["comma", "dot"] },
        "decimalPlaces": { "type": "integer", "minimum": 0, "maximum": 6 }
      }
    },
    "branding": {
      "type": "object",
      "additionalProperties": false,
      "required": ["source", "logo"],
      "properties": {
        "source": { "type": "string" },
        "logo": {
          "type": ["object", "null"],
          "additionalProperties": false,
          "required": ["name", "dataUrl", "x", "y", "width", "height"],
          "properties": {
            "name": { "type": "string" },
            "dataUrl": { "type": "string", "pattern": "^data:image/(png|jpeg|webp);base64,[A-Za-z0-9+/=]+$" },
            "x": { "type": "number", "minimum": 0, "maximum": 1 },
            "y": { "type": "number", "minimum": 0, "maximum": 1 },
            "width": { "type": "number", "minimum": 0.04, "maximum": 0.45 },
            "height": { "type": "number", "minimum": 0.03, "maximum": 0.45 }
          }
        }
      }
    },
    "position": {
      "type": "object",
      "additionalProperties": false,
      "required": ["x", "y"],
      "properties": {
        "x": { "type": "number", "minimum": 0, "maximum": 1 },
        "y": { "type": "number", "minimum": 0, "maximum": 1 }
      }
    },
    "provenance": {
      "type": "object",
      "additionalProperties": false,
      "required": ["label", "url", "asOf"],
      "properties": {
        "label": { "type": "string", "minLength": 1 },
        "url": { "type": "string", "pattern": "^https?://" },
        "asOf": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" }
      }
    }
  }
}
