{
  "$schema_version": "1.0",
  "$canonical_source": "design-system/Page Patterns.html",
  "$notes": [
    "Machine-readable inventory of canonical page layouts. Each pattern lists its ordered regions,",
    "which regions are required, and the real component classes / React exports that fill them.",
    "The pp-* classes in Page Patterns.html are showcase scaffolding only — never ship them.",
    "Component contracts live in design-system/components.json; tokens in design-system/tokens.json.",
    "Template composition (components, class hooks, behaviours, what to replace) lives in design-system/templates.json.",
    "Usage rules (severity, detection, fix) live in design-system/adherence.rules.yaml.",
    "This file picks the page layout. To pick the controls that fill it, use selection_cheat_sheet in components.json.",
    "To pick a starting file rather than a layout, read start_here in design-system/templates.json — the recommended default is templates/app-shell."
  ],
  "shell": {
    "note": "Every product page sits in the app shell. The header pins to the top; nav and content share the row below.",
    "class_root": ".app-shell",
    "regions": [
      {
        "id": "header",
        "class": ".app-shell-header",
        "component": "TopHeader",
        "required": true
      },
      {
        "id": "nav",
        "class": ".app-shell-nav",
        "component": "Sidenav",
        "required": true,
        "note": "The header hamburger drives Sidenav's `collapsed` prop."
      },
      {
        "id": "main",
        "class": ".app-shell-main > main",
        "required": true,
        "note": "Scrolls independently. Padding 28px 24px 40px."
      }
    ],
    "template": "templates/app-shell"
  },
  "universal_rules": [
    {
      "id": "page_header",
      "rule": "Every page opens with an H1 page title and a result/context line beneath it. The page-level primary action is right-aligned on that row. On a detail page a Breadcrumb sits ABOVE the H1 at --fs-ui-small — never at heading scale, and never instead of the H1 (the retired .pp-crumb scaffolding did both)."
    },
    {
      "id": "action_placement",
      "rule": "Primary action top-right of the page header. Destructive actions bottom-left of a form footer, cancel + save bottom-right. Never put an action in a ButtonGroup."
    },
    {
      "id": "filters",
      "rule": "Filter controls sit in a bordered filter bar above the table card: search first, then field filters, then the status ButtonGroup, then a right-aligned reset IconButton."
    },
    {
      "id": "table_required_toolbar",
      "rule": "A list page's table is always .table-card + .table-toolbar. Never ship a bare <table>."
    },
    {
      "id": "loading_and_states",
      "rule": "Use Skeleton in the shape of the eventual content while loading, EmptyState when a query returns nothing, ErrorState when a fetch fails. Never leave a blank card."
    },
    {
      "id": "density",
      "rule": "Default list density is 25 rows; the toolbar density control toggles compact (.is-compact)."
    },
    {
      "id": "theme",
      "rule": "Dark is the default. Every page ships a working ThemeSwitch; light is opt-in via data-theme=\"light\"."
    }
  ],
  "patterns": [
    {
      "id": "list_page",
      "name": "List page",
      "purpose": "Browse, filter, and act on a collection of objects.",
      "when_to_use": "The default for any collection: ad products, automations, scripts, deals.",
      "anchor": "Page Patterns.html#patterns",
      "template": "templates/data-table-page",
      "regions": [
        {
          "id": "page_header",
          "required": true,
          "contains": [
            "h1 page title",
            "result count line",
            "primary action (Button)"
          ]
        },
        {
          "id": "filter_bar",
          "required": true,
          "contains": [
            "TextField (search)",
            "SelectField filters",
            "ButtonGroup (status)",
            "IconButton (reset)"
          ]
        },
        {
          "id": "selection_bar",
          "required": false,
          "note": "Appears only while rows are selected; holds bulk actions and a clear action."
        },
        {
          "id": "table_card",
          "required": true,
          "class": ".table-card.table-card-outline",
          "contains": [
            ".table-toolbar",
            ".table",
            ".table-pagination"
          ]
        },
        {
          "id": "empty_or_error",
          "required": true,
          "contains": [
            "EmptyState",
            "ErrorState"
          ],
          "note": "Rendered inside the table card in place of rows."
        }
      ],
      "toolbar": {
        "note": "Sort, Columns, Filters, Density, Reset — see components.json Table.toolbar_menus for each popover's anatomy."
      },
      "defaults": {
        "rows_per_page": 25,
        "page_size_options": [
          8,
          25,
          50
        ],
        "density": "default",
        "sort": "most recently updated",
        "card_variant": "outline"
      }
    },
    {
      "id": "list_page_tabbed",
      "name": "List page · tabbed",
      "purpose": "One collection split into named subsets that share the same chrome.",
      "when_to_use": "When rows fall into a few mutually exclusive buckets that each deserve their own table (e.g. Active / Archived).",
      "anchor": "Page Patterns.html#patterns",
      "extends": "list_page",
      "regions": [
        {
          "id": "page_header",
          "required": true
        },
        {
          "id": "tabs",
          "required": true,
          "component": "Tabs",
          "class": ".tabs-bar",
          "note": "Sits at the top of the body card; switches the table beneath it."
        },
        {
          "id": "filter_bar",
          "required": true,
          "note": "Search spans the row beneath the tabs."
        },
        {
          "id": "table_card",
          "required": true
        }
      ],
      "template": "templates/list-page-tabbed"
    },
    {
      "id": "detail_two_column_form",
      "name": "Detail page · two-column form",
      "purpose": "Read and edit one object's attributes.",
      "when_to_use": "Any object detail or create/edit flow with more than a handful of fields.",
      "anchor": "Page Patterns.html#patterns",
      "template": "templates/two-column-form",
      "regions": [
        {
          "id": "breadcrumb",
          "required": true,
          "components": [
            "Breadcrumb"
          ],
          "classes": [
            ".breadcrumb"
          ],
          "note": "Sits above the page title, never as it. On an object page the last segment is the copyable ID chip and the object name is the <h1> beneath."
        },
        {
          "id": "tabs",
          "required": false,
          "component": "Tabs"
        },
        {
          "id": "body",
          "required": true,
          "layout": "two columns of section cards",
          "contains": [
            "Card (per section)",
            "TextField",
            "SelectField",
            "TagInput",
            "Switch"
          ]
        },
        {
          "id": "footer",
          "required": true,
          "contains": [
            "destructive action bottom-left",
            "Cancel + Save bottom-right (Button, SplitButton)"
          ]
        }
      ],
      "rules": [
        "Required fields carry the red marker; help text sits under the field, not in a tooltip.",
        "Group fields into named section Cards — never one flat column of inputs."
      ]
    },
    {
      "id": "detail_split_panel",
      "name": "Automation detail · split panel",
      "purpose": "Monitor a running object: history on the left, live controls on the right.",
      "when_to_use": "Objects with executions or runs (automations, scripts, agent jobs).",
      "anchor": "Page Patterns.html#patterns",
      "regions": [
        {
          "id": "chart",
          "required": false,
          "note": "Collapsible chart at the top. Follows design-system/charts.rules.yaml."
        },
        {
          "id": "history_table",
          "required": true,
          "class": ".table-card.table-card-outline",
          "note": "Left column: executions."
        },
        {
          "id": "run_panel",
          "required": true,
          "note": "Sticky right column: metric cards plus an actions table."
        }
      ]
    },
    {
      "id": "activity_page",
      "name": "Activity page",
      "purpose": "An audit stream of what users and agents did, each row expandable into the record it wrote.",
      "template": "templates/activity-page",
      "regions": [
        {
          "id": "page_header",
          "required": true,
          "components": [
            "Button"
          ],
          "classes": [],
          "note": "Title, event count, and an Export action."
        },
        {
          "id": "stream_toolbar",
          "required": false,
          "components": [
            "TextField",
            "Button"
          ],
          "classes": [],
          "note": "Search and expand/collapse all. Narrow — the facets and clear-filters live in the rail."
        },
        {
          "id": "stream",
          "required": true,
          "components": [
            "ActivityPanel"
          ],
          "classes": [
            ".activity",
            ".act-day",
            ".act-event"
          ],
          "note": "Day-grouped events, newest first. Days are derived from what survived the filters."
        },
        {
          "id": "filter_rail",
          "required": true,
          "components": [
            "IconButton",
            "DatePicker",
            "SelectField",
            "Checkbox"
          ],
          "classes": [
            ".filter-rail",
            ".fr-head",
            ".fr-group",
            ".fr-row",
            ".fr-only",
            ".range-field",
            ".range-trigger"
          ],
          "note": "280px column: a header with the title and a clear-filters IconButton, a date-range trigger with an inset label, a user multi-select, then grouped checkbox facets for activity type and entity."
        }
      ],
      "layout": "grid, minmax(0,1fr) / 280px, 20px gap, align-items start"
    },
    {
      "id": "agent_workspace",
      "name": "Agent workspace",
      "purpose": "Converse with an agent while the artifacts it produces stay open beside the transcript.",
      "template": "templates/beacon-workspace",
      "regions": [
        {
          "id": "banner",
          "required": false,
          "components": [],
          "classes": [
            ".as-banner"
          ],
          "note": "Advisory strip spanning both panes — alpha status, review warnings."
        },
        {
          "id": "chat",
          "required": true,
          "components": [
            "ThemeSwitch",
            "IconButton"
          ],
          "classes": [
            ".as-chat",
            ".as-stream",
            ".as-turn",
            ".as-bubble",
            ".as-step",
            ".as-done",
            ".as-prose",
            ".as-feedback",
            ".as-working",
            ".as-composer",
            ".as-chat-foot"
          ],
          "note": "Transcript, composer pinned below it, session controls and a context meter in the foot. A user turn is a bubble; an agent turn is not."
        },
        {
          "id": "panel",
          "required": true,
          "components": [
            "TextField",
            "ButtonGroup",
            "Switch",
            "IconButton",
            "KpiCard",
            "Chart"
          ],
          "classes": [
            ".as-panel",
            ".as-tabs",
            ".as-tab",
            ".as-panel-body",
            ".as-panel-title",
            ".as-panel-controls"
          ],
          "note": "Tabbed artifact panes. A dot marks a pane holding output the user has not seen; it clears on visit."
        }
      ],
      "layout": "grid, minmax(360px,1fr) / minmax(0,1.35fr), full height; the banner spans both columns",
      "panes": {
        "canvas": "Pinned result sets, each a card the user can bookmark, expand, or dismiss — a workbench rather than a feed.",
        "focus": "A working set the agent surfaced, as object cards: ID badge carrying the type, name, cadence, state switch.",
        "sandbox": "A full artifact the agent built, shown as it will ship, with its source one click away."
      }
    }
  ],
  "selection_cheat_sheet": [
    {
      "need": "Browse a collection",
      "use": "list_page"
    },
    {
      "need": "Browse a collection split into buckets",
      "use": "list_page_tabbed"
    },
    {
      "need": "Read or edit one object's fields",
      "use": "detail_two_column_form"
    },
    {
      "need": "Monitor runs or executions",
      "use": "detail_split_panel"
    },
    {
      "need": "Lifecycle objects needing badges and summaries",
      "use": "beacon_object_list"
    },
    {
      "need": "Deep single-object workspace",
      "use": "beacon_focus_object"
    }
  ]
}
