{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://ganterlab.com/schemas/ganter-symbol-v2.schema.json",
  "title": "Ganter symbol package v2",
  "description": "Pre-release portable static SVG artwork with host-owned declarative bindings, shared type configurations and complete typed terminals from Ganter Lab's global connection-anchor catalog.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "$schema",
    "format",
    "version",
    "name",
    "svg",
    "viewBox",
    "fit",
    "footprint",
    "slots",
    "typeConfigurations",
    "terminals",
    "suggestions"
  ],
  "properties": {
    "$schema": { "const": "https://ganterlab.com/schemas/ganter-symbol-v2.schema.json" },
    "format": { "const": "ganter-symbol" },
    "version": {
      "description": "This pre-release transport contract uses version 2; the numeric discriminator must match the v2 schema.",
      "const": 2
    },
    "name": { "type": "string", "minLength": 1 },
    "svg": { "type": "string", "minLength": 1, "maxLength": 524288 },
    "viewBox": { "type": "string", "minLength": 1 },
    "fit": { "enum": ["contain", "stretch", "stretch-x", "stretch-y"] },
    "footprint": {
      "type": "object",
      "additionalProperties": false,
      "required": ["width", "height"],
      "properties": {
        "width": { "type": "integer", "minimum": 1, "maximum": 24 },
        "height": { "type": "integer", "minimum": 1, "maximum": 12 }
      }
    },
    "slots": {
      "type": "array",
      "items": { "$ref": "#/$defs/slot" }
    },
    "typeConfigurations": {
      "description": "Shared per-instance connection-type choices. Every id is unique within the definition, defaultType belongs to allowedTypes, and all terminals that reference the same id resolve together.",
      "type": "array",
      "maxItems": 16,
      "uniqueItems": true,
      "items": { "$ref": "#/$defs/typeConfiguration" }
    },
    "terminals": {
      "description": "Zero or more complete connectable terminals. An empty array keeps artwork visual-only: Ganter Lab does not infer a missing role, type, direction or internal path from a plain SVG, its name, category or geometry.",
      "type": "array",
      "maxItems": 16,
      "uniqueItems": true,
      "items": { "$ref": "#/$defs/terminal" }
    },
    "suggestions": {
      "description": "Ordered, directional and explicitly authored composition suggestions. They exercise intentional pairs but never restrict other terminals whose resolved connection types match.",
      "type": "array",
      "uniqueItems": true,
      "items": { "$ref": "#/$defs/suggestion" }
    }
  },
  "$defs": {
    "identifier": {
      "type": "string",
      "pattern": "^[A-Za-z_][A-Za-z0-9_.:-]{0,127}$"
    },
    "stableKey": {
      "description": "Importer-stable identity: strict lower-kebab (lowercase letters and digits, single hyphens), exactly what the app accepts.",
      "type": "string",
      "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
    },
    "portableTargetSymbol": {
      "type": "string",
      "description": "Portable suggestions may target only a product-owned system Symbol; user GUIDs are station-local identities.",
      "pattern": "^system:[a-z0-9]+(?:-[a-z0-9]+)*$"
    },
    "anchorId": {
      "enum": [
        "top-left",
        "top-25",
        "top-50",
        "top-75",
        "top-right",
        "right-25",
        "right-50",
        "right-75",
        "bottom-right",
        "bottom-75",
        "bottom-50",
        "bottom-25",
        "bottom-left",
        "left-75",
        "left-50",
        "left-25"
      ]
    },
    "connectionType": {
      "description": "Stable global connection type. Color and visual stroke width are application-owned settings, not copied into the package.",
      "enum": [
        "liquid",
        "gas",
        "air-duct",
        "electrical",
        "signal",
        "network",
        "material",
        "mechanical-shaft"
      ]
    },
    "terminalRole": {
      "description": "The role informs authoring plausibility and ordering but does not by itself prohibit a Dashboard connection.",
      "enum": ["input", "output", "bidirectional"]
    },
    "terminalDirection": {
      "description": "External direction in the Symbol's unrotated, unflipped orientation.",
      "enum": ["north", "east", "south", "west"]
    },
    "slot": {
      "type": "object",
      "additionalProperties": false,
      "required": ["name", "elementId", "kind"],
      "properties": {
        "name": { "$ref": "#/$defs/identifier" },
        "elementId": { "$ref": "#/$defs/identifier" },
        "kind": {
          "description": "Closed host primitive. strokeWidth is the additive final kind and accepts only a direct finite SVG-user-unit value from 0.5 through 32; invalid or bad-quality input restores the authored width.",
          "enum": ["rotate", "opacity", "fill", "visible", "text", "translate", "scale", "stroke", "spin", "spinEnabled", "fillColor", "strokeColor", "strokeWidth"]
        },
        "inMin": { "type": "number" },
        "inMax": { "type": "number" },
        "outMin": { "type": "number" },
        "outMax": { "type": "number" },
        "outMinSecondary": { "type": ["number", "null"] },
        "outMaxSecondary": { "type": ["number", "null"] },
        "onColor": { "type": "string", "minLength": 1 },
        "offColor": { "type": "string", "minLength": 1 }
      }
    },
    "typeConfiguration": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name", "allowedTypes", "defaultType"],
      "properties": {
        "id": { "$ref": "#/$defs/stableKey" },
        "name": { "type": "string", "minLength": 1 },
        "allowedTypes": {
          "type": "array",
          "minItems": 1,
          "maxItems": 8,
          "uniqueItems": true,
          "items": { "$ref": "#/$defs/connectionType" }
        },
        "defaultType": { "$ref": "#/$defs/connectionType" }
      }
    },
    "point": {
      "type": "object",
      "additionalProperties": false,
      "required": ["x", "y"],
      "properties": {
        "x": { "type": "number" },
        "y": { "type": "number" }
      }
    },
    "terminal": {
      "description": "A complete terminal enables one global perimeter anchor. It declares exactly one fixed type or shared typeConfiguration, plus its role, outward direction and internal arrival path.",
      "type": "object",
      "additionalProperties": false,
      "required": ["anchorId", "role", "direction", "internalPath"],
      "properties": {
        "anchorId": { "$ref": "#/$defs/anchorId" },
        "role": { "$ref": "#/$defs/terminalRole" },
        "type": { "$ref": "#/$defs/connectionType" },
        "typeConfiguration": { "$ref": "#/$defs/stableKey" },
        "direction": { "$ref": "#/$defs/terminalDirection" },
        "internalPath": {
          "description": "One or more absolute SVG viewBox points. The global anchor is the implicit start and must not be repeated. The start-to-first and subsequent segments stay inside the viewBox, are horizontal, vertical or exactly 45 degrees, and each direction change is at most 90 degrees. These relational geometry rules are enforced semantically.",
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": { "$ref": "#/$defs/point" }
        }
      },
      "oneOf": [
        {
          "required": ["type"],
          "not": { "required": ["typeConfiguration"] }
        },
        {
          "required": ["typeConfiguration"],
          "not": { "required": ["type"] }
        }
      ]
    },
    "suggestion": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "sourceAnchorId",
        "targetSymbol",
        "targetAnchorId",
        "targetRotationDegrees",
        "targetFlipHorizontal",
        "targetFlipVertical"
      ],
      "properties": {
        "sourceAnchorId": { "$ref": "#/$defs/anchorId" },
        "targetSymbol": { "$ref": "#/$defs/portableTargetSymbol" },
        "targetAnchorId": { "$ref": "#/$defs/anchorId" },
        "targetRotationDegrees": { "enum": [0, 90, 180, 270] },
        "targetFlipHorizontal": { "type": "boolean" },
        "targetFlipVertical": { "type": "boolean" },
        "connectionType": {
          "description": "Resolved type used by this scenario. It is required semantically when either endpoint references a type configuration.",
          "$ref": "#/$defs/connectionType"
        }
      }
    }
  }
}
