{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://theamateur.co.uk/schemas/dpl/dpl.plan-1.schema.json",
  "title": "DPL plan (dpl.plan/1)",
  "description": "A film as a director plans it: every scene and shot, what each is for, where attention goes and what is heard. Times are whole milliseconds from the start of the work. This schema describes exactly what the dpl reader accepts: unknown members, misspelt terms, fractional milliseconds and missing required members are refused.",
  "$comment": "Structure only. dpl validate (PlanValidator) also enforces what a schema cannot express: ids unique and every reference resolved (DPL-DUPLICATE, DPL-REF, DPL-REFERENCE, DPL-RECALL), times non-negative, in order, not overlapping and inside their scene or the work (DPL-TIME, DPL-RANGE, DPL-GAP, DPL-RUNTIME, DPL-ANCHOR, DPL-TIMING), empty required text (DPL-REQUIRED, DPL-EMPTY, DPL-FORMAT) and the editorial and sound rules (DPL-OVERLAY, DPL-LAYER, DPL-RIG, DPL-SFX, DPL-SOUND and the rest), and the short-form profile's members (DPL-CLOCK for clock times, DPL-KIT for a kit that contradicts itself, DPL-PROFILE for short-form members on a plan that is not short). A plan that passes this schema is readable; it is valid only when dpl validate reports no error. See contracts/dpl/README.md for every code.",
  "type": "object",
  "properties": {
    "format": {
      "type": "string",
      "const": "dpl.plan/1"
    },
    "work": {
      "$ref": "#/$defs/Work"
    },
    "characters": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/Character"
      }
    },
    "locations": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/Location"
      }
    },
    "props": {
      "description": "Objects the story depends on (for example the lamp that must be relit).",
      "type": "array",
      "items": {
        "$ref": "#/$defs/Prop"
      }
    },
    "continuity_rules": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/ContinuityRule"
      }
    },
    "must_not": {
      "description": "Things the whole film must never show or do.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "sequences": {
      "description": "Optional grouping of scenes into larger movements of the story.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/Sequence"
      }
    },
    "journey": {
      "description": "Where the whole film travels, for the scenes no sequence's own journey governs.",
      "anyOf": [
        {
          "$ref": "#/$defs/Journey"
        },
        {
          "type": "null"
        }
      ]
    },
    "none": {
      "description": "What the whole film deliberately does not have, said outright: today only journey (the camera travels nowhere across the film). Saying so is different from saying nothing: dpl missing stops asking about it.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/Absence"
      }
    },
    "scenes": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/Scene"
      }
    },
    "transitions": {
      "description": "Authored joins between shots. A join that is not listed is an unremarked cut.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/Transition"
      }
    },
    "sound": {
      "$ref": "#/$defs/Sound"
    },
    "delivery": {
      "description": "Short-form profile: the picture shape and runtime limit the work is delivered in.",
      "anyOf": [
        {
          "$ref": "#/$defs/Delivery"
        },
        {
          "type": "null"
        }
      ]
    },
    "kit": {
      "description": "Short-form profile: the cameras, supports, sound gear and lights the crew has.",
      "anyOf": [
        {
          "$ref": "#/$defs/Kit"
        },
        {
          "type": "null"
        }
      ]
    },
    "people": {
      "description": "Short-form profile: the cast and crew, their roles and when they are there.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/Person"
      }
    },
    "schedule": {
      "description": "Short-form profile: the shoot days.",
      "anyOf": [
        {
          "$ref": "#/$defs/Schedule"
        },
        {
          "type": "null"
        }
      ]
    },
    "slice_of": {
      "description": "Present only on a slice (dpl slice): the whole plan this sub-plan was cut from, so an agent that holds only the slice can tell which revision it came from and which scenes and shots it covers. A whole plan has none.",
      "anyOf": [
        {
          "$ref": "#/$defs/SliceOf"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "format",
    "work",
    "scenes"
  ],
  "additionalProperties": false,
  "$defs": {
    "Absence": {
      "type": "object",
      "description": "Something a plan deliberately does not have. An empty or missing member says \"not answered yet\"; an entry here says \"answered: there is none\".",
      "properties": {
        "kind": {
          "$ref": "#/$defs/AbsenceKind"
        },
        "why": {
          "description": "Why there is none (a 360-degree master, a silent film, a route that goes nowhere). Required for line_of_action, which departs from editing convention; otherwise optional.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "kind"
      ],
      "additionalProperties": false
    },
    "AbsenceKind": {
      "type": "string",
      "description": "What a plan can say it deliberately does not have: nobody speaks on screen in the scene (spoken_lines), the scene is staged without a line of action (line_of_action), or the film or sequence travels nowhere (journey).",
      "enum": [
        "spoken_lines",
        "line_of_action",
        "journey"
      ]
    },
    "Acceptance": {
      "type": "object",
      "properties": {
        "code": {
          "description": "The finding code accepted (for example RDY-COVERAGE).",
          "type": "string"
        },
        "shot": {
          "description": "The shot it applies to; every shot in the scene when absent.",
          "type": [
            "string",
            "null"
          ]
        },
        "why": {
          "type": "string"
        }
      },
      "required": [
        "code",
        "why"
      ],
      "additionalProperties": false
    },
    "Acoustics": {
      "type": "string",
      "description": "How a place sounds: fixed noise cannot be switched off.",
      "enum": [
        "quiet",
        "controllable",
        "fixed"
      ]
    },
    "ActionWordAnchor": {
      "type": "object",
      "properties": {
        "line": {
          "type": "string"
        },
        "word": {
          "type": "integer"
        },
        "on": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "line",
        "word"
      ],
      "additionalProperties": false
    },
    "AspectRatio": {
      "type": "string",
      "description": "The picture shape the work is delivered in.",
      "enum": [
        "9:16",
        "16:9",
        "1:1",
        "4:5"
      ]
    },
    "AttentionBeat": {
      "type": "object",
      "description": "Who or what should hold the audience's attention. Without a span it covers the whole scene.",
      "properties": {
        "start_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "end_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "primary": {
          "type": "string"
        },
        "secondary": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "target": {
          "description": "The character, prop or sound carrier that holds attention, when it is one.",
          "type": [
            "string",
            "null"
          ]
        },
        "effect": {
          "anyOf": [
            {
              "$ref": "#/$defs/AttentionEffect"
            },
            {
              "type": "null"
            }
          ]
        },
        "over": {
          "description": "What the target leads while it holds attention.",
          "type": [
            "string",
            "null"
          ]
        },
        "resolves_to": {
          "description": "What attention settles on afterwards.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "primary"
      ],
      "additionalProperties": false
    },
    "AttentionEffect": {
      "type": "string",
      "enum": [
        "concentrate",
        "suppress_competition",
        "intensify_recognition",
        "reveal",
        "withhold",
        "disorient",
        "waking",
        "trance",
        "sleep",
        "distort_hearing",
        "restore_clarity",
        "alter_scale"
      ]
    },
    "Axis": {
      "type": "object",
      "properties": {
        "left": {
          "type": "string"
        },
        "right": {
          "type": "string"
        }
      },
      "required": [
        "left",
        "right"
      ],
      "additionalProperties": false
    },
    "BeatFunction": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "function": {
          "$ref": "#/$defs/BeatFunctionKind"
        },
        "start_ms": {
          "type": "integer"
        },
        "end_ms": {
          "type": "integer"
        },
        "speech": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "words": {
          "type": [
            "string",
            "null"
          ]
        },
        "source": {
          "type": [
            "string",
            "null"
          ]
        },
        "note": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "function",
        "start_ms",
        "end_ms"
      ],
      "additionalProperties": false
    },
    "BeatFunctionKind": {
      "type": "string",
      "enum": [
        "claim_from_source",
        "law_or_official_fact",
        "where",
        "who",
        "unfilmed_event",
        "action",
        "opinion_or_joke",
        "new_act",
        "figure_of_speech",
        "quote",
        "promise_or_question",
        "sponsor",
        "call_to_action"
      ]
    },
    "BedLift": {
      "type": "object",
      "description": "An authored moment where the bed comes up: after a spoken line ends, for this long.",
      "properties": {
        "after": {
          "type": "string",
          "default": ""
        },
        "ms": {
          "type": "integer",
          "default": 0
        }
      },
      "additionalProperties": false
    },
    "Camera": {
      "type": "object",
      "properties": {
        "size": {
          "$ref": "#/$defs/ShotSize"
        },
        "movement": {
          "$ref": "#/$defs/CameraMovement"
        },
        "angle": {
          "anyOf": [
            {
              "$ref": "#/$defs/CameraAngle"
            },
            {
              "type": "null"
            }
          ]
        },
        "lens": {
          "anyOf": [
            {
              "$ref": "#/$defs/Lens"
            },
            {
              "type": "null"
            }
          ]
        },
        "perspective": {
          "anyOf": [
            {
              "$ref": "#/$defs/Perspective"
            },
            {
              "type": "null"
            }
          ]
        },
        "note": {
          "type": [
            "string",
            "null"
          ]
        },
        "focus": {
          "anyOf": [
            {
              "$ref": "#/$defs/FocusQuality"
            },
            {
              "type": "null"
            }
          ]
        },
        "whose": {
          "description": "For a subjective view: the character (or prop) whose eyes the camera is. Never seen itself.",
          "type": [
            "string",
            "null"
          ]
        },
        "eyeline": {
          "description": "Where eyes go: who looks at what, or that no one meets the lens.",
          "type": [
            "string",
            "null"
          ]
        },
        "motion": {
          "description": "The character of a moving camera.",
          "anyOf": [
            {
              "$ref": "#/$defs/MotionFeel"
            },
            {
              "type": "null"
            }
          ]
        },
        "lens_multiplier": {
          "description": "Short-form profile, for a phone: the lens as a multiplier of the main camera (0.5, 1, 3).",
          "type": [
            "number",
            "null"
          ]
        },
        "focal_mm": {
          "description": "Short-form profile, for a camera: the lens's focal length in millimetres.",
          "type": [
            "number",
            "null"
          ]
        },
        "support": {
          "description": "Short-form profile: what carries the camera in this shot.",
          "anyOf": [
            {
              "$ref": "#/$defs/Support"
            },
            {
              "type": "null"
            }
          ]
        },
        "device": {
          "description": "Short-form profile: the kit camera that films it (a camera id of the kit); any camera that can when absent.",
          "type": [
            "string",
            "null"
          ]
        },
        "frame_rate": {
          "description": "Short-form profile: frames per second, when it is not the usual rate (slow motion).",
          "type": [
            "number",
            "null"
          ]
        },
        "orientation": {
          "description": "Short-form profile: how the camera is held, which decides the frame's shape.",
          "anyOf": [
            {
              "$ref": "#/$defs/FrameOrientation"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "size",
        "movement"
      ],
      "additionalProperties": false
    },
    "CameraAngle": {
      "type": "string",
      "enum": [
        "overhead",
        "high",
        "eye_level",
        "shoulder_level",
        "low",
        "hip_level",
        "knee_level",
        "ground_level",
        "worms_eye",
        "dutch"
      ]
    },
    "CameraKind": {
      "type": "string",
      "description": "A phone has lens multipliers (0.5, 1, 3); a camera (mirrorless, DSLR, cinema) has lenses in millimetres.",
      "enum": [
        "phone",
        "camera"
      ]
    },
    "CameraMovement": {
      "type": "string",
      "enum": [
        "hold",
        "handheld_hold",
        "pan",
        "whip_pan",
        "tilt",
        "push_in",
        "pull_out",
        "track",
        "dolly",
        "crab",
        "arc",
        "orbit",
        "zoom",
        "dolly_zoom",
        "vertigo",
        "handheld",
        "steadicam",
        "boom",
        "jib",
        "crane",
        "pedestal",
        "roll",
        "aerial",
        "helicopter",
        "drone",
        "rise",
        "drift",
        "descend"
      ]
    },
    "CaptionTiming": {
      "type": "object",
      "description": "How long a caption may stay: an allowed range for its end, never a reason to move its start.",
      "properties": {
        "min_hold_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "max_hold_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "clear_before": {
          "description": "It must be gone by this spoken moment.",
          "anyOf": [
            {
              "$ref": "#/$defs/SpeechAnchor"
            },
            {
              "type": "null"
            }
          ]
        },
        "within_shot": {
          "description": "It ends with its shot at the latest (default true).",
          "type": [
            "boolean",
            "null"
          ]
        }
      },
      "additionalProperties": false
    },
    "CaptureKind": {
      "type": "string",
      "description": "Filming that makes no picture: room tone recorded before leaving a place, a voiceover recorded in a quiet room.",
      "enum": [
        "room_tone",
        "voiceover"
      ]
    },
    "CaptureTask": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "kind": {
          "$ref": "#/$defs/CaptureKind"
        },
        "location": {
          "description": "Where it is recorded (a location id).",
          "type": "string"
        },
        "minutes": {
          "description": "How long it takes, when that is not the profile's own figure for this kind.",
          "type": [
            "integer",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "kind",
        "location"
      ],
      "additionalProperties": false
    },
    "CarrierMessage": {
      "type": "object",
      "properties": {
        "language": {
          "type": "string"
        },
        "words": {
          "description": "The message as it is said (in its own language), when known.",
          "type": [
            "string",
            "null"
          ]
        },
        "meaning": {
          "description": "What it means.",
          "type": "string"
        },
        "phrases": {
          "description": "Where each statement of the message falls in the carrier's own recording (media time).",
          "type": "array",
          "items": {
            "$ref": "#/$defs/MessagePhrase"
          }
        }
      },
      "required": [
        "language",
        "meaning"
      ],
      "additionalProperties": false
    },
    "ChapterConstructStyle": {
      "type": "object",
      "properties": {
        "ground": {
          "type": "string",
          "default": "#101820"
        },
        "accent": {
          "type": "string",
          "default": "#e4b75e"
        },
        "ink": {
          "type": "string",
          "default": "#ffffff"
        },
        "font_family": {
          "type": [
            "string",
            "null"
          ]
        },
        "entrance_ms": {
          "type": "integer",
          "default": 700
        },
        "exit_ms": {
          "type": "integer",
          "default": 500
        }
      },
      "additionalProperties": false
    },
    "Character": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "appearance": {
          "description": "What the character looks like in a picture (for generation), apart from what they mean.",
          "type": [
            "string",
            "null"
          ]
        },
        "costume": {
          "type": [
            "string",
            "null"
          ]
        },
        "performance": {
          "type": [
            "string",
            "null"
          ]
        },
        "rig": {
          "description": "The character as a cut-out puppet (cloud brief c, 5 Oct 2026, Part 4; owner on The Unstruck Sound, §10: \"there isn't sufficient animation\"): its parts, which staging actions move. The pictures of the parts are bound.",
          "anyOf": [
            {
              "$ref": "#/$defs/CharacterRig"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "description"
      ],
      "additionalProperties": false
    },
    "CharacterRig": {
      "type": "object",
      "description": "A puppet rig: the standard jointed figure (ten parts: torso, head, upper and fore arms, thighs and shins), or parts of its own, cut from the art.",
      "properties": {
        "body": {
          "$ref": "#/$defs/RigBody"
        },
        "parts": {
          "description": "For a rig of its own: its parts. A part named as a role of the figure (torso, head, thigh_l…) is moved by the actions; others hold.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/RigPart"
          }
        },
        "roles": {
          "description": "Canonical action role to a part id in this rig.",
          "type": [
            "object",
            "null"
          ],
          "additionalProperties": {
            "type": "string"
          }
        },
        "ground": {
          "description": "Ground polyline as [x,y] points in normalized frame coordinates.",
          "type": [
            "array",
            "null"
          ],
          "items": {
            "type": "array",
            "items": {
              "type": "number"
            }
          }
        },
        "mirror": {
          "description": "Reflect the painted figure about its root when its travel faces against the art.",
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "body"
      ],
      "additionalProperties": false
    },
    "ConsideredAlternative": {
      "type": "object",
      "properties": {
        "choice": {
          "type": "string"
        },
        "why_not": {
          "type": "string"
        }
      },
      "required": [
        "choice",
        "why_not"
      ],
      "additionalProperties": false
    },
    "ContinuityRule": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "domain": {
          "type": [
            "string",
            "null"
          ]
        },
        "rule": {
          "type": "string"
        },
        "measures": {
          "description": "What the rule means in the rendered picture, measured on every shot that cites it (for example \"the Father of lights: no heavy shadow\" as a minimum brightness and a maximum share of deep shadow).",
          "type": "array",
          "items": {
            "$ref": "#/$defs/RuleMeasure"
          }
        }
      },
      "required": [
        "id",
        "rule"
      ],
      "additionalProperties": false
    },
    "Count": {
      "type": "object",
      "properties": {
        "from": {
          "type": "integer"
        },
        "to": {
          "type": "integer"
        }
      },
      "required": [
        "from",
        "to"
      ],
      "additionalProperties": false
    },
    "Coverage": {
      "type": "object",
      "properties": {
        "role": {
          "$ref": "#/$defs/CoverageRole"
        },
        "priority": {
          "$ref": "#/$defs/CoveragePriority",
          "default": "should"
        }
      },
      "required": [
        "role"
      ],
      "additionalProperties": false
    },
    "CoveragePriority": {
      "type": "string",
      "description": "How much the edit depends on a shot: a must cannot be dropped without losing the scene's job.",
      "enum": [
        "must",
        "should",
        "nice"
      ]
    },
    "CoverageRole": {
      "type": "string",
      "description": "What a shot gives the edit.",
      "enum": [
        "establishing",
        "master",
        "two_shot",
        "single",
        "over_shoulder",
        "reaction",
        "insert",
        "cutaway",
        "pov",
        "neutral"
      ]
    },
    "DaySetup": {
      "type": "object",
      "properties": {
        "id": {
          "description": "A set-up id: the setup of the shots filmed from it.",
          "type": "string"
        },
        "not_before": {
          "description": "The set-up cannot begin before this clock time (the place opens, a person arrives).",
          "type": [
            "string",
            "null"
          ]
        },
        "at": {
          "description": "The set-up must begin at this clock time: an event that happens then, whether or not the crew is ready.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "id"
      ],
      "additionalProperties": false
    },
    "Delivery": {
      "type": "object",
      "properties": {
        "aspect": {
          "$ref": "#/$defs/AspectRatio"
        },
        "max_runtime_ms": {
          "description": "The longest the finished work may run, in milliseconds (a platform's limit).",
          "type": [
            "integer",
            "null"
          ]
        },
        "platform": {
          "description": "Where it is posted (free text).",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "aspect"
      ],
      "additionalProperties": false
    },
    "DesignNote": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "problem": {
          "type": "string"
        },
        "choice": {
          "type": "string"
        },
        "outcome": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "problem",
        "choice"
      ],
      "additionalProperties": false
    },
    "Diagram": {
      "type": "object",
      "description": "Labelled boxes placed on the frame, links that draw on between them, and markers that travel. Positions are fractions of the frame (0 to 1, the box's centre); times are film ms, each tied by the plan's author to a word.",
      "properties": {
        "nodes": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/DiagramNode"
          }
        },
        "links": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/DiagramLink"
          }
        },
        "movers": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/DiagramMover"
          }
        }
      },
      "required": [
        "nodes"
      ],
      "additionalProperties": false
    },
    "DiagramLink": {
      "type": "object",
      "properties": {
        "from": {
          "type": "string"
        },
        "to": {
          "type": "string"
        },
        "at_ms": {
          "type": "integer"
        },
        "draw_ms": {
          "type": "integer",
          "default": 600
        },
        "out_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "tone": {
          "$ref": "#/$defs/DiagramTone",
          "default": "ink"
        }
      },
      "required": [
        "from",
        "to",
        "at_ms"
      ],
      "additionalProperties": false
    },
    "DiagramMover": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "label": {
          "type": [
            "string",
            "null"
          ]
        },
        "from": {
          "type": "array",
          "items": {
            "type": "number"
          }
        },
        "to": {
          "type": "array",
          "items": {
            "type": "number"
          }
        },
        "at_ms": {
          "type": "integer"
        },
        "dur_ms": {
          "type": "integer"
        },
        "out_ms": {
          "type": [
            "integer",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "from",
        "to",
        "at_ms",
        "dur_ms"
      ],
      "additionalProperties": false
    },
    "DiagramNode": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "label": {
          "type": "string"
        },
        "x": {
          "type": "number"
        },
        "y": {
          "type": "number"
        },
        "at_ms": {
          "type": "integer"
        },
        "out_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "tone": {
          "$ref": "#/$defs/DiagramTone",
          "default": "ink"
        }
      },
      "required": [
        "id",
        "label",
        "x",
        "y",
        "at_ms"
      ],
      "additionalProperties": false
    },
    "DiagramTone": {
      "type": "string",
      "description": "A diagram element's colour: the house ink, or the claim colour for what the scene asserts.",
      "enum": [
        "ink",
        "claim"
      ]
    },
    "DialogueLine": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "speaker": {
          "type": "string"
        },
        "to": {
          "description": "Who the line is said to: a character id, or one of the typed targets camera (direct address to the viewer), audience (to the viewer, but not at the lens) or group (to a room or several characters, who may be listed in among). A character whose id is one of those words is the character.",
          "type": [
            "string",
            "null"
          ]
        },
        "among": {
          "description": "For a line said to the group: the characters in it, when they are individually known.",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "start_ms": {
          "type": "integer"
        },
        "end_ms": {
          "type": "integer"
        },
        "turn": {
          "description": "The line turns the scene: how the listener takes it is part of the moment, so we need to see them.",
          "type": "boolean",
          "default": false
        },
        "text": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "speaker",
        "start_ms",
        "end_ms"
      ],
      "additionalProperties": false
    },
    "DramaticFunction": {
      "type": "string",
      "description": "A scene's dramatic job. The same words as EIL's function vocabulary, owned here and mapped by the adapter.",
      "enum": [
        "propulsion",
        "evidence",
        "contrast",
        "emphasis",
        "reset",
        "retention",
        "humour",
        "dread",
        "anticipation",
        "release",
        "rest"
      ]
    },
    "DrawingSoundStyle": {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean",
          "default": true
        },
        "gain_db": {
          "type": "number",
          "default": -18
        },
        "highlight": {
          "type": "boolean",
          "default": true
        },
        "circle": {
          "type": "boolean",
          "default": true
        },
        "route": {
          "type": "boolean",
          "default": true
        },
        "pins": {
          "type": "boolean",
          "default": false
        },
        "chapter": {
          "type": "boolean",
          "default": false
        },
        "subscribe": {
          "type": "boolean",
          "default": false
        }
      },
      "additionalProperties": false
    },
    "Facing": {
      "type": "string",
      "description": "Where a subject looks on screen.",
      "enum": [
        "left",
        "right",
        "camera",
        "away"
      ]
    },
    "Filming": {
      "type": "object",
      "description": "How a shot is filmed, when that differs from what its coverage role implies. role names a timing role of the profile's timing file (Shoot Plan's own names: MASTER, SINGLE, INSERT and so on).",
      "properties": {
        "role": {
          "type": [
            "string",
            "null"
          ]
        },
        "takes": {
          "description": "How many takes are planned.",
          "type": [
            "integer",
            "null"
          ]
        },
        "record_ms": {
          "description": "The recording that must be usable, in milliseconds, when it is more than the role's minimum.",
          "type": [
            "integer",
            "null"
          ]
        },
        "speech": {
          "description": "The shot records people speaking. When absent, it does if a line of its scene is spoken during it.",
          "type": [
            "boolean",
            "null"
          ]
        },
        "once": {
          "description": "The moment happens once and cannot be filmed again, so it is recorded in one go with a margin either side.",
          "type": [
            "boolean",
            "null"
          ]
        },
        "preroll_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "postroll_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "with": {
          "description": "A shot of this plan filmed at the same time, by a second camera. This shot takes no time of its own.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "additionalProperties": false
    },
    "FocusQuality": {
      "type": "string",
      "enum": [
        "clear",
        "selective",
        "narrowed",
        "disoriented",
        "recovering"
      ]
    },
    "FrameOrientation": {
      "type": "string",
      "description": "How a camera is held for a shot, which decides the frame's shape. A square frame is cut from either.",
      "enum": [
        "portrait",
        "landscape",
        "square"
      ]
    },
    "FrameSide": {
      "type": "string",
      "enum": [
        "left",
        "centre",
        "right"
      ]
    },
    "Hearing": {
      "type": "object",
      "properties": {
        "mode": {
          "$ref": "#/$defs/HearingMode"
        },
        "quality": {
          "$ref": "#/$defs/HearingQuality"
        },
        "whose": {
          "description": "For subjective hearing: the character or prop whose hearing it is.",
          "type": [
            "string",
            "null"
          ]
        },
        "why": {
          "type": "string"
        }
      },
      "required": [
        "mode",
        "quality",
        "why"
      ],
      "additionalProperties": false
    },
    "HearingMode": {
      "type": "string",
      "enum": [
        "objective",
        "subjective",
        "contrast",
        "silence"
      ]
    },
    "HearingQuality": {
      "type": "string",
      "enum": [
        "open",
        "near_body",
        "narrowed",
        "off_axis",
        "muffled",
        "expanding",
        "silent"
      ]
    },
    "HighlightConstructStyle": {
      "type": "object",
      "properties": {
        "colour": {
          "type": "string",
          "default": "#f4ce55"
        },
        "surface": {
          "type": "string",
          "default": "#253039"
        }
      },
      "additionalProperties": false
    },
    "HouseStyle": {
      "type": "object",
      "properties": {
        "map": {
          "type": [
            "string",
            "null"
          ]
        },
        "highlight": {
          "type": [
            "string",
            "null"
          ]
        },
        "chapter_card": {
          "type": [
            "string",
            "null"
          ]
        },
        "map_palette": {
          "anyOf": [
            {
              "$ref": "#/$defs/MapConstructStyle"
            },
            {
              "type": "null"
            }
          ]
        },
        "highlight_palette": {
          "anyOf": [
            {
              "$ref": "#/$defs/HighlightConstructStyle"
            },
            {
              "type": "null"
            }
          ]
        },
        "chapter_palette": {
          "anyOf": [
            {
              "$ref": "#/$defs/ChapterConstructStyle"
            },
            {
              "type": "null"
            }
          ]
        },
        "drawing_sounds": {
          "anyOf": [
            {
              "$ref": "#/$defs/DrawingSoundStyle"
            },
            {
              "type": "null"
            }
          ]
        },
        "presenter": {
          "anyOf": [
            {
              "$ref": "#/$defs/PresenterStyle"
            },
            {
              "type": "null"
            }
          ]
        },
        "timing": {
          "anyOf": [
            {
              "$ref": "#/$defs/TimingStyle"
            },
            {
              "type": "null"
            }
          ]
        },
        "structure": {
          "anyOf": [
            {
              "$ref": "#/$defs/StructureStyle"
            },
            {
              "type": "null"
            }
          ]
        },
        "sound": {
          "anyOf": [
            {
              "$ref": "#/$defs/SoundTargetStyle"
            },
            {
              "type": "null"
            }
          ]
        },
        "readiness": {
          "anyOf": [
            {
              "$ref": "#/$defs/ReadinessStyle"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "additionalProperties": false
    },
    "IntensityLevel": {
      "type": "string",
      "description": "How far forward a song's drums come (owner, 3 Oct 2026: \"the drums as an intensity lever\"): the song itself plays on; only its drums' prominence moves.",
      "enum": [
        "rest",
        "low",
        "mid",
        "high",
        "peak"
      ]
    },
    "IntensityPoint": {
      "type": "object",
      "description": "From this moment a music carrier's drums are at this level, reached over ramp_ms before it.",
      "properties": {
        "carrier": {
          "type": "string"
        },
        "at_ms": {
          "type": "integer"
        },
        "level": {
          "$ref": "#/$defs/IntensityLevel"
        },
        "why": {
          "description": "Why feeling rises or settles here.",
          "type": "string"
        },
        "ramp_ms": {
          "type": "integer",
          "default": 1200
        }
      },
      "required": [
        "carrier",
        "at_ms",
        "level",
        "why"
      ],
      "additionalProperties": false
    },
    "IntroductionWay": {
      "type": "string",
      "enum": [
        "entrance",
        "discovery",
        "light_reveal",
        "emergence",
        "return",
        "cause"
      ]
    },
    "JoinNeed": {
      "type": "string",
      "enum": [
        "existing_sufficient",
        "new_world",
        "distinct_planes",
        "comparison",
        "attention_transfer",
        "deliberate_break"
      ]
    },
    "JoinServes": {
      "type": "string",
      "description": "What a join is for: the story (something happens or changes) or attention (look here now).",
      "enum": [
        "story",
        "attention"
      ]
    },
    "Journey": {
      "type": "object",
      "description": "The journey (owner, 4 Oct 2026: \"Beats are silver; a journey is gold. Plan where the camera travels, and why, before the beats\"). M7 v1 matched pictures to lines and \"meant nothing\"; M7 v2 climbed a ladder. Stages are the route in order, each a place or state with its value on one axis, so every change of stage is a move the camera can make: up or down a height, out from home or back, in toward a destination.",
      "properties": {
        "axis": {
          "$ref": "#/$defs/JourneyAxis"
        },
        "stages": {
          "description": "The route in order. A place visited again is a new stage with the same name and a return reason.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/JourneyStage"
          }
        },
        "destination": {
          "$ref": "#/$defs/JourneyDestination"
        }
      },
      "required": [
        "axis",
        "stages",
        "destination"
      ],
      "additionalProperties": false
    },
    "JourneyAxis": {
      "type": "string",
      "description": "What a journey's stages measure: height (a ladder), distance out from home, or how far inward toward a destination.",
      "enum": [
        "height",
        "distance",
        "inward"
      ]
    },
    "JourneyDestination": {
      "type": "object",
      "properties": {
        "stage": {
          "type": "string"
        },
        "why": {
          "description": "Why the journey ends there, in the owner's words.",
          "type": "string"
        }
      },
      "required": [
        "stage",
        "why"
      ],
      "additionalProperties": false
    },
    "JourneyStage": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "name": {
          "description": "The place as it is called on screen (\"earth\"); the id when absent. Stages sharing a name are one place.",
          "type": [
            "string",
            "null"
          ]
        },
        "value": {
          "description": "Where the stage is on the journey's axis: a height, a distance out, or how far in.",
          "type": "number"
        },
        "return": {
          "description": "Why the route comes back to a place it has been (M7: \"down again to earth\").",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "value"
      ],
      "additionalProperties": false
    },
    "Kit": {
      "type": "object",
      "description": "What the crew has to shoot with.",
      "properties": {
        "cameras": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/KitCamera"
          }
        },
        "supports": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Support"
          }
        },
        "sound": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/SoundGear"
          }
        },
        "lights": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/LightGear"
          }
        }
      },
      "additionalProperties": false
    },
    "KitCamera": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "kind": {
          "$ref": "#/$defs/CameraKind"
        },
        "description": {
          "description": "The device in words (\"iPhone 15\", \"Sony FX3\").",
          "type": [
            "string",
            "null"
          ]
        },
        "lens_multipliers": {
          "description": "For a phone: its lenses as multipliers of the main camera, for example [0.5, 1, 3].",
          "type": "array",
          "items": {
            "type": "number"
          }
        },
        "lenses": {
          "description": "For a camera: its lenses. A prime gives mm; a zoom gives min_mm and max_mm.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/KitLens"
          }
        },
        "crop_factor": {
          "description": "For a camera: the sensor's crop factor against full frame (1 full frame, 1.5 APS-C, 2 Micro Four Thirds).",
          "type": [
            "number",
            "null"
          ]
        },
        "sensor": {
          "description": "For a camera: the sensor in words (\"full frame\", \"Super 35\").",
          "type": [
            "string",
            "null"
          ]
        },
        "frame_rates": {
          "description": "The frame rates it records, in frames per second.",
          "type": "array",
          "items": {
            "type": "number"
          }
        }
      },
      "required": [
        "id",
        "kind"
      ],
      "additionalProperties": false
    },
    "KitLens": {
      "type": "object",
      "properties": {
        "mm": {
          "description": "A prime lens's focal length in millimetres.",
          "type": [
            "number",
            "null"
          ]
        },
        "min_mm": {
          "description": "A zoom's shortest and longest focal length in millimetres.",
          "type": [
            "number",
            "null"
          ]
        },
        "max_mm": {
          "type": [
            "number",
            "null"
          ]
        }
      },
      "additionalProperties": false
    },
    "LandsOn": {
      "type": "object",
      "properties": {
        "carrier": {
          "type": "string"
        },
        "unit": {
          "$ref": "#/$defs/PulseUnit",
          "default": "beat"
        }
      },
      "required": [
        "carrier"
      ],
      "additionalProperties": false
    },
    "Layer": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "depth": {
          "$ref": "#/$defs/LayerDepth"
        },
        "subject": {
          "description": "The character, prop or location the layer shows.",
          "type": "string"
        },
        "purpose": {
          "description": "What this layer does for the story.",
          "type": "string"
        },
        "start_ms": {
          "description": "When the layer is present, inside the shot; without them, the whole shot.",
          "type": [
            "integer",
            "null"
          ]
        },
        "end_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "motion": {
          "anyOf": [
            {
              "$ref": "#/$defs/LayerMove"
            },
            {
              "type": "null"
            }
          ]
        },
        "presence": {
          "$ref": "#/$defs/LayerPresence",
          "default": "full"
        },
        "entrance": {
          "$ref": "#/$defs/LayerEntrance",
          "default": "cut"
        },
        "exit": {
          "$ref": "#/$defs/LayerExit",
          "default": "cut"
        },
        "region": {
          "$ref": "#/$defs/LayerRegion",
          "default": "full"
        },
        "style": {
          "description": "A named technique for how the layer appears and behaves (for example vision_swell).",
          "type": [
            "string",
            "null"
          ]
        },
        "at": {
          "description": "Where a converging layer settles: its centre and its size, as shares of the frame (motion converge).",
          "anyOf": [
            {
              "$ref": "#/$defs/LayerPlace"
            },
            {
              "type": "null"
            }
          ]
        },
        "keys": {
          "description": "The layer animated by keys (a cut-out figure crossing a painted landscape, a mist band sweeping across): where it is, how large, how turned and how strongly there at each key, eased between them. A key can land on a spoken moment. Keys and a motion together are the motion first, then the keys.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/LayerKey"
          }
        },
        "actions": {
          "description": "Repeating movements over the keys (the bob of a walk while the keys carry the figure along).",
          "type": "array",
          "items": {
            "$ref": "#/$defs/LayerAction"
          }
        },
        "parallax": {
          "description": "How much the layer follows the shot's window as it travels over its picture: 1 moves with the picture behind, more than 1 is nearer (moves further), less is farther. Default by depth: background 1, intermediate 1.25, foreground 1.6.",
          "type": [
            "number",
            "null"
          ]
        },
        "pivot": {
          "description": "The point the layer turns and scales about, as fractions of the frame (a figure's feet: [0.5, 0.92]). Default: the centre of its subject's box when one is bound, else the frame's centre.",
          "type": [
            "array",
            "null"
          ],
          "items": {
            "type": "number"
          }
        }
      },
      "required": [
        "id",
        "depth",
        "subject",
        "purpose"
      ],
      "additionalProperties": false
    },
    "LayerAction": {
      "type": "object",
      "description": "A repeating movement of a layer for part of its time (the whole of it without start and end).",
      "properties": {
        "kind": {
          "$ref": "#/$defs/LayerActionKind"
        },
        "start_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "end_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "amount": {
          "$ref": "#/$defs/LayerAmount",
          "default": "subtle"
        },
        "rate": {
          "description": "Cycles per second (a walk's steps); default by kind.",
          "type": [
            "number",
            "null"
          ]
        }
      },
      "required": [
        "kind"
      ],
      "additionalProperties": false
    },
    "LayerActionKind": {
      "type": "string",
      "description": "A repeating movement of a cut-out figure, in the shadow-puppet idiom: the bob and sway of a walk, a slow breath, a sway in wind, the flicker of a flame, the flutter of cloth.",
      "enum": [
        "walk",
        "bob",
        "sway",
        "breathe",
        "flicker",
        "flutter"
      ]
    },
    "LayerAmount": {
      "type": "string",
      "enum": [
        "subtle",
        "moderate",
        "strong"
      ]
    },
    "LayerDepth": {
      "type": "string",
      "description": "Where a layer sits in a shot's depth: the room behind, anything between, the subject in front.",
      "enum": [
        "background",
        "intermediate",
        "foreground"
      ]
    },
    "LayerDirection": {
      "type": "string",
      "enum": [
        "none",
        "left",
        "right",
        "up",
        "down",
        "toward",
        "away"
      ]
    },
    "LayerEase": {
      "type": "string",
      "description": "How a keyed layer moves into a key: evenly, easing in and out, starting slow, arriving slow, jumping, or smooth (passing through the key without stopping: the velocity carries on; at rest only at the ends and where the move turns).",
      "enum": [
        "in_out",
        "linear",
        "in",
        "out",
        "hold",
        "smooth"
      ]
    },
    "LayerEntrance": {
      "type": "string",
      "enum": [
        "cut",
        "fade",
        "emerge",
        "slide_in"
      ]
    },
    "LayerExit": {
      "type": "string",
      "enum": [
        "cut",
        "fade",
        "dissolve",
        "slide_off"
      ]
    },
    "LayerKey": {
      "type": "object",
      "description": "One key of an animated layer or of a shot's window. Offsets are fractions of the frame from the layer's own place; scale multiplies; rotation is in degrees; a property left out holds its value from the key before.",
      "properties": {
        "at_ms": {
          "type": "integer"
        },
        "anchor": {
          "description": "The spoken moment the key lands on; at_ms must agree with it (a step lands on a word).",
          "anyOf": [
            {
              "$ref": "#/$defs/SpeechAnchor"
            },
            {
              "type": "null"
            }
          ]
        },
        "x": {
          "type": [
            "number",
            "null"
          ]
        },
        "y": {
          "type": [
            "number",
            "null"
          ]
        },
        "scale": {
          "type": [
            "number",
            "null"
          ]
        },
        "rotation": {
          "type": [
            "number",
            "null"
          ]
        },
        "opacity": {
          "type": [
            "number",
            "null"
          ]
        },
        "turn": {
          "description": "Which way a cut-out faces: 1 as drawn, -1 mirrored. Between them it turns edge-on, as a shadow puppet is turned on its screen.",
          "type": [
            "number",
            "null"
          ]
        },
        "ease": {
          "description": "How the layer moves from the key before into this one.",
          "$ref": "#/$defs/LayerEase",
          "default": "in_out"
        }
      },
      "required": [
        "at_ms"
      ],
      "additionalProperties": false
    },
    "LayerMove": {
      "type": "object",
      "properties": {
        "kind": {
          "$ref": "#/$defs/LayerMoveKind"
        },
        "direction": {
          "$ref": "#/$defs/LayerDirection",
          "default": "none"
        },
        "amount": {
          "$ref": "#/$defs/LayerAmount",
          "default": "subtle"
        },
        "distance": {
          "description": "How far exactly, as a share of the frame (0 to 1), in place of the house style's amount: a layer that travels (Kingdom M3, 3 Oct 2026: the fire pot and torch cross the whole drawn path, about two thirds of the frame; the house amounts move a layer a few percent). A drift travels from this far one way to this far the other.",
          "type": [
            "number",
            "null"
          ]
        }
      },
      "required": [
        "kind"
      ],
      "additionalProperties": false
    },
    "LayerMoveKind": {
      "type": "string",
      "enum": [
        "hold",
        "drift",
        "rise",
        "descend",
        "slide_in",
        "slide_off",
        "push",
        "pull",
        "parallax",
        "converge"
      ]
    },
    "LayerPlace": {
      "type": "object",
      "description": "A place in the frame: centre x and y (0 left/top to 1 right/bottom) and size (the layer's width as a share of the frame's). The convergence (Yahweh M6, \"That's him\": the flame, the cloud and the throne travel into one frame and settle, the three icons together for the first time).",
      "properties": {
        "x": {
          "type": "number"
        },
        "y": {
          "type": "number"
        },
        "scale": {
          "type": "number"
        }
      },
      "required": [
        "x",
        "y",
        "scale"
      ],
      "additionalProperties": false
    },
    "LayerPresence": {
      "type": "string",
      "enum": [
        "faint",
        "present",
        "full"
      ]
    },
    "LayerRegion": {
      "type": "string",
      "description": "Where in the frame a layer sits; the rest of it is left to the layers behind.",
      "enum": [
        "full",
        "upper",
        "lower",
        "left",
        "right"
      ]
    },
    "Lens": {
      "type": "string",
      "enum": [
        "fisheye",
        "ultra_wide",
        "wide",
        "medium",
        "telephoto",
        "super_telephoto",
        "anamorphic_ultra_wide",
        "anamorphic_wide",
        "anamorphic_medium",
        "anamorphic_telephoto"
      ]
    },
    "LightGear": {
      "type": "string",
      "enum": [
        "none",
        "practical",
        "led_panel",
        "reflector"
      ]
    },
    "Location": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "parts": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "time_of_day": {
          "type": [
            "string",
            "null"
          ]
        },
        "light": {
          "type": [
            "string",
            "null"
          ]
        },
        "setting": {
          "description": "Short-form profile: indoors or outdoors. Outdoors, the shots are held to the usable light.",
          "anyOf": [
            {
              "$ref": "#/$defs/Setting"
            },
            {
              "type": "null"
            }
          ]
        },
        "usable_light": {
          "description": "Short-form profile: when there is light enough to film, as clock times. Outdoors it is the daylight; indoors, say it only when daylight is what lights the room.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/TimeWindow"
          }
        },
        "sun": {
          "description": "Short-form profile: where the sun is, if known (\"low from the west after 16:00\").",
          "type": [
            "string",
            "null"
          ]
        },
        "access": {
          "description": "Short-form profile: when the crew may be at the place (it opens, closes, is booked). Without any, always.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/TimeWindow"
          }
        },
        "acoustics": {
          "description": "Short-form profile: how the place sounds while people speak.",
          "anyOf": [
            {
              "$ref": "#/$defs/Acoustics"
            },
            {
              "type": "null"
            }
          ]
        },
        "quieter_position": {
          "description": "Short-form profile: whether a speaker may move somewhere quieter.",
          "type": [
            "boolean",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "description"
      ],
      "additionalProperties": false
    },
    "LoudnessTarget": {
      "type": "object",
      "properties": {
        "integrated_lufs": {
          "type": "number",
          "default": -14
        },
        "true_peak_dbtp": {
          "type": "number",
          "default": -1
        }
      },
      "additionalProperties": false
    },
    "MapCameraPlan": {
      "type": "object",
      "properties": {
        "from": {
          "description": "Longitude/latitude bounds [west, south, east, north] at each end of the zoom.",
          "type": "array",
          "items": {
            "type": "number"
          }
        },
        "to": {
          "type": "array",
          "items": {
            "type": "number"
          }
        },
        "start_ms": {
          "type": "integer"
        },
        "end_ms": {
          "type": "integer"
        }
      },
      "required": [
        "from",
        "to",
        "start_ms",
        "end_ms"
      ],
      "additionalProperties": false
    },
    "MapConstructStyle": {
      "type": "object",
      "properties": {
        "sea": {
          "type": "string",
          "default": "#102938"
        },
        "land": {
          "type": "string",
          "default": "#d3c5a7"
        },
        "border": {
          "type": "string",
          "default": "#61747a"
        },
        "label": {
          "type": "string",
          "default": "#ffffff"
        },
        "accent": {
          "type": "string",
          "default": "#e4b75e"
        },
        "highlight": {
          "type": "string",
          "default": "#e4b75e"
        },
        "places": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/MapNamedPlace"
          }
        }
      },
      "additionalProperties": false
    },
    "MapNamedPlace": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "label": {
          "type": "string"
        },
        "lon": {
          "type": "number",
          "default": 0
        },
        "lat": {
          "type": "number",
          "default": 0
        },
        "emblem": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "label"
      ],
      "additionalProperties": false
    },
    "MessagePhrase": {
      "type": "object",
      "properties": {
        "in_ms": {
          "type": "integer"
        },
        "out_ms": {
          "type": "integer"
        }
      },
      "required": [
        "in_ms",
        "out_ms"
      ],
      "additionalProperties": false
    },
    "MotionEnergyKind": {
      "type": "string",
      "enum": [
        "still",
        "low",
        "building",
        "high",
        "decaying"
      ]
    },
    "MotionFeel": {
      "type": "object",
      "properties": {
        "path": {
          "$ref": "#/$defs/MotionPathKind"
        },
        "variation": {
          "$ref": "#/$defs/MotionVariationKind"
        },
        "weight": {
          "$ref": "#/$defs/MotionWeight"
        },
        "force": {
          "$ref": "#/$defs/MotionForce"
        },
        "energy": {
          "$ref": "#/$defs/MotionEnergyKind"
        },
        "irregular": {
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "path",
        "variation",
        "weight",
        "force",
        "energy"
      ],
      "additionalProperties": false
    },
    "MotionForce": {
      "type": "string",
      "enum": [
        "wind",
        "gravity",
        "contact",
        "current",
        "self_propelled",
        "none"
      ]
    },
    "MotionPathKind": {
      "type": "string",
      "enum": [
        "onward",
        "meander",
        "rise",
        "settle",
        "orbit",
        "stationary"
      ]
    },
    "MotionRamp": {
      "type": "object",
      "properties": {
        "to": {
          "description": "What the motion ramps to; today: still.",
          "type": "string"
        },
        "at_ms": {
          "description": "The moment the motion has stopped (film time).",
          "type": "integer"
        },
        "why": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "to",
        "at_ms"
      ],
      "additionalProperties": false
    },
    "MotionVariationKind": {
      "type": "string",
      "enum": [
        "organic",
        "controlled",
        "still"
      ]
    },
    "MotionWeight": {
      "type": "string",
      "enum": [
        "buoyant",
        "ballistic",
        "resisted",
        "weighty",
        "controlled"
      ]
    },
    "Move": {
      "type": "object",
      "properties": {
        "from": {
          "type": "string"
        },
        "to": {
          "type": "string"
        },
        "minutes": {
          "type": "integer"
        }
      },
      "required": [
        "from",
        "to",
        "minutes"
      ],
      "additionalProperties": false
    },
    "MusicShape": {
      "type": "string",
      "enum": [
        "sustain",
        "build",
        "resolve"
      ]
    },
    "Overlay": {
      "type": "object",
      "description": "A layer over a shot's picture for part of its length.",
      "properties": {
        "id": {
          "type": "string"
        },
        "kind": {
          "$ref": "#/$defs/OverlayKind"
        },
        "start_ms": {
          "type": "integer"
        },
        "end_ms": {
          "type": "integer"
        },
        "purpose": {
          "type": "string"
        },
        "position": {
          "$ref": "#/$defs/OverlayPosition",
          "default": "centre"
        },
        "subject": {
          "description": "For an inset: the character, prop or location it shows.",
          "type": [
            "string",
            "null"
          ]
        },
        "text": {
          "type": [
            "string",
            "null"
          ]
        },
        "subtitle": {
          "type": [
            "string",
            "null"
          ]
        },
        "words": {
          "description": "For words that land one by one: each word and when it lands.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/TimedWord"
          }
        },
        "style": {
          "description": "A named technique for how the layer arrives (for example soft_rise); realisation draws it.",
          "type": [
            "string",
            "null"
          ]
        },
        "count": {
          "description": "For a counter: the number it runs from and to over its time (its text is the label).",
          "anyOf": [
            {
              "$ref": "#/$defs/Count"
            },
            {
              "type": "null"
            }
          ]
        },
        "anchor": {
          "description": "The spoken moment it appears on; start_ms must agree with it.",
          "anyOf": [
            {
              "$ref": "#/$defs/SpeechAnchor"
            },
            {
              "type": "null"
            }
          ]
        },
        "timing": {
          "description": "The allowed range for its end (a caption held long enough, cleared in time).",
          "anyOf": [
            {
              "$ref": "#/$defs/CaptionTiming"
            },
            {
              "type": "null"
            }
          ]
        },
        "backing": {
          "description": "The shape behind the text: solid black, the house accent panel, a band, or none. Chosen per use (owner, 1 Oct 2026); absent, the house style's default for the text's technique applies.",
          "anyOf": [
            {
              "$ref": "#/$defs/TextBacking"
            },
            {
              "type": "null"
            }
          ]
        },
        "diagram": {
          "description": "For a diagram: a simple animation over the picture that explains the point (owner, 3 Oct 2026: the videos are the background; \"the layer over the top should be simple animation\"). It may run on across the following shots of its scene, since an argument drawn over a cut must not restart.",
          "anyOf": [
            {
              "$ref": "#/$defs/Diagram"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "kind",
        "start_ms",
        "end_ms",
        "purpose"
      ],
      "additionalProperties": false
    },
    "OverlayKind": {
      "type": "string",
      "enum": [
        "inset",
        "nameplate",
        "words",
        "caption",
        "counter",
        "diagram"
      ]
    },
    "OverlayPosition": {
      "type": "string",
      "enum": [
        "centre",
        "top_left",
        "top_right",
        "bottom_left",
        "bottom_right",
        "lower_third",
        "upper_third"
      ]
    },
    "PathPhase": {
      "type": "object",
      "description": "One phase of a travelling camera's path: from this moment, how it moves and relative to what.",
      "properties": {
        "at_ms": {
          "type": "integer"
        },
        "movement": {
          "$ref": "#/$defs/CameraMovement"
        },
        "size": {
          "description": "The framing this phase arrives at; without it, the shot's size.",
          "anyOf": [
            {
              "$ref": "#/$defs/ShotSize"
            },
            {
              "type": "null"
            }
          ]
        },
        "relation": {
          "anyOf": [
            {
              "$ref": "#/$defs/PathRelationKind"
            },
            {
              "type": "null"
            }
          ]
        },
        "subject": {
          "description": "The character, prop or location the phase moves relative to.",
          "type": [
            "string",
            "null"
          ]
        },
        "side": {
          "$ref": "#/$defs/PathSideKind",
          "default": "none"
        },
        "into": {
          "description": "The part of the scene's location this phase carries the viewer into.",
          "type": [
            "string",
            "null"
          ]
        },
        "note": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "at_ms",
        "movement"
      ],
      "additionalProperties": false
    },
    "PathRelationKind": {
      "type": "string",
      "description": "How a camera path phase moves relative to its subject.",
      "enum": [
        "from",
        "around",
        "past",
        "over",
        "through",
        "toward",
        "into",
        "away_from",
        "with"
      ]
    },
    "PathSideKind": {
      "type": "string",
      "enum": [
        "none",
        "left",
        "right",
        "above",
        "below",
        "behind",
        "front",
        "over_left_shoulder",
        "over_right_shoulder"
      ]
    },
    "Person": {
      "type": "object",
      "description": "A cast or crew member, the roles they play on the shoot and when they are there.",
      "properties": {
        "id": {
          "type": "string"
        },
        "name": {
          "type": [
            "string",
            "null"
          ]
        },
        "roles": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/PersonRole"
          }
        },
        "character": {
          "description": "For talent: the character they play (a character id), so a shot that shows the character needs this person.",
          "type": [
            "string",
            "null"
          ]
        },
        "available": {
          "description": "When they are on hand. Without any, they are on hand all day, every day.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/TimeWindow"
          }
        }
      },
      "required": [
        "id",
        "roles"
      ],
      "additionalProperties": false
    },
    "PersonRole": {
      "type": "string",
      "enum": [
        "talent",
        "camera",
        "sound",
        "director",
        "helper"
      ]
    },
    "Perspective": {
      "type": "string",
      "description": "Whose view the shot takes. DPL-only; the MovieLabs vocabulary has no such category.",
      "enum": [
        "objective",
        "subjective",
        "witness",
        "inspection",
        "over_the_shoulder"
      ]
    },
    "Phrase": {
      "type": "object",
      "properties": {
        "carrier": {
          "type": "string"
        },
        "move": {
          "$ref": "#/$defs/PhraseMove"
        },
        "timing": {
          "$ref": "#/$defs/PhraseTiming"
        },
        "keeps": {
          "$ref": "#/$defs/PhraseKeeps"
        },
        "what": {
          "description": "What the phrase is, in words.",
          "type": "string"
        },
        "register_from": {
          "type": "string"
        },
        "register_to": {
          "type": "string"
        },
        "placement": {
          "$ref": "#/$defs/PhrasePlacement",
          "default": "with_picture"
        },
        "side": {
          "description": "Where it is heard across the stereo field; both is two placements answering each other (antiphony). Realised by the binding as clip pan, checked on the render (stereo_placement).",
          "anyOf": [
            {
              "$ref": "#/$defs/StereoSide"
            },
            {
              "type": "null"
            }
          ]
        },
        "motif": {
          "description": "For music: the motif the phrase plays.",
          "type": [
            "string",
            "null"
          ]
        },
        "shape": {
          "description": "For music: where the cue is heading.",
          "anyOf": [
            {
              "$ref": "#/$defs/MusicShape"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "carrier",
        "move",
        "timing",
        "keeps",
        "what",
        "register_from",
        "register_to"
      ],
      "additionalProperties": false
    },
    "PhraseKeeps": {
      "type": "string",
      "description": "What stays recognisable in the phrase.",
      "enum": [
        "motif",
        "tempo",
        "harmony",
        "motif_tempo_harmony",
        "environment",
        "deliberate_break"
      ]
    },
    "PhraseMove": {
      "type": "string",
      "description": "What a sound's phrase does in a scene: begins, carries on across the cut, changes, returns, ends or leads.",
      "enum": [
        "enter",
        "carry",
        "transform",
        "reprise",
        "exit",
        "foreground"
      ]
    },
    "PhrasePlacement": {
      "type": "string",
      "enum": [
        "with_picture",
        "before_picture",
        "across_picture",
        "after_picture"
      ]
    },
    "PhraseTiming": {
      "type": "string",
      "enum": [
        "rhetorical",
        "breath",
        "musical",
        "emphasis"
      ]
    },
    "PictureKind": {
      "type": "string",
      "enum": [
        "source_document",
        "official_document",
        "map",
        "portrait",
        "reconstruction",
        "footage",
        "presenter",
        "chapter_card",
        "visual_pun",
        "quote_text",
        "split_screen",
        "sponsor_set",
        "other"
      ]
    },
    "PictureMeasure": {
      "type": "string",
      "description": "What a rule measures on the picture: mean brightness at least Value, or the share of the frame in deep shadow (luma under 12 %) at most Value.",
      "enum": [
        "min_brightness",
        "max_shadow_share"
      ]
    },
    "PresenterStyle": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "max_gap_ms": {
          "type": [
            "integer",
            "null"
          ]
        }
      },
      "required": [
        "id"
      ],
      "additionalProperties": false
    },
    "Prop": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "appearance": {
          "description": "What the prop looks like in a picture (for generation), apart from what it means in the story.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "description"
      ],
      "additionalProperties": false
    },
    "PropState": {
      "type": "object",
      "properties": {
        "prop": {
          "type": "string"
        },
        "state": {
          "type": "string"
        }
      },
      "required": [
        "prop",
        "state"
      ],
      "additionalProperties": false
    },
    "Provenance": {
      "type": "object",
      "description": "Where an element of the plan came from. It does not change what the element means, and no rule reads it.",
      "properties": {
        "by": {
          "$ref": "#/$defs/ProvenanceBy"
        },
        "ref": {
          "description": "What it came from, when that can be named: a Shoot Plan shot id, a script line, a study's id.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "by"
      ],
      "additionalProperties": false
    },
    "ProvenanceBy": {
      "type": "string",
      "description": "Who or what set a scene or shot (provenance). Inert metadata: readiness ignores it.",
      "enum": [
        "human",
        "draft_model",
        "shoot_plan_planner",
        "script_import",
        "ingress"
      ]
    },
    "Pulse": {
      "type": "object",
      "properties": {
        "period_ms": {
          "type": "integer"
        },
        "phase_ms": {
          "description": "Film time of one beat on the grid (the first downbeat).",
          "type": "integer"
        },
        "beats_per_bar": {
          "type": "integer",
          "default": 4
        }
      },
      "required": [
        "period_ms",
        "phase_ms"
      ],
      "additionalProperties": false
    },
    "PulseUnit": {
      "type": "string",
      "enum": [
        "beat",
        "bar"
      ]
    },
    "QuietBegins": {
      "type": "string",
      "description": "How an authored quiet begins: sounds are held back from starting, or cut off.",
      "enum": [
        "hold_back",
        "cut_off"
      ]
    },
    "QuietDesign": {
      "type": "object",
      "properties": {
        "begins": {
          "$ref": "#/$defs/QuietBegins"
        },
        "ends": {
          "$ref": "#/$defs/QuietEnds"
        },
        "must_hold": {
          "description": "True when nothing may break into the quiet.",
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "begins",
        "ends"
      ],
      "additionalProperties": false
    },
    "QuietEnds": {
      "type": "string",
      "description": "How an authored quiet ends: the same sounds return, something new is revealed, or the world resets.",
      "enum": [
        "return",
        "reveal",
        "reset"
      ]
    },
    "ReadinessStyle": {
      "type": "object",
      "description": "Where this film's craft differs from the conventions the edit-readiness checks start from. Every member is optional; one left out keeps the check's own figure. A slow, held-shot film raises the hold limits; a fast one lowers them.",
      "properties": {
        "hold_insert_ms": {
          "description": "The longest an insert (a detail glance) may run before the hold is flagged, apart from speech inside it.",
          "type": [
            "integer",
            "null"
          ]
        },
        "hold_reaction_ms": {
          "description": "The longest a reaction may run, apart from speech inside it.",
          "type": [
            "integer",
            "null"
          ]
        },
        "hold_cutaway_ms": {
          "description": "The longest a cutaway may run, apart from speech inside it.",
          "type": [
            "integer",
            "null"
          ]
        },
        "hold_neutral_ms": {
          "description": "The longest a neutral (bridging) shot may run, apart from speech inside it.",
          "type": [
            "integer",
            "null"
          ]
        },
        "reaction_window_ms": {
          "description": "How long after a turning line ends the listener may first appear and still count as answering it.",
          "type": [
            "integer",
            "null"
          ]
        }
      },
      "additionalProperties": false
    },
    "Recall": {
      "type": "object",
      "properties": {
        "movement": {
          "description": "The earlier movement's id (for example m3, opening).",
          "type": "string"
        },
        "shot": {
          "description": "The shot in that movement whose approved media is reused.",
          "type": "string"
        }
      },
      "required": [
        "movement",
        "shot"
      ],
      "additionalProperties": false
    },
    "Removal": {
      "type": "object",
      "description": "Something taken away at a moment: a layer of a shot in the scene, or one stem of a sound carrier (for example the drums of the music).",
      "properties": {
        "at_ms": {
          "type": "integer"
        },
        "layer": {
          "type": [
            "string",
            "null"
          ]
        },
        "carrier": {
          "type": [
            "string",
            "null"
          ]
        },
        "stem": {
          "description": "The part of the carrier's sound removed (drums, bass, other, vocals); the whole carrier when absent.",
          "type": [
            "string",
            "null"
          ]
        },
        "why": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "at_ms"
      ],
      "additionalProperties": false
    },
    "RigAction": {
      "type": "string",
      "description": "A puppet rig's named actions (tools/eil-prep/puppet.py, ported in the realiser).",
      "enum": [
        "walk",
        "argue",
        "turn_head",
        "kneel",
        "look_up",
        "point",
        "climb",
        "stumble"
      ]
    },
    "RigAttachment": {
      "type": "object",
      "properties": {
        "to": {
          "type": "string"
        },
        "at": {
          "type": "array",
          "items": {
            "type": "number"
          }
        },
        "upright": {
          "description": "For a walking staff, keep its painted shaft at its rest world angle.",
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "to",
        "at"
      ],
      "additionalProperties": false
    },
    "RigBody": {
      "type": "string",
      "description": "A rig's body: the standard jointed figure, or parts of its own.",
      "enum": [
        "figure",
        "own"
      ]
    },
    "RigPart": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "parent": {
          "description": "The part it hangs from; none for the root.",
          "type": [
            "string",
            "null"
          ]
        },
        "size": {
          "description": "The part's picture as fractions of the rig's box (width, height).",
          "type": "array",
          "items": {
            "type": "number"
          }
        },
        "pivot": {
          "description": "Where it turns, as fractions of its own picture.",
          "type": "array",
          "items": {
            "type": "number"
          }
        },
        "at": {
          "description": "Where its pivot sits: on its parent's picture (fractions), or for the root on the box.",
          "type": "array",
          "items": {
            "type": "number"
          }
        },
        "sole": {
          "description": "For a painted shin, its opaque sole point in its own image.",
          "type": [
            "array",
            "null"
          ],
          "items": {
            "type": "number"
          }
        },
        "min": {
          "description": "Neutral-relative rotation bounds in degrees, measured from this cutout's paint overlap.",
          "type": [
            "number",
            "null"
          ]
        },
        "max": {
          "type": [
            "number",
            "null"
          ]
        },
        "z": {
          "description": "Drawing order: higher in front.",
          "type": "integer",
          "default": 0
        },
        "secondary": {
          "anyOf": [
            {
              "$ref": "#/$defs/RigSecondary"
            },
            {
              "type": "null"
            }
          ]
        },
        "attach": {
          "anyOf": [
            {
              "$ref": "#/$defs/RigAttachment"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "size",
        "pivot",
        "at"
      ],
      "additionalProperties": false
    },
    "RigSecondary": {
      "type": "object",
      "properties": {
        "lag_ms": {
          "type": "integer",
          "default": 120
        },
        "damping": {
          "type": "number",
          "default": 0.35
        }
      },
      "additionalProperties": false
    },
    "RuleMeasure": {
      "type": "object",
      "description": "One measurable consequence of a continuity rule, checked on the render. Values are fractions (0..1).",
      "properties": {
        "kind": {
          "$ref": "#/$defs/PictureMeasure"
        },
        "value": {
          "type": "number"
        }
      },
      "required": [
        "kind",
        "value"
      ],
      "additionalProperties": false
    },
    "Scene": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "purpose": {
          "description": "What the scene is for.",
          "type": "string"
        },
        "change": {
          "description": "What is different for the audience at the end of the scene.",
          "type": [
            "string",
            "null"
          ]
        },
        "functions": {
          "description": "The scene's dramatic job in typed form, and which shot or sound carries it.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/SceneFunction"
          }
        },
        "start_ms": {
          "type": "integer"
        },
        "end_ms": {
          "type": "integer"
        },
        "location": {
          "type": [
            "string",
            "null"
          ]
        },
        "entry": {
          "description": "How the audience arrives in the scene's world.",
          "anyOf": [
            {
              "$ref": "#/$defs/SceneEntry"
            },
            {
              "type": "null"
            }
          ]
        },
        "shots": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Shot"
          }
        },
        "attention": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/AttentionBeat"
          }
        },
        "sound": {
          "description": "What each kind of sound is doing in this scene, beyond the moment-by-moment sound beats.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/SceneSound"
          }
        },
        "hearing": {
          "description": "Whose hearing the scene takes, and how the world sounds to it.",
          "anyOf": [
            {
              "$ref": "#/$defs/Hearing"
            },
            {
              "type": "null"
            }
          ]
        },
        "phrases": {
          "description": "How each heard sound's phrase runs in this scene.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Phrase"
          }
        },
        "treatments": {
          "description": "The directions considered for this scene — different angles on the same job — with the one taken and why. Imagination made visible: a plan says what it could have been, not only what it is.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Treatment"
          }
        },
        "strands": {
          "description": "What words, sound and picture each do in this scene. One leads; the others answer, support or rest.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/StrandPart"
          }
        },
        "removals": {
          "description": "Subtraction: things taken away at a moment (a picture layer, a part of the music), so picture and sound can lose something together on the same word.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Removal"
          }
        },
        "axis": {
          "description": "The line of action: which subject is on the left of frame and which on the right, from the camera's side of the line. Every shot keeps them there unless the camera crosses on screen.",
          "anyOf": [
            {
              "$ref": "#/$defs/Axis"
            },
            {
              "type": "null"
            }
          ]
        },
        "travel": {
          "description": "The way travel runs across the screen in this scene (a chase, a journey), kept from shot to shot.",
          "anyOf": [
            {
              "$ref": "#/$defs/ScreenTravel"
            },
            {
              "type": "null"
            }
          ]
        },
        "lines": {
          "description": "The spoken lines: who says each, to whom, when, and whether the listener's response carries the moment.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/DialogueLine"
          }
        },
        "state_changes": {
          "description": "Things that change state during the scene (a glass breaks, a door opens), and whether we must see it.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/StateChange"
          }
        },
        "accepts": {
          "description": "Deliberate departures from editing convention, each with its reason (for example a scene played in one take). An accepted finding is reported as overridden, not as a fault.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Acceptance"
          }
        },
        "none": {
          "description": "What the scene deliberately does not have, said outright: spoken_lines (nobody speaks on screen) and line_of_action (staged without a line, such as 360-degree or line-free staging). Saying so is different from saying nothing: dpl missing stops asking, and the line-of-action check knows the staging is chosen.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Absence"
          }
        },
        "journey_stage": {
          "description": "The journey stage the scene is at (a stage id of the journey that governs it). Its location, parts and entry stay as they are; the stage sits above them.",
          "type": [
            "string",
            "null"
          ]
        },
        "provenance": {
          "description": "Who or what set this scene. Inert: no rule reads it.",
          "anyOf": [
            {
              "$ref": "#/$defs/Provenance"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "purpose",
        "start_ms",
        "end_ms",
        "shots"
      ],
      "additionalProperties": false
    },
    "SceneEntry": {
      "type": "object",
      "properties": {
        "how": {
          "description": "For example \"open\", \"transport\" (taken somewhere new) or \"return\" (back to a known place).",
          "type": "string"
        },
        "from_part": {
          "description": "The part of the location the audience leaves, if any.",
          "type": [
            "string",
            "null"
          ]
        },
        "to_part": {
          "description": "The part of the scene's location the audience arrives in.",
          "type": [
            "string",
            "null"
          ]
        },
        "established_by": {
          "description": "The shot that establishes the arrival.",
          "type": [
            "string",
            "null"
          ]
        },
        "note": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "how"
      ],
      "additionalProperties": false
    },
    "SceneFunction": {
      "type": "object",
      "properties": {
        "type": {
          "$ref": "#/$defs/DramaticFunction"
        },
        "lead": {
          "description": "The shot or sound carrier chiefly responsible.",
          "type": "string"
        },
        "support": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "type",
        "lead"
      ],
      "additionalProperties": false
    },
    "SceneSound": {
      "type": "object",
      "properties": {
        "kind": {
          "$ref": "#/$defs/SoundKind"
        },
        "intent": {
          "type": "string"
        },
        "must_not": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "kind",
        "intent"
      ],
      "additionalProperties": false
    },
    "Schedule": {
      "type": "object",
      "description": "The shoot days. A shot belongs to a day through its set-up (the day lists the set-ups it films), or else through its scene's location.",
      "properties": {
        "days": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ShootDay"
          }
        }
      },
      "required": [
        "days"
      ],
      "additionalProperties": false
    },
    "ScreenTravel": {
      "type": "string",
      "description": "Which way something travels on screen.",
      "enum": [
        "left",
        "right",
        "toward",
        "away",
        "none"
      ]
    },
    "Sequence": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "purpose": {
          "type": "string"
        },
        "scenes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "direction": {
          "description": "Where the sequence is heading: the one direction words, sound and picture all move in (for example \"from vast to one\").",
          "type": [
            "string",
            "null"
          ]
        },
        "strands": {
          "description": "What each strand (words, sound, picture) does across the sequence: its own line, like the parts of a band.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/StrandPart"
          }
        },
        "journey": {
          "description": "Where the sequence's camera travels, stage by stage, and why it ends where it does.",
          "anyOf": [
            {
              "$ref": "#/$defs/Journey"
            },
            {
              "type": "null"
            }
          ]
        },
        "none": {
          "description": "What the sequence deliberately does not have: journey (no route of its own).",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Absence"
          }
        }
      },
      "required": [
        "id",
        "purpose",
        "scenes"
      ],
      "additionalProperties": false
    },
    "Setting": {
      "type": "string",
      "enum": [
        "indoor",
        "outdoor"
      ]
    },
    "ShootDay": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "date": {
          "description": "The calendar date (YYYY-MM-DD), when it is known.",
          "type": [
            "string",
            "null"
          ]
        },
        "start": {
          "description": "When the day starts and ends, as clock times (\"09:00\", \"17:30\").",
          "type": "string"
        },
        "end": {
          "type": "string"
        },
        "locations": {
          "description": "The locations the day uses (location ids).",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "setups": {
          "description": "The set-ups filmed this day, in the order they are filmed. A set-up is a shot's setup.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/DaySetup"
          }
        },
        "moves": {
          "description": "How long it takes to travel between two locations, each way.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Move"
          }
        },
        "tasks": {
          "description": "Filming that makes no picture (room tone, a voiceover).",
          "type": "array",
          "items": {
            "$ref": "#/$defs/CaptureTask"
          }
        }
      },
      "required": [
        "id",
        "start",
        "end"
      ],
      "additionalProperties": false
    },
    "Shot": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "picture": {
          "anyOf": [
            {
              "$ref": "#/$defs/ShotPicture"
            },
            {
              "type": "null"
            }
          ]
        },
        "purpose": {
          "description": "What the shot is for: what the audience should read from it.",
          "type": "string"
        },
        "subject": {
          "type": "string"
        },
        "start_ms": {
          "type": "integer"
        },
        "end_ms": {
          "type": "integer"
        },
        "camera": {
          "$ref": "#/$defs/Camera"
        },
        "source": {
          "description": "What the picture is made from: recorded footage, a still, a designed graphic, words, or generated imagery. A camera move on a still is realised as a move across it.",
          "anyOf": [
            {
              "$ref": "#/$defs/ShotSource"
            },
            {
              "type": "null"
            }
          ]
        },
        "characters": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "props": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "path": {
          "description": "A travelling camera's path, phase by phase (for example: rise from the table, arc over a shoulder, fly out of a window).",
          "type": "array",
          "items": {
            "$ref": "#/$defs/PathPhase"
          }
        },
        "introduces": {
          "description": "Characters, props or the location that first come into view in this shot, and how.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/ShotIntroduction"
          }
        },
        "visual_state": {
          "description": "What must be true in frame (state of the world, light, props).",
          "type": [
            "string",
            "null"
          ]
        },
        "continuity": {
          "description": "Continuity rules this shot must honour.",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "image_brief": {
          "type": [
            "string",
            "null"
          ]
        },
        "motion_brief": {
          "type": [
            "string",
            "null"
          ]
        },
        "recall": {
          "description": "For a recalled shot: the earlier movement and its shot whose approved picture or clip this reuses (callbacks are the film's spine; owner's rule: the same file, not a regeneration that looks different).",
          "anyOf": [
            {
              "$ref": "#/$defs/Recall"
            },
            {
              "type": "null"
            }
          ]
        },
        "motion_ramp": {
          "description": "An authored change of the picture's speed inside the shot: the clip slows to stillness on a word (M4: the storm on \"be still\"). Not a freeze: the hold after it is the plan's intent.",
          "anyOf": [
            {
              "$ref": "#/$defs/MotionRamp"
            },
            {
              "type": "null"
            }
          ]
        },
        "must_not": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "overlays": {
          "description": "Layers over the shot's picture: an inset, a nameplate, words that land.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Overlay"
          }
        },
        "layers": {
          "description": "The shot built in depth: the world behind, anything between, the subject in front — each layer with its own job, timing and motion, so they can work independently (drift apart, slide off different ways). Without layers the shot is one picture.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Layer"
          }
        },
        "window": {
          "description": "The camera as a window travelling over the shot's painted picture (a long panorama walked through): keys of offset and scale for the view, which every layer follows at its own parallax. Without it the view holds.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/LayerKey"
          }
        },
        "coverage": {
          "description": "What this shot gives the edit (master, single, reaction, insert…) and how much it matters.",
          "anyOf": [
            {
              "$ref": "#/$defs/Coverage"
            },
            {
              "type": "null"
            }
          ]
        },
        "staging": {
          "description": "Where each subject is in frame, which way they look and move.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Staging"
          }
        },
        "setup": {
          "description": "The camera setup the shot is filmed from; shots sharing a setup are shot together.",
          "type": [
            "string",
            "null"
          ]
        },
        "crosses_line": {
          "description": "The camera crosses the line of action on screen, taking the audience with it.",
          "type": [
            "boolean",
            "null"
          ]
        },
        "states": {
          "description": "The state of each prop that matters, as seen in this shot.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/PropState"
          }
        },
        "filming": {
          "description": "Short-form profile: how the shot is filmed (its timing role, takes, whether it is a one-take moment).",
          "anyOf": [
            {
              "$ref": "#/$defs/Filming"
            },
            {
              "type": "null"
            }
          ]
        },
        "provenance": {
          "description": "Who or what set this shot. Inert: no rule reads it. (Not source, which already says what the picture is made from.)",
          "anyOf": [
            {
              "$ref": "#/$defs/Provenance"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "purpose",
        "subject",
        "start_ms",
        "end_ms",
        "camera"
      ],
      "additionalProperties": false
    },
    "ShotIntroduction": {
      "type": "object",
      "description": "Our own camera terms. Industry names come from a versioned vocabulary mapping, never from here.",
      "properties": {
        "subject": {
          "type": "string"
        },
        "how": {
          "$ref": "#/$defs/IntroductionWay"
        },
        "minor": {
          "type": "boolean",
          "default": false
        },
        "why": {
          "type": "string"
        }
      },
      "required": [
        "subject",
        "how",
        "why"
      ],
      "additionalProperties": false
    },
    "ShotPicture": {
      "type": "object",
      "properties": {
        "kind": {
          "$ref": "#/$defs/PictureKind"
        },
        "map_camera": {
          "anyOf": [
            {
              "$ref": "#/$defs/MapCameraPlan"
            },
            {
              "type": "null"
            }
          ]
        },
        "serves": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "source_shown": {
          "type": [
            "boolean",
            "null"
          ]
        },
        "note": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "kind"
      ],
      "additionalProperties": false
    },
    "ShotSize": {
      "type": "string",
      "enum": [
        "extreme_wide",
        "wide",
        "full",
        "medium_wide",
        "cowboy",
        "medium",
        "medium_close_up",
        "close_up",
        "extreme_close_up",
        "detail",
        "macro"
      ]
    },
    "ShotSource": {
      "type": "string",
      "description": "Where a shot's picture comes from: generated is a still made for it; generated_video moves (image-to-video); recall reuses an approved picture or clip from an earlier movement, the same file (the shot's recall names it).",
      "enum": [
        "footage",
        "still",
        "graphic",
        "typography",
        "generated",
        "generated_video",
        "recall"
      ]
    },
    "SimplerJoin": {
      "type": "string",
      "enum": [
        "none",
        "hold",
        "reframe",
        "cut"
      ]
    },
    "SliceOf": {
      "type": "object",
      "description": "Where a slice came from: the revision hash of the whole plan (as dpl readiness reports it), and the scenes and shots the slice holds, in the whole plan's own ids.",
      "properties": {
        "plan_hash": {
          "description": "SHA-256 of the whole plan in its standard layout.",
          "type": "string"
        },
        "scenes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "shots": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "plan_hash",
        "scenes",
        "shots"
      ],
      "additionalProperties": false
    },
    "Sound": {
      "type": "object",
      "properties": {
        "carriers": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/SoundCarrier"
          }
        },
        "speech": {
          "description": "Spoken lines, placed in time.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/SpeechLine"
          }
        },
        "beat_functions": {
          "description": "What fixed spoken words are doing, to guide picture choice.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/BeatFunction"
          }
        },
        "beats": {
          "description": "Which carrier owns the foreground, span by span.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/SoundBeat"
          }
        },
        "bridges": {
          "description": "How sound crosses a change of picture or world.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/SoundBridge"
          }
        },
        "rules": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/SoundRule"
          }
        },
        "events": {
          "description": "Sounds that happen at a moment (a hit, a chime, a landing), placed in time like spoken lines.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/SoundEvent"
          }
        },
        "pauses": {
          "description": "Pauses in the speech that give sound events room (the plan's times already include them).",
          "type": "array",
          "items": {
            "$ref": "#/$defs/SpeechPause"
          }
        },
        "intensity": {
          "description": "The intensity lever: where a music carrier's drums come forward or sit back while its song plays on (owner, 3 Oct 2026: \"The prominence is when you want their feelings to rise. Change the style/song when the subject changes\").",
          "type": "array",
          "items": {
            "$ref": "#/$defs/IntensityPoint"
          }
        },
        "takeovers": {
          "description": "A takeover: inside one span a carrier rises on one straight line in dB, and nothing ducks it, not even under the voice (owner, 2 Oct 2026, the joy-break: \"starts off in the background and gradually gets louder as I speak... by the time I say Hey and turn it should be at least as loud as my voice\"; \"Let it be a linear rise\").",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Takeover"
          }
        }
      },
      "additionalProperties": false
    },
    "SoundBeat": {
      "type": "object",
      "properties": {
        "start_ms": {
          "type": "integer"
        },
        "end_ms": {
          "type": "integer"
        },
        "foreground": {
          "description": "The carrier in the foreground, or null when nothing owns it.",
          "type": [
            "string",
            "null"
          ]
        },
        "supporting": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "withheld": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "purpose": {
          "type": "string"
        },
        "note": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "start_ms",
        "end_ms",
        "purpose"
      ],
      "additionalProperties": false
    },
    "SoundBridge": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "at_ms": {
          "type": "integer"
        },
        "from": {
          "type": [
            "string",
            "null"
          ]
        },
        "to": {
          "type": [
            "string",
            "null"
          ]
        },
        "retained": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "exiting": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "entering": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "rule": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "at_ms",
        "rule"
      ],
      "additionalProperties": false
    },
    "SoundCarrier": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "kind": {
          "$ref": "#/$defs/SoundKind"
        },
        "identity": {
          "type": [
            "string",
            "null"
          ]
        },
        "job": {
          "description": "What this carrier does for the story.",
          "type": "string"
        },
        "constraints": {
          "description": "Limits on how the sound may be made or used (source, number of takes, allowed cues).",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "pulse": {
          "description": "A measured beat grid, for music with a steady pulse. It carries on in the listener's head after the music stops, so later moments can land on it.",
          "anyOf": [
            {
              "$ref": "#/$defs/Pulse"
            },
            {
              "type": "null"
            }
          ]
        },
        "message": {
          "description": "What the carrier says, when it speaks (a talking drum, a sung line in another language): it is part of the story, not decoration, so its phrases are never cut part-way and never buried.",
          "anyOf": [
            {
              "$ref": "#/$defs/CarrierMessage"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "kind",
        "job"
      ],
      "additionalProperties": false
    },
    "SoundEvent": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "carrier": {
          "type": "string"
        },
        "at_ms": {
          "type": "integer"
        },
        "what": {
          "description": "What is heard and why it happens here.",
          "type": "string"
        },
        "lands_on": {
          "anyOf": [
            {
              "$ref": "#/$defs/LandsOn"
            },
            {
              "type": "null"
            }
          ]
        },
        "length_ms": {
          "description": "How long the sound needs to be heard complete.",
          "type": [
            "integer",
            "null"
          ]
        },
        "clear_of_speech": {
          "description": "The sound must not overlap any spoken line (it has its own room).",
          "type": "boolean",
          "default": false
        },
        "sync_to": {
          "description": "The picture it must match: a shot or layer that starts on it.",
          "type": [
            "string",
            "null"
          ]
        },
        "lead_ms": {
          "description": "The sound anticipates its picture: it is already heard building this long before at_ms, and peaks on it (owner, 3 Oct 2026: the transition sound \"should kick in slightly early before the actual transition\"). Needs sync_to. A bound clip places its own peak (peak_ms) on at_ms; verify measures the lead as heard.",
          "type": [
            "integer",
            "null"
          ]
        },
        "anchor": {
          "description": "The spoken moment it belongs to (a line's onset or offset), so it follows the words when the speech is placed differently. at_ms must agree with it.",
          "anyOf": [
            {
              "$ref": "#/$defs/SpeechAnchor"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "carrier",
        "at_ms",
        "what"
      ],
      "additionalProperties": false
    },
    "SoundGear": {
      "type": "string",
      "enum": [
        "phone_mic",
        "lav",
        "shotgun",
        "recorder"
      ]
    },
    "SoundKind": {
      "type": "string",
      "enum": [
        "speech",
        "music",
        "ambience",
        "effect",
        "silence"
      ]
    },
    "SoundRule": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "rule": {
          "type": "string"
        },
        "withhold": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "allow": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "spans": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/SoundSpan"
          }
        },
        "fails_if": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "check": {
          "description": "How a reviewer tells whether the rule was kept.",
          "type": [
            "string",
            "null"
          ]
        },
        "quiet": {
          "description": "When the rule is an authored quiet: how it begins and ends.",
          "anyOf": [
            {
              "$ref": "#/$defs/QuietDesign"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "rule"
      ],
      "additionalProperties": false
    },
    "SoundSpan": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "start_ms": {
          "type": "integer"
        },
        "end_ms": {
          "type": [
            "integer",
            "null"
          ]
        }
      },
      "required": [
        "name",
        "start_ms"
      ],
      "additionalProperties": false
    },
    "SoundTargetStyle": {
      "type": "object",
      "properties": {
        "bed_under_voice_db": {
          "type": "number",
          "default": 21
        },
        "drawing_sfx_under_voice_db": {
          "type": "number",
          "default": 18
        },
        "bed_presence_cut_db": {
          "type": "number",
          "default": 0
        },
        "bed_hold_ms": {
          "type": "integer",
          "default": 0
        },
        "bed_lifts": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/BedLift"
          }
        },
        "loudness": {
          "$ref": "#/$defs/LoudnessTarget"
        },
        "cut_sfx": {
          "type": "string",
          "default": "few"
        }
      },
      "additionalProperties": false
    },
    "SpeechAnchor": {
      "type": "object",
      "description": "A moment defined by the speech: a spoken line's onset or offset, plus an optional signed offset (adopted from the speech-anchors proposal, 1 Oct 2026: the plan keeps \"on this line\", not only its milliseconds).",
      "properties": {
        "line": {
          "description": "The id of a line in sound.speech.",
          "type": "string"
        },
        "boundary": {
          "$ref": "#/$defs/SpeechBoundary",
          "default": "onset"
        },
        "offset_ms": {
          "type": "integer",
          "default": 0
        }
      },
      "required": [
        "line"
      ],
      "additionalProperties": false
    },
    "SpeechBoundary": {
      "type": "string",
      "description": "Which edge of a spoken line a speech anchor names.",
      "enum": [
        "onset",
        "offset"
      ]
    },
    "SpeechLine": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "carrier": {
          "type": "string"
        },
        "text": {
          "type": "string"
        },
        "start_ms": {
          "type": "integer"
        },
        "end_ms": {
          "type": "integer"
        },
        "direction": {
          "type": [
            "string",
            "null"
          ]
        },
        "lands_on": {
          "description": "The line starts on a beat (or bar) of a carrier's pulse.",
          "anyOf": [
            {
              "$ref": "#/$defs/LandsOn"
            },
            {
              "type": "null"
            }
          ]
        },
        "source_ms": {
          "description": "Where the line starts in its recording, so the recording can be placed line by line (and a pause put between lines without losing sync with the face that speaks them).",
          "type": [
            "integer",
            "null"
          ]
        },
        "words": {
          "description": "One-based words with offsets in this line's recording. Every anchored word needs measured timing.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/SpeechWord"
          }
        }
      },
      "required": [
        "id",
        "carrier",
        "text",
        "start_ms",
        "end_ms"
      ],
      "additionalProperties": false
    },
    "SpeechPause": {
      "type": "object",
      "description": "A pause in the speech after a line, to give a sound effect room to be heard complete (owner, 1 Oct 2026: not only ducking — pausing the speech for the effect).",
      "properties": {
        "after_line": {
          "type": "string"
        },
        "length_ms": {
          "type": "integer"
        },
        "for": {
          "description": "The sound event the pause is for (or, with ForJoin, none).",
          "type": [
            "string",
            "null"
          ]
        },
        "for_join": {
          "description": "The picture join the pause is for: the shot id the join enters. A travel join (a climb, a descent) needs his silence to move in, with no sound made for it (owner, 4 Oct 2026, on M7 v2: the air on the climbs \"has to go\", the breaths stay).",
          "type": [
            "string",
            "null"
          ]
        },
        "why": {
          "type": [
            "string",
            "null"
          ]
        },
        "hides_speaker": {
          "description": "The speaker's window fades out for the pause (an interlude: the scene plays, then he returns).",
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "after_line",
        "length_ms"
      ],
      "additionalProperties": false
    },
    "SpeechWord": {
      "type": "object",
      "properties": {
        "index": {
          "type": "integer"
        },
        "start_ms": {
          "type": "integer"
        },
        "stress_ms": {
          "type": [
            "integer",
            "null"
          ]
        }
      },
      "required": [
        "index",
        "start_ms"
      ],
      "additionalProperties": false
    },
    "Staging": {
      "type": "object",
      "properties": {
        "subject": {
          "type": "string"
        },
        "side": {
          "$ref": "#/$defs/FrameSide"
        },
        "facing": {
          "description": "Where the subject looks, on screen.",
          "$ref": "#/$defs/Facing",
          "default": "camera"
        },
        "moving": {
          "anyOf": [
            {
              "$ref": "#/$defs/ScreenTravel"
            },
            {
              "type": "null"
            }
          ]
        },
        "actions": {
          "description": "What a rigged character does in the shot, in order (walk, argue, turn_head, kneel, look_up), each over its own span of film time. The rig stands in the frame on the subject's side.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/StagingAction"
          }
        }
      },
      "required": [
        "subject",
        "side"
      ],
      "additionalProperties": false
    },
    "StagingAction": {
      "type": "object",
      "properties": {
        "kind": {
          "$ref": "#/$defs/RigAction"
        },
        "start_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "end_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "at": {
          "description": "The hit of point or climb lands on this word; the action's start is derived from its duration.",
          "anyOf": [
            {
              "$ref": "#/$defs/ActionWordAnchor"
            },
            {
              "type": "null"
            }
          ]
        },
        "duration_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "side": {
          "description": "For argue: the arm (left or right; right when absent).",
          "anyOf": [
            {
              "$ref": "#/$defs/FrameSide"
            },
            {
              "type": "null"
            }
          ]
        },
        "to": {
          "description": "For head actions: an angle; for walk: a frame x or destination box [x,y,w,h]."
        },
        "distance": {
          "description": "For walk: horizontal travel as a fraction of frame width.",
          "type": [
            "number",
            "null"
          ]
        },
        "step_ms": {
          "description": "For walk: the length of a step cycle (1000 ms when absent).",
          "type": [
            "integer",
            "null"
          ]
        }
      },
      "required": [
        "kind"
      ],
      "additionalProperties": false
    },
    "StateChange": {
      "type": "object",
      "properties": {
        "prop": {
          "type": "string"
        },
        "from": {
          "type": "string"
        },
        "to": {
          "type": "string"
        },
        "at_ms": {
          "type": "integer"
        },
        "shown": {
          "description": "The change must be seen happening.",
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "prop",
        "from",
        "to",
        "at_ms"
      ],
      "additionalProperties": false
    },
    "StereoSide": {
      "type": "string",
      "description": "Where a phrase is heard across the stereo field (Yahweh M6: the seraphim calling to one another, left and right).",
      "enum": [
        "centre",
        "left",
        "right",
        "both"
      ]
    },
    "Strand": {
      "type": "string",
      "description": "The three strands a film tells its story on.",
      "enum": [
        "words",
        "sound",
        "picture"
      ]
    },
    "StrandPart": {
      "type": "object",
      "description": "One strand's part: what it does, in its own way, toward the shared direction.",
      "properties": {
        "strand": {
          "$ref": "#/$defs/Strand"
        },
        "role": {
          "$ref": "#/$defs/StrandRole",
          "default": "support"
        },
        "does": {
          "description": "What the strand does here, in its own terms.",
          "type": "string"
        },
        "at_ms": {
          "description": "When the strand makes its move (a hit), for example the onset of the word a unison lands on.",
          "type": [
            "integer",
            "null"
          ]
        }
      },
      "required": [
        "strand",
        "does"
      ],
      "additionalProperties": false
    },
    "StrandRole": {
      "type": "string",
      "description": "A strand's role at a moment: it leads, answers the lead (call and response), supports it, rests, or all three strike together (unison: a deliberate hit, rare).",
      "enum": [
        "lead",
        "answer",
        "support",
        "rest",
        "unison"
      ]
    },
    "StructureStyle": {
      "type": "object",
      "properties": {
        "cold_open": {
          "type": "boolean",
          "default": false
        },
        "sponsor": {
          "type": "boolean",
          "default": false
        },
        "subscribe": {
          "type": "boolean",
          "default": false
        },
        "end_card": {
          "type": "boolean",
          "default": false
        },
        "presenter_after_ms": {
          "type": "integer",
          "default": 15000
        },
        "cold_open_max_ms": {
          "type": "integer",
          "default": 60000
        },
        "subscribe_from_ms": {
          "type": "integer",
          "default": 40000
        },
        "subscribe_to_ms": {
          "type": "integer",
          "default": 70000
        },
        "end_card_ms": {
          "type": "integer",
          "default": 18000
        }
      },
      "additionalProperties": false
    },
    "Support": {
      "type": "string",
      "description": "Where the camera is carried. handheld, shoulder, gimbal and steadicam need an operator holding the camera.",
      "enum": [
        "handheld",
        "gimbal",
        "tripod",
        "clamp",
        "slider",
        "shoulder",
        "dolly",
        "jib",
        "crane",
        "steadicam",
        "drone"
      ]
    },
    "Takeover": {
      "type": "object",
      "description": "From start to end a carrier rises from from_db to to_db (relative to its bound level, where 0 dB is the voice's level), linear in dB, with no duck, rest or lever inside the span.",
      "properties": {
        "carrier": {
          "type": "string"
        },
        "start_ms": {
          "type": "integer"
        },
        "end_ms": {
          "type": "integer"
        },
        "from_db": {
          "type": "number"
        },
        "to_db": {
          "type": "number"
        },
        "why": {
          "description": "Why the sound takes over here.",
          "type": "string"
        }
      },
      "required": [
        "carrier",
        "start_ms",
        "end_ms",
        "from_db",
        "to_db",
        "why"
      ],
      "additionalProperties": false
    },
    "TextBacking": {
      "type": "string",
      "description": "The shape behind on-screen text.",
      "enum": [
        "solid",
        "accent",
        "band",
        "none"
      ]
    },
    "TimeWindow": {
      "type": "object",
      "description": "A span of clock time on a shoot day (\"09:30\" to \"11:00\", 24-hour). Without day it applies on every shoot day.",
      "properties": {
        "day": {
          "description": "The shoot day (a day id of schedule) it applies to.",
          "type": [
            "string",
            "null"
          ]
        },
        "from": {
          "type": "string"
        },
        "to": {
          "type": "string"
        }
      },
      "required": [
        "from",
        "to"
      ],
      "additionalProperties": false
    },
    "TimedWord": {
      "type": "object",
      "properties": {
        "text": {
          "type": "string"
        },
        "at_ms": {
          "type": "integer"
        },
        "highlight": {
          "description": "One word of the text made brighter and heavier on its spoken moment (Yahweh M6, John 12:41: \"Isaiah said these things because he saw his glory and spoke of HIM\").",
          "anyOf": [
            {
              "$ref": "#/$defs/WordHighlight"
            },
            {
              "type": "null"
            }
          ]
        },
        "page_break": {
          "description": "On the narrator's page (the words technique \"page\"): this word begins a new page; the card stays and the words before it clear (Kingdom restart, 10 Oct 2026: one page overlay per scene, so the card never blinks).",
          "type": [
            "boolean",
            "null"
          ]
        },
        "turn": {
          "description": "On the narrator's page or strip: how what was said before leaves as this word's page begins — beneath (written under it), card_left, card_right, card_up, fold, lift, file_away, drop, slide_left, slide_right. Absent, the renderer varies it (owner, 11 Oct 2026: \"some imagination and at least some variation\").",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "text",
        "at_ms"
      ],
      "additionalProperties": false
    },
    "TimingStyle": {
      "type": "object",
      "description": "Per-film picture rhythm targets. Null shot medians disable that section's length gate.",
      "properties": {
        "lead_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "word_window_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "paragraph_window_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "max_shift_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "min_onset_share": {
          "type": [
            "number",
            "null"
          ]
        },
        "min_paragraph_share": {
          "type": [
            "number",
            "null"
          ]
        },
        "hook_median_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "body_median_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "presenter_median_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "shot_median_tolerance_ms": {
          "type": [
            "integer",
            "null"
          ]
        }
      },
      "additionalProperties": false
    },
    "Transition": {
      "type": "object",
      "properties": {
        "from": {
          "type": "string"
        },
        "to": {
          "type": "string"
        },
        "kind": {
          "$ref": "#/$defs/TransitionKind"
        },
        "detail": {
          "description": "How the join is played beyond its kind (for example, a cut that then holds).",
          "type": [
            "string",
            "null"
          ]
        },
        "purpose": {
          "description": "What the audience learns across the join.",
          "type": [
            "string",
            "null"
          ]
        },
        "keeps": {
          "description": "What stays the same across the join.",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "considered": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ConsideredAlternative"
          }
        },
        "need": {
          "description": "Why the picture must change here.",
          "anyOf": [
            {
              "$ref": "#/$defs/JoinNeed"
            },
            {
              "type": "null"
            }
          ]
        },
        "simpler": {
          "description": "The simplest join that was considered instead.",
          "anyOf": [
            {
              "$ref": "#/$defs/SimplerJoin"
            },
            {
              "type": "null"
            }
          ]
        },
        "persisting": {
          "description": "Characters, props or the location that stay in view across the join.",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "style": {
          "description": "A named technique that plays the join beyond its kind (for example analogue_channel_change, inset_expand, light_point_reveal): lower-case words joined by underscores. Realisation decides how to draw it.",
          "type": [
            "string",
            "null"
          ]
        },
        "duration_ms": {
          "description": "How long the join takes, when it is not instant.",
          "type": [
            "integer",
            "null"
          ]
        },
        "serves": {
          "description": "Whether the change moves the story on (something happens, is learned or transformed) or only moves the audience's attention for the moment.",
          "anyOf": [
            {
              "$ref": "#/$defs/JoinServes"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "from",
        "to",
        "kind"
      ],
      "additionalProperties": false
    },
    "TransitionKind": {
      "type": "string",
      "description": "How two shots join. Hold: the next shot keeps the same subject and framing intent, so the join is felt as a hold.",
      "enum": [
        "cut",
        "match_cut",
        "jump_cut",
        "smash_cut",
        "dissolve",
        "fade",
        "wipe",
        "hold"
      ]
    },
    "Treatment": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "angle": {
          "description": "The angle, in a few words (for example \"stay on his face and close in\").",
          "type": "string"
        },
        "idea": {
          "description": "The idea: what the audience would see and hear, and why it could work.",
          "type": "string"
        },
        "source": {
          "description": "Where the idea comes from: prior art, a reference, an owner note.",
          "type": [
            "string",
            "null"
          ]
        },
        "chosen": {
          "type": "boolean",
          "default": false
        },
        "why": {
          "description": "Why it was taken, or why not.",
          "type": "string"
        }
      },
      "required": [
        "id",
        "angle",
        "idea",
        "why"
      ],
      "additionalProperties": false
    },
    "WordHighlight": {
      "type": "object",
      "properties": {
        "word": {
          "type": "string"
        },
        "at_ms": {
          "type": "integer"
        }
      },
      "required": [
        "word",
        "at_ms"
      ],
      "additionalProperties": false
    },
    "Work": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "profile": {
          "description": "What kind of plan this is: film (the default, and every plan written before the short-form profile) or short, which opts in to the short-form rules about kit, people, light and the shoot day.",
          "anyOf": [
            {
              "$ref": "#/$defs/WorkProfile"
            },
            {
              "type": "null"
            }
          ]
        },
        "house_style": {
          "anyOf": [
            {
              "$ref": "#/$defs/HouseStyle"
            },
            {
              "type": "null"
            }
          ]
        },
        "thesis": {
          "description": "What the film argues or makes the audience feel, in one or two sentences.",
          "type": [
            "string",
            "null"
          ]
        },
        "target_runtime_ms": {
          "type": [
            "integer",
            "null"
          ]
        },
        "approach": {
          "description": "How the plan approaches the film (its method, not its content).",
          "type": [
            "string",
            "null"
          ]
        },
        "design_notes": {
          "description": "Choices the plan makes deliberately, each against what an ordinary plan would do.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/DesignNote"
          }
        },
        "quality_floor": {
          "description": "The minimum the finished film must achieve, whatever else changes.",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "motion_share": {
          "description": "The share of screen time (0 to 1) that should be in motion: recorded or generated video, not a camera move across a still (owner, 1 Oct 2026: \"a greater proportion of the same material to be video\").",
          "type": [
            "number",
            "null"
          ]
        },
        "motion_counts_animation": {
          "description": "Whether animation counts toward the motion share: a diagram while it evolves, a layer drifting with noise. An infographic film moves by drawing (Kingdom, 3 Oct 2026); a film asked for more video (Yahweh, 1 Oct) counts video only, the default.",
          "type": [
            "boolean",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "title"
      ],
      "additionalProperties": false
    },
    "WorkProfile": {
      "type": "string",
      "description": "What kind of plan this is. film is every plan written before the short-form profile and the default.",
      "enum": [
        "film",
        "short"
      ]
    }
  }
}
