{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "project.schema.json",
  "title": "Studio Project",
  "description": "Portable metadata for a screenshot studio project. Binary assets are referenced by key and are not embedded.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schemaVersion",
    "id",
    "name",
    "createdAt",
    "updatedAt",
    "screenshotTarget",
    "allowTargetDeviceMismatch",
    "slides"
  ],
  "properties": {
    "schemaVersion": {
      "const": 8
    },
    "id": {
      "type": "string",
      "minLength": 1
    },
    "name": {
      "type": "string"
    },
    "listingSource": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "appId",
        "country",
        "text",
        "source",
        "capturedAt"
      ],
      "properties": {
        "appId": {
          "type": [
            "string",
            "null"
          ]
        },
        "country": {
          "type": "string",
          "pattern": "^[a-z]{2}$"
        },
        "text": {
          "type": "string",
          "maxLength": 16384
        },
        "source": {
          "type": "string",
          "maxLength": 16384
        },
        "capturedAt": {
          "type": "number",
          "minimum": 0
        }
      }
    },
    "appIcon": {
      "type": "string"
    },
    "createdAt": {
      "type": "number"
    },
    "updatedAt": {
      "type": "number"
    },
    "screenshotTarget": {
      "enum": [
        "iphone-6_9",
        "ipad-13"
      ]
    },
    "allowTargetDeviceMismatch": {
      "type": "boolean"
    },
    "slides": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/slide"
      }
    },
    "locales": {
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      },
      "uniqueItems": true
    },
    "defaultLocale": {
      "type": "string",
      "minLength": 1
    }
  },
  "$defs": {
    "slide": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "background",
        "text",
        "device",
        "screenshotRef"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "name": {
          "type": "string",
          "maxLength": 48
        },
        "role": {
          "enum": [
            "hero",
            "feature",
            "proof",
            "differentiator",
            "cta"
          ]
        },
        "background": {
          "$ref": "#/$defs/background"
        },
        "text": {
          "$ref": "#/$defs/text"
        },
        "device": {
          "$ref": "#/$defs/device"
        },
        "screenshotRef": {
          "type": [
            "string",
            "null"
          ]
        },
        "elements": {
          "type": "array",
          "maxItems": 8,
          "items": {
            "$ref": "#/$defs/slideElement"
          }
        },
        "localizedText": {
          "type": "object",
          "additionalProperties": {
            "$ref": "#/$defs/localizedText"
          }
        }
      }
    },
    "background": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type"
      ],
      "properties": {
        "type": {
          "enum": [
            "solid",
            "gradient",
            "image"
          ]
        },
        "color": {
          "type": "string"
        },
        "gradient": {
          "$ref": "#/$defs/gradient"
        },
        "imageRef": {
          "type": "string"
        },
        "blur": {
          "type": "number"
        },
        "panorama": {
          "$ref": "#/$defs/panorama"
        }
      }
    },
    "panorama": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "groupId",
        "index",
        "span"
      ],
      "properties": {
        "groupId": {
          "type": "string",
          "minLength": 1
        },
        "index": {
          "type": "integer",
          "minimum": 0,
          "maximum": 4
        },
        "span": {
          "type": "integer",
          "minimum": 2,
          "maximum": 5
        }
      }
    },
    "gradient": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "colors",
        "direction"
      ],
      "properties": {
        "colors": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "minItems": 2
        },
        "direction": {
          "type": "number"
        }
      }
    },
    "localizedText": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "content",
        "subCaption"
      ],
      "properties": {
        "content": {
          "type": "string"
        },
        "subCaption": {
          "type": "string"
        }
      }
    },
    "elementPosition": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "zone",
        "nudgeX",
        "nudgeY"
      ],
      "properties": {
        "zone": {
          "enum": [
            "top-left",
            "top-center",
            "top-right",
            "middle-left",
            "middle-center",
            "middle-right",
            "bottom-left",
            "bottom-center",
            "bottom-right"
          ]
        },
        "nudgeX": {
          "type": "number",
          "minimum": -100,
          "maximum": 100,
          "description": "Authored templates may exceed the interactive ±12 adjustment range."
        },
        "nudgeY": {
          "type": "number",
          "minimum": -100,
          "maximum": 100
        }
      }
    },
    "elementSize": {
      "enum": [
        "small",
        "medium",
        "large"
      ]
    },
    "elementColor": {
      "type": "string",
      "pattern": "^(text|#[0-9a-fA-F]{6})$"
    },
    "textColor": {
      "anyOf": [
        {
          "enum": [
            "white",
            "black",
            "auto"
          ]
        },
        {
          "type": "string",
          "pattern": "^#[0-9a-fA-F]{6}$"
        }
      ]
    },
    "slideElement": {
      "oneOf": [
        {
          "$ref": "#/$defs/badgeElement"
        },
        {
          "$ref": "#/$defs/ratingElement"
        },
        {
          "$ref": "#/$defs/laurelElement"
        },
        {
          "$ref": "#/$defs/appIconBadgeElement"
        },
        {
          "$ref": "#/$defs/deviceExtraElement"
        },
        {
          "$ref": "#/$defs/shapeElement"
        },
        {
          "$ref": "#/$defs/stickerElement"
        }
      ]
    },
    "badgeElement": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "type",
        "position",
        "text",
        "style"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "type": {
          "const": "badge"
        },
        "position": {
          "$ref": "#/$defs/elementPosition"
        },
        "text": {
          "type": "string",
          "maxLength": 28
        },
        "style": {
          "enum": [
            "solid",
            "soft",
            "outline"
          ]
        },
        "size": {
          "$ref": "#/$defs/elementSize"
        },
        "color": {
          "$ref": "#/$defs/elementColor"
        }
      }
    },
    "ratingElement": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "type",
        "position",
        "rating",
        "countText",
        "tone"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "type": {
          "const": "rating"
        },
        "position": {
          "$ref": "#/$defs/elementPosition"
        },
        "rating": {
          "enum": [
            4,
            4.5,
            4.8,
            5
          ]
        },
        "countText": {
          "type": "string",
          "maxLength": 32
        },
        "tone": {
          "enum": [
            "light",
            "dark"
          ]
        },
        "size": {
          "$ref": "#/$defs/elementSize"
        },
        "color": {
          "$ref": "#/$defs/elementColor"
        }
      }
    },
    "laurelElement": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "type",
        "position",
        "text",
        "tone"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "type": {
          "const": "laurel"
        },
        "position": {
          "$ref": "#/$defs/elementPosition"
        },
        "text": {
          "type": "string",
          "maxLength": 36
        },
        "tone": {
          "enum": [
            "light",
            "dark"
          ]
        },
        "size": {
          "$ref": "#/$defs/elementSize"
        },
        "color": {
          "$ref": "#/$defs/elementColor"
        }
      }
    },
    "appIconBadgeElement": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "type",
        "position",
        "size",
        "style"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "type": {
          "const": "appIconBadge"
        },
        "position": {
          "$ref": "#/$defs/elementPosition"
        },
        "size": {
          "enum": [
            "small",
            "medium",
            "large"
          ]
        },
        "style": {
          "enum": [
            "plain",
            "card"
          ]
        }
      }
    },
    "deviceExtraElement": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "type",
        "position",
        "device"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "type": {
          "const": "deviceExtra"
        },
        "position": {
          "$ref": "#/$defs/elementPosition"
        },
        "device": {
          "$ref": "#/$defs/device"
        },
        "screenshotRef": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "shapeElement": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "type",
        "position",
        "shape",
        "tone",
        "size"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "type": {
          "const": "shape"
        },
        "position": {
          "$ref": "#/$defs/elementPosition"
        },
        "shape": {
          "enum": [
            "blob",
            "ring",
            "grid-dots"
          ]
        },
        "tone": {
          "enum": [
            "accent",
            "light",
            "dark"
          ]
        },
        "size": {
          "enum": [
            "small",
            "medium",
            "large"
          ]
        },
        "color": {
          "$ref": "#/$defs/elementColor"
        }
      }
    },
    "stickerElement": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "type",
        "position",
        "imageRef",
        "size",
        "mask",
        "layer"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "type": {
          "const": "sticker"
        },
        "position": {
          "$ref": "#/$defs/elementPosition"
        },
        "imageRef": {
          "type": [
            "string",
            "null"
          ]
        },
        "size": {
          "enum": [
            "small",
            "medium",
            "large"
          ]
        },
        "mask": {
          "enum": [
            "none",
            "rounded",
            "circle"
          ]
        },
        "layer": {
          "enum": [
            "behind",
            "front"
          ]
        }
      }
    },
    "text": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "content",
        "font",
        "size",
        "color",
        "align",
        "verticalPosition",
        "horizontalOffset",
        "showSubCaption",
        "subCaption",
        "subCaptionFont",
        "subCaptionSize",
        "subCaptionSpacing"
      ],
      "properties": {
        "content": {
          "type": "string"
        },
        "font": {
          "$ref": "#/$defs/font"
        },
        "size": {
          "type": "number"
        },
        "color": {
          "$ref": "#/$defs/textColor"
        },
        "align": {
          "enum": [
            "left",
            "center",
            "right"
          ]
        },
        "verticalPosition": {
          "type": "number"
        },
        "horizontalOffset": {
          "type": "number"
        },
        "showSubCaption": {
          "type": "boolean"
        },
        "subCaption": {
          "type": "string"
        },
        "subCaptionFont": {
          "$ref": "#/$defs/font"
        },
        "subCaptionSize": {
          "type": "number"
        },
        "subCaptionSpacing": {
          "type": "number"
        }
      }
    },
    "font": {
      "oneOf": [
        {
          "enum": [
            "sf-pro",
            "inter",
            "poppins",
            "dm-sans",
            "playfair",
            "space-grotesk",
            "manrope",
            "montserrat",
            "outfit",
            "sora",
            "fraunces",
            "dm-serif-display",
            "oswald",
            "archivo-black",
            "bricolage-grotesque",
            "jetbrains-mono",
            "noto-sans-jp",
            "noto-sans-kr",
            "noto-sans-sc",
            "noto-sans-tc",
            "noto-sans-arabic",
            "noto-sans-devanagari"
          ]
        },
        {
          "type": "string",
          "pattern": "^custom:.+",
          "description": "A font you uploaded under the id after the prefix"
        }
      ]
    },
    "device": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "model",
        "angle",
        "verticalPosition",
        "frameScale",
        "horizontalPosition",
        "allowOffCanvasPosition"
      ],
      "properties": {
        "model": {
          "enum": [
            "iphone-17-pro-max",
            "iphone-17-pro",
            "iphone-16-pro-max",
            "iphone-16-pro",
            "ipad-13"
          ]
        },
        "angle": {
          "enum": [
            "straight",
            "slight-left",
            "slight-right",
            "dramatic-left",
            "dramatic-right"
          ]
        },
        "verticalPosition": {
          "type": "number"
        },
        "frameScale": {
          "type": "number"
        },
        "horizontalPosition": {
          "type": "number"
        },
        "allowOffCanvasPosition": {
          "type": "boolean"
        },
        "hidden": {
          "type": "boolean"
        },
        "spanPanorama": {
          "type": "boolean"
        },
        "tilt": {
          "type": "number",
          "minimum": -30,
          "maximum": 30
        },
        "popOuts": {
          "type": "array",
          "maxItems": 5,
          "items": {
            "$ref": "#/$defs/popOut"
          }
        }
      }
    },
    "popOut": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "x",
        "y",
        "width",
        "height",
        "scale"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "x": {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        "y": {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        "width": {
          "type": "number",
          "minimum": 0.04,
          "maximum": 1
        },
        "height": {
          "type": "number",
          "minimum": 0.04,
          "maximum": 1
        },
        "scale": {
          "type": "number",
          "minimum": 1.1,
          "maximum": 2.2
        }
      }
    }
  }
}
