# Phase Z 8-preset Layout Catalog # # User lock (2026-05-07) — Step 7-A: # Layout definitions migrated from hardcoded dict (src/phase_z2_composition.py # LAYOUT_PRESETS) to this catalog. Logic unchanged — backward compat # (load_layout_presets() returns same dict shape). # # Hierarchy: # slide_base (templates/phase_z2/slide_base.html) # -> layout (this catalog) # -> zone (positions) # -> frame family partial (templates/phase_z2/families/*.html) # -> slot payload # # Per-layout fields: # render_ready: bool # - true: layout has full grid definition + verified rendering path # - false: layout defined but render path incomplete (future-proof marker) # default_selection: bool # - true: select_layout_preset() picks this as default for matching unit_count # - false: defined alternative — not picked by current single-decision logic # (consumed by Step 7-B multiple-candidate generation, Step 9 fit eval) # candidate_when: # unit_count + optional signals (orientation / layout topology hint) # used by Step 7-B / Step 9 (currently inert — single-decision logic in # select_layout_preset() ignores this field). # unit_count = layout placement unit count (Step 4 output) = # raw section_count + promoted lead_orphans 등. single: zones: 1 topology: single positions: [primary] css_areas: '"primary"' css_cols: 1fr css_rows: 1fr render_ready: true default_selection: true candidate_when: unit_count: 1 horizontal-2: zones: 2 topology: rows positions: [top, bottom] css_areas: '"top" "bottom"' css_cols: 1fr css_rows: 1fr 1fr render_ready: true default_selection: true candidate_when: unit_count: 2 orientation: horizontal vertical-2: zones: 2 topology: cols positions: [left, right] css_areas: '"left right"' css_cols: 1fr 1fr css_rows: 1fr render_ready: true default_selection: false candidate_when: unit_count: 2 orientation: vertical top-1-bottom-2: zones: 3 topology: T positions: [top, bottom-left, bottom-right] css_areas: '"top top" "bottom-left bottom-right"' css_cols: 1fr 1fr css_rows: 1fr 1fr render_ready: true default_selection: true candidate_when: unit_count: 3 layout: T top-2-bottom-1: zones: 3 topology: inverted-T positions: [top-left, top-right, bottom] css_areas: '"top-left top-right" "bottom bottom"' css_cols: 1fr 1fr css_rows: 1fr 1fr render_ready: true default_selection: false candidate_when: unit_count: 3 layout: inverted-T left-1-right-2: zones: 3 topology: side-T-left positions: [left, right-top, right-bottom] css_areas: '"left right-top" "left right-bottom"' css_cols: 1fr 1fr css_rows: 1fr 1fr render_ready: true default_selection: false candidate_when: unit_count: 3 layout: side-T-left left-2-right-1: zones: 3 topology: side-T-right positions: [left-top, right, left-bottom] css_areas: '"left-top right" "left-bottom right"' css_cols: 1fr 1fr css_rows: 1fr 1fr render_ready: true default_selection: false candidate_when: unit_count: 3 layout: side-T-right grid-2x2: zones: 4 topology: 2x2 positions: [top-left, top-right, bottom-left, bottom-right] css_areas: '"top-left top-right" "bottom-left bottom-right"' css_cols: 1fr 1fr css_rows: 1fr 1fr render_ready: true default_selection: true candidate_when: unit_count: 4