{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:onenterframe:elearning-format-research:ccf:0.1",
  "title": "CommonCourse",
  "description": "Common Course Format (CCF) draft 0.1. A normalized, tool-neutral representation of an e-learning course. Every importer must emit the same canonical shape: see schema/SPECIFICATION.md for normalization rules, units and evidence levels. An absent optional property means 'not specified by the source / unknown'; the schema deliberately declares no defaults.",
  "type": "object",
  "additionalProperties": false,
  "required": ["schemaVersion", "id", "title", "source", "canvas", "slides"],
  "properties": {
    "schemaVersion": {
      "type": "string",
      "pattern": "^0\\.1\\.[0-9]+$",
      "description": "CCF version. Draft 0.1.x; breaking changes are allowed until 1.0."
    },
    "id": { "$ref": "#/$defs/Id" },
    "title": { "type": "string", "minLength": 1 },
    "description": { "type": "string" },
    "language": {
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$",
      "description": "BCP 47 language tag."
    },
    "locales": {
      "type": "object",
      "additionalProperties": false,
      "required": ["available"],
      "description": "Languages packaged in the source when it carries more than one. Text in this document is in \"language\".",
      "properties": {
        "available": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": { "type": "string", "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$" }
        },
        "default": { "type": "string", "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$" }
      }
    },
    "textDirection": { "$ref": "#/$defs/TextDirection" },
    "source": { "$ref": "#/$defs/SourceInfo" },
    "metadata": { "$ref": "#/$defs/Metadata" },
    "canvas": { "$ref": "#/$defs/Canvas" },
    "player": { "$ref": "#/$defs/Player" },
    "interfaceSettings": { "$ref": "#/$defs/InterfaceSettings" },
    "theme": { "$ref": "#/$defs/Theme" },
    "master": { "$ref": "#/$defs/SlideMaster" },
    "layouts": {
      "type": "array",
      "items": { "$ref": "#/$defs/SlideLayout" },
      "description": "Named slide layouts with placeholders and optional layout-specific background or header/footer."
    },
    "tracking": { "$ref": "#/$defs/Tracking" },
    "variables": { "type": "array", "items": { "$ref": "#/$defs/Variable" } },
    "assets": { "type": "array", "items": { "$ref": "#/$defs/Asset" } },
    "sections": {
      "type": "array",
      "items": { "$ref": "#/$defs/Section" },
      "description": "Ordered groupings of slides (Storyline scenes, chapters, modules). Each slide belongs to at most one section."
    },
    "startSlideId": { "$ref": "#/$defs/Id" },
    "slides": {
      "type": "array",
      "minItems": 1,
      "items": { "$ref": "#/$defs/Slide" },
      "description": "Slides in canonical presentation order (resolved from native scene/slide-manager order, never from archive or file order)."
    },
    "questions": {
      "type": "array",
      "items": { "$ref": "#/$defs/Question" },
      "description": "Registry of every question. Slides and blocks reference questions by id."
    },
    "questionPools": { "type": "array", "items": { "$ref": "#/$defs/QuestionPool" } },
    "quizzes": { "type": "array", "items": { "$ref": "#/$defs/Quiz" } },
    "actionGroups": {
      "type": "array",
      "items": { "$ref": "#/$defs/ActionGroup" },
      "description": "Reusable named action lists (Captivate shared/advanced actions, Storyline published action groups) invoked by runActionGroup."
    },
    "textStyles": {
      "type": "array",
      "items": { "$ref": "#/$defs/TextStylePreset" },
      "description": "Reusable named paragraph and heading text styles referenced by Paragraph.styleId."
    },
    "defaultFormatting": {
      "$ref": "#/$defs/DefaultObjectFormatting",
      "description": "Course-level default formatting applied only when creating new objects (shapes, buttons, text). Existing objects are never altered."
    },
    "objectStyles": {
      "type": "array",
      "items": { "$ref": "#/$defs/ObjectStylePreset" },
      "description": "Reusable named object quick styles referenced by SlideObject.styleId."
    },
    "omissions": {
      "type": "array",
      "items": { "$ref": "#/$defs/Omission" },
      "description": "Explicit record of every source construct the importer could not represent faithfully."
    },
    "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
  },
  "$defs": {
    "Id": {
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",
      "description": "Stable CCF identifier. Native GUIDs and numeric ids are valid values but native identity belongs in sourceRef."
    },
    "TextDirection": {
      "type": "string",
      "enum": ["ltr", "rtl"],
      "description": "Explicit text direction when declared by the source."
    },
    "Color": {
      "type": "string",
      "pattern": "^#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$",
      "description": "Literal sRGB colour #RRGGBB or #RRGGBBAA. Theme palette entries are always literal; object colours may instead reference a palette slot (ColorValue)."
    },
    "ThemeColorSlot": {
      "enum": ["dark1", "light1", "dark2", "light2", "accent1", "accent2", "accent3", "accent4", "accent5", "accent6", "hyperlink", "followedHyperlink", "correct", "incorrect"],
      "description": "A named slot of Theme.colors."
    },
    "ThemeColorRef": {
      "type": "object",
      "additionalProperties": false,
      "required": ["themeColor"],
      "description": "Reference to a theme palette slot, resolved against Theme.colors at render time so a theme change recolours every referencing object. Modifiers apply in this order: lumMod multiplies the HSL lightness, then lumOff adds to it (each clamped to 0..1), then alpha sets opacity. The referenced slot must be defined in Theme.colors (semantic rule).",
      "properties": {
        "themeColor": { "$ref": "#/$defs/ThemeColorSlot" },
        "lumMod": { "type": "number", "minimum": 0, "maximum": 10, "description": "Lightness multiplier (DrawingML lumMod / 100000), e.g. 0.75 for 25% darker." },
        "lumOff": { "type": "number", "minimum": -1, "maximum": 1, "description": "Lightness offset added after lumMod (DrawingML lumOff / 100000), e.g. 0.4 with lumMod 0.6 for 40% lighter." },
        "alpha": { "$ref": "#/$defs/Fraction", "description": "Opacity 0..1; absent means the slot colour is used as it is." }
      }
    },
    "ColorValue": {
      "oneOf": [{ "$ref": "#/$defs/Color" }, { "$ref": "#/$defs/ThemeColorRef" }],
      "description": "A literal colour, or a reference to a theme palette slot."
    },
    "Seconds": { "type": "number", "minimum": 0, "description": "Time in seconds." },
    "Fraction": { "type": "number", "minimum": 0, "maximum": 1 },
    "Percent": { "type": "number", "minimum": 0, "maximum": 100 },
    "Sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
    "Attempts": { "oneOf": [{ "type": "integer", "minimum": 1 }, { "const": "unlimited" }] },
    "SourceRef": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "description": "Native identity of the construct this entity was normalized from (e.g. a Storyline g GUID or a Captivate record id and class).",
      "properties": {
        "id": { "type": "string", "minLength": 1 },
        "kind": {
          "type": "string",
          "minLength": 1,
          "description": "Native element name or class, e.g. 'sldLayer' or 'class-1268'."
        },
        "part": { "type": "string", "minLength": 1, "description": "Package part or file that owns the construct." }
      }
    },
    "VendorExtensions": {
      "type": "object",
      "description": "Native data the core model does not represent, keyed by vendor (storyline, captivateClassic, captivateModern, rise, lectora, ...). Preserved verbatim; consumers that edit the core must treat it as possibly stale.",
      "propertyNames": { "pattern": "^[a-z][A-Za-z0-9]*$" },
      "additionalProperties": { "type": "object" }
    },
    "Omission": {
      "type": "object",
      "additionalProperties": false,
      "required": ["reason", "message"],
      "properties": {
        "path": {
          "type": "string",
          "pattern": "^(/.*)?$",
          "description": "JSON Pointer to the closest CCF location, if any."
        },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "reason": { "enum": ["unsupported", "unmapped", "ambiguous", "unverified", "invalidSource", "lossy"] },
        "aspect": {
          "enum": [
            "content",
            "structure",
            "layout",
            "styling",
            "interactions",
            "states",
            "variables",
            "assessment",
            "media",
            "tracking"
          ],
          "description": "Which dimension of fidelity is affected; lets consumers warn about whole classes of gaps (e.g. shape paint not recoverable)."
        },
        "message": { "type": "string", "minLength": 1 }
      }
    },
    "SourceInfo": {
      "type": "object",
      "additionalProperties": false,
      "required": ["format", "artifact"],
      "properties": {
        "format": {
          "enum": [
            "storyline",
            "captivateClassic",
            "captivateModern",
            "rise",
            "lectora",
            "ispring",
            "powerpoint",
            "scormPackage",
            "xapiPackage",
            "cmi5Package",
            "native",
            "synthetic",
            "other"
          ]
        },
        "artifact": {
          "enum": ["sourceProject", "publishedPackage", "webExport", "synthetic"],
          "description": "What was imported: the authoring project file, a published LMS/web package, an un-packaged web export, or a hand-written fixture. Evidence from one artifact kind says nothing about the others."
        },
        "exportVariant": {
          "type": "string",
          "description": "Publish target or profile recorded in the artifact, verbatim (e.g. \"SCORM 1.2\", \"Web\")."
        },
        "generator": { "type": "string", "description": "Generator string recorded in the artifact, verbatim." },
        "fileName": { "type": "string" },
        "sha256": { "$ref": "#/$defs/Sha256" },
        "applicationVersion": {
          "type": "string",
          "description": "Authoring application version, only when actually known."
        },
        "applicationBuild": { "type": "string", "description": "Build identifier recorded in the artifact, verbatim." },
        "playerVersion": {
          "type": "string",
          "description": "Runtime/player version recorded in published output, verbatim."
        },
        "manifestVersion": {
          "type": "string",
          "description": "Version string recorded inside the source, verbatim (e.g. Captivate's literal '13.0..168')."
        },
        "importer": {
          "type": "object",
          "additionalProperties": false,
          "required": ["name"],
          "properties": {
            "name": { "type": "string", "minLength": 1 },
            "version": { "type": "string" },
            "revision": { "type": "string" }
          }
        },
        "importedAt": { "type": "string", "format": "date-time" }
      },
      "if": { "properties": { "format": { "const": "synthetic" } } },
      "then": { "properties": { "artifact": { "const": "synthetic" } } },
      "else": { "properties": { "artifact": { "not": { "const": "synthetic" } } } },
      "allOf": [
        {
          "if": { "properties": { "format": { "const": "native" } } },
          "then": { "properties": { "artifact": { "const": "sourceProject" } } }
        }
      ]
    },
    "Metadata": {
      "type": "object",
      "additionalProperties": false,
      "description": "Descriptive course metadata as recorded by the source (package manifest, course settings).",
      "properties": {
        "authors": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["name"],
            "properties": {
              "name": { "type": "string", "minLength": 1 },
              "organization": { "type": "string" },
              "avatarAssetId": { "$ref": "#/$defs/Id" }
            }
          }
        },
        "createdAt": { "type": "string", "format": "date-time" },
        "publishedAt": { "type": "string", "format": "date-time" },
        "typicalLearningTimeSeconds": { "$ref": "#/$defs/Seconds" },
        "keywords": { "type": "array", "items": { "type": "string", "minLength": 1 } },
        "catalog": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["catalog", "entry"],
            "description": "Catalog entry, e.g. a manifest LOM identifier.",
            "properties": {
              "catalog": { "type": "string", "minLength": 1 },
              "entry": { "type": "string", "minLength": 1 }
            }
          }
        },
        "coverImageAssetId": { "$ref": "#/$defs/Id" }
      }
    },
    "Canvas": {
      "type": "object",
      "additionalProperties": false,
      "required": ["layout"],
      "properties": {
        "layout": {
          "enum": ["fixed", "responsive", "flow"],
          "description": "fixed: one authored canvas (Storyline, Captivate Classic, fixed modern Captivate). responsive: an authored canvas with breakpoint overrides. flow: no authored canvas; content reflows as stacked blocks (e.g. Rise)."
        },
        "width": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "Authored canvas width in CSS pixels. For flow layouts: maximum content width, when stated."
        },
        "height": { "type": "number", "exclusiveMinimum": 0 },
        "breakpoints": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Breakpoint" } }
      },
      "allOf": [
        {
          "if": { "properties": { "layout": { "const": "fixed" } } },
          "then": { "required": ["width", "height"], "not": { "required": ["breakpoints"] } }
        },
        {
          "if": { "properties": { "layout": { "const": "responsive" } } },
          "then": { "required": ["width", "height", "breakpoints"] }
        },
        {
          "if": { "properties": { "layout": { "const": "flow" } } },
          "then": { "not": { "required": ["breakpoints"] } }
        }
      ]
    },
    "Breakpoint": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "minWidth"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "name": { "type": "string" },
        "minWidth": { "type": "number", "minimum": 0 },
        "maxWidth": { "type": "number", "minimum": 0 },
        "canvasWidth": { "type": "number", "exclusiveMinimum": 0 },
        "canvasHeight": { "type": "number", "exclusiveMinimum": 0 }
      }
    },
    "Player": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "title": { "type": "string" },
        "navigation": {
          "enum": ["free", "restricted", "locked"],
          "description": "Learner navigation policy through the player menu, when the source states one."
        },
        "controls": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "playbar": { "type": "boolean" },
            "playPause": { "type": "boolean" },
            "previous": { "type": "boolean" },
            "next": { "type": "boolean" },
            "submit": { "type": "boolean" },
            "seekBar": { "type": "boolean" },
            "volume": { "type": "boolean" },
            "captions": { "type": "boolean" },
            "menuButton": { "type": "boolean" },
            "fullscreen": { "type": "boolean" },
            "replay": { "type": "boolean" },
            "hidePlaybarInQuiz": { "type": "boolean" },
            "tooltips": { "type": "boolean" },
            "playbackSpeed": {
              "type": "boolean",
              "description": "Learner playback speed control on the player bar (the slide timeline and its media together). Absent: not stated (resolved through the engine profile)."
            }
          }
        },
        "sidebar": {
          "type": "object",
          "additionalProperties": false,
          "minProperties": 1,
          "description": "Sidebar tab order and visibility, and whether the sidebar starts open, when stated by the source or author. Absent fields leave the player to decide.",
          "properties": {
            "initiallyOpen": {
              "type": "boolean",
              "description": "Whether a left, right or top sidebar starts open (false: it starts collapsed behind the menu button). Absent: resolved through the engine profile. A narrow player still starts with the sidebar closed."
            },
            "tabOrder": {
              "type": "array",
              "minItems": 1,
              "maxItems": 3,
              "uniqueItems": true,
              "items": { "enum": ["outline", "resources", "glossary"] },
              "description": "Order of the sidebar tabs. Panels not listed follow in the player's own order (outline, resources, glossary)."
            },
            "showResources": {
              "type": "boolean",
              "description": "true: the Resources tab shows even when the list is empty; false: never shown. Absent: shown only when there are resources."
            },
            "showGlossary": {
              "type": "boolean",
              "description": "true: the Glossary tab shows even when the list is empty; false: never shown. Absent: shown only when there are terms."
            }
          }
        },
        "captionStyle": {
          "type": "object",
          "additionalProperties": false,
          "minProperties": 1,
          "description": "Course-wide closed-caption look, when stated by the source or author. A media object's own captions.appearance wins field by field. Absent fields use the player's own caption look.",
          "properties": {
            "textColor": { "$ref": "#/$defs/ColorValue" },
            "backgroundColor": { "$ref": "#/$defs/ColorValue" },
            "backgroundOpacity": { "$ref": "#/$defs/Fraction", "description": "Opacity of the caption background (0 transparent, 1 solid)." },
            "fontFamily": { "type": "string", "minLength": 1 },
            "fontSize": { "enum": ["small", "medium", "large"], "description": "Initial caption size; a learner's own size choice wins." },
            "textShadow": { "type": "boolean", "description": "true: a dark outline shadow behind the caption text." },
            "position": { "enum": ["bottom", "top"] }
          }
        },
        "menu": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": { "type": "boolean" },
            "position": { "enum": ["left", "right", "top", "overlay"] },
            "showSlideNumbers": { "type": "boolean" },
            "style": {
              "enum": ["tree", "flat", "compact"],
              "description": "Table-of-contents visual/structural presentation style when stated by the source or author."
            },
            "entries": { "type": "array", "items": { "$ref": "#/$defs/MenuEntry" } }
          }
        },
        "colors": {
          "type": "object",
          "additionalProperties": false,
          "minProperties": 1,
          "description": "Player chrome colour overrides carried from the source or set in the editor.",
          "properties": {
            "background": { "$ref": "#/$defs/ColorValue" },
            "text": { "$ref": "#/$defs/ColorValue" },
            "accent": { "$ref": "#/$defs/ColorValue" }
          }
        },
        "fontFamily": {
          "type": "string",
          "minLength": 1,
          "description": "Player chrome font family when specified separately from or in addition to the course theme."
        },
        "playbarPosition": {
          "enum": ["bottom", "top"],
          "description": "Player control bar position when stated by the source or author."
        },
        "buttonStyle": {
          "enum": ["standard", "filled", "outline", "pill"],
          "description": "Player control button visual style (original Course Studio look)."
        },
        "coverImageAssetId": {
          "$ref": "#/$defs/Id",
          "description": "Cover photo asset shown in the player chrome when present."
        },
        "glossary": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["term", "definition"],
            "properties": {
              "id": { "$ref": "#/$defs/Id", "description": "Stable document-wide id, so tools can edit or reorder the term." },
              "term": { "type": "string", "minLength": 1 },
              "definition": { "$ref": "#/$defs/Text" },
              "sourceRef": { "$ref": "#/$defs/SourceRef" },
              "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
            }
          }
        },
        "resources": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["title"],
            "properties": {
              "id": { "$ref": "#/$defs/Id", "description": "Stable document-wide id, so tools can edit or reorder the resource." },
              "title": { "type": "string", "minLength": 1 },
              "url": { "type": "string", "format": "uri-reference" },
              "assetId": { "$ref": "#/$defs/Id" },
              "description": { "type": "string" },
              "sourceRef": { "$ref": "#/$defs/SourceRef" },
              "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
            },
            "oneOf": [{ "required": ["url"] }, { "required": ["assetId"] }]
          }
        },
        "logo": {
          "type": "object",
          "additionalProperties": false,
          "required": ["assetId"],
          "properties": {
            "assetId": { "$ref": "#/$defs/Id" },
            "linkUrl": { "type": "string", "format": "uri-reference" },
            "altText": { "type": "string" }
          }
        },
        "labels": {
          "type": "object",
          "description": "Player UI strings keyed by the source's own label key, verbatim (e.g. a localized \"Next\" label).",
          "propertyNames": { "minLength": 1 },
          "additionalProperties": { "type": "string" }
        },
        "runCourseJavaScript": {
          "type": "boolean",
          "description": "Whether Execute JavaScript trigger actions run in the published player. Absent: not stated (resolved through the engine profile, default true)."
        },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      }
    },
    "MenuEntry": {
      "type": "object",
      "additionalProperties": false,
      "required": ["label"],
      "description": "Table-of-contents entry. Separate from section membership: a source menu may differ from its scene structure.",
      "properties": {
        "label": { "type": "string" },
        "slideId": { "$ref": "#/$defs/Id" },
        "sectionId": { "$ref": "#/$defs/Id" },
        "expanded": { "type": "boolean" },
        "children": { "type": "array", "items": { "$ref": "#/$defs/MenuEntry" } },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      },
      "oneOf": [{ "required": ["slideId"] }, { "required": ["sectionId"] }]
    },
    "Theme": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "name": { "type": "string" },
        "colors": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "dark1": { "$ref": "#/$defs/Color" },
            "light1": { "$ref": "#/$defs/Color" },
            "dark2": { "$ref": "#/$defs/Color" },
            "light2": { "$ref": "#/$defs/Color" },
            "accent1": { "$ref": "#/$defs/Color" },
            "accent2": { "$ref": "#/$defs/Color" },
            "accent3": { "$ref": "#/$defs/Color" },
            "accent4": { "$ref": "#/$defs/Color" },
            "accent5": { "$ref": "#/$defs/Color" },
            "accent6": { "$ref": "#/$defs/Color" },
            "hyperlink": { "$ref": "#/$defs/Color" },
            "followedHyperlink": { "$ref": "#/$defs/Color" },
            "correct": { "$ref": "#/$defs/Color" },
            "incorrect": { "$ref": "#/$defs/Color" },
            "custom": {
              "type": "object",
              "propertyNames": { "pattern": "^[A-Za-z][A-Za-z0-9_-]*$" },
              "additionalProperties": { "$ref": "#/$defs/Color" }
            }
          }
        },
        "fonts": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "major": { "type": "string", "description": "Heading font family." },
            "minor": { "type": "string", "description": "Body font family." }
          }
        },
        "fontFamilies": {
          "type": "array",
          "description": "Font families the source declares, with the variants it lists.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["family"],
            "properties": {
              "family": { "type": "string", "minLength": 1 },
              "assetId": { "$ref": "#/$defs/Id", "description": "Embedded font file, when packaged." },
              "variants": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": { "bold": { "type": "boolean" }, "italic": { "type": "boolean" } }
                }
              }
            }
          }
        },
        "fontScalePercent": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "Global font scale (100 = none)."
        },
        "background": {
          "$ref": "#/$defs/Background",
          "description": "Theme slide background used when a slide does not declare its own background."
        },
        "objectStyles": {
          "$ref": "#/$defs/ThemeObjectStyles",
          "description": "Theme default styles for shapes, buttons, and text objects when individual properties are absent."
        },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      }
    },
    "ThemeObjectStyles": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "shape": { "$ref": "#/$defs/ShapeStyle" },
        "button": { "$ref": "#/$defs/ShapeStyle" },
        "text": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "color": { "$ref": "#/$defs/ColorValue" },
            "fontSizePt": {
              "type": "number",
              "exclusiveMinimum": 0
            },
            "bold": { "type": "boolean" },
            "italic": { "type": "boolean" }
          }
        }
      }
    },
    "DefaultObjectFormatting": {
      "type": "object",
      "additionalProperties": false,
      "description": "Course-level default formatting applied only to newly created objects.",
      "properties": {
        "shape": { "$ref": "#/$defs/ShapeStyle" },
        "button": { "$ref": "#/$defs/ShapeStyle" },
        "text": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "color": { "$ref": "#/$defs/ColorValue" },
            "fontSizePt": {
              "type": "number",
              "exclusiveMinimum": 0
            },
            "bold": { "type": "boolean" },
            "italic": { "type": "boolean" },
            "fontFamily": { "type": "string", "minLength": 1 }
          }
        }
      }
    },
    "Tracking": {
      "type": "object",
      "additionalProperties": false,
      "description": "Publishing/LMS configuration recorded in the source or package. Recording it makes no claim about how any LMS behaves.",
      "properties": {
        "packaging": {
          "enum": [
            "scorm12",
            "scorm2004",
            "scorm2004_2",
            "scorm2004_3",
            "scorm2004_4",
            "cmi5",
            "xapi",
            "aicc",
            "web",
            "none"
          ],
          "description": "Package standard. scorm2004: a SCORM 2004 package whose edition is not determinable. web: un-tracked web output."
        },
        "reporting": {
          "type": "array",
          "uniqueItems": true,
          "items": { "enum": ["scorm12", "scorm2004", "cmi5", "xapi", "aicc"] },
          "description": "Runtime reporting channels the source configures (a SCORM package can also emit xAPI statements). Absent: not stated."
        },
        "packageIdentifier": { "type": "string" },
        "manifest": {
          "type": "object",
          "additionalProperties": false,
          "description": "Package manifest facts (imsmanifest.xml or equivalent), verbatim.",
          "properties": {
            "organizationId": { "type": "string" },
            "organizationTitle": { "type": "string" },
            "schemaVersion": { "type": "string", "description": "e.g. the manifest metadata schemaversion value." },
            "metadataScheme": {
              "type": "string",
              "description": "Metadata scheme/standard referenced by the manifest."
            },
            "items": {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": false,
                "required": ["identifier"],
                "properties": {
                  "identifier": { "type": "string", "minLength": 1 },
                  "identifierRef": { "type": "string" },
                  "title": { "type": "string" },
                  "launchHref": { "type": "string" },
                  "scormType": { "enum": ["sco", "asset"] }
                }
              }
            }
          }
        },
        "reportedQuizId": { "$ref": "#/$defs/Id" },
        "completion": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "criterion": {
              "enum": [
                "slidesViewed",
                "quizCompleted",
                "quizPassed",
                "slidesViewedOrQuizPassed",
                "slidesViewedAndQuizPassed",
                "manual",
                "other"
              ],
              "description": "Normalized completion rule. Use other plus nativeCriterion when no member fits."
            },
            "nativeCriterion": { "type": "string", "description": "The source's own setting, verbatim." },
            "slidesViewedPercent": { "$ref": "#/$defs/Percent" },
            "slidesViewedCount": { "type": "integer", "minimum": 1 },
            "quizId": { "$ref": "#/$defs/Id" }
          },
          "if": { "properties": { "criterion": { "const": "other" } }, "required": ["criterion"] },
          "then": { "required": ["nativeCriterion"] }
        },
        "success": {
          "type": "object",
          "additionalProperties": false,
          "required": ["quizId"],
          "description": "Which quiz determines success status, when configured.",
          "properties": { "quizId": { "$ref": "#/$defs/Id" }, "requirePass": { "type": "boolean" } }
        },
        "resume": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "mode": { "enum": ["always", "prompt", "never"] },
            "suspendDataEncoding": {
              "type": "string",
              "description": "Suspend-data format identifier, when documented (e.g. a compressed or plain encoding name)."
            }
          }
        },
        "xapi": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "activityId": { "type": "string", "format": "uri" },
            "launchHref": { "type": "string" },
            "manifestFile": { "enum": ["tincan.xml", "cmi5.xml"] },
            "endpointConfigured": {
              "type": "boolean",
              "description": "Whether an LRS endpoint is embedded in the package. Credentials are never recorded."
            }
          }
        },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      }
    },
    "InterfaceSettings": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "description": "Course-level permissions and preview settings for outside interactions (sandboxed frames). Absent means not set.",
      "properties": {
        "exposeAllUserVariables": {
          "type": "boolean",
          "description": "When true, all user variables are exposed to outside interactions without per-variable opt-in."
        },
        "developerModePreview": {
          "type": "boolean",
          "description": "When true, Preview allows user variable access by outside interactions and logs accesses that would be refused once published."
        }
      }
    },
    "VariableExposure": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "description": "Declared permission for outside interactions (sandboxed frames) to read or write this variable. Absent means not exposed.",
      "properties": {
        "read": { "type": "boolean" },
        "write": { "type": "boolean" },
        "description": {
          "type": "string",
          "minLength": 1,
          "maxLength": 280,
          "pattern": "^[^\\r\\n]+$",
          "description": "One-line plain-text meaning of the variable for outside interactions and agents."
        }
      }
    },
    "Variable": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "name",
        "type",
        "kind"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/Id"
        },
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Display/native name used in source text references."
        },
        "type": {
          "enum": [
            "text",
            "number",
            "boolean"
          ]
        },
        "kind": {
          "enum": [
            "user",
            "system",
            "quizProperty",
            "random",
            "elapsedTime",
            "questionResult"
          ],
          "description": "user: authored variable with an initial value. system: built-in engine variable. quizProperty: live quiz value (score, pass mark...), never an ordinary zero-initialized variable. random: explicit runtime startup range. elapsedTime: read-only milliseconds for an explicit scope. questionResult: read-only submitted points or correctness for one question; unknown before submission."
        },
        "defaultValue": {
          "type": [
            "string",
            "number",
            "boolean"
          ]
        },
        "quizId": {
          "$ref": "#/$defs/Id"
        },
        "quizProperty": {
          "enum": [
            "scorePoints",
            "scorePercent",
            "maxScore",
            "passPoints",
            "passPercent",
            "correctCount",
            "questionCount",
            "attemptCount"
          ]
        },
        "random": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "min",
            "max"
          ],
          "properties": {
            "min": {
              "type": "number"
            },
            "max": {
              "type": "number"
            }
          }
        },
        "description": {
          "type": "string"
        },
        "exposure": {
          "$ref": "#/$defs/VariableExposure"
        },
        "sourceRef": {
          "$ref": "#/$defs/SourceRef"
        },
        "vendorExtensions": {
          "$ref": "#/$defs/VendorExtensions"
        },
        "elapsedTimeScope": {
          "enum": [
            "project",
            "section",
            "slide"
          ]
        },
        "questionId": {
          "$ref": "#/$defs/Id"
        },
        "questionProperty": {
          "enum": [
            "points",
            "correct"
          ]
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "kind": {
                "const": "user"
              }
            }
          },
          "then": {
            "required": [
              "defaultValue"
            ]
          }
        },
        {
          "if": {
            "properties": {
              "kind": {
                "const": "quizProperty"
              }
            }
          },
          "then": {
            "required": [
              "quizId",
              "quizProperty"
            ],
            "properties": {
              "type": {
                "const": "number"
              }
            }
          },
          "else": {
            "not": {
              "anyOf": [
                {
                  "required": [
                    "quizId"
                  ]
                },
                {
                  "required": [
                    "quizProperty"
                  ]
                }
              ]
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "text"
              }
            }
          },
          "then": {
            "properties": {
              "defaultValue": {
                "type": "string"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "number"
              }
            }
          },
          "then": {
            "properties": {
              "defaultValue": {
                "type": "number"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "type": {
                "const": "boolean"
              }
            }
          },
          "then": {
            "properties": {
              "defaultValue": {
                "type": "boolean"
              }
            }
          }
        },
        {
          "if": {
            "required": [
              "random"
            ]
          },
          "then": {
            "properties": {
              "type": {
                "const": "number"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "kind": {
                "const": "random"
              }
            }
          },
          "then": {
            "required": [
              "random"
            ],
            "properties": {
              "type": {
                "const": "number"
              }
            },
            "not": {
              "required": [
                "defaultValue"
              ]
            }
          }
        },
        {
          "if": {
            "properties": {
              "kind": {
                "const": "elapsedTime"
              }
            }
          },
          "then": {
            "required": [
              "elapsedTimeScope"
            ],
            "properties": {
              "type": {
                "const": "number"
              }
            },
            "not": {
              "anyOf": [
                {
                  "required": [
                    "defaultValue"
                  ]
                },
                {
                  "required": [
                    "random"
                  ]
                }
              ]
            }
          },
          "else": {
            "not": {
              "required": [
                "elapsedTimeScope"
              ]
            }
          }
        },
        {
          "if": {
            "properties": {
              "kind": {
                "const": "questionResult"
              }
            }
          },
          "then": {
            "required": [
              "questionId",
              "questionProperty"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "defaultValue"
                  ]
                },
                {
                  "required": [
                    "random"
                  ]
                }
              ]
            },
            "allOf": [
              {
                "if": {
                  "properties": {
                    "questionProperty": {
                      "const": "correct"
                    }
                  }
                },
                "then": {
                  "properties": {
                    "type": {
                      "const": "boolean"
                    }
                  }
                },
                "else": {
                  "properties": {
                    "type": {
                      "const": "number"
                    }
                  }
                }
              }
            ]
          },
          "else": {
            "not": {
              "anyOf": [
                {
                  "required": [
                    "questionId"
                  ]
                },
                {
                  "required": [
                    "questionProperty"
                  ]
                }
              ]
            }
          }
        }
      ]
    },
    "Asset": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "type", "uri"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "type": { "enum": ["image", "vector", "audio", "video", "font", "document", "captions", "other"] },
        "role": {
          "enum": ["content", "thumbnail", "poster", "chrome", "icon", "cursor", "font"],
          "description": "Why the asset exists. chrome: player/skin artwork rather than course content."
        },
        "uri": {
          "type": "string",
          "format": "uri-reference",
          "description": "Location relative to the CCF document or absolute URI."
        },
        "containerUri": {
          "type": "string",
          "format": "uri-reference",
          "description": "Archive or bundle holding the asset when uri is relative to it."
        },
        "availability": {
          "enum": ["present", "bundled", "external", "missing"],
          "description": "present: file available beside the document. bundled: inside containerUri. external: remote URL. missing: referenced by the source but not found."
        },
        "variants": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["role", "uri"],
            "properties": {
              "role": {
                "type": "string",
                "minLength": 1,
                "description": "Variant purpose as recorded (e.g. a resolution or format variant)."
              },
              "uri": { "type": "string", "format": "uri-reference" },
              "mimeType": { "type": "string", "pattern": "^[a-z]+/[A-Za-z0-9.+-]+$" },
              "width": { "type": "number", "exclusiveMinimum": 0 },
              "height": { "type": "number", "exclusiveMinimum": 0 }
            }
          }
        },
        "mimeType": { "type": "string", "pattern": "^[a-z]+/[A-Za-z0-9.+-]+$" },
        "sizeBytes": { "type": "integer", "minimum": 0 },
        "sha256": { "$ref": "#/$defs/Sha256" },
        "altText": { "type": "string" },
        "width": { "type": "number", "exclusiveMinimum": 0 },
        "height": { "type": "number", "exclusiveMinimum": 0 },
        "durationSeconds": { "$ref": "#/$defs/Seconds" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      }
    },
    "Section": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "title", "slideIds"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "title": { "type": "string" },
        "slideIds": { "type": "array", "items": { "$ref": "#/$defs/Id" }, "uniqueItems": true },
        "collapsed": { "type": "boolean" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      }
    },
    "Text": {
      "type": "object",
      "additionalProperties": false,
      "required": ["plain"],
      "description": "Text content. 'plain' is always present (searchable, accessible fallback). 'paragraphs' carries formatting when the source has it. When present, 'plain' must equal the paragraphs' run text joined by '\\n', with each variable run written as '{{variableId}}'.",
      "properties": {
        "plain": { "type": "string" },
        "direction": { "$ref": "#/$defs/TextDirection" },
        "paragraphs": { "type": "array", "items": { "$ref": "#/$defs/Paragraph" } }
      }
    },
    "Paragraph": {
      "type": "object",
      "additionalProperties": false,
      "required": ["runs"],
      "properties": {
        "runs": { "type": "array", "items": { "$ref": "#/$defs/Run" } },
        "styleId": {
          "$ref": "#/$defs/Id",
          "description": "Optional reference to a named text style in textStyles[]."
        },
        "direction": { "$ref": "#/$defs/TextDirection" },
        "align": { "enum": ["left", "center", "right", "justify"] },
        "list": {
          "type": "object",
          "additionalProperties": false,
          "required": ["type"],
          "properties": {
            "type": { "enum": ["bullet", "numbered"] },
            "level": { "type": "integer", "minimum": 0, "maximum": 8 },
            "bulletChar": { "type": "string", "minLength": 1, "maxLength": 2 },
            "numberFormat": { "enum": ["arabic", "alphaLower", "alphaUpper", "romanLower", "romanUpper"] },
            "numberSuffix": { "enum": ["period", "parenRight", "parenBoth", "none"] }
          }
        },
        "indentPx": { "type": "number", "minimum": 0 },
        "firstLineIndentPx": { "type": "number" },
        "spaceBeforePt": { "type": "number", "minimum": 0 },
        "spaceAfterPt": { "type": "number", "minimum": 0 },
        "lineSpacing": { "type": "number", "exclusiveMinimum": 0, "description": "Multiple of single line spacing." },
        "lineSpacingPx": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "Exact line height in px. Mutually exclusive with lineSpacing."
        }
      },
      "not": { "required": ["lineSpacing", "lineSpacingPx"] }
    },
    "TextStylePreset": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name"],
      "description": "Named paragraph and heading text style referenced by Paragraph.styleId.",
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "name": { "type": "string", "minLength": 1 },
        "headingLevel": { "type": "integer", "minimum": 1, "maximum": 6 },
        "fontFamily": { "type": "string" },
        "fontRole": { "enum": ["major", "minor"] },
        "fontSizePt": { "type": "number", "exclusiveMinimum": 0 },
        "bold": { "type": "boolean" },
        "italic": { "type": "boolean" },
        "underline": { "type": "boolean" },
        "strikethrough": { "type": "boolean" },
        "color": { "$ref": "#/$defs/ColorValue" },
        "highlightColor": { "$ref": "#/$defs/ColorValue" },
        "letterSpacingPx": { "type": "number" },
        "textTransform": { "enum": ["none", "uppercase", "lowercase", "capitalize"] },
        "align": { "enum": ["left", "center", "right", "justify"] },
        "spaceBeforePt": { "type": "number", "minimum": 0 },
        "spaceAfterPt": { "type": "number", "minimum": 0 },
        "lineSpacing": { "type": "number", "exclusiveMinimum": 0 },
        "lineSpacingPx": { "type": "number", "exclusiveMinimum": 0 }
      },
      "not": { "required": ["lineSpacing", "lineSpacingPx"] }
    },
    "Run": {
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["text"],
          "properties": { "text": { "type": "string" }, "style": { "$ref": "#/$defs/RunStyle" } }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["variableId"],
          "description": "Live variable value (normalized from Storyline %Name%, Captivate $$Name$$, etc.).",
          "properties": { "variableId": { "$ref": "#/$defs/Id" }, "style": { "$ref": "#/$defs/RunStyle" } }
        }
      ]
    },
    "HyperlinkStateStyle": {
      "type": "object",
      "additionalProperties": false,
      "description": "Appearance overrides for a hyperlink state (normal, hover, visited, focus).",
      "properties": {
        "color": { "$ref": "#/$defs/ColorValue" },
        "underline": { "type": "boolean" },
        "highlightColor": { "$ref": "#/$defs/ColorValue" },
        "bold": { "type": "boolean" },
        "italic": { "type": "boolean" }
      }
    },
    "HyperlinkStates": {
      "type": "object",
      "additionalProperties": false,
      "description": "Per-state visual styling for hyperlinks on text runs and shapes.",
      "properties": {
        "normal": { "$ref": "#/$defs/HyperlinkStateStyle" },
        "hover": { "$ref": "#/$defs/HyperlinkStateStyle" },
        "visited": { "$ref": "#/$defs/HyperlinkStateStyle" },
        "focus": { "$ref": "#/$defs/HyperlinkStateStyle" }
      }
    },
    "ObjectLink": {
      "type": "object",
      "additionalProperties": false,
      "description": "Hyperlink attached to a shape or slide object, with optional per-state styling.",
      "properties": {
        "url": { "type": "string", "format": "uri-reference" },
        "slideId": { "$ref": "#/$defs/Id", "description": "Internal link to a slide." },
        "openInNewWindow": { "type": "boolean" },
        "states": { "$ref": "#/$defs/HyperlinkStates" }
      },
      "oneOf": [{ "required": ["url"] }, { "required": ["slideId"] }]
    },
    "RunStyle": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "fontFamily": { "type": "string" },
        "fontRole": { "enum": ["major", "minor"], "description": "Theme font binding, when the source uses one." },
        "fontSizePt": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "Typographic points (1pt = 96/72 px at authored scale)."
        },
        "bold": { "type": "boolean" },
        "italic": { "type": "boolean" },
        "underline": { "type": "boolean" },
        "strikethrough": { "type": "boolean" },
        "color": { "$ref": "#/$defs/ColorValue" },
        "highlightColor": { "$ref": "#/$defs/ColorValue" },
        "linkUrl": { "type": "string", "format": "uri-reference" },
        "linkSlideId": { "$ref": "#/$defs/Id", "description": "Internal link to a slide." },
        "linkStates": { "$ref": "#/$defs/HyperlinkStates" },
        "letterSpacingPx": { "type": "number" },
        "textTransform": { "enum": ["none", "uppercase", "lowercase", "capitalize"] },
        "outline": { "$ref": "#/$defs/Stroke" },
        "shadow": { "$ref": "#/$defs/Shadow" },
        "glow": { "$ref": "#/$defs/Glow" }
      },
      "not": { "required": ["linkUrl", "linkSlideId"] }
    },
    "Bounds": {
      "type": "object",
      "additionalProperties": false,
      "required": ["x", "y", "width", "height"],
      "description": "Axis-aligned box in canvas pixels, origin top-left, before rotation. Fractional values allowed.",
      "properties": {
        "x": { "type": "number" },
        "y": { "type": "number" },
        "width": { "type": "number", "minimum": 0 },
        "height": { "type": "number", "minimum": 0 }
      }
    },
    "Gradient": {
      "type": "object",
      "additionalProperties": false,
      "required": ["kind", "stops"],
      "properties": {
        "kind": { "enum": ["linear", "radial"] },
        "angleDeg": {
          "type": "number",
          "description": "Linear gradients: direction, degrees clockwise from 'to right'."
        },
        "center": {
          "type": "object",
          "additionalProperties": false,
          "required": ["x", "y"],
          "properties": { "x": { "$ref": "#/$defs/Fraction" }, "y": { "$ref": "#/$defs/Fraction" } }
        },
        "stops": {
          "type": "array",
          "minItems": 2,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["color", "position"],
            "properties": { "color": { "$ref": "#/$defs/ColorValue" }, "position": { "$ref": "#/$defs/Fraction" } }
          }
        }
      }
    },
    "Fill": {
      "type": "object",
      "required": ["type"],
      "discriminator": { "propertyName": "type" },
      "oneOf": [
        { "$ref": "#/$defs/FillNone" },
        { "$ref": "#/$defs/FillSolid" },
        { "$ref": "#/$defs/FillGradient" },
        { "$ref": "#/$defs/FillImage" }
      ]
    },
    "FillNone": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "properties": { "type": { "const": "none" } }
    },
    "FillSolid": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "color"],
      "properties": { "type": { "const": "solid" }, "color": { "$ref": "#/$defs/ColorValue" } }
    },
    "FillGradient": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "gradient"],
      "properties": { "type": { "const": "gradient" }, "gradient": { "$ref": "#/$defs/Gradient" } }
    },
    "FillImage": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "assetId"],
      "properties": {
        "type": { "const": "image" },
        "assetId": { "$ref": "#/$defs/Id" },
        "fit": { "enum": ["cover", "contain", "stretch", "tile", "none"] }
      }
    },
    "Stroke": {
      "type": "object",
      "additionalProperties": false,
      "required": ["widthPx"],
      "properties": {
        "color": { "$ref": "#/$defs/ColorValue" },
        "widthPx": { "type": "number", "minimum": 0 },
        "dash": { "enum": ["solid", "dash", "dot", "dashDot", "longDash"] }
      }
    },
    "Shadow": {
      "type": "object",
      "additionalProperties": false,
      "required": ["color"],
      "properties": {
        "color": { "$ref": "#/$defs/ColorValue" },
        "blurPx": { "type": "number", "minimum": 0 },
        "offsetXPx": { "type": "number" },
        "offsetYPx": { "type": "number" }
      }
    },
    "Glow": {
      "type": "object",
      "additionalProperties": false,
      "required": ["color", "radiusPx"],
      "properties": {
        "color": { "$ref": "#/$defs/ColorValue" },
        "radiusPx": { "type": "number", "minimum": 0 }
      }
    },
    "Background": {
      "type": "object",
      "additionalProperties": false,
      "required": ["fill"],
      "properties": {
        "hidden": { "type": "boolean", "description": "Explicitly hides the recorded background without discarding its fill or provenance." },
        "inherit": {
          "type": "boolean",
          "description": "True when the slide follows its master/layout background; 'fill' then records the resolved appearance."
        },
        "fill": { "$ref": "#/$defs/Fill" },
        "opacity": { "$ref": "#/$defs/Fraction" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "SlideLayoutPlaceholder": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "role", "bounds"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "role": { "enum": ["title", "content", "subtitle", "picture", "caption", "header", "footer", "other"] },
        "bounds": { "$ref": "#/$defs/Bounds" },
        "shapeKind": {
          "enum": ["rectangle", "roundedRectangle", "ellipse", "triangle", "arrow", "callout", "line", "polygon", "freeform", "other", "diamond", "hexagon", "star", "doubleArrow", "elbowConnector", "pointerCallout", "banner", "badge"]
        },
        "prompt": { "type": "string" }
      }
    },
    "SlideLayout": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "name": { "type": "string", "minLength": 1 },
        "kind": {
          "enum": ["title", "title-and-text", "two-columns", "picture-and-caption", "blank", "custom"]
        },
        "background": { "$ref": "#/$defs/Background" },
        "header": { "$ref": "#/$defs/Text" },
        "footer": { "$ref": "#/$defs/Text" },
        "placeholders": {
          "type": "array",
          "items": { "$ref": "#/$defs/SlideLayoutPlaceholder" }
        },
        "objects": {
          "type": "array",
          "items": { "$ref": "#/$defs/SlideObject" }
        }
      }
    },
    "SlideMaster": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "background": { "$ref": "#/$defs/Background" },
        "header": { "$ref": "#/$defs/Text" },
        "footer": { "$ref": "#/$defs/Text" },
        "placeholders": {
          "type": "array",
          "items": { "$ref": "#/$defs/SlideLayoutPlaceholder" }
        },
        "objects": {
          "type": "array",
          "items": { "$ref": "#/$defs/SlideObject" }
        }
      }
    },
    "Slide": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "kind", "layout"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "title": { "type": "string", "description": "Absent when the source stores no title." },
        "kind": { "enum": ["content", "question", "results"] },
        "layout": {
          "enum": ["fixed", "blocks"],
          "description": "fixed: absolutely positioned objects. blocks: vertically stacked content blocks (Rise-style)."
        },
        "layoutName": {
          "enum": ["title", "title-and-text", "two-columns", "picture-and-caption", "blank"],
          "description": "Named layout preset this slide follows."
        },
        "layoutId": {
          "$ref": "#/$defs/Id",
          "description": "Optional reference to a layout in layouts[]."
        },
        "canvas": {
          "type": "object",
          "additionalProperties": false,
          "required": ["width", "height"],
          "description": "Slide-specific canvas size when the source records one that differs from the course canvas.",
          "properties": {
            "width": { "type": "number", "exclusiveMinimum": 0 },
            "height": { "type": "number", "exclusiveMinimum": 0 }
          }
        },
        "questionId": { "$ref": "#/$defs/Id" },
        "poolId": {
          "$ref": "#/$defs/Id",
          "description": "Question pool drawn by a random-draw slide."
        },
        "drawCount": {
          "type": "integer",
          "minimum": 1,
          "description": "Number of questions drawn by a random-draw slide when overriding the pool's drawCount."
        },
        "results": { "$ref": "#/$defs/ResultsView" },
        "durationSeconds": { "$ref": "#/$defs/Seconds" },
        "advance": { "enum": ["user", "auto"] },
        "menu": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "hidden": { "type": "boolean" },
            "includeInSlideCount": { "type": "boolean" },
            "showInReview": { "type": "boolean" }
          }
        },
        "revisit": { "enum": ["resume", "reset", "auto"] },
        "background": { "$ref": "#/$defs/Background" },
        "transition": { "$ref": "#/$defs/Transition" },
        "audio": { "type": "array", "items": { "$ref": "#/$defs/TimelineAudio" } },
        "cuePoints": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "timeSeconds"],
            "properties": {
              "id": { "$ref": "#/$defs/Id" },
              "name": { "type": "string" },
              "timeSeconds": { "$ref": "#/$defs/Seconds" }
            }
          }
        },
        "objects": {
          "type": "array",
          "items": { "$ref": "#/$defs/SlideObject" },
          "description": "Base-layer objects, back to front."
        },
        "layers": { "type": "array", "items": { "$ref": "#/$defs/Layer" } },
        "blocks": { "type": "array", "items": { "$ref": "#/$defs/ContentBlock" } },
        "triggers": {
          "type": "array",
          "items": { "$ref": "#/$defs/Trigger" },
          "description": "Triggers owned by the base layer timeline, in source order (order is not a claim about engine scheduling)."
        },
        "simulation": {
          "$ref": "#/$defs/SimulationConfig",
          "description": "Software-simulation playback configuration for demo, training and assessment slides (task 1C-61)."
        },
        "notes": { "$ref": "#/$defs/Text" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      },
      "allOf": [
        {
          "if": { "properties": { "kind": { "const": "question" } } },
          "then": {
            "oneOf": [{ "required": ["questionId"] }, { "required": ["poolId"] }],
            "not": { "required": ["results"] }
          }
        },
        {
          "if": { "properties": { "kind": { "const": "results" } } },
          "then": {
            "required": ["results"],
            "not": { "anyOf": [{ "required": ["questionId"] }, { "required": ["poolId"] }, { "required": ["drawCount"] }] }
          }
        },
        {
          "if": { "properties": { "kind": { "const": "content" } } },
          "then": {
            "not": {
              "anyOf": [
                { "required": ["questionId"] },
                { "required": ["poolId"] },
                { "required": ["drawCount"] },
                { "required": ["results"] }
              ]
            }
          }
        },
        {
          "if": { "required": ["drawCount"] },
          "then": { "required": ["poolId"] }
        },
        {
          "if": { "properties": { "layout": { "const": "blocks" } } },
          "then": { "required": ["blocks"] },
          "else": { "not": { "required": ["blocks"] } }
        }
      ]
    },
    "Transition": {
      "type": "object",
      "additionalProperties": false,
      "required": ["effect"],
      "properties": {
        "effect": { "enum": ["none", "fade", "push", "wipe", "cover", "uncover", "zoom", "morph", "other"] },
        "nativeEffect": { "type": "string" },
        "direction": { "enum": ["left", "right", "up", "down"] },
        "durationSeconds": { "$ref": "#/$defs/Seconds" }
      }
    },
    "TimelineAudio": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "assetId"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "assetId": { "$ref": "#/$defs/Id" },
        "startSeconds": { "$ref": "#/$defs/Seconds" },
        "durationSeconds": { "$ref": "#/$defs/Seconds" },
        "assetOffsetSeconds": { "$ref": "#/$defs/Seconds" },
        "volume": { "$ref": "#/$defs/Fraction" },
        "loop": { "type": "boolean" },
        "captionsAssetId": { "$ref": "#/$defs/Id" },
        "transcript": { "$ref": "#/$defs/Text" },
        "transcriptAssetId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "Layer": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "kind"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "name": {
          "type": "string",
          "description": "Absent when the source stores no name (e.g. published Storyline output keeps only an opaque layer id)."
        },
        "kind": {
          "enum": ["layer", "feedback", "panel"],
          "description": "panel: a show/hide container normalized from a source without native layers (e.g. a Captivate responsive container)."
        },
        "feedbackRole": {
          "enum": ["correct", "incorrect", "tryAgain", "incomplete", "timeout", "pass", "fail", "other"]
        },
        "initiallyVisible": { "type": "boolean" },
        "hideBaseLayer": { "type": "boolean" },
        "hideOtherLayers": { "type": "boolean" },
        "pauseBaseTimeline": { "type": "boolean" },
        "modal": { "type": "boolean" },
        "preventBaseClicks": { "type": "boolean" },
        "hideOnTimelineEnd": { "type": "boolean" },
        "durationSeconds": { "$ref": "#/$defs/Seconds" },
        "background": { "$ref": "#/$defs/Background" },
        "objects": { "type": "array", "items": { "$ref": "#/$defs/SlideObject" } },
        "blocks": {
          "type": "array",
          "items": { "$ref": "#/$defs/ContentBlock" },
          "description": "Flow content of a layer on a block-layout slide: the player shows the layer as a dialog card over the dimmed page (EM-65). Only on a slide whose layout is blocks (Course Studio addition, docs/decisions/block-layers.md)."
        },
        "triggers": { "type": "array", "items": { "$ref": "#/$defs/Trigger" } },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      },
      "if": { "properties": { "kind": { "const": "feedback" } } },
      "then": { "required": ["feedbackRole"] },
      "else": { "not": { "required": ["feedbackRole"] } }
    },
    "Table": {
      "type": "object",
      "additionalProperties": false,
      "required": ["rows"],
      "properties": {
        "rows": {
          "type": "array", "minItems": 1,
          "items": {
            "type": "object", "additionalProperties": false, "required": ["cells"],
            "properties": { "cells": { "type": "array", "minItems": 1, "items": {
              "type": "object", "additionalProperties": false, "required": ["text"],
              "properties": { "text": { "$ref": "#/$defs/Text" } }
            } } }
          }
        },
        "headerRow": { "type": "boolean" },
        "caption": { "$ref": "#/$defs/Text" }
      }
    },
    "SlideObject": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "type"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "name": { "type": "string" },
        "scriptName": {
          "type": "string",
          "pattern": "^[A-Za-z][A-Za-z0-9_]*$",
          "maxLength": 128,
          "description": "Optional author-chosen name course JavaScript uses to reach this object (course.object('Name')). Letters, digits and underscores, starting with a letter; unique within its slide (master and each layout are their own scopes). Never invented by an importer (docs/decisions/course-script-api.md)."
        },
        "type": {
          "enum": [
            "text",
            "image",
            "vector",
            "shape",
            "button",
            "video",
            "audio",
            "webObject",
            "table",
            "textEntry",
            "slider",
            "dial",
            "checkbox",
            "radioButton",
            "dropdown",
            "group",
            "widget",
            "other"
          ]
        },
        "nativeKind": { "type": "string", "description": "Native element/class, required for type 'other'." },
        "bounds": { "$ref": "#/$defs/Bounds" },
        "rotationDeg": {
          "type": "number",
          "minimum": 0,
          "exclusiveMaximum": 360,
          "description": "Clockwise rotation about the box centre, normalized to [0, 360)."
        },
        "scale": {
          "type": "object",
          "additionalProperties": false,
          "required": ["x", "y"],
          "description": "Scale multipliers (1 = 100%) applied about the box centre to the unscaled bounds. Omit when 1. Importers must convert source percentages and record an omission when the source relation between bounds and scale is not established.",
          "properties": {
            "x": { "type": "number", "exclusiveMinimum": 0 },
            "y": { "type": "number", "exclusiveMinimum": 0 }
          }
        },
        "flipHorizontal": { "type": "boolean" },
        "flipVertical": { "type": "boolean" },
        "opacity": { "$ref": "#/$defs/Fraction" },
        "initiallyHidden": { "type": "boolean" },
        "styleId": {
          "$ref": "#/$defs/Id",
          "description": "Optional reference to a named object style in objectStyles[]."
        },
        "placeholderRole": { "enum": ["title", "content", "subtitle", "picture", "caption", "header", "footer", "other"] },
        "breakpointOverrides": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["breakpointId"],
            "properties": {
              "breakpointId": { "$ref": "#/$defs/Id" },
              "bounds": { "$ref": "#/$defs/Bounds" },
              "hidden": { "type": "boolean" },
              "fontSizePt": { "type": "number", "exclusiveMinimum": 0 }
            }
          }
        },
        "timing": { "$ref": "#/$defs/Timing" },
        "animations": { "$ref": "#/$defs/Animations" },
        "states": { "type": "array", "items": { "$ref": "#/$defs/ObjectState" } },
        "initialState": {
          "type": "string",
          "minLength": 1,
          "description": "Name of the state shown first; must match an entry in 'states'."
        },
        "accessibility": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "altText": { "type": "string" },
            "role": {
              "enum": ["heading", "button", "link", "caption", "none"],
              "description": "Explicit semantic role, when the source sets one."
            },
            "headingLevel": { "type": "integer", "minimum": 1, "maximum": 6 },
            "hidden": { "type": "boolean", "description": "Hidden from assistive technology." },
            "tabOrder": { "type": "integer", "minimum": 0 }
          },
          "dependentSchemas": {
            "headingLevel": { "required": ["role"], "properties": { "role": { "const": "heading" } } }
          }
        },
        "text": { "$ref": "#/$defs/Text" },
        "textBox": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "verticalAlign": { "enum": ["top", "middle", "bottom"] },
            "autoFit": { "enum": ["none", "shrinkText", "resizeShape"] },
            "wrap": { "type": "boolean" },
            "scrollOverflow": { "type": "boolean" },
            "marginsPx": {
              "type": "object",
              "additionalProperties": false,
              "required": ["left", "top", "right", "bottom"],
              "properties": {
                "left": { "type": "number", "minimum": 0 },
                "top": { "type": "number", "minimum": 0 },
                "right": { "type": "number", "minimum": 0 },
                "bottom": { "type": "number", "minimum": 0 }
              }
            }
          }
        },
        "shape": { "$ref": "#/$defs/ShapeStyle" },
        "image": {
          "type": "object",
          "additionalProperties": false,
          "required": ["assetId"],
          "properties": {
            "assetId": { "$ref": "#/$defs/Id" },
            "fit": { "enum": ["cover", "contain", "stretch", "none"] },
            "crop": {
              "type": "object",
              "additionalProperties": false,
              "required": ["left", "top", "right", "bottom"],
              "description": "Fraction of the source image removed from each edge.",
              "properties": {
                "left": { "$ref": "#/$defs/Fraction" },
                "top": { "$ref": "#/$defs/Fraction" },
                "right": { "$ref": "#/$defs/Fraction" },
                "bottom": { "$ref": "#/$defs/Fraction" }
              }
            }
          }
        },
        "media": {
          "type": "object",
          "additionalProperties": false,
          "required": ["assetId"],
          "properties": {
            "assetId": { "$ref": "#/$defs/Id" },
            "autoplay": { "type": "boolean" },
            "loop": { "type": "boolean" },
            "showControls": { "type": "boolean" },
            "volume": { "$ref": "#/$defs/Fraction" },
            "captionsAssetId": { "$ref": "#/$defs/Id" },
            "captions": { "$ref": "#/$defs/CaptionTrack" },
            "bookmarks": { "type": "array", "items": { "$ref": "#/$defs/MediaBookmark" } },
            "posterAssetId": { "$ref": "#/$defs/Id" },
            "transcript": { "$ref": "#/$defs/Text" },
            "transcriptAssetId": { "$ref": "#/$defs/Id" }
          }
        },
        "table": { "$ref": "#/$defs/Table" },
        "web": {
          "type": "object",
          "additionalProperties": false,
          "required": ["url"],
          "properties": {
            "url": { "type": "string", "format": "uri-reference" },
            "openInNewWindow": { "type": "boolean" },
            "interaction": { "$ref": "#/$defs/WebInteractionManifest" }
          }
        },
        "input": { "$ref": "#/$defs/InputConfig" },
        "dragDrop": {
          "$ref": "#/$defs/DragDropConfig",
          "description": "Freeform drag-and-drop configuration for stage draggables and drop targets (task 1C-58)."
        },
        "simulation": {
          "$ref": "#/$defs/SimulationObjectConfig",
          "description": "Software-simulation role, accepted inputs and feedback for click boxes, text-entry fields, highlight boxes and recorded pointers (task 1C-61)."
        },
        "widget": { "$ref": "#/$defs/Widget" },
        "link": { "$ref": "#/$defs/ObjectLink" },
        "scrollOverflow": {
          "type": "boolean",
          "description": "True when a group container scrolls overflowing child objects vertically (scrolling panel)."
        },
        "children": {
          "type": "array",
          "items": { "$ref": "#/$defs/SlideObject" },
          "description": "Group members, back to front. Child bounds are in the same coordinate space as the group (slide canvas or objects-block canvas); importers translate group-relative source coordinates."
        },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      },
      "allOf": [
        { "if": { "properties": { "type": { "const": "table" } } }, "then": { "required": ["table"] } },
        { "if": { "properties": { "type": { "const": "text" } } }, "then": { "required": ["text"] } },
        { "if": { "properties": { "type": { "const": "image" } } }, "then": { "required": ["image"] } },
        {
          "if": { "properties": { "type": { "const": "vector" } } },
          "then": {
            "anyOf": [
              { "required": ["image"] },
              { "required": ["shape"], "properties": { "shape": { "type": "object", "required": ["pathData"] } } }
            ]
          }
        },
        { "if": { "properties": { "type": { "const": "shape" } } }, "then": { "required": ["shape"] } },
        { "if": { "properties": { "type": { "enum": ["video", "audio"] } } }, "then": { "required": ["media"] } },
        { "if": { "properties": { "type": { "const": "webObject" } } }, "then": { "required": ["web"] } },
        {
          "if": { "properties": { "type": { "enum": ["textEntry", "slider", "dial", "checkbox", "radioButton", "dropdown"] } } },
          "then": { "required": ["input"] },
          "else": { "not": { "required": ["input"] } }
        },
        {
          "if": { "properties": { "type": { "const": "group" } } },
          "then": { "required": ["children"] },
          "else": { "not": { "required": ["children"] } }
        },
        {
          "if": { "properties": { "type": { "const": "widget" } } },
          "then": { "required": ["widget"] },
          "else": { "not": { "required": ["widget"] } }
        },
        { "if": { "properties": { "type": { "const": "other" } } }, "then": { "required": ["nativeKind"] } },
        { "if": { "required": ["initialState"] }, "then": { "required": ["states"] } }
      ]
    },
    "DragDropConfig": {
      "type": "object",
      "additionalProperties": false,
      "description": "Freeform stage drag-and-drop role, behaviour and quiz scoring hooks for a slide object (task 1C-58).",
      "properties": {
        "draggable": {
          "type": "boolean",
          "description": "True when the learner can drag this object across the slide stage."
        },
        "dropTarget": {
          "type": "boolean",
          "description": "True when this object acts as a drop target for stage draggables."
        },
        "returnOnMiss": {
          "type": "boolean",
          "description": "Whether the draggable returns to its origin bounds when dropped outside an accepted target. Absent means unknown (resolved via engine profile)."
        },
        "snapToTarget": {
          "type": "boolean",
          "description": "Whether a dropped object snaps to the drop target's bounds. Absent means unknown (resolved via engine profile)."
        },
        "oneItemPerTarget": {
          "type": "boolean",
          "description": "Whether this drop target accepts at most one draggable at a time. Absent means unknown (resolved via engine profile)."
        },
        "acceptObjectIds": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": { "$ref": "#/$defs/Id" },
          "description": "Optional allow-list of draggable object IDs on the same slide accepted by this drop target."
        },
        "draggableId": {
          "$ref": "#/$defs/Id",
          "description": "Optional quiz scoring hook linking this stage draggable to QuestionDragAndDrop.draggables[].id on the hosting slide."
        },
        "dropTargetId": {
          "$ref": "#/$defs/Id",
          "description": "Optional quiz scoring hook linking this stage drop target to QuestionDragAndDrop.dropTargets[].id on the hosting slide."
        }
      },
      "anyOf": [
        { "required": ["draggable"] },
        { "required": ["dropTarget"] }
      ]
    },
    "SimulationConfig": {
      "type": "object",
      "additionalProperties": false,
      "required": ["mode"],
      "description": "Slide-level software-simulation mode and recorded pointer path (task 1C-61).",
      "properties": {
        "mode": {
          "enum": ["demo", "training", "assessment"],
          "description": "demo: plays the recorded path automatically. training: highlights and waits for the right click or text entry with hints/feedback. assessment: scores attempts without hints."
        },
        "pointerPath": {
          "type": "array",
          "minItems": 1,
          "description": "Optional recorded pointer coordinates in canvas pixels for demo mode.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["x", "y"],
            "properties": {
              "x": { "type": "number" },
              "y": { "type": "number" },
              "timeSeconds": { "$ref": "#/$defs/Seconds" }
            }
          }
        },
        "pointerDurationSeconds": {
          "$ref": "#/$defs/Seconds",
          "description": "Optional duration in seconds for pointer travel in demo mode."
        },
        "advanceOnComplete": {
          "type": "boolean",
          "description": "Whether completing all simulation steps on the slide automatically advances to the next slide when no step trigger navigates."
        }
      }
    },
    "SimulationObjectConfig": {
      "type": "object",
      "additionalProperties": false,
      "required": ["role"],
      "description": "Software-simulation role, accepted answers, attempts, scoring and feedback for a slide object (task 1C-61).",
      "properties": {
        "role": {
          "enum": ["clickBox", "inputField", "highlightBox", "pointer"],
          "description": "clickBox: interactive hotspot target. inputField: simulated text-entry field. highlightBox: visual indicator box. pointer: recorded mouse pointer visual."
        },
        "stepOrder": {
          "type": "integer",
          "minimum": 0,
          "description": "Optional explicit 0-based or 1-based step index ordering simulation steps on the slide."
        },
        "acceptedValues": {
          "type": "array",
          "minItems": 1,
          "description": "Accepted text responses for an inputField simulation step.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["text"],
            "properties": {
              "text": { "type": "string", "minLength": 1 },
              "caseSensitive": { "type": "boolean" }
            }
          }
        },
        "demoText": {
          "type": "string",
          "description": "Text typed automatically into an inputField during demo mode when stated separately from acceptedValues."
        },
        "attempts": {
          "$ref": "#/$defs/Attempts",
          "description": "Maximum learner attempts for this step before locking or advancing in training/assessment mode."
        },
        "points": {
          "type": "number",
          "minimum": 0,
          "description": "Points awarded when this step is completed within its allowed attempts in assessment mode."
        },
        "hintText": {
          "$ref": "#/$defs/Text",
          "description": "Hint text shown and announced in training mode (suppressed in assessment mode)."
        },
        "failureText": {
          "$ref": "#/$defs/Text",
          "description": "Failure/try-again text shown and announced when the learner clicks outside the clickBox or submits an unaccepted inputField value."
        },
        "successText": {
          "$ref": "#/$defs/Text",
          "description": "Success text shown and announced when the learner completes the step."
        },
        "highlightObjectId": {
          "$ref": "#/$defs/Id",
          "description": "Optional highlightBox object ID on the same slide associated with this simulation step."
        }
      }
    },
    "ShapeStyle": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "kind": {
          "enum": [
            "rectangle",
            "roundedRectangle",
            "ellipse",
            "line",
            "triangle",
            "polygon",
            "arrow",
            "callout",
            "freeform",
            "other",
            "diamond",
            "hexagon",
            "star",
            "doubleArrow",
            "elbowConnector",
            "pointerCallout",
            "banner",
            "badge"
          ]
        },
        "nativePreset": { "type": "string" },
        "cornerRadiusPx": { "type": "number", "minimum": 0 },
        "fill": { "$ref": "#/$defs/Fill" },
        "stroke": { "$ref": "#/$defs/Stroke" },
        "shadow": { "$ref": "#/$defs/Shadow" },
        "glow": { "$ref": "#/$defs/Glow" },
        "pathData": {
          "type": "string",
          "minLength": 1,
          "description": "SVG path data in the object's local coordinate box (px, origin top-left of bounds)."
        }
      }
    },
    "ObjectStylePreset": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name"],
      "description": "Named object quick style referenced by SlideObject.styleId.",
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "name": { "type": "string", "minLength": 1 },
        "shape": { "$ref": "#/$defs/ShapeStyle" },
        "textBox": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "verticalAlign": { "enum": ["top", "middle", "bottom"] },
            "autoFit": { "enum": ["none", "shrinkText", "resizeShape"] },
            "wrap": { "type": "boolean" },
            "scrollOverflow": { "type": "boolean" },
            "marginsPx": {
              "type": "object",
              "additionalProperties": false,
              "required": ["left", "top", "right", "bottom"],
              "properties": {
                "left": { "type": "number", "minimum": 0 },
                "top": { "type": "number", "minimum": 0 },
                "right": { "type": "number", "minimum": 0 },
                "bottom": { "type": "number", "minimum": 0 }
              }
            }
          }
        },
        "image": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "fit": { "enum": ["cover", "contain", "stretch", "none"] },
            "crop": {
              "type": "object",
              "additionalProperties": false,
              "required": ["left", "top", "right", "bottom"],
              "description": "Fraction of the source image removed from each edge.",
              "properties": {
                "left": { "$ref": "#/$defs/Fraction" },
                "top": { "$ref": "#/$defs/Fraction" },
                "right": { "$ref": "#/$defs/Fraction" },
                "bottom": { "$ref": "#/$defs/Fraction" }
              }
            }
          }
        },
        "opacity": { "$ref": "#/$defs/Fraction" }
      }
    },
    "InputConfig": {
      "type": "object",
      "additionalProperties": false,
      "required": ["variableId"],
      "properties": {
        "variableId": { "$ref": "#/$defs/Id", "description": "Variable that stores the learner's value." },
        "placeholderText": { "type": "string" },
        "multiline": { "type": "boolean" },
        "numeric": { "type": "boolean" },
        "maxLength": { "type": "integer", "minimum": 1, "description": "Absent means unlimited." },
        "min": { "type": "number" },
        "max": { "type": "number" },
        "step": { "type": "number", "exclusiveMinimum": 0 },
        "orientation": { "enum": ["horizontal", "vertical"] },
        "startAngleDeg": { "type": "number", "description": "Dial starting angle in degrees." },
        "endAngleDeg": { "type": "number", "description": "Dial ending angle in degrees." },
        "groupName": { "type": "string", "description": "Radio-button group." },
        "checkedValue": {
          "type": ["string", "number", "boolean"],
          "description": "Value assigned when a checkbox/radio is selected."
        },
        "options": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["value", "label"],
            "properties": { "value": { "type": ["string", "number", "boolean"] }, "label": { "type": "string" } }
          }
        }
      }
    },
    "Timing": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "startSeconds": { "$ref": "#/$defs/Seconds" },
        "durationSeconds": { "$ref": "#/$defs/Seconds" },
        "untilEnd": { "type": "boolean", "description": "Visible until the end of its timeline." },
        "alwaysShow": { "type": "boolean" }
      },
      "not": { "required": ["durationSeconds", "untilEnd"] }
    },
    "Animations": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "entrance": { "$ref": "#/$defs/Effect" },
        "exit": { "$ref": "#/$defs/Effect" },
        "emphasis": { "type": "array", "items": { "$ref": "#/$defs/EmphasisEffect" } },
        "motionPaths": { "type": "array", "items": { "$ref": "#/$defs/MotionPath" } }
      }
    },
    "Effect": {
      "type": "object",
      "additionalProperties": false,
      "required": ["effect"],
      "properties": {
        "effect": {
          "enum": ["fade", "fly", "float", "grow", "shrink", "growShrink", "wipe", "split", "zoom", "spin", "other"]
        },
        "nativeEffect": { "type": "string" },
        "direction": {
          "enum": [
            "left",
            "right",
            "top",
            "bottom",
            "verticalIn",
            "verticalOut",
            "horizontalIn",
            "horizontalOut",
            "ascending",
            "descending"
          ]
        },
        "durationSeconds": { "$ref": "#/$defs/Seconds" },
        "delaySeconds": { "$ref": "#/$defs/Seconds" },
        "easing": { "enum": ["linear", "easeIn", "easeOut", "easeInOut"] }
      }
    },
    "EmphasisEffect": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "effect"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "effect": { "enum": ["pulse", "bounce", "spin", "other"] },
        "nativeEffect": { "type": "string" },
        "durationSeconds": { "$ref": "#/$defs/Seconds" },
        "intensity": { "enum": ["low", "medium", "high"] },
        "easing": { "enum": ["linear", "easeIn", "easeOut", "easeInOut"] }
      }
    },
    "MotionPath": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "path"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "name": { "type": "string" },
        "path": { "enum": ["line", "arc", "turn", "circle", "square", "trapezoid", "freeform", "other"] },
        "points": {
          "type": "array",
          "minItems": 2,
          "description": "Path vertices in canvas pixels, relative to the object's bounds origin at path start.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["x", "y"],
            "properties": { "x": { "type": "number" }, "y": { "type": "number" } }
          }
        },
        "durationSeconds": { "$ref": "#/$defs/Seconds" },
        "reverse": { "type": "boolean" },
        "autoReverse": { "type": "boolean" },
        "relativeStart": { "type": "boolean" },
        "orientToPath": { "type": "boolean" },
        "easing": { "enum": ["linear", "easeIn", "easeOut", "easeInOut"] },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ObjectState": {
      "type": "object",
      "additionalProperties": false,
      "required": ["name"],
      "description": "A named visual/behavioural variant. Absent appearance properties inherit from the object itself.",
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80,
          "description": "Case-sensitive, unique per object."
        },
        "builtIn": {
          "enum": [
            "normal",
            "hover",
            "down",
            "selected",
            "visited",
            "focus",
            "disabled",
            "hidden",
            "dragOver",
            "dropCorrect",
            "dropIncorrect",
            "correct",
            "incorrect",
            "hint",
            "incomplete",
            "retry",
            "timeout"
          ],
          "description": "Semantic role when the state is a tool built-in; absent for custom states."
        },
        "isDefault": { "type": "boolean" },
        "text": { "$ref": "#/$defs/Text" },
        "fill": { "$ref": "#/$defs/Fill" },
        "stroke": { "$ref": "#/$defs/Stroke" },
        "shadow": { "$ref": "#/$defs/Shadow" },
        "glow": { "$ref": "#/$defs/Glow" },
        "opacity": { "$ref": "#/$defs/Fraction" },
        "image": {
          "type": "object",
          "additionalProperties": false,
          "required": ["assetId"],
          "properties": { "assetId": { "$ref": "#/$defs/Id" } }
        },
        "bounds": {
          "$ref": "#/$defs/Bounds",
          "description": "State-specific geometry in canvas pixels, only when the state does not share the object's box."
        },
        "objects": {
          "type": "array",
          "items": { "$ref": "#/$defs/SlideObject" },
          "description": "Additional visuals that exist only in this state."
        },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "Widget": {
      "type": "object",
      "required": ["family"],
      "discriminator": { "propertyName": "family" },
      "oneOf": [
        { "$ref": "#/$defs/WidgetFlipCards" },
        { "$ref": "#/$defs/WidgetTabs" },
        { "$ref": "#/$defs/WidgetCarousel" },
        { "$ref": "#/$defs/WidgetTimeline" },
        { "$ref": "#/$defs/WidgetProcess" },
        { "$ref": "#/$defs/WidgetHotspots" },
        { "$ref": "#/$defs/WidgetClickToReveal" },
        { "$ref": "#/$defs/WidgetAccordion" },
        { "$ref": "#/$defs/WidgetOther" }
      ]
    },
    "WidgetCommon": {
      "type": "object",
      "properties": {
        "completionRequired": {
          "type": "boolean",
          "description": "Learner must open every item before continuing (Captivate 'forced navigation')."
        },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      }
    },
    "Panel": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id"],
      "description": "A content region inside a widget item: positioned objects or stacked blocks, never both.",
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "objects": { "type": "array", "items": { "$ref": "#/$defs/SlideObject" } },
        "blocks": { "type": "array", "items": { "$ref": "#/$defs/ContentBlock" } },
        "triggers": { "type": "array", "items": { "$ref": "#/$defs/Trigger" } },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      },
      "oneOf": [{ "required": ["objects"] }, { "required": ["blocks"] }]
    },
    "WidgetFlipCards": {
      "allOf": [{ "$ref": "#/$defs/WidgetCommon" }],
      "type": "object",
      "required": ["family", "cards"],
      "unevaluatedProperties": false,
      "properties": {
        "family": { "const": "flipCards" },
        "flipOn": { "enum": ["click", "hover"] },
        "cards": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "front", "back"],
            "properties": {
              "id": { "$ref": "#/$defs/Id" },
              "front": { "$ref": "#/$defs/Panel" },
              "back": { "$ref": "#/$defs/Panel" },
              "sourceRef": { "$ref": "#/$defs/SourceRef" }
            }
          }
        }
      }
    },
    "WidgetTabs": {
      "allOf": [{ "$ref": "#/$defs/WidgetCommon" }],
      "type": "object",
      "required": ["family", "tabs"],
      "unevaluatedProperties": false,
      "properties": {
        "family": { "const": "tabs" },
        "initialIndex": { "type": "integer", "minimum": 0 },
        "tabs": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "label", "content"],
            "properties": {
              "id": { "$ref": "#/$defs/Id" },
              "label": { "$ref": "#/$defs/Text" },
              "content": { "$ref": "#/$defs/Panel" },
              "sourceRef": { "$ref": "#/$defs/SourceRef" }
            }
          }
        }
      }
    },
    "WidgetCarousel": {
      "allOf": [{ "$ref": "#/$defs/WidgetCommon" }],
      "type": "object",
      "required": ["family", "items"],
      "unevaluatedProperties": false,
      "properties": {
        "family": { "const": "carousel" },
        "initialIndex": { "type": "integer", "minimum": 0 },
        "wrap": { "enum": ["wrap", "clamp", "unknown"] },
        "autoplay": { "type": "boolean" },
        "items": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "content"],
            "properties": {
              "id": { "$ref": "#/$defs/Id" },
              "content": { "$ref": "#/$defs/Panel" },
              "sourceRef": { "$ref": "#/$defs/SourceRef" }
            }
          }
        }
      }
    },
    "WidgetTimeline": {
      "allOf": [{ "$ref": "#/$defs/WidgetCommon" }],
      "type": "object",
      "required": ["family", "nodes"],
      "unevaluatedProperties": false,
      "properties": {
        "family": { "const": "timeline" },
        "initiallyOpenIndex": { "type": "integer", "minimum": 0, "description": "Absent: nothing open." },
        "nodes": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "label", "content"],
            "properties": {
              "id": { "$ref": "#/$defs/Id" },
              "label": { "$ref": "#/$defs/Text" },
              "timeLabel": { "$ref": "#/$defs/Text" },
              "content": { "$ref": "#/$defs/Panel" },
              "sourceRef": { "$ref": "#/$defs/SourceRef" }
            }
          }
        }
      }
    },
    "WidgetProcess": {
      "allOf": [{ "$ref": "#/$defs/WidgetCommon" }],
      "type": "object",
      "required": ["family", "steps"],
      "unevaluatedProperties": false,
      "properties": {
        "family": { "const": "process" },
        "steps": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "label", "content"],
            "properties": {
              "id": { "$ref": "#/$defs/Id" },
              "label": { "$ref": "#/$defs/Text" },
              "content": { "$ref": "#/$defs/Panel" },
              "sourceRef": { "$ref": "#/$defs/SourceRef" }
            }
          }
        }
      },
      "description": "Ordered steps the learner moves through one at a time (e.g. a Rise process block)."
    },
    "WidgetHotspots": {
      "allOf": [{ "$ref": "#/$defs/WidgetCommon" }],
      "type": "object",
      "required": ["family", "spots"],
      "unevaluatedProperties": false,
      "properties": {
        "family": { "const": "hotspots" },
        "imageAssetId": { "$ref": "#/$defs/Id" },
        "spots": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "content"],
            "properties": {
              "id": { "$ref": "#/$defs/Id" },
              "x": {
                "type": "number",
                "description": "Pin position in canvas pixels relative to the widget bounds origin."
              },
              "y": { "type": "number" },
              "xPercent": {
                "$ref": "#/$defs/Percent",
                "description": "Pin centre as a percentage of the base image width (responsive sources)."
              },
              "yPercent": { "$ref": "#/$defs/Percent" },
              "revealOn": { "enum": ["click", "hover"] },
              "content": { "$ref": "#/$defs/Panel" },
              "sourceRef": { "$ref": "#/$defs/SourceRef" }
            },
            "oneOf": [{ "required": ["x", "y"] }, { "required": ["xPercent", "yPercent"] }]
          }
        }
      }
    },
    "WidgetClickToReveal": {
      "allOf": [{ "$ref": "#/$defs/WidgetCommon" }],
      "type": "object",
      "required": ["family", "cards"],
      "unevaluatedProperties": false,
      "properties": {
        "family": { "const": "clickToReveal" },
        "initiallyActiveIndex": { "type": "integer", "minimum": 0, "description": "Absent: nothing revealed." },
        "cards": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "label", "content"],
            "properties": {
              "id": { "$ref": "#/$defs/Id" },
              "label": { "$ref": "#/$defs/Text" },
              "content": { "$ref": "#/$defs/Panel" },
              "sourceRef": { "$ref": "#/$defs/SourceRef" }
            }
          }
        }
      }
    },
    "WidgetAccordion": {
      "allOf": [{ "$ref": "#/$defs/WidgetCommon" }],
      "type": "object",
      "required": ["family", "items"],
      "unevaluatedProperties": false,
      "properties": {
        "family": { "const": "accordion" },
        "allowMultipleOpen": { "type": "boolean" },
        "items": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "title", "content"],
            "properties": {
              "id": { "$ref": "#/$defs/Id" },
              "title": { "$ref": "#/$defs/Text" },
              "initiallyExpanded": { "type": "boolean" },
              "content": { "$ref": "#/$defs/Panel" },
              "sourceRef": { "$ref": "#/$defs/SourceRef" }
            }
          }
        }
      }
    },
    "WidgetOther": {
      "allOf": [{ "$ref": "#/$defs/WidgetCommon" }],
      "type": "object",
      "required": ["family", "nativeKind", "panels"],
      "unevaluatedProperties": false,
      "properties": {
        "family": { "const": "other" },
        "nativeKind": { "type": "string", "minLength": 1 },
        "panels": { "type": "array", "items": { "$ref": "#/$defs/Panel" } }
      }
    },
    "ContentBlock": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "type"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "type": {
          "enum": [
            "heading",
            "text",
            "image",
            "gallery",
            "media",
            "video",
            "videoGrid",
            "embed",
            "attachment",
            "statement",
            "quote",
            "list",
            "divider",
            "button",
            "continue",
            "container",
            "widget",
            "question",
            "objects",
            "other"
          ],
          "description": "continue: a gate the learner activates to reveal the following blocks. container: a nested group of blocks (e.g. a modern Captivate responsive container)."
        },
        "nativeKind": {
          "type": "string",
          "description": "Native block type/variant, verbatim. Required for type other."
        },
        "text": { "$ref": "#/$defs/Text" },
        "headingLevel": { "type": "integer", "minimum": 1, "maximum": 6 },
        "attribution": { "$ref": "#/$defs/Text", "description": "Quote attribution or media credit." },
        "assetId": { "$ref": "#/$defs/Id" },
        "assetIds": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": { "$ref": "#/$defs/Id" },
          "description": "Gallery images in display order."
        },
        "link": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "url": { "type": "string", "format": "uri-reference" },
            "slideId": { "$ref": "#/$defs/Id" },
            "openInNewWindow": { "type": "boolean" }
          },
          "oneOf": [{ "required": ["url"] }, { "required": ["slideId"] }]
        },
        "widget": { "$ref": "#/$defs/Widget" },
        "questionId": { "$ref": "#/$defs/Id" },
        "objects": { "type": "array", "items": { "$ref": "#/$defs/SlideObject" } },
        "blocks": {
          "type": "array",
          "items": { "$ref": "#/$defs/ContentBlock" },
          "description": "Children of a container block, in flow order."
        },
        "canvas": {
          "type": "object",
          "additionalProperties": false,
          "required": ["width", "height"],
          "description": "Coordinate space for objects in an objects block.",
          "properties": {
            "width": { "type": "number", "exclusiveMinimum": 0 },
            "height": { "type": "number", "exclusiveMinimum": 0 }
          }
        },
        "column": {
          "enum": ["full", "inset", "left", "right"],
          "description": "Horizontal placement within the flow column."
        },
        "spacing": {
          "type": "object",
          "additionalProperties": false,
          "properties": { "topPx": { "type": "number", "minimum": 0 }, "bottomPx": { "type": "number", "minimum": 0 } }
        },
        "mediaSize": {
          "type": "object",
          "additionalProperties": false,
          "minProperties": 1,
          "description": "Image, gallery, video or media blocks only: how large the picture or player is inside the block. widthPercent is a share of the block's width (the aspect ratio is always kept); align places a narrower picture; maxHeightPx caps a tall picture; narrow says what happens in a narrow reading column (full: fill the column; keep: keep widthPercent). Each absent key resolves through the runtime profile; an absent object keeps the full block width.",
          "properties": {
            "widthPercent": { "type": "number", "exclusiveMinimum": 0, "maximum": 100 },
            "align": { "enum": ["left", "center", "right"] },
            "maxHeightPx": { "type": "number", "exclusiveMinimum": 0 },
            "narrow": { "enum": ["full", "keep"] }
          }
        },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      },
      "allOf": [
        {
          "if": { "required": ["mediaSize"] },
          "then": { "properties": { "type": { "enum": ["image", "gallery", "video", "media"] } } }
        },
        {
          "if": { "properties": { "type": { "enum": ["heading", "text", "quote", "list", "statement"] } } },
          "then": { "required": ["text"] }
        },
        {
          "if": { "properties": { "type": { "enum": ["image", "media", "attachment"] } } },
          "then": { "required": ["assetId"] }
        },
        { "if": { "properties": { "type": { "const": "gallery" } } }, "then": { "required": ["assetIds"] } },
        {
          "if": { "properties": { "type": { "enum": ["video", "videoGrid"] } } },
          "then": {
            "required": ["objects"],
            "properties": {
              "objects": { "type": "array", "minItems": 1, "items": { "type": "object", "properties": { "type": { "const": "video" } } } }
            }
          }
        },
        { "if": { "properties": { "type": { "const": "video" } } }, "then": { "properties": { "objects": { "type": "array", "maxItems": 1 } } } },
        {
          "if": { "properties": { "type": { "const": "embed" } } },
          "then": { "required": ["link"], "properties": { "link": { "type": "object", "required": ["url"] } } }
        },
        { "if": { "properties": { "type": { "const": "button" } } }, "then": { "required": ["text"] } },
        { "if": { "properties": { "type": { "const": "widget" } } }, "then": { "required": ["widget"] } },
        { "if": { "properties": { "type": { "const": "question" } } }, "then": { "required": ["questionId"] } },
        { "if": { "properties": { "type": { "const": "objects" } } }, "then": { "required": ["objects"] } },
        { "if": { "properties": { "type": { "const": "other" } } }, "then": { "required": ["nativeKind"] } },
        { "if": { "required": ["headingLevel"] }, "then": { "properties": { "type": { "const": "heading" } } } },
        { "if": { "required": ["canvas"] }, "then": { "properties": { "type": { "const": "objects" } } } },
        {
          "if": { "properties": { "type": { "const": "container" } } },
          "then": { "required": ["blocks"] },
          "else": { "not": { "required": ["blocks"] } }
        }
      ],
      "description": "One stacked block of a block-layout slide or panel."
    },
    "Trigger": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "event", "actions"],
      "description": "Event -> ordered actions. Conditional logic is expressed only with 'if' actions.",
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "name": { "type": "string" },
        "enabled": { "type": "boolean" },
        "event": { "$ref": "#/$defs/Event" },
        "actions": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Action" } },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      }
    },
    "Event": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "properties": {
        "type": {
          "enum": [
            "click",
            "doubleClick",
            "mouseEnter",
            "mouseLeave",
            "timelineStart",
            "timelineEnd",
            "lostFocus",
            "valueChanged",
            "variableChanged",
            "stateChanged",
            "keyPress",
            "dropOn",
            "objectsIntersect",
            "intersectionEnds",
            "objectEntersSlide",
            "objectLeavesSlide",
            "mediaEnded",
            "mediaBookmark",
            "cuePoint",
            "playerNext",
            "playerPrevious",
            "playerSubmit",
            "questionSubmitted"
          ]
        },
        "objectId": { "$ref": "#/$defs/Id" },
        "blockId": {
          "$ref": "#/$defs/Id",
          "description": "click only: a button or image ContentBlock on the owning block slide, in place of objectId (Course Studio addition, docs/decisions/block-triggers.md)."
        },
        "questionId": { "$ref": "#/$defs/Id" },
        "targetObjectId": { "$ref": "#/$defs/Id", "description": "dropOn / objectsIntersect / intersectionEnds: the target object." },
        "variableId": { "$ref": "#/$defs/Id" },
        "state": { "type": "string", "minLength": 1 },
        "key": { "type": "string", "minLength": 1 },
        "modifiers": { "type": "array", "uniqueItems": true, "items": { "enum": ["ctrl", "shift", "alt", "meta"] } },
        "keyCaseSensitive": { "type": "boolean" },
        "cuePointId": { "$ref": "#/$defs/Id" },
        "bookmarkId": { "$ref": "#/$defs/Id" },
        "nativeEvent": {
          "type": "string",
          "minLength": 1,
          "description": "Source event token, verbatim, when the normalized type loses a distinction (e.g. press vs release)."
        }
      },
      "allOf": [
        {
          "if": { "properties": { "type": { "const": "mediaBookmark" } } },
          "then": { "required": ["bookmarkId"] },
          "else": { "not": { "required": ["bookmarkId"] } }
        },
        {
          "if": { "properties": { "type": { "const": "questionSubmitted" } } },
          "then": { "required": ["questionId"], "not": { "required": ["objectId"] } },
          "else": { "not": { "required": ["questionId"] } }
        },
        {
          "if": {
            "properties": {
              "type": {
                "enum": [
                  "doubleClick",
                  "mouseEnter",
                  "mouseLeave",
                  "lostFocus",
                  "valueChanged",
                  "stateChanged",
                  "mediaEnded",
            "mediaBookmark",
                  "dropOn",
                  "objectsIntersect",
                  "intersectionEnds",
                  "objectEntersSlide",
                  "objectLeavesSlide"
                ]
              }
            }
          },
          "then": { "required": ["objectId"] }
        },
        {
          "if": { "properties": { "type": { "const": "click" } } },
          "then": { "oneOf": [{ "required": ["objectId"] }, { "required": ["blockId"] }] }
        },
        {
          "if": { "required": ["blockId"] },
          "then": { "properties": { "type": { "const": "click" } } }
        },
        {
          "if": { "properties": { "type": { "enum": ["dropOn", "objectsIntersect", "intersectionEnds"] } } },
          "then": { "required": ["targetObjectId"] },
          "else": { "not": { "required": ["targetObjectId"] } }
        },
        {
          "if": { "properties": { "type": { "const": "variableChanged" } } },
          "then": { "required": ["variableId"] },
          "else": { "not": { "required": ["variableId"] } }
        },
        {
          "if": { "properties": { "type": { "const": "stateChanged" } } },
          "then": { "required": ["state"] },
          "else": { "not": { "required": ["state"] } }
        },
        {
          "if": { "properties": { "type": { "const": "keyPress" } } },
          "then": { "required": ["key"] },
          "else": {
            "not": {
              "anyOf": [{ "required": ["key"] }, { "required": ["modifiers"] }, { "required": ["keyCaseSensitive"] }]
            }
          }
        },
        {
          "if": { "properties": { "type": { "const": "cuePoint" } } },
          "then": { "required": ["cuePointId"] },
          "else": { "not": { "required": ["cuePointId"] } }
        }
      ]
    },
    "Operand": {
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["variableId"],
          "properties": { "variableId": { "$ref": "#/$defs/Id" } }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["literal"],
          "properties": { "literal": { "type": ["string", "number", "boolean"] } }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["objectStateOf"],
          "description": "The current state name of an object (compare with a text literal).",
          "properties": {
            "objectStateOf": { "$ref": "#/$defs/Id" },
            "slideId": { "$ref": "#/$defs/Id" },
            "target": { "$ref": "#/$defs/CrossSlideObjectRef" }
          }
        }
      ]
    },
    "CrossSlideObjectRef": {
      "type": "object",
      "additionalProperties": false,
      "required": ["slide", "object"],
      "description": "Stable cross-slide or shared-object reference ({ slide, object }). Use 'master' for a slide-master object.",
      "properties": {
        "slide": { "$ref": "#/$defs/Id" },
        "object": { "$ref": "#/$defs/Id" }
      }
    },
    "Condition": {
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["all"],
          "properties": { "all": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Condition" } } }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["any"],
          "properties": { "any": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Condition" } } }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["not"],
          "properties": { "not": { "$ref": "#/$defs/Condition" } }
        },
        { "$ref": "#/$defs/Comparison" }
      ]
    },
    "Comparison": {
      "type": "object",
      "additionalProperties": false,
      "required": ["left", "operator"],
      "properties": {
        "left": { "$ref": "#/$defs/Operand" },
        "operator": {
          "enum": ["eq", "neq", "gt", "gte", "lt", "lte", "contains", "notContains", "isEmpty", "isNotEmpty"]
        },
        "right": { "$ref": "#/$defs/Operand" },
        "caseSensitive": {
          "type": "boolean",
          "description": "Text comparisons only. Absent means the source did not say."
        }
      },
      "if": { "properties": { "operator": { "enum": ["isEmpty", "isNotEmpty"] } } },
      "then": { "not": { "required": ["right"] } },
      "else": { "required": ["right"] }
    },
    "Action": {
      "type": "object",
      "required": ["type"],
      "discriminator": { "propertyName": "type" },
      "oneOf": [
        { "$ref": "#/$defs/ActionGoToSlide" },
        { "$ref": "#/$defs/ActionNextSlide" },
        { "$ref": "#/$defs/ActionPreviousSlide" },
        { "$ref": "#/$defs/ActionExitCourse" },
        { "$ref": "#/$defs/ActionCompleteCourse" },
        { "$ref": "#/$defs/ActionPrintSlide" },
        { "$ref": "#/$defs/ActionDelay" },
        { "$ref": "#/$defs/ActionShowLayer" },
        { "$ref": "#/$defs/ActionHideLayer" },
        { "$ref": "#/$defs/ActionShowObject" },
        { "$ref": "#/$defs/ActionHideObject" },
        { "$ref": "#/$defs/ActionEnableObject" },
        { "$ref": "#/$defs/ActionDisableObject" },
        { "$ref": "#/$defs/ActionSetState" },
        { "$ref": "#/$defs/ActionCycleState" },
        { "$ref": "#/$defs/ActionSetVariable" },
        { "$ref": "#/$defs/ActionPlayMedia" },
        { "$ref": "#/$defs/ActionPauseMedia" },
        { "$ref": "#/$defs/ActionStopMedia" },
        { "$ref": "#/$defs/ActionEmphasize" },
        { "$ref": "#/$defs/ActionMoveAlongPath" },
        { "$ref": "#/$defs/ActionSubmitQuestion" },
        { "$ref": "#/$defs/ActionSubmitQuiz" },
        { "$ref": "#/$defs/ActionReviewQuiz" },
        { "$ref": "#/$defs/ActionRetryQuiz" },
        { "$ref": "#/$defs/ActionExecuteJavaScript" },
        { "$ref": "#/$defs/ActionOpenUrl" },
        { "$ref": "#/$defs/ActionGoToSection" },
        { "$ref": "#/$defs/ActionLightboxSlide" },
        { "$ref": "#/$defs/ActionShowMediaOverlay" },
        { "$ref": "#/$defs/ActionPauseTimeline" },
        { "$ref": "#/$defs/ActionResumeTimeline" },
        { "$ref": "#/$defs/ActionSeekTimeline" },
        { "$ref": "#/$defs/ActionRunActionGroup" },
        { "$ref": "#/$defs/ActionDownloadAsset" },
        { "$ref": "#/$defs/ActionIf" },
        { "$ref": "#/$defs/ActionUnmapped" }
      ]
    },
    "ActionCompleteCourse": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "properties": {
        "type": { "const": "completeCourse" },
        "status": { "enum": ["completed", "passed", "failed", "incomplete"] },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionPrintSlide": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "description": "Without slideId: print the currently active slide.",
      "properties": {
        "type": { "const": "printSlide" },
        "slideId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionDelay": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "durationSeconds"],
      "description": "Pause execution of subsequent actions in the list for durationSeconds.",
      "properties": {
        "type": { "const": "delay" },
        "durationSeconds": { "$ref": "#/$defs/Seconds" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionGoToSlide": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "slideId"],
      "properties": {
        "type": { "const": "goToSlide" },
        "slideId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionNextSlide": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "properties": { "type": { "const": "nextSlide" }, "sourceRef": { "$ref": "#/$defs/SourceRef" } }
    },
    "ActionPreviousSlide": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "properties": { "type": { "const": "previousSlide" }, "sourceRef": { "$ref": "#/$defs/SourceRef" } }
    },
    "ActionExitCourse": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "properties": { "type": { "const": "exitCourse" }, "sourceRef": { "$ref": "#/$defs/SourceRef" } }
    },
    "ActionShowLayer": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "layerId"],
      "properties": {
        "type": { "const": "showLayer" },
        "layerId": { "$ref": "#/$defs/Id" },
        "slideId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionHideLayer": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "description": "Without layerId: hide the layer that owns the trigger.",
      "properties": {
        "type": { "const": "hideLayer" },
        "layerId": { "$ref": "#/$defs/Id" },
        "slideId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionShowObject": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId"],
      "properties": {
        "type": { "const": "showObject" },
        "objectId": { "$ref": "#/$defs/Id" },
        "slideId": { "$ref": "#/$defs/Id" },
        "target": { "$ref": "#/$defs/CrossSlideObjectRef" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionHideObject": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId"],
      "properties": {
        "type": { "const": "hideObject" },
        "objectId": { "$ref": "#/$defs/Id" },
        "slideId": { "$ref": "#/$defs/Id" },
        "target": { "$ref": "#/$defs/CrossSlideObjectRef" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionEnableObject": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId"],
      "properties": {
        "type": { "const": "enableObject" },
        "objectId": { "$ref": "#/$defs/Id" },
        "slideId": { "$ref": "#/$defs/Id" },
        "target": { "$ref": "#/$defs/CrossSlideObjectRef" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionDisableObject": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId"],
      "properties": {
        "type": { "const": "disableObject" },
        "objectId": { "$ref": "#/$defs/Id" },
        "slideId": { "$ref": "#/$defs/Id" },
        "target": { "$ref": "#/$defs/CrossSlideObjectRef" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionSetState": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId", "state"],
      "properties": {
        "type": { "const": "setState" },
        "objectId": { "$ref": "#/$defs/Id" },
        "state": { "type": "string", "minLength": 1 },
        "slideId": { "$ref": "#/$defs/Id" },
        "target": { "$ref": "#/$defs/CrossSlideObjectRef" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionCycleState": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId", "direction", "wrap"],
      "description": "Move to the next/previous entry of the object's 'states' order. 'wrap' must state what is known about edge behaviour.",
      "properties": {
        "type": { "const": "cycleState" },
        "objectId": { "$ref": "#/$defs/Id" },
        "direction": { "enum": ["next", "previous"] },
        "wrap": { "enum": ["wrap", "clamp", "unknown"] },
        "slideId": { "$ref": "#/$defs/Id" },
        "target": { "$ref": "#/$defs/CrossSlideObjectRef" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionSetVariable": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "variableId", "operator"],
      "properties": {
        "type": { "const": "setVariable" },
        "variableId": { "$ref": "#/$defs/Id" },
        "operator": { "enum": ["assign", "add", "subtract", "multiply", "divide", "modulo", "toggle"] },
        "value": { "$ref": "#/$defs/Operand" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      },
      "if": { "properties": { "operator": { "const": "toggle" } } },
      "then": { "not": { "required": ["value"] } },
      "else": { "required": ["value"] }
    },
    "ActionPlayMedia": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId"],
      "properties": {
        "type": { "const": "playMedia" },
        "objectId": { "$ref": "#/$defs/Id" },
        "slideId": { "$ref": "#/$defs/Id" },
        "target": { "$ref": "#/$defs/CrossSlideObjectRef" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionPauseMedia": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId"],
      "properties": {
        "type": { "const": "pauseMedia" },
        "objectId": { "$ref": "#/$defs/Id" },
        "slideId": { "$ref": "#/$defs/Id" },
        "target": { "$ref": "#/$defs/CrossSlideObjectRef" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionStopMedia": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId"],
      "properties": {
        "type": { "const": "stopMedia" },
        "objectId": { "$ref": "#/$defs/Id" },
        "slideId": { "$ref": "#/$defs/Id" },
        "target": { "$ref": "#/$defs/CrossSlideObjectRef" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionEmphasize": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId", "emphasisId"],
      "properties": {
        "type": { "const": "emphasize" },
        "objectId": { "$ref": "#/$defs/Id" },
        "emphasisId": { "$ref": "#/$defs/Id", "description": "An entry of the object's animations.emphasis." },
        "slideId": { "$ref": "#/$defs/Id" },
        "target": { "$ref": "#/$defs/CrossSlideObjectRef" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionMoveAlongPath": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId", "motionPathId"],
      "properties": {
        "type": { "const": "moveAlongPath" },
        "objectId": { "$ref": "#/$defs/Id" },
        "motionPathId": { "$ref": "#/$defs/Id" },
        "slideId": { "$ref": "#/$defs/Id" },
        "target": { "$ref": "#/$defs/CrossSlideObjectRef" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionSubmitQuestion": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "description": "Without questionId: the question on the owning slide.",
      "properties": {
        "type": { "const": "submitQuestion" },
        "questionId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionSubmitQuiz": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "quizId"],
      "properties": {
        "type": { "const": "submitQuiz" },
        "quizId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionReviewQuiz": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "quizId"],
      "properties": {
        "type": { "const": "reviewQuiz" },
        "quizId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionRetryQuiz": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "quizId"],
      "description": "Reset the quiz responses and return to its first question.",
      "properties": {
        "type": { "const": "retryQuiz" },
        "quizId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionExecuteJavaScript": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "code"],
      "description": "Opaque author script. Consumers must never execute it during import, validation or conversion.",
      "properties": {
        "type": { "const": "executeJavaScript" },
        "code": { "type": "string" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionOpenUrl": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "url"],
      "properties": {
        "type": { "const": "openUrl" },
        "url": { "type": "string", "format": "uri-reference" },
        "target": { "enum": ["newWindow", "sameWindow"] },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionGoToSection": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "sectionId"],
      "description": "Jump to the first slide of a section (e.g. a Storyline scene).",
      "properties": {
        "type": { "const": "goToSection" },
        "sectionId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionLightboxSlide": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "slideId"],
      "description": "Show a slide in a lightbox over the current slide.",
      "properties": {
        "type": { "const": "lightboxSlide" },
        "slideId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionShowMediaOverlay": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "objectId", "slideId", "pauseMedia"],
      "description": "Present a slide over a video; explicit slide choices branch the containing course. Pause/continue is required.",
      "properties": {
        "type": { "const": "showMediaOverlay" },
        "objectId": { "$ref": "#/$defs/Id" },
        "slideId": { "$ref": "#/$defs/Id" },
        "pauseMedia": { "type": "boolean" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionPauseTimeline": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "description": "Without layerId: the base timeline of the owning slide.",
      "properties": {
        "type": { "const": "pauseTimeline" },
        "layerId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionResumeTimeline": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "description": "Without layerId: the base timeline of the owning slide.",
      "properties": {
        "type": { "const": "resumeTimeline" },
        "layerId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionSeekTimeline": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "description": "Jump the timeline to a time or cue point.",
      "properties": {
        "type": { "const": "seekTimeline" },
        "layerId": { "$ref": "#/$defs/Id" },
        "timeSeconds": { "$ref": "#/$defs/Seconds" },
        "cuePointId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      },
      "oneOf": [{ "required": ["timeSeconds"] }, { "required": ["cuePointId"] }]
    },
    "ActionRunActionGroup": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "actionGroupId"],
      "properties": {
        "type": { "const": "runActionGroup" },
        "actionGroupId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionDownloadAsset": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "assetId"],
      "properties": {
        "type": { "const": "downloadAsset" },
        "assetId": { "$ref": "#/$defs/Id" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionIf": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "condition", "then"],
      "description": "Evaluate 'condition' once; run 'then' or 'else'. Independent source decisions are emitted as consecutive 'if' actions, never collapsed into else-if chains.",
      "properties": {
        "type": { "const": "if" },
        "condition": { "$ref": "#/$defs/Condition" },
        "then": { "type": "array", "items": { "$ref": "#/$defs/Action" } },
        "else": { "type": "array", "items": { "$ref": "#/$defs/Action" } },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "ActionUnmapped": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "nativeKind"],
      "description": "Placeholder that keeps the position of a source action CCF cannot express. Must be accompanied by an omission.",
      "properties": {
        "type": { "const": "unmapped" },
        "nativeKind": { "type": "string", "minLength": 1 },
        "description": { "type": "string" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "FeedbackItem": {
      "type": "object",
      "additionalProperties": false,
      "properties": { "text": { "$ref": "#/$defs/Text" }, "layerId": { "$ref": "#/$defs/Id" } },
      "minProperties": 1
    },
    "Choice": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "content"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "content": { "$ref": "#/$defs/Text" },
        "mediaAssetId": { "$ref": "#/$defs/Id" },
        "feedback": {
          "$ref": "#/$defs/FeedbackItem",
          "description": "Choice-specific feedback, when the source has it."
        },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "QuestionCommon": {
      "type": "object",
      "required": ["id", "prompt", "scoring"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "title": { "type": "string" },
        "prompt": { "$ref": "#/$defs/Text" },
        "accessibleText": {
          "$ref": "#/$defs/Text",
          "description": "Separate screen-reader text for the prompt, when the source stores one."
        },
        "mediaAssetId": { "$ref": "#/$defs/Id" },
        "scoring": {
          "type": "object",
          "additionalProperties": false,
          "required": ["mode"],
          "properties": {
            "mode": { "enum": ["graded", "survey", "pretest", "knowledgeCheck"] },
            "correctPoints": { "type": "number", "minimum": 0 },
            "incorrectPoints": {
              "type": "number",
              "description": "Points awarded (may be negative) for an incorrect response."
            },
            "partialCredit": { "type": "boolean" }
          }
        },
        "answerKeyStatus": {
          "enum": ["declared", "inferred", "unresolved"],
          "description": "declared: the key is stated by the source's documented answer-key representation. inferred: derived from indirect evidence (e.g. published runtime data whose key encoding is not documented). unresolved: the key could not be recovered; type-specific key properties are then forbidden and an omission must point at the question. Absent: treated as declared."
        },
        "attempts": { "$ref": "#/$defs/Attempts" },
        "shuffle": {
          "type": "boolean",
          "description": "Whether the source randomizes choice order. Absent: not stated."
        },
        "timeLimitSeconds": { "type": "number", "exclusiveMinimum": 0 },
        "feedback": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "correct": { "$ref": "#/$defs/FeedbackItem" },
            "incorrect": { "$ref": "#/$defs/FeedbackItem" },
            "tryAgain": { "$ref": "#/$defs/FeedbackItem" },
            "incomplete": { "$ref": "#/$defs/FeedbackItem" },
            "timeout": { "$ref": "#/$defs/FeedbackItem" },
            "hint": { "$ref": "#/$defs/FeedbackItem" }
          }
        },
        "responseVariableId": {
          "$ref": "#/$defs/Id",
          "description": "Variable receiving the learner's response, when the source exposes one."
        },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      }
    },
    "Question": {
      "type": "object",
      "required": ["type"],
      "discriminator": { "propertyName": "type" },
      "oneOf": [
        { "$ref": "#/$defs/QuestionMultipleChoice" },
        { "$ref": "#/$defs/QuestionMultipleResponse" },
        { "$ref": "#/$defs/QuestionTrueFalse" },
        { "$ref": "#/$defs/QuestionFillInBlank" },
        { "$ref": "#/$defs/QuestionNumeric" },
        { "$ref": "#/$defs/QuestionMatching" },
        { "$ref": "#/$defs/QuestionSequence" },
        { "$ref": "#/$defs/QuestionHotspot" },
        { "$ref": "#/$defs/QuestionDragAndDrop" },
        { "$ref": "#/$defs/QuestionWordBank" },
        { "$ref": "#/$defs/QuestionCategorize" },
        { "$ref": "#/$defs/QuestionLikert" },
        { "$ref": "#/$defs/QuestionEssay" }
      ]
    },
    "QuestionMultipleChoice": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type", "choices"],
      "unevaluatedProperties": false,
      "properties": {
        "type": { "const": "multiple_choice" },
        "choices": { "type": "array", "minItems": 2, "items": { "$ref": "#/$defs/Choice" } },
        "correctChoiceId": { "$ref": "#/$defs/Id" }
      },
      "if": { "$ref": "#/$defs/KeyUnresolved" },
      "then": { "not": { "required": ["correctChoiceId"] } },
      "else": { "required": ["correctChoiceId"] }
    },
    "QuestionMultipleResponse": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type", "choices"],
      "unevaluatedProperties": false,
      "properties": {
        "type": { "const": "multiple_response" },
        "choices": { "type": "array", "minItems": 2, "items": { "$ref": "#/$defs/Choice" } },
        "correctChoiceIds": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "$ref": "#/$defs/Id" } }
      },
      "if": { "$ref": "#/$defs/KeyUnresolved" },
      "then": { "not": { "required": ["correctChoiceIds"] } },
      "else": { "required": ["correctChoiceIds"] }
    },
    "QuestionTrueFalse": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type"],
      "unevaluatedProperties": false,
      "properties": {
        "type": { "const": "true_false" },
        "correctValue": { "type": "boolean" },
        "trueLabel": { "$ref": "#/$defs/Text" },
        "falseLabel": { "$ref": "#/$defs/Text" }
      },
      "if": { "$ref": "#/$defs/KeyUnresolved" },
      "then": { "not": { "required": ["correctValue"] } },
      "else": { "required": ["correctValue"] }
    },
    "QuestionFillInBlank": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type"],
      "unevaluatedProperties": false,
      "properties": {
        "type": { "const": "fill_in_blank" },
        "acceptedAnswers": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["text"],
            "properties": {
              "text": { "type": "string", "minLength": 1 },
              "caseSensitive": { "type": "boolean", "description": "Absent: not stated by the source." }
            }
          }
        },
        "trimWhitespace": { "type": "boolean" }
      },
      "if": { "$ref": "#/$defs/KeyUnresolved" },
      "then": { "not": { "required": ["acceptedAnswers"] } },
      "else": { "required": ["acceptedAnswers"] }
    },
    "QuestionNumeric": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type"],
      "unevaluatedProperties": false,
      "properties": {
        "type": { "const": "numeric" },
        "answer": {
          "oneOf": [
            {
              "type": "object",
              "additionalProperties": false,
              "required": ["equals"],
              "properties": { "equals": { "type": "number" }, "tolerance": { "type": "number", "minimum": 0 } }
            },
            {
              "type": "object",
              "additionalProperties": false,
              "required": ["min", "max"],
              "properties": { "min": { "type": "number" }, "max": { "type": "number" } }
            }
          ]
        }
      },
      "if": { "$ref": "#/$defs/KeyUnresolved" },
      "then": { "not": { "required": ["answer"] } },
      "else": { "required": ["answer"] }
    },
    "QuestionMatching": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type", "sources", "targets"],
      "unevaluatedProperties": false,
      "properties": {
        "type": { "const": "matching" },
        "answeringMethod": { "enum": ["dragAndDrop", "dropdown"] },
        "sources": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Choice" } },
        "targets": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Choice" } },
        "correctPairs": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["sourceId", "targetId"],
            "properties": { "sourceId": { "$ref": "#/$defs/Id" }, "targetId": { "$ref": "#/$defs/Id" } }
          }
        }
      },
      "if": { "$ref": "#/$defs/KeyUnresolved" },
      "then": { "not": { "required": ["correctPairs"] } },
      "else": { "required": ["correctPairs"] }
    },
    "QuestionSequence": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type", "items"],
      "unevaluatedProperties": false,
      "properties": {
        "type": { "const": "sequence" },
        "answeringMethod": { "enum": ["dragAndDrop", "dropdown"] },
        "items": {
          "type": "array",
          "minItems": 2,
          "items": { "$ref": "#/$defs/Choice" },
          "description": "Items in authored display order."
        },
        "correctOrder": { "type": "array", "minItems": 2, "uniqueItems": true, "items": { "$ref": "#/$defs/Id" } }
      },
      "if": { "$ref": "#/$defs/KeyUnresolved" },
      "then": { "not": { "required": ["correctOrder"] } },
      "else": { "required": ["correctOrder"] }
    },
    "QuestionHotspot": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type"],
      "unevaluatedProperties": false,
      "properties": {
        "type": { "const": "hotspot" },
        "imageAssetId": { "$ref": "#/$defs/Id" },
        "regions": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "shape", "points"],
            "properties": {
              "id": { "$ref": "#/$defs/Id" },
              "shape": { "enum": ["rectangle", "ellipse", "polygon"] },
              "points": {
                "type": "array",
                "minItems": 2,
                "description": "Image pixels. rectangle/ellipse: two opposite corners of the bounding box. polygon: >= 3 vertices.",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": ["x", "y"],
                  "properties": { "x": { "type": "number" }, "y": { "type": "number" } }
                }
              }
            }
          }
        },
        "correctRegionIds": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "$ref": "#/$defs/Id" } }
      },
      "if": { "$ref": "#/$defs/KeyUnresolved" },
      "then": { "not": { "required": ["correctRegionIds"] } },
      "else": { "required": ["imageAssetId", "regions", "correctRegionIds"] }
    },
    "QuestionDragAndDrop": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type", "draggables", "dropTargets"],
      "unevaluatedProperties": false,
      "properties": {
        "type": { "const": "drag_and_drop" },
        "draggables": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Choice" } },
        "dropTargets": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Choice" } },
        "correctPlacements": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["draggableId", "dropTargetId"],
            "properties": { "draggableId": { "$ref": "#/$defs/Id" }, "dropTargetId": { "$ref": "#/$defs/Id" } }
          }
        },
        "snapToTarget": { "type": "boolean" },
        "returnInvalidDrops": { "type": "boolean" },
        "oneItemPerTarget": { "type": "boolean" }
      },
      "if": { "$ref": "#/$defs/KeyUnresolved" },
      "then": { "not": { "required": ["correctPlacements"] } },
      "else": { "required": ["correctPlacements"] }
    },
    "QuestionWordBank": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type", "tokens"],
      "unevaluatedProperties": false,
      "properties": {
        "type": { "const": "word_bank" },
        "segments": {
          "type": "array",
          "minItems": 1,
          "description": "The sentence: literal text interleaved with blanks.",
          "items": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": ["text"],
                "properties": { "text": { "type": "string" } }
              },
              {
                "type": "object",
                "additionalProperties": false,
                "required": ["blankId"],
                "properties": { "blankId": { "$ref": "#/$defs/Id" } }
              }
            ]
          }
        },
        "tokens": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Choice" } },
        "correctTokens": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["blankId", "tokenId"],
            "properties": { "blankId": { "$ref": "#/$defs/Id" }, "tokenId": { "$ref": "#/$defs/Id" } }
          }
        }
      },
      "if": { "$ref": "#/$defs/KeyUnresolved" },
      "then": { "not": { "required": ["correctTokens"] } },
      "else": { "required": ["segments", "correctTokens"] }
    },
    "QuestionCategorize": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type", "items", "categories"],
      "unevaluatedProperties": false,
      "description": "Sort items into categories (e.g. a Rise sorting activity). Item/category parts are a proposal: no sample has been decoded yet.",
      "properties": {
        "type": { "const": "categorize" },
        "items": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Choice" } },
        "categories": { "type": "array", "minItems": 2, "items": { "$ref": "#/$defs/Choice" } },
        "correctAssignments": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["itemId", "categoryId"],
            "properties": { "itemId": { "$ref": "#/$defs/Id" }, "categoryId": { "$ref": "#/$defs/Id" } }
          }
        }
      },
      "if": { "$ref": "#/$defs/KeyUnresolved" },
      "then": { "not": { "required": ["correctAssignments"] } },
      "else": { "required": ["correctAssignments"] }
    },
    "QuestionLikert": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type", "statements", "scale"],
      "unevaluatedProperties": false,
      "properties": {
        "type": { "const": "likert" },
        "statements": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Choice" } },
        "scale": {
          "type": "array",
          "minItems": 2,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["value", "label"],
            "properties": { "value": { "type": "number" }, "label": { "type": "string" } }
          }
        }
      }
    },
    "QuestionEssay": {
      "allOf": [{ "$ref": "#/$defs/QuestionCommon" }],
      "type": "object",
      "required": ["type"],
      "unevaluatedProperties": false,
      "properties": { "type": { "const": "essay" }, "maxLength": { "type": "integer", "minimum": 1 } }
    },
    "KeyUnresolved": {
      "type": "object",
      "required": ["answerKeyStatus"],
      "properties": { "answerKeyStatus": { "const": "unresolved" } }
    },
    "QuestionPool": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "title", "questionIds"],
      "description": "A named group of questions. With drawCount, a random subset is presented per attempt; without it, every question is presented.",
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "title": { "type": "string" },
        "questionIds": { "type": "array", "uniqueItems": true, "items": { "$ref": "#/$defs/Id" } },
        "drawCount": { "type": "integer", "minimum": 1 },
        "randomize": {
          "type": "boolean",
          "description": "Whether questions drawn from this pool are selected in random order. Absent: not stated."
        },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      }
    },
    "Quiz": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "title", "passingScore"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "title": { "type": "string" },
        "questionIds": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": { "$ref": "#/$defs/Id" },
          "description": "Explicit membership in presentation order."
        },
        "poolIds": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "$ref": "#/$defs/Id" } },
        "passingScore": {
          "oneOf": [
            {
              "type": "object",
              "additionalProperties": false,
              "required": ["percent"],
              "properties": { "percent": { "$ref": "#/$defs/Percent" } }
            },
            {
              "type": "object",
              "additionalProperties": false,
              "required": ["points"],
              "properties": { "points": { "type": "number", "minimum": 0 } }
            }
          ],
          "description": "Exactly one passing rule. Normalize each tool's own enum (Captivate Classic and modern encode percent/points with opposite numeric values)."
        },
        "maxPoints": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "Total available points when the source states it."
        },
        "timeLimitSeconds": { "type": "number", "exclusiveMinimum": 0 },
        "attempts": { "$ref": "#/$defs/Attempts" },
        "allowReview": { "type": "boolean" },
        "allowRetake": { "type": "boolean" },
        "requireAllQuestions": {
          "type": "boolean",
          "description": "Whether learners must answer every presented question before advancing or submitting the quiz. Absent: not stated."
        },
        "submitAllAtOnce": {
          "type": "boolean",
          "description": "Whether individual question slides defer submission and submit together when the quiz is submitted. Absent: not stated."
        },
        "confirmBeforeSubmit": {
          "type": "boolean",
          "description": "Whether the learner is asked to confirm before finalizing quiz submission. Absent: not stated."
        },
        "retryOnlyIncorrect": {
          "type": "boolean",
          "description": "Whether retaking the quiz resets and presents only questions that were not answered correctly on the previous attempt. Absent: not stated."
        },
        "scoreOnlyViewed": {
          "type": "boolean",
          "description": "Whether quiz scoring counts only presented questions that were viewed or submitted during the attempt. Absent: not stated."
        },
        "introSlideId": { "$ref": "#/$defs/Id" },
        "resultsSlideId": { "$ref": "#/$defs/Id" },
        "reportToLms": { "type": "boolean" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      },
      "anyOf": [{ "required": ["questionIds"] }, { "required": ["poolIds"] }]
    },
    "ResultsView": {
      "type": "object",
      "additionalProperties": false,
      "required": ["quizId"],
      "properties": {
        "quizId": { "$ref": "#/$defs/Id" },
        "passLayerId": { "$ref": "#/$defs/Id" },
        "failLayerId": { "$ref": "#/$defs/Id" },
        "buttons": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "review": { "type": "boolean" },
            "retry": { "type": "boolean" },
            "continue": { "type": "boolean" },
            "print": { "type": "boolean" }
          }
        }
      }
    },
    "ActionGroup": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "actions"],
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "name": { "type": "string" },
        "exposedAs": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",
          "description": "Unique name under which this action group is exposed to outside interactions. Absent means not exposed."
        },
        "slideId": {
          "$ref": "#/$defs/Id",
          "description": "Owning slide when the source scopes the group to one slide; object and layer references then resolve on that slide."
        },
        "actions": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/Action" } },
        "sourceRef": { "$ref": "#/$defs/SourceRef" },
        "vendorExtensions": { "$ref": "#/$defs/VendorExtensions" }
      }
    },
    "MediaBookmark": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "timeSeconds"],
      "description": "A declared point on a video's own media clock; never a slide-timeline cue or an inferred navigation edge.",
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "timeSeconds": { "type": "number", "minimum": 0 },
        "label": { "type": "string" },
        "sourceRef": { "$ref": "#/$defs/SourceRef" }
      }
    },
    "CaptionCue": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "startSeconds", "endSeconds", "text"],
      "description": "A timed closed-caption cue on a media object.",
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "startSeconds": { "$ref": "#/$defs/Seconds" },
        "endSeconds": { "$ref": "#/$defs/Seconds" },
        "text": { "type": "string" },
        "speaker": { "type": "string" }
      }
    },
    "CaptionAppearance": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional appearance settings for closed captions on a media object. Absent fields use runtime defaults.",
      "properties": {
        "position": { "enum": ["bottom", "top"] },
        "fontSize": { "enum": ["small", "medium", "large"] },
        "backgroundColor": { "$ref": "#/$defs/ColorValue" },
        "textColor": { "$ref": "#/$defs/ColorValue" }
      }
    },
    "CaptionTrack": {
      "type": "object",
      "additionalProperties": false,
      "required": ["cues"],
      "description": "Structured closed-caption track and optional appearance settings on a media object.",
      "properties": {
        "language": {
          "type": "string",
          "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$",
          "description": "BCP 47 language tag."
        },
        "cues": {
          "type": "array",
          "items": { "$ref": "#/$defs/CaptionCue" }
        },
        "appearance": { "$ref": "#/$defs/CaptionAppearance" }
      }
    },
    "WebInteractionManifest": {
      "type": "object",
      "additionalProperties": false,
      "required": ["entryAssetId"],
      "description": "Declared manifest for an outside interaction packaged with the course and mounted inside a sandboxed iframe.",
      "properties": {
        "id": { "$ref": "#/$defs/Id" },
        "entryAssetId": {
          "$ref": "#/$defs/Id",
          "description": "HTML entry asset inside the course package (for example interactions/colour-picker/index.html)."
        },
        "assetIds": {
          "type": "array",
          "uniqueItems": true,
          "items": { "$ref": "#/$defs/Id" },
          "description": "Additional packaged assets belonging to the interaction folder."
        },
        "reads": {
          "type": "array",
          "uniqueItems": true,
          "items": { "type": "string", "minLength": 1, "maxLength": 128 },
          "description": "Variable names the interaction declares it reads."
        },
        "writes": {
          "type": "array",
          "uniqueItems": true,
          "items": { "type": "string", "minLength": 1, "maxLength": 128 },
          "description": "User variable names the interaction declares it writes."
        },
        "actions": {
          "type": "array",
          "uniqueItems": true,
          "items": { "type": "string", "minLength": 1, "maxLength": 128 },
          "description": "Player actions or exposed action group handles (ActionGroup.exposedAs) the interaction declares it invokes."
        },
        "events": {
          "type": "array",
          "uniqueItems": true,
          "items": { "enum": ["variableChanged", "slideEntered", "layerShown", "layerHidden"] },
          "description": "Player events the interaction subscribes to."
        }
      }
    }
  }
}
