{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:mechanism-atlas:scene:1.0",
  "title": "Mechanism Atlas scene, version 1.0",
  "description": "Declarative, data-only medication-mechanism teaching scene. Validate with the application runtime as well as this schema.",
  "$comment": "parseScene additionally limits source text to 150000 JavaScript string code units. Dynamic canvas bounds, reference membership, ID uniqueness, total duration and secure URL parsing are runtime checks.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schemaVersion",
    "id",
    "title",
    "englishName",
    "category",
    "subtitle",
    "question",
    "summary",
    "scope",
    "color",
    "viewBox",
    "entities",
    "steps",
    "sources",
    "limitations",
    "outcome"
  ],
  "properties": {
    "schemaVersion": {
      "const": "1.0"
    },
    "id": {
      "$ref": "#/$defs/id"
    },
    "title": {
      "type": "string",
      "minLength": 1,
      "maxLength": 300
    },
    "englishName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 300
    },
    "category": {
      "type": "string",
      "minLength": 1,
      "maxLength": 300
    },
    "subtitle": {
      "type": "string",
      "minLength": 1,
      "maxLength": 300
    },
    "question": {
      "type": "string",
      "minLength": 1,
      "maxLength": 300
    },
    "summary": {
      "type": "string",
      "minLength": 1,
      "maxLength": 1000
    },
    "scope": {
      "type": "string",
      "minLength": 1,
      "maxLength": 300
    },
    "color": {
      "$ref": "#/$defs/color"
    },
    "viewBox": {
      "type": "array",
      "minItems": 2,
      "maxItems": 2,
      "items": {
        "type": "number",
        "minimum": 200,
        "maximum": 2000
      },
      "description": "Canvas [width, height], in SVG units."
    },
    "entities": {
      "type": "array",
      "minItems": 1,
      "maxItems": 80,
      "items": {
        "$ref": "#/$defs/entity"
      }
    },
    "steps": {
      "type": "array",
      "minItems": 1,
      "maxItems": 24,
      "items": {
        "$ref": "#/$defs/step"
      }
    },
    "sources": {
      "type": "array",
      "minItems": 1,
      "maxItems": 12,
      "items": {
        "$ref": "#/$defs/source"
      }
    },
    "limitations": {
      "type": "array",
      "minItems": 1,
      "maxItems": 10,
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 500
      }
    },
    "outcome": {
      "$ref": "#/$defs/outcome"
    }
  },
  "$defs": {
    "id": {
      "type": "string",
      "pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,63}$",
      "minLength": 1,
      "maxLength": 64
    },
    "color": {
      "enum": [
        "teal",
        "amber",
        "blue",
        "violet",
        "rose",
        "muted"
      ]
    },
    "coordinate": {
      "type": "number",
      "minimum": 0,
      "maximum": 2000
    },
    "entity": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "type",
        "label",
        "x",
        "y"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/id"
        },
        "type": {
          "enum": [
            "compartment",
            "membrane",
            "molecule",
            "receptor",
            "enzyme",
            "signal",
            "output",
            "label"
          ]
        },
        "label": {
          "type": "string",
          "minLength": 1,
          "maxLength": 100
        },
        "sublabel": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500
        },
        "description": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500
        },
        "x": {
          "$ref": "#/$defs/coordinate"
        },
        "y": {
          "$ref": "#/$defs/coordinate"
        },
        "width": {
          "type": "number",
          "exclusiveMinimum": 0,
          "maximum": 2000
        },
        "height": {
          "type": "number",
          "exclusiveMinimum": 0,
          "maximum": 2000
        },
        "radius": {
          "type": "number",
          "exclusiveMinimum": 0,
          "maximum": 2000
        },
        "color": {
          "$ref": "#/$defs/color"
        },
        "variant": {
          "enum": [
            "airway",
            "acid",
            "inflammation",
            "pill",
            "dot",
            "proton",
            "camp",
            "protein",
            "none"
          ]
        },
        "hidden": {
          "type": "boolean"
        }
      },
      "$comment": "The runtime additionally checks x <= viewBox[0], y <= viewBox[1], and uniqueness of entity IDs."
    },
    "action": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type"
      ],
      "properties": {
        "type": {
          "enum": [
            "move",
            "activate",
            "inhibit",
            "bind",
            "show",
            "hide",
            "dim",
            "connect"
          ]
        },
        "target": {},
        "actor": {},
        "from": {},
        "to": {},
        "x": {},
        "y": {},
        "label": {
          "type": "string",
          "minLength": 1,
          "maxLength": 100
        }
      },
      "$comment": "Unused reference and coordinate fields remain unconstrained, matching the runtime. Required reference fields must identify an entity in this scene. No executable fields are allowed.",
      "allOf": [
        {
          "if": {
            "properties": {
              "type": {
                "const": "connect"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "required": [
              "from",
              "to"
            ],
            "properties": {
              "from": {
                "$ref": "#/$defs/id"
              },
              "to": {
                "$ref": "#/$defs/id"
              }
            }
          },
          "else": {
            "required": [
              "target"
            ],
            "properties": {
              "target": {
                "$ref": "#/$defs/id"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "bind"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "required": [
              "actor"
            ],
            "properties": {
              "actor": {
                "$ref": "#/$defs/id"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "move"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "required": [
              "x",
              "y"
            ],
            "properties": {
              "x": {
                "$ref": "#/$defs/coordinate"
              },
              "y": {
                "$ref": "#/$defs/coordinate"
              }
            }
          }
        }
      ]
    },
    "step": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "title",
        "caption",
        "detail",
        "duration",
        "actions"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/id"
        },
        "title": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1000
        },
        "caption": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1000
        },
        "detail": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1000
        },
        "duration": {
          "type": "number",
          "minimum": 1,
          "maximum": 60
        },
        "actions": {
          "type": "array",
          "minItems": 0,
          "maxItems": 80,
          "items": {
            "$ref": "#/$defs/action"
          }
        }
      },
      "$comment": "Step IDs must be unique and the total duration must not exceed 600 seconds; the runtime enforces both."
    },
    "source": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "title",
        "url",
        "publisher"
      ],
      "properties": {
        "title": {
          "type": "string",
          "minLength": 1,
          "maxLength": 300
        },
        "url": {
          "type": "string",
          "format": "uri",
          "pattern": "^[Hh][Tt][Tt][Pp][Ss]:(?![\\\\/]*[^\\\\/?#]*@)",
          "description": "An HTTPS source URL without a username or password. Use an absolute https:// URL. Runtime URL parsing is authoritative."
        },
        "publisher": {
          "type": "string",
          "minLength": 1,
          "maxLength": 100
        }
      },
      "$comment": "The runtime uses the WHATWG URL parser and rejects non-HTTPS URLs or embedded username/password credentials. JSON Schema URI format may be an annotation, not an assertion, depending on the validator."
    },
    "outcome": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "title",
        "detail",
        "before",
        "after",
        "variant"
      ],
      "properties": {
        "title": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500
        },
        "detail": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500
        },
        "before": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500
        },
        "after": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500
        },
        "variant": {
          "enum": [
            "airway",
            "acid",
            "inflammation"
          ]
        }
      }
    }
  }
}
