{
  "type": "object",
  "properties": {
    "shots": {
      "type": "array",
      "items": {
        "type": "object",
          "properties": {
            "scene_index": {"type": "integer"},
            "shot_index": {"type": "integer"},
            "framing_scale": {
              "type": "string",
              "enum": ["close", "medium", "wide", "insert"],
              "description": "Primary framing scale of this shot. close = primary subject fills most of the frame. medium = upper body or mid-distance subject with some surrounding context. wide = full body or environment/layout visible. insert = isolated detail without surrounding context. Judge by camera distance and primary subject visibility, not vocabulary."
            },
            "perspective": {"type": "string", "description": "Camera POV: subjective_pov, over_shoulder, observer, omniscient, object_pov, voyeur"},
          "pov_character": {"type": "string", "description": "For subjective POV: the character whose eyes we see through (must NOT appear in image). Empty string if not subjective."},
          "perception_mode": {"type": "string", "description": "How the scene is perceived: direct (normal vision), hallucination, dream, memory, reflection, through_device (CCTV/phone/binoculars), projection. Affects visual treatment."},
          "camera_direction": {"type": "string", "description": "Creative camera placement, framing, physical layout, and composition including POV (2-3 sentences, English)"},
          "lighting_mood": {"type": "string", "description": "Lighting technique and color mood (1 sentence, English)"},
          "character_angles": {
            "type": "array",
            "description": "Camera-relative angle and body pose for each visible character (exclude POV character)",
            "items": {
              "type": "object",
              "properties": {
                "character": {"type": "string", "description": "Character name (as registered)"},
                "angle": {"type": "string", "description": "Camera-relative direction: facing_camera, back_to_camera, profile_left, profile_right, three_quarter_left, three_quarter_right, over_shoulder, looking_away"},
                "body_pose": {"type": "string", "description": "Specific body posture in English (2-5 words). Must NOT use 'standing' alone — use action-grounded or state-grounded poses such as 'one foot on pedal', 'leaning against doorframe', 'crouching behind desk', 'slumping into chair', 'mid-stride paused'. See system prompt 'body_pose 다양화' for full guidance."},
                "gaze_target": {"type": "string", "description": "Where the character's eyes are looking: another character's name, an object name, 'camera', 'down', 'up', 'distant', 'closed', 'unconscious', 'dead', 'severely_injured'"}
              },
              "required": ["character", "angle", "body_pose", "gaze_target"],
              "additionalProperties": false
            }
          },
          "key_bg_elements": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "element": {"type": "string", "description": "Background element name"},
                "state": {"type": "string", "description": "State in this shot (open/closed, clean/dirty, on/off, etc.)"},
                "orientation": {"type": "string", "description": "Natural-language description tied to directionality_class. For content_surface: which face/side is visible to camera and what is on it. For reflective_surface: what is being reflected and how. For transparent_surface: surface state and what is seen through it. For directional_3d: which side faces camera. For non_directional: empty allowed. content_surface and reflective_surface MUST have a non-empty orientation."},
                "camera_use": {"type": "string", "description": "How this element is used in the shot (foreground frame, focal point, light source, etc.)"},
                "directionality_class": {
                  "type": "string",
                  "enum": ["content_surface", "reflective_surface", "transparent_surface", "directional_3d", "non_directional"],
                  "description": "Semantic classification of this element. content_surface = thin object that carries content on one face; which face is visible changes the meaning. reflective_surface = object whose surface reflects content; what is reflected determines meaning. transparent_surface = object the camera sees through; surface state and what lies beyond determine meaning. directional_3d = 3D object with multiple faces where the camera-facing side matters. non_directional = texture/surface/ambient element with no directional meaning. Judge by meaning, not by surface vocabulary or specific examples (non-exhaustive examples, do not classify by this list)."
                }
              },
              "required": ["element", "state", "orientation", "camera_use", "directionality_class"],
              "additionalProperties": false
            }
          },
          "frame_spatial_contract": {
            "oneOf": [
              {"type": "null"},
              {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "enum": ["movement_direction", "points_to_anchor", "looks_to_anchor",
                             "shared_space_relation", "required_background_position",
                             "primary_subject_isolation"],
                    "description": "Single primary trigger — see system prompt 'Frame Spatial Contract' section."
                  },
                  "constraints": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 3,
                    "items": {
                      "type": "object",
                      "properties": {
                        "target_kind": {"type": "string", "enum": ["character", "prop", "background"]},
                        "target_id": {"type": "string", "description": "character → C##, prop → P##, background → empty string"},
                        "label": {"type": "string", "minLength": 1, "description": "Human-readable label"},
                        "screen_zone": {"type": "string", "enum": [
                          "upper_left", "upper_center", "upper_right",
                          "middle_left", "middle_center", "middle_right",
                          "lower_left", "lower_center", "lower_right"]},
                        "depth_plane": {"type": "string", "enum": ["foreground", "midground", "background"]},
                        "gesture_action": {"type": "string", "enum": [
                          "none", "points_to", "reaches_for", "looks_toward", "moves_toward"]},
                        "gesture_target_label": {"type": "string",
                          "description": "non-empty when gesture_action != 'none', empty string when gesture_action == 'none'"}
                      },
                      "required": ["target_kind", "target_id", "label", "screen_zone",
                                   "depth_plane", "gesture_action", "gesture_target_label"],
                      "additionalProperties": false
                    }
                  }
                },
                "required": ["reason", "constraints"],
                "additionalProperties": false
              }
            ],
            "description": "Opt-in frame-space contract. null when not needed. See system prompt 'Frame Spatial Contract' section for emit gating."
          },
          "subject_reference_policy": {
            "type": "array",
            "description": "Per-subject identity reference policy (exceptions-first). Default 'id_and_outlook_required' if subject omitted. Empty array allowed.",
            "items": {
              "type": "object",
              "properties": {
                "subject_id": {
                  "type": "string",
                  "pattern": "^C\\d{2,3}(?:O\\d{2,3})?$",
                  "description": "Base C## recommended. C##O## form is accepted and normalized to base (outlook context not available in shot_staging input)."
                },
                "policy_type": {
                  "type": "string",
                  "enum": ["identity_reference"],
                  "description": "v1 single value. Other values forbidden (envelope future hook only)."
                },
                "policy": {
                  "type": "string",
                  "enum": [
                    "id_and_outlook_required",
                    "base_id_required",
                    "generic_descriptor_allowed"
                  ]
                },
                "reason": {
                  "type": "string",
                  "minLength": 1
                }
              },
              "required": ["subject_id", "policy_type", "policy", "reason"],
              "additionalProperties": false
            }
          }
        },
        "required": ["scene_index", "shot_index", "framing_scale", "perspective", "pov_character", "perception_mode", "camera_direction", "lighting_mood", "character_angles", "key_bg_elements", "frame_spatial_contract", "subject_reference_policy"],
        "additionalProperties": false
      }
    }
  },
  "required": ["shots"],
  "additionalProperties": false
}
