{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "shell-app",
  "title": "ShellApp",
  "description": "The standard managed workspace root for authenticated applications, assembling responsive navigation, page chrome, global tools, and stable application-owned content from typed data.",
  "type": "registry:ui",
  "meta": {
    "library": "@ds-mo/ui",
    "distribution": "npm",
    "storybook": "https://zainadeel.github.io/compomo/?path=/story/shell-app",
    "source": "src/wc/components/ShellApp/ShellApp.tsx",
    "intentStatus": "complete",
    "intent": {
      "audience": "specialized",
      "status": "stable",
      "summary": "The standard managed workspace root for authenticated applications, assembling responsive navigation, page chrome, global tools, and stable application-owned content from typed data.",
      "useWhen": [
        "Building any standard authenticated product or workspace page with CompoMo.",
        "Navigation, page chrome, global tools, responsive behavior, and shell shortcuts must be composed as one application frame without parallel desktop and mobile owners.",
        "Future application templates need the supported base-page composition boundary."
      ],
      "avoidWhen": [
        "Building authentication, onboarding, public, marketing, print, error, or intentionally focused full-screen experiences that require a different layout.",
        "Another ShellApp already exists in the page; applications must render exactly one and must never nest ShellApps.",
        "Building content inside an existing ShellApp; render the page content rather than another shell."
      ],
      "commonlyComposedWith": [
        "component:ds-panel-nav",
        "component:ds-bar-nav",
        "component:ds-panel-tools",
        "component:ds-shell-tools",
        "component:ds-mobile-sheet-nav",
        "component:ds-mobile-header",
        "component:ds-mobile-bar-nav",
        "component:ds-shell-page",
        "component:ds-banner",
        "component:ds-swatch-picker",
        "component:ds-paper-texture"
      ],
      "patterns": [
        "pattern:application-shell",
        "pattern:shell-page"
      ],
      "accessibility": [
        "Supply primary destinations through navigation and secondary route sections through pageChrome so managed ShellApp preserves their correct semantics.",
        "Ensure the default content region contains the page's single main landmark.",
        "Keep the shell-owned routed-content scroller keyboard-focusable while its page landmark retains content semantics. Do not paint a control focus ring around that scroller; it is a layout region, and a page-column outline looks like a selected card.",
        "Keep the managed desktop/tablet and mobile presentations available without conditionally recreating routed or tool content.",
        "Do not override browser keyboard chords when enabling shell shortcuts.",
        "Require Search and Help as the minimum PanelTools actions and provide accessible labels for every tool.",
        "Preserve focus and accessible names across navigation, collapse, overflow, and responsive presentation changes."
      ],
      "states": [
        "Render exactly one ShellApp as the workspace root and never nest it.",
        "Managed composition is the standard. Supply navigation, pageChrome, and tools objects plus named tool-view and default page-content slots.",
        "The application router owns the current route, authorization filtering, Dashboard or Settings context, destination collections, section collections, and localized content labels.",
        "ShellApp renders the lower-level desktop, tablet, and mobile chrome, coordinates layout, gradient alignment, transitions, shortcuts, global surfaces, Inbox state, focus restoration, and mobile-stage visibility.",
        "The application router owns authorization, current URLs, history, and route execution; ShellApp forwards navigation and page intent events without routing.",
        "The application supplies each routed content owner and tool view once. ShellApp changes visibility, inert state, and presentation without recreating those nodes.",
        "The optional banner slot accepts one application-owned ds-banner above the complete shell row. The application owns its open state, orientation for the current shell capacity, action consequences, dismissal persistence, and resolution when several messages compete.",
        "Use composition=slotted only for advanced application architectures that intentionally own all lower-level chrome wiring.",
        "Set gradientPreset to the single resolved global chrome preference: neutral by default, none for solid chrome, or cool, neutral, warm, and fresh for token-based washes.",
        "The application owns gradient preference persistence, cross-tab synchronization, and server or account precedence; ShellApp owns rendering the resolved preset across authenticated routes.",
        "The optional paperTexture property adds a decorative Paper Design texture layer above the gradient or solid chrome frame; its color values are literal shader inputs rather than design tokens.",
        "Dashboard represents day-to-day operational work while Settings represents administrative and configuration work; route state selects the context.",
        "Replace navigation and pageChrome together from one completed route state.",
        "When pageChrome.showBack marks a detail depth, mobile chrome keeps the page heading as its identity and presents only local subsections; peer route tabs remain in desktop BarNav rather than replacing the detail heading.",
        "Managed ShellApp keeps BarNav and ShellPage presentations present and owns page header stickiness and the content scroller.",
        "Route-owned pageChrome independently selects ShellPage contentInset, roomy and compact top seams, scroll compaction, expanded and compact header-divider visibility, and contentSurface; ShellApp forwards them without making page-specific layout or surface decisions.",
        "The application filters unauthorized destinations, sections, and tools before passing them into shell components."
      ],
      "responsiveBehavior": [
        "ShellApp owns the fixed system breakpoints and resolves desktop at 1200px and wider, tablet from 768px through 1199px, and mobile below 768px.",
        "On desktop, PanelNav defaults expanded, permits user collapse or expansion, and respects the locally persisted desktop preference.",
        "On tablet, ShellApp keeps PanelNav permanently collapsed and prevents user expansion without overwriting the desktop preference.",
        "On mobile, ShellApp hides and inerts desktop chrome, uses a solid primary stage, and composes MobileHeader, MobileSheetNav, ShellTools, and the persistent MobileBarNav around the same application-owned content.",
        "Opening MobileSheetNav temporarily overlays the active routed page or global tool; closing it without selecting an area restores that same destination and its nested state. The sheet exposes the router-derived current area only while the routed area owns the mobile stage, so a global tool never leaves an underlying route highlighted.",
        "Selecting Help from the managed mobile sheet activates the configured Help tool and promotes Help to the primary mobile destination instead of forwarding it as route navigation.",
        "Set navigation.showMobileAccount to false when the Settings destination collection already includes account and user preferences.",
        "When a suspended mobile document returns to the foreground, ShellApp refreshes the complete stage paint layer without remounting routed content or tool owners.",
        "When the application enables viewport-fit=cover, ShellApp reserves the shared top safe-area inset once for the complete mobile stage; nested routed, sheet, and tool headers must not add a second top inset.",
        "A slotted Banner remains below the top safe-area inset and above routed, navigation, fullscreen-tool, and mobile global-surface presentations.",
        "Mobile global surfaces conceal and inert the routed workspace without unmounting it, preserving nested routes and scroll state.",
        "Keep PanelNav width, BarNav height, PanelTools chrome, and main-content origin stable except during intentional responsive or collapse transitions.",
        "Paper texture chrome is desktop/tablet-only and follows the fixed viewport phase of the shell chrome; mobile remains a solid primary stage.",
        "Managed page-header capacity follows only the fixed shell breakpoint: roomy on desktop and compact on tablet. Opening PanelTools does not compact or constrain BarTitle.",
        "The bottom bar remains visible at all route and tool depths in the foundation release."
      ],
      "frameworkCaveats": {
        "customElements": [
          "Assign navigation, pageChrome, and tools as JavaScript properties on the managed ShellApp.",
          "Assign paperTexture as a JavaScript property when enabling the optional decorative layer.",
          "Stamp route-derived nav-style before components connect when deterministic first paint is required."
        ],
        "react": [
          "Use the generated ShellApp wrapper, structured object props, named tool-view slots, and stable routed children.",
          "Pass paperTexture as a structured property when the shell needs the optional texture layer.",
          "Resolve all shell inputs from one router state before rendering the composition."
        ],
        "angular": [
          "Use the generated standalone ShellApp adapter, bind structured object props, and preserve named tool-view slots.",
          "Bind paperTexture as a property so nested shader parameters are preserved.",
          "Bind complex inputs as properties and update navigation inputs atomically from one resolved router state.",
          "Bind both nav-style attributes and properties when deterministic first paint is required."
        ]
      },
      "references": [
        {
          "label": "Component guide",
          "path": "src/wc/components/ShellApp/ShellApp.mdx"
        },
        {
          "label": "Executable Storybook composition",
          "path": "src/wc/components/ShellApp/ShellApp.stories.ts"
        },
        {
          "label": "Framework integration",
          "path": "docs/framework-integration.md"
        },
        {
          "label": "Application shell contract",
          "path": "agent/patterns/application-shell/pattern.agent.json"
        }
      ]
    },
    "api": {
      "props": {
        "composition": {
          "type": "ShellAppComposition",
          "resolvedType": "\"managed\" | \"slotted\"",
          "attribute": "composition",
          "default": "'managed'",
          "required": false,
          "mutable": false,
          "description": "Managed renders the complete responsive chrome; slotted exposes the advanced composition."
        },
        "gradientPreset": {
          "type": "ShellGradientPreset",
          "resolvedType": "\"cool\" | \"fresh\" | \"neutral\" | \"none\" | \"warm\"",
          "attribute": "gradient-preset",
          "default": "'neutral'",
          "required": false,
          "mutable": false,
          "description": "Shell chrome wash preset. `none` renders solid chrome; the remaining\npresets use token-based washes that adapt to the active color theme."
        },
        "mobileDestination": {
          "type": "MobileDestination",
          "resolvedType": "\"agents\" | \"area\" | \"help\" | \"inbox\" | \"messages\" | \"search\"",
          "attribute": "mobile-destination",
          "default": "'area'",
          "required": false,
          "mutable": false,
          "description": "Controlled mobile surface shown above the persistent bottom bar."
        },
        "mobileSheetNavOpen": {
          "type": "boolean",
          "resolvedType": "boolean",
          "attribute": "mobile-sheet-nav-open",
          "default": "false",
          "required": false,
          "mutable": false,
          "description": "Controlled full-stage Mobile Sheet Nav state."
        },
        "navStyle": {
          "type": "NavChromeStyle",
          "resolvedType": "\"dashboard\" | \"settings\"",
          "attribute": "nav-style",
          "default": "'dashboard'",
          "required": false,
          "mutable": false,
          "description": "Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`."
        },
        "navigation": {
          "type": "ShellNavigationConfig",
          "resolvedType": "ShellNavigationConfig",
          "default": "{}",
          "required": false,
          "mutable": false,
          "description": "Router-owned navigation data used by managed composition."
        },
        "pageChrome": {
          "type": "ShellPageChromeConfig",
          "resolvedType": "ShellPageChromeConfig",
          "default": "{}",
          "required": false,
          "mutable": false,
          "description": "Route-owned page title and section data used by managed composition."
        },
        "paperTexture": {
          "type": "PaperTextureConfig",
          "resolvedType": "PaperTextureConfig | undefined",
          "required": false,
          "mutable": false,
          "description": "Optional decorative Paper Design texture layered above the shell wash."
        },
        "shortcutsEnabled": {
          "type": "boolean",
          "resolvedType": "boolean",
          "attribute": "shortcuts-enabled",
          "default": "true",
          "required": false,
          "mutable": false,
          "description": "When `true` (default), registers global shell keyboard shortcuts. `[` toggles panel nav; `]` closes tools; K, A, S, M, N, and / toggle tool drawers. Modifiers are ignored so browser chords like ⌘N stay native."
        },
        "tools": {
          "type": "ShellToolsConfig",
          "resolvedType": "ShellToolsConfig",
          "default": "{}",
          "required": false,
          "mutable": false,
          "description": "Product-owned global tool definitions used by managed composition."
        }
      },
      "events": [
        {
          "name": "dsBreadcrumbSelect",
          "detail": "BreadcrumbSelectDetail",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed breadcrumb intent from BarTitle."
        },
        {
          "name": "dsBrowseContextChange",
          "detail": "NavChromeStyle",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed mobile Dashboard/Settings browsing intent."
        },
        {
          "name": "dsHeaderAction",
          "detail": "{ tool: PanelToolsToolId; id: string }",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed tool-header action intent."
        },
        {
          "name": "dsHeaderBack",
          "detail": "{ tool: PanelToolsToolId }",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed tool-header Back intent."
        },
        {
          "name": "dsNavFooterAction",
          "detail": "void",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed account-footer action intent."
        },
        {
          "name": "dsNavSelect",
          "detail": "string",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed primary-navigation intent; the application router performs navigation."
        },
        {
          "name": "dsNavUserAction",
          "detail": "PanelNavUserActionDetail",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed user/account trigger intent."
        },
        {
          "name": "dsPageAction",
          "detail": "string",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed page action intent from BarTitle."
        },
        {
          "name": "dsPageBack",
          "detail": "MouseEvent",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed page-level Back intent."
        },
        {
          "name": "dsPresentationChange",
          "detail": "{ presentation: 'drawer' | 'fullscreen' }",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed drawer/fullscreen presentation state."
        },
        {
          "name": "dsResponsiveModeChange",
          "detail": "{ mode: ShellResponsiveMode }",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Emitted after crossing the fixed 768px or 1200px shell boundaries."
        },
        {
          "name": "dsSubsectionChange",
          "detail": "string",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed page-subsection intent from BarTitle or MobileHeader."
        },
        {
          "name": "dsTabChange",
          "detail": "string",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed desktop BarNav or mobile section-selection intent."
        },
        {
          "name": "dsToolChange",
          "detail": "{ id: PanelToolsToolId; selected: boolean }",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Managed tools selection state."
        }
      ],
      "methods": [
        {
          "name": "activateTool",
          "signature": "activateTool(id: PanelToolsToolId) => Promise<void>",
          "description": "Open or toggle a managed global tool from application-owned UI."
        },
        {
          "name": "closeGlobalSurface",
          "signature": "closeGlobalSurface() => Promise<void>",
          "description": "Close whichever managed global surface is currently visible."
        },
        {
          "name": "setToolPresentation",
          "signature": "setToolPresentation(presentation: \"drawer\" | \"fullscreen\") => Promise<void>",
          "description": "Request drawer or fullscreen presentation for the active managed tool."
        }
      ],
      "slots": []
    },
    "props": {
      "composition": {
        "type": "ShellAppComposition",
        "resolvedType": "\"managed\" | \"slotted\"",
        "attribute": "composition",
        "default": "'managed'",
        "required": false,
        "mutable": false,
        "description": "Managed renders the complete responsive chrome; slotted exposes the advanced composition."
      },
      "gradientPreset": {
        "type": "ShellGradientPreset",
        "resolvedType": "\"cool\" | \"fresh\" | \"neutral\" | \"none\" | \"warm\"",
        "attribute": "gradient-preset",
        "default": "'neutral'",
        "required": false,
        "mutable": false,
        "description": "Shell chrome wash preset. `none` renders solid chrome; the remaining\npresets use token-based washes that adapt to the active color theme."
      },
      "mobileDestination": {
        "type": "MobileDestination",
        "resolvedType": "\"agents\" | \"area\" | \"help\" | \"inbox\" | \"messages\" | \"search\"",
        "attribute": "mobile-destination",
        "default": "'area'",
        "required": false,
        "mutable": false,
        "description": "Controlled mobile surface shown above the persistent bottom bar."
      },
      "mobileSheetNavOpen": {
        "type": "boolean",
        "resolvedType": "boolean",
        "attribute": "mobile-sheet-nav-open",
        "default": "false",
        "required": false,
        "mutable": false,
        "description": "Controlled full-stage Mobile Sheet Nav state."
      },
      "navStyle": {
        "type": "NavChromeStyle",
        "resolvedType": "\"dashboard\" | \"settings\"",
        "attribute": "nav-style",
        "default": "'dashboard'",
        "required": false,
        "mutable": false,
        "description": "Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`."
      },
      "navigation": {
        "type": "ShellNavigationConfig",
        "resolvedType": "ShellNavigationConfig",
        "default": "{}",
        "required": false,
        "mutable": false,
        "description": "Router-owned navigation data used by managed composition."
      },
      "pageChrome": {
        "type": "ShellPageChromeConfig",
        "resolvedType": "ShellPageChromeConfig",
        "default": "{}",
        "required": false,
        "mutable": false,
        "description": "Route-owned page title and section data used by managed composition."
      },
      "paperTexture": {
        "type": "PaperTextureConfig",
        "resolvedType": "PaperTextureConfig | undefined",
        "required": false,
        "mutable": false,
        "description": "Optional decorative Paper Design texture layered above the shell wash."
      },
      "shortcutsEnabled": {
        "type": "boolean",
        "resolvedType": "boolean",
        "attribute": "shortcuts-enabled",
        "default": "true",
        "required": false,
        "mutable": false,
        "description": "When `true` (default), registers global shell keyboard shortcuts. `[` toggles panel nav; `]` closes tools; K, A, S, M, N, and / toggle tool drawers. Modifiers are ignored so browser chords like ⌘N stay native."
      },
      "tools": {
        "type": "ShellToolsConfig",
        "resolvedType": "ShellToolsConfig",
        "default": "{}",
        "required": false,
        "mutable": false,
        "description": "Product-owned global tool definitions used by managed composition."
      }
    },
    "events": [
      {
        "name": "dsBreadcrumbSelect",
        "detail": "BreadcrumbSelectDetail",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed breadcrumb intent from BarTitle."
      },
      {
        "name": "dsBrowseContextChange",
        "detail": "NavChromeStyle",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed mobile Dashboard/Settings browsing intent."
      },
      {
        "name": "dsHeaderAction",
        "detail": "{ tool: PanelToolsToolId; id: string }",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed tool-header action intent."
      },
      {
        "name": "dsHeaderBack",
        "detail": "{ tool: PanelToolsToolId }",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed tool-header Back intent."
      },
      {
        "name": "dsNavFooterAction",
        "detail": "void",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed account-footer action intent."
      },
      {
        "name": "dsNavSelect",
        "detail": "string",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed primary-navigation intent; the application router performs navigation."
      },
      {
        "name": "dsNavUserAction",
        "detail": "PanelNavUserActionDetail",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed user/account trigger intent."
      },
      {
        "name": "dsPageAction",
        "detail": "string",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed page action intent from BarTitle."
      },
      {
        "name": "dsPageBack",
        "detail": "MouseEvent",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed page-level Back intent."
      },
      {
        "name": "dsPresentationChange",
        "detail": "{ presentation: 'drawer' | 'fullscreen' }",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed drawer/fullscreen presentation state."
      },
      {
        "name": "dsResponsiveModeChange",
        "detail": "{ mode: ShellResponsiveMode }",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Emitted after crossing the fixed 768px or 1200px shell boundaries."
      },
      {
        "name": "dsSubsectionChange",
        "detail": "string",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed page-subsection intent from BarTitle or MobileHeader."
      },
      {
        "name": "dsTabChange",
        "detail": "string",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed desktop BarNav or mobile section-selection intent."
      },
      {
        "name": "dsToolChange",
        "detail": "{ id: PanelToolsToolId; selected: boolean }",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Managed tools selection state."
      }
    ],
    "methods": [
      {
        "name": "activateTool",
        "signature": "activateTool(id: PanelToolsToolId) => Promise<void>",
        "description": "Open or toggle a managed global tool from application-owned UI."
      },
      {
        "name": "closeGlobalSurface",
        "signature": "closeGlobalSurface() => Promise<void>",
        "description": "Close whichever managed global surface is currently visible."
      },
      {
        "name": "setToolPresentation",
        "signature": "setToolPresentation(presentation: \"drawer\" | \"fullscreen\") => Promise<void>",
        "description": "Request drawer or fullscreen presentation for the active managed tool."
      }
    ],
    "slots": [],
    "exports": {
      "customElement": "ds-shell-app",
      "react": "DsShellApp",
      "vue": "DsShellApp",
      "angular": "DsShellApp"
    },
    "consumption": {
      "install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
      "cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
      "customElements": {
        "import": "import '@ds-mo/ui/dist/components/ds-shell-app.js';",
        "example": "<ds-shell-app></ds-shell-app>"
      },
      "react": {
        "import": "import { DsShellApp } from '@ds-mo/ui/react';",
        "example": "<DsShellApp />"
      },
      "vue": {
        "import": "import { DsShellApp } from '@ds-mo/ui/vue';",
        "example": "<DsShellApp />"
      },
      "angular": {
        "import": "import { DsShellApp } from '@ds-mo/ui/angular/ds-shell-app';",
        "example": "<ds-shell-app></ds-shell-app>"
      },
      "complexPropertyNote": "Assign these non-primitive values as JavaScript properties: navigation, pageChrome, paperTexture, tools.",
      "peerDependencies": {
        "required": [
          "@ds-mo/tokens ^6.5.0",
          "@ds-mo/icons ^7.0.0"
        ],
        "frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
      }
    }
  },
  "dependencies": [
    "@ds-mo/ui",
    "@ds-mo/tokens"
  ],
  "registryDependencies": [
    "bar-nav",
    "bar-title",
    "button-unfilled",
    "mobile-bar-nav",
    "mobile-header",
    "mobile-sheet-nav",
    "panel-nav",
    "paper-texture",
    "shell-page",
    "shell-tools",
    "tooltip"
  ],
  "files": [
    {
      "path": "src/wc/components/ShellApp/ShellApp.css",
      "content": ":host {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n  min-height: 0;\n  box-sizing: border-box;\n  overflow: hidden;\n  overscroll-behavior: none;\n  font-family: var(--typography-font-family-ui, system-ui);\n  --ds-shell-panel-tools-width: var(--dimension-panel-width-xs);\n}\n\n:host {\n  --_shell-chrome-bg: var(--color-background-secondary);\n}\n\n.shell-app__row {\n  display: flex;\n  flex: 1 1 0;\n  min-height: 0;\n  position: relative;\n}\n\n.shell-app__banner {\n  flex: 0 0 auto;\n  min-width: 0;\n  position: relative;\n  z-index: 5;\n}\n\n.shell-app__banner ::slotted(ds-banner) {\n  display: grid;\n  width: 100%;\n}\n\n/*\n * One chrome layer behind the shell row. Wash uses background-attachment: fixed\n * so the pattern is locked to the viewport — panel/tools collapse only changes the\n * transparent window, not the pattern phase. JS sets wash background-size from\n * readShellViewportDimensions(), not the shell element box.\n */\n.shell-app__chrome {\n  position: absolute;\n  inset: 0;\n  pointer-events: none;\n  z-index: 0;\n  overflow: hidden;\n  background-color: var(--_shell-chrome-bg);\n}\n\n:host(.shell-app--gradient) .shell-app__chrome::before {\n  content: '';\n  position: absolute;\n  inset: 0;\n  pointer-events: none;\n  background-position: 0 0;\n  background-attachment: fixed;\n  z-index: 1;\n  opacity: var(--ds-shell-gradient-opacity, 1);\n  background-image: var(--ds-shell-gradient-image, none);\n  background-size: var(--ds-shell-gradient-size, auto);\n  background-repeat: no-repeat;\n}\n\n.shell-app__paper-texture {\n  z-index: 2;\n}\n\n.shell-app__panel {\n  flex: 0 0 auto;\n  min-height: 0;\n  height: 100%;\n  position: relative;\n  /* Above main so resize handle line can paint over the workspace edge */\n  z-index: 2;\n}\n\n.shell-app__panel ::slotted(ds-panel-nav) {\n  display: block;\n  height: 100%;\n}\n\n.shell-app__panel > ds-panel-nav {\n  display: block;\n  height: 100%;\n}\n\n.shell-app__main {\n  flex: 1 1 auto;\n  min-width: 0;\n  min-height: 0;\n  display: grid;\n  grid-template-columns: minmax(0, 1fr) auto;\n  grid-template-rows: auto minmax(0, 1fr);\n  grid-template-areas:\n    'bar tools'\n    'content tools';\n  position: relative;\n  z-index: 1;\n}\n\n.shell-app__mobile-sheet-nav,\n.shell-app__mobile-bar-nav {\n  display: none;\n}\n\n.shell-app__bar {\n  grid-area: bar;\n  flex: 0 0 auto;\n  min-width: 0;\n}\n\n.shell-app__bar ::slotted(ds-bar-nav) {\n  display: block;\n}\n\n.shell-app__bar > ds-bar-nav {\n  display: block;\n}\n\n.shell-app__content {\n  grid-area: content;\n  flex: 1 1 auto;\n  min-width: 0;\n  min-height: 0;\n  overflow: auto;\n  overscroll-behavior: none;\n  /* Limit reflow to the page column — not on __main (breaks tools flex shrink). */\n  contain: layout;\n}\n\n/* Layout scroller, not a control: keep it keyboard-focusable without a page-sized ring. */\n.shell-app__content:focus,\n.shell-app__content:focus-visible {\n  outline: none;\n}\n\n/* Opaque page surface — masks the fixed chrome layer in the content column. */\n.shell-app__content {\n  position: relative;\n  z-index: 1;\n  background-color: var(--color-background-primary);\n}\n\n.shell-app__tools {\n  grid-area: tools;\n  flex: 0 0 auto;\n  min-width: 0;\n  min-height: 0;\n  height: 100%;\n  display: flex;\n  position: relative;\n  z-index: 0;\n  overflow: hidden;\n}\n\n.shell-app__tools ::slotted(ds-panel-tools) {\n  display: block;\n  height: 100%;\n  min-height: 0;\n}\n\n.shell-app__tools ::slotted(ds-shell-tools) {\n  display: block;\n  height: 100%;\n  min-height: 0;\n}\n\n.shell-app__tools > ds-shell-tools {\n  display: block;\n  height: 100%;\n  min-height: 0;\n}\n\n.shell-app__tool-slot-proxy {\n  display: block;\n  width: 100%;\n  height: 100%;\n  min-width: 0;\n  min-height: 0;\n}\n\n.shell-app__content > ds-shell-page {\n  display: block;\n  min-height: 100%;\n}\n\n:host(.shell-app--tools-fullscreen) .shell-app__panel,\n:host(.shell-app--tools-fullscreen) .shell-app__bar,\n:host(.shell-app--tools-fullscreen) .shell-app__content {\n  visibility: hidden;\n}\n\n:host(.shell-app--mobile) {\n  --_shell-chrome-bg: var(--color-background-primary);\n  padding-block-start: env(safe-area-inset-top);\n}\n\n/* Refresh the Mobile WebKit stage layer without remounting app content. */\n:host(.shell-app--mobile.shell-app--foreground-refresh) .shell-app__main {\n  transform: translateZ(0);\n}\n\n:host(.shell-app--mobile) .shell-app__chrome,\n:host(.shell-app--mobile) .shell-app__panel,\n:host(.shell-app--mobile) .shell-app__bar {\n  display: none;\n}\n\n:host(.shell-app--mobile) .shell-app__main {\n  width: 100%;\n  box-sizing: border-box;\n  grid-template-columns: minmax(0, 1fr);\n  grid-template-rows: minmax(0, 1fr) auto;\n  grid-template-areas:\n    'stage'\n    'mobile-bar-nav';\n  background: var(--color-background-primary);\n}\n\n:host(.shell-app--mobile) .shell-app__content,\n:host(.shell-app--mobile) .shell-app__tools,\n:host(.shell-app--mobile) .shell-app__mobile-sheet-nav {\n  grid-area: stage;\n  min-width: 0;\n  min-height: 0;\n  height: 100%;\n  position: relative;\n}\n\n:host(.shell-app--mobile) .shell-app__content {\n  z-index: 1;\n  overflow: auto;\n}\n\n:host(.shell-app--mobile) .shell-app__tools {\n  z-index: 2;\n  width: 100%;\n  overflow: hidden;\n  background: var(--color-background-primary);\n}\n\n:host(.shell-app--mobile) .shell-app__mobile-sheet-nav {\n  display: block;\n  z-index: 3;\n  overflow: hidden;\n  background: var(--color-background-primary);\n}\n\n:host(.shell-app--mobile) .shell-app__mobile-bar-nav {\n  grid-area: mobile-bar-nav;\n  display: block;\n  min-width: 0;\n  position: relative;\n  z-index: 4;\n}\n\n:host(.shell-app--mobile) .shell-app__tools[hidden],\n:host(.shell-app--mobile) .shell-app__mobile-sheet-nav[hidden],\n:host(.shell-app--mobile) .shell-app__mobile-bar-nav[hidden] {\n  display: none;\n}\n\n:host(.shell-app--mobile-tool-active) .shell-app__content,\n:host(.shell-app--mobile-sheet-nav-open) .shell-app__content,\n:host(.shell-app--mobile-sheet-nav-open) .shell-app__tools {\n  visibility: hidden;\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "src/wc/components/ShellApp/ShellApp.tsx",
      "content": "import {\n  Component,\n  Prop,\n  Element,\n  Event,\n  EventEmitter,\n  State,\n  Watch,\n  Listen,\n  Method,\n  h,\n  Host,\n} from '@stencil/core';\nimport type { NavChromeStyle } from '../../shell/nav-chrome';\nimport {\n  isEditableShortcutTarget,\n  resolveShellShortcut,\n} from '../../shell/shell-shortcuts';\nimport type {\n  PanelToolsToolId,\n  PanelToolsHeaderAction,\n} from '../PanelTools/panel-tools-types';\nimport { PANEL_TOOLS_DEFAULT_ITEMS } from '../PanelTools/panel-tools-types';\nimport type { BarTitleSectionItem } from '../BarTitle/bar-title-types';\nimport type { BreadcrumbSelectDetail } from '../Breadcrumb/breadcrumb-types';\nimport {\n  DEFAULT_SHELL_GRADIENT_PRESET,\n  normalizeShellGradientPreset,\n  type ShellGradientPreset,\n} from '../../shell/shell-gradient-presets';\nimport {\n  CHROME_TRANSITION_END,\n  CHROME_TRANSITION_START,\n  ChromeTransitionDepth,\n  createRafCoalescer,\n  readChromeTransitionSource,\n} from '../../shell/chrome-transition';\nimport {\n  isPanelNavCollapsed,\n  panelWidthPxFromTokens,\n  readPanelNavWidthTokens,\n  type PanelNavWidthTokens,\n} from '../../shell/shell-chrome-metrics';\nimport {\n  SHELL_GRADIENT_IMAGE_VAR,\n  SHELL_GRADIENT_OPACITY_VAR,\n  SHELL_GRADIENT_POSITION_BAR_VAR,\n  SHELL_GRADIENT_POSITION_PANEL_VAR,\n  SHELL_GRADIENT_SIZE_VAR,\n  buildShellRadialGradient,\n  readShellViewportDimensions,\n  shellGradientPositionBar,\n  shellGradientPositionPanel,\n  shellGradientPresetOpacity,\n  shellGradientSize,\n} from '../../shell/shell-gradient';\nimport {\n  isShellInboxTool,\n  resolveAvailableInboxTool,\n  resolveManagedShellPageCapacity,\n  resolveShellResponsiveMode,\n  SHELL_DESKTOP_BREAKPOINT,\n  shellMobileDestinationForTool,\n  type MobileDestination,\n  type ShellInboxToolId,\n  type ShellResponsiveMode,\n} from '../../shell/shell-responsive';\nimport { deriveActiveIdFromUrl } from '../PanelNav/panel-nav-utils';\nimport type { PanelNavItem } from '../PanelNav/panel-nav-types';\nimport type { PanelNavUserActionDetail } from '../PanelNav/panel-nav-types';\nimport type { MobileBarNavDestinationDetail } from '../MobileBarNav/mobile-bar-nav-types';\nimport type {\n  ShellAppComposition,\n  ShellNavigationConfig,\n  ShellPageChromeConfig,\n  ShellToolsConfig,\n} from './shell-app-types';\nimport type { PaperTextureConfig } from '../PaperTexture/paper-texture-types';\n\n@Component({\n  tag: 'ds-shell-app',\n  styleUrl: 'ShellApp.css',\n  scoped: true,\n})\nexport class ShellApp {\n  private static readonly FOREGROUND_REFRESH_MS = 50;\n\n  /** Managed renders the complete responsive chrome; slotted exposes the advanced composition. */\n  @Prop({ reflect: true }) composition: ShellAppComposition = 'managed';\n\n  /** Router-owned navigation data used by managed composition. */\n  @Prop() navigation: ShellNavigationConfig = {};\n\n  /** Route-owned page title and section data used by managed composition. */\n  @Prop({ attribute: 'page-chrome' }) pageChrome: ShellPageChromeConfig = {};\n\n  /** Product-owned global tool definitions used by managed composition. */\n  @Prop() tools: ShellToolsConfig = {};\n\n  /** Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`. */\n  @Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'dashboard';\n\n  /**\n   * Shell chrome wash preset. `none` renders solid chrome; the remaining\n   * presets use token-based washes that adapt to the active color theme.\n   */\n  @Prop({ attribute: 'gradient-preset', reflect: true }) gradientPreset: ShellGradientPreset =\n    DEFAULT_SHELL_GRADIENT_PRESET;\n\n  /** Optional decorative Paper Design texture layered above the shell wash. */\n  @Prop({ attribute: 'paper-texture' }) paperTexture?: PaperTextureConfig;\n\n  /** When `true` (default), registers global shell keyboard shortcuts. `[` toggles panel nav; `]` closes tools; K, A, S, M, N, and / toggle tool drawers. Modifiers are ignored so browser chords like ⌘N stay native. */\n  @Prop({ attribute: 'shortcuts-enabled' }) shortcutsEnabled: boolean = true;\n\n  /** Controlled mobile surface shown above the persistent bottom bar. */\n  @Prop({ attribute: 'mobile-destination' })\n  mobileDestination: MobileDestination = 'area';\n\n  /** Controlled full-stage Mobile Sheet Nav state. */\n  @Prop({ attribute: 'mobile-sheet-nav-open' })\n  mobileSheetNavOpen: boolean = false;\n\n  /** Emitted after crossing the fixed 768px or 1200px shell boundaries. */\n  @Event() dsResponsiveModeChange!: EventEmitter<{ mode: ShellResponsiveMode }>;\n\n  /** Managed primary-navigation intent; the application router performs navigation. */\n  @Event() dsNavSelect!: EventEmitter<string>;\n\n  /** Managed desktop BarNav or mobile section-selection intent. */\n  @Event() dsTabChange!: EventEmitter<string>;\n\n  /** Managed page-subsection intent from BarTitle or MobileHeader. */\n  @Event() dsSubsectionChange!: EventEmitter<string>;\n\n  /** Managed page-level Back intent. */\n  @Event() dsPageBack!: EventEmitter<MouseEvent>;\n\n  /** Managed breadcrumb intent from BarTitle. */\n  @Event({ cancelable: true })\n  dsBreadcrumbSelect!: EventEmitter<BreadcrumbSelectDetail>;\n\n  /** Managed page action intent from BarTitle. */\n  @Event() dsPageAction!: EventEmitter<string>;\n\n  /** Managed tools selection state. */\n  @Event() dsToolChange!: EventEmitter<{ id: PanelToolsToolId; selected: boolean }>;\n\n  /** Managed tool-header Back intent. */\n  @Event() dsHeaderBack!: EventEmitter<{ tool: PanelToolsToolId }>;\n\n  /** Managed tool-header action intent. */\n  @Event() dsHeaderAction!: EventEmitter<{ tool: PanelToolsToolId; id: string }>;\n\n  /** Managed drawer/fullscreen presentation state. */\n  @Event() dsPresentationChange!: EventEmitter<{ presentation: 'drawer' | 'fullscreen' }>;\n\n  /** Managed mobile Dashboard/Settings browsing intent. */\n  @Event() dsBrowseContextChange!: EventEmitter<NavChromeStyle>;\n\n  /** Managed account-footer action intent. */\n  @Event() dsNavFooterAction!: EventEmitter<void>;\n\n  /** Managed user/account trigger intent. */\n  @Event() dsNavUserAction!: EventEmitter<PanelNavUserActionDetail>;\n\n  @Element() el!: HTMLElement;\n  @State() private toolsFullscreen = false;\n  @State() private resolvedMode: ShellResponsiveMode = 'desktop';\n  @State() private managedToolsOpen = false;\n  @State() private managedActiveTool: PanelToolsToolId | '' = '';\n  @State() private managedToolPresentation: 'drawer' | 'fullscreen' = 'drawer';\n  @State() private managedMobileDestination: MobileDestination = 'area';\n  @State() private managedMobileSheetNavOpen = false;\n  @State() private managedInboxTool: ShellInboxToolId | '' = '';\n  @State() private managedBrowseContext: NavChromeStyle = 'dashboard';\n\n  private resizeObserver: ResizeObserver | null = null;\n  private hasLoaded = false;\n  private foregroundRefreshTimer: number | null = null;\n  private readonly panelNavTransition = new ChromeTransitionDepth();\n  private readonly chromeSyncCoalescer = createRafCoalescer(() => this.syncChrome());\n  private panelWidthTokens: PanelNavWidthTokens = { expandedPx: 0, collapsedPx: 0 };\n  private cachedViewportWidth = 0;\n  private cachedViewportHeight = 0;\n  private onWindowResize = () => {\n    this.updateResponsiveMode();\n    if (this.panelNavTransition.isActive) return;\n    this.scheduleChromeSync();\n  };\n  private onVisualViewportChange = () => {\n    if (this.panelNavTransition.isActive) return;\n    this.scheduleChromeSync();\n  };\n\n  private get managed(): boolean {\n    return this.composition === 'managed';\n  }\n\n  private get effectiveMobileDestination(): MobileDestination {\n    return this.managed ? this.managedMobileDestination : this.mobileDestination;\n  }\n\n  private get effectiveMobileSheetNavOpen(): boolean {\n    return this.managed ? this.managedMobileSheetNavOpen : this.mobileSheetNavOpen;\n  }\n\n  private get resolvedNavigationGroups() {\n    if (this.navigation.groups?.length) return this.navigation.groups;\n    return this.managedBrowseContext === 'settings'\n      ? (this.navigation.settingsGroups ?? [])\n      : (this.navigation.dashboardGroups ?? []);\n  }\n\n  private get resolvedToolItems() {\n    return this.tools.items ?? PANEL_TOOLS_DEFAULT_ITEMS;\n  }\n\n  private get availableInboxTools(): ShellInboxToolId[] {\n    return this.resolvedToolItems\n      .filter(\n        item =>\n          !item.isInactive &&\n          (item.mobileDestination === 'inbox' || isShellInboxTool(item.id))\n      )\n      .map(item => item.id as ShellInboxToolId);\n  }\n\n  componentWillLoad() {\n    if (typeof window !== 'undefined') {\n      this.resolvedMode = resolveShellResponsiveMode(window.innerWidth);\n    }\n    this.managedBrowseContext = this.navigation.browseContext ?? this.navStyle;\n    this.managedMobileDestination = this.mobileDestination;\n    this.managedMobileSheetNavOpen = this.mobileSheetNavOpen;\n    this.managedInboxTool = resolveAvailableInboxTool('', this.availableInboxTools);\n  }\n\n  componentDidLoad() {\n    this.hasLoaded = true;\n    this.syncSlottedNavStyle();\n    this.syncSlottedMobileState();\n    this.connectMetricsObserver();\n    this.connectViewportListeners();\n    this.connectPageLifecycleListeners();\n    this.el.addEventListener(CHROME_TRANSITION_START, this.onChromeTransitionStart);\n    this.el.addEventListener(CHROME_TRANSITION_END, this.onChromeTransitionEnd);\n    requestAnimationFrame(() => {\n      this.cachePanelWidthTokens();\n      this.scheduleChromeSync();\n    });\n    const tools = this.el.querySelector('ds-shell-tools, ds-panel-tools');\n    this.toolsFullscreen = tools?.getAttribute('presentation') === 'fullscreen';\n  }\n\n  @Listen('dsPresentationChange')\n  handleToolsPresentation(event: CustomEvent<{ presentation?: string }>) {\n    this.toolsFullscreen = event.detail?.presentation === 'fullscreen';\n  }\n\n  disconnectedCallback() {\n    this.resizeObserver?.disconnect();\n    this.resizeObserver = null;\n    this.disconnectViewportListeners();\n    this.disconnectPageLifecycleListeners();\n    this.cancelForegroundRefresh();\n    this.chromeSyncCoalescer.cancel();\n    this.el.removeEventListener(CHROME_TRANSITION_START, this.onChromeTransitionStart);\n    this.el.removeEventListener(CHROME_TRANSITION_END, this.onChromeTransitionEnd);\n  }\n\n  @Watch('navStyle')\n  @Watch('gradientPreset')\n  @Watch('paperTexture')\n  onShellPropsChange() {\n    this.syncSlottedNavStyle();\n    this.scheduleChromeSync();\n  }\n\n  @Watch('navigation')\n  handleNavigationChange() {\n    this.managedBrowseContext = this.navigation.browseContext ?? this.navStyle;\n  }\n\n  @Watch('tools')\n  handleToolsChange() {\n    if (\n      this.managedActiveTool &&\n      !this.resolvedToolItems.some(item => item.id === this.managedActiveTool)\n    ) {\n      this.managedToolsOpen = false;\n      this.managedActiveTool = '';\n      this.managedMobileDestination = 'area';\n    }\n    this.managedInboxTool = resolveAvailableInboxTool(\n      this.managedInboxTool,\n      this.availableInboxTools\n    );\n  }\n\n  @Watch('mobileDestination')\n  @Watch('mobileSheetNavOpen')\n  onMobileStateChange(_next: unknown, previous: unknown) {\n    if (this.managed) {\n      this.managedMobileDestination = this.mobileDestination;\n      this.managedMobileSheetNavOpen = this.mobileSheetNavOpen;\n    }\n    this.syncSlottedMobileState();\n    if (\n      previous === true &&\n      !this.effectiveMobileSheetNavOpen &&\n      this.resolvedMode === 'mobile'\n    ) {\n      requestAnimationFrame(() => {\n        const bar = this.el.querySelector('ds-mobile-bar-nav') as\n          | (HTMLElement & {\n              focusDestination?: (\n                destination: MobileDestination | 'sheet-nav'\n              ) => Promise<void>;\n            })\n          | null;\n        const activeElement = document.activeElement;\n        if (activeElement instanceof HTMLElement && bar?.contains(activeElement)) return;\n        void bar?.focusDestination?.('sheet-nav');\n      });\n    }\n  }\n\n  private updateResponsiveMode() {\n    if (typeof window === 'undefined') return;\n    const next = resolveShellResponsiveMode(window.innerWidth);\n    if (next === this.resolvedMode) return;\n    this.resolvedMode = next;\n    this.syncSlottedMobileState();\n    this.scheduleChromeSync();\n    if (this.hasLoaded) this.dsResponsiveModeChange.emit({ mode: next });\n  }\n\n  private onChromeTransitionStart = (event: Event) => {\n    const source = readChromeTransitionSource(event);\n    if (source !== 'panel-nav') return;\n\n    this.panelNavTransition.enter();\n    const viewport = readShellViewportDimensions();\n    this.cachedViewportWidth = viewport.width;\n    this.cachedViewportHeight = viewport.height;\n    this.syncChrome();\n  };\n\n  private onChromeTransitionEnd = (event: Event) => {\n    const source = readChromeTransitionSource(event);\n    if (source !== 'panel-nav') return;\n\n    this.panelNavTransition.exit();\n    if (!this.panelNavTransition.isActive) {\n      this.scheduleChromeSync();\n    }\n  };\n\n  /** Coalesce ResizeObserver bursts to one layout read per frame. */\n  private scheduleChromeSync() {\n    this.chromeSyncCoalescer.schedule();\n  }\n\n  private syncSlottedNavStyle() {\n    const panel = this.el.querySelector('ds-panel-nav') as (HTMLElement & { navStyle: NavChromeStyle }) | null;\n    const bar = this.el.querySelector('ds-bar-nav') as (HTMLElement & { navStyle: NavChromeStyle }) | null;\n    if (panel) {\n      panel.setAttribute('nav-style', this.navStyle);\n      panel.navStyle = this.navStyle;\n    }\n    if (bar) {\n      bar.setAttribute('nav-style', this.navStyle);\n      bar.navStyle = this.navStyle;\n    }\n  }\n\n  private syncSlottedMobileState() {\n    const tools = this.el.querySelector('ds-shell-tools') as\n      | (HTMLElement & { responsiveMode: ShellResponsiveMode })\n      | null;\n    const bar = this.el.querySelector('ds-mobile-bar-nav') as\n      | (HTMLElement & {\n          activeDestination: MobileDestination;\n          sheetNavExpanded: boolean;\n        })\n      | null;\n    const sheetNav = this.el.querySelector('ds-mobile-sheet-nav') as\n      | (HTMLElement & { open: boolean })\n      | null;\n\n    if (tools) {\n      tools.setAttribute('responsive-mode', this.resolvedMode);\n      tools.responsiveMode = this.resolvedMode;\n    }\n    if (bar) {\n      bar.activeDestination = this.effectiveMobileDestination;\n      bar.sheetNavExpanded = this.effectiveMobileSheetNavOpen;\n    }\n    if (sheetNav) {\n      sheetNav.open = this.effectiveMobileSheetNavOpen;\n    }\n  }\n\n  private connectMetricsObserver() {\n    this.resizeObserver = new ResizeObserver(() => {\n      if (this.panelNavTransition.isActive) return;\n      this.scheduleChromeSync();\n    });\n    this.resizeObserver.observe(this.el);\n    const panelWrap = this.el.querySelector('.shell-app__panel');\n    if (panelWrap) this.resizeObserver.observe(panelWrap);\n  }\n\n  private connectViewportListeners() {\n    if (typeof window === 'undefined') return;\n\n    window.addEventListener('resize', this.onWindowResize, { passive: true });\n\n    const visual = window.visualViewport;\n    if (visual) {\n      visual.addEventListener('resize', this.onVisualViewportChange, { passive: true });\n      visual.addEventListener('scroll', this.onVisualViewportChange, { passive: true });\n    }\n  }\n\n  private disconnectViewportListeners() {\n    if (typeof window === 'undefined') return;\n\n    window.removeEventListener('resize', this.onWindowResize);\n\n    const visual = window.visualViewport;\n    if (visual) {\n      visual.removeEventListener('resize', this.onVisualViewportChange);\n      visual.removeEventListener('scroll', this.onVisualViewportChange);\n    }\n  }\n\n  private connectPageLifecycleListeners() {\n    if (typeof window === 'undefined' || typeof document === 'undefined') return;\n    document.addEventListener('visibilitychange', this.onDocumentVisibilityChange);\n    window.addEventListener('pageshow', this.onPageShow);\n  }\n\n  private disconnectPageLifecycleListeners() {\n    if (typeof window === 'undefined' || typeof document === 'undefined') return;\n    document.removeEventListener('visibilitychange', this.onDocumentVisibilityChange);\n    window.removeEventListener('pageshow', this.onPageShow);\n  }\n\n  private onDocumentVisibilityChange = () => {\n    if (!document.hidden) this.refreshAfterForegroundRestore();\n  };\n\n  private onPageShow = (event: PageTransitionEvent) => {\n    if (event.persisted) this.refreshAfterForegroundRestore();\n  };\n\n  private cancelForegroundRefresh() {\n    if (this.foregroundRefreshTimer !== null) {\n      window.clearTimeout(this.foregroundRefreshTimer);\n      this.foregroundRefreshTimer = null;\n    }\n    this.el.classList.remove('shell-app--foreground-refresh');\n  }\n\n  private refreshAfterForegroundRestore() {\n    this.updateResponsiveMode();\n    this.syncSlottedMobileState();\n    this.scheduleChromeSync();\n\n    if (this.resolvedMode !== 'mobile') return;\n\n    /*\n     * Mobile WebKit can restore a live, correctly sized DOM without repainting\n     * its suspended shell layer. Re-promote the complete mobile stage for one\n     * painted frame; routed and tool owners remain mounted and keep their state.\n     */\n    this.cancelForegroundRefresh();\n    this.el.classList.add('shell-app--foreground-refresh');\n    void this.el.offsetHeight;\n    // A short timer spans a real compositor commit; resumed WebKit may coalesce\n    // nested animation frames before presenting either one.\n    this.foregroundRefreshTimer = window.setTimeout(() => {\n      this.foregroundRefreshTimer = null;\n      this.el.classList.remove('shell-app--foreground-refresh');\n      // WebKit otherwise may keep presenting the retired composite layer.\n      void this.el.querySelector<HTMLElement>('.shell-app__main')?.offsetHeight;\n    }, ShellApp.FOREGROUND_REFRESH_MS);\n  }\n\n  private cachePanelWidthTokens() {\n    const navRoot = this.el.querySelector('ds-panel-nav .panel-nav') as HTMLElement | null;\n    if (!navRoot) return;\n    this.panelWidthTokens = readPanelNavWidthTokens(navRoot);\n  }\n\n  private applyGradientVars(target: HTMLElement, vars: Record<string, string | null>) {\n    const style = target.style;\n    for (const [name, value] of Object.entries(vars)) {\n      if (value === null) style.removeProperty(name);\n      else style.setProperty(name, value);\n    }\n  }\n\n  private clearGradientPaintVars(targets: HTMLElement[]) {\n    const keys = [\n      SHELL_GRADIENT_IMAGE_VAR,\n      SHELL_GRADIENT_SIZE_VAR,\n      SHELL_GRADIENT_OPACITY_VAR,\n    ];\n    for (const target of targets) {\n      for (const key of keys) target.style.removeProperty(key);\n    }\n  }\n\n  /** Panel/bar wash offsets for scroll fades and badge rings. */\n  private syncChromeLayoutVars(\n    panel: HTMLElement | null,\n    bar: HTMLElement | null,\n    panelPosition: string,\n    barPosition: string,\n  ) {\n    if (panel) {\n      panel.style.setProperty(SHELL_GRADIENT_POSITION_PANEL_VAR, panelPosition);\n    }\n    if (bar) {\n      bar.style.setProperty(SHELL_GRADIENT_POSITION_BAR_VAR, barPosition);\n    }\n  }\n\n  private resolvePanelWidthPx(panelNav: HTMLElement | null): number {\n    const navRoot = panelNav?.querySelector('.panel-nav') as HTMLElement | null;\n\n    if (this.panelNavTransition.isActive && this.panelWidthTokens.expandedPx > 0) {\n      const collapsed = isPanelNavCollapsed(panelNav, navRoot);\n      return panelWidthPxFromTokens(this.panelWidthTokens, collapsed);\n    }\n\n    const panelWrap = this.el.querySelector('.shell-app__panel') as HTMLElement | null;\n    const measured = panelWrap?.getBoundingClientRect().width ?? 0;\n    if (measured > 0) return measured;\n\n    if (this.panelWidthTokens.expandedPx > 0) {\n      const collapsed = isPanelNavCollapsed(panelNav, navRoot);\n      return panelWidthPxFromTokens(this.panelWidthTokens, collapsed);\n    }\n\n    return 0;\n  }\n\n  /** Fixed-attachment wash/grid size — always the viewport, never the shell element box. */\n  private resolveViewportDimensions(): { width: number; height: number } {\n    if (this.panelNavTransition.isActive && this.cachedViewportWidth > 0) {\n      return {\n        width: this.cachedViewportWidth,\n        height: this.cachedViewportHeight,\n      };\n    }\n\n    return readShellViewportDimensions();\n  }\n\n  private chromeLayerActive(): boolean {\n    return this.gradientLayerActive() || this.paperTextureLayerActive();\n  }\n\n  private gradientLayerActive(): boolean {\n    return (\n      this.resolvedMode !== 'mobile' &&\n      normalizeShellGradientPreset(this.gradientPreset) !== 'none'\n    );\n  }\n\n  private paperTextureLayerActive(): boolean {\n    return this.resolvedMode !== 'mobile' && this.paperTexture != null;\n  }\n\n  @Listen('keydown', { target: 'window', capture: true })\n  handleWindowKeyDown(e: KeyboardEvent) {\n    if (!this.shortcutsEnabled) return;\n    if (isEditableShortcutTarget(e.target)) return;\n\n    const action = resolveShellShortcut(e);\n    if (!action) return;\n\n    e.preventDefault();\n\n    const panel = this.el.querySelector('ds-panel-nav') as HTMLDsPanelNavElement | null;\n    const tools = this.el.querySelector('ds-shell-tools, ds-panel-tools') as\n      | (HTMLElement & {\n          closeDrawer?: () => Promise<void>;\n          activateTool?: (id: PanelToolsToolId) => Promise<void>;\n        })\n      | null;\n\n    if (action === 'toggle-panel-nav') {\n      void panel?.toggleCollapsed();\n      return;\n    }\n\n    if (action === 'close-panel-tools') {\n      void tools?.closeDrawer?.();\n      return;\n    }\n\n    if (action.startsWith('open-tool:') && tools) {\n      const toolId = action.slice('open-tool:'.length) as PanelToolsToolId;\n      void tools.activateTool?.(toolId);\n    }\n  }\n\n  /** Open or toggle a managed global tool from application-owned UI. */\n  @Method()\n  async activateTool(id: PanelToolsToolId) {\n    const tools = this.el.querySelector('ds-shell-tools, ds-panel-tools') as\n      | (HTMLElement & { activateTool?: (tool: PanelToolsToolId) => Promise<void> })\n      | null;\n    await tools?.activateTool?.(id);\n  }\n\n  /** Close whichever managed global surface is currently visible. */\n  @Method()\n  async closeGlobalSurface() {\n    if (this.managedMobileSheetNavOpen) {\n      this.managedMobileSheetNavOpen = false;\n      return;\n    }\n    const tools = this.el.querySelector('ds-shell-tools, ds-panel-tools') as\n      | (HTMLElement & { closeDrawer?: () => Promise<void> })\n      | null;\n    await tools?.closeDrawer?.();\n  }\n\n  /** Request drawer or fullscreen presentation for the active managed tool. */\n  @Method()\n  async setToolPresentation(presentation: 'drawer' | 'fullscreen') {\n    this.managedToolPresentation = presentation;\n    this.toolsFullscreen = presentation === 'fullscreen';\n  }\n\n  private handleManagedNavSelect = (event: CustomEvent<string>) => {\n    event.stopPropagation();\n    this.dsNavSelect.emit(event.detail);\n  };\n\n  private handleManagedTabChange = (event: CustomEvent<string>) => {\n    event.stopPropagation();\n    this.dsTabChange.emit(event.detail);\n  };\n\n  private handleManagedPageBack = (event: CustomEvent<MouseEvent>) => {\n    event.stopPropagation();\n    this.dsPageBack.emit(event.detail);\n  };\n\n  private handleManagedBreadcrumbSelect = (\n    event: CustomEvent<BreadcrumbSelectDetail>\n  ) => {\n    event.stopPropagation();\n    const forwarded = this.dsBreadcrumbSelect.emit(event.detail);\n    if (forwarded.defaultPrevented) event.preventDefault();\n  };\n\n  private handleManagedSubsectionChange = (event: CustomEvent<string>) => {\n    event.stopPropagation();\n    this.dsSubsectionChange.emit(event.detail);\n  };\n\n  private handleManagedPageAction = (event: CustomEvent<string>) => {\n    event.stopPropagation();\n    this.dsPageAction.emit(event.detail);\n  };\n\n  private handleManagedToolChange = (\n    event: CustomEvent<{ id: PanelToolsToolId; selected: boolean }>\n  ) => {\n    event.stopPropagation();\n    const { id, selected } = event.detail;\n    this.managedActiveTool = id;\n    this.managedToolsOpen = selected;\n    const item = this.resolvedToolItems.find(candidate => candidate.id === id);\n    if (item?.mobileDestination === 'inbox' || isShellInboxTool(id)) {\n      this.managedInboxTool = id;\n    }\n    this.managedMobileDestination = selected\n      ? (item?.mobileDestination ?? shellMobileDestinationForTool(true, id))\n      : 'area';\n    this.managedMobileSheetNavOpen = false;\n    this.dsToolChange.emit(event.detail);\n  };\n\n  private handleManagedPresentationChange = (\n    event: CustomEvent<{ presentation: 'drawer' | 'fullscreen' }>\n  ) => {\n    event.stopPropagation();\n    this.managedToolPresentation = event.detail.presentation;\n    this.toolsFullscreen = event.detail.presentation === 'fullscreen';\n    this.dsPresentationChange.emit(event.detail);\n  };\n\n  private handleManagedHeaderBack = (event: CustomEvent<{ tool: PanelToolsToolId }>) => {\n    event.stopPropagation();\n    this.dsHeaderBack.emit(event.detail);\n  };\n\n  private handleManagedHeaderAction = (\n    event: CustomEvent<{ tool: PanelToolsToolId; id: string }>\n  ) => {\n    event.stopPropagation();\n    this.dsHeaderAction.emit(event.detail);\n  };\n\n  private handleManagedSheetToggle = (event: CustomEvent<boolean>) => {\n    event.stopPropagation();\n    this.managedMobileSheetNavOpen = event.detail;\n  };\n\n  private activateManagedMobileTool(destination: Exclude<MobileDestination, 'area'>): boolean {\n    const canonicalId =\n      destination === 'inbox'\n        ? resolveAvailableInboxTool(this.managedInboxTool, this.availableInboxTools)\n        : destination;\n    if (!canonicalId) return false;\n\n    const item = this.resolvedToolItems.find(\n      candidate =>\n        candidate.id === canonicalId ||\n        (destination !== 'inbox' && candidate.mobileDestination === destination)\n    );\n    if (!item || item.isInactive) return false;\n\n    const id = item.id;\n    this.managedActiveTool = id;\n    this.managedToolsOpen = true;\n    if (isShellInboxTool(id)) this.managedInboxTool = id;\n    this.dsToolChange.emit({ id: id as PanelToolsToolId, selected: true });\n    return true;\n  }\n\n  private handleManagedMobileDestination = (\n    event: CustomEvent<MobileBarNavDestinationDetail>\n  ) => {\n    event.stopPropagation();\n    const destination = event.detail.destination;\n    this.managedMobileSheetNavOpen = false;\n    this.managedMobileDestination = destination;\n\n    if (destination === 'area') {\n      this.managedToolsOpen = false;\n      return;\n    }\n\n    this.activateManagedMobileTool(destination);\n  };\n\n  private handleManagedAreaSelect = (event: CustomEvent<string>) => {\n    event.stopPropagation();\n    if (event.detail === 'help') {\n      this.managedMobileSheetNavOpen = false;\n      this.managedMobileDestination = 'help';\n      if (this.activateManagedMobileTool('help')) return;\n    }\n\n    this.managedMobileSheetNavOpen = false;\n    this.managedMobileDestination = 'area';\n    this.managedToolsOpen = false;\n    this.dsNavSelect.emit(event.detail);\n  };\n\n  private handleManagedBrowseContext = (event: CustomEvent<NavChromeStyle>) => {\n    event.stopPropagation();\n    this.managedBrowseContext = event.detail;\n    this.dsBrowseContextChange.emit(event.detail);\n  };\n\n  private handleManagedSheetClose = (event: CustomEvent<void>) => {\n    event.stopPropagation();\n    this.managedMobileSheetNavOpen = false;\n  };\n\n  private handleManagedNavFooterAction = (event: CustomEvent<void>) => {\n    event.stopPropagation();\n    this.dsNavFooterAction.emit();\n  };\n\n  private handleManagedNavUserAction = (\n    event: CustomEvent<PanelNavUserActionDetail>\n  ) => {\n    event.stopPropagation();\n    this.dsNavUserAction.emit(event.detail);\n  };\n\n  private get currentArea(): PanelNavItem {\n    const groups = [\n      ...(this.navigation.dashboardGroups ?? []),\n      ...(this.navigation.settingsGroups ?? []),\n      ...this.resolvedNavigationGroups,\n    ];\n    const items = groups.flatMap(group => group.items);\n    const activeId =\n      this.navigation.activeId ||\n      deriveActiveIdFromUrl(\n        this.navigation.currentUrl ?? this.pageChrome.currentUrl ?? '',\n        items\n      );\n    return (\n      items.find(item => item.id === activeId) ??\n      items[0] ?? {\n        id: 'area',\n        icon: 'MapPage',\n        label: this.pageChrome.heading || 'Area',\n      }\n    );\n  }\n\n  private toolDot(id: PanelToolsToolId): boolean {\n    return this.resolvedToolItems.find(item => item.id === id)?.dot ?? false;\n  }\n\n  private syncChrome() {\n    const panelNav = this.el.querySelector('ds-panel-nav') as HTMLElement | null;\n    const bar = this.el.querySelector('ds-bar-nav') as HTMLElement | null;\n    const targets = [this.el, panelNav, bar].filter((el): el is HTMLElement => el !== null);\n\n    const clearLayoutVars = () => {\n      if (panelNav) panelNav.style.removeProperty(SHELL_GRADIENT_POSITION_PANEL_VAR);\n      if (bar) bar.style.removeProperty(SHELL_GRADIENT_POSITION_BAR_VAR);\n    };\n\n    const preset = normalizeShellGradientPreset(this.gradientPreset);\n    if (this.resolvedMode === 'mobile') {\n      this.clearGradientPaintVars(targets);\n      clearLayoutVars();\n      return;\n    }\n\n    const viewport = this.resolveViewportDimensions();\n    if (preset === 'none') {\n      this.clearGradientPaintVars(targets);\n      clearLayoutVars();\n      this.syncPaperTextureLayout(viewport);\n      return;\n    }\n\n    const panelWidth = this.resolvePanelWidthPx(panelNav);\n\n    const panelPosition = shellGradientPositionPanel();\n    const barPosition = shellGradientPositionBar(panelWidth);\n\n    this.syncChromeLayoutVars(panelNav, bar, panelPosition, barPosition);\n\n    const image = buildShellRadialGradient(preset);\n\n    const size = shellGradientSize({\n      width: viewport.width,\n      height: viewport.height,\n    });\n    const opacity = shellGradientPresetOpacity(preset);\n\n    for (const target of targets) {\n      if (target === this.el) {\n        this.applyGradientVars(target, {\n          [SHELL_GRADIENT_IMAGE_VAR]: image,\n          [SHELL_GRADIENT_SIZE_VAR]: size,\n          [SHELL_GRADIENT_POSITION_PANEL_VAR]: panelPosition,\n          [SHELL_GRADIENT_POSITION_BAR_VAR]: barPosition,\n          [SHELL_GRADIENT_OPACITY_VAR]: opacity,\n        });\n        continue;\n      }\n\n      const isBar = target.tagName.toLowerCase() === 'ds-bar-nav';\n      this.applyGradientVars(target, {\n        [SHELL_GRADIENT_IMAGE_VAR]: image,\n        [SHELL_GRADIENT_SIZE_VAR]: size,\n        [SHELL_GRADIENT_OPACITY_VAR]: opacity,\n        [SHELL_GRADIENT_POSITION_PANEL_VAR]: isBar ? null : panelPosition,\n        [SHELL_GRADIENT_POSITION_BAR_VAR]: isBar ? barPosition : null,\n      });\n    }\n\n    this.syncPaperTextureLayout(viewport);\n  }\n\n  private syncPaperTextureLayout(viewport: { width: number; height: number }) {\n    const chrome = this.el.querySelector<HTMLElement>('.shell-app__chrome');\n    const paperTexture = this.el.querySelector<HTMLElement>(\n      '.shell-app__chrome > ds-paper-texture',\n    );\n    if (!chrome || !paperTexture) return;\n\n    const chromeRect = chrome.getBoundingClientRect();\n    paperTexture.style.setProperty('width', `${Math.round(viewport.width)}px`);\n    paperTexture.style.setProperty('height', `${Math.round(viewport.height)}px`);\n    paperTexture.style.setProperty('left', `${-Math.round(chromeRect.left)}px`);\n    paperTexture.style.setProperty('top', `${-Math.round(chromeRect.top)}px`);\n  }\n\n  private renderPaperTexture() {\n    if (!this.paperTextureLayerActive()) return null;\n    return <ds-paper-texture class=\"shell-app__paper-texture\" config={this.paperTexture} />;\n  }\n\n  private renderManagedPanelNav() {\n    const navigation = this.navigation;\n    return (\n      <ds-panel-nav\n        navStyle={this.navStyle}\n        groups={this.resolvedNavigationGroups}\n        breakpoint={SHELL_DESKTOP_BREAKPOINT}\n        routerMode={navigation.routerMode ?? 'event'}\n        activeId={navigation.activeId ?? ''}\n        currentUrl={navigation.currentUrl ?? this.pageChrome.currentUrl ?? ''}\n        storageKey={navigation.storageKey ?? ''}\n        userName={navigation.userName ?? ''}\n        userInitial={navigation.userInitial ?? ''}\n        accountMenuExpanded={navigation.accountMenuExpanded ?? false}\n        dashboardLabel={navigation.dashboardLabel ?? 'Dashboard'}\n        settingsLabel={navigation.settingsLabel ?? 'Settings'}\n        accountLabel={navigation.accountLabel ?? 'Account'}\n        dashboardNavigationLabel={\n          navigation.dashboardNavigationLabel ?? 'Dashboard navigation'\n        }\n        settingsNavigationLabel={\n          navigation.settingsNavigationLabel ?? 'Settings navigation'\n        }\n        navigationItemsLabel={navigation.navigationLabel ?? 'Navigation items'}\n        onDsNavSelect={this.handleManagedNavSelect}\n        onDsNavFooterAction={this.handleManagedNavFooterAction}\n        onDsNavUserAction={this.handleManagedNavUserAction}\n      />\n    );\n  }\n\n  private renderManagedBarNav() {\n    return (\n      <ds-bar-nav\n        navStyle={this.navStyle}\n        tabs={this.pageChrome.tabs ?? []}\n        value={this.pageChrome.value ?? ''}\n        heading={this.pageChrome.routeHeading ?? this.pageChrome.heading}\n        basePath={this.pageChrome.basePath ?? ''}\n        currentUrl={this.pageChrome.currentUrl ?? this.navigation.currentUrl ?? ''}\n        onDsTabChange={this.handleManagedTabChange}\n      />\n    );\n  }\n\n  private renderManagedToolSlots() {\n    const ids = [...new Set(this.resolvedToolItems.map(item => item.id))].sort();\n    return ids.flatMap(id => [\n      <div key={`tool:${id}`} class=\"shell-app__tool-slot-proxy\" slot={id}>\n        <slot name={id} />\n      </div>,\n      <div\n        key={`tool-view:${id}`}\n        class=\"shell-app__tool-slot-proxy\"\n        slot={`${id}-view`}\n      >\n        <slot name={`${id}-view`} />\n      </div>,\n    ]);\n  }\n\n  private renderManagedTools() {\n    const tools = this.tools;\n    return (\n      <ds-shell-tools\n        responsiveMode={this.resolvedMode}\n        open={this.managedToolsOpen}\n        activeTool={this.managedActiveTool}\n        presentation={this.managedToolPresentation}\n        fullscreenHeaderMode={tools.fullscreenHeaderMode ?? 'shared'}\n        items={this.resolvedToolItems}\n        headers={tools.headers ?? {}}\n        storageKey={tools.storageKey ?? ''}\n        toolsLabel={tools.toolsLabel ?? 'Tools'}\n        toolShortcutsLabel={tools.toolShortcutsLabel ?? 'Tool shortcuts'}\n        inboxLabel={tools.inboxLabel ?? 'Inbox'}\n        inboxNavigationLabel={tools.inboxNavigationLabel ?? 'Inbox sections'}\n        onDsToolChange={this.handleManagedToolChange}\n        onDsPresentationChange={this.handleManagedPresentationChange}\n        onDsHeaderBack={this.handleManagedHeaderBack}\n        onDsHeaderAction={this.handleManagedHeaderAction}\n      >\n        {this.renderManagedToolSlots()}\n      </ds-shell-tools>\n    );\n  }\n\n  private renderManagedMobileSheetNav() {\n    const navigation = this.navigation;\n    return (\n      <ds-mobile-sheet-nav\n        open={this.managedMobileSheetNavOpen}\n        browseContext={this.managedBrowseContext}\n        dashboardGroups={navigation.dashboardGroups ?? navigation.groups ?? []}\n        settingsGroups={navigation.settingsGroups ?? []}\n        currentUrl={navigation.currentUrl ?? this.pageChrome.currentUrl ?? ''}\n        routeSelectionActive={this.managedMobileDestination === 'area'}\n        navigationLabel={navigation.navigationLabel ?? 'Application navigation'}\n        dashboardLabel={navigation.dashboardLabel ?? 'Dashboard'}\n        settingsLabel={navigation.settingsLabel ?? 'Settings'}\n        accountLabel={navigation.accountLabel ?? 'Account'}\n        showAccount={navigation.showMobileAccount ?? true}\n        onDsAreaSelect={this.handleManagedAreaSelect}\n        onDsBrowseContextChange={this.handleManagedBrowseContext}\n        onDsClose={this.handleManagedSheetClose}\n      />\n    );\n  }\n\n  private renderManagedMobileBarNav() {\n    return (\n      <ds-mobile-bar-nav\n        hidden={this.tools.mobileBarHidden ?? false}\n        activeDestination={this.managedMobileDestination}\n        currentArea={this.currentArea}\n        sheetNavExpanded={this.managedMobileSheetNavOpen}\n        searchLabel={this.tools.items?.find(item => item.id === 'search')?.ariaLabel ?? 'Search'}\n        inboxLabel={this.tools.inboxLabel ?? 'Inbox'}\n        messagesLabel={this.tools.items?.find(item => item.id === 'messages')?.ariaLabel ?? 'Messages'}\n        agentsLabel={this.tools.items?.find(item => item.id === 'agents')?.ariaLabel ?? 'Agents'}\n        helpLabel={this.tools.items?.find(item => item.id === 'help')?.ariaLabel ?? 'Help & Support'}\n        searchDot={this.toolDot('search')}\n        inboxDot={\n          this.toolDot('stacks') || this.toolDot('activity')\n        }\n        messagesDot={this.toolDot('messages')}\n        agentsDot={this.toolDot('agents')}\n        onDsSheetNavToggle={this.handleManagedSheetToggle}\n        onDsDestinationChange={this.handleManagedMobileDestination}\n      />\n    );\n  }\n\n  private renderManagedMobileLeading() {\n    if (!this.pageChrome.showBack) {\n      return <slot name=\"page-header-leading\" slot=\"leading\" />;\n    }\n    return [\n      <ds-tooltip\n        slot=\"leading\"\n        label={this.pageChrome.backAriaLabel ?? 'Back'}\n        side=\"bottom\"\n        size=\"sm\"\n      >\n        <ds-button-unfilled\n          variant=\"icon\"\n          icon={this.pageChrome.backIcon ?? 'ChevronLeft'}\n          size=\"md\"\n          aria-label={this.pageChrome.backAriaLabel ?? 'Back'}\n          activeFill={false}\n          hasBorder={false}\n          onDsClick={(event: CustomEvent<MouseEvent>) =>\n            this.dsPageBack.emit(event.detail)\n          }\n        />\n      </ds-tooltip>,\n      <slot name=\"page-header-leading\" slot=\"leading\" />,\n    ];\n  }\n\n  private renderManagedMobileActions() {\n    const actions = this.pageChrome.mobileActions ?? [];\n    return [\n      ...actions.map((action: PanelToolsHeaderAction) => (\n        <ds-tooltip\n          slot=\"trailing\"\n          key={action.id}\n          label={action.ariaLabel}\n          side=\"bottom\"\n          size=\"sm\"\n        >\n          <ds-button-unfilled\n            id={action.triggerId || undefined}\n            variant=\"icon\"\n            icon={action.icon}\n            size=\"md\"\n            aria-label={action.ariaLabel}\n            haspopup={action.haspopup}\n            controls={action.controls}\n            expanded={action.expanded}\n            pressed={action.pressed}\n            isInactive={action.isInactive}\n            activeFill={false}\n            hasBorder={false}\n            onDsClick={() => this.dsPageAction.emit(action.id)}\n          />\n        </ds-tooltip>\n      )),\n      <slot name=\"page-header-trailing\" slot=\"trailing\" />,\n    ];\n  }\n\n  private renderManagedPage() {\n    const page = this.pageChrome;\n    const mobileSections = page.showBack ? [] : (page.tabs ?? []);\n    const barTitleSections: BarTitleSectionItem[] = (page.subsections ?? []).map(item =>\n      'type' in item\n        ? { type: 'divider' }\n        : {\n            id: item.id,\n            label: item.label,\n            isInactive: item.isInactive,\n          }\n    );\n    return (\n      <ds-shell-page\n        responsiveMode={this.resolvedMode}\n        headerCapacity={resolveManagedShellPageCapacity(this.resolvedMode)}\n        contentInset={page.contentInset ?? 'default'}\n        contentInsetBlockStart={page.contentInsetBlockStart ?? 'default'}\n        contentInsetBlockStartSize={page.contentInsetBlockStartSize}\n        compactContentInsetBlockStartSize={page.compactContentInsetBlockStartSize}\n        scrollCompaction={page.scrollCompaction ?? true}\n        contentSurface={page.contentSurface ?? 'primary'}\n      >\n        <ds-bar-title\n          slot=\"header\"\n          heading={page.heading ?? ''}\n          description={page.description ?? ''}\n          showBack={page.showBack ?? false}\n          backAriaLabel={page.backAriaLabel ?? 'Back'}\n          backLabel={page.backLabel ?? 'Back'}\n          breadcrumbs={page.breadcrumbs ?? []}\n          breadcrumbAriaLabel={page.breadcrumbAriaLabel ?? 'Breadcrumb'}\n          sections={barTitleSections}\n          value={page.subvalue ?? ''}\n          sectionsAriaLabel={page.subsectionsAriaLabel ?? 'Change page subsection'}\n          primaryAction={page.primaryAction ?? null}\n          actions={page.actions ?? []}\n          actionsAriaLabel={page.actionsAriaLabel ?? 'More page actions'}\n          showDivider={page.showHeaderDivider ?? true}\n          showCompactDivider={page.showCompactHeaderDivider}\n          onDsBack={this.handleManagedPageBack}\n          onDsBreadcrumbSelect={this.handleManagedBreadcrumbSelect}\n          onDsSectionChange={this.handleManagedSubsectionChange}\n          onDsAction={this.handleManagedPageAction}\n        />\n        <ds-mobile-header\n          slot=\"mobile-header\"\n          heading={page.heading ?? ''}\n          headingLevel={page.headingLevel ?? 'h1'}\n          sections={mobileSections}\n          value={page.showBack ? '' : (page.value ?? '')}\n          sectionsAriaLabel={page.sectionsAriaLabel ?? 'Change page section'}\n          subsections={page.subsections ?? []}\n          subvalue={page.subvalue ?? ''}\n          subsectionsAriaLabel={\n            page.subsectionsAriaLabel ?? 'Change page subsection'\n          }\n          tone={page.tone ?? 'default'}\n          onDsSectionChange={this.handleManagedTabChange}\n          onDsSubsectionChange={this.handleManagedSubsectionChange}\n        >\n          {this.renderManagedMobileLeading()}\n          {this.renderManagedMobileActions()}\n        </ds-mobile-header>\n        <slot />\n      </ds-shell-page>\n    );\n  }\n\n  private renderSlottedShell(\n    shellCls: Record<string, boolean>,\n    mobile: boolean,\n    mobileToolActive: boolean,\n    mobileStageBlocked: boolean,\n    fullscreen: boolean\n  ) {\n    return (\n      <Host class={shellCls} responsive-mode={this.resolvedMode}>\n        <div class=\"shell-app__banner\">\n          <slot name=\"banner\" />\n        </div>\n        <div class=\"shell-app__row\">\n          <div class=\"shell-app__chrome\" aria-hidden=\"true\">\n            {this.renderPaperTexture()}\n          </div>\n          <div\n            class=\"shell-app__panel\"\n            aria-hidden={fullscreen ? 'true' : undefined}\n            inert={fullscreen ? true : undefined}\n          >\n            <slot name=\"panel\" />\n          </div>\n          <div class=\"shell-app__main\">\n            <div\n              class=\"shell-app__bar\"\n              aria-hidden={fullscreen ? 'true' : undefined}\n              inert={fullscreen ? true : undefined}\n            >\n              <slot name=\"bar\" />\n            </div>\n            <div\n              class=\"shell-app__tools\"\n              aria-hidden={\n                mobile && (!mobileToolActive || this.mobileSheetNavOpen)\n                  ? 'true'\n                  : undefined\n              }\n              inert={\n                mobile && (!mobileToolActive || this.mobileSheetNavOpen)\n                  ? true\n                  : undefined\n              }\n              hidden={mobile && !mobileToolActive}\n            >\n              <slot name=\"tools\" />\n            </div>\n            <div\n              class=\"shell-app__content\"\n              aria-hidden={fullscreen || mobileStageBlocked ? 'true' : undefined}\n              inert={fullscreen || mobileStageBlocked ? true : undefined}\n              tabIndex={0}\n            >\n              <slot />\n            </div>\n            <div\n              class=\"shell-app__mobile-sheet-nav\"\n              hidden={!mobile || !this.mobileSheetNavOpen}\n              aria-hidden={\n                mobile && this.mobileSheetNavOpen ? undefined : 'true'\n              }\n              inert={mobile && this.mobileSheetNavOpen ? undefined : true}\n            >\n              <slot name=\"mobile-sheet-nav\" />\n            </div>\n            <div class=\"shell-app__mobile-bar-nav\" hidden={!mobile}>\n              <slot name=\"mobile-bar-nav\" />\n            </div>\n          </div>\n        </div>\n      </Host>\n    );\n  }\n\n  render() {\n    const chromeActive = this.chromeLayerActive();\n    const gradientActive = this.gradientLayerActive();\n    const paperTextureActive = this.paperTextureLayerActive();\n    const mobile = this.resolvedMode === 'mobile';\n    const mobileToolActive = mobile && this.effectiveMobileDestination !== 'area';\n    const mobileStageBlocked =\n      mobile && (mobileToolActive || this.effectiveMobileSheetNavOpen);\n    const fullscreen = !mobile && this.toolsFullscreen;\n    const shellCls: Record<string, boolean> = {\n      'shell-app': true,\n      'shell-app--chrome': chromeActive,\n      'shell-app--gradient': gradientActive,\n      'shell-app--paper-texture': paperTextureActive,\n      [`shell-app--${this.navStyle}`]: true,\n      [`shell-app--${this.resolvedMode}`]: true,\n      'shell-app--tools-fullscreen': fullscreen,\n      'shell-app--mobile-tool-active': mobileToolActive,\n      'shell-app--mobile-sheet-nav-open':\n        mobile && this.effectiveMobileSheetNavOpen,\n      'shell-app--managed': this.managed,\n      'shell-app--slotted': !this.managed,\n    };\n\n    if (!this.managed) {\n      return this.renderSlottedShell(\n        shellCls,\n        mobile,\n        mobileToolActive,\n        mobileStageBlocked,\n        fullscreen\n      );\n    }\n\n    return (\n      <Host class={shellCls} responsive-mode={this.resolvedMode}>\n        <div class=\"shell-app__banner\">\n          <slot name=\"banner\" />\n        </div>\n        <div class=\"shell-app__row\">\n          <div class=\"shell-app__chrome\" aria-hidden=\"true\">\n            {this.renderPaperTexture()}\n          </div>\n          <div class=\"shell-app__panel\" aria-hidden={fullscreen ? 'true' : undefined} inert={fullscreen ? true : undefined}>\n            {this.renderManagedPanelNav()}\n          </div>\n          <div class=\"shell-app__main\">\n            <div class=\"shell-app__bar\" aria-hidden={fullscreen ? 'true' : undefined} inert={fullscreen ? true : undefined}>\n              {this.renderManagedBarNav()}\n            </div>\n            <div\n              class=\"shell-app__tools\"\n              aria-hidden={\n                mobile && (!mobileToolActive || this.effectiveMobileSheetNavOpen)\n                  ? 'true'\n                  : undefined\n              }\n              inert={\n                mobile && (!mobileToolActive || this.effectiveMobileSheetNavOpen)\n                  ? true\n                  : undefined\n              }\n              hidden={mobile && !mobileToolActive}\n            >\n              {this.renderManagedTools()}\n            </div>\n            <div\n              class=\"shell-app__content\"\n              aria-hidden={fullscreen || mobileStageBlocked ? 'true' : undefined}\n              inert={fullscreen || mobileStageBlocked ? true : undefined}\n              tabIndex={0}\n            >\n              {this.renderManagedPage()}\n            </div>\n            <div\n              class=\"shell-app__mobile-sheet-nav\"\n              hidden={!mobile || !this.effectiveMobileSheetNavOpen}\n              aria-hidden={\n                mobile && this.effectiveMobileSheetNavOpen ? undefined : 'true'\n              }\n              inert={mobile && this.effectiveMobileSheetNavOpen ? undefined : true}\n            >\n              {this.renderManagedMobileSheetNav()}\n            </div>\n            <div class=\"shell-app__mobile-bar-nav\" hidden={!mobile}>\n              {this.renderManagedMobileBarNav()}\n            </div>\n          </div>\n        </div>\n      </Host>\n    );\n  }\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "src/wc/components/ShellApp/shell-app-types.ts",
      "content": "import type { BarNavTab } from '../BarNav/bar-nav-types';\nimport type {\n  BarTitleActionItem,\n  BarTitlePrimaryAction,\n} from '../BarTitle/bar-title-types';\nimport type { BreadcrumbItem } from '../Breadcrumb/breadcrumb-types';\nimport type { MobileHeaderTone } from '../MobileHeader/mobile-header-types';\nimport type {\n  PanelNavGroup,\n  PanelNavItem,\n  PanelNavRouterMode,\n} from '../PanelNav/panel-nav-types';\nimport type {\n  PanelToolsHeaders,\n  PanelToolsHeaderAction,\n  PanelToolsItem,\n} from '../PanelTools/panel-tools-types';\nimport type { NavChromeStyle } from '../../shell/nav-chrome';\nimport type {\n  ShellPageContentInset,\n  ShellPageContentSurface,\n} from '../ShellPage/shell-page-types';\n\nexport type ShellAppComposition = 'managed' | 'slotted';\n\n/**\n * Router-owned application navigation supplied to managed ShellApp.\n * ShellApp renders the responsive chrome but never performs route changes.\n */\nexport interface ShellNavigationConfig {\n  /** Groups shown in the current desktop/tablet PanelNav context. */\n  groups?: PanelNavGroup[];\n  /** Complete Dashboard collection used by the mobile navigation sheet. */\n  dashboardGroups?: PanelNavGroup[];\n  /** Complete Settings collection used by the mobile navigation sheet. */\n  settingsGroups?: PanelNavGroup[];\n  /** Current application URL used for active-item matching. */\n  currentUrl?: string;\n  /** Optional explicit active destination when URL matching is unavailable. */\n  activeId?: string;\n  /** Current Dashboard or Settings browsing context. */\n  browseContext?: NavChromeStyle;\n  /** Managed shells default to event routing so the application router stays authoritative. */\n  routerMode?: PanelNavRouterMode;\n  /** Persisted desktop PanelNav collapse preference key. */\n  storageKey?: string;\n  userName?: string;\n  userInitial?: string;\n  accountMenuExpanded?: boolean;\n  dashboardLabel?: string;\n  settingsLabel?: string;\n  accountLabel?: string;\n  /** Hide the mobile-sheet Account shortcut when Settings already owns that destination. */\n  showMobileAccount?: boolean;\n  navigationLabel?: string;\n  dashboardNavigationLabel?: string;\n  settingsNavigationLabel?: string;\n}\n\n/**\n * Route-owned page chrome. The same data drives desktop BarNav/BarTitle and\n * the intentionally different mobile MobileHeader presentation.\n */\nexport interface ShellPageChromeConfig {\n  heading?: string;\n  /** Desktop/tablet route-navigation heading when it differs from the page h1. */\n  routeHeading?: string;\n  description?: string;\n  headingLevel?: 'h1' | 'h2';\n  tabs?: BarNavTab[];\n  value?: string;\n  basePath?: string;\n  currentUrl?: string;\n  sectionsAriaLabel?: string;\n  subsections?: BarNavTab[];\n  subvalue?: string;\n  subsectionsAriaLabel?: string;\n  showBack?: boolean;\n  backAriaLabel?: string;\n  backIcon?: string;\n  backLabel?: string;\n  breadcrumbs?: BreadcrumbItem[];\n  breadcrumbAriaLabel?: string;\n  primaryAction?: BarTitlePrimaryAction | null;\n  actions?: BarTitleActionItem[];\n  /** Icon actions rendered in the mobile header's trailing lane. */\n  mobileActions?: PanelToolsHeaderAction[];\n  actionsAriaLabel?: string;\n  tone?: MobileHeaderTone;\n  contentInset?: ShellPageContentInset;\n  /** Remove only the content gutter adjacent to the page header. */\n  contentInsetBlockStart?: ShellPageContentInset;\n  /** Exact roomy block-start content inset when the responsive default is not appropriate. */\n  contentInsetBlockStartSize?: string;\n  /** Exact block-start content inset while page-title capacity is compact or constrained. */\n  compactContentInsetBlockStartSize?: string;\n  /** Allow a roomy page title to compact in response to page scrolling. */\n  scrollCompaction?: boolean;\n  /** Draw the divider beneath the desktop/tablet page title. */\n  showHeaderDivider?: boolean;\n  /** Override divider visibility while the page title is compact or constrained. */\n  showCompactHeaderDivider?: boolean;\n  /** Canvas surface painted by ShellPage around and beneath routed content. */\n  contentSurface?: ShellPageContentSurface;\n}\n\n/**\n * Product-owned global tools supplied to managed ShellApp. Tool view content\n * remains mounted in the corresponding named `*-view` slots.\n */\nexport interface ShellToolsConfig {\n  items?: PanelToolsItem[];\n  headers?: PanelToolsHeaders;\n  storageKey?: string;\n  fullscreenHeaderMode?: 'shared' | 'split';\n  toolsLabel?: string;\n  toolShortcutsLabel?: string;\n  inboxLabel?: string;\n  inboxNavigationLabel?: string;\n  /** Temporarily conceal the mobile bar while a tool-owned keyboard interaction needs the space. */\n  mobileBarHidden?: boolean;\n}\n\nexport interface ShellNavigationSelectDetail {\n  id: string;\n  item?: PanelNavItem;\n}\n",
      "type": "registry:ui"
    }
  ]
}
