{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Script Breakdown Schema",
  "description": "Data contract for the script breakdown system. Defines the structure of breakdown.json output.",
  "type": "object",
  "required": ["version", "project", "generated", "episodes_processed", "episode_range", "characters", "locations", "props", "sfx_elements", "vfx_elements", "shot_estimates", "asset_lock_status"],
  "properties": {
    "version": {
      "type": "integer",
      "const": 1,
      "description": "Schema version. Always 1."
    },
    "project": {
      "type": "string",
      "description": "Project folder name (e.g., 'leviathan')"
    },
    "generated": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp of generation"
    },
    "episodes_processed": {
      "type": "integer",
      "minimum": 1,
      "maximum": 60,
      "description": "Number of episodes processed in this run"
    },
    "episode_range": {
      "type": "array",
      "items": { "type": "integer", "minimum": 1, "maximum": 60 },
      "minItems": 2,
      "maxItems": 2,
      "description": "[first, last] episode numbers processed"
    },
    "story_timeline": {
      "type": "object",
      "properties": {
        "total_story_days": { "type": "integer", "description": "Total in-universe days across all episodes" },
        "episodes_to_days": {
          "type": "object",
          "additionalProperties": { "type": "integer" },
          "description": "Map of episode number (string key) to story day number"
        }
      }
    },
    "characters": {
      "type": "object",
      "additionalProperties": {
        "type": "object",
        "required": ["display_name", "episodes", "episode_count", "visual_description", "wardrobe", "reference_status"],
        "properties": {
          "display_name": { "type": "string" },
          "episodes": { "type": "array", "items": { "type": "integer" } },
          "episode_count": { "type": "integer" },
          "first_appearance": { "type": "integer" },
          "dialogue_count": { "type": "integer", "description": "Number of dialogue cues across all episodes" },
          "visual_description": { "type": "string", "description": "Full visual description from characters.md" },
          "color_palette": {
            "type": "array",
            "description": "Character color palette. Items may be simple HEX strings or enriched objects extracted by palette tool.",
            "items": {
              "oneOf": [
                { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$" },
                {
                  "type": "object",
                  "required": ["hex"],
                  "properties": {
                    "hex": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$" },
                    "rgb": { "type": "array", "items": { "type": "integer", "minimum": 0, "maximum": 255 }, "minItems": 3, "maxItems": 3 },
                    "name": { "type": "string", "description": "Semantic name (e.g., 'wardrobe_primary', 'hair', 'skin_shadow')" },
                    "percentage": { "type": "number", "description": "Percentage of palette this color occupies" }
                  }
                }
              ]
            }
          },
          "story_days_present": { "type": "array", "items": { "type": "integer" } },
          "wardrobe": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "required": ["episodes", "description"],
              "properties": {
                "episodes": { "type": "array", "items": { "type": "integer" }, "minItems": 2, "maxItems": 2, "description": "[start, end] episode range" },
                "story_days": { "type": "array", "items": { "type": "integer" }, "minItems": 2, "maxItems": 2 },
                "description": { "type": "string" },
                "reference_images": {
                  "type": "object",
                  "description": "Per-angle reference images for this wardrobe variant. Populated by batch_generate_refs.py.",
                  "properties": {
                    "front": { "type": ["string", "null"] },
                    "profile": { "type": ["string", "null"] },
                    "three_quarter": { "type": ["string", "null"] },
                    "close_up": { "type": ["string", "null"] },
                    "full_body": { "type": ["string", "null"] },
                    "back": { "type": ["string", "null"] }
                  }
                }
              }
            }
          },
          "hair_makeup": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "episodes": { "type": "array", "items": { "type": "integer" }, "minItems": 2, "maxItems": 2 },
                "story_days": { "type": "array", "items": { "type": "integer" }, "minItems": 2, "maxItems": 2 },
                "hair": { "type": "string" },
                "skin": { "type": "string" },
                "notes": { "type": "string" }
              }
            }
          },
          "state_changes": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["episode", "change"],
              "properties": {
                "episode": { "type": "integer" },
                "story_day": { "type": "integer" },
                "change": { "type": "string" }
              }
            }
          },
          "signature_props": { "type": "array", "items": { "type": "string" } },
          "hero_baseline": {
            "type": "object",
            "description": "Physical ground truth extracted from hero image via vision API. Written by reconciliation step in batch_generate_refs.py.",
            "properties": {
              "hair_length": { "type": "string", "description": "Specific length (e.g., 'cropped above ears', 'shoulder-length', 'waist-length')" },
              "hair_color": { "type": "string" },
              "hair_style": { "type": "string", "description": "Style/texture (e.g., 'tightly coiled locs', 'straight with side part')" },
              "skin_tone": { "type": "string" },
              "build": { "type": "string", "description": "Body type and posture (e.g., 'lean wiry build, slightly hunched')" },
              "distinguishing_marks": { "type": "string", "description": "Scars, tattoos, freckles, birthmarks visible in hero" },
              "baseline_wardrobe": { "type": "string", "description": "What they are wearing in the hero image" }
            }
          },
          "physical_scale": {
            "type": "object",
            "description": "Height, build, and relative proportion data for multi-character compositing.",
            "properties": {
              "height_cm": { "type": "integer", "description": "Character height in centimeters." },
              "height_description": { "type": "string", "description": "Human-readable height (e.g., '5\\'6\", small frame')." },
              "build": { "type": "string", "description": "Body type shorthand (e.g., 'lean/wiry', 'massive armored chassis')." },
              "shoulder_width": { "type": "string", "enum": ["narrow", "medium", "broad", "massive"], "description": "Relative shoulder width." },
              "relative_scale": {
                "type": "object",
                "description": "Scale relative to other characters. Keyed by character name.",
                "additionalProperties": { "type": "string" }
              },
              "scale_prompt_fragment": {
                "type": "string",
                "description": "Pre-written prose fragment for injection into multi-character storyboard prompts. E.g., 'barely reaches Kian\\'s chest plate, small-framed and wiry beside the massive chassis'."
              }
            }
          },
          "reference_status": {
            "type": "string",
            "enum": ["not_started", "in_progress", "locked"],
            "description": "Reference image generation status"
          },
          "reference_images": {
            "type": "object",
            "description": "Top-level character reference images (hero + angles). Wardrobe-specific refs live under wardrobe[variant].reference_images.",
            "properties": {
              "front": { "type": ["string", "null"] },
              "profile": { "type": ["string", "null"] },
              "three_quarter": { "type": ["string", "null"] },
              "close_up": { "type": ["string", "null"] },
              "full_body": { "type": ["string", "null"] },
              "back": { "type": ["string", "null"] },
              "hero": { "type": ["string", "null"], "description": "Path to hero reference image (identity anchor)" },
              "_flux2_slot_map": { "type": "string" }
            }
          },
          "prompts": {
            "type": "object",
            "properties": {
              "reference": {
                "description": "Rich photorealistic engine-agnostic prompt. For characters: object with hero + variants. For locations/props: string.",
                "oneOf": [
                  { "type": "string" },
                  { "type": "null" },
                  {
                    "type": "object",
                    "properties": {
                      "hero": { "type": "string", "description": "Hero prompt (60-120 words) — full visual description for identity reference." },
                      "variants": {
                        "type": "object",
                        "description": "Wardrobe/state variant prompts keyed by phase name.",
                        "additionalProperties": { "type": "string" }
                      }
                    }
                  }
                ]
              },
              "flux2": { "type": ["string", "null", "object"] }
            }
          }
        }
      }
    },
    "style_references": {
      "type": "object",
      "properties": {
        "color_grade": { "type": ["string", "null"] },
        "film_stock": { "type": ["string", "null"] },
        "look_dev_images": {
          "type": "object",
          "properties": {
            "slot_9": { "type": ["string", "null"], "description": "Lighting reference (mood)" }
          }
        },
        "notes": { "type": "string" }
      }
    },
    "locations": {
      "type": "object",
      "additionalProperties": {
        "type": "object",
        "required": ["type", "episodes", "episode_count", "description_samples", "reference_status"],
        "properties": {
          "type": { "type": "string", "enum": ["INT", "EXT", "INT/EXT"] },
          "episodes": { "type": "array", "items": { "type": "integer" } },
          "episode_count": { "type": "integer" },
          "description_samples": { "type": "array", "items": { "type": "string" } },
          "habitat_zone": { "type": "string", "description": "Habitat zone super-category (e.g., 'Lower Decks', 'The Root')" },
          "aliases": { "type": "array", "items": { "type": "string" }, "description": "Original screenplay scene headings mapped to this location" },
          "lighting_notes": { "type": "array", "items": { "type": "string" } },
          "lighting_profile": {
            "type": "object",
            "description": "Structured default lighting profile for this location.",
            "properties": {
              "primary_source": { "type": "string", "description": "Primary practical light source (e.g., 'headlamp and amber emergency strips')" },
              "direction": { "type": "string", "enum": ["ABOVE", "BELOW", "LEFT", "RIGHT", "ABOVE_LEFT", "ABOVE_RIGHT", "BELOW_LEFT", "BELOW_RIGHT", "BEHIND", "FRONT", "FROM_SUBJECT", "SELF_ILLUMINATED"] },
              "quality": { "type": "string", "enum": ["hard", "soft", "mixed"] },
              "color_temp": { "type": "string", "enum": ["warm", "cool", "amber", "blue", "neutral", "mixed"] }
            }
          },
          "color_palette": { "type": "array", "items": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$" } },
          "reference_status": {
            "type": "string",
            "enum": ["not_started", "in_progress", "locked"]
          },
          "reference_images": {
            "type": "object",
            "properties": {
              "wide_establishing": { "type": ["string", "null"] },
              "detail_texture": { "type": ["string", "null"] }
            }
          },
          "prompts": {
            "type": "object",
            "properties": {
              "reference": { "type": ["string", "null"], "description": "Rich photorealistic engine-agnostic prompt (60-120 words)." },
              "flux2": { "type": ["string", "null", "object"] }
            }
          }
        }
      }
    },
    "props": {
      "type": "object",
      "additionalProperties": {
        "type": "object",
        "required": ["display_name", "episodes", "episode_count", "confidence", "reference_status"],
        "properties": {
          "display_name": { "type": "string" },
          "owner": { "type": ["string", "null"], "description": "Character who owns/uses this prop (ALL CAPS key)" },
          "episodes": { "type": "array", "items": { "type": "integer" } },
          "episode_count": { "type": "integer" },
          "description_samples": { "type": "array", "items": { "type": "string" } },
          "states": { "type": "array", "items": { "type": "string" }, "description": "Visual states (default, active, damaged, etc.)" },
          "confidence": { "type": "string", "enum": ["high", "medium", "low"], "description": "Extraction confidence: high=signature prop, medium=capitalized, low=inferred" },
          "reference_status": {
            "type": "string",
            "enum": ["not_started", "in_progress", "locked"]
          },
          "reference_images": {
            "type": "object",
            "properties": {
              "default": { "type": ["string", "null"] },
              "active_state": { "type": ["string", "null"] }
            }
          },
          "prompts": {
            "type": "object",
            "properties": {
              "reference": { "type": ["string", "null"], "description": "Rich photorealistic engine-agnostic prompt (60-120 words)." },
              "flux2": { "type": ["string", "null", "object"] }
            }
          }
        }
      }
    },
    "sfx_elements": {
      "type": "object",
      "additionalProperties": {
        "type": "object",
        "required": ["display_name", "type", "episodes", "production_method"],
        "properties": {
          "display_name": { "type": "string" },
          "type": { "type": "string", "const": "promptable" },
          "episodes": { "type": "array", "items": { "type": "integer" } },
          "episode_count": { "type": "integer" },
          "description_samples": { "type": "array", "items": { "type": "string" } },
          "production_method": { "type": "string", "enum": ["prompt_directly", "post_composite", "hybrid"] }
        }
      }
    },
    "vfx_elements": {
      "type": "object",
      "additionalProperties": {
        "type": "object",
        "required": ["display_name", "type", "episodes", "production_method"],
        "properties": {
          "display_name": { "type": "string" },
          "type": { "type": "string", "enum": ["promptable", "post_composite"] },
          "episodes": { "type": "array", "items": { "type": "integer" } },
          "episode_count": { "type": "integer" },
          "description_samples": { "type": "array", "items": { "type": "string" } },
          "colors": { "type": "array", "items": { "type": "string" } },
          "production_method": { "type": "string", "enum": ["prompt_directly", "post_composite", "hybrid"] }
        }
      }
    },
    "specialty_shots": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["episode", "beat", "description", "technique"],
        "properties": {
          "episode": { "type": "integer" },
          "beat": { "type": "string" },
          "description": { "type": "string" },
          "technique": { "type": "string", "enum": ["sandwich_workflow", "multi_lora", "extended_clip", "cloud_gpu", "complex_motion"] },
          "estimated_duration_seconds": { "type": "number" }
        }
      }
    },
    "audio_flags": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["episode", "type", "note"],
        "properties": {
          "episode": { "type": "integer" },
          "type": { "type": "string", "enum": ["vo", "ambient", "music", "sfx"] },
          "note": { "type": "string" }
        }
      }
    },
    "shot_estimates": {
      "type": "object",
      "required": ["total_episodes", "shots_per_episode", "total_shots"],
      "properties": {
        "total_episodes": { "type": "integer" },
        "shots_per_episode": { "type": "integer", "description": "Average shots per episode (typically 18-24)" },
        "total_shots": { "type": "integer" },
        "character_shots": {
          "type": "object",
          "additionalProperties": { "type": "integer" },
          "description": "Estimated shots per character (ALL CAPS key → count)"
        },
        "specialty_shots_count": { "type": "integer" }
      }
    },
    "asset_lock_status": {
      "type": "object",
      "required": ["characters_locked", "characters_total", "locations_locked", "locations_total", "props_locked", "props_total", "vfx_locked", "vfx_total"],
      "properties": {
        "characters_locked": { "type": "integer" },
        "characters_total": { "type": "integer" },
        "locations_locked": { "type": "integer" },
        "locations_total": { "type": "integer" },
        "props_locked": { "type": "integer" },
        "props_total": { "type": "integer" },
        "vfx_locked": { "type": "integer" },
        "vfx_total": { "type": "integer" }
      }
    }
  }
}
