> ## Documentation Index
> Fetch the complete documentation index at: https://docs.framelane.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Get a template

> One template by project id or listing slug, with its `render_request`, so it can be inspected before it is used. No API key needed for a published template.



## OpenAPI

````yaml /openapi.json get /v1/templates/{id_or_slug}
openapi: 3.1.0
info:
  title: Framelane API
  description: >
    Framelane renders video and runs AI media tasks from a single declarative
    request.


    ### Getting access (self-serve, no human needed)

    You can provision your own workspace and API key end-to-end — no dashboard,
    no sales call:

    1. `POST /v1/signup` with a `workspace_name` and an `email` you control. You
    get an API key
       back immediately and a 6-digit code is emailed to that address.
    2. Read the code from that inbox and `POST /v1/signup/verify` with it. If
    you drive a
       programmatic inbox (e.g. an agent mail service), this whole loop runs unattended.
    3. Your key works the moment the email is verified. Until then every authed
    call returns
       `403 email_not_verified` — verification is the only gate, so an unverified workspace can
       never spend. Re-calling `POST /v1/signup` re-keys the workspace and requires re-verifying.

    `POST /v1/signup`, `POST /v1/signup/verify`, and `GET /v1/capabilities` need
    **no** API key.


    ### Authentication

    Send your API key as a bearer token: `Authorization: Bearer fl_live_...`.


    ### Core workflow

    1. **Get media in.** Pass any publicly accessible URL directly, or `POST
    /v1/uploads` to
       get a signed URL, then `PUT` your file to it.
    2. **Submit one JSON payload.** `POST /v1/renders` composes a whole scene
    (canvas +
       `elements[]` + `transitions[]`) in a single request; `POST /v1/tasks/{type}` runs an AI
       operation (transcribe).
    3. **Wait for completion.** Jobs are async: poll `GET
    /v1/{renders|tasks}/{id}` or register
       a webhook. Statuses end in `completed`, `failed`, or `cancelled`.
    4. **Fetch the result.** A render's artifact is a file: use `output.url` or
       `GET /v1/renders/{id}/download` for a short-lived signed URL. A task that returns data
       puts it on the job's `result` object — `transcribe` returns its transcript there, has no
       `output`, and `404`s on `/v1/tasks/{id}/download`.

    ### Discovering what's possible

    Call **`GET /v1/capabilities`** (no auth) for the machine-readable catalog
    of every effect,

    motion, transition, format, element type, and task parameter — each flagged
    with whether the

    renderer supports it, plus all numeric ranges and rate limits. Validate
    against it before

    submitting to avoid `422`s.


    ### Idempotency

    `POST` endpoints accept an `Idempotency-Key` header. The same key with the
    same body replays

    the original response (`200`); with a different body it returns `409
    Conflict`.


    ### Errors

    Every error has the shape `{"error": {"code", "message", "details"}}`. The
    machine-readable

    `code` (e.g. `source_not_found`, `quota_exceeded`, `codec_unsupported`) is
    stable — branch on

    it to self-correct.
  version: 0.2.0
servers:
  - url: https://api.framelane.io
    description: Production
security: []
tags:
  - name: capabilities
    description: Discover supported features, formats, and limits.
  - name: renders
    description: Compose and render video from a declarative timeline.
  - name: projects
    description: >-
      Stateful editing: read a composition, apply targeted ops, preview, and
      render the head.
  - name: templates
    description: >-
      Browse the template gallery, and manage the public catalogue (admin
      workspaces).
  - name: preview
    description: >-
      Validate a composition for free and preview a frame or window cheaply,
      without a full render.
  - name: tasks
    description: 'Run AI media operations: transcription.'
  - name: uploads
    description: Get signed URLs to upload source media into Framelane storage.
  - name: webhooks
    description: Subscribe to job lifecycle events with signed delivery.
  - name: workspace
    description: Manage your workspace, usage, and assets.
  - name: api-keys
    description: Create and revoke API keys.
  - name: auth
    description: Session sync for the first-party console.
  - name: signup
    description: Create a workspace and verify email.
  - name: billing
    description: Manage your subscription and billing portal.
  - name: system
    description: Health, readiness, and version probes.
paths:
  /v1/templates/{id_or_slug}:
    get:
      tags:
        - templates
      summary: Get a template
      description: >-
        One template by project id or listing slug, with its `render_request`,
        so it can be inspected before it is used. No API key needed for a
        published template.
      operationId: get_template
      parameters:
        - name: id_or_slug
          in: path
          required: true
          schema:
            type: string
            title: Id Or Slug
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TemplateDetail'
        '401':
          description: Invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '404':
          description: No template you can read has this id or slug.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
      security:
        - ApiKey: []
components:
  schemas:
    TemplateDetail:
      properties:
        id:
          type: string
          title: Id
          description: The template's project id.
        scope:
          type: string
          enum:
            - public
            - workspace
          title: Scope
          description: >-
            `public` (in the gallery, from a listing) or `workspace` (one of
            your templates).
        name:
          type: string
          title: Name
          description: The listing title for a public template, else the project name.
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: Listing copy. Null for workspace templates.
        category:
          anyOf:
            - type: string
            - type: 'null'
          title: Category
          description: Listing category. Null for workspace templates.
        tags:
          items:
            type: string
          type: array
          title: Tags
        width:
          type: integer
          title: Width
          description: Canvas width in pixels.
        height:
          type: integer
          title: Height
          description: Canvas height in pixels.
        aspect:
          type: string
          enum:
            - landscape
            - portrait
            - square
          title: Aspect
          description: From width and height.
        duration:
          type: number
          title: Duration
          description: Composition length in seconds.
        element_count:
          type: integer
          title: Element Count
        thumbnail_url:
          anyOf:
            - type: string
            - type: 'null'
          title: Thumbnail Url
          description: Poster image; null until one is generated.
        version:
          type: integer
          title: Version
          description: The head version the card describes.
        thumbnail_version:
          anyOf:
            - type: integer
            - type: 'null'
          title: Thumbnail Version
          description: The head version the thumbnail shows; stale while below `version`.
        preview_url:
          anyOf:
            - type: string
            - type: 'null'
          title: Preview Url
          description: A finished render to play on hover. Public templates only for now.
        premium:
          type: boolean
          title: Premium
          default: false
        listing_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Listing Id
          description: The gallery listing, for public templates.
        slug:
          anyOf:
            - type: string
            - type: 'null'
          title: Slug
          description: 'The listing''s stable handle: GET /v1/templates/{slug}.'
        priority:
          anyOf:
            - type: integer
            - type: 'null'
          title: Priority
          description: Listing order, highest first.
        status:
          anyOf:
            - type: string
              enum:
                - draft
                - published
            - type: 'null'
          title: Status
          description: Listing status. `draft` reaches admin workspaces only.
        family:
          anyOf:
            - type: string
            - type: 'null'
          title: Family
          description: Links the size variants of one design.
        created_at:
          type: string
          format: date-time
          title: Created At
        updated_at:
          type: string
          format: date-time
          title: Updated At
        render_request:
          $ref: '#/components/schemas/RenderRequest'
          description: The template's head composition.
      type: object
      required:
        - id
        - scope
        - name
        - width
        - height
        - aspect
        - duration
        - element_count
        - version
        - created_at
        - updated_at
        - render_request
      title: TemplateDetail
      description: >-
        A gallery card plus the composition, so it can be inspected before it is
        used.
    ApiError:
      description: >-
        The error envelope returned on every 4xx/5xx response: `{"error":
        {...}}`.
      properties:
        error:
          $ref: '#/components/schemas/ErrorResponse'
      required:
        - error
      title: ApiError
      type: object
    RenderRequest:
      properties:
        version:
          anyOf:
            - type: string
              enum:
                - v4
                - v5
            - type: 'null'
          title: Version
          description: >-
            Wire version of this request. Omit it: the render task is v5 exactly
            when the request carries a v5 block, and v4 otherwise. `v5` accepts
            the v5 blocks (a shape's `geometry`, `frame` and `instance`
            elements, `operations`, `components`); `v4` refuses them.
          example: v5
        schema_version:
          anyOf:
            - type: string
              const: v5
            - type: 'null'
          title: Schema Version
          description: >-
            The document format an editor wrote this request in. Not sent to the
            renderer; `version` is the wire's.
          example: v5
        width:
          anyOf:
            - type: integer
              maximum: 8192
              minimum: 16
            - type: 'null'
          title: Width
          description: >-
            Output width in pixels. Must be set together with `height` or both
            omitted.
          example: 1920
        height:
          anyOf:
            - type: integer
              maximum: 8192
              minimum: 16
            - type: 'null'
          title: Height
          description: >-
            Output height in pixels. Must be set together with `width` or both
            omitted.
          example: 1080
        duration:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Duration
          description: >-
            Total composition duration in seconds. Inferred from elements when
            omitted.
          example: 15
        frame_rate:
          anyOf:
            - type: integer
              maximum: 240
              minimum: 1
            - type: 'null'
          title: Frame Rate
          description: >-
            Frames per second. When set, the output encodes at exactly this
            rate. Defaults to 30 when omitted.
          example: 30
        output_format:
          $ref: '#/components/schemas/OutputFormat'
          description: Container and codec for the output file.
          default: mp4
          example: mp4
        output_filename:
          anyOf:
            - type: string
            - type: 'null'
          title: Output Filename
          description: >-
            Custom filename for the artifact (without extension). Auto-generated
            when omitted.
          example: my-render
        background_color:
          type: string
          pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
          title: Background Color
          description: RGBA background color in hex format (#RRGGBBAA).
          default: '#000000ff'
          example: '#000000ff'
        background_image_url:
          anyOf:
            - type: string
              maxLength: 2083
              minLength: 1
              format: uri
            - type: 'null'
          title: Background Image Url
          description: URL of an image to use as the composition background.
          example: https://cdn.example.com/bg.jpg
        alpha:
          type: boolean
          title: Alpha
          description: >-
            When true, produce an alpha-channel (transparent background) output.
            Requires `output_format=webm` or `output_format=mov`.
          default: false
          example: false
        elements:
          items:
            oneOf:
              - $ref: '#/components/schemas/VideoElement'
              - $ref: '#/components/schemas/AudioElement'
              - $ref: '#/components/schemas/TextElement'
              - $ref: '#/components/schemas/ImageElement'
              - $ref: '#/components/schemas/ShapeElement'
              - $ref: '#/components/schemas/FrameElement'
              - $ref: '#/components/schemas/InstanceElement'
            discriminator:
              propertyName: type
              mapping:
                audio: '#/components/schemas/AudioElement'
                frame: '#/components/schemas/FrameElement'
                image: '#/components/schemas/ImageElement'
                instance: '#/components/schemas/InstanceElement'
                shape: '#/components/schemas/ShapeElement'
                text: '#/components/schemas/TextElement'
                video: '#/components/schemas/VideoElement'
          type: array
          title: Elements
          description: Ordered list of timeline elements (video, image, text, audio, etc.).
        transitions:
          items:
            $ref: '#/components/schemas/Transition'
          type: array
          title: Transitions
          description: Transition effects applied between consecutive elements.
        groups:
          items:
            $ref: '#/components/schemas/Group'
          type: array
          title: Groups
          description: >-
            Transform groups over member elements (compose member→group→parent;
            optional stack layout), the alias of frames (§5). A request with
            `frame` or `instance` elements carries none.
        design_size:
          anyOf:
            - $ref: '#/components/schemas/DesignSize'
            - type: 'null'
          description: >-
            The logical root in design px: frames and instances at the root are
            placed in this box, and the render scales it uniformly to `width` x
            `height`, whose aspect it must have. Canvas percent at the root is
            unchanged.
        operations:
          anyOf:
            - $ref: '#/components/schemas/OperationsBlock'
            - type: 'null'
          description: >-
            Element properties driven over time (GVR §4.3): groups of
            operations, each counting from an anchor. Schema:
            `blocks.operations` in GET /v1/capabilities.
        components:
          items:
            $ref: '#/components/schemas/Component'
          type: array
          title: Components
          description: >-
            Component definitions, which `instance` elements place. Schema of
            one: `blocks.components` in GET /v1/capabilities.
        motion_blur:
          anyOf:
            - $ref: '#/components/schemas/MotionBlur'
            - type: 'null'
          description: >-
            Shutter-based motion blur over the whole composition (§5c). Graphics
            motion smears; video holds its decoded frame within the shutter (as
            in AE). Opt-in and costly: accumulation is CPU-side, so the render
            falls back to software encoding — expect roughly `samples`× render
            time.
        background_gradient:
          anyOf:
            - type: string
              pattern: ^\s*(?:linear|radial)-gradient\s*\(
              description: >-
                CSS gradient shorthand — `linear-gradient(...)` or
                `radial-gradient(...)`. Normalised to the Gradient object during
                validation; the renderer never sees a CSS string, and reading
                the composition back returns the object.
              example: 'linear-gradient(90deg, #ff0000 0%, #0000ff 100%)'
            - $ref: '#/components/schemas/Gradient'
            - type: 'null'
          description: >-
            Canvas-sized gradient fill behind every layer (§6). Rendered for the
            full output slice. Accepts a Gradient object or a CSS
            `linear-gradient(...)` string.
        watermark_url:
          anyOf:
            - type: string
              maxLength: 2083
              minLength: 1
              format: uri
            - type: 'null'
          title: Watermark Url
          description: Watermark image overlaid on the output (§6).
        custom_animations:
          anyOf:
            - $ref: '#/components/schemas/CustomAnimationRegistry'
            - type: 'null'
          description: >-
            Inline custom-animation definitions (§3), registered with the engine
            before any element is built. Reference an `element` entry by name
            via `motion[].custom` on any video/image/shape element or group;
            reference a `text` entry via a text element's `animation_preset` or
            `motion[].custom`. The catalog stops being a ceiling — anything
            expressible as keyframes is expressible in the request.
        metadata:
          additionalProperties:
            type: string
          type: object
          title: Metadata
          description: Arbitrary key-value pairs echoed back in all webhook payloads.
          example:
            project_id: proj_123
        webhook_url:
          anyOf:
            - type: string
              maxLength: 2083
              minLength: 1
              format: uri
            - type: 'null'
          title: Webhook Url
          description: >-
            Per-request webhook URL. Overrides workspace-level webhooks for this
            job only. Receives `render.completed`, `render.failed`, and progress
            events.
          example: https://app.example.com/hooks/framelane
        ingest_external:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Ingest External
          description: >-
            Controls handling of video/audio source URLs that are not hosted by
            Framelane. When `true` (the default), each such file is probed for
            its duration before rendering: the job starts in the `ingesting`
            state and moves to `queued` once the probe answers, so elements may
            omit `out_point`. The file itself is fetched by the renderer at
            render time, so the URL must be a direct, public https link to the
            media (not a share page) that supports range requests. When `false`,
            the URL is passed straight through and video/audio require an
            explicit `out_point`.
          example: true
      additionalProperties: false
      type: object
      title: RenderRequest
      description: >-
        Body for ``POST /v1/renders``.


        Defines the composition to render: dimensions, output format, timeline
        elements,

        and optional transitions. The render engine produces a single video
        artifact.
    ErrorResponse:
      description: >-
        The structured error body — the value of the `error` key on an error
        response.
      properties:
        code:
          $ref: '#/components/schemas/ApiErrorCode'
        message:
          type: string
          title: Message
        details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          default: null
          title: Details
      required:
        - code
        - message
      title: ErrorResponse
      type: object
    OutputFormat:
      type: string
      enum:
        - mp4
        - webm
        - mov
        - gif
        - png
        - jpg
      title: OutputFormat
      description: >-
        Values:

        - `mp4` — H.264 MP4 video — the default, widely compatible delivery
        format.

        - `webm` — VP9 WebM video — supports a transparent (alpha) background.

        - `mov` — QuickTime MOV — ProRes 4444 with a transparent (alpha)
        background; large files.

        - `gif` — Animated GIF — roadmap; not yet emitted by the renderer.

        - `png` — Single-frame PNG still — roadmap; not yet emitted by the
        renderer.

        - `jpg` — Single-frame JPEG still — roadmap; not yet emitted by the
        renderer.
      x-enumDescriptions:
        mp4: H.264 MP4 video — the default, widely compatible delivery format.
        webm: VP9 WebM video — supports a transparent (alpha) background.
        mov: >-
          QuickTime MOV — ProRes 4444 with a transparent (alpha) background;
          large files.
        gif: Animated GIF — roadmap; not yet emitted by the renderer.
        png: Single-frame PNG still — roadmap; not yet emitted by the renderer.
        jpg: Single-frame JPEG still — roadmap; not yet emitted by the renderer.
    VideoElement:
      properties:
        lut_url:
          anyOf:
            - type: string
              maxLength: 2083
              minLength: 1
              format: uri
            - type: 'null'
          title: Lut Url
        lut_intensity:
          type: number
          maximum: 100
          minimum: 0
          title: Lut Intensity
          default: 100
        brightness:
          type: number
          title: Brightness
          description: >-
            Brightness, -100..100, applied as a 0..2 colour multiplier (0 = no
            change). `-100` multiplies by 0: the element renders pure black
            while keeping its alpha — a silhouette of the artwork. Combine a
            `brightness: -100` copy of an image with `blur`, reduced `opacity`
            and a small offset, stacked beneath the real one, to fake the
            content-shaped drop shadow `shadow_x`/`shadow_y` cannot produce.
          default: 0
        contrast:
          type: number
          title: Contrast
          default: 0
        saturation:
          type: number
          title: Saturation
          default: 0
        exposure:
          type: number
          title: Exposure
          default: 0
        sharpness:
          type: number
          title: Sharpness
          default: 0
        blur:
          type: number
          title: Blur
          default: 0
        noise:
          type: number
          title: Noise
          default: 0
        vignette:
          type: number
          title: Vignette
          default: 0
        hue_rotate:
          type: number
          title: Hue Rotate
          default: 0
        temperature:
          type: number
          title: Temperature
          description: >-
            White balance warm (+) / cool (−), −1..1; 0 = no-op. Applied as a
            channel matrix, after the classic brightness/contrast chain.
          default: 0
        tint:
          type: number
          title: Tint
          description: White balance magenta (+) / green (−), −1..1; 0 = no-op.
          default: 0
        vibrance:
          type: number
          title: Vibrance
          description: >-
            Saturation boost weighted toward muted colours, −1..1; 0 = no-op.
            Boosts flat colour without wrecking skin tones.
          default: 0
        highlights:
          type: number
          title: Highlights
          description: Luma-masked tone lift of the brightest range, −1..1; 0 = no-op.
          default: 0
        shadows:
          type: number
          title: Shadows
          description: Luma-masked tone lift of the darkest range, −1..1; 0 = no-op.
          default: 0
        crop_top:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Top
          default: 0
        crop_bottom:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Bottom
          default: 0
        crop_left:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Left
          default: 0
        crop_right:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Right
          default: 0
        border_radius:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - $ref: '#/components/schemas/CornerRadiiPx'
          title: Border Radius
          description: >-
            Corner radius in pixels — a single value, or per-corner `{top_left,
            top_right, bottom_right, bottom_left}` for asymmetric corners (e.g.
            a card rounded only on top). The renderer normalizes px to a 0–1
            fraction of the element's shorter side (≈ half the shorter side is
            fully rounded) and clamps to that range. For shape elements use
            `corner_radius` (already 0–1).
          default: 0
        border_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Border Color
        border_width:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Border Width
          description: >-
            Border thickness in pixels (converted to the renderer's relative
            border scale against the element's shorter side, so it survives
            resizes). The border outlines the element's border_radius-rounded
            quad — not the image's alpha silhouette.
          default: 0
        shadow_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Shadow Color
        shadow_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Blur
          description: >-
            Shadow softness, 0–100 (renderer blur-strength scale, not pixels): 0
            = hard edge, 100 = maximum blur. Values are clamped to that range.
          default: 0
        shadow_x:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow X
          description: >-
            Horizontal shadow offset in pixels (converted to the renderer's
            relative offset scale). The shadow silhouette is the element's
            border_radius-rounded quad, not the content alpha — a cut-out PNG
            gets a card shadow, not an outline-shaped one. On a cut-out that
            reads as a floating rectangle (rotated with the element, which is
            the tell). For a shadow that follows the artwork, drop these fields
            and stack a second copy of the same image underneath at `brightness:
            -100` (a black silhouette, alpha preserved) with `blur` and reduced
            `opacity`, offset a few percent — see `brightness`.
          default: 0
        shadow_y:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Y
          description: Vertical shadow offset in pixels. See `shadow_x`.
          default: 0
        blend_mode:
          $ref: '#/components/schemas/BlendMode'
          default: none
        mask_shape:
          anyOf:
            - $ref: '#/components/schemas/MaskShape'
            - type: 'null'
        mask:
          anyOf:
            - $ref: '#/components/schemas/MaskConfig'
            - type: 'null'
        mask_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/MaskKeyframe'
              type: array
            - type: 'null'
          title: Mask Keyframes
        matte:
          anyOf:
            - $ref: '#/components/schemas/Matte'
            - type: 'null'
        backdrop_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Backdrop Blur
          description: >-
            Glass: the scene behind the element, Gaussian-blurred, fills its
            footprint under its translucent content (a shape's own silhouette, a
            video's or image's box with its corner radius and mask). A Gaussian
            sigma in canvas px, as CSS `backdrop-filter: blur()` takes it; the
            renderer caps it at 128. 0 is off.
          default: 0
        backdrop_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Backdrop Blur Keyframes
          description: >-
            Animate `backdrop_blur` over the timeline; replaces the static value
            while present.
        layer_blur:
          type: number
          maximum: 128
          minimum: 0
          title: Layer Blur
          description: >-
            Blurs the element itself as drawn: a Gaussian sigma in canvas px, as
            CSS `filter: blur()` takes it, spreading three sigma past its box.
            At most 128; 0 is off. Not on a shape yet (the editor's preview
            cannot draw it), and not with `blend_mode`, `backdrop_blur` or
            `matte`, beside which the renderer would draw the element without
            it.
          default: 0
        layer_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Layer Blur Keyframes
          description: >-
            Animate `layer_blur` over the timeline; replaces the static value
            while present.
        glass:
          anyOf:
            - $ref: '#/components/schemas/Glass'
            - type: 'null'
          description: >-
            A refracting glass edge: the scene behind bends inward near the
            element's outline. Pair it with `backdrop_blur` for frosted glass.
        x:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X
          default: 50%
        'y':
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: 'Y'
          default: 50%
        width:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Width
          description: >-
            Box width. Together with `height` this is a **cover** box, not a fit
            box: sized media (video, image) scales to fill it and the overflow
            is **cropped** — it is never letterboxed. A box whose aspect ratio
            differs from the source's therefore silently cuts the source's
            edges. To show a source whole, match the box aspect to it: on a
            `W`x`H` canvas, for a source of aspect `a`, `width% = a * height% *
            (H/W)` — e.g. a 1024x1536 asset (a=0.667) on 1920x1080 needs `width%
            = 0.375 * height%`. Percentages are of the canvas, not the parent.
          default: 100%
          example: 40%
        height:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Height
          description: >-
            Box height. See `width` — the pair is a cover box and crops rather
            than letterboxes. Percentages are of the canvas.
          default: 100%
          example: 80%
        aspect_ratio:
          anyOf:
            - type: number
            - type: 'null'
          title: Aspect Ratio
          description: >-
            **Accepted but ignored — the renderer never receives this field.**
            It does NOT constrain or correct the `width`/`height` box, so it
            cannot be used to stop a mismatched box from cropping the source;
            size the box per `width` instead. Kept for backward compatibility.
        x_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Anchor
          description: >-
            Horizontal pivot the element **rotates** about, measured across its
            own box (`0%` = left edge, `50%` = centre, `100%` = right edge; px
            values are an offset from the left edge). This is a pivot, not an
            alignment — `x` always places the element's centre, so the anchor
            changes nothing on an unrotated element. Honored on video, image and
            shape; text and the generator elements rotate about their centre and
            reject a non-default anchor.
          default: 50%
          example: 0%
        y_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Anchor
          description: >-
            Vertical pivot the element **rotates** about, measured across its
            own box (`0%` = top edge, `50%` = centre, `100%` = bottom edge).
            Same rules as `x_anchor`: a pivot, not an alignment.
          default: 50%
          example: 100%
        x_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Rotation
          default: 0°
        y_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Rotation
          default: 0°
        z_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Z Rotation
          default: 0°
        x_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Scale
          default: 100%
        y_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Scale
          default: 100%
        flip_horizontal:
          type: boolean
          title: Flip Horizontal
          default: false
        flip_vertical:
          type: boolean
          title: Flip Vertical
          default: false
        opacity:
          type: number
          maximum: 100
          minimum: 0
          title: Opacity
          default: 100
        z_index:
          anyOf:
            - type: integer
            - type: 'null'
          title: Z Index
          description: >-
            Stacking order (higher = on top). When omitted, elements stack in
            array order — later elements render above earlier ones (painter's
            rule); text defaults one layer above non-text. Set explicit values
            only when array order isn't the order you want.
        clip:
          type: boolean
          title: Clip
          default: false
        color_overlay:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Color Overlay
          description: >-
            **Not supported by the renderer — setting this is rejected.** A
            strict submit returns `unsupported_feature` (422) and a dry-run
            reports it as a blocking violation; there is no native tint field to
            map it onto. To darken an element use `brightness` (`-100` is a
            black silhouette); to lay a colour over one, stack a `shape` with a
            flat or gradient `fill` above it.
        type:
          type: string
          const: video
          title: Type
          default: video
        id:
          anyOf:
            - type: string
            - type: 'null'
          title: Id
          description: >-
            Unique identifier for this element. **Required on image elements** —
            an empty or missing ID causes the image to be silently skipped by
            the renderer. Recommended on all elements that are referenced by a
            transition.
          example: clip_01
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional human-readable label. Not used by the renderer.
        track:
          anyOf:
            - type: integer
              maximum: 255
              minimum: 0
            - type: 'null'
          title: Track
          description: >-
            Timeline track index (0–255). Informational only; not used by the
            renderer.
        time:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Time
          description: >-
            When this element starts on the output timeline, in seconds.
            Defaults to `0` when omitted.
          example: 0
        visible:
          type: boolean
          title: Visible
          description: >-
            Set to `false` to skip this element without removing it from the
            request.
          default: true
        locked:
          type: boolean
          title: Locked
          description: Marks the element locked in editors. Not sent to the renderer.
          default: false
        source_url:
          type: string
          maxLength: 2083
          minLength: 1
          format: uri
          title: Source Url
          description: >-
            URL of the video file — MP4, MOV or WebM only; any other extension
            is rejected at submit. Must be accessible by the renderer.
          example: https://cdn.example.com/clip.mp4
        in_point:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: In Point
          description: >-
            In point — seconds into the source file to start playing from.
            Defaults to `0`.
          example: 0
        out_point:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Out Point
          description: >-
            Out point — seconds into the source file to stop playing. Defaults
            to end of source when omitted. Controls clip length on the timeline.
          example: 6
        speed:
          type: number
          maximum: 4
          minimum: 0.25
          title: Speed
          description: >-
            Playback speed. `1.0` = normal speed, `2.0` = 2× (half duration),
            `0.5` = half speed (double duration).
          default: 1
          example: 1
        playback_rate_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/RateKeyframe'
              type: array
              maxItems: 512
            - type: 'null'
          title: Playback Rate Keyframes
          description: >-
            Speed ramp (§5c): piecewise-linear playback-rate keyframes (absolute
            timeline seconds; rate 0 = freeze; at most 512 keyframes — the
            renderer truncates beyond that). Audio keeps the static rate — mute
            or detach it on ramped clips.
        volume:
          type: number
          maximum: 100
          minimum: 0
          title: Volume
          description: >-
            Embedded audio volume as a percentage (0–100). Affects only the
            audio track inside this video clip.
          default: 100
          example: 100
        muted:
          type: boolean
          title: Muted
          description: >-
            Silence the clip's embedded audio. `volume` is kept, so unmuting
            restores the level.
          default: false
        loop:
          type: boolean
          title: Loop
          description: >-
            Repeat the source to fill the clip: when playback passes the end of
            the file it restarts from the file's first frame (not `in_point`).
            Set `out_point` past the source's length to choose how long the clip
            runs — its timeline length is still `(out_point − in_point) /
            speed`. Requires `out_point`.
          default: false
        fade_in_duration:
          type: number
          title: Fade In Duration
          description: Duration in seconds of a linear audio fade-in at the clip's start.
          default: 0
          example: 0
        fade_out_duration:
          type: number
          title: Fade Out Duration
          description: Duration in seconds of a linear audio fade-out at the clip's end.
          default: 0
          example: 0
        effects:
          items:
            $ref: '#/components/schemas/Effect'
          type: array
          title: Effects
          default: []
        motion:
          items:
            $ref: '#/components/schemas/Motion'
          type: array
          title: Motion
          default: []
      additionalProperties: false
      type: object
      required:
        - source_url
      title: VideoElement
    AudioElement:
      properties:
        type:
          type: string
          const: audio
          title: Type
          default: audio
        id:
          anyOf:
            - type: string
            - type: 'null'
          title: Id
          description: >-
            Unique identifier for this element. **Required on image elements** —
            an empty or missing ID causes the image to be silently skipped by
            the renderer. Recommended on all elements that are referenced by a
            transition.
          example: clip_01
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional human-readable label. Not used by the renderer.
        track:
          anyOf:
            - type: integer
              maximum: 255
              minimum: 0
            - type: 'null'
          title: Track
          description: >-
            Timeline track index (0–255). Informational only; not used by the
            renderer.
        time:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Time
          description: >-
            When this element starts on the output timeline, in seconds.
            Defaults to `0` when omitted.
          example: 0
        visible:
          type: boolean
          title: Visible
          description: >-
            Set to `false` to skip this element. Audio has no picture — `false`
            drops the whole track from the render, it does not mute or hide it
            (the linter reports that as `AUDIO_HIDDEN`). To keep the track but
            silence it, set `muted: true`.
          default: true
        locked:
          type: boolean
          title: Locked
          description: Marks the element locked in editors. Not sent to the renderer.
          default: false
        source_url:
          type: string
          maxLength: 2083
          minLength: 1
          format: uri
          title: Source Url
          description: URL of the audio file. Must be accessible by the renderer.
          example: https://cdn.example.com/music.mp3
        in_point:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: In Point
          description: >-
            In point — seconds into the source file to start from. Defaults to
            `0`.
          example: 0
        out_point:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Out Point
          description: >-
            Out point — seconds into the source file to stop. Defaults to end of
            source when omitted. Controls clip length on the timeline.
          example: 30
        speed:
          type: number
          maximum: 4
          minimum: 0.25
          title: Speed
          description: Playback speed. `1.0` = normal speed, `2.0` = 2× speed.
          default: 1
          example: 1
        volume:
          type: number
          maximum: 100
          exclusiveMinimum: 0
          title: Volume
          description: >-
            Audio volume as a percentage (1–100). **Must be greater than 0** —
            the renderer silently drops audio streams with `volume <= 0`. Use
            `muted` to silence the track.
          default: 100
          example: 100
        muted:
          type: boolean
          title: Muted
          description: >-
            Silence this track while keeping it, and its `volume`, in the
            composition. A muted track is left out of the render's audio.
          default: false
        fade_in_duration:
          type: number
          title: Fade In Duration
          description: Duration in seconds of a linear audio fade-in at the clip's start.
          default: 0
          example: 0
        fade_out_duration:
          type: number
          title: Fade Out Duration
          description: Duration in seconds of a linear audio fade-out at the clip's end.
          default: 0
          example: 0
      additionalProperties: false
      type: object
      required:
        - source_url
      title: AudioElement
    TextElement:
      properties:
        border_radius:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - $ref: '#/components/schemas/CornerRadiiPx'
          title: Border Radius
          description: >-
            Corner radius in pixels — a single value, or per-corner `{top_left,
            top_right, bottom_right, bottom_left}` for asymmetric corners (e.g.
            a card rounded only on top). The renderer normalizes px to a 0–1
            fraction of the element's shorter side (≈ half the shorter side is
            fully rounded) and clamps to that range. For shape elements use
            `corner_radius` (already 0–1).
          default: 0
        border_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Border Color
        border_width:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Border Width
          description: >-
            Border thickness in pixels (converted to the renderer's relative
            border scale against the element's shorter side, so it survives
            resizes). The border outlines the element's border_radius-rounded
            quad — not the image's alpha silhouette.
          default: 0
        shadow_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Shadow Color
        shadow_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Blur
          description: >-
            Shadow softness, 0–100 (renderer blur-strength scale, not pixels): 0
            = hard edge, 100 = maximum blur. Values are clamped to that range.
          default: 0
        shadow_x:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow X
          description: >-
            Horizontal shadow offset in pixels (converted to the renderer's
            relative offset scale). The shadow silhouette is the element's
            border_radius-rounded quad, not the content alpha — a cut-out PNG
            gets a card shadow, not an outline-shaped one. On a cut-out that
            reads as a floating rectangle (rotated with the element, which is
            the tell). For a shadow that follows the artwork, drop these fields
            and stack a second copy of the same image underneath at `brightness:
            -100` (a black silhouette, alpha preserved) with `blur` and reduced
            `opacity`, offset a few percent — see `brightness`.
          default: 0
        shadow_y:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Y
          description: Vertical shadow offset in pixels. See `shadow_x`.
          default: 0
        x:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X
          default: 50%
        'y':
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: 'Y'
          default: 50%
        width:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Width
          description: >-
            Box width. Together with `height` this is a **cover** box, not a fit
            box: sized media (video, image) scales to fill it and the overflow
            is **cropped** — it is never letterboxed. A box whose aspect ratio
            differs from the source's therefore silently cuts the source's
            edges. To show a source whole, match the box aspect to it: on a
            `W`x`H` canvas, for a source of aspect `a`, `width% = a * height% *
            (H/W)` — e.g. a 1024x1536 asset (a=0.667) on 1920x1080 needs `width%
            = 0.375 * height%`. Percentages are of the canvas, not the parent.
          default: 100%
          example: 40%
        height:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Height
          description: >-
            Box height. See `width` — the pair is a cover box and crops rather
            than letterboxes. Percentages are of the canvas.
          default: 100%
          example: 80%
        aspect_ratio:
          anyOf:
            - type: number
            - type: 'null'
          title: Aspect Ratio
          description: >-
            **Accepted but ignored — the renderer never receives this field.**
            It does NOT constrain or correct the `width`/`height` box, so it
            cannot be used to stop a mismatched box from cropping the source;
            size the box per `width` instead. Kept for backward compatibility.
        x_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Anchor
          description: >-
            Horizontal pivot the element **rotates** about, measured across its
            own box (`0%` = left edge, `50%` = centre, `100%` = right edge; px
            values are an offset from the left edge). This is a pivot, not an
            alignment — `x` always places the element's centre, so the anchor
            changes nothing on an unrotated element. Honored on video, image and
            shape; text and the generator elements rotate about their centre and
            reject a non-default anchor.
          default: 50%
          example: 0%
        y_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Anchor
          description: >-
            Vertical pivot the element **rotates** about, measured across its
            own box (`0%` = top edge, `50%` = centre, `100%` = bottom edge).
            Same rules as `x_anchor`: a pivot, not an alignment.
          default: 50%
          example: 100%
        x_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Rotation
          default: 0°
        y_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Rotation
          default: 0°
        z_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Z Rotation
          default: 0°
        x_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Scale
          default: 100%
        y_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Scale
          default: 100%
        flip_horizontal:
          type: boolean
          title: Flip Horizontal
          default: false
        flip_vertical:
          type: boolean
          title: Flip Vertical
          default: false
        opacity:
          type: number
          maximum: 100
          minimum: 0
          title: Opacity
          default: 100
        z_index:
          anyOf:
            - type: integer
            - type: 'null'
          title: Z Index
          description: >-
            Stacking order (higher = on top). When omitted, elements stack in
            array order — later elements render above earlier ones (painter's
            rule); text defaults one layer above non-text. Set explicit values
            only when array order isn't the order you want.
        clip:
          type: boolean
          title: Clip
          default: false
        color_overlay:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Color Overlay
          description: >-
            **Not supported by the renderer — setting this is rejected.** A
            strict submit returns `unsupported_feature` (422) and a dry-run
            reports it as a blocking violation; there is no native tint field to
            map it onto. To darken an element use `brightness` (`-100` is a
            black silhouette); to lay a colour over one, stack a `shape` with a
            flat or gradient `fill` above it.
        type:
          type: string
          const: text
          title: Type
          default: text
        id:
          anyOf:
            - type: string
            - type: 'null'
          title: Id
          description: >-
            Unique identifier for this element. **Required on image elements** —
            an empty or missing ID causes the image to be silently skipped by
            the renderer. Recommended on all elements that are referenced by a
            transition.
          example: clip_01
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional human-readable label. Not used by the renderer.
        track:
          anyOf:
            - type: integer
              maximum: 255
              minimum: 0
            - type: 'null'
          title: Track
          description: >-
            Timeline track index (0–255). Informational only; not used by the
            renderer.
        time:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Time
          description: >-
            When this element starts on the output timeline, in seconds.
            Defaults to `0` when omitted.
          example: 0
        visible:
          type: boolean
          title: Visible
          description: >-
            Set to `false` to skip this element without removing it from the
            request.
          default: true
        locked:
          type: boolean
          title: Locked
          description: Marks the element locked in editors. Not sent to the renderer.
          default: false
        duration:
          type: number
          exclusiveMinimum: 0
          title: Duration
          description: >-
            **Required.** Duration in seconds the text is visible. Omitting this
            field produces a zero-length window and the element will not appear
            in the render.
          example: 5
        text:
          type: string
          minLength: 1
          title: Text
          description: The text content to render.
          example: Hello World
        font_family:
          type: string
          title: Font Family
          description: Font family name. Must be available to the renderer.
          default: Inter
          example: Inter
        font_size:
          type: number
          exclusiveMinimum: 0
          title: Font Size
          description: Font size in pixels.
          default: 16
          example: 48
        font_weight:
          type: integer
          maximum: 900
          minimum: 100
          title: Font Weight
          description: >-
            Font weight (100–900). The renderer has no variable weights: `600`
            and above render **bold**, everything below renders regular.
            Composes with `font_style` (italic + ≥600 = bold italic).
          default: 400
          example: 400
        font_style:
          type: string
          enum:
            - normal
            - italic
            - bold
            - bolditalic
          title: Font Style
          description: Font style variant.
          default: normal
        text_color:
          type: string
          pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
          title: Text Color
          description: Text fill color as a hex string.
          default: '#ffffff'
          example: '#ffffff'
        text_align:
          type: string
          enum:
            - left
            - center
            - right
          title: Text Align
          description: Horizontal text alignment.
          default: center
        text_direction:
          type: string
          enum:
            - ltr
            - rtl
          title: Text Direction
          description: Text direction. `rtl` for right-to-left scripts (§4).
          default: ltr
        text_decoration:
          type: string
          enum:
            - none
            - underline
            - strikethrough
          title: Text Decoration
          description: >-
            Text decoration. Only `none` renders. `underline` is reachable only
            per word — `words[].style.underline` — and `strikethrough` has no
            renderer path at all; both are rejected here (422) rather than
            silently dropped. Draw a strike-through as a thin `shape` rectangle
            over the text.
          default: none
        tracking:
          type: number
          title: Tracking
          description: >-
            Letter spacing (tracking) in pixels (converted to the renderer's
            width-relative unit on the wire). Positive values add space between
            characters.
          default: 0
          example: 0
        tabular_numbers:
          type: boolean
          title: Tabular Numbers
          description: >-
            Tabular (monospaced) figures: every digit takes the same advance, so
            columns of numbers align across lines and a `counter` does not
            jitter as its value changes. The renderer's baked fonts carry the
            OpenType alternates; a font whose face lacks the feature gets
            synthesised tabular digits in the export only. Default `false` = the
            font's proportional figures.
          default: false
          example: false
        leading:
          type: number
          title: Leading
          description: >-
            Line height (leading) as a multiplier of font size. `1.2` = 20%
            taller than the font size.
          default: 1.2
          example: 1.2
        stroke_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Stroke Color
          description: >-
            Outline color. Set together with `stroke_width` to enable text
            outlines.
          example: '#000000'
        stroke_width:
          type: number
          title: Stroke Width
          description: >-
            Outline width, em-relative (fraction of `font_size` — e.g. `0.05` =
            5% of the font size). Has no effect unless `stroke_color` is also
            set.
          default: 0
        background_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Background Color
          description: >-
            Secondary colour. Fills the box behind the text when `background` is
            `true`, **and** is the highlight colour for `word_animation.style`
            `color` (the active word) and `box` (the box) — those two styles
            require it.
        background_opacity:
          type: number
          maximum: 100
          minimum: 0
          title: Background Opacity
          default: 100
        x_padding:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Padding
          default: 0
        y_padding:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Padding
          default: 0
        background:
          type: boolean
          title: Background
          description: >-
            Solid color box behind the full text block. Set `background_color`
            to choose the color.
          default: false
        background_style:
          type: string
          enum:
            - block
            - line_hard
            - line_round
          title: Background Style
          description: >-
            Shape of the `background` plate: `block` — one box behind the whole
            text; `line_hard` — a square-cornered box behind each line;
            `line_round` — a rounded box behind each line. Requires `background:
            true`. An animation that centres its words draws the `block` plate
            whichever style is set.
          default: block
        stroke:
          type: boolean
          title: Stroke
          description: >-
            Outline-only mode — draws the glyph stroke with no fill. Requires
            `stroke_color` and `stroke_width`. When `false`, setting
            `stroke_color` + `stroke_width` gives fill + stroke (outlined).
          default: false
        shadow:
          type: boolean
          title: Shadow
          description: Built-in drop shadow.
          default: false
        motion:
          items:
            $ref: '#/components/schemas/Motion'
          type: array
          title: Motion
          default: []
        word_animation:
          anyOf:
            - $ref: '#/components/schemas/WordAnimation'
            - type: 'null'
          description: >-
            Word-level animation style with per-word timestamps. When provided,
            this takes precedence over the `animation_preset` field.
          example:
            style: glow
            words:
              - end: 0.5
                start: 0
                text: Hello
        words:
          anyOf:
            - items:
                $ref: '#/components/schemas/WordSpec'
              type: array
            - type: 'null'
          title: Words
          description: >-
            Per-word timing and optional per-word style overrides (§4). Emitted
            as a top-level `words[]` array; use alongside
            `motion`/`animation_preset` for the animation. Editorial-emphasis
            captions = word timings + a styled keyword.
          example:
            - end: 3
              start: 0
              style:
                color: '#e8734a'
              word: launch
        counter:
          anyOf:
            - $ref: '#/components/schemas/ValueCounter'
            - type: 'null'
          description: >-
            Rewrite this text from an eased numeric sweep (§4). Takes precedence
            over countdown on the same element.
        glow:
          anyOf:
            - $ref: '#/components/schemas/TextGlow'
            - type: 'null'
          description: Soft glow around the glyphs (§4).
        text_wrap:
          type: string
          enum:
            - wrap
            - nowrap
          title: Text Wrap
          default: wrap
        animation_preset:
          anyOf:
            - type: string
            - type: 'null'
          title: Animation Preset
          description: >-
            Renderer text-animation name (e.g. `'typewriter'`) — a catalog text
            animation or a `custom_animations.text` entry. Runs for the whole
            element window with per-glyph timings computed by the renderer. Use
            `motion[]` (with `type`, `preset` or `custom`) for structured timing
            control — but not both. Ignored when `word_animation` is set.
      additionalProperties: false
      type: object
      required:
        - duration
        - text
      title: TextElement
    ImageElement:
      properties:
        lut_url:
          anyOf:
            - type: string
              maxLength: 2083
              minLength: 1
              format: uri
            - type: 'null'
          title: Lut Url
        lut_intensity:
          type: number
          maximum: 100
          minimum: 0
          title: Lut Intensity
          default: 100
        brightness:
          type: number
          title: Brightness
          description: >-
            Brightness, -100..100, applied as a 0..2 colour multiplier (0 = no
            change). `-100` multiplies by 0: the element renders pure black
            while keeping its alpha — a silhouette of the artwork. Combine a
            `brightness: -100` copy of an image with `blur`, reduced `opacity`
            and a small offset, stacked beneath the real one, to fake the
            content-shaped drop shadow `shadow_x`/`shadow_y` cannot produce.
          default: 0
        contrast:
          type: number
          title: Contrast
          default: 0
        saturation:
          type: number
          title: Saturation
          default: 0
        exposure:
          type: number
          title: Exposure
          default: 0
        sharpness:
          type: number
          title: Sharpness
          default: 0
        blur:
          type: number
          title: Blur
          default: 0
        noise:
          type: number
          title: Noise
          default: 0
        vignette:
          type: number
          title: Vignette
          default: 0
        hue_rotate:
          type: number
          title: Hue Rotate
          default: 0
        temperature:
          type: number
          title: Temperature
          description: >-
            White balance warm (+) / cool (−), −1..1; 0 = no-op. Applied as a
            channel matrix, after the classic brightness/contrast chain.
          default: 0
        tint:
          type: number
          title: Tint
          description: White balance magenta (+) / green (−), −1..1; 0 = no-op.
          default: 0
        vibrance:
          type: number
          title: Vibrance
          description: >-
            Saturation boost weighted toward muted colours, −1..1; 0 = no-op.
            Boosts flat colour without wrecking skin tones.
          default: 0
        highlights:
          type: number
          title: Highlights
          description: Luma-masked tone lift of the brightest range, −1..1; 0 = no-op.
          default: 0
        shadows:
          type: number
          title: Shadows
          description: Luma-masked tone lift of the darkest range, −1..1; 0 = no-op.
          default: 0
        crop_top:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Top
          default: 0
        crop_bottom:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Bottom
          default: 0
        crop_left:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Left
          default: 0
        crop_right:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Right
          default: 0
        border_radius:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - $ref: '#/components/schemas/CornerRadiiPx'
          title: Border Radius
          description: >-
            Corner radius in pixels — a single value, or per-corner `{top_left,
            top_right, bottom_right, bottom_left}` for asymmetric corners (e.g.
            a card rounded only on top). The renderer normalizes px to a 0–1
            fraction of the element's shorter side (≈ half the shorter side is
            fully rounded) and clamps to that range. For shape elements use
            `corner_radius` (already 0–1).
          default: 0
        border_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Border Color
        border_width:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Border Width
          description: >-
            Border thickness in pixels (converted to the renderer's relative
            border scale against the element's shorter side, so it survives
            resizes). The border outlines the element's border_radius-rounded
            quad — not the image's alpha silhouette.
          default: 0
        shadow_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Shadow Color
        shadow_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Blur
          description: >-
            Shadow softness, 0–100 (renderer blur-strength scale, not pixels): 0
            = hard edge, 100 = maximum blur. Values are clamped to that range.
          default: 0
        shadow_x:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow X
          description: >-
            Horizontal shadow offset in pixels (converted to the renderer's
            relative offset scale). The shadow silhouette is the element's
            border_radius-rounded quad, not the content alpha — a cut-out PNG
            gets a card shadow, not an outline-shaped one. On a cut-out that
            reads as a floating rectangle (rotated with the element, which is
            the tell). For a shadow that follows the artwork, drop these fields
            and stack a second copy of the same image underneath at `brightness:
            -100` (a black silhouette, alpha preserved) with `blur` and reduced
            `opacity`, offset a few percent — see `brightness`.
          default: 0
        shadow_y:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Y
          description: Vertical shadow offset in pixels. See `shadow_x`.
          default: 0
        blend_mode:
          $ref: '#/components/schemas/BlendMode'
          default: none
        mask_shape:
          anyOf:
            - $ref: '#/components/schemas/MaskShape'
            - type: 'null'
        mask:
          anyOf:
            - $ref: '#/components/schemas/MaskConfig'
            - type: 'null'
        mask_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/MaskKeyframe'
              type: array
            - type: 'null'
          title: Mask Keyframes
        matte:
          anyOf:
            - $ref: '#/components/schemas/Matte'
            - type: 'null'
        backdrop_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Backdrop Blur
          description: >-
            Glass: the scene behind the element, Gaussian-blurred, fills its
            footprint under its translucent content (a shape's own silhouette, a
            video's or image's box with its corner radius and mask). A Gaussian
            sigma in canvas px, as CSS `backdrop-filter: blur()` takes it; the
            renderer caps it at 128. 0 is off.
          default: 0
        backdrop_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Backdrop Blur Keyframes
          description: >-
            Animate `backdrop_blur` over the timeline; replaces the static value
            while present.
        layer_blur:
          type: number
          maximum: 128
          minimum: 0
          title: Layer Blur
          description: >-
            Blurs the element itself as drawn: a Gaussian sigma in canvas px, as
            CSS `filter: blur()` takes it, spreading three sigma past its box.
            At most 128; 0 is off. Not on a shape yet (the editor's preview
            cannot draw it), and not with `blend_mode`, `backdrop_blur` or
            `matte`, beside which the renderer would draw the element without
            it.
          default: 0
        layer_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Layer Blur Keyframes
          description: >-
            Animate `layer_blur` over the timeline; replaces the static value
            while present.
        glass:
          anyOf:
            - $ref: '#/components/schemas/Glass'
            - type: 'null'
          description: >-
            A refracting glass edge: the scene behind bends inward near the
            element's outline. Pair it with `backdrop_blur` for frosted glass.
        x:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X
          default: 50%
        'y':
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: 'Y'
          default: 50%
        width:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Width
          description: >-
            Box width. Together with `height` this is a **cover** box, not a fit
            box: sized media (video, image) scales to fill it and the overflow
            is **cropped** — it is never letterboxed. A box whose aspect ratio
            differs from the source's therefore silently cuts the source's
            edges. To show a source whole, match the box aspect to it: on a
            `W`x`H` canvas, for a source of aspect `a`, `width% = a * height% *
            (H/W)` — e.g. a 1024x1536 asset (a=0.667) on 1920x1080 needs `width%
            = 0.375 * height%`. Percentages are of the canvas, not the parent.
          default: 100%
          example: 40%
        height:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Height
          description: >-
            Box height. See `width` — the pair is a cover box and crops rather
            than letterboxes. Percentages are of the canvas.
          default: 100%
          example: 80%
        aspect_ratio:
          anyOf:
            - type: number
            - type: 'null'
          title: Aspect Ratio
          description: >-
            **Accepted but ignored — the renderer never receives this field.**
            It does NOT constrain or correct the `width`/`height` box, so it
            cannot be used to stop a mismatched box from cropping the source;
            size the box per `width` instead. Kept for backward compatibility.
        x_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Anchor
          description: >-
            Horizontal pivot the element **rotates** about, measured across its
            own box (`0%` = left edge, `50%` = centre, `100%` = right edge; px
            values are an offset from the left edge). This is a pivot, not an
            alignment — `x` always places the element's centre, so the anchor
            changes nothing on an unrotated element. Honored on video, image and
            shape; text and the generator elements rotate about their centre and
            reject a non-default anchor.
          default: 50%
          example: 0%
        y_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Anchor
          description: >-
            Vertical pivot the element **rotates** about, measured across its
            own box (`0%` = top edge, `50%` = centre, `100%` = bottom edge).
            Same rules as `x_anchor`: a pivot, not an alignment.
          default: 50%
          example: 100%
        x_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Rotation
          default: 0°
        y_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Rotation
          default: 0°
        z_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Z Rotation
          default: 0°
        x_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Scale
          default: 100%
        y_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Scale
          default: 100%
        flip_horizontal:
          type: boolean
          title: Flip Horizontal
          default: false
        flip_vertical:
          type: boolean
          title: Flip Vertical
          default: false
        opacity:
          type: number
          maximum: 100
          minimum: 0
          title: Opacity
          default: 100
        z_index:
          anyOf:
            - type: integer
            - type: 'null'
          title: Z Index
          description: >-
            Stacking order (higher = on top). When omitted, elements stack in
            array order — later elements render above earlier ones (painter's
            rule); text defaults one layer above non-text. Set explicit values
            only when array order isn't the order you want.
        clip:
          type: boolean
          title: Clip
          default: false
        color_overlay:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Color Overlay
          description: >-
            **Not supported by the renderer — setting this is rejected.** A
            strict submit returns `unsupported_feature` (422) and a dry-run
            reports it as a blocking violation; there is no native tint field to
            map it onto. To darken an element use `brightness` (`-100` is a
            black silhouette); to lay a colour over one, stack a `shape` with a
            flat or gradient `fill` above it.
        type:
          type: string
          const: image
          title: Type
          default: image
        id:
          type: string
          minLength: 1
          title: Id
          description: >-
            **Required.** Unique identifier for this image. An empty or missing
            ID causes the image to be silently skipped by the renderer.
          example: logo_01
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional human-readable label. Not used by the renderer.
        track:
          anyOf:
            - type: integer
              maximum: 255
              minimum: 0
            - type: 'null'
          title: Track
          description: >-
            Timeline track index (0–255). Informational only; not used by the
            renderer.
        time:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Time
          description: >-
            When this element starts on the output timeline, in seconds.
            Defaults to `0` when omitted.
          example: 0
        visible:
          type: boolean
          title: Visible
          description: >-
            Set to `false` to skip this element without removing it from the
            request.
          default: true
        locked:
          type: boolean
          title: Locked
          description: Marks the element locked in editors. Not sent to the renderer.
          default: false
        duration:
          type: number
          exclusiveMinimum: 0
          title: Duration
          description: >-
            **Required.** Duration in seconds the image is visible. Omitting
            this field produces a zero-length window and the image will not
            appear in the render.
          example: 5
        source_url:
          type: string
          maxLength: 2083
          minLength: 1
          format: uri
          title: Source Url
          description: >-
            URL of the image file (PNG, JPG, WebP, GIF, SVG). A `.svg` draws a
            subset: groups and the basic shapes and paths, filled and stroked
            with flat colours. An element that uses anything else (a `style`
            attribute, `clip-path`, `mask`, `filter`, `stroke-dasharray`, a
            gradient or `currentColor` paint, `<text>`, `<use>`, `<image>`) is
            not drawn, nor anything inside it; an SVG that would draw nothing at
            all is refused at submit. Set explicit colours and presentation
            attributes, or pre-rasterize to PNG.
          example: https://cdn.example.com/logo.png
        effects:
          items:
            $ref: '#/components/schemas/Effect'
          type: array
          title: Effects
          default: []
        motion:
          items:
            $ref: '#/components/schemas/Motion'
          type: array
          title: Motion
          default: []
      additionalProperties: false
      type: object
      required:
        - id
        - duration
        - source_url
      title: ImageElement
    ShapeElement:
      properties:
        blend_mode:
          $ref: '#/components/schemas/BlendMode'
          default: none
        mask_shape:
          anyOf:
            - $ref: '#/components/schemas/MaskShape'
            - type: 'null'
        mask:
          anyOf:
            - $ref: '#/components/schemas/MaskConfig'
            - type: 'null'
        mask_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/MaskKeyframe'
              type: array
            - type: 'null'
          title: Mask Keyframes
        matte:
          anyOf:
            - $ref: '#/components/schemas/Matte'
            - type: 'null'
        backdrop_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Backdrop Blur
          description: >-
            Glass: the scene behind the element, Gaussian-blurred, fills its
            footprint under its translucent content (a shape's own silhouette, a
            video's or image's box with its corner radius and mask). A Gaussian
            sigma in canvas px, as CSS `backdrop-filter: blur()` takes it; the
            renderer caps it at 128. 0 is off.
          default: 0
        backdrop_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Backdrop Blur Keyframes
          description: >-
            Animate `backdrop_blur` over the timeline; replaces the static value
            while present.
        layer_blur:
          type: number
          maximum: 128
          minimum: 0
          title: Layer Blur
          description: >-
            Blurs the element itself as drawn: a Gaussian sigma in canvas px, as
            CSS `filter: blur()` takes it, spreading three sigma past its box.
            At most 128; 0 is off. Not on a shape yet (the editor's preview
            cannot draw it), and not with `blend_mode`, `backdrop_blur` or
            `matte`, beside which the renderer would draw the element without
            it.
          default: 0
        layer_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Layer Blur Keyframes
          description: >-
            Animate `layer_blur` over the timeline; replaces the static value
            while present.
        glass:
          anyOf:
            - $ref: '#/components/schemas/Glass'
            - type: 'null'
          description: >-
            A refracting glass edge: the scene behind bends inward near the
            element's outline. Pair it with `backdrop_blur` for frosted glass.
        x:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X
          default: 50%
        'y':
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: 'Y'
          default: 50%
        width:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Width
          description: >-
            Box width. Together with `height` this is a **cover** box, not a fit
            box: sized media (video, image) scales to fill it and the overflow
            is **cropped** — it is never letterboxed. A box whose aspect ratio
            differs from the source's therefore silently cuts the source's
            edges. To show a source whole, match the box aspect to it: on a
            `W`x`H` canvas, for a source of aspect `a`, `width% = a * height% *
            (H/W)` — e.g. a 1024x1536 asset (a=0.667) on 1920x1080 needs `width%
            = 0.375 * height%`. Percentages are of the canvas, not the parent.
          default: 100%
          example: 40%
        height:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Height
          description: >-
            Box height. See `width` — the pair is a cover box and crops rather
            than letterboxes. Percentages are of the canvas.
          default: 100%
          example: 80%
        aspect_ratio:
          anyOf:
            - type: number
            - type: 'null'
          title: Aspect Ratio
          description: >-
            **Accepted but ignored — the renderer never receives this field.**
            It does NOT constrain or correct the `width`/`height` box, so it
            cannot be used to stop a mismatched box from cropping the source;
            size the box per `width` instead. Kept for backward compatibility.
        x_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Anchor
          description: >-
            Horizontal pivot the element **rotates** about, measured across its
            own box (`0%` = left edge, `50%` = centre, `100%` = right edge; px
            values are an offset from the left edge). This is a pivot, not an
            alignment — `x` always places the element's centre, so the anchor
            changes nothing on an unrotated element. Honored on video, image and
            shape; text and the generator elements rotate about their centre and
            reject a non-default anchor.
          default: 50%
          example: 0%
        y_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Anchor
          description: >-
            Vertical pivot the element **rotates** about, measured across its
            own box (`0%` = top edge, `50%` = centre, `100%` = bottom edge).
            Same rules as `x_anchor`: a pivot, not an alignment.
          default: 50%
          example: 100%
        x_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Rotation
          default: 0°
        y_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Rotation
          default: 0°
        z_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Z Rotation
          default: 0°
        x_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Scale
          default: 100%
        y_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Scale
          default: 100%
        flip_horizontal:
          type: boolean
          title: Flip Horizontal
          default: false
        flip_vertical:
          type: boolean
          title: Flip Vertical
          default: false
        opacity:
          type: number
          maximum: 100
          minimum: 0
          title: Opacity
          default: 100
        z_index:
          anyOf:
            - type: integer
            - type: 'null'
          title: Z Index
          description: >-
            Stacking order (higher = on top). When omitted, elements stack in
            array order — later elements render above earlier ones (painter's
            rule); text defaults one layer above non-text. Set explicit values
            only when array order isn't the order you want.
        clip:
          type: boolean
          title: Clip
          default: false
        color_overlay:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Color Overlay
          description: >-
            **Not supported by the renderer — setting this is rejected.** A
            strict submit returns `unsupported_feature` (422) and a dry-run
            reports it as a blocking violation; there is no native tint field to
            map it onto. To darken an element use `brightness` (`-100` is a
            black silhouette); to lay a colour over one, stack a `shape` with a
            flat or gradient `fill` above it.
        type:
          type: string
          const: shape
          title: Type
          default: shape
        id:
          type: string
          minLength: 1
          title: Id
          description: '**Required.** Unique identifier.'
          example: blob-1
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional human-readable label. Not used by the renderer.
        track:
          anyOf:
            - type: integer
              maximum: 255
              minimum: 0
            - type: 'null'
          title: Track
          description: >-
            Timeline track index (0–255). Informational only; not used by the
            renderer.
        time:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Time
          description: >-
            When this element starts on the output timeline, in seconds.
            Defaults to `0` when omitted.
          example: 0
        visible:
          type: boolean
          title: Visible
          description: >-
            Set to `false` to skip this element without removing it from the
            request.
          default: true
        locked:
          type: boolean
          title: Locked
          description: Marks the element locked in editors. Not sent to the renderer.
          default: false
        duration:
          type: number
          exclusiveMinimum: 0
          title: Duration
          description: '**Required.** Seconds the shape is visible.'
          example: 4
        path:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Path
          description: >-
            SVG path `d` (M L H V C S Q T A Z; elliptical arcs render as arcs).
            **Required** unless `geometry` is set.
          example: M50 0 L100 100 L0 100 Z
        geometry:
          anyOf:
            - $ref: '#/components/schemas/ShapeGeometry'
            - type: 'null'
          description: >-
            Draw a closed-form shape instead of a `path`: exactly one of `rect`
            (`corner_radius` per corner, `smoothing`), `ellipse`, `line`, `ring`
            or `arc` (`inner_ratio`, `start_angle`, `sweep`, `cap`, `segments`),
            `polygon` (`sides`, `corner_radius`) or `star` (`points`,
            `inner_ratio`, `corner_radius`), plus `stroke_align` and `shadows`.
            Lengths are px, angles degrees clockwise from 3 o'clock. The box,
            `fill`, `stroke`, `stroke_width`, `stroke_dash`, motion, masks,
            mattes, blend modes and effects work as on any shape; `path`,
            `path_keyframes`, `trim`, `corner_radius` and `shadow` do not apply.
        view_box:
          items:
            type: number
          type: array
          maxItems: 4
          minItems: 4
          title: View Box
          description: '`[x, y, width, height]` path coordinate space.'
          default:
            - 0
            - 0
            - 100
            - 100
        fill:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: string
              const: none
            - type: string
              pattern: ^\s*(?:linear|radial)-gradient\s*\(
              description: >-
                CSS gradient shorthand — `linear-gradient(...)` or
                `radial-gradient(...)`. Normalised to the Gradient object during
                validation; the renderer never sees a CSS string, and reading
                the composition back returns the object.
              example: 'linear-gradient(90deg, #ff0000 0%, #0000ff 100%)'
            - $ref: '#/components/schemas/Gradient'
          title: Fill
          description: >-
            Fill: a hex colour (alpha ok), `"none"` for outline-only, a Gradient
            object (`{kind, angle_degrees, center, stops}`, ≥2 stops), or a CSS
            `linear-gradient(...)` / `radial-gradient(...)` string, which is
            normalised to the Gradient object at validation — read back, the
            composition holds the object. A gradient fill on a rectangular path
            is how you draw a gradient card, bar or pill — there is no separate
            `gradient` element. The gradient paints the shape's own path, so it
            fills any silhouette — a triangle, a blob, an icon outline, a
            morphing path — and layers on the same element with `stroke` /
            `stroke_width` / `stroke_dash`, `path_keyframes` (morph), `trim` /
            `trim_keyframes` (draw-on), `blend_mode`, `mask_shape` / `mask` /
            `mask_keyframes`, `matte` and `backdrop_blur`. `fill_rule`,
            `corner_radius` and `opacity` behave with a gradient exactly as they
            do with a flat colour.
          default: '#000000'
          example: 'linear-gradient(90deg, #e07a4f 0%, #7c3aed 100%)'
        fill_rule:
          type: string
          enum:
            - nonzero
            - evenodd
          title: Fill Rule
          default: nonzero
        stroke:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Stroke
          description: Stroke colour.
        stroke_width:
          anyOf:
            - type: number
              maximum: 100000
              minimum: 0
            - type: 'null'
          title: Stroke Width
          description: >-
            Stroke width in viewBox units, at most 100000 (absent = SVG 1.0; 0
            disables).
        stroke_linecap:
          type: string
          enum:
            - butt
            - round
            - square
          title: Stroke Linecap
          description: >-
            Cap on the open ends of a stroked path (SVG `stroke-linecap`):
            `butt` stops at the endpoint, `round` and `square` extend it by half
            the stroke width. Closed paths have joins, not caps.
          default: round
        stroke_linejoin:
          type: string
          enum:
            - miter
            - round
            - bevel
          title: Stroke Linejoin
          description: >-
            Corner join of a stroked path (SVG `stroke-linejoin`): `miter` is
            the sharp corner (subject to `stroke_miter_limit`), `bevel` cuts it
            flat.
          default: round
        stroke_miter_limit:
          type: number
          minimum: 1
          title: Stroke Miter Limit
          description: >-
            SVG `stroke-miterlimit`: miter length over stroke width. A `miter`
            join sharper than this falls back to `bevel`.
          default: 4
        stroke_dash:
          anyOf:
            - items:
                type: number
              type: array
              minItems: 1
            - type: 'null'
          title: Stroke Dash
          description: >-
            Dash pattern in viewBox units, SVG `stroke-dasharray` semantics:
            `[on, off, ...]` of any length — an odd-length array repeats so
            on/off alternate (`[4]` is 4 on, 4 off). Trim applies before dash,
            so a dashed path can draw itself on without the pattern re-flowing.
        stroke_dash_offset:
          type: number
          title: Stroke Dash Offset
          description: >-
            Where the dash pattern starts along the path, in viewBox units (SVG
            `stroke-dashoffset`): positive shifts the pattern forward, negative
            backward.
          default: 0
        stroke_dash_offset_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/DashOffsetKeyframe'
              type: array
            - type: 'null'
          title: Stroke Dash Offset Keyframes
          description: >-
            Animate the dash offset (timeline seconds) — marching ants: keyframe
            `offset` linearly by one dash period (the pattern's sum) per second.
            Needs a `stroke_dash`; while present it drives the offset in place
            of the static `stroke_dash_offset`.
        stroke_marker_start:
          type: string
          enum:
            - none
            - triangle
            - open_chevron
            - dot
          title: Stroke Marker Start
          description: >-
            A head at the start of each open subpath, in the stroke's colour
            (render-node V2.6): `triangle` is filled, its tip on the endpoint
            (the shaft shortens to meet it); `open_chevron` is an open head
            stroked like the path; `dot` is a disc. Placed after the trim, so a
            draw-on keeps its head at the visible end, and left out of the dash.
            A closed subpath takes none. Needs a `stroke`; path shapes only.
          default: none
        stroke_marker_end:
          type: string
          enum:
            - none
            - triangle
            - open_chevron
            - dot
          title: Stroke Marker End
          description: A head at the end of each open subpath; see `stroke_marker_start`.
          default: none
        stroke_marker_size:
          type: number
          maximum: 32
          minimum: 1
          title: Stroke Marker Size
          description: >-
            Marker length in stroke widths, 1–32 (4 is four times
            `stroke_width`).
          default: 4
        corner_radius:
          anyOf:
            - type: number
              maximum: 1
              minimum: 0
            - $ref: '#/components/schemas/CornerRadii'
          title: Corner Radius
          description: >-
            Corner radius, normalized 0–1 — a single value or per-corner
            `{top_left, top_right, bottom_right, bottom_left}`.
          default: 0
        shadow:
          anyOf:
            - $ref: '#/components/schemas/DropShadow'
            - type: 'null'
          description: >-
            Optional drop shadow. `distance`/`blur` are the renderer's 0–100
            strength scale, not pixels — see DropShadow.
        path_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/PathKeyframe'
              type: array
            - type: 'null'
          title: Path Keyframes
          description: Morph the path between keyframes (timeline seconds).
        trim:
          anyOf:
            - $ref: '#/components/schemas/TrimPath'
            - type: 'null'
          description: >-
            Static trim window: keep only this arc-length fraction of each
            subpath.
        trim_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/TrimKeyframe'
              type: array
            - type: 'null'
          title: Trim Keyframes
          description: >-
            Animate the trim window (timeline seconds) — the draw-on: animate
            `end` 0→1 on a stroked path to draw a signature, underline or chart
            line. Absent `start`/`end` inherit the static `trim`.
        effects:
          items:
            $ref: '#/components/schemas/Effect'
          type: array
          title: Effects
          description: Shader effect chain (§6 W3).
          default: []
        motion:
          items:
            $ref: '#/components/schemas/Motion'
          type: array
          title: Motion
          default: []
      additionalProperties: false
      type: object
      required:
        - id
        - duration
      title: ShapeElement
      description: 'A first-class vector primitive: SVG path fill + stroke, morphing (§5b).'
    FrameElement:
      properties:
        x:
          anyOf:
            - type: number
            - type: 'null'
          title: X
          description: >-
            Left edge in design px from its parent's left edge (at the root, the
            `design_size` box's, else the output's). Omitted: 0.
        'y':
          anyOf:
            - type: number
            - type: 'null'
          title: 'Y'
          description: 'Top edge in design px from its parent''s top edge. Omitted: 0.'
        x_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - type: 'null'
          title: X Anchor
          description: >-
            Pivot it turns and scales about, across its own box: a percent, or
            design px from its left edge. Omitted: the centre.
        y_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - type: 'null'
          title: Y Anchor
          description: >-
            Pivot across its own box's height: a percent, or design px from its
            top edge. Omitted: the centre.
        z_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - type: 'null'
          title: Z Rotation
          description: 'Rotation in degrees. Omitted: 0.'
        x_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - type: 'null'
          title: X Rotation
          description: >-
            Perspective tilt (X axis) each element inside takes about its own
            centre, as a group's does. Omitted: 0.
        y_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - type: 'null'
          title: Y Rotation
          description: 'Perspective tilt (Y axis); see `x_rotation`. Omitted: 0.'
        scale:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Scale
          description: 'Uniform scale about the anchor. Omitted: 1.'
        opacity:
          anyOf:
            - type: number
              maximum: 100
              minimum: 0
            - type: 'null'
          title: Opacity
          description: 'Opacity 0 to 100 of everything inside. Omitted: 100.'
        z_index:
          anyOf:
            - type: integer
            - type: 'null'
          title: Z Index
          description: >-
            Stacking order among its siblings (higher on top); ties draw in
            array order. Everything inside draws together at its place.
        duration:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Duration
          description: 'Seconds active. Omitted: until its parent ends.'
        motion:
          items:
            $ref: '#/components/schemas/Motion'
          type: array
          title: Motion
          description: Animations of the whole, about its anchor.
          default: []
        clip:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Clip
          description: Crop what is inside to its box.
        corner_radius:
          anyOf:
            - type: number
              minimum: 0
            - items:
                type: number
                minimum: 0
              type: array
              maxItems: 4
              minItems: 4
            - $ref: '#/components/schemas/GeometryCornerRadii'
            - type: 'null'
          title: Corner Radius
          description: >-
            Corner radius of the clip in design px: a number, `[top_left,
            top_right, bottom_right, bottom_left]` or `{top_left, ...}`. Applies
            with `clip`.
        blend_mode:
          anyOf:
            - $ref: '#/components/schemas/BlendMode'
            - type: 'null'
          description: 'Blend of its content over what is below. Omitted: normal.'
        mask_shape:
          anyOf:
            - $ref: '#/components/schemas/MaskShape'
            - type: 'null'
          description: Analytic mask over its box.
        mask:
          anyOf:
            - $ref: '#/components/schemas/MaskConfig'
            - type: 'null'
          description: Feather, scale and centre of `mask_shape`.
        matte:
          anyOf:
            - $ref: '#/components/schemas/Matte'
            - type: 'null'
          description: 'Track matte: another element''s render drives its coverage.'
        effects:
          items:
            $ref: '#/components/schemas/Effect'
          type: array
          title: Effects
          description: Shader effects run over its content.
          default: []
        render:
          anyOf:
            - type: string
              enum:
                - flatten
                - composite
            - type: 'null'
          title: Render
          description: >-
            `composite` makes it a nested composition whose content runs on
            `time_map`'s clock; its content is graphics only (no video).
            Omitted: `flatten`.
        time_map:
          anyOf:
            - $ref: '#/components/schemas/TimeMap'
            - type: 'null'
          description: >-
            A composite's clock: `offset`, `speed`, `loop` and `freeze`. Needs
            `render: "composite"`.
        type:
          type: string
          const: frame
          title: Type
          default: frame
        id:
          type: string
          minLength: 1
          title: Id
          description: >-
            **Required.** Unique in the request; operations, mattes and anchors
            name it.
          example: card
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional human-readable label. Not used by the renderer.
        track:
          anyOf:
            - type: integer
              maximum: 255
              minimum: 0
            - type: 'null'
          title: Track
          description: >-
            Timeline track index (0–255). Informational only; not used by the
            renderer.
        time:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Time
          description: >-
            Start in seconds on its parent's clock (the composition's at the
            root). Omitted: its parent's start.
        visible:
          type: boolean
          title: Visible
          description: >-
            Set to `false` to skip this element without removing it from the
            request.
          default: true
        locked:
          type: boolean
          title: Locked
          description: Marks the element locked in editors. Not sent to the renderer.
          default: false
        width:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Width
          description: 'Width in design px. Omitted: its parent''s width.'
        height:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Height
          description: 'Height in design px. Omitted: its parent''s height.'
        slot:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Slot
          description: >-
            In a component body: the slot name an instance's `slots` fill in
            place of this frame's own children.
        children:
          items:
            oneOf:
              - $ref: '#/components/schemas/ChildVideoElement'
              - $ref: '#/components/schemas/ChildTextElement'
              - $ref: '#/components/schemas/ChildImageElement'
              - $ref: '#/components/schemas/ChildShapeElement'
              - $ref: '#/components/schemas/FrameElement'
              - $ref: '#/components/schemas/InstanceElement'
            discriminator:
              propertyName: type
              mapping:
                frame: '#/components/schemas/FrameElement'
                image: '#/components/schemas/ChildImageElement'
                instance: '#/components/schemas/InstanceElement'
                shape: '#/components/schemas/ChildShapeElement'
                text: '#/components/schemas/ChildTextElement'
                video: '#/components/schemas/ChildVideoElement'
          type: array
          title: Children
          description: >-
            The elements inside, in design px from this frame's top-left corner;
            frames and instances nest. Audio stays at the root.
          default: []
      additionalProperties: false
      type: object
      required:
        - id
      title: FrameElement
      description: >-
        A frame (render-node AEC §5): elements nested in its box, with its own
        clip, mask,

        matte, blend, effects and, as a composite, its own clock. Its children
        use design px

        from its top-left corner, frames and instances included.
    InstanceElement:
      properties:
        x:
          anyOf:
            - type: number
            - type: 'null'
          title: X
          description: >-
            Left edge in design px from its parent's left edge (at the root, the
            `design_size` box's, else the output's). Omitted: 0.
        'y':
          anyOf:
            - type: number
            - type: 'null'
          title: 'Y'
          description: 'Top edge in design px from its parent''s top edge. Omitted: 0.'
        x_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - type: 'null'
          title: X Anchor
          description: >-
            Pivot it turns and scales about, across its own box: a percent, or
            design px from its left edge. Omitted: the centre.
        y_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - type: 'null'
          title: Y Anchor
          description: >-
            Pivot across its own box's height: a percent, or design px from its
            top edge. Omitted: the centre.
        z_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - type: 'null'
          title: Z Rotation
          description: 'Rotation in degrees. Omitted: 0.'
        x_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - type: 'null'
          title: X Rotation
          description: >-
            Perspective tilt (X axis) each element inside takes about its own
            centre, as a group's does. Omitted: 0.
        y_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - type: 'null'
          title: Y Rotation
          description: 'Perspective tilt (Y axis); see `x_rotation`. Omitted: 0.'
        scale:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Scale
          description: 'Uniform scale about the anchor. Omitted: 1.'
        opacity:
          anyOf:
            - type: number
              maximum: 100
              minimum: 0
            - type: 'null'
          title: Opacity
          description: 'Opacity 0 to 100 of everything inside. Omitted: 100.'
        z_index:
          anyOf:
            - type: integer
            - type: 'null'
          title: Z Index
          description: >-
            Stacking order among its siblings (higher on top); ties draw in
            array order. Everything inside draws together at its place.
        duration:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Duration
          description: 'Seconds active. Omitted: until its parent ends.'
        motion:
          items:
            $ref: '#/components/schemas/Motion'
          type: array
          title: Motion
          description: Animations of the whole, about its anchor.
          default: []
        clip:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Clip
          description: Crop what is inside to its box.
        corner_radius:
          anyOf:
            - type: number
              minimum: 0
            - items:
                type: number
                minimum: 0
              type: array
              maxItems: 4
              minItems: 4
            - $ref: '#/components/schemas/GeometryCornerRadii'
            - type: 'null'
          title: Corner Radius
          description: >-
            Corner radius of the clip in design px: a number, `[top_left,
            top_right, bottom_right, bottom_left]` or `{top_left, ...}`. Applies
            with `clip`.
        blend_mode:
          anyOf:
            - $ref: '#/components/schemas/BlendMode'
            - type: 'null'
          description: 'Blend of its content over what is below. Omitted: normal.'
        mask_shape:
          anyOf:
            - $ref: '#/components/schemas/MaskShape'
            - type: 'null'
          description: Analytic mask over its box.
        mask:
          anyOf:
            - $ref: '#/components/schemas/MaskConfig'
            - type: 'null'
          description: Feather, scale and centre of `mask_shape`.
        matte:
          anyOf:
            - $ref: '#/components/schemas/Matte'
            - type: 'null'
          description: 'Track matte: another element''s render drives its coverage.'
        effects:
          items:
            $ref: '#/components/schemas/Effect'
          type: array
          title: Effects
          description: Shader effects run over its content.
          default: []
        render:
          anyOf:
            - type: string
              enum:
                - flatten
                - composite
            - type: 'null'
          title: Render
          description: >-
            `composite` makes it a nested composition whose content runs on
            `time_map`'s clock; its content is graphics only (no video).
            Omitted: `flatten`.
        time_map:
          anyOf:
            - $ref: '#/components/schemas/TimeMap'
            - type: 'null'
          description: >-
            A composite's clock: `offset`, `speed`, `loop` and `freeze`. Needs
            `render: "composite"`.
        type:
          type: string
          const: instance
          title: Type
          default: instance
        id:
          type: string
          minLength: 1
          title: Id
          description: '**Required.** Its content''s ids become `<id>/<child id>`.'
          example: card1
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional human-readable label. Not used by the renderer.
        track:
          anyOf:
            - type: integer
              maximum: 255
              minimum: 0
            - type: 'null'
          title: Track
          description: >-
            Timeline track index (0–255). Informational only; not used by the
            renderer.
        time:
          type: number
          minimum: 0
          title: Time
          description: >-
            **Required.** Start in seconds on its parent's clock; every time
            inside the component counts from it.
        visible:
          type: boolean
          title: Visible
          description: >-
            Set to `false` to skip this element without removing it from the
            request.
          default: true
        locked:
          type: boolean
          title: Locked
          description: Marks the element locked in editors. Not sent to the renderer.
          default: false
        component:
          type: string
          minLength: 1
          title: Component
          description: The `components` entry it places.
        variant:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Variant
          description: 'The variant by name. Omitted: the first.'
        properties:
          additionalProperties:
            anyOf:
              - type: boolean
              - type: integer
              - type: number
              - type: string
          type: object
          title: Properties
          description: >-
            Values of the component's declared properties, by name, each of its
            type.
          default: {}
        slots:
          additionalProperties:
            items:
              oneOf:
                - $ref: '#/components/schemas/ChildVideoElement'
                - $ref: '#/components/schemas/ChildTextElement'
                - $ref: '#/components/schemas/ChildImageElement'
                - $ref: '#/components/schemas/ChildShapeElement'
                - $ref: '#/components/schemas/FrameElement'
                - $ref: '#/components/schemas/InstanceElement'
              discriminator:
                propertyName: type
                mapping:
                  frame: '#/components/schemas/FrameElement'
                  image: '#/components/schemas/ChildImageElement'
                  instance: '#/components/schemas/InstanceElement'
                  shape: '#/components/schemas/ChildShapeElement'
                  text: '#/components/schemas/ChildTextElement'
                  video: '#/components/schemas/ChildVideoElement'
            type: array
          type: object
          title: Slots
          description: >-
            Content for the component's slots, by slot name, in place of the
            slot frame's own children.
          default: {}
      additionalProperties: false
      type: object
      required:
        - id
        - time
        - component
      title: InstanceElement
      description: >-
        An instance (render-node AEC §5g): a placed copy of a `components`
        definition. Its box

        is the component's (or its variant's); it sets the component's
        properties, fills its

        slots, and places, times and composites itself as a frame does.
    Transition:
      properties:
        type:
          $ref: '#/components/schemas/TransitionType'
          description: >-
            Transition effect applied between two video elements (transitions
            are video-only).
          example: cross_dissolve
        duration:
          type: number
          exclusiveMinimum: 0
          title: Duration
          description: Transition duration in seconds.
          example: 0.5
        from_id:
          anyOf:
            - type: string
            - type: 'null'
          title: From Id
          description: >-
            ID of the outgoing video element (omit for the very first
            transition).
          example: video_01
        to_id:
          anyOf:
            - type: string
            - type: 'null'
          title: To Id
          description: >-
            ID of the incoming video element (omit for the very last
            transition).
          example: video_02
        z_index:
          anyOf:
            - type: integer
            - type: 'null'
          title: Z Index
          description: >-
            **Deprecated — ignored by the renderer.** The transition draws at
            its linked videos' position; the engine reads no z key on a
            transition. Accepted for backward compatibility only.
          example: 1
      additionalProperties: false
      type: object
      required:
        - type
        - duration
      title: Transition
      description: |-
        A transition between two **video** elements.

        The engine links transitions to video clips only — referencing an image,
        text or other element type is rejected (it would crash the render node).
    Group:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: '**Required.** Unique group id.'
          example: hero
        parent:
          anyOf:
            - type: string
            - type: 'null'
          title: Parent
          description: Parent group id for nesting.
        pivot:
          $ref: '#/components/schemas/Point'
          description: Rotation/scale pivot.
        translate:
          anyOf:
            - $ref: '#/components/schemas/Point'
            - type: 'null'
          description: Group translation (normalized).
        scale:
          type: number
          exclusiveMinimum: 0
          title: Scale
          description: Group scale.
          default: 1
        rotation_degrees:
          type: number
          title: Rotation Degrees
          description: In-plane (z) rotation of the group.
          default: 0
        x_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Rotation
          description: >-
            Perspective tilt (X axis) applied to each member's orientation about
            its own centre — the tilted card-gallery look.
          default: 0°
        y_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Rotation
          description: Perspective tilt (Y axis). See `x_rotation`.
          default: 0°
        opacity:
          type: number
          maximum: 1
          minimum: 0
          title: Opacity
          description: Group opacity.
          default: 1
        members:
          items:
            type: string
          type: array
          title: Members
          description: Member element uuids.
        animations:
          items:
            $ref: '#/components/schemas/Motion'
          type: array
          title: Animations
          description: >-
            Animations driving the whole group about `pivot`. Translate, scale,
            rotation and colour all compose — that is how you move or resize a
            whole scene as one unit. Two channels do NOT: a preset that animates
            **clip** (`wipe_left/right/up/down`) or **crop** (`ken_burns_*`) is
            a silent no-op on a group, doing nothing at all rather than erroring
            — put those on a member element instead. Non-uniform scale is also
            flattened here: only the x component of a `custom_animations`
            `scale` keyframe is used, so `{x:2,y:1}` scales uniformly. Note
            `zoom_out` barely moves (scale 1.0->0.9 under a full fade), so it
            reads as a pure fade; for a real shrink use a `custom` entry with
            the scale travel you want, or the group's static `scale`.
        layout:
          anyOf:
            - $ref: '#/components/schemas/StackLayout'
            - type: 'null'
          description: Optional stack layout.
      additionalProperties: false
      type: object
      required:
        - id
      title: Group
      description: >-
        A transform group over member elements (§5, A6). Composes member → group
        → parent.


        The alias of a frame (render-node AEC §5, TR1): the engine lowers each
        group to a frame

        whose box is the canvas, anchored at `pivot`, offset by `translate`,
        with `members` as

        its children, so nothing it draws changes. Kept for existing documents;
        nest elements

        in a `frame` element instead. A request with `frame` or `instance`
        elements carries no

        groups.
    DesignSize:
      properties:
        width:
          type: number
          exclusiveMinimum: 0
          title: Width
          description: Width in design px.
          example: 1920
        height:
          type: number
          exclusiveMinimum: 0
          title: Height
          description: Height in design px.
          example: 1080
      additionalProperties: false
      type: object
      required:
        - width
        - height
      title: DesignSize
      description: >-
        The logical root (render-node AEC §6): the box frames and instances at
        the root are

        placed in, in design px, scaled uniformly to the output, whose aspect it
        must have.
    OperationsBlock:
      properties:
        markers:
          items:
            $ref: '#/components/schemas/OperationMarker'
          type: array
          title: Markers
          description: Named times `marker` anchors read.
          default: []
        groups:
          items:
            $ref: '#/components/schemas/OperationGroup'
          type: array
          maxItems: 1000
          title: Groups
          description: At most 1000 groups and 10000 operations.
          default: []
      additionalProperties: false
      type: object
      title: OperationsBlock
      description: >-
        The `operations` block (render-node AEC §3; GVR §4.3): element
        properties driven over

        time and evaluated by the engine every frame. Each group counts from its
        anchor.
    Component:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: What an instance's `component` names.
          example: card
        width:
          type: number
          exclusiveMinimum: 0
          title: Width
          description: Box width in design px.
          example: 552
        height:
          type: number
          exclusiveMinimum: 0
          title: Height
          description: Box height in design px.
          example: 400
        clip:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Clip
          description: Its instances clip to the box unless they say otherwise.
        corner_radius:
          anyOf:
            - type: number
              minimum: 0
            - items:
                type: number
                minimum: 0
              type: array
              maxItems: 4
              minItems: 4
            - $ref: '#/components/schemas/GeometryCornerRadii'
            - type: 'null'
          title: Corner Radius
          description: Its instances' clip corner radius in design px.
        properties:
          items:
            $ref: '#/components/schemas/ComponentProperty'
          type: array
          title: Properties
          description: What an instance can set.
          default: []
        children:
          anyOf:
            - items:
                oneOf:
                  - $ref: '#/components/schemas/ChildVideoElement'
                  - $ref: '#/components/schemas/ChildTextElement'
                  - $ref: '#/components/schemas/ChildImageElement'
                  - $ref: '#/components/schemas/ChildShapeElement'
                  - $ref: '#/components/schemas/FrameElement'
                  - $ref: '#/components/schemas/InstanceElement'
                discriminator:
                  propertyName: type
                  mapping:
                    frame: '#/components/schemas/FrameElement'
                    image: '#/components/schemas/ChildImageElement'
                    instance: '#/components/schemas/InstanceElement'
                    shape: '#/components/schemas/ChildShapeElement'
                    text: '#/components/schemas/ChildTextElement'
                    video: '#/components/schemas/ChildVideoElement'
              type: array
            - type: 'null'
          title: Children
          description: >-
            The body of a single-variant component, in design px from the box's
            top-left corner. Give this or `variants`.
        operations:
          anyOf:
            - $ref: '#/components/schemas/OperationsBlock'
            - type: 'null'
          description: >-
            The body's operations (beside `children`); groups count from
            `instance.start` unless they say otherwise.
        variants:
          anyOf:
            - items:
                $ref: '#/components/schemas/ComponentVariant'
              type: array
              minItems: 1
            - type: 'null'
          title: Variants
          description: Named bodies, the first the default. Give this or `children`.
      additionalProperties: false
      type: object
      required:
        - id
        - width
        - height
      title: Component
      description: >-
        A component definition (render-node AEC §5g): a precomp kept in the
        request, with a

        fixed box, typed properties bound to its body, variants and slots.
        `instance` elements

        place it.
    MotionBlur:
      properties:
        samples:
          type: integer
          maximum: 32
          minimum: 2
          title: Samples
          description: Temporal samples per frame (2–32).
        shutter:
          type: number
          maximum: 1
          exclusiveMinimum: 0
          title: Shutter
          description: >-
            Shutter fraction of the frame interval, (0–1]. (Exactly 0 is
            rejected: it renders N identical samples — full cost, zero blur.)
          default: 0.5
      additionalProperties: false
      type: object
      required:
        - samples
      title: MotionBlur
      description: >-
        Shutter-based temporal supersampling of the whole composition (§5c).


        Opt-in and costly: every frame renders `samples` times, and accumulation
        is

        CPU-side, so the render falls back to **software encoding** — expect
        roughly

        `samples`× render time.
    Gradient:
      properties:
        kind:
          type: string
          enum:
            - linear
            - radial
            - conic
          title: Kind
          description: >-
            `conic` sweeps the stops clockwise once round `center`, starting at
            `angle_degrees`.
          default: linear
        angle_degrees:
          type: number
          title: Angle Degrees
          description: >-
            Linear gradient angle in degrees, measured clockwise on the screen
            (y down): 0 = left→right (offset 0 at the left), 90 = top→bottom, 45
            = top-left→bottom-right, 315 = bottom-left→top-right, and the same
            convention governs a shape's gradient `fill`. A numeric CSS angle is
            box-relative: `135deg` behaves as `to bottom right` whatever the
            card's shape; only 0/90/180/270 are pixel-exact. On a conic
            gradient: where the sweep starts, degrees clockwise from 3 o'clock.
          default: 0
        center:
          anyOf:
            - $ref: '#/components/schemas/Point'
            - type: 'null'
          description: >-
            Radial focus point as fractions of the fill's box — the element box
            for a shape's gradient fill, the canvas for the background. `y` runs
            down the screen, as CSS `at` does: `{x: 0.5, y: 0}` is the top edge
            and `{x: 0.5, y: 1}` the bottom (CSS `at 50% 100%`). Defaults to the
            centre; ignored for linear gradients. The renderer's radial is an
            ellipse inscribed in the box, reaching the last stop at the corners:
            it keeps that centred radius when the focus moves, rather than
            re-deriving it from the focus the way CSS `farthest-corner` does, so
            an off-centre focus imported from CSS completes its ramp earlier
            than a browser shows it. A CSS `circle` is drawn as that same
            ellipse, so it only comes out round on a square box. A conic
            gradient sweeps round this point.
        stops:
          items:
            $ref: '#/components/schemas/GradientStop'
          type: array
          minItems: 2
          title: Stops
          description: Two or more colour stops.
      additionalProperties: false
      type: object
      required:
        - stops
      title: Gradient
    CustomAnimationRegistry:
      properties:
        element:
          items:
            $ref: '#/components/schemas/CustomAnimation'
          type: array
          title: Element
          default: []
        text:
          items:
            $ref: '#/components/schemas/CustomAnimation'
          type: array
          title: Text
          default: []
      additionalProperties: false
      type: object
      title: CustomAnimationRegistry
      description: >-
        Inline custom-animation registries for element and text animations (§3).


        `element` entries are bindable from any video/image/shape/group

        animation via `motion[].custom` (or `animations[].custom` on groups).

        `text` entries bind from a text element's `animation_preset` or

        `motion[].custom`.


        Names that shadow a catalog preset — or the reserved static keys `none`/

        `overlay`/`difference` — are rejected here: the engine
        warn-and-*ignores*

        such a definition (renderer_node.cpp:1500-1516) and the reference then

        silently resolves to the catalog preset instead of yours.
    ApiErrorCode:
      description: >-
        Machine-readable error code returned on a 4xx/5xx response. Branch on
        `error.code` to

        handle failures programmatically.


        Values:

        - `invalid_request` — The request was malformed or failed validation.

        - `unauthorized` — Missing or invalid API key.

        - `forbidden` — Authenticated but not permitted to perform this action.

        - `not_found` — The requested resource does not exist.

        - `conflict` — Conflicts with current state (e.g. a reused
        Idempotency-Key with a different body).

        - `unsupported_feature` — The composition uses a feature the renderer
        does not support.

        - `invalid_source` — A source URL is malformed or unreachable.

        - `source_not_found` — A referenced source file could not be found.

        - `source_too_large` — A source file exceeds the allowed size.

        - `asset_not_ready` — A referenced asset is still ingesting and is not
        ready yet.

        - `ingest_failed` — Copying an external source into Framelane storage
        failed.

        - `codec_unsupported` — A source uses a codec the renderer cannot
        decode.

        - `quota_exceeded` — The workspace has exhausted its plan quota (HTTP
        402).

        - `rate_limited` — Too many requests; retry after the Retry-After
        interval.

        - `internal` — An unexpected server error occurred.

        - `email_not_verified` — The workspace email is unverified; verify it
        before making authed calls.

        - `email_in_use` — The email is already associated with another
        workspace.

        - `invalid_otp` — The verification code is incorrect or expired.

        - `unsupported_content_type` — The provided content type is not
        supported.

        - `plan_required` — The feature needs a higher plan (HTTP 402);
        `details.required_plan` names it.

        - `admin_only` — Only an admin workspace may call this route (HTTP 403).

        - `template_assets_not_shared` — A published template may reference only
        shared CDN media; `details.urls` lists the rest.

        - `template_has_webhook` — A published template may not carry a
        `webhook_url`: every copy of it would notify that URL.

        - `http_error` — A generic HTTP error not covered by a more specific
        code.

        - `shader_compile_error` — A shader's SkSL did not compile;
        `details.errors` gives each error's line and column.

        - `shader_compiler_unavailable` — The shader compiler is not installed
        on this deployment or did not answer in time (HTTP 503).

        - `shader_not_found` — An effect names a shader id that is neither this
        workspace's nor shared.

        - `shader_param_invalid` — An effect sets a shader param the shader does
        not declare, or a value of the wrong type or range.
      enum:
        - invalid_request
        - unauthorized
        - forbidden
        - not_found
        - conflict
        - unsupported_feature
        - invalid_source
        - source_not_found
        - source_too_large
        - asset_not_ready
        - ingest_failed
        - codec_unsupported
        - quota_exceeded
        - rate_limited
        - internal
        - email_not_verified
        - email_in_use
        - invalid_otp
        - unsupported_content_type
        - inline_too_large
        - plan_required
        - admin_only
        - template_assets_not_shared
        - template_has_webhook
        - http_error
        - shader_compile_error
        - shader_compiler_unavailable
        - shader_not_found
        - shader_param_invalid
      title: ErrorCode
      type: string
      x-enumDescriptions:
        invalid_request: The request was malformed or failed validation.
        unauthorized: Missing or invalid API key.
        forbidden: Authenticated but not permitted to perform this action.
        not_found: The requested resource does not exist.
        conflict: >-
          Conflicts with current state (e.g. a reused Idempotency-Key with a
          different body).
        unsupported_feature: The composition uses a feature the renderer does not support.
        invalid_source: A source URL is malformed or unreachable.
        source_not_found: A referenced source file could not be found.
        source_too_large: A source file exceeds the allowed size.
        asset_not_ready: A referenced asset is still ingesting and is not ready yet.
        ingest_failed: Copying an external source into Framelane storage failed.
        codec_unsupported: A source uses a codec the renderer cannot decode.
        quota_exceeded: The workspace has exhausted its plan quota (HTTP 402).
        rate_limited: Too many requests; retry after the Retry-After interval.
        internal: An unexpected server error occurred.
        email_not_verified: >-
          The workspace email is unverified; verify it before making authed
          calls.
        email_in_use: The email is already associated with another workspace.
        invalid_otp: The verification code is incorrect or expired.
        unsupported_content_type: The provided content type is not supported.
        plan_required: >-
          The feature needs a higher plan (HTTP 402); `details.required_plan`
          names it.
        admin_only: Only an admin workspace may call this route (HTTP 403).
        template_assets_not_shared: >-
          A published template may reference only shared CDN media;
          `details.urls` lists the rest.
        template_has_webhook: >-
          A published template may not carry a `webhook_url`: every copy of it
          would notify that URL.
        http_error: A generic HTTP error not covered by a more specific code.
        shader_compile_error: >-
          A shader's SkSL did not compile; `details.errors` gives each error's
          line and column.
        shader_compiler_unavailable: >-
          The shader compiler is not installed on this deployment or did not
          answer in time (HTTP 503).
        shader_not_found: >-
          An effect names a shader id that is neither this workspace's nor
          shared.
        shader_param_invalid: >-
          An effect sets a shader param the shader does not declare, or a value
          of the wrong type or range.
    CornerRadiiPx:
      properties:
        top_left:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Top Left
          default: 0
        top_right:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Top Right
          default: 0
        bottom_right:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Bottom Right
          default: 0
        bottom_left:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Bottom Left
          default: 0
      additionalProperties: false
      type: object
      title: CornerRadiiPx
      description: >-
        Per-corner radii in pixels (for `border_radius` on video/image/text
        elements).
    BlendMode:
      type: string
      enum:
        - none
        - multiply
        - screen
        - overlay
        - darken
        - lighten
        - color_dodge
        - color_burn
        - hard_light
        - soft_light
        - difference
        - add
        - exclusion
        - hue
        - saturation
        - color
        - luminosity
      title: BlendMode
      description: >-
        Values:

        - `none` — Normal compositing (source-over) — the default.

        - `multiply` — Multiply blend — darkens by multiplying layer colors.

        - `screen` — Screen blend — lightens by inverting, multiplying,
        inverting.

        - `overlay` — Overlay blend — multiply in shadows, screen in highlights.

        - `darken` — Darken blend — keeps the darker of the two layers per
        channel.

        - `lighten` — Lighten blend — keeps the lighter of the two layers per
        channel.

        - `color_dodge` — Color-dodge blend — brightens the base toward the
        blend color.

        - `color_burn` — Color-burn blend — darkens the base toward the blend
        color.

        - `hard_light` — Hard-light blend — overlay with layers swapped.

        - `soft_light` — Soft-light blend — a gentler hard-light.

        - `difference` — Difference blend — absolute difference of the two
        layers per channel.

        - `add` — Additive (linear-dodge) blend — sums layer colors, clamped.

        - `exclusion` — Exclusion blend — like difference, lower contrast.

        - `hue` — Hue blend — backdrop luminosity + saturation with the blend
        layer's hue.

        - `saturation` — Saturation blend — backdrop hue + luminosity with the
        blend layer's saturation.

        - `color` — Color blend — backdrop luminosity with the blend layer's hue
        + saturation.

        - `luminosity` — Luminosity blend — backdrop hue + saturation with the
        blend layer's luminosity.
      x-enumDescriptions:
        none: Normal compositing (source-over) — the default.
        multiply: Multiply blend — darkens by multiplying layer colors.
        screen: Screen blend — lightens by inverting, multiplying, inverting.
        overlay: Overlay blend — multiply in shadows, screen in highlights.
        darken: Darken blend — keeps the darker of the two layers per channel.
        lighten: Lighten blend — keeps the lighter of the two layers per channel.
        color_dodge: Color-dodge blend — brightens the base toward the blend color.
        color_burn: Color-burn blend — darkens the base toward the blend color.
        hard_light: Hard-light blend — overlay with layers swapped.
        soft_light: Soft-light blend — a gentler hard-light.
        difference: Difference blend — absolute difference of the two layers per channel.
        add: Additive (linear-dodge) blend — sums layer colors, clamped.
        exclusion: Exclusion blend — like difference, lower contrast.
        hue: >-
          Hue blend — backdrop luminosity + saturation with the blend layer's
          hue.
        saturation: >-
          Saturation blend — backdrop hue + luminosity with the blend layer's
          saturation.
        color: >-
          Color blend — backdrop luminosity with the blend layer's hue +
          saturation.
        luminosity: >-
          Luminosity blend — backdrop hue + saturation with the blend layer's
          luminosity.
    MaskShape:
      type: string
      enum:
        - circle
        - diamond
        - hexagon
        - star
        - heart
        - triangle
      title: MaskShape
      description: >-
        Analytic (SDF) shape mask that clips an element's coverage. §1.


        Values:

        - `circle` — Circular (ellipse) analytic mask, fit to the element
        bounds.

        - `diamond` — Diamond (rotated square) analytic mask.

        - `hexagon` — Regular hexagon analytic mask.

        - `star` — Five-point star analytic mask.

        - `heart` — Heart analytic mask.

        - `triangle` — Upward triangle analytic mask.
      x-enumDescriptions:
        circle: Circular (ellipse) analytic mask, fit to the element bounds.
        diamond: Diamond (rotated square) analytic mask.
        hexagon: Regular hexagon analytic mask.
        star: Five-point star analytic mask.
        heart: Heart analytic mask.
        triangle: Upward triangle analytic mask.
    MaskConfig:
      properties:
        feather:
          type: number
          minimum: 0
          title: Feather
          description: Soften the mask's SDF edge.
          default: 0
        scale:
          type: number
          exclusiveMinimum: 0
          title: Scale
          description: Mask scale (iris reveals).
          default: 1
        center:
          anyOf:
            - $ref: '#/components/schemas/Point'
            - type: 'null'
          description: Mask centre (wipes).
      additionalProperties: false
      type: object
      title: MaskConfig
      description: >-
        Animated/feathered mask params layered on the analytic `mask_shape`
        (§5c).
    MaskKeyframe:
      properties:
        time:
          type: number
          minimum: 0
          title: Time
          description: Timeline seconds.
        feather:
          anyOf:
            - type: number
            - type: 'null'
          title: Feather
        scale:
          anyOf:
            - type: number
            - type: 'null'
          title: Scale
        center:
          anyOf:
            - $ref: '#/components/schemas/Point'
            - type: 'null'
        easing:
          anyOf:
            - type: string
            - type: 'null'
          title: Easing
          description: >-
            Easing curve name — any `easings` catalog value (`linear`, `hold`,
            `ease_in_out`, `quad_out`, `expo_in_out`, `square_in_out` = step,
            …). Unknown names are rejected: the engine would silently fall back
            to linear. For spring/back/elastic/bezier use the sub-objects
            instead.
        spring:
          anyOf:
            - $ref: '#/components/schemas/SpringEasing'
            - type: 'null'
        back:
          anyOf:
            - $ref: '#/components/schemas/BackEasing'
            - type: 'null'
        elastic:
          anyOf:
            - $ref: '#/components/schemas/ElasticEasing'
            - type: 'null'
        bezier:
          anyOf:
            - $ref: '#/components/schemas/BezierEasing'
            - type: 'null'
      additionalProperties: false
      type: object
      required:
        - time
      title: MaskKeyframe
      description: >-
        A keyframe for animated masks (§5c). Absent fields inherit the static
        mask.
    Matte:
      properties:
        source:
          type: string
          minLength: 1
          title: Source
          description: uuid of the source layer (auto-hidden). Text sources supported.
        mode:
          type: string
          enum:
            - alpha
            - luma
          title: Mode
          default: alpha
        invert:
          type: boolean
          title: Invert
          default: false
      additionalProperties: false
      type: object
      required:
        - source
      title: Matte
      description: >-
        Track matte (§5d): drive this element's coverage from another layer's
        render.
    BlurKeyframe:
      properties:
        time:
          type: number
          minimum: 0
          title: Time
          description: Timeline seconds.
        value:
          type: number
          maximum: 128
          minimum: 0
          title: Value
          description: Gaussian sigma in canvas px at `time`, at most 128.
      additionalProperties: false
      type: object
      required:
        - time
        - value
      title: BlurKeyframe
      description: >-
        A blur sigma at a time (V6): linear between keyframes, the first and
        last held

        outside them. Any order: the renderer sorts them by time.
    Glass:
      properties:
        bevel:
          anyOf:
            - type: number
              maximum: 256
              minimum: 0
            - type: 'null'
          title: Bevel
          description: >-
            Width of the bent band at the edge in canvas px, at most 256.
            Omitted: 16.
        refraction:
          anyOf:
            - type: number
              maximum: 1
              minimum: 0
            - type: 'null'
          title: Refraction
          description: 'How far the edge bends the scene inward, 0–1. Omitted: 0.5.'
        dispersion:
          anyOf:
            - type: number
              maximum: 1
              minimum: 0
            - type: 'null'
          title: Dispersion
          description: 'How far red and blue split apart at the edge, 0–1. Omitted: 0.'
      additionalProperties: false
      type: object
      title: Glass
      description: >-
        A refracting glass surface (V6, render-node AEC §1): within `bevel` px
        of the

        element's edge (a shape's own outline, a box's rounded corners) the
        scene behind is

        read inward, bent by `refraction`, its red and blue split by
        `dispersion`. With

        `backdrop_blur` it bends the blurred scene, without it the sharp one.
    RateKeyframe:
      properties:
        time:
          type: number
          minimum: 0
          title: Time
          description: Timeline seconds.
        rate:
          type: number
          minimum: 0
          title: Rate
          description: Playback rate (0 = freeze-frame).
      additionalProperties: false
      type: object
      required:
        - time
        - rate
      title: RateKeyframe
      description: A point on a video speed ramp (§5c). Absolute timeline seconds.
    Effect:
      properties:
        type:
          anyOf:
            - $ref: '#/components/schemas/EffectType'
            - type: 'null'
          description: >-
            The effect from the vocabulary (`effects` in GET /v1/capabilities).
            Omit it beside `shader`.
        intensity:
          anyOf:
            - type: number
              maximum: 100
              minimum: 0
            - type: 'null'
          title: Intensity
          description: >-
            Effect strength, 0 to 100. On a shader effect it is null unless set:
            the shader then runs at the engine's own full intensity rather than
            the effect-wide 50.
          default: 50
        intensity_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/IntensityKeyframe'
              type: array
            - type: 'null'
          title: Intensity Keyframes
          description: >-
            Animate intensity over time (§2): piecewise-linear `[{time, value}]`
            in timeline seconds. Overrides the static `intensity` when set.
        props:
          anyOf:
            - additionalProperties:
                type: number
              type: object
            - type: 'null'
          title: Props
          description: >-
            Per-shader scalar props (§2), bound as `u_prop_<key>` (e.g.
            `{"speed": 0.6}` for aurora). Ignored for chroma_key (use
            `chroma_settings`).
        chroma_settings:
          anyOf:
            - $ref: '#/components/schemas/ChromaKeyProps'
            - type: 'null'
        shader:
          anyOf:
            - $ref: '#/components/schemas/EffectShader'
            - type: 'null'
          description: >-
            A runtime shader in place of `type` (render-node AEC §2): `{id,
            params}`, with `intensity` beside it and nothing else. Authoring:
            `shaders` in GET /v1/capabilities.
      additionalProperties: false
      type: object
      title: Effect
    Motion:
      properties:
        type:
          anyOf:
            - $ref: '#/components/schemas/MotionType'
            - type: 'null'
          description: >-
            Catalog preset. Exactly one of `type`, `preset` or `custom` must be
            set.
        preset:
          anyOf:
            - type: string
            - type: 'null'
          title: Preset
          description: >-
            Renderer animation name, used verbatim — any element animation on
            video/image/shape/group (e.g. `'pop'`, `'floatUp'`,
            `'out-slideBounceLeft'`, `'loop-heartBeat'`), any text animation on
            text (e.g. `'typewriter'`, `'roll'`, `'out-sink'`, `'loop-wavey'`).
            Reaches the presets `type` has no name for. The name carries its own
            direction: `out-*` names are exits, `loop-*` names loop with
            `duration` as one cycle, so `reversed` and `scope` do not apply.
            Exactly one of `type`, `preset` or `custom` must be set.
        custom:
          anyOf:
            - type: string
            - type: 'null'
          title: Custom
          description: >-
            Name of a `custom_animations` entry to run instead of a catalog
            preset — the `element` registry for video/image/shape/group
            animations, the `text` registry for text. Exactly one of `type`,
            `preset` or `custom` must be set.
        time:
          type: number
          minimum: 0
          title: Time
          description: >-
            Absolute start time on the output timeline in seconds. For entrances
            and loops set this to the element's `time`. For exits set this to
            element `time + duration − motion duration`. An entrance timed later
            than the element's own `time` leaves it fully rendered on screen
            until the entrance fires; the linter reports that as
            `ENTRANCE_AFTER_ELEMENT_START`.
        duration:
          type: number
          exclusiveMinimum: 0
          title: Duration
          description: How long the animation runs in seconds.
        easing:
          $ref: '#/components/schemas/Easing'
          description: >-
            **Ignored by the renderer** — animation curves are baked into the
            preset's keyframes (or your `custom_animations` keyframes'
            `easing`). Kept for backward compatibility only.
          default: ease_in_out
        reversed:
          type: boolean
          title: Reversed
          description: >-
            Run the preset's exit form instead of its entrance. Only presets
            that ship both forms accept it — `fade`, `slide_*`, `wipe_*`,
            `rotate_*`, `bounce`. Entrance-only presets have a separate
            exit-only type instead: use `zoom_out` (not `zoom_in` reversed),
            `evaporate` (not `blur`), `whip_down`, `drift_out`, `swing_out`,
            `elastic_drop` — those six set this flag for you. See `GET
            /v1/capabilities` `motion[].element_exit` for the per-preset truth.
          default: false
        scope:
          $ref: '#/components/schemas/MotionScope'
          default: element
        delay:
          type: number
          minimum: 0
          title: Delay
          description: >-
            Seconds to wait before the animation starts (folded into the
            animation's start time on the wire — the engine has no standalone
            pre-start delay). On loop presets it spaces the repeats instead.
          default: 0
        spacing:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Spacing
          description: >-
            Stagger multiplier between per-glyph/word windows on text animations
            (engine default 1 = fully sequential; smaller values overlap the
            windows). Ignored by block animations.
        loop:
          type: boolean
          title: Loop
          default: false
        audio:
          anyOf:
            - $ref: '#/components/schemas/AudioGenerator'
            - type: 'null'
          description: >-
            Audio-reactive offsets (§3). Element animations only
            (video/image/shape/group) — the engine does not run the generator on
            text animations; put the text in an audio-reactive group instead.
            For a pure generator with no visible motion, use `type: "identity"`,
            the engine's no-op carrier.
      additionalProperties: false
      type: object
      required:
        - time
        - duration
      title: Motion
    WordAnimation:
      properties:
        style:
          $ref: '#/components/schemas/WordAnimationStyle'
        words:
          items:
            $ref: '#/components/schemas/Word'
          type: array
          minItems: 1
          title: Words
      additionalProperties: false
      type: object
      required:
        - style
        - words
      title: WordAnimation
    WordSpec:
      properties:
        word:
          type: string
          minLength: 1
          title: Word
          description: The word (whole-word match; UTF-8).
        start:
          type: number
          minimum: 0
          title: Start
          description: >-
            Word reveal start, seconds — drives a word-group animation; ignored
            without one.
        end:
          type: number
          exclusiveMinimum: 0
          title: End
          description: >-
            Word reveal end, seconds — drives a word-group animation; ignored
            without one.
        style:
          anyOf:
            - $ref: '#/components/schemas/WordStyle'
            - type: 'null'
          description: Optional per-word style override (static for the element's life).
      additionalProperties: false
      type: object
      required:
        - word
        - start
        - end
      title: WordSpec
      description: |-
        Per-word timing + optional style, keyed by word text (§4).

        ``style`` overrides are static for the element's whole life. ``start``/
        ``end`` drive **word-group animations only** — bind one via
        ``word_animation`` (or a word-group ``animation_preset``) or the timings
        have nothing to time and are ignored by the renderer.
    ValueCounter:
      properties:
        from_value:
          type: number
          title: From Value
          description: Start value.
          default: 0
        to_value:
          type: number
          title: To Value
          description: End value.
          default: 100
        decimals:
          type: integer
          maximum: 6
          minimum: 0
          title: Decimals
          description: Decimal places (0–6).
          default: 0
        prefix:
          type: string
          title: Prefix
          description: Text prepended to the number.
          default: ''
        suffix:
          type: string
          title: Suffix
          description: Text appended to the number, e.g. '%'.
          default: ''
        easing:
          anyOf:
            - type: string
            - type: 'null'
          title: Easing
          description: >-
            Easing curve name from the `easings` catalog (e.g. 'quad_out',
            'expo_out'). Only named curves apply here — the parameterized
            spring/back/elastic families are not available on the counter.
        start_time:
          type: number
          minimum: 0
          title: Start Time
          description: Seconds into the element to start the sweep.
          default: 0
        length:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Length
          description: Sweep duration; defaults to the element's remaining life.
      additionalProperties: false
      type: object
      title: ValueCounter
      description: >-
        Rewrite a text element from an eased numeric sweep (the animated stat
        hero, §4).
    TextGlow:
      properties:
        color:
          type: string
          pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
          title: Color
          example: '#ffffff'
        size:
          type: number
          minimum: 0
          title: Size
          description: Glow size, em-relative.
          default: 0.15
        intensity:
          type: number
          minimum: 0
          title: Intensity
          description: Glow intensity.
          default: 1
      additionalProperties: false
      type: object
      required:
        - color
      title: TextGlow
      description: Soft glow around the glyphs (§4).
    ShapeGeometry:
      properties:
        rect:
          anyOf:
            - $ref: '#/components/schemas/GeometryRect'
            - type: 'null'
        ellipse:
          anyOf:
            - $ref: '#/components/schemas/GeometryEllipse'
            - type: 'null'
        line:
          anyOf:
            - $ref: '#/components/schemas/GeometryLine'
            - type: 'null'
        ring:
          anyOf:
            - $ref: '#/components/schemas/GeometryRing'
            - type: 'null'
        arc:
          anyOf:
            - $ref: '#/components/schemas/GeometryRing'
            - type: 'null'
          description: The same parameters as `ring`.
        polygon:
          anyOf:
            - $ref: '#/components/schemas/GeometryPolygon'
            - type: 'null'
        star:
          anyOf:
            - $ref: '#/components/schemas/GeometryStar'
            - type: 'null'
        stroke_align:
          anyOf:
            - type: string
              enum:
                - inside
                - center
                - outside
            - type: 'null'
          title: Stroke Align
          description: >-
            Where the stroke sits on the outline (`centre` is read as `center`).
            Omitted: `center`. A polygon, a star and a smoothed rect stroke
            centred.
        shadows:
          anyOf:
            - items:
                $ref: '#/components/schemas/GeometryShadow'
              type: array
              maxItems: 4
            - type: 'null'
          title: Shadows
          description: >-
            Up to 4 shadows, the first on top, on a rect or an ellipse drawn as
            a circle.
      additionalProperties: false
      type: object
      title: ShapeGeometry
      description: >-
        A closed-form shape the engines draw analytically (AEC §5f): exactly one
        kind.
    DashOffsetKeyframe:
      properties:
        time:
          type: number
          minimum: 0
          title: Time
          description: Timeline seconds.
        offset:
          type: number
          title: Offset
          description: >-
            Dash offset at this time, in viewBox units (SVG
            `stroke-dashoffset`).
        easing:
          anyOf:
            - $ref: '#/components/schemas/Easing'
            - type: 'null'
          description: Ease into this keyframe.
        spring:
          anyOf:
            - $ref: '#/components/schemas/SpringEasing'
            - type: 'null'
        back:
          anyOf:
            - $ref: '#/components/schemas/BackEasing'
            - type: 'null'
        elastic:
          anyOf:
            - $ref: '#/components/schemas/ElasticEasing'
            - type: 'null'
        bezier:
          anyOf:
            - $ref: '#/components/schemas/BezierEasing'
            - type: 'null'
      additionalProperties: false
      type: object
      required:
        - time
        - offset
      title: DashOffsetKeyframe
      description: >-
        A keyframe on the dash offset (marching ants).


        ``offset`` is the whole SVG ``stroke-dashoffset`` at ``time`` in the
        same

        viewBox units as the static ``stroke_dash_offset`` — nothing inherits on

        this channel, there is one value to animate.
    CornerRadii:
      properties:
        top_left:
          type: number
          maximum: 1
          minimum: 0
          title: Top Left
          default: 0
        top_right:
          type: number
          maximum: 1
          minimum: 0
          title: Top Right
          default: 0
        bottom_right:
          type: number
          maximum: 1
          minimum: 0
          title: Bottom Right
          default: 0
        bottom_left:
          type: number
          maximum: 1
          minimum: 0
          title: Bottom Left
          default: 0
      additionalProperties: false
      type: object
      title: CornerRadii
      description: >-
        Per-corner radii, each normalized 0–1 (1 = fully round on the shorter
        side).
    DropShadow:
      properties:
        color:
          type: string
          pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
          title: Color
          example: '#00000066'
        distance:
          type: number
          minimum: 0
          title: Distance
          description: >-
            Shadow offset **strength**, 0–100 on the renderer's own scale —
            **not pixels**. The rendered offset is `distance × 0.0005 × the
            element's shorter side`, so on a 400px-tall card `60` drops the
            shadow 12px while a px-looking `8` drops it 1.6px, and on a 200px
            chip that same `8` is 0.8px — invisible. Author 60–70 (what the
            reference scenes use) and it scales with the element; for a specific
            N px offset use `distance = N ÷ (0.0005 × shorter_side_px)`. Must be
            > 0: distance 0 is skipped by the renderer entirely. For pixel-unit
            shadows use `shadow_x`/`shadow_y` on video/image elements.
          default: 0
        direction:
          type: number
          title: Direction
          description: Shadow direction in degrees (polar; 90 = down).
          default: 90
        blur:
          type: number
          minimum: 0
          title: Blur
          description: >-
            Shadow softness, 0–100 on the renderer's blur-strength scale —
            **not** a pixel radius, and unlike `distance` it does not scale with
            the element. `0` is a hard-edged offset copy (a legitimate poster
            look); 60–70 is the soft card shadow the reference scenes author. A
            px-looking `8` is a near-hard edge, not an 8px feather.
          default: 0
      additionalProperties: false
      type: object
      required:
        - color
      title: DropShadow
      description: >-
        Drop shadow for shape elements, in the renderer's own units.


        ``distance`` and ``blur`` are the engine's unitless 0–100 strength
        scale,

        NOT pixels — a px-looking ``distance: 10`` is a fraction of what you
        meant

        on a small element and renders as nothing at all. The offset lands at

        ``distance × 0.0005 × the element's shorter side``; the reference scenes

        author 60–70 for both. For pixel-unit shadows use
        ``shadow_x``/``shadow_y``

        on video and image elements. The layout lint reports a distance that

        renders under a pixel as ``SHADOW_BELOW_MIN_SIZE``.
    PathKeyframe:
      properties:
        time:
          type: number
          minimum: 0
          title: Time
          description: Timeline seconds for this path.
        d:
          type: string
          minLength: 1
          title: D
          description: SVG path `d` at this time.
        easing:
          anyOf:
            - $ref: '#/components/schemas/Easing'
            - type: 'null'
          description: >-
            Ease into this keyframe. A spring/back/elastic/bezier sub-object
            overrides it — overshooting eases visibly overshoot the *geometry*
            and settle back.
        spring:
          anyOf:
            - $ref: '#/components/schemas/SpringEasing'
            - type: 'null'
        back:
          anyOf:
            - $ref: '#/components/schemas/BackEasing'
            - type: 'null'
        elastic:
          anyOf:
            - $ref: '#/components/schemas/ElasticEasing'
            - type: 'null'
        bezier:
          anyOf:
            - $ref: '#/components/schemas/BezierEasing'
            - type: 'null'
      additionalProperties: false
      type: object
      required:
        - time
        - d
      title: PathKeyframe
    TrimPath:
      properties:
        start:
          type: number
          maximum: 1
          minimum: 0
          title: Start
          description: Trim start, 0–1 along the path.
          default: 0
        end:
          type: number
          maximum: 1
          minimum: 0
          title: End
          description: Trim end, 0–1 along the path.
          default: 1
      additionalProperties: false
      type: object
      title: TrimPath
    TrimKeyframe:
      properties:
        time:
          type: number
          minimum: 0
          title: Time
          description: Timeline seconds.
        start:
          anyOf:
            - type: number
              maximum: 1
              minimum: 0
            - type: 'null'
          title: Start
        end:
          anyOf:
            - type: number
              maximum: 1
              minimum: 0
            - type: 'null'
          title: End
        easing:
          anyOf:
            - $ref: '#/components/schemas/Easing'
            - type: 'null'
          description: Ease into this keyframe.
        spring:
          anyOf:
            - $ref: '#/components/schemas/SpringEasing'
            - type: 'null'
        back:
          anyOf:
            - $ref: '#/components/schemas/BackEasing'
            - type: 'null'
        elastic:
          anyOf:
            - $ref: '#/components/schemas/ElasticEasing'
            - type: 'null'
        bezier:
          anyOf:
            - $ref: '#/components/schemas/BezierEasing'
            - type: 'null'
      additionalProperties: false
      type: object
      required:
        - time
      title: TrimKeyframe
      description: >-
        A keyframe on the trim window (draw-on, §5b).


        Absent ``start``/``end`` inherit the static ``trim`` values, so
        animating

        ``end`` 0→1 on a stroked path draws it on while ``start`` stays put.
    GeometryCornerRadii:
      properties:
        top_left:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Top Left
        top_right:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Top Right
        bottom_right:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Bottom Right
        bottom_left:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Bottom Left
      additionalProperties: false
      type: object
      title: GeometryCornerRadii
      description: Per-corner radii in px; a corner left out is 0.
    TimeMap:
      properties:
        offset:
          anyOf:
            - type: number
            - type: 'null'
          title: Offset
          description: >-
            Seconds into the children's timeline at the frame's start. Omitted:
            0.
        speed:
          anyOf:
            - type: number
            - type: 'null'
          title: Speed
          description: >-
            Rate of the children's timeline: 0 holds, below 0 plays backwards.
            Omitted: 1.
        loop:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Loop
          description: >-
            Seconds after which the children's timeline repeats. Omitted: no
            loop.
        freeze:
          anyOf:
            - type: number
            - type: 'null'
          title: Freeze
          description: >-
            Hold the children at this many seconds into their timeline;
            overrides `offset` and `speed`.
      additionalProperties: false
      type: object
      title: TimeMap
      description: 'A composite frame''s clock (AEC §5): the time its children read.'
    ChildVideoElement:
      properties:
        lut_url:
          anyOf:
            - type: string
              maxLength: 2083
              minLength: 1
              format: uri
            - type: 'null'
          title: Lut Url
        lut_intensity:
          type: number
          maximum: 100
          minimum: 0
          title: Lut Intensity
          default: 100
        brightness:
          type: number
          title: Brightness
          description: >-
            Brightness, -100..100, applied as a 0..2 colour multiplier (0 = no
            change). `-100` multiplies by 0: the element renders pure black
            while keeping its alpha — a silhouette of the artwork. Combine a
            `brightness: -100` copy of an image with `blur`, reduced `opacity`
            and a small offset, stacked beneath the real one, to fake the
            content-shaped drop shadow `shadow_x`/`shadow_y` cannot produce.
          default: 0
        contrast:
          type: number
          title: Contrast
          default: 0
        saturation:
          type: number
          title: Saturation
          default: 0
        exposure:
          type: number
          title: Exposure
          default: 0
        sharpness:
          type: number
          title: Sharpness
          default: 0
        blur:
          type: number
          title: Blur
          default: 0
        noise:
          type: number
          title: Noise
          default: 0
        vignette:
          type: number
          title: Vignette
          default: 0
        hue_rotate:
          type: number
          title: Hue Rotate
          default: 0
        temperature:
          type: number
          title: Temperature
          description: >-
            White balance warm (+) / cool (−), −1..1; 0 = no-op. Applied as a
            channel matrix, after the classic brightness/contrast chain.
          default: 0
        tint:
          type: number
          title: Tint
          description: White balance magenta (+) / green (−), −1..1; 0 = no-op.
          default: 0
        vibrance:
          type: number
          title: Vibrance
          description: >-
            Saturation boost weighted toward muted colours, −1..1; 0 = no-op.
            Boosts flat colour without wrecking skin tones.
          default: 0
        highlights:
          type: number
          title: Highlights
          description: Luma-masked tone lift of the brightest range, −1..1; 0 = no-op.
          default: 0
        shadows:
          type: number
          title: Shadows
          description: Luma-masked tone lift of the darkest range, −1..1; 0 = no-op.
          default: 0
        crop_top:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Top
          default: 0
        crop_bottom:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Bottom
          default: 0
        crop_left:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Left
          default: 0
        crop_right:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Right
          default: 0
        border_radius:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - $ref: '#/components/schemas/CornerRadiiPx'
          title: Border Radius
          description: >-
            Corner radius in pixels — a single value, or per-corner `{top_left,
            top_right, bottom_right, bottom_left}` for asymmetric corners (e.g.
            a card rounded only on top). The renderer normalizes px to a 0–1
            fraction of the element's shorter side (≈ half the shorter side is
            fully rounded) and clamps to that range. For shape elements use
            `corner_radius` (already 0–1).
          default: 0
        border_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Border Color
        border_width:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Border Width
          description: >-
            Border thickness in pixels (converted to the renderer's relative
            border scale against the element's shorter side, so it survives
            resizes). The border outlines the element's border_radius-rounded
            quad — not the image's alpha silhouette.
          default: 0
        shadow_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Shadow Color
        shadow_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Blur
          description: >-
            Shadow softness, 0–100 (renderer blur-strength scale, not pixels): 0
            = hard edge, 100 = maximum blur. Values are clamped to that range.
          default: 0
        shadow_x:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow X
          description: >-
            Horizontal shadow offset in pixels (converted to the renderer's
            relative offset scale). The shadow silhouette is the element's
            border_radius-rounded quad, not the content alpha — a cut-out PNG
            gets a card shadow, not an outline-shaped one. On a cut-out that
            reads as a floating rectangle (rotated with the element, which is
            the tell). For a shadow that follows the artwork, drop these fields
            and stack a second copy of the same image underneath at `brightness:
            -100` (a black silhouette, alpha preserved) with `blur` and reduced
            `opacity`, offset a few percent — see `brightness`.
          default: 0
        shadow_y:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Y
          description: Vertical shadow offset in pixels. See `shadow_x`.
          default: 0
        blend_mode:
          $ref: '#/components/schemas/BlendMode'
          default: none
        mask_shape:
          anyOf:
            - $ref: '#/components/schemas/MaskShape'
            - type: 'null'
        mask:
          anyOf:
            - $ref: '#/components/schemas/MaskConfig'
            - type: 'null'
        mask_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/MaskKeyframe'
              type: array
            - type: 'null'
          title: Mask Keyframes
        matte:
          anyOf:
            - $ref: '#/components/schemas/Matte'
            - type: 'null'
        backdrop_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Backdrop Blur
          description: >-
            Glass: the scene behind the element, Gaussian-blurred, fills its
            footprint under its translucent content (a shape's own silhouette, a
            video's or image's box with its corner radius and mask). A Gaussian
            sigma in canvas px, as CSS `backdrop-filter: blur()` takes it; the
            renderer caps it at 128. 0 is off.
          default: 0
        backdrop_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Backdrop Blur Keyframes
          description: >-
            Animate `backdrop_blur` over the timeline; replaces the static value
            while present.
        layer_blur:
          type: number
          maximum: 128
          minimum: 0
          title: Layer Blur
          description: >-
            Blurs the element itself as drawn: a Gaussian sigma in canvas px, as
            CSS `filter: blur()` takes it, spreading three sigma past its box.
            At most 128; 0 is off. Not on a shape yet (the editor's preview
            cannot draw it), and not with `blend_mode`, `backdrop_blur` or
            `matte`, beside which the renderer would draw the element without
            it.
          default: 0
        layer_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Layer Blur Keyframes
          description: >-
            Animate `layer_blur` over the timeline; replaces the static value
            while present.
        glass:
          anyOf:
            - $ref: '#/components/schemas/Glass'
            - type: 'null'
          description: >-
            A refracting glass edge: the scene behind bends inward near the
            element's outline. Pair it with `backdrop_blur` for frosted glass.
        x:
          anyOf:
            - type: number
            - type: 'null'
          title: X
          description: 'Left edge in design px from the frame''s left edge. Omitted: 0.'
        'y':
          anyOf:
            - type: number
            - type: 'null'
          title: 'Y'
          description: 'Top edge in design px from the frame''s top edge. Omitted: 0.'
        width:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Width
          description: 'Width in design px. Omitted: the frame''s width.'
        height:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Height
          description: >-
            Height in design px. Omitted: the frame's height (a text's: twice
            its `font_size`).
        aspect_ratio:
          anyOf:
            - type: number
            - type: 'null'
          title: Aspect Ratio
          description: >-
            **Accepted but ignored — the renderer never receives this field.**
            It does NOT constrain or correct the `width`/`height` box, so it
            cannot be used to stop a mismatched box from cropping the source;
            size the box per `width` instead. Kept for backward compatibility.
        x_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Anchor
          description: >-
            Pivot the element turns and scales about, across its own box: a
            percent, or design px from its left edge.
          default: 50%
        y_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Anchor
          description: >-
            Pivot across its own box's height: a percent, or design px from its
            top edge.
          default: 50%
        x_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Rotation
          default: 0°
        y_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Rotation
          default: 0°
        z_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Z Rotation
          default: 0°
        x_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Scale
          default: 100%
        y_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Scale
          default: 100%
        flip_horizontal:
          type: boolean
          title: Flip Horizontal
          default: false
        flip_vertical:
          type: boolean
          title: Flip Vertical
          default: false
        opacity:
          type: number
          maximum: 100
          minimum: 0
          title: Opacity
          default: 100
        z_index:
          anyOf:
            - type: integer
            - type: 'null'
          title: Z Index
          description: >-
            Stacking order among the frame's children (higher on top); ties draw
            in array order.
        clip:
          type: boolean
          title: Clip
          default: false
        color_overlay:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Color Overlay
          description: >-
            **Not supported by the renderer — setting this is rejected.** A
            strict submit returns `unsupported_feature` (422) and a dry-run
            reports it as a blocking violation; there is no native tint field to
            map it onto. To darken an element use `brightness` (`-100` is a
            black silhouette); to lay a colour over one, stack a `shape` with a
            flat or gradient `fill` above it.
        type:
          type: string
          const: video
          title: Type
          default: video
        id:
          anyOf:
            - type: string
            - type: 'null'
          title: Id
          description: >-
            Unique identifier for this element. **Required on image elements** —
            an empty or missing ID causes the image to be silently skipped by
            the renderer. Recommended on all elements that are referenced by a
            transition.
          example: clip_01
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional human-readable label. Not used by the renderer.
        track:
          anyOf:
            - type: integer
              maximum: 255
              minimum: 0
            - type: 'null'
          title: Track
          description: >-
            Timeline track index (0–255). Informational only; not used by the
            renderer.
        time:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Time
          description: >-
            Start in seconds on the clock the frame gives its children (the
            composition's, unless a composite frame maps it; inside a component,
            0 is the instance's start). Omitted: the frame's start.
        visible:
          type: boolean
          title: Visible
          description: >-
            Set to `false` to skip this element without removing it from the
            request.
          default: true
        locked:
          type: boolean
          title: Locked
          description: Marks the element locked in editors. Not sent to the renderer.
          default: false
        source_url:
          type: string
          maxLength: 2083
          minLength: 1
          format: uri
          title: Source Url
          description: >-
            URL of the video file — MP4, MOV or WebM only; any other extension
            is rejected at submit. Must be accessible by the renderer.
          example: https://cdn.example.com/clip.mp4
        in_point:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: In Point
          description: >-
            In point — seconds into the source file to start playing from.
            Defaults to `0`.
          example: 0
        out_point:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Out Point
          description: >-
            Out point — seconds into the source file to stop playing. Defaults
            to end of source when omitted. Controls clip length on the timeline.
          example: 6
        speed:
          type: number
          maximum: 4
          minimum: 0.25
          title: Speed
          description: >-
            Playback speed. `1.0` = normal speed, `2.0` = 2× (half duration),
            `0.5` = half speed (double duration).
          default: 1
          example: 1
        playback_rate_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/RateKeyframe'
              type: array
              maxItems: 512
            - type: 'null'
          title: Playback Rate Keyframes
          description: >-
            Speed ramp (§5c): piecewise-linear playback-rate keyframes (absolute
            timeline seconds; rate 0 = freeze; at most 512 keyframes — the
            renderer truncates beyond that). Audio keeps the static rate — mute
            or detach it on ramped clips.
        volume:
          type: number
          maximum: 100
          minimum: 0
          title: Volume
          description: >-
            Embedded audio volume as a percentage (0–100). Affects only the
            audio track inside this video clip.
          default: 100
          example: 100
        muted:
          type: boolean
          title: Muted
          description: >-
            Silence the clip's embedded audio. `volume` is kept, so unmuting
            restores the level.
          default: false
        loop:
          type: boolean
          title: Loop
          description: >-
            Repeat the source to fill the clip: when playback passes the end of
            the file it restarts from the file's first frame (not `in_point`).
            Set `out_point` past the source's length to choose how long the clip
            runs — its timeline length is still `(out_point − in_point) /
            speed`. Requires `out_point`.
          default: false
        fade_in_duration:
          type: number
          title: Fade In Duration
          description: Duration in seconds of a linear audio fade-in at the clip's start.
          default: 0
          example: 0
        fade_out_duration:
          type: number
          title: Fade Out Duration
          description: Duration in seconds of a linear audio fade-out at the clip's end.
          default: 0
          example: 0
        effects:
          items:
            $ref: '#/components/schemas/Effect'
          type: array
          title: Effects
          default: []
        motion:
          items:
            $ref: '#/components/schemas/Motion'
          type: array
          title: Motion
          default: []
      additionalProperties: false
      type: object
      required:
        - source_url
      title: ChildVideoElement
      description: >-
        A video inside a frame. Not inside a composite frame, whose content is
        graphics only.
    ChildTextElement:
      properties:
        border_radius:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - $ref: '#/components/schemas/CornerRadiiPx'
          title: Border Radius
          description: >-
            Corner radius in pixels — a single value, or per-corner `{top_left,
            top_right, bottom_right, bottom_left}` for asymmetric corners (e.g.
            a card rounded only on top). The renderer normalizes px to a 0–1
            fraction of the element's shorter side (≈ half the shorter side is
            fully rounded) and clamps to that range. For shape elements use
            `corner_radius` (already 0–1).
          default: 0
        border_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Border Color
        border_width:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Border Width
          description: >-
            Border thickness in pixels (converted to the renderer's relative
            border scale against the element's shorter side, so it survives
            resizes). The border outlines the element's border_radius-rounded
            quad — not the image's alpha silhouette.
          default: 0
        shadow_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Shadow Color
        shadow_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Blur
          description: >-
            Shadow softness, 0–100 (renderer blur-strength scale, not pixels): 0
            = hard edge, 100 = maximum blur. Values are clamped to that range.
          default: 0
        shadow_x:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow X
          description: >-
            Horizontal shadow offset in pixels (converted to the renderer's
            relative offset scale). The shadow silhouette is the element's
            border_radius-rounded quad, not the content alpha — a cut-out PNG
            gets a card shadow, not an outline-shaped one. On a cut-out that
            reads as a floating rectangle (rotated with the element, which is
            the tell). For a shadow that follows the artwork, drop these fields
            and stack a second copy of the same image underneath at `brightness:
            -100` (a black silhouette, alpha preserved) with `blur` and reduced
            `opacity`, offset a few percent — see `brightness`.
          default: 0
        shadow_y:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Y
          description: Vertical shadow offset in pixels. See `shadow_x`.
          default: 0
        x:
          anyOf:
            - type: number
            - type: 'null'
          title: X
          description: 'Left edge in design px from the frame''s left edge. Omitted: 0.'
        'y':
          anyOf:
            - type: number
            - type: 'null'
          title: 'Y'
          description: 'Top edge in design px from the frame''s top edge. Omitted: 0.'
        width:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Width
          description: 'Width in design px. Omitted: the frame''s width.'
        height:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Height
          description: >-
            Height in design px. Omitted: the frame's height (a text's: twice
            its `font_size`).
        aspect_ratio:
          anyOf:
            - type: number
            - type: 'null'
          title: Aspect Ratio
          description: >-
            **Accepted but ignored — the renderer never receives this field.**
            It does NOT constrain or correct the `width`/`height` box, so it
            cannot be used to stop a mismatched box from cropping the source;
            size the box per `width` instead. Kept for backward compatibility.
        x_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Anchor
          description: >-
            Pivot the element turns and scales about, across its own box: a
            percent, or design px from its left edge.
          default: 50%
        y_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Anchor
          description: >-
            Pivot across its own box's height: a percent, or design px from its
            top edge.
          default: 50%
        x_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Rotation
          default: 0°
        y_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Rotation
          default: 0°
        z_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Z Rotation
          default: 0°
        x_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Scale
          default: 100%
        y_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Scale
          default: 100%
        flip_horizontal:
          type: boolean
          title: Flip Horizontal
          default: false
        flip_vertical:
          type: boolean
          title: Flip Vertical
          default: false
        opacity:
          type: number
          maximum: 100
          minimum: 0
          title: Opacity
          default: 100
        z_index:
          anyOf:
            - type: integer
            - type: 'null'
          title: Z Index
          description: >-
            Stacking order among the frame's children (higher on top); ties draw
            in array order.
        clip:
          type: boolean
          title: Clip
          default: false
        color_overlay:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Color Overlay
          description: >-
            **Not supported by the renderer — setting this is rejected.** A
            strict submit returns `unsupported_feature` (422) and a dry-run
            reports it as a blocking violation; there is no native tint field to
            map it onto. To darken an element use `brightness` (`-100` is a
            black silhouette); to lay a colour over one, stack a `shape` with a
            flat or gradient `fill` above it.
        type:
          type: string
          const: text
          title: Type
          default: text
        id:
          anyOf:
            - type: string
            - type: 'null'
          title: Id
          description: >-
            Unique identifier for this element. **Required on image elements** —
            an empty or missing ID causes the image to be silently skipped by
            the renderer. Recommended on all elements that are referenced by a
            transition.
          example: clip_01
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional human-readable label. Not used by the renderer.
        track:
          anyOf:
            - type: integer
              maximum: 255
              minimum: 0
            - type: 'null'
          title: Track
          description: >-
            Timeline track index (0–255). Informational only; not used by the
            renderer.
        time:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Time
          description: >-
            Start in seconds on the clock the frame gives its children (the
            composition's, unless a composite frame maps it; inside a component,
            0 is the instance's start). Omitted: the frame's start.
        visible:
          type: boolean
          title: Visible
          description: >-
            Set to `false` to skip this element without removing it from the
            request.
          default: true
        locked:
          type: boolean
          title: Locked
          description: Marks the element locked in editors. Not sent to the renderer.
          default: false
        duration:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Duration
          description: 'Seconds visible. Omitted: until the frame ends.'
        text:
          type: string
          minLength: 1
          title: Text
          description: The text content to render.
          example: Hello World
        font_family:
          type: string
          title: Font Family
          description: Font family name. Must be available to the renderer.
          default: Inter
          example: Inter
        font_size:
          type: number
          exclusiveMinimum: 0
          title: Font Size
          description: Font size in pixels.
          default: 16
          example: 48
        font_weight:
          type: integer
          maximum: 900
          minimum: 100
          title: Font Weight
          description: >-
            Font weight (100–900). The renderer has no variable weights: `600`
            and above render **bold**, everything below renders regular.
            Composes with `font_style` (italic + ≥600 = bold italic).
          default: 400
          example: 400
        font_style:
          type: string
          enum:
            - normal
            - italic
            - bold
            - bolditalic
          title: Font Style
          description: Font style variant.
          default: normal
        text_color:
          type: string
          pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
          title: Text Color
          description: Text fill color as a hex string.
          default: '#ffffff'
          example: '#ffffff'
        text_align:
          type: string
          enum:
            - left
            - center
            - right
          title: Text Align
          description: Horizontal text alignment.
          default: center
        text_direction:
          type: string
          enum:
            - ltr
            - rtl
          title: Text Direction
          description: Text direction. `rtl` for right-to-left scripts (§4).
          default: ltr
        text_decoration:
          type: string
          enum:
            - none
            - underline
            - strikethrough
          title: Text Decoration
          description: >-
            Text decoration. Only `none` renders. `underline` is reachable only
            per word — `words[].style.underline` — and `strikethrough` has no
            renderer path at all; both are rejected here (422) rather than
            silently dropped. Draw a strike-through as a thin `shape` rectangle
            over the text.
          default: none
        tracking:
          type: number
          title: Tracking
          description: >-
            Letter spacing (tracking) in pixels (converted to the renderer's
            width-relative unit on the wire). Positive values add space between
            characters.
          default: 0
          example: 0
        tabular_numbers:
          type: boolean
          title: Tabular Numbers
          description: >-
            Tabular (monospaced) figures: every digit takes the same advance, so
            columns of numbers align across lines and a `counter` does not
            jitter as its value changes. The renderer's baked fonts carry the
            OpenType alternates; a font whose face lacks the feature gets
            synthesised tabular digits in the export only. Default `false` = the
            font's proportional figures.
          default: false
          example: false
        leading:
          type: number
          title: Leading
          description: >-
            Line height (leading) as a multiplier of font size. `1.2` = 20%
            taller than the font size.
          default: 1.2
          example: 1.2
        stroke_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Stroke Color
          description: >-
            Outline color. Set together with `stroke_width` to enable text
            outlines.
          example: '#000000'
        stroke_width:
          type: number
          title: Stroke Width
          description: >-
            Outline width, em-relative (fraction of `font_size` — e.g. `0.05` =
            5% of the font size). Has no effect unless `stroke_color` is also
            set.
          default: 0
        background_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Background Color
          description: >-
            Secondary colour. Fills the box behind the text when `background` is
            `true`, **and** is the highlight colour for `word_animation.style`
            `color` (the active word) and `box` (the box) — those two styles
            require it.
        background_opacity:
          type: number
          maximum: 100
          minimum: 0
          title: Background Opacity
          default: 100
        x_padding:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Padding
          default: 0
        y_padding:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Padding
          default: 0
        background:
          type: boolean
          title: Background
          description: >-
            Solid color box behind the full text block. Set `background_color`
            to choose the color.
          default: false
        background_style:
          type: string
          enum:
            - block
            - line_hard
            - line_round
          title: Background Style
          description: >-
            Shape of the `background` plate: `block` — one box behind the whole
            text; `line_hard` — a square-cornered box behind each line;
            `line_round` — a rounded box behind each line. Requires `background:
            true`. An animation that centres its words draws the `block` plate
            whichever style is set.
          default: block
        stroke:
          type: boolean
          title: Stroke
          description: >-
            Outline-only mode — draws the glyph stroke with no fill. Requires
            `stroke_color` and `stroke_width`. When `false`, setting
            `stroke_color` + `stroke_width` gives fill + stroke (outlined).
          default: false
        shadow:
          type: boolean
          title: Shadow
          description: Built-in drop shadow.
          default: false
        motion:
          items:
            $ref: '#/components/schemas/Motion'
          type: array
          title: Motion
          default: []
        word_animation:
          anyOf:
            - $ref: '#/components/schemas/WordAnimation'
            - type: 'null'
          description: >-
            Word-level animation style with per-word timestamps. When provided,
            this takes precedence over the `animation_preset` field.
          example:
            style: glow
            words:
              - end: 0.5
                start: 0
                text: Hello
        words:
          anyOf:
            - items:
                $ref: '#/components/schemas/WordSpec'
              type: array
            - type: 'null'
          title: Words
          description: >-
            Per-word timing and optional per-word style overrides (§4). Emitted
            as a top-level `words[]` array; use alongside
            `motion`/`animation_preset` for the animation. Editorial-emphasis
            captions = word timings + a styled keyword.
          example:
            - end: 3
              start: 0
              style:
                color: '#e8734a'
              word: launch
        counter:
          anyOf:
            - $ref: '#/components/schemas/ValueCounter'
            - type: 'null'
          description: >-
            Rewrite this text from an eased numeric sweep (§4). Takes precedence
            over countdown on the same element.
        glow:
          anyOf:
            - $ref: '#/components/schemas/TextGlow'
            - type: 'null'
          description: Soft glow around the glyphs (§4).
        text_wrap:
          type: string
          enum:
            - wrap
            - nowrap
          title: Text Wrap
          default: wrap
        animation_preset:
          anyOf:
            - type: string
            - type: 'null'
          title: Animation Preset
          description: >-
            Renderer text-animation name (e.g. `'typewriter'`) — a catalog text
            animation or a `custom_animations.text` entry. Runs for the whole
            element window with per-glyph timings computed by the renderer. Use
            `motion[]` (with `type`, `preset` or `custom`) for structured timing
            control — but not both. Ignored when `word_animation` is set.
      additionalProperties: false
      type: object
      required:
        - text
      title: ChildTextElement
      description: A text inside a frame; `width` is its wrap width.
    ChildImageElement:
      properties:
        lut_url:
          anyOf:
            - type: string
              maxLength: 2083
              minLength: 1
              format: uri
            - type: 'null'
          title: Lut Url
        lut_intensity:
          type: number
          maximum: 100
          minimum: 0
          title: Lut Intensity
          default: 100
        brightness:
          type: number
          title: Brightness
          description: >-
            Brightness, -100..100, applied as a 0..2 colour multiplier (0 = no
            change). `-100` multiplies by 0: the element renders pure black
            while keeping its alpha — a silhouette of the artwork. Combine a
            `brightness: -100` copy of an image with `blur`, reduced `opacity`
            and a small offset, stacked beneath the real one, to fake the
            content-shaped drop shadow `shadow_x`/`shadow_y` cannot produce.
          default: 0
        contrast:
          type: number
          title: Contrast
          default: 0
        saturation:
          type: number
          title: Saturation
          default: 0
        exposure:
          type: number
          title: Exposure
          default: 0
        sharpness:
          type: number
          title: Sharpness
          default: 0
        blur:
          type: number
          title: Blur
          default: 0
        noise:
          type: number
          title: Noise
          default: 0
        vignette:
          type: number
          title: Vignette
          default: 0
        hue_rotate:
          type: number
          title: Hue Rotate
          default: 0
        temperature:
          type: number
          title: Temperature
          description: >-
            White balance warm (+) / cool (−), −1..1; 0 = no-op. Applied as a
            channel matrix, after the classic brightness/contrast chain.
          default: 0
        tint:
          type: number
          title: Tint
          description: White balance magenta (+) / green (−), −1..1; 0 = no-op.
          default: 0
        vibrance:
          type: number
          title: Vibrance
          description: >-
            Saturation boost weighted toward muted colours, −1..1; 0 = no-op.
            Boosts flat colour without wrecking skin tones.
          default: 0
        highlights:
          type: number
          title: Highlights
          description: Luma-masked tone lift of the brightest range, −1..1; 0 = no-op.
          default: 0
        shadows:
          type: number
          title: Shadows
          description: Luma-masked tone lift of the darkest range, −1..1; 0 = no-op.
          default: 0
        crop_top:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Top
          default: 0
        crop_bottom:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Bottom
          default: 0
        crop_left:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Left
          default: 0
        crop_right:
          type: number
          maximum: 1
          minimum: 0
          title: Crop Right
          default: 0
        border_radius:
          anyOf:
            - type: integer
            - type: number
            - type: string
            - $ref: '#/components/schemas/CornerRadiiPx'
          title: Border Radius
          description: >-
            Corner radius in pixels — a single value, or per-corner `{top_left,
            top_right, bottom_right, bottom_left}` for asymmetric corners (e.g.
            a card rounded only on top). The renderer normalizes px to a 0–1
            fraction of the element's shorter side (≈ half the shorter side is
            fully rounded) and clamps to that range. For shape elements use
            `corner_radius` (already 0–1).
          default: 0
        border_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Border Color
        border_width:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Border Width
          description: >-
            Border thickness in pixels (converted to the renderer's relative
            border scale against the element's shorter side, so it survives
            resizes). The border outlines the element's border_radius-rounded
            quad — not the image's alpha silhouette.
          default: 0
        shadow_color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Shadow Color
        shadow_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Blur
          description: >-
            Shadow softness, 0–100 (renderer blur-strength scale, not pixels): 0
            = hard edge, 100 = maximum blur. Values are clamped to that range.
          default: 0
        shadow_x:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow X
          description: >-
            Horizontal shadow offset in pixels (converted to the renderer's
            relative offset scale). The shadow silhouette is the element's
            border_radius-rounded quad, not the content alpha — a cut-out PNG
            gets a card shadow, not an outline-shaped one. On a cut-out that
            reads as a floating rectangle (rotated with the element, which is
            the tell). For a shadow that follows the artwork, drop these fields
            and stack a second copy of the same image underneath at `brightness:
            -100` (a black silhouette, alpha preserved) with `blur` and reduced
            `opacity`, offset a few percent — see `brightness`.
          default: 0
        shadow_y:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Shadow Y
          description: Vertical shadow offset in pixels. See `shadow_x`.
          default: 0
        blend_mode:
          $ref: '#/components/schemas/BlendMode'
          default: none
        mask_shape:
          anyOf:
            - $ref: '#/components/schemas/MaskShape'
            - type: 'null'
        mask:
          anyOf:
            - $ref: '#/components/schemas/MaskConfig'
            - type: 'null'
        mask_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/MaskKeyframe'
              type: array
            - type: 'null'
          title: Mask Keyframes
        matte:
          anyOf:
            - $ref: '#/components/schemas/Matte'
            - type: 'null'
        backdrop_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Backdrop Blur
          description: >-
            Glass: the scene behind the element, Gaussian-blurred, fills its
            footprint under its translucent content (a shape's own silhouette, a
            video's or image's box with its corner radius and mask). A Gaussian
            sigma in canvas px, as CSS `backdrop-filter: blur()` takes it; the
            renderer caps it at 128. 0 is off.
          default: 0
        backdrop_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Backdrop Blur Keyframes
          description: >-
            Animate `backdrop_blur` over the timeline; replaces the static value
            while present.
        layer_blur:
          type: number
          maximum: 128
          minimum: 0
          title: Layer Blur
          description: >-
            Blurs the element itself as drawn: a Gaussian sigma in canvas px, as
            CSS `filter: blur()` takes it, spreading three sigma past its box.
            At most 128; 0 is off. Not on a shape yet (the editor's preview
            cannot draw it), and not with `blend_mode`, `backdrop_blur` or
            `matte`, beside which the renderer would draw the element without
            it.
          default: 0
        layer_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Layer Blur Keyframes
          description: >-
            Animate `layer_blur` over the timeline; replaces the static value
            while present.
        glass:
          anyOf:
            - $ref: '#/components/schemas/Glass'
            - type: 'null'
          description: >-
            A refracting glass edge: the scene behind bends inward near the
            element's outline. Pair it with `backdrop_blur` for frosted glass.
        x:
          anyOf:
            - type: number
            - type: 'null'
          title: X
          description: 'Left edge in design px from the frame''s left edge. Omitted: 0.'
        'y':
          anyOf:
            - type: number
            - type: 'null'
          title: 'Y'
          description: 'Top edge in design px from the frame''s top edge. Omitted: 0.'
        width:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Width
          description: 'Width in design px. Omitted: the frame''s width.'
        height:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Height
          description: >-
            Height in design px. Omitted: the frame's height (a text's: twice
            its `font_size`).
        aspect_ratio:
          anyOf:
            - type: number
            - type: 'null'
          title: Aspect Ratio
          description: >-
            **Accepted but ignored — the renderer never receives this field.**
            It does NOT constrain or correct the `width`/`height` box, so it
            cannot be used to stop a mismatched box from cropping the source;
            size the box per `width` instead. Kept for backward compatibility.
        x_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Anchor
          description: >-
            Pivot the element turns and scales about, across its own box: a
            percent, or design px from its left edge.
          default: 50%
        y_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Anchor
          description: >-
            Pivot across its own box's height: a percent, or design px from its
            top edge.
          default: 50%
        x_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Rotation
          default: 0°
        y_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Rotation
          default: 0°
        z_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Z Rotation
          default: 0°
        x_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Scale
          default: 100%
        y_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Scale
          default: 100%
        flip_horizontal:
          type: boolean
          title: Flip Horizontal
          default: false
        flip_vertical:
          type: boolean
          title: Flip Vertical
          default: false
        opacity:
          type: number
          maximum: 100
          minimum: 0
          title: Opacity
          default: 100
        z_index:
          anyOf:
            - type: integer
            - type: 'null'
          title: Z Index
          description: >-
            Stacking order among the frame's children (higher on top); ties draw
            in array order.
        clip:
          type: boolean
          title: Clip
          default: false
        color_overlay:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Color Overlay
          description: >-
            **Not supported by the renderer — setting this is rejected.** A
            strict submit returns `unsupported_feature` (422) and a dry-run
            reports it as a blocking violation; there is no native tint field to
            map it onto. To darken an element use `brightness` (`-100` is a
            black silhouette); to lay a colour over one, stack a `shape` with a
            flat or gradient `fill` above it.
        type:
          type: string
          const: image
          title: Type
          default: image
        id:
          type: string
          minLength: 1
          title: Id
          description: >-
            **Required.** Unique identifier for this image. An empty or missing
            ID causes the image to be silently skipped by the renderer.
          example: logo_01
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional human-readable label. Not used by the renderer.
        track:
          anyOf:
            - type: integer
              maximum: 255
              minimum: 0
            - type: 'null'
          title: Track
          description: >-
            Timeline track index (0–255). Informational only; not used by the
            renderer.
        time:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Time
          description: >-
            Start in seconds on the clock the frame gives its children (the
            composition's, unless a composite frame maps it; inside a component,
            0 is the instance's start). Omitted: the frame's start.
        visible:
          type: boolean
          title: Visible
          description: >-
            Set to `false` to skip this element without removing it from the
            request.
          default: true
        locked:
          type: boolean
          title: Locked
          description: Marks the element locked in editors. Not sent to the renderer.
          default: false
        duration:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Duration
          description: 'Seconds visible. Omitted: until the frame ends.'
        source_url:
          type: string
          maxLength: 2083
          minLength: 1
          format: uri
          title: Source Url
          description: >-
            URL of the image file (PNG, JPG, WebP, GIF, SVG). A `.svg` draws a
            subset: groups and the basic shapes and paths, filled and stroked
            with flat colours. An element that uses anything else (a `style`
            attribute, `clip-path`, `mask`, `filter`, `stroke-dasharray`, a
            gradient or `currentColor` paint, `<text>`, `<use>`, `<image>`) is
            not drawn, nor anything inside it; an SVG that would draw nothing at
            all is refused at submit. Set explicit colours and presentation
            attributes, or pre-rasterize to PNG.
          example: https://cdn.example.com/logo.png
        effects:
          items:
            $ref: '#/components/schemas/Effect'
          type: array
          title: Effects
          default: []
        motion:
          items:
            $ref: '#/components/schemas/Motion'
          type: array
          title: Motion
          default: []
      additionalProperties: false
      type: object
      required:
        - id
        - source_url
      title: ChildImageElement
      description: An image inside a frame.
    ChildShapeElement:
      properties:
        blend_mode:
          $ref: '#/components/schemas/BlendMode'
          default: none
        mask_shape:
          anyOf:
            - $ref: '#/components/schemas/MaskShape'
            - type: 'null'
        mask:
          anyOf:
            - $ref: '#/components/schemas/MaskConfig'
            - type: 'null'
        mask_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/MaskKeyframe'
              type: array
            - type: 'null'
          title: Mask Keyframes
        matte:
          anyOf:
            - $ref: '#/components/schemas/Matte'
            - type: 'null'
        backdrop_blur:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Backdrop Blur
          description: >-
            Glass: the scene behind the element, Gaussian-blurred, fills its
            footprint under its translucent content (a shape's own silhouette, a
            video's or image's box with its corner radius and mask). A Gaussian
            sigma in canvas px, as CSS `backdrop-filter: blur()` takes it; the
            renderer caps it at 128. 0 is off.
          default: 0
        backdrop_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Backdrop Blur Keyframes
          description: >-
            Animate `backdrop_blur` over the timeline; replaces the static value
            while present.
        layer_blur:
          type: number
          maximum: 128
          minimum: 0
          title: Layer Blur
          description: >-
            Blurs the element itself as drawn: a Gaussian sigma in canvas px, as
            CSS `filter: blur()` takes it, spreading three sigma past its box.
            At most 128; 0 is off. Not on a shape yet (the editor's preview
            cannot draw it), and not with `blend_mode`, `backdrop_blur` or
            `matte`, beside which the renderer would draw the element without
            it.
          default: 0
        layer_blur_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/BlurKeyframe'
              type: array
              minItems: 1
            - type: 'null'
          title: Layer Blur Keyframes
          description: >-
            Animate `layer_blur` over the timeline; replaces the static value
            while present.
        glass:
          anyOf:
            - $ref: '#/components/schemas/Glass'
            - type: 'null'
          description: >-
            A refracting glass edge: the scene behind bends inward near the
            element's outline. Pair it with `backdrop_blur` for frosted glass.
        x:
          anyOf:
            - type: number
            - type: 'null'
          title: X
          description: 'Left edge in design px from the frame''s left edge. Omitted: 0.'
        'y':
          anyOf:
            - type: number
            - type: 'null'
          title: 'Y'
          description: 'Top edge in design px from the frame''s top edge. Omitted: 0.'
        width:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Width
          description: 'Width in design px. Omitted: the frame''s width.'
        height:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Height
          description: >-
            Height in design px. Omitted: the frame's height (a text's: twice
            its `font_size`).
        aspect_ratio:
          anyOf:
            - type: number
            - type: 'null'
          title: Aspect Ratio
          description: >-
            **Accepted but ignored — the renderer never receives this field.**
            It does NOT constrain or correct the `width`/`height` box, so it
            cannot be used to stop a mismatched box from cropping the source;
            size the box per `width` instead. Kept for backward compatibility.
        x_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Anchor
          description: >-
            Pivot the element turns and scales about, across its own box: a
            percent, or design px from its left edge.
          default: 50%
        y_anchor:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Anchor
          description: >-
            Pivot across its own box's height: a percent, or design px from its
            top edge.
          default: 50%
        x_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Rotation
          default: 0°
        y_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Rotation
          default: 0°
        z_rotation:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Z Rotation
          default: 0°
        x_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: X Scale
          default: 100%
        y_scale:
          anyOf:
            - type: integer
            - type: number
            - type: string
          title: Y Scale
          default: 100%
        flip_horizontal:
          type: boolean
          title: Flip Horizontal
          default: false
        flip_vertical:
          type: boolean
          title: Flip Vertical
          default: false
        opacity:
          type: number
          maximum: 100
          minimum: 0
          title: Opacity
          default: 100
        z_index:
          anyOf:
            - type: integer
            - type: 'null'
          title: Z Index
          description: >-
            Stacking order among the frame's children (higher on top); ties draw
            in array order.
        clip:
          type: boolean
          title: Clip
          default: false
        color_overlay:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Color Overlay
          description: >-
            **Not supported by the renderer — setting this is rejected.** A
            strict submit returns `unsupported_feature` (422) and a dry-run
            reports it as a blocking violation; there is no native tint field to
            map it onto. To darken an element use `brightness` (`-100` is a
            black silhouette); to lay a colour over one, stack a `shape` with a
            flat or gradient `fill` above it.
        type:
          type: string
          const: shape
          title: Type
          default: shape
        id:
          type: string
          minLength: 1
          title: Id
          description: '**Required.** Unique identifier.'
          example: blob-1
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional human-readable label. Not used by the renderer.
        track:
          anyOf:
            - type: integer
              maximum: 255
              minimum: 0
            - type: 'null'
          title: Track
          description: >-
            Timeline track index (0–255). Informational only; not used by the
            renderer.
        time:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Time
          description: >-
            Start in seconds on the clock the frame gives its children (the
            composition's, unless a composite frame maps it; inside a component,
            0 is the instance's start). Omitted: the frame's start.
        visible:
          type: boolean
          title: Visible
          description: >-
            Set to `false` to skip this element without removing it from the
            request.
          default: true
        locked:
          type: boolean
          title: Locked
          description: Marks the element locked in editors. Not sent to the renderer.
          default: false
        duration:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Duration
          description: 'Seconds visible. Omitted: until the frame ends.'
        path:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Path
          description: >-
            SVG path `d` (M L H V C S Q T A Z; elliptical arcs render as arcs).
            **Required** unless `geometry` is set.
          example: M50 0 L100 100 L0 100 Z
        geometry:
          anyOf:
            - $ref: '#/components/schemas/ShapeGeometry'
            - type: 'null'
          description: >-
            Draw a closed-form shape instead of a `path`: exactly one of `rect`
            (`corner_radius` per corner, `smoothing`), `ellipse`, `line`, `ring`
            or `arc` (`inner_ratio`, `start_angle`, `sweep`, `cap`, `segments`),
            `polygon` (`sides`, `corner_radius`) or `star` (`points`,
            `inner_ratio`, `corner_radius`), plus `stroke_align` and `shadows`.
            Lengths are px, angles degrees clockwise from 3 o'clock. The box,
            `fill`, `stroke`, `stroke_width`, `stroke_dash`, motion, masks,
            mattes, blend modes and effects work as on any shape; `path`,
            `path_keyframes`, `trim`, `corner_radius` and `shadow` do not apply.
        view_box:
          items:
            type: number
          type: array
          maxItems: 4
          minItems: 4
          title: View Box
          description: '`[x, y, width, height]` path coordinate space.'
          default:
            - 0
            - 0
            - 100
            - 100
        fill:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: string
              const: none
            - type: string
              pattern: ^\s*(?:linear|radial)-gradient\s*\(
              description: >-
                CSS gradient shorthand — `linear-gradient(...)` or
                `radial-gradient(...)`. Normalised to the Gradient object during
                validation; the renderer never sees a CSS string, and reading
                the composition back returns the object.
              example: 'linear-gradient(90deg, #ff0000 0%, #0000ff 100%)'
            - $ref: '#/components/schemas/Gradient'
          title: Fill
          description: >-
            Fill: a hex colour (alpha ok), `"none"` for outline-only, a Gradient
            object (`{kind, angle_degrees, center, stops}`, ≥2 stops), or a CSS
            `linear-gradient(...)` / `radial-gradient(...)` string, which is
            normalised to the Gradient object at validation — read back, the
            composition holds the object. A gradient fill on a rectangular path
            is how you draw a gradient card, bar or pill — there is no separate
            `gradient` element. The gradient paints the shape's own path, so it
            fills any silhouette — a triangle, a blob, an icon outline, a
            morphing path — and layers on the same element with `stroke` /
            `stroke_width` / `stroke_dash`, `path_keyframes` (morph), `trim` /
            `trim_keyframes` (draw-on), `blend_mode`, `mask_shape` / `mask` /
            `mask_keyframes`, `matte` and `backdrop_blur`. `fill_rule`,
            `corner_radius` and `opacity` behave with a gradient exactly as they
            do with a flat colour.
          default: '#000000'
          example: 'linear-gradient(90deg, #e07a4f 0%, #7c3aed 100%)'
        fill_rule:
          type: string
          enum:
            - nonzero
            - evenodd
          title: Fill Rule
          default: nonzero
        stroke:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Stroke
          description: Stroke colour.
        stroke_width:
          anyOf:
            - type: number
              maximum: 100000
              minimum: 0
            - type: 'null'
          title: Stroke Width
          description: >-
            Stroke width in viewBox units, at most 100000 (absent = SVG 1.0; 0
            disables).
        stroke_linecap:
          type: string
          enum:
            - butt
            - round
            - square
          title: Stroke Linecap
          description: >-
            Cap on the open ends of a stroked path (SVG `stroke-linecap`):
            `butt` stops at the endpoint, `round` and `square` extend it by half
            the stroke width. Closed paths have joins, not caps.
          default: round
        stroke_linejoin:
          type: string
          enum:
            - miter
            - round
            - bevel
          title: Stroke Linejoin
          description: >-
            Corner join of a stroked path (SVG `stroke-linejoin`): `miter` is
            the sharp corner (subject to `stroke_miter_limit`), `bevel` cuts it
            flat.
          default: round
        stroke_miter_limit:
          type: number
          minimum: 1
          title: Stroke Miter Limit
          description: >-
            SVG `stroke-miterlimit`: miter length over stroke width. A `miter`
            join sharper than this falls back to `bevel`.
          default: 4
        stroke_dash:
          anyOf:
            - items:
                type: number
              type: array
              minItems: 1
            - type: 'null'
          title: Stroke Dash
          description: >-
            Dash pattern in viewBox units, SVG `stroke-dasharray` semantics:
            `[on, off, ...]` of any length — an odd-length array repeats so
            on/off alternate (`[4]` is 4 on, 4 off). Trim applies before dash,
            so a dashed path can draw itself on without the pattern re-flowing.
        stroke_dash_offset:
          type: number
          title: Stroke Dash Offset
          description: >-
            Where the dash pattern starts along the path, in viewBox units (SVG
            `stroke-dashoffset`): positive shifts the pattern forward, negative
            backward.
          default: 0
        stroke_dash_offset_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/DashOffsetKeyframe'
              type: array
            - type: 'null'
          title: Stroke Dash Offset Keyframes
          description: >-
            Animate the dash offset (timeline seconds) — marching ants: keyframe
            `offset` linearly by one dash period (the pattern's sum) per second.
            Needs a `stroke_dash`; while present it drives the offset in place
            of the static `stroke_dash_offset`.
        stroke_marker_start:
          type: string
          enum:
            - none
            - triangle
            - open_chevron
            - dot
          title: Stroke Marker Start
          description: >-
            A head at the start of each open subpath, in the stroke's colour
            (render-node V2.6): `triangle` is filled, its tip on the endpoint
            (the shaft shortens to meet it); `open_chevron` is an open head
            stroked like the path; `dot` is a disc. Placed after the trim, so a
            draw-on keeps its head at the visible end, and left out of the dash.
            A closed subpath takes none. Needs a `stroke`; path shapes only.
          default: none
        stroke_marker_end:
          type: string
          enum:
            - none
            - triangle
            - open_chevron
            - dot
          title: Stroke Marker End
          description: A head at the end of each open subpath; see `stroke_marker_start`.
          default: none
        stroke_marker_size:
          type: number
          maximum: 32
          minimum: 1
          title: Stroke Marker Size
          description: >-
            Marker length in stroke widths, 1–32 (4 is four times
            `stroke_width`).
          default: 4
        corner_radius:
          anyOf:
            - type: number
              maximum: 1
              minimum: 0
            - $ref: '#/components/schemas/CornerRadii'
          title: Corner Radius
          description: >-
            Corner radius, normalized 0–1 — a single value or per-corner
            `{top_left, top_right, bottom_right, bottom_left}`.
          default: 0
        shadow:
          anyOf:
            - $ref: '#/components/schemas/DropShadow'
            - type: 'null'
          description: >-
            Optional drop shadow. `distance`/`blur` are the renderer's 0–100
            strength scale, not pixels — see DropShadow.
        path_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/PathKeyframe'
              type: array
            - type: 'null'
          title: Path Keyframes
          description: Morph the path between keyframes (timeline seconds).
        trim:
          anyOf:
            - $ref: '#/components/schemas/TrimPath'
            - type: 'null'
          description: >-
            Static trim window: keep only this arc-length fraction of each
            subpath.
        trim_keyframes:
          anyOf:
            - items:
                $ref: '#/components/schemas/TrimKeyframe'
              type: array
            - type: 'null'
          title: Trim Keyframes
          description: >-
            Animate the trim window (timeline seconds) — the draw-on: animate
            `end` 0→1 on a stroked path to draw a signature, underline or chart
            line. Absent `start`/`end` inherit the static `trim`.
        effects:
          items:
            $ref: '#/components/schemas/Effect'
          type: array
          title: Effects
          description: Shader effect chain (§6 W3).
          default: []
        motion:
          items:
            $ref: '#/components/schemas/Motion'
          type: array
          title: Motion
          default: []
      additionalProperties: false
      type: object
      required:
        - id
      title: ChildShapeElement
      description: A shape inside a frame.
    TransitionType:
      type: string
      enum:
        - cross_dissolve
        - dip_to_black
        - dip_to_white
        - wipe_left
        - wipe_right
        - wipe_up
        - wipe_down
        - diagonal_wipe
        - barn_doors_horizontal
        - barn_doors_vertical
        - iris
        - page_turn
        - cross_zoom
        - gradient_wipe
        - band_wipe
        - box_wipe
        - swirl
        - glitch_memories
        - window_slice
        - cube
        - doorway
        - pinwheel
        - water_drop
        - crosshatch
        - dreamy
        - angular
        - burn
        - heart
        - circle_open
        - color_phase
        - squares_wire
        - whip_pan
        - cinematic_zoom
        - burn_edge
        - shatter
        - domain_warp
        - circle_crop
        - cross_warp
        - fold_horizontal
        - fold_vertical
        - linear_blur
        - minimise_bottom_left
        - minimise_bottom_right
        - minimise_top_left
        - minimise_top_right
        - ripple
        - rotate
        - splice
        - three_stripes
      title: TransitionType
      description: >-
        Values:

        - `cross_dissolve` — Cross-dissolve — fades one clip into the next.

        - `dip_to_black` — Dips through black between clips.

        - `dip_to_white` — Dips through white between clips.

        - `wipe_left` — Wipes the next clip in from the right to the left.

        - `wipe_right` — Wipes the next clip in from the left to the right.

        - `wipe_up` — Wipes the next clip in from the bottom upward.

        - `wipe_down` — Wipes the next clip in from the top downward.

        - `diagonal_wipe` — Wipes across on a diagonal split.

        - `barn_doors_horizontal` — Two halves part horizontally like barn
        doors.

        - `barn_doors_vertical` — Two halves part vertically like barn doors.

        - `iris` — Circular iris (bullseye) open/close between clips.

        - `page_turn` — Page-flip / page-curl reveal.

        - `cross_zoom` — Zoom-blur push from one clip into the next.

        - `gradient_wipe` — Soft gradient-driven dissolve wipe.

        - `band_wipe` — Interleaved stripes wipe the next clip in.

        - `box_wipe` — Rectangular box grows to reveal the next clip.

        - `swirl` — Swirls the outgoing clip away in a spiral.

        - `glitch_memories` — Glitchy, memory-like distortion between clips.

        - `window_slice` — Slices the frame into sliding window strips.

        - `cube` — 3D cube rotation between the two clips.

        - `doorway` — Clips part like opening doors to reveal the next.

        - `pinwheel` — Pinwheel rotation wipe between clips.

        - `water_drop` — Rippling water-drop distortion reveals the next clip.

        - `crosshatch` — Crosshatch pattern dissolve between clips.

        - `dreamy` — Soft, dreamy blur-dissolve between clips.

        - `angular` — Angular wedge sweep between clips.

        - `burn` — Burning-film dissolve between clips.

        - `heart` — Heart-shaped iris reveal of the next clip.

        - `circle_open` — Expanding circle opens onto the next clip.

        - `color_phase` — Color-phase shift blends between clips.

        - `squares_wire` — Wireframe squares wipe between clips.

        - `whip_pan` — Directional motion-blur whip pan.

        - `cinematic_zoom` — Punch zoom with radial blur + chromatic fringing.

        - `burn_edge` — Noise-ridged burn front with ember glow and sparks.

        - `shatter` — Jittered shards fly apart revealing the incoming clip.

        - `domain_warp` — Organic noise-warped dissolve with an emissive
        frontier.

        - `circle_crop` — Both clips crop to a shrinking/growing circle through
        the cut.

        - `cross_warp` — Clips warp into each other along a dissolving
        threshold.

        - `fold_horizontal` — Outgoing clip folds away horizontally, revealing
        the incoming.

        - `fold_vertical` — Outgoing clip folds away vertically, revealing the
        incoming.

        - `linear_blur` — Cross-dissolve through a heavy directional blur peak.

        - `minimise_bottom_left` — Outgoing clip shrinks away into the
        bottom-left corner.

        - `minimise_bottom_right` — Outgoing clip shrinks away into the
        bottom-right corner.

        - `minimise_top_left` — Outgoing clip shrinks away into the top-left
        corner.

        - `minimise_top_right` — Outgoing clip shrinks away into the top-right
        corner.

        - `ripple` — Water-ripple distortion sweeps the cut across the frame.

        - `rotate` — Both clips rotate about the frame centre through the cut.

        - `splice` — Angled splice slides the incoming clip over the outgoing.

        - `three_stripes` — Three offset stripes sweep the incoming clip in.
      x-enumDescriptions:
        cross_dissolve: Cross-dissolve — fades one clip into the next.
        dip_to_black: Dips through black between clips.
        dip_to_white: Dips through white between clips.
        wipe_left: Wipes the next clip in from the right to the left.
        wipe_right: Wipes the next clip in from the left to the right.
        wipe_up: Wipes the next clip in from the bottom upward.
        wipe_down: Wipes the next clip in from the top downward.
        diagonal_wipe: Wipes across on a diagonal split.
        barn_doors_horizontal: Two halves part horizontally like barn doors.
        barn_doors_vertical: Two halves part vertically like barn doors.
        iris: Circular iris (bullseye) open/close between clips.
        page_turn: Page-flip / page-curl reveal.
        cross_zoom: Zoom-blur push from one clip into the next.
        gradient_wipe: Soft gradient-driven dissolve wipe.
        band_wipe: Interleaved stripes wipe the next clip in.
        box_wipe: Rectangular box grows to reveal the next clip.
        swirl: Swirls the outgoing clip away in a spiral.
        glitch_memories: Glitchy, memory-like distortion between clips.
        window_slice: Slices the frame into sliding window strips.
        cube: 3D cube rotation between the two clips.
        doorway: Clips part like opening doors to reveal the next.
        pinwheel: Pinwheel rotation wipe between clips.
        water_drop: Rippling water-drop distortion reveals the next clip.
        crosshatch: Crosshatch pattern dissolve between clips.
        dreamy: Soft, dreamy blur-dissolve between clips.
        angular: Angular wedge sweep between clips.
        burn: Burning-film dissolve between clips.
        heart: Heart-shaped iris reveal of the next clip.
        circle_open: Expanding circle opens onto the next clip.
        color_phase: Color-phase shift blends between clips.
        squares_wire: Wireframe squares wipe between clips.
        whip_pan: Directional motion-blur whip pan.
        cinematic_zoom: Punch zoom with radial blur + chromatic fringing.
        burn_edge: Noise-ridged burn front with ember glow and sparks.
        shatter: Jittered shards fly apart revealing the incoming clip.
        domain_warp: Organic noise-warped dissolve with an emissive frontier.
        circle_crop: Both clips crop to a shrinking/growing circle through the cut.
        cross_warp: Clips warp into each other along a dissolving threshold.
        fold_horizontal: Outgoing clip folds away horizontally, revealing the incoming.
        fold_vertical: Outgoing clip folds away vertically, revealing the incoming.
        linear_blur: Cross-dissolve through a heavy directional blur peak.
        minimise_bottom_left: Outgoing clip shrinks away into the bottom-left corner.
        minimise_bottom_right: Outgoing clip shrinks away into the bottom-right corner.
        minimise_top_left: Outgoing clip shrinks away into the top-left corner.
        minimise_top_right: Outgoing clip shrinks away into the top-right corner.
        ripple: Water-ripple distortion sweeps the cut across the frame.
        rotate: Both clips rotate about the frame centre through the cut.
        splice: Angled splice slides the incoming clip over the outgoing.
        three_stripes: Three offset stripes sweep the incoming clip in.
    Point:
      properties:
        x:
          type: number
          title: X
          default: 0.5
        'y':
          type: number
          title: 'Y'
          default: 0.5
      additionalProperties: false
      type: object
      title: Point
    StackLayout:
      properties:
        type:
          type: string
          const: stack
          title: Type
          default: stack
        direction:
          type: string
          enum:
            - row
            - column
          title: Direction
          default: row
        gap:
          type: number
          title: Gap
          description: Gap between members, normalized along the main axis.
          default: 0
        align:
          type: string
          enum:
            - start
            - center
            - end
          title: Align
          default: center
        origin:
          anyOf:
            - $ref: '#/components/schemas/Point'
            - type: 'null'
          description: Centre of the whole stack.
      additionalProperties: false
      type: object
      title: StackLayout
      description: >-
        Resolve members into a row/column stack (§5, A2): the groups alias's
        stack, placed as

        before. A `frame` has no layout of its own (the host's, render-node
        O27).
    OperationMarker:
      properties:
        name:
          type: string
          minLength: 1
          title: Name
          example: beat
        time:
          type: number
          title: Time
          description: Seconds on the composition clock.
          example: 1.5
      additionalProperties: false
      type: object
      required:
        - name
        - time
      title: OperationMarker
      description: A named time a `marker` anchor reads.
    OperationGroup:
      properties:
        id:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Id
          description: A stable id for editors.
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: A label, e.g. `Animate in`.
        role:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Role
          description: >-
            What the group is for in editors (`in`, `out`, `loop`, …). Not sent
            to the renderer.
        anchor:
          anyOf:
            - $ref: '#/components/schemas/OperationAnchor'
            - type: 'null'
          description: >-
            Where its times count from. Required in the request's `operations`;
            a component's groups default to `instance.start`.
        ops:
          items:
            oneOf:
              - $ref: '#/components/schemas/Operation'
              - $ref: '#/components/schemas/PresetOperation'
          type: array
          title: Ops
          description: >-
            Operations and presets in document order: at equal starts the later
            replace wins.
      additionalProperties: false
      type: object
      required:
        - ops
      title: OperationGroup
      description: Operations sharing one anchor (GVR §4.3).
    ComponentProperty:
      properties:
        name:
          type: string
          pattern: ^[A-Za-z][A-Za-z0-9_]*$
          title: Name
          description: A letter, then letters, digits or `_`.
          example: title
        type:
          type: string
          enum:
            - text
            - colour
            - number
            - boolean
            - media
            - component
            - variant
          title: Type
          description: >-
            `text`, `colour` (`#RRGGBB` or `#RRGGBBAA`), `number`, `boolean`,
            `media` (a URL), `component` (an id from `allowed`) or `variant` (a
            nested instance's).
        bind:
          items:
            type: string
          type: array
          minItems: 1
          title: Bind
          description: >-
            Body fields it writes, as `<child id>.<field>` in this API's field
            names: any element's `x`, `y`, `z_rotation`, `opacity` and
            `visible`; a frame's or instance's `scale` and `clip`; a text's
            `text`, `text_color` and `font_size`; a shape's `fill`, `stroke` and
            `stroke_width`; an image's or video's `source_url`; an instance's
            `component`, `variant` and `properties.<name>`.
          example:
            - title.text
        default:
          anyOf:
            - type: boolean
            - type: integer
            - type: number
            - type: string
            - type: 'null'
          title: Default
          description: 'Its value when an instance sets none. Omitted: the body''s own.'
        min:
          anyOf:
            - type: number
            - type: 'null'
          title: Min
          description: A `number` property's lowest value.
        max:
          anyOf:
            - type: number
            - type: 'null'
          title: Max
          description: A `number` property's highest value.
        allowed:
          anyOf:
            - items:
                type: string
              type: array
              minItems: 1
            - type: 'null'
          title: Allowed
          description: >-
            The component ids a `component` property may swap in. Required on
            one.
      additionalProperties: false
      type: object
      required:
        - name
        - type
        - bind
      title: ComponentProperty
      description: >-
        A typed property an instance sets, written into the body fields it
        binds.
    ComponentVariant:
      properties:
        name:
          type: string
          minLength: 1
          title: Name
          example: compact
        width:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Width
          description: 'Its box width in design px. Omitted: the component''s.'
        height:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Height
          description: 'Its box height in design px. Omitted: the component''s.'
        children:
          items:
            oneOf:
              - $ref: '#/components/schemas/ChildVideoElement'
              - $ref: '#/components/schemas/ChildTextElement'
              - $ref: '#/components/schemas/ChildImageElement'
              - $ref: '#/components/schemas/ChildShapeElement'
              - $ref: '#/components/schemas/FrameElement'
              - $ref: '#/components/schemas/InstanceElement'
            discriminator:
              propertyName: type
              mapping:
                frame: '#/components/schemas/FrameElement'
                image: '#/components/schemas/ChildImageElement'
                instance: '#/components/schemas/InstanceElement'
                shape: '#/components/schemas/ChildShapeElement'
                text: '#/components/schemas/ChildTextElement'
                video: '#/components/schemas/ChildVideoElement'
          type: array
          title: Children
          description: Its body, in design px from the box's top-left corner.
          default: []
        operations:
          anyOf:
            - $ref: '#/components/schemas/OperationsBlock'
            - type: 'null'
          description: >-
            Its operations; groups count from `instance.start` unless they say
            otherwise.
      additionalProperties: false
      type: object
      required:
        - name
      title: ComponentVariant
      description: One of a component's bodies; an instance picks it by name.
    GradientStop:
      properties:
        offset:
          type: number
          maximum: 1
          minimum: 0
          title: Offset
          description: Position along the gradient, 0–1.
        color:
          type: string
          pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
          title: Color
          description: Stop colour (hex, alpha supported).
          example: '#e07a4f'
      additionalProperties: false
      type: object
      required:
        - offset
        - color
      title: GradientStop
    CustomAnimation:
      properties:
        name:
          type: string
          minLength: 1
          title: Name
          description: Reference name (reserved/catalog-shadowing names are rejected).
        keyframes:
          items:
            $ref: '#/components/schemas/AnimationKeyframe'
          type: array
          minItems: 1
          title: Keyframes
        group:
          type: string
          enum:
            - block
            - word
            - glyph
          title: Group
          description: >-
            What the animation repeats over: the whole text block, each word, or
            each glyph. Required for `selector` to shape anything — a selector
            over a single block has nothing to range across. Text registries
            only.
          default: block
        selector:
          anyOf:
            - $ref: '#/components/schemas/RangeSelector'
            - type: 'null'
          description: >-
            AE-style range selector: shape this animation's amount by position
            along the text (§4, W2). Set `group` to `word` or `glyph` alongside
            it. Text registries only.
      additionalProperties: false
      type: object
      required:
        - name
        - keyframes
      title: CustomAnimation
      description: >-
        A task-supplied keyframe animation, referenced by `name` from an
        animation entry (§3).
    SpringEasing:
      properties:
        stiffness:
          type: number
          exclusiveMinimum: 0
          title: Stiffness
          description: Stiffness; higher lowers zeta, so the spring rings more.
          default: 100
        damping:
          type: number
          exclusiveMinimum: 0
          title: Damping
          description: Damping; higher raises zeta (less overshoot), lower rings longer.
          default: 10
        mass:
          type: number
          exclusiveMinimum: 0
          title: Mass
          description: >-
            Mass; like stiffness, higher lowers zeta, so the spring rings more.
            It does not swing slower: the segment's duration sets the timing.
          default: 1
      additionalProperties: false
      type: object
      title: SpringEasing
      description: >-
        Damped-spring ease over the keyframe segment.


        Only the damping ratio zeta = damping / (2 * sqrt(stiffness * mass))
        shapes the

        curve; the segment's duration sets its timing. Below 1 the spring
        overshoots and

        rings (the defaults give zeta = 0.5); at 1 or above it settles without
        overshoot.

        An underdamped spring runs until its envelope decays to 0.1% (settle x
        w0 =

        ln(1000) / zeta, w0 = sqrt(stiffness / mass)). Since render-node's
        overdamped settle

        fix (GVR M3), a spring at zeta >= 1 runs over its slowest decay rate
        instead; before

        it, an overdamped spring's window was cut short and it reached the
        target still

        moving.
    BackEasing:
      properties:
        overshoot:
          type: number
          title: Overshoot
          description: Overshoot amount; AE default 1.70158.
          default: 1.70158
        dir:
          type: string
          enum:
            - in
            - out
          title: Dir
          description: '''in'' anticipates, ''out'' overshoots.'
          default: out
      additionalProperties: false
      type: object
      title: BackEasing
      description: Anticipate/overshoot ease (AE 'back').
    ElasticEasing:
      properties:
        amplitude:
          type: number
          title: Amplitude
          description: Ring amplitude.
          default: 1
        period:
          type: number
          exclusiveMinimum: 0
          title: Period
          description: Ring period as a fraction of the segment.
          default: 0.3
        dir:
          type: string
          enum:
            - in
            - out
          title: Dir
          description: '''in'' rings before, ''out'' after.'
          default: out
      additionalProperties: false
      type: object
      title: ElasticEasing
      description: Elastic ring-out/ring-in ease.
    BezierEasing:
      properties:
        x1:
          type: number
          title: X1
          default: 0
        y1:
          type: number
          title: Y1
          default: 0
        x2:
          type: number
          title: X2
          default: 1
        y2:
          type: number
          title: Y2
          default: 1
      additionalProperties: false
      type: object
      title: BezierEasing
      description: CSS cubic-bezier(x1, y1, x2, y2) timing curve.
    EffectType:
      type: string
      enum:
        - vintage
        - polaroid
        - portra
        - super8
        - filmic
        - add_grain
        - rgb_split
        - ghosting
        - displacement_map
        - posterize
        - mosaic
        - mosaic_blur
        - mosaic_posterize
        - cc_halftone
        - cc_halftone_blue
        - cc_halftone_green
        - cc_halftone_red
        - invert
        - optics_compensation
        - viewfinder
        - night_vision
        - thermal
        - camera_lens_blur
        - camera_lens_blur_bg
        - box_blur
        - lens_flare
        - strobe_light
        - snow
        - glow
        - sepia
        - echo
        - chromatic_aberration
        - vhs
        - vhs_overlay
        - crt
        - television
        - glitch
        - compression_glitch
        - scanlines
        - prism
        - light_leaks
        - film_burn
        - duotone
        - cross_process
        - bleach_bypass
        - chroma_key
        - aurora
      title: EffectType
      description: >-
        Values:

        - `vintage` — Faded, warm retro film look with lifted blacks.

        - `polaroid` — Instant-photo look — soft contrast, warm cast, subtle
        vignette.

        - `portra` — Kodak Portra-style film emulation with natural skin tones.

        - `super8` — Super-8 home-movie look — grain, gate weave, warm color.

        - `filmic` — Cinematic film grade with rolled-off highlights and rich
        contrast.

        - `add_grain` — Adds dusty analog film grain over the image.

        - `rgb_split` — Offsets the red/green/blue channels for a glitchy
        chromatic split.

        - `ghosting` — Trailing motion echoes, like persistence-of-vision
        smearing.

        - `displacement_map` — Warps the image with a displacement texture for a
        melted look.

        - `posterize` — Reduces the image to a small number of flat color bands.

        - `mosaic` — Pixelates the image into large mosaic blocks.

        - `mosaic_blur` — Pixelated mosaic with softened, blurred block edges.

        - `mosaic_posterize` — Combines mosaic pixelation with posterized
        colors.

        - `cc_halftone` — Comic-style halftone dot pattern.

        - `cc_halftone_blue` — Halftone dot pattern tinted blue.

        - `cc_halftone_green` — Halftone dot pattern tinted green.

        - `cc_halftone_red` — Halftone dot pattern tinted red.

        - `invert` — Inverts all colors (photo-negative).

        - `optics_compensation` — Fish-eye / lens-distortion warp.

        - `viewfinder` — Camera viewfinder overlay (REC indicator, framing
        marks).

        - `night_vision` — Green night-vision look with glow and noise.

        - `thermal` — Thermal-camera false-color heat map.

        - `camera_lens_blur` — Realistic bokeh lens blur across the frame.

        - `camera_lens_blur_bg` — Bokeh lens blur applied to the background
        only.

        - `box_blur` — Fast, uniform box blur.

        - `lens_flare` — Adds an anamorphic lens-flare streak and glints.

        - `strobe_light` — Rapid brightness strobing / flashing.

        - `snow` — Falling-snow particle overlay.

        - `glow` — Dreamy soft-glow bloom over highlights.

        - `sepia` — Sepia-toned monochrome with a vignette.

        - `echo` — Soft ghosted dream-echo overlay.

        - `chromatic_aberration` — Color-fringing at edges, like cheap-lens
        dispersion.

        - `vhs` — VHS tape look: tracking noise, color bleed, and wobble.

        - `vhs_overlay` — VHS noise/scanline layer composited over the source.

        - `crt` — CRT monitor look: screen curvature, scanlines, and phosphor
        glow.

        - `television` — Analog TV look: scanlines, static, and signal
        distortion.

        - `glitch` — Digital glitch: block displacement and channel tearing.

        - `compression_glitch` — MPEG compression artifacts: blocky macroblocks
        and datamosh.

        - `scanlines` — Horizontal scanlines overlaid on the image.

        - `prism` — Prismatic light refraction with a rainbow color split.

        - `light_leaks` — Warm light-leak streaks bleeding across the frame.

        - `film_burn` — Burning-film look with scorched edges and flares.

        - `duotone` — Two-tone color map (shadows to one hue, highlights to
        another).

        - `cross_process` — Cross-processed film look with shifted, punchy
        colors.

        - `bleach_bypass` — High-contrast, desaturated bleach-bypass film grade.

        - `chroma_key` — Keys out a color (green/blue screen); tune via
        `chroma_settings`.

        - `aurora` — Animated domain-warped fluid-gradient wash (§6); tune flow
        via `props` speed.
      x-enumDescriptions:
        vintage: Faded, warm retro film look with lifted blacks.
        polaroid: Instant-photo look — soft contrast, warm cast, subtle vignette.
        portra: Kodak Portra-style film emulation with natural skin tones.
        super8: Super-8 home-movie look — grain, gate weave, warm color.
        filmic: Cinematic film grade with rolled-off highlights and rich contrast.
        add_grain: Adds dusty analog film grain over the image.
        rgb_split: Offsets the red/green/blue channels for a glitchy chromatic split.
        ghosting: Trailing motion echoes, like persistence-of-vision smearing.
        displacement_map: Warps the image with a displacement texture for a melted look.
        posterize: Reduces the image to a small number of flat color bands.
        mosaic: Pixelates the image into large mosaic blocks.
        mosaic_blur: Pixelated mosaic with softened, blurred block edges.
        mosaic_posterize: Combines mosaic pixelation with posterized colors.
        cc_halftone: Comic-style halftone dot pattern.
        cc_halftone_blue: Halftone dot pattern tinted blue.
        cc_halftone_green: Halftone dot pattern tinted green.
        cc_halftone_red: Halftone dot pattern tinted red.
        invert: Inverts all colors (photo-negative).
        optics_compensation: Fish-eye / lens-distortion warp.
        viewfinder: Camera viewfinder overlay (REC indicator, framing marks).
        night_vision: Green night-vision look with glow and noise.
        thermal: Thermal-camera false-color heat map.
        camera_lens_blur: Realistic bokeh lens blur across the frame.
        camera_lens_blur_bg: Bokeh lens blur applied to the background only.
        box_blur: Fast, uniform box blur.
        lens_flare: Adds an anamorphic lens-flare streak and glints.
        strobe_light: Rapid brightness strobing / flashing.
        snow: Falling-snow particle overlay.
        glow: Dreamy soft-glow bloom over highlights.
        sepia: Sepia-toned monochrome with a vignette.
        echo: Soft ghosted dream-echo overlay.
        chromatic_aberration: Color-fringing at edges, like cheap-lens dispersion.
        vhs: 'VHS tape look: tracking noise, color bleed, and wobble.'
        vhs_overlay: VHS noise/scanline layer composited over the source.
        crt: 'CRT monitor look: screen curvature, scanlines, and phosphor glow.'
        television: 'Analog TV look: scanlines, static, and signal distortion.'
        glitch: 'Digital glitch: block displacement and channel tearing.'
        compression_glitch: 'MPEG compression artifacts: blocky macroblocks and datamosh.'
        scanlines: Horizontal scanlines overlaid on the image.
        prism: Prismatic light refraction with a rainbow color split.
        light_leaks: Warm light-leak streaks bleeding across the frame.
        film_burn: Burning-film look with scorched edges and flares.
        duotone: Two-tone color map (shadows to one hue, highlights to another).
        cross_process: Cross-processed film look with shifted, punchy colors.
        bleach_bypass: High-contrast, desaturated bleach-bypass film grade.
        chroma_key: Keys out a color (green/blue screen); tune via `chroma_settings`.
        aurora: >-
          Animated domain-warped fluid-gradient wash (§6); tune flow via `props`
          speed.
    IntensityKeyframe:
      properties:
        time:
          type: number
          minimum: 0
          title: Time
          description: Absolute timeline seconds.
        value:
          type: number
          maximum: 1
          minimum: 0
          title: Value
          description: Effect intensity 0–1 at this time.
      additionalProperties: false
      type: object
      required:
        - time
        - value
      title: IntensityKeyframe
      description: >-
        A point on a keyframed effect-intensity ramp (§2). Absolute timeline
        seconds.
    ChromaKeyProps:
      properties:
        key_color:
          type: string
          pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
          title: Key Color
          description: The colour to key out. Green-screen green by default.
          default: '#00FF00'
        threshold:
          type: number
          maximum: 1
          minimum: 0
          title: Threshold
          description: >-
            Chroma distance from `key_color` below which a pixel is fully
            transparent. Raise it if fringes of the screen survive.
          default: 0.1
        smoothness:
          type: number
          maximum: 1
          minimum: 0
          title: Smoothness
          description: >-
            Width of the ramp from transparent to opaque, starting at
            `threshold`. Raise it to soften hard edges; 0 gives a hard cut.
          default: 0.15
        spill_suppress:
          type: number
          maximum: 1
          minimum: 0
          title: Spill Suppress
          description: >-
            How strongly surviving pixels near the key colour are pulled toward
            their own luma, removing green spill on hair and edges. 0 disables.
          default: 0.5
      additionalProperties: false
      type: object
      title: ChromaKeyProps
      description: >-
        Used when ``EffectType.CHROMA_KEY`` is selected.


        Keys against an explicit key **colour** in the YCbCr chroma plane, which
        is

        luma-independent, so shadows falling on the screen key out along with
        it.


        This replaced an hue/saturation/luminance-window shape that could not
        work. It

        was wired to ``effect_chroma.frag``, whose ``sensitivity`` is

        ``(hueMax - hueMin) * 0.5`` compared against an Oklab distance bounded
        around

        0..0.9 — so the old defaults (60/180) produced a sensitivity of 60,
        discarded

        every pixel, and made the element vanish from the render. Nothing could
        have

        depended on the previous shape, because nothing rendered with it.
    EffectShader:
      properties:
        id:
          type: string
          minLength: 1
          title: Id
          description: >-
            The shader's id (`sh_...`): one of this workspace's or a shared one
            (GET /v1/shaders).
          example: sh_01J8QR2K5VKDGN2T4FBM3CZYX7
        params:
          additionalProperties:
            anyOf:
              - type: number
              - type: string
              - items:
                  type: number
                type: array
                maxItems: 2
                minItems: 2
              - items:
                  $ref: '#/components/schemas/ShaderParamKeyframe'
                type: array
                minItems: 1
          type: object
          title: Params
          description: >-
            Values of the shader's declared params by name: a number (`float`),
            "#RRGGBBAA" (`color`) or [x, y] (`vec2`), or keyframes `[{time,
            value}]` in timeline seconds. A param left out takes its declared
            default.
          default: {}
      additionalProperties: false
      type: object
      required:
        - id
      title: EffectShader
      description: >-
        A runtime shader (render-node AEC §2) by its stored id. `POST
        /v1/shaders` compiles one

        from SkSL source; the render carries its compiled variants, so it
        renders on the farm and

        in the preview alike.
    MotionType:
      type: string
      enum:
        - identity
        - fade
        - slide_up
        - slide_down
        - slide_left
        - slide_right
        - zoom_in
        - zoom_out
        - rotate_cw
        - rotate_ccw
        - bounce
        - wipe_left
        - wipe_right
        - wipe_up
        - wipe_down
        - ken_burns_in
        - ken_burns_out
        - ken_burns_in_out
        - loop_wiggle
        - loop_rotate
        - loop_rotate_smooth
        - loop_3d_spin
        - loop_3d_sway
        - blur
        - evaporate
        - overlay
        - difference
        - rubber_in
        - whip_up
        - whip_down
        - glitch_pop
        - drift_in
        - drift_out
        - loop_breathe
        - loop_shimmer
        - swing_in
        - swing_out
        - elastic_rise
        - elastic_drop
        - tilt_zoom
        - loop_orbit
        - smooth_pop
      title: MotionType
      description: >-
        Values:

        - `identity` — Deliberate no-op carrier (no visible motion): attach a
        pure `audio` generator to any element without animating it. Element
        surfaces only.

        - `fade` — Fade in (entrance) or out (exit) via opacity.

        - `slide_up` — Slide in from below / out upward.

        - `slide_down` — Slide in from above / out downward.

        - `slide_left` — Slide in from the right / out to the left.

        - `slide_right` — Slide in from the left / out to the right.

        - `zoom_in` — Scale up from small to full size (entrance).

        - `zoom_out` — Scale down to small (exit).

        - `rotate_cw` — Spin in/out clockwise.

        - `rotate_ccw` — Spin in/out counter-clockwise.

        - `bounce` — Bounce in with an elastic overshoot / bounce out.

        - `wipe_left` — Reveal/conceal with a left-moving wipe.

        - `wipe_right` — Reveal/conceal with a right-moving wipe.

        - `wipe_up` — Reveal/conceal with an upward wipe.

        - `wipe_down` — Reveal/conceal with a downward wipe.

        - `ken_burns_in` — Slow zoom-in pan (Ken Burns) — entrance only.

        - `ken_burns_out` — Slow zoom-out pan (Ken Burns) — entrance only.

        - `ken_burns_in_out` — Slow zoom in then out (Ken Burns) — entrance
        only.

        - `loop_wiggle` — Continuous subtle wiggle (looping).

        - `loop_rotate` — Continuous stepped rotation (looping).

        - `loop_rotate_smooth` — Continuous smooth rotation (looping).

        - `loop_3d_spin` — Continuous 3D spin (looping).

        - `loop_3d_sway` — Continuous 3D sway (looping).

        - `blur` — Per-glyph soft blur-in (text entrance).

        - `evaporate` — Per-glyph rise, blur, and fade-out (text exit only).

        - `overlay` — Static Overlay blend fill (timing ignored).

        - `difference` — Static Difference blend fill (timing ignored).

        - `rubber_in` — Per-glyph rubbery overshoot as text springs in (text
        entrance).

        - `whip_up` — Per-glyph whip upward into place (text entrance).

        - `whip_down` — Per-glyph whip downward out of frame (text exit only).

        - `glitch_pop` — Per-glyph glitchy pop-in with jitter (text entrance).

        - `drift_in` — Per-glyph gentle drift and fade in (text entrance).

        - `drift_out` — Per-glyph gentle drift and fade out (text exit only).

        - `loop_breathe` — Per-glyph continuous breathing scale loop (text).

        - `loop_shimmer` — Per-glyph continuous shimmer loop (text).

        - `swing_in` — Element swings into place from an angle (entrance).

        - `swing_out` — Element swings away out of frame (exit only).

        - `elastic_rise` — Element rises with an elastic overshoot (entrance).

        - `elastic_drop` — Element drops away with an elastic recoil (exit
        only).

        - `tilt_zoom` — Element tilts and zooms into place (entrance).

        - `loop_orbit` — Element orbits continuously around its center (loop).

        - `smooth_pop` — Element pops in from 60% scale, overshooting slightly
        past full size before it settles, as it fades in (entrance).
      x-enumDescriptions:
        identity: >-
          Deliberate no-op carrier (no visible motion): attach a pure `audio`
          generator to any element without animating it. Element surfaces only.
        fade: Fade in (entrance) or out (exit) via opacity.
        slide_up: Slide in from below / out upward.
        slide_down: Slide in from above / out downward.
        slide_left: Slide in from the right / out to the left.
        slide_right: Slide in from the left / out to the right.
        zoom_in: Scale up from small to full size (entrance).
        zoom_out: Scale down to small (exit).
        rotate_cw: Spin in/out clockwise.
        rotate_ccw: Spin in/out counter-clockwise.
        bounce: Bounce in with an elastic overshoot / bounce out.
        wipe_left: Reveal/conceal with a left-moving wipe.
        wipe_right: Reveal/conceal with a right-moving wipe.
        wipe_up: Reveal/conceal with an upward wipe.
        wipe_down: Reveal/conceal with a downward wipe.
        ken_burns_in: Slow zoom-in pan (Ken Burns) — entrance only.
        ken_burns_out: Slow zoom-out pan (Ken Burns) — entrance only.
        ken_burns_in_out: Slow zoom in then out (Ken Burns) — entrance only.
        loop_wiggle: Continuous subtle wiggle (looping).
        loop_rotate: Continuous stepped rotation (looping).
        loop_rotate_smooth: Continuous smooth rotation (looping).
        loop_3d_spin: Continuous 3D spin (looping).
        loop_3d_sway: Continuous 3D sway (looping).
        blur: Per-glyph soft blur-in (text entrance).
        evaporate: Per-glyph rise, blur, and fade-out (text exit only).
        overlay: Static Overlay blend fill (timing ignored).
        difference: Static Difference blend fill (timing ignored).
        rubber_in: Per-glyph rubbery overshoot as text springs in (text entrance).
        whip_up: Per-glyph whip upward into place (text entrance).
        whip_down: Per-glyph whip downward out of frame (text exit only).
        glitch_pop: Per-glyph glitchy pop-in with jitter (text entrance).
        drift_in: Per-glyph gentle drift and fade in (text entrance).
        drift_out: Per-glyph gentle drift and fade out (text exit only).
        loop_breathe: Per-glyph continuous breathing scale loop (text).
        loop_shimmer: Per-glyph continuous shimmer loop (text).
        swing_in: Element swings into place from an angle (entrance).
        swing_out: Element swings away out of frame (exit only).
        elastic_rise: Element rises with an elastic overshoot (entrance).
        elastic_drop: Element drops away with an elastic recoil (exit only).
        tilt_zoom: Element tilts and zooms into place (entrance).
        loop_orbit: Element orbits continuously around its center (loop).
        smooth_pop: >-
          Element pops in from 60% scale, overshooting slightly past full size
          before it settles, as it fades in (entrance).
    Easing:
      type: string
      enum:
        - linear
        - hold
        - ease_in
        - ease_out
        - ease_in_out
        - sin_in
        - sin_out
        - sin_in_out
        - square_in
        - square_out
        - square_in_out
        - expo_in
        - expo_out
        - expo_in_out
        - quad_in
        - quad_out
        - quad_in_out
        - cubic_in
        - cubic_out
        - cubic_in_out
        - quart_in
        - quart_out
        - quart_in_out
        - quint_in
        - quint_out
        - quint_in_out
      title: Easing
      description: >-
        The engine's shared easing name map (Easing.cpp `StringToEasingType`).


        Every name here resolves on every keyframe surface
        (morph/trim/mask/custom

        animation keyframes, selector ``ease``, counter ``easing``). ``ease_in``
        /

        ``ease_out`` / ``ease_in_out`` are the AE/CSS aliases of the ``quad_*``

        curves. ``square_*`` is a **step** family (``square_in_out`` snaps at
        the

        midpoint), not a power curve — the quadratic curves are ``quad_*``.

        ``hold`` freezes the prior value until the next keyframe.


        The parameterized families (spring / back / elastic / bezier) are not

        names: express them through the dedicated keyframe sub-objects.


        Values:

        - `linear` — Constant speed, no acceleration.

        - `hold` — Freeze the previous value until the next keyframe (step).

        - `ease_in` — Starts slow, accelerates (alias of quad_in).

        - `ease_out` — Starts fast, decelerates (alias of quad_out).

        - `ease_in_out` — Slow start and end, faster in the middle (alias of
        quad_in_out).

        - `sin_in` — Gentle sinusoidal acceleration in.

        - `sin_out` — Gentle sinusoidal deceleration out.

        - `sin_in_out` — Gentle sinusoidal ease both ends.

        - `square_in` — Step: snaps at the segment end (not a power curve).

        - `square_out` — Step: snaps at the segment start (not a power curve).

        - `square_in_out` — Step: snaps at the midpoint — the typewriter/glitch
        snap.

        - `expo_in` — Extreme exponential acceleration in.

        - `expo_out` — Extreme exponential deceleration out.

        - `expo_in_out` — Extreme exponential ease both ends.

        - `quad_in` — Quadratic acceleration in.

        - `quad_out` — Quadratic deceleration out.

        - `quad_in_out` — Quadratic ease both ends.

        - `cubic_in` — Cubic acceleration in (stronger than quad).

        - `cubic_out` — Cubic deceleration out (stronger than quad).

        - `cubic_in_out` — Cubic ease both ends.

        - `quart_in` — Quartic acceleration in.

        - `quart_out` — Quartic deceleration out.

        - `quart_in_out` — Quartic ease both ends.

        - `quint_in` — Quintic acceleration in (near-expo).

        - `quint_out` — Quintic deceleration out (near-expo).

        - `quint_in_out` — Quintic ease both ends.
      x-enumDescriptions:
        linear: Constant speed, no acceleration.
        hold: Freeze the previous value until the next keyframe (step).
        ease_in: Starts slow, accelerates (alias of quad_in).
        ease_out: Starts fast, decelerates (alias of quad_out).
        ease_in_out: Slow start and end, faster in the middle (alias of quad_in_out).
        sin_in: Gentle sinusoidal acceleration in.
        sin_out: Gentle sinusoidal deceleration out.
        sin_in_out: Gentle sinusoidal ease both ends.
        square_in: 'Step: snaps at the segment end (not a power curve).'
        square_out: 'Step: snaps at the segment start (not a power curve).'
        square_in_out: 'Step: snaps at the midpoint — the typewriter/glitch snap.'
        expo_in: Extreme exponential acceleration in.
        expo_out: Extreme exponential deceleration out.
        expo_in_out: Extreme exponential ease both ends.
        quad_in: Quadratic acceleration in.
        quad_out: Quadratic deceleration out.
        quad_in_out: Quadratic ease both ends.
        cubic_in: Cubic acceleration in (stronger than quad).
        cubic_out: Cubic deceleration out (stronger than quad).
        cubic_in_out: Cubic ease both ends.
        quart_in: Quartic acceleration in.
        quart_out: Quartic deceleration out.
        quart_in_out: Quartic ease both ends.
        quint_in: Quintic acceleration in (near-expo).
        quint_out: Quintic deceleration out (near-expo).
        quint_in_out: Quintic ease both ends.
    MotionScope:
      type: string
      enum:
        - element
        - character
      title: MotionScope
      description: >-
        Whether a motion preset applies to the whole element or per-glyph (text
        only).


        Values:

        - `element` — The motion animates the whole element as one unit.

        - `character` — The motion animates each text glyph individually (text
        only).
      x-enumDescriptions:
        element: The motion animates the whole element as one unit.
        character: The motion animates each text glyph individually (text only).
    AudioGenerator:
      properties:
        feature:
          type: string
          enum:
            - amplitude
            - bass
            - mid
            - high
            - beat
          title: Feature
          default: amplitude
        scale:
          type: number
          title: Scale
          description: Scale offset per unit feature.
          default: 0
        opacity:
          type: number
          title: Opacity
          description: Opacity offset per unit feature.
          default: 0
        translate:
          anyOf:
            - $ref: '#/components/schemas/Point'
            - type: 'null'
          description: Translate offset per unit feature.
        rotate:
          type: number
          title: Rotate
          description: Rotation offset (degrees) per unit feature.
          default: 0
      additionalProperties: false
      type: object
      title: AudioGenerator
      description: >-
        Audio-reactive property offsets driven by the soundtrack FFT (§3, W2).


        Each frame the mix reduces to the chosen feature and the element's
        properties

        get offset by ``amount × feature`` — beat-bounce, bass-pumped scale,
        etc.
    WordAnimationStyle:
      type: string
      enum:
        - glow
        - box
        - scale_pop
        - slide_up
        - fly_in
        - color
      title: WordAnimationStyle
      description: |-
        [API extension] Word-level animations (require word_animation.words).

        Values:
        - `glow` — Highlights the active word with a glowing emphasis.
        - `box` — Draws a colored box behind the active word.
        - `scale_pop` — Pops the active word with a quick scale punch.
        - `slide_up` — Floats each word up into place as it becomes active.
        - `fly_in` — Drops each word in from above as it becomes active.
        - `color` — Recolors the active word (karaoke-style highlight).
      x-enumDescriptions:
        glow: Highlights the active word with a glowing emphasis.
        box: Draws a colored box behind the active word.
        scale_pop: Pops the active word with a quick scale punch.
        slide_up: Floats each word up into place as it becomes active.
        fly_in: Drops each word in from above as it becomes active.
        color: Recolors the active word (karaoke-style highlight).
    Word:
      properties:
        text:
          type: string
          title: Text
        start:
          type: number
          minimum: 0
          title: Start
          description: ABSOLUTE output-timeline seconds (not element-relative).
        end:
          type: number
          exclusiveMinimum: 0
          title: End
          description: ABSOLUTE output-timeline seconds (not element-relative).
      additionalProperties: false
      type: object
      required:
        - text
        - start
        - end
      title: Word
    WordStyle:
      properties:
        color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Color
        bold:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Bold
        italic:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Italic
        underline:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Underline
      additionalProperties: false
      type: object
      title: WordStyle
      description: Per-word style override (§4 W3). Whole-word matches only.
    GeometryRect:
      properties:
        corner_radius:
          anyOf:
            - type: number
              minimum: 0
            - items:
                type: number
                minimum: 0
              type: array
              maxItems: 4
              minItems: 4
            - $ref: '#/components/schemas/GeometryCornerRadii'
            - type: 'null'
          title: Corner Radius
          description: >-
            Corner radius in px: one number for all four corners, `[top_left,
            top_right, bottom_right, bottom_left]`, or `{top_left, top_right,
            bottom_right, bottom_left}` (missing corners 0). Omitted: square
            corners.
        smoothing:
          anyOf:
            - type: number
              maximum: 1
              minimum: 0
            - type: 'null'
          title: Smoothing
          description: >-
            Figma's corner smoothing, 0 to 1: above 0 the corners ease into the
            edges (a squircle at 1). Omitted: 0, the plain rounded rect.
      additionalProperties: false
      type: object
      title: GeometryRect
      description: A rectangle filling the element box, optionally with rounded corners.
    GeometryEllipse:
      properties: {}
      additionalProperties: false
      type: object
      title: GeometryEllipse
      description: >-
        The ellipse inscribed in the element box (a circle when the box is
        square).
    GeometryLine:
      properties:
        x1:
          anyOf:
            - type: number
            - type: 'null'
          title: X1
        y1:
          anyOf:
            - type: number
            - type: 'null'
          title: Y1
        x2:
          anyOf:
            - type: number
            - type: 'null'
          title: X2
        y2:
          anyOf:
            - type: number
            - type: 'null'
          title: Y2
      additionalProperties: false
      type: object
      title: GeometryLine
      description: >-
        A straight line painted with the shape's `stroke` (else its `fill`).


        Endpoints are px from the element box's top-left corner; the width is

        `stroke_width` and the ends are `stroke_linecap`. Endpoints left out
        give

        the box's horizontal centre line.
    GeometryRing:
      properties:
        inner_ratio:
          anyOf:
            - type: number
              maximum: 1
              minimum: 0
            - type: 'null'
          title: Inner Ratio
          description: 'The hole''s share of the radius, 0–1 (0 is a pie). Omitted: 0.'
        inner_ratio_percent:
          anyOf:
            - type: number
              maximum: 100
              minimum: 0
            - type: 'null'
          title: Inner Ratio Percent
          description: '`inner_ratio` as a percent (85 is 0.85).'
        start_angle:
          anyOf:
            - type: number
            - type: 'null'
          title: Start Angle
          description: >-
            Where the sweep starts, degrees, 0 at 3 o'clock, clockwise. Omitted:
            0.
        sweep:
          anyOf:
            - type: number
              maximum: 360
              minimum: 0
            - type: 'null'
          title: Sweep
          description: >-
            Degrees drawn clockwise from `start_angle`. Omitted: 360. With
            `segments` the sweep is theirs, so leave it out.
        sweep_percent:
          anyOf:
            - type: number
              maximum: 100
              minimum: 0
            - type: 'null'
          title: Sweep Percent
          description: '`sweep` as a percent of the full circle (100 = 360°).'
        cap:
          anyOf:
            - type: string
              enum:
                - butt
                - round
            - type: 'null'
          title: Cap
          description: 'Ends of a partial sweep. Omitted: `butt`.'
        segments:
          anyOf:
            - items:
                $ref: '#/components/schemas/GeometrySegment'
              type: array
              maxItems: 32
              minItems: 1
            - type: 'null'
          title: Segments
          description: >-
            Consecutive solid slices (at most 32, together at most 360°) drawn
            as one shape, so abutting slices leave no seam.
      additionalProperties: false
      type: object
      title: GeometryRing
      description: >-
        A ring, arc, pie or segmented ring: circular, inscribed in the box's
        shorter side.
    GeometryPolygon:
      properties:
        sides:
          anyOf:
            - type: integer
              maximum: 60
              minimum: 3
            - type: 'null'
          title: Sides
          description: 'Vertices. Omitted: 5.'
        corner_radius:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Corner Radius
          description: 'Rounds every vertex with an arc of this radius in px. Omitted: 0.'
      additionalProperties: false
      type: object
      title: GeometryPolygon
      description: >-
        A regular polygon inscribed in the ellipse the box holds, its first
        vertex at 12

        o'clock.
    GeometryStar:
      properties:
        points:
          anyOf:
            - type: integer
              maximum: 60
              minimum: 3
            - type: 'null'
          title: Points
          description: 'Outer vertices. Omitted: 5.'
        inner_ratio:
          anyOf:
            - type: number
              maximum: 1
              minimum: 0
            - type: 'null'
          title: Inner Ratio
          description: >-
            The inner vertices' share of the outer ellipse. Omitted: 0.382, a
            regular pentagram's.
        corner_radius:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Corner Radius
          description: 'Rounds every vertex with an arc of this radius in px. Omitted: 0.'
      additionalProperties: false
      type: object
      title: GeometryStar
      description: >-
        A star: `points` outer vertices on the ellipse the box holds (the first
        at 12

        o'clock), alternating with inner ones on that ellipse scaled by
        `inner_ratio`.
    GeometryShadow:
      properties:
        type:
          anyOf:
            - type: string
              enum:
                - drop
                - inner
            - type: 'null'
          title: Type
          description: 'Omitted: `drop`.'
        x:
          anyOf:
            - type: number
              maximum: 100000
              minimum: -100000
            - type: 'null'
          title: X
          description: Horizontal offset in px.
        'y':
          anyOf:
            - type: number
              maximum: 100000
              minimum: -100000
            - type: 'null'
          title: 'Y'
          description: Vertical offset in px, down positive.
        blur:
          anyOf:
            - type: number
              maximum: 100000
              minimum: 0
            - type: 'null'
          title: Blur
          description: CSS blur radius in px (sigma = blur / 2).
        spread:
          anyOf:
            - type: number
              maximum: 100000
              minimum: -100000
            - type: 'null'
          title: Spread
          description: Spread in px.
        color:
          type: string
          pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
          title: Color
          example: '#00000066'
      additionalProperties: false
      type: object
      required:
        - color
      title: GeometryShadow
      description: >-
        A CSS-style box shadow on a rect or circle geometry, in px, each length
        within

        +-100000.
    OperationAnchor:
      properties:
        ref:
          type: string
          enum:
            - composition
            - element.start
            - element.end
            - marker
            - instance.start
          title: Ref
          description: >-
            `composition` (0 s), `element.start` or `element.end` of `target`, a
            marker by `name`, or `instance.start` of `target` (in a component,
            its own instance).
        target:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Target
          description: The element or instance an element or instance anchor reads.
        name:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Name
          description: The marker a `marker` anchor reads.
        offset:
          anyOf:
            - type: number
            - type: 'null'
          title: Offset
          description: 'Seconds added to the anchor. Omitted: 0.'
      additionalProperties: false
      type: object
      required:
        - ref
      title: OperationAnchor
      description: Where a group's times count from (GVR §4.3 item 8).
    Operation:
      properties:
        id:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Id
          description: A stable id for editors.
        target:
          type: string
          minLength: 1
          title: Target
          description: The element id it drives; with a `child` split, a frame's.
          example: donut
        start:
          type: number
          title: Start
          description: Seconds after the group's anchor.
          example: 0
        end:
          type: number
          title: End
          description: Seconds after the group's anchor; equal to `start` switches at once.
          example: 1
        property:
          type: string
          title: Property
          description: >-
            A property registry path: `position.x`, `size.y`, `scale`,
            `rotation`, `opacity`, `fill`, `geometry.ring.segments[0].sweep`,
            `text.value`, …; a colour takes `.r`/`.g`/`.b`/`.a`. GET
            /v1/capabilities lists every path, its unit and range under
            `registry.properties`.
          example: geometry.ring.segments[0].sweep
        from:
          anyOf:
            - type: number
            - type: string
            - type: 'null'
          title: From
          description: >-
            Value at `start` (a string on `text.value`). Omitted on a replace:
            the value below it at `start`; on an add or multiply: the identity.
        to:
          anyOf:
            - type: number
            - type: string
            - type: 'null'
          title: To
          description: >-
            Value at `end`, held after it. Omitted on a replace: the live base
            value; on an add or multiply: the identity.
        easing:
          anyOf:
            - oneOf:
                - $ref: '#/components/schemas/Easing'
                - $ref: '#/components/schemas/EaseFamily'
                - $ref: '#/components/schemas/EaseBezier'
                - $ref: '#/components/schemas/EaseSpring'
                - $ref: '#/components/schemas/EaseBack'
                - $ref: '#/components/schemas/EaseElastic'
            - type: 'null'
          title: Easing
          description: >-
            A curve name (`ease_in_out`, `hold`, …), `{family, intensity}`, or
            one of `{bezier: [x1, y1, x2, y2]}`, `{spring}`, `{back}`,
            `{elastic}`. Omitted: linear.
        composite:
          anyOf:
            - type: string
              enum:
                - replace
                - add
                - multiply
            - type: 'null'
          title: Composite
          description: >-
            `replace` overrides what is below it from `start`; `add` and
            `multiply` stack on it (multiply on scale, opacity and colours).
            Omitted: `replace`.
        split:
          anyOf:
            - $ref: '#/components/schemas/OperationSplit'
            - type: 'null'
          description: Run it per child, letter, word or line, staggered.
        takeover:
          anyOf:
            - type: string
              const: velocity
            - type: 'null'
          title: Takeover
          description: 'On a replace without `from`: start at the speed of what it replaces.'
      additionalProperties: false
      type: object
      required:
        - target
        - start
        - end
        - property
      title: Operation
      description: >-
        One property driven over a window (GVR §4.3): the base value, then the
        replaces by

        start, then the multiplies and the adds. It holds `to` after `end`.
    PresetOperation:
      properties:
        id:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Id
          description: A stable id for editors.
        target:
          type: string
          minLength: 1
          title: Target
          description: The element id it drives; with a `child` split, a frame's.
          example: donut
        start:
          type: number
          title: Start
          description: Seconds after the group's anchor.
          example: 0
        end:
          type: number
          title: End
          description: Seconds after the group's anchor; equal to `start` switches at once.
          example: 1
        preset:
          type: string
          enum:
            - arcRise
            - bounceIn
            - drop
            - elasticRise
            - fade
            - floatDown
            - floatLeft
            - floatRight
            - floatUp
            - gentleFloatDown
            - gentleFloatLeft
            - gentleFloatRight
            - gentleFloatUp
            - identity
            - kenBurnsIn
            - kenBurnsInOut
            - kenBurnsOut
            - loop-3DSpin
            - loop-3DSway
            - loop-bounce
            - loop-heartBeat
            - loop-jiggle
            - loop-orbit
            - loop-rotateBasic
            - loop-rotateSmooth
            - loop-squeezy
            - loop-sway
            - out-bounceOut
            - out-dropOut
            - out-elasticDrop
            - out-fade
            - out-floatDown
            - out-floatLeft
            - out-floatRight
            - out-floatUp
            - out-gentleFloatDown
            - out-gentleFloatLeft
            - out-gentleFloatRight
            - out-gentleFloatUp
            - out-pop
            - out-slideBounceDown
            - out-slideBounceLeft
            - out-slideBounceRight
            - out-slideBounceUp
            - out-slideDown
            - out-slideLeft
            - out-slideRight
            - out-slideUp
            - out-spinAntiClockwise
            - out-spinClockwise
            - out-swingOut
            - out-wipeDown
            - out-wipeLeft
            - out-wipeRight
            - out-wipeUp
            - out-zoomOut
            - pop
            - slideBounceDown
            - slideBounceLeft
            - slideBounceRight
            - slideBounceUp
            - slideDown
            - slideLeft
            - slideRight
            - slideUp
            - smoothPop
            - spinAntiClockwise
            - spinClockwise
            - springPop
            - swingIn
            - tiltZoom
            - wipeDown
            - wipeLeft
            - wipeRight
            - wipeUp
            - zoomIn
          title: Preset
          description: >-
            An element preset by the engine's name (`slideUp`, `zoomIn`, `fade`,
            …). Text presets stay on a text's `motion`.
          example: zoomIn
      additionalProperties: false
      type: object
      required:
        - target
        - start
        - end
        - preset
      title: PresetOperation
      description: An element preset run as operations over the window (GVR §4.3 item 10).
    AnimationKeyframe:
      properties:
        time:
          type: number
          maximum: 1
          minimum: 0
          title: Time
          description: Fraction of the animation (0–1).
        easing:
          anyOf:
            - type: string
            - type: 'null'
          title: Easing
          description: >-
            Easing into this keyframe — any `easings` catalog name (`quad_out`,
            `expo_in_out`, `hold`, …). Unknown names are rejected. A
            `bezier`/`spring`/`back`/`elastic` sub-object overrides it.
        bezier:
          anyOf:
            - $ref: '#/components/schemas/BezierEasing'
            - type: 'null'
        spring:
          anyOf:
            - $ref: '#/components/schemas/SpringEasing'
            - type: 'null'
        back:
          anyOf:
            - $ref: '#/components/schemas/BackEasing'
            - type: 'null'
        elastic:
          anyOf:
            - $ref: '#/components/schemas/ElasticEasing'
            - type: 'null'
        translate:
          anyOf:
            - $ref: '#/components/schemas/Point'
            - type: 'null'
        path:
          anyOf:
            - $ref: '#/components/schemas/SpatialPath'
            - type: 'null'
          description: >-
            Curve the translate into this keyframe along a cubic bezier
            (`c1`/`c2` control points).
        scale:
          anyOf:
            - type: number
            - $ref: '#/components/schemas/ScaleXY'
            - type: 'null'
          title: Scale
          description: Uniform scale factor, or `{x, y}` for non-uniform scale.
        rotate:
          anyOf:
            - type: number
            - type: 'null'
          title: Rotate
          description: In-plane (z) rotation, degrees.
        rotation:
          anyOf:
            - $ref: '#/components/schemas/RotationXYZ'
            - type: 'null'
          description: >-
            Full 3D rotation `{x, y, z}` (degrees); overrides `rotate`. Non-zero
            x/y = animated perspective tilt.
        opacity:
          anyOf:
            - type: number
            - type: 'null'
          title: Opacity
        color:
          anyOf:
            - type: string
              pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
            - type: 'null'
          title: Color
          description: >-
            Colour tint at this keyframe (multiplied with the element). An
            8-digit hex alpha composes with `opacity` — when both are set,
            `opacity` wins for the alpha channel.
        blur:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Blur
          description: Per-glyph blur amount (fraction of the em). Text registries only.
      additionalProperties: false
      type: object
      required:
        - time
      title: AnimationKeyframe
      description: >-
        A keyframe in a custom animation definition (§3). Time is 0–1 of the
        animation.


        Property channels mirror the engine's catalog file format, so anything a

        shipped preset expresses is expressible here: position (`translate`,
        curved

        via `path`), uniform or per-axis `scale`, scalar z `rotate` or full 3D

        `rotation`, `opacity`/`color` tint, per-glyph `blur` (text registries
        only),

        and the full easing vocabulary including the parameterized families

        (`bezier` / `spring` / `back` / `elastic`).
    RangeSelector:
      properties:
        based_on:
          type: string
          enum:
            - glyph
            - word
          title: Based On
          default: glyph
        start:
          type: number
          maximum: 1
          minimum: 0
          title: Start
          description: Range start, fraction along the text.
          default: 0
        end:
          type: number
          maximum: 1
          minimum: 0
          title: End
          description: Range end, fraction along the text.
          default: 1
        falloff:
          type: number
          minimum: 0
          title: Falloff
          description: Ease to zero over this fraction beyond each edge.
          default: 0
        ease:
          anyOf:
            - type: string
            - type: 'null'
          title: Ease
          description: Easing curve name for the falloff.
        invert:
          type: boolean
          title: Invert
          default: false
      additionalProperties: false
      type: object
      title: RangeSelector
      description: >-
        AE-style range selector: shape a text animation's amount by position
        (§4, W2).
    ShaderParamKeyframe:
      properties:
        time:
          type: number
          minimum: 0
          title: Time
          description: Absolute timeline seconds.
        value:
          anyOf:
            - type: number
            - type: string
            - items:
                type: number
              type: array
              maxItems: 2
              minItems: 2
          title: Value
          description: The param's value at this time, of its type.
      additionalProperties: false
      type: object
      required:
        - time
        - value
      title: ShaderParamKeyframe
      description: >-
        A point on a runtime shader param's track (AEC §2): linear between, held
        outside.
    GeometrySegment:
      properties:
        sweep:
          anyOf:
            - type: number
              maximum: 360
              minimum: 0
            - type: 'null'
          title: Sweep
          description: Slice angle in degrees.
        sweep_percent:
          anyOf:
            - type: number
              maximum: 100
              minimum: 0
            - type: 'null'
          title: Sweep Percent
          description: Slice as a percent of the full circle (100 = 360°).
        fill:
          type: string
          pattern: ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
          title: Fill
          description: The slice's solid colour.
          example: '#2a85ff'
      additionalProperties: false
      type: object
      required:
        - fill
      title: GeometrySegment
      description: >-
        One solid-coloured slice of a segmented ring, drawn after the previous
        one.
    EaseFamily:
      properties:
        family:
          type: string
          enum:
            - smooth
            - natural
            - slowdown
            - accelerate
            - overshoot
            - anticipate
            - anticipate_overshoot
            - spring
            - elastic
            - bounce
            - impulse
            - throw
          title: Family
          description: The curve's character.
        intensity:
          type: number
          maximum: 100
          minimum: 0
          title: Intensity
          description: How strongly, 0 to 100.
          example: 65
      additionalProperties: false
      type: object
      required:
        - family
        - intensity
      title: EaseFamily
      description: One of the engine's twelve easing families at an intensity (GVR M3).
    EaseBezier:
      properties:
        bezier:
          items:
            type: number
          type: array
          maxItems: 4
          minItems: 4
          title: Bezier
          description: '`[x1, y1, x2, y2]`, as CSS `cubic-bezier`.'
      additionalProperties: false
      type: object
      required:
        - bezier
      title: EaseBezier
      description: A CSS cubic-bezier curve.
    EaseSpring:
      properties:
        spring:
          $ref: '#/components/schemas/SpringEasing'
      additionalProperties: false
      type: object
      required:
        - spring
      title: EaseSpring
      description: A damped spring over the window.
    EaseBack:
      properties:
        back:
          $ref: '#/components/schemas/BackEasing'
      additionalProperties: false
      type: object
      required:
        - back
      title: EaseBack
      description: An anticipate or overshoot ease (AE 'back').
    EaseElastic:
      properties:
        elastic:
          $ref: '#/components/schemas/ElasticEasing'
      additionalProperties: false
      type: object
      required:
        - elastic
      title: EaseElastic
      description: An elastic ring-in or ring-out ease.
    OperationSplit:
      properties:
        unit:
          type: string
          enum:
            - element
            - child
            - letter
            - word
            - line
          title: Unit
          description: >-
            `child` runs over a frame's children; `letter`, `word` and `line`
            over a text's.
        stagger:
          anyOf:
            - type: number
            - type: 'null'
          title: Stagger
          description: 'Seconds between units. Omitted: 0.'
        order:
          anyOf:
            - type: string
              enum:
                - forward
                - reverse
                - centre
                - edges
                - seeded
            - type: 'null'
          title: Order
          description: 'The order the units take their turns in. Omitted: `forward`.'
      additionalProperties: false
      type: object
      required:
        - unit
      title: OperationSplit
      description: >-
        Run the operation once per unit, each starting `rank × stagger` seconds
        after the

        operation's start, the rank by `order` (GVR §4.3 item 7).
    SpatialPath:
      properties:
        c1:
          $ref: '#/components/schemas/Point'
        c2:
          $ref: '#/components/schemas/Point'
      additionalProperties: false
      type: object
      required:
        - c1
        - c2
      title: SpatialPath
      description: >-
        Cubic-bezier control points curving the translate into this keyframe.


        The segment from the previous keyframe's position travels along the
        bezier

        defined by `c1`/`c2` (same units as `translate`) instead of a straight
        line.
    ScaleXY:
      properties:
        x:
          type: number
          title: X
          default: 1
        'y':
          type: number
          title: 'Y'
          default: 1
      additionalProperties: false
      type: object
      title: ScaleXY
      description: Non-uniform scale for a custom-animation keyframe.
    RotationXYZ:
      properties:
        x:
          type: number
          title: X
          default: 0
        'y':
          type: number
          title: 'Y'
          default: 0
        z:
          type: number
          title: Z
          default: 0
      additionalProperties: false
      type: object
      title: RotationXYZ
      description: >-
        3D rotation (degrees) for a custom-animation keyframe.


        Non-zero `x`/`y` renders true perspective tilt through the engine's 45°
        fov

        camera — the animated 3D-card family (spins, tilting reveals). This is
        also

        the engine-recommended way to tilt *text*, which has no static tilt.
  securitySchemes:
    ApiKey:
      type: http
      scheme: bearer

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.