{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "slide-element.schema.json",
  "title": "Slide Element",
  "type": "object",
  "description": "One absolutely-positioned element on a slide or slide template. Maps 1:1 to the Doolittle SlideElementInput. A type of null marks an author-fillable element that carries a slot; a non-null type (e.g. 'richtext', 'shape', 'image') is a concrete element. Style/properties are opaque JSON strings.",
  "required": [ "id", "x", "y", "w", "h", "z" ],
  "properties": {
    "id": {
      "type": "string",
      "minLength": 1,
      "description": "Stable element id within the slide (e.g. 'title', 'activity_card_bg')."
    },
    "type": {
      "type": [ "string", "null" ],
      "description": "Element type discriminator. null for an unfilled slot; otherwise a concrete type such as 'richtext', 'shape' or 'image'."
    },
    "x": { "type": "integer", "description": "Left edge in pixels." },
    "y": { "type": "integer", "description": "Top edge in pixels." },
    "w": { "type": "integer", "minimum": 0, "description": "Width in pixels." },
    "h": { "type": "integer", "minimum": 0, "description": "Height in pixels." },
    "z": { "type": "integer", "description": "Stacking order." },
    "rotation": { "type": "number", "description": "Clockwise rotation in degrees." },
    "style": { "type": "string", "description": "Opaque JSON-encoded style string; stored and rendered as-is by CTK." },
    "properties": { "type": "string", "description": "Opaque JSON-encoded properties string (e.g. {\"text\": \"...\"}); stored as-is by CTK." },
    "slot": { "$ref": "slidetemplate-slot.schema.json" }
  }
}
