{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "version": {
      "type": "number",
      "const": 1
    },
    "canvas": {
      "type": "object",
      "properties": {
        "width": {
          "type": "integer",
          "minimum": 16,
          "maximum": 3840
        },
        "height": {
          "type": "integer",
          "minimum": 16,
          "maximum": 3840
        },
        "fps": {
          "default": 30,
          "type": "integer",
          "minimum": 1,
          "maximum": 60
        },
        "durationInFrames": {
          "type": "integer",
          "minimum": 1,
          "maximum": 18000
        }
      },
      "required": [
        "width",
        "height",
        "durationInFrames"
      ],
      "additionalProperties": false
    },
    "background": {
      "type": "object",
      "properties": {
        "color": {
          "type": "string",
          "maxLength": 64,
          "pattern": "^(#[0-9a-fA-F]{3,8}|rgba?\\([\\d\\s.,%]+\\)|hsla?\\([\\d\\s.,%deg]+\\)|transparent|[a-zA-Z]+)$"
        },
        "gradient": {
          "type": "object",
          "properties": {
            "from": {
              "type": "string",
              "maxLength": 64,
              "pattern": "^(#[0-9a-fA-F]{3,8}|rgba?\\([\\d\\s.,%]+\\)|hsla?\\([\\d\\s.,%deg]+\\)|transparent|[a-zA-Z]+)$"
            },
            "to": {
              "type": "string",
              "maxLength": 64,
              "pattern": "^(#[0-9a-fA-F]{3,8}|rgba?\\([\\d\\s.,%]+\\)|hsla?\\([\\d\\s.,%deg]+\\)|transparent|[a-zA-Z]+)$"
            },
            "angle": {
              "default": 180,
              "type": "number",
              "minimum": 0,
              "maximum": 360
            }
          },
          "required": [
            "from",
            "to"
          ],
          "additionalProperties": false
        },
        "imageUrl": {
          "type": "string",
          "maxLength": 2048,
          "format": "uri"
        },
        "videoUrl": {
          "description": "Plays muted behind every layer.",
          "type": "string",
          "maxLength": 2048,
          "format": "uri"
        },
        "videoPlaybackRate": {
          "description": "Speed of the background video; below 1 stretches a short clip over a longer video.",
          "type": "number",
          "minimum": 0.25,
          "maximum": 4
        }
      },
      "additionalProperties": false
    },
    "layers": {
      "minItems": 1,
      "maxItems": 60,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "pattern": "^[a-zA-Z0-9_-]+$"
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "image",
              "shape",
              "video"
            ]
          },
          "label": {
            "type": "string",
            "maxLength": 120
          },
          "x": {
            "type": "number",
            "minimum": -3840,
            "maximum": 3840
          },
          "y": {
            "type": "number",
            "minimum": -3840,
            "maximum": 3840
          },
          "width": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 7680
          },
          "height": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 7680
          },
          "rotation": {
            "type": "number",
            "minimum": -360,
            "maximum": 360
          },
          "zIndex": {
            "type": "integer",
            "minimum": -1000,
            "maximum": 1000
          },
          "frameRange": {
            "description": "[firstFrame, lastFrame] the layer is on screen. Defaults to the whole video.",
            "type": "array",
            "prefixItems": [
              {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              }
            ],
            "items": false,
            "minItems": 2,
            "maxItems": 2
          },
          "text": {
            "description": "Text layers.",
            "type": "string",
            "maxLength": 2000
          },
          "src": {
            "description": "Image and video layers.",
            "type": "string",
            "maxLength": 2048,
            "format": "uri"
          },
          "style": {
            "type": "object",
            "properties": {
              "color": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^(#[0-9a-fA-F]{3,8}|rgba?\\([\\d\\s.,%]+\\)|hsla?\\([\\d\\s.,%deg]+\\)|transparent|[a-zA-Z]+)$"
              },
              "background": {
                "anyOf": [
                  {
                    "type": "string",
                    "maxLength": 64,
                    "pattern": "^(#[0-9a-fA-F]{3,8}|rgba?\\([\\d\\s.,%]+\\)|hsla?\\([\\d\\s.,%deg]+\\)|transparent|[a-zA-Z]+)$"
                  },
                  {
                    "type": "string",
                    "maxLength": 300,
                    "pattern": "^linear-gradient\\([^;{}<>]*\\)$"
                  }
                ]
              },
              "fontFamily": {
                "type": "string",
                "enum": [
                  "Inter",
                  "Roboto",
                  "Open Sans",
                  "Lato",
                  "Montserrat",
                  "Poppins",
                  "Raleway",
                  "Nunito",
                  "Quicksand",
                  "Oswald",
                  "Bebas Neue",
                  "Playfair Display"
                ]
              },
              "fontSize": {
                "type": "number",
                "minimum": 4,
                "maximum": 1000
              },
              "fontWeight": {
                "anyOf": [
                  {
                    "type": "number",
                    "const": 300
                  },
                  {
                    "type": "number",
                    "const": 400
                  },
                  {
                    "type": "number",
                    "const": 500
                  },
                  {
                    "type": "number",
                    "const": 600
                  },
                  {
                    "type": "number",
                    "const": 700
                  },
                  {
                    "type": "number",
                    "const": 800
                  },
                  {
                    "type": "number",
                    "const": 900
                  }
                ]
              },
              "lineHeight": {
                "type": "number",
                "minimum": 0.5,
                "maximum": 4
              },
              "letterSpacing": {
                "description": "Pixels.",
                "type": "number",
                "minimum": -50,
                "maximum": 200
              },
              "textAlign": {
                "type": "string",
                "enum": [
                  "left",
                  "center",
                  "right"
                ]
              },
              "textTransform": {
                "type": "string",
                "enum": [
                  "none",
                  "uppercase",
                  "lowercase",
                  "capitalize"
                ]
              },
              "verticalAlign": {
                "description": "Where text sits inside the layer box.",
                "type": "string",
                "enum": [
                  "top",
                  "center",
                  "bottom"
                ]
              },
              "padding": {
                "type": "number",
                "minimum": 0,
                "maximum": 400
              },
              "borderRadius": {
                "type": "number",
                "minimum": 0,
                "maximum": 2000
              },
              "borderColor": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^(#[0-9a-fA-F]{3,8}|rgba?\\([\\d\\s.,%]+\\)|hsla?\\([\\d\\s.,%deg]+\\)|transparent|[a-zA-Z]+)$"
              },
              "borderWidth": {
                "type": "number",
                "minimum": 0,
                "maximum": 100
              },
              "opacity": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "objectFit": {
                "description": "Images and videos.",
                "type": "string",
                "enum": [
                  "cover",
                  "contain"
                ]
              },
              "shadow": {
                "description": "A soft drop shadow: under the box when it has a background, under the letters of text without one.",
                "type": "boolean"
              }
            },
            "additionalProperties": false
          },
          "enter": {
            "type": "object",
            "properties": {
              "preset": {
                "type": "string",
                "enum": [
                  "none",
                  "fade",
                  "rise",
                  "pop",
                  "slideLeft",
                  "slideRight",
                  "wipe"
                ]
              },
              "durationInFrames": {
                "description": "Defaults to 15 frames.",
                "type": "integer",
                "minimum": 1,
                "maximum": 300
              }
            },
            "required": [
              "preset"
            ],
            "additionalProperties": false
          },
          "exit": {
            "type": "object",
            "properties": {
              "preset": {
                "type": "string",
                "enum": [
                  "none",
                  "fade",
                  "rise",
                  "pop",
                  "slideLeft",
                  "slideRight",
                  "wipe"
                ]
              },
              "durationInFrames": {
                "description": "Defaults to 15 frames.",
                "type": "integer",
                "minimum": 1,
                "maximum": 300
              }
            },
            "required": [
              "preset"
            ],
            "additionalProperties": false
          },
          "locked": {
            "description": "Shown in the editor but not draggable (backgrounds, logos).",
            "type": "boolean"
          },
          "groupId": {
            "description": "Layers with the same groupId move together.",
            "type": "string",
            "maxLength": 64
          }
        },
        "required": [
          "id",
          "type",
          "x",
          "y",
          "width",
          "height"
        ],
        "additionalProperties": false
      }
    },
    "motion": {
      "maxItems": 60,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "layerId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "points": {
            "minItems": 2,
            "maxItems": 500,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "frame": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "x": {
                  "type": "number",
                  "description": "Offset from the layer's position, in pixels."
                },
                "y": {
                  "type": "number"
                }
              },
              "required": [
                "frame",
                "x",
                "y"
              ],
              "additionalProperties": false
            }
          },
          "easing": {
            "default": "easeInOut",
            "type": "string",
            "enum": [
              "linear",
              "easeInOut",
              "easeIn",
              "easeOut",
              "overshoot"
            ]
          }
        },
        "required": [
          "layerId",
          "points"
        ],
        "additionalProperties": false
      }
    },
    "audio": {
      "type": "object",
      "properties": {
        "src": {
          "type": "string",
          "maxLength": 2048,
          "format": "uri"
        },
        "volume": {
          "default": 0.8,
          "type": "number",
          "minimum": 0,
          "maximum": 1
        }
      },
      "required": [
        "src"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "version",
    "canvas",
    "layers"
  ],
  "additionalProperties": false,
  "$id": "https://framlit.app/schemas/framlit-scene.v1.json",
  "title": "Framlit Scene v1",
  "description": "A video as data: canvas, background, positioned layers with entrance and exit presets, and motion paths. POST it to https://framlit.app/api/mcp/scenes (or `framlit scene create`) and Framlit compiles it into an editable, renderable project."
}
