@praxisui/page-builder 9.0.65 → 9.0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Inject, ChangeDetectionStrategy, Component, EventEmitter, Output, Input, inject, ElementRef, signal, computed, effect, Optional, input, output, InjectionToken, ENVIRONMENT_INITIALIZER, Injectable, Injector, ViewChild } from '@angular/core';
2
+ import { Inject, ChangeDetectionStrategy, Component, EventEmitter, Output, Input, inject, ElementRef, signal, computed, effect, Optional, input, output, afterEveryRender, InjectionToken, ENVIRONMENT_INITIALIZER, Injectable, Injector, ViewChild } from '@angular/core';
3
3
  import * as i2 from '@angular/material/button';
4
4
  import { MatButtonModule } from '@angular/material/button';
5
5
  import * as i1 from '@angular/material/dialog';
@@ -7,10 +7,10 @@ import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
7
7
  import * as i2$1 from '@angular/material/icon';
8
8
  import { MatIconModule } from '@angular/material/icon';
9
9
  import * as i2$2 from '@praxisui/core';
10
- import { PraxisIconDirective, providePraxisI18n, BUILTIN_SHELL_PRESETS, PraxisIconButtonComponent, PraxisI18nService, GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, SurfaceOpenActionEditorComponent, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, PraxisJsonLogicService, NestedWidgetConfigAccessor, canonicalJsonSha256, CompositionValidatorService, ComponentMetadataRegistry, ResourceDiscoveryService, ComponentRuntimeProfileService, buildApiUrl, deepMerge, generateId, ObservabilityDashboardService, PraxisRuntimeComponentObservationRegistryService, domainKnowledgeTimelineToRichContentDocument, DomainRuleService, DomainKnowledgeService, SETTINGS_PANEL_BRIDGE, DynamicWidgetPageComponent, DYNAMIC_PAGE_SHELL_EDITOR } from '@praxisui/core';
10
+ import { PraxisIconDirective, providePraxisI18n, BUILTIN_SHELL_PRESETS, PraxisI18nService, PraxisIconButtonComponent, GLOBAL_ACTION_CATALOG, ComponentMetadataRegistry, CompositionValidatorService, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, SurfaceOpenActionEditorComponent, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, PraxisJsonLogicService, NestedWidgetConfigAccessor, canonicalJsonSha256, ResourceDiscoveryService, ComponentRuntimeProfileService, buildApiUrl, deepMerge, generateId, ObservabilityDashboardService, PraxisRuntimeComponentObservationRegistryService, domainKnowledgeTimelineToRichContentDocument, DomainRuleService, DomainKnowledgeService, SETTINGS_PANEL_BRIDGE, DynamicWidgetPageComponent, DYNAMIC_PAGE_SHELL_EDITOR } from '@praxisui/core';
11
11
  import * as i3 from '@angular/material/tooltip';
12
12
  import { MatTooltipModule } from '@angular/material/tooltip';
13
- import * as i3$1 from '@angular/material/form-field';
13
+ import * as i4 from '@angular/material/form-field';
14
14
  import { MatFormFieldModule } from '@angular/material/form-field';
15
15
  import * as i5 from '@angular/material/input';
16
16
  import { MatInputModule } from '@angular/material/input';
@@ -22,13 +22,15 @@ import * as i5$1 from '@angular/material/checkbox';
22
22
  import { MatCheckboxModule } from '@angular/material/checkbox';
23
23
  import * as i6 from '@angular/material/divider';
24
24
  import { MatDividerModule } from '@angular/material/divider';
25
- import * as i6$1 from '@angular/material/select';
25
+ import * as i10 from '@angular/material/select';
26
26
  import { MatSelectModule } from '@angular/material/select';
27
27
  import { BehaviorSubject, merge, timeout, map, catchError, throwError, firstValueFrom, from, share, concatMap, of, timer, takeUntil, Subject, tap, filter, take } from 'rxjs';
28
28
  import { SETTINGS_PANEL_DATA } from '@praxisui/settings-panel';
29
29
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
30
30
  import { CdkTrapFocus } from '@angular/cdk/a11y';
31
- import * as i7 from '@angular/material/tabs';
31
+ import * as i3$1 from '@angular/material/button-toggle';
32
+ import { MatButtonToggleModule } from '@angular/material/button-toggle';
33
+ import * as i8 from '@angular/material/tabs';
32
34
  import { MatTabsModule } from '@angular/material/tabs';
33
35
  import { HttpClient, HttpHeaders } from '@angular/common/http';
34
36
  import { AgenticAuthoringTurnClientService, withAuthoringScopePolicyContextHints, toPraxisAssistantConversationMessages, PraxisAssistantTurnOrchestratorService, PraxisAssistantSessionRegistryService, createPraxisAssistantViewportLayout, PraxisAiAssistantShellComponent } from '@praxisui/ai';
@@ -361,6 +363,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
361
363
  }] } });
362
364
 
363
365
  const PRAXIS_PAGE_BUILDER_EN_US = {
366
+ "praxis.pageBuilder.editor.devices.target": "Device to configure",
367
+ "praxis.pageBuilder.editor.devices.advanced": "Advanced overrides",
368
+ "praxis.pageBuilder.editor.grouping.canvasNotice": "Canvas positions control this page. Groups below are preserved for flow layouts and do not arrange widgets in the canvas. Use the widget move and resize controls to place them side by side.",
369
+ "praxis.pageBuilder.editor.devices.stack": "Stack widgets",
370
+ "praxis.pageBuilder.editor.devices.stackHelp": "Use full width and natural height, in reading order. This replaces positions and width limits only for this device; other layouts stay unchanged.",
371
+ "praxis.pageBuilder.editor.devices.stacked": "Single-column layout prepared. Save to apply; Reset undoes the changes.",
372
+ 'praxis.pageBuilder.shell.menuPlacement': 'More actions menu',
373
+ 'praxis.pageBuilder.editor.limits.tab': "Widget limits",
374
+ 'praxis.pageBuilder.editor.limits.help': 'Grid limits use columns and rows. Content-row layouts also offer pixel height below. Leave limits blank for defaults or to inherit the base layout.',
375
+ 'praxis.pageBuilder.editor.limits.target': "Widget and layout",
376
+ 'praxis.pageBuilder.editor.limits.base': "Base layout",
377
+ 'praxis.pageBuilder.editor.limits.minColSpan': "Minimum width (columns)",
378
+ 'praxis.pageBuilder.editor.limits.maxColSpan': "Maximum width (columns)",
379
+ 'praxis.pageBuilder.editor.limits.minRowSpan': "Minimum height (rows)",
380
+ 'praxis.pageBuilder.editor.limits.maxRowSpan': "Maximum height (rows)",
381
+ 'praxis.pageBuilder.editor.limits.autoHeight': 'Outer height, with internal scrolling. The editor opened by the page also checks usable space in the current geometry. Review other variants on their device. Default inherits the base; Automatic clears height and inset.',
382
+ 'praxis.pageBuilder.editor.height.mode': 'Widget height',
383
+ 'praxis.pageBuilder.editor.height.inherit': 'Default',
384
+ 'praxis.pageBuilder.editor.height.auto': 'Automatic',
385
+ 'praxis.pageBuilder.editor.height.fixed': 'Adjustable',
386
+ 'praxis.pageBuilder.editor.height.value': 'Height (px)',
387
+ 'praxis.pageBuilder.editor.height.top': 'Top inset (px)',
388
+ 'praxis.pageBuilder.editor.height.min': 'Minimum height (px)',
389
+ 'praxis.pageBuilder.editor.height.max': 'Maximum height (px)',
390
+ 'praxis.pageBuilder.editor.height.error': 'Use a whole height of at least 160px, within the effective limits, and a non-negative top inset.',
391
+ 'praxis.pageBuilder.editor.limits.empty': "Add widgets to the canvas to configure their limits.",
392
+ 'praxis.pageBuilder.editor.limits.geometry': "Current size: {{width}} columns × {{height}} rows. Grid: {{columns}} columns.",
393
+ 'praxis.pageBuilder.editor.limits.errors.integer': "Use positive whole numbers or leave blank.",
394
+ 'praxis.pageBuilder.editor.limits.errors.range': "The minimum cannot exceed the maximum, including inherited limits.",
395
+ 'praxis.pageBuilder.editor.limits.errors.columns': "The minimum width exceeds the grid columns.",
396
+ 'praxis.pageBuilder.editor.limits.errors.size': "The current size is outside these limits. Resize the widget first, or review the limits.",
397
+ 'praxis.pageBuilder.shell.invalidCss': 'Invalid CSS value. Use a valid color, dimension or expression for this field.',
398
+ 'praxis.pageBuilder.shell.stickyHeader': 'Keep header visible while scrolling the page',
364
399
  'praxis.pageBuilder.authoringSource.alert.title': 'Semantic lineage requires attention',
365
400
  'praxis.pageBuilder.authoringSource.alert.description': 'The page remains available, but this source will not be reused until new governed authoring restores its provenance.',
366
401
  'praxis.pageBuilder.authoringSource.diagnostics.authoring-source-schema-invalid': 'The authoring envelope does not match the schema supported by Page Builder.',
@@ -685,9 +720,9 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
685
720
  'praxis.pageBuilder.connections.validation.statePathNotDeclared': 'State path is not declared in page state values.',
686
721
  'praxis.pageBuilder.connections.validation.feedbackCycleUnguarded': 'Connection participates in an unguarded feedback cycle.',
687
722
  'praxis.pageBuilder.connections.validation.feedbackCycleGuarded': 'Connection participates in an intentional guarded feedback cycle.',
688
- 'praxis.pageBuilder.connections.suggestions.title': 'Smart suggestions',
689
- 'praxis.pageBuilder.connections.suggestions.recommendedTitle': 'Recommended connection',
690
- 'praxis.pageBuilder.connections.suggestions.quickConnect': 'Create connection',
723
+ 'praxis.pageBuilder.connections.suggestions.title': 'Wiring possibilities',
724
+ 'praxis.pageBuilder.connections.suggestions.recommendedTitle': 'Shortcut · table and detail',
725
+ 'praxis.pageBuilder.connections.suggestions.quickConnect': 'Review connection',
691
726
  'praxis.pageBuilder.connections.suggestions.connectSelectionToForm': 'Connect selection to form',
692
727
  'praxis.pageBuilder.connections.suggestions.outcome.masterDetail': 'Selecting a row in {{source}} updates {{target}}.',
693
728
  'praxis.pageBuilder.connections.suggestions.outcome.default': '{{source}} updates {{target}}.',
@@ -701,9 +736,9 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
701
736
  'praxis.pageBuilder.connections.suggestions.preview.rowIdToDetail': 'Will deliver the selected row id (payload.row.id) to the form.',
702
737
  'praxis.pageBuilder.connections.suggestions.preview.stateToInput': 'Will deliver the current page state value.',
703
738
  'praxis.pageBuilder.connections.suggestions.preview.sourcePayload': 'Will deliver the source payload unless you add a transform.',
704
- 'praxis.pageBuilder.connections.trace.simulate': 'Simulate',
705
- 'praxis.pageBuilder.connections.trace.title': 'Simulation trace',
706
- 'praxis.pageBuilder.connections.trace.summary': 'Derived runtime propagation preview',
739
+ 'praxis.pageBuilder.connections.trace.simulate': 'Explore route',
740
+ 'praxis.pageBuilder.connections.trace.title': 'Structural route',
741
+ 'praxis.pageBuilder.connections.trace.summary': 'Configuration walkthrough. Does not execute real events, conditions or requests.',
707
742
  'praxis.pageBuilder.connections.trace.livePath': 'Live path',
708
743
  'praxis.pageBuilder.connections.trace.sourceNode': 'Source',
709
744
  'praxis.pageBuilder.connections.trace.reactingNode': 'Reacting',
@@ -718,7 +753,7 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
718
753
  'praxis.pageBuilder.connections.tooltip.stateData': 'State - Data',
719
754
  'praxis.pageBuilder.connections.tooltip.capability': 'Capability',
720
755
  'praxis.pageBuilder.connections.tooltip.dataFlow': 'Data Flow',
721
- 'praxis.pageBuilder.connections.trace.empty': 'No connections to simulate.',
756
+ 'praxis.pageBuilder.connections.trace.empty': 'No connections to explore.',
722
757
  'praxis.pageBuilder.connections.trace.phase.emit': 'Emit',
723
758
  'praxis.pageBuilder.connections.trace.phase.condition': 'Condition',
724
759
  'praxis.pageBuilder.connections.trace.phase.transform': 'Transform',
@@ -1023,9 +1058,82 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
1023
1058
  'praxis.pageBuilder.agentic.errors.componentManifestUnavailable': 'This component does not provide an AI authoring manifest compatible with the requested change.',
1024
1059
  'praxis.pageBuilder.agentic.errors.componentPlanInvalid': 'The proposed change did not pass the component validations. Review the request and try again.',
1025
1060
  'praxis.pageBuilder.agentic.errors.generic': 'AI authoring failed.',
1061
+ 'praxis.pageBuilder.connections.draft.new': 'New connection',
1062
+ 'praxis.pageBuilder.connections.draft.backToEdit': 'Back to editing',
1063
+ 'praxis.pageBuilder.connections.draft.present': 'Present diagram',
1064
+ 'praxis.pageBuilder.connections.draft.title': 'Connection draft',
1065
+ 'praxis.pageBuilder.connections.draft.prerequisites': 'Enable the source event in its component settings. For row selection, enable single selection; configure the detail as read-only when needed.',
1066
+ 'praxis.pageBuilder.connections.draft.help': 'Choose the source and destination. Review the route and delivered value before applying.',
1067
+ 'praxis.pageBuilder.connections.draft.source': '1 · Source / event',
1068
+ 'praxis.pageBuilder.connections.draft.target': '2 · Destination / input',
1069
+ 'praxis.pageBuilder.connections.draft.chooseSource': 'Choose a source',
1070
+ 'praxis.pageBuilder.connections.draft.chooseTarget': 'Choose a destination',
1071
+ 'praxis.pageBuilder.connections.draft.review': 'Review the route',
1072
+ 'praxis.pageBuilder.connections.draft.valuePath': 'Delivered value · payload path',
1073
+ 'praxis.pageBuilder.connections.draft.nullFallback': 'When the value is absent, deliver null to clear the destination.',
1074
+ 'praxis.pageBuilder.connections.draft.existingFallback': 'Missing values follow the transformation and delivery policy configured below.',
1075
+ 'praxis.pageBuilder.connections.draft.wholePayload': 'The whole source value will be delivered. Use the transformation settings to extract a field.',
1076
+ 'praxis.pageBuilder.connections.draft.conflict': 'The page changed during editing. Cancel this draft and reopen the connection to use the latest version.',
1077
+ 'praxis.pageBuilder.connections.draft.pending': 'Draft · not yet applied to the page',
1078
+ 'praxis.pageBuilder.connections.draft.apply': 'Apply connection',
1079
+ 'praxis.pageBuilder.connections.draft.cancel': 'Cancel',
1080
+ 'praxis.pageBuilder.connections.draft.pageSave': 'Connections apply to the page in the builder. Save the page to persist changes.',
1081
+ 'praxis.pageBuilder.connections.draft.edit': 'Edit connection',
1082
+ 'praxis.pageBuilder.connections.draft.advanced': 'Transformation, conditions and delivery',
1083
+ 'praxis.pageBuilder.connections.mode.simulation': 'Route',
1084
+ 'praxis.pageBuilder.connections.mode.all': 'All',
1085
+ 'praxis.pageBuilder.connections.mode.data': 'Data',
1086
+ 'praxis.pageBuilder.connections.mode.event': 'Events',
1087
+ 'praxis.pageBuilder.connections.mode.capability': 'Actions',
1088
+ 'praxis.pageBuilder.connections.draft.extract': 'Extract a field',
1089
+ 'praxis.pageBuilder.connections.draft.clearMissing': 'Clear when absent',
1090
+ 'praxis.pageBuilder.connections.draft.keepMissing': 'Keep destination when absent',
1091
+ 'praxis.pageBuilder.connections.editor.segmentKindLegend': 'Port types',
1092
+ 'praxis.pageBuilder.connections.closeAria': 'Close connection editor',
1093
+ 'praxis.pageBuilder.connections.mode.showFullSchemaAria': 'Show ports and technical details',
1094
+ 'praxis.pageBuilder.connections.mode.showGuidedViewAria': 'Show guided view',
1095
+ 'praxis.pageBuilder.connections.mode.statusGuided': 'Guided view',
1096
+ 'praxis.pageBuilder.connections.mode.statusAll': 'Ports and details',
1097
+ 'praxis.pageBuilder.connections.mode.statusSimulation': 'Configuration walkthrough',
1098
+ 'praxis.pageBuilder.connections.mode.aria': 'Filter connections by type',
1099
+ 'praxis.pageBuilder.connections.editor.segmentKinds': 'Port types',
1100
+ 'praxis.pageBuilder.connections.draft.duplicate': 'An identical connection already exists. Edit the existing connection or change this draft.',
1026
1101
  };
1027
1102
 
1028
1103
  const PRAXIS_PAGE_BUILDER_PT_BR = {
1104
+ "praxis.pageBuilder.editor.devices.target": "Dispositivo para configurar",
1105
+ "praxis.pageBuilder.editor.devices.advanced": "Ajustes avançados",
1106
+ "praxis.pageBuilder.editor.grouping.canvasNotice": "As posições no canvas controlam esta página. Os agrupamentos abaixo são preservados para layouts de fluxo e não organizam os widgets no canvas. Para colocá-los lado a lado, use os controles de mover e redimensionar de cada widget.",
1107
+ "praxis.pageBuilder.editor.devices.stack": "Empilhar widgets",
1108
+ "praxis.pageBuilder.editor.devices.stackHelp": "Usa a largura total e altura natural, na ordem de leitura. Substitui as posições e os limites de largura apenas neste dispositivo; os outros layouts são preservados.",
1109
+ "praxis.pageBuilder.editor.devices.stacked": "Layout de uma coluna preparado. Salve para aplicar; Redefinir desfaz os ajustes.",
1110
+ 'praxis.pageBuilder.shell.menuPlacement': 'Menu Mais ações',
1111
+ 'praxis.pageBuilder.editor.limits.tab': "Limites dos widgets",
1112
+ 'praxis.pageBuilder.editor.limits.help': 'Os limites da grade usam colunas e linhas. Em linhas de conteúdo, a seção de altura também permite pixels. Deixe os limites em branco para usar os padrões ou herdar o layout base.',
1113
+ 'praxis.pageBuilder.editor.limits.target': "Widget e layout",
1114
+ 'praxis.pageBuilder.editor.limits.base': "Layout base",
1115
+ 'praxis.pageBuilder.editor.limits.minColSpan': "Largura mínima (colunas)",
1116
+ 'praxis.pageBuilder.editor.limits.maxColSpan': "Largura máxima (colunas)",
1117
+ 'praxis.pageBuilder.editor.limits.minRowSpan': "Altura mínima (linhas)",
1118
+ 'praxis.pageBuilder.editor.limits.maxRowSpan': "Altura máxima (linhas)",
1119
+ 'praxis.pageBuilder.editor.limits.autoHeight': 'Altura externa, com rolagem interna. O editor aberto pela página também verifica a área útil na geometria atual. Confira outras variantes no dispositivo correspondente. Padrão herda a base; Automática remove altura e recuo.',
1120
+ 'praxis.pageBuilder.editor.height.mode': 'Altura do widget',
1121
+ 'praxis.pageBuilder.editor.height.inherit': 'Padrão',
1122
+ 'praxis.pageBuilder.editor.height.auto': 'Automática',
1123
+ 'praxis.pageBuilder.editor.height.fixed': 'Ajustável',
1124
+ 'praxis.pageBuilder.editor.height.value': 'Altura (px)',
1125
+ 'praxis.pageBuilder.editor.height.top': 'Recuo superior (px)',
1126
+ 'praxis.pageBuilder.editor.height.min': 'Altura mínima (px)',
1127
+ 'praxis.pageBuilder.editor.height.max': 'Altura máxima (px)',
1128
+ 'praxis.pageBuilder.editor.height.error': 'Use altura inteira de pelo menos 160px, dentro dos limites efetivos, e recuo superior não negativo.',
1129
+ 'praxis.pageBuilder.editor.limits.empty': "Adicione widgets ao canvas para configurar seus limites.",
1130
+ 'praxis.pageBuilder.editor.limits.geometry': "Tamanho atual: {{width}} colunas × {{height}} linhas. Grade: {{columns}} colunas.",
1131
+ 'praxis.pageBuilder.editor.limits.errors.integer': "Use números inteiros positivos ou deixe em branco.",
1132
+ 'praxis.pageBuilder.editor.limits.errors.range': "O mínimo não pode ultrapassar o máximo, incluindo os limites herdados.",
1133
+ 'praxis.pageBuilder.editor.limits.errors.columns': "A largura mínima ultrapassa as colunas da grade.",
1134
+ 'praxis.pageBuilder.editor.limits.errors.size': "O tamanho atual está fora destes limites. Redimensione o widget antes ou revise os limites.",
1135
+ 'praxis.pageBuilder.shell.invalidCss': 'Valor CSS inválido. Use uma cor, medida ou expressão válida para este campo.',
1136
+ 'praxis.pageBuilder.shell.stickyHeader': 'Manter cabeçalho visível ao rolar a página',
1029
1137
  'praxis.pageBuilder.authoringSource.alert.title': 'Linhagem semântica requer atenção',
1030
1138
  'praxis.pageBuilder.authoringSource.alert.description': 'A página continua disponível, mas esta fonte não será reutilizada até que uma nova autoria governada restabeleça sua proveniência.',
1031
1139
  'praxis.pageBuilder.authoringSource.diagnostics.authoring-source-schema-invalid': 'O envelope de autoria não atende ao schema suportado pelo Page Builder.',
@@ -1350,9 +1458,9 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
1350
1458
  'praxis.pageBuilder.connections.validation.statePathNotDeclared': 'O caminho de estado não está declarado nos valores de estado da página.',
1351
1459
  'praxis.pageBuilder.connections.validation.feedbackCycleUnguarded': 'A conexão participa de um ciclo de feedback sem guarda.',
1352
1460
  'praxis.pageBuilder.connections.validation.feedbackCycleGuarded': 'A conexão participa de um ciclo de feedback intencional e guardado.',
1353
- 'praxis.pageBuilder.connections.suggestions.title': 'Sugestões inteligentes',
1354
- 'praxis.pageBuilder.connections.suggestions.recommendedTitle': 'Conexão recomendada',
1355
- 'praxis.pageBuilder.connections.suggestions.quickConnect': 'Criar conexão',
1461
+ 'praxis.pageBuilder.connections.suggestions.title': 'Possibilidades de ligação',
1462
+ 'praxis.pageBuilder.connections.suggestions.recommendedTitle': 'Atalho · tabela e detalhe',
1463
+ 'praxis.pageBuilder.connections.suggestions.quickConnect': 'Revisar conexão',
1356
1464
  'praxis.pageBuilder.connections.suggestions.connectSelectionToForm': 'Conectar seleção ao formulário',
1357
1465
  'praxis.pageBuilder.connections.suggestions.outcome.masterDetail': 'Selecionar uma linha em {{source}} atualiza {{target}}.',
1358
1466
  'praxis.pageBuilder.connections.suggestions.outcome.default': '{{source}} atualiza {{target}}.',
@@ -1366,9 +1474,9 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
1366
1474
  'praxis.pageBuilder.connections.suggestions.preview.rowIdToDetail': 'Entregará o ID da linha selecionada (payload.row.id) ao formulário.',
1367
1475
  'praxis.pageBuilder.connections.suggestions.preview.stateToInput': 'Entregará o valor atual do estado da página.',
1368
1476
  'praxis.pageBuilder.connections.suggestions.preview.sourcePayload': 'Entregará o payload da origem, a menos que você adicione uma transformação.',
1369
- 'praxis.pageBuilder.connections.trace.simulate': 'Simular',
1370
- 'praxis.pageBuilder.connections.trace.title': 'Trace de simulação',
1371
- 'praxis.pageBuilder.connections.trace.summary': 'Prévia derivada da propagação em runtime',
1477
+ 'praxis.pageBuilder.connections.trace.simulate': 'Explorar percurso',
1478
+ 'praxis.pageBuilder.connections.trace.title': 'Percurso estrutural',
1479
+ 'praxis.pageBuilder.connections.trace.summary': 'Leitura da configuração. Não executa eventos, condições ou chamadas reais.',
1372
1480
  'praxis.pageBuilder.connections.trace.livePath': 'Caminho ativo',
1373
1481
  'praxis.pageBuilder.connections.trace.sourceNode': 'Origem',
1374
1482
  'praxis.pageBuilder.connections.trace.reactingNode': 'Reagindo',
@@ -1383,7 +1491,7 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
1383
1491
  'praxis.pageBuilder.connections.tooltip.stateData': 'Estado - Dados',
1384
1492
  'praxis.pageBuilder.connections.tooltip.capability': 'Capacidade',
1385
1493
  'praxis.pageBuilder.connections.tooltip.dataFlow': 'Fluxo de dados',
1386
- 'praxis.pageBuilder.connections.trace.empty': 'Nenhuma conexão para simular.',
1494
+ 'praxis.pageBuilder.connections.trace.empty': 'Nenhuma conexão para explorar.',
1387
1495
  'praxis.pageBuilder.connections.trace.phase.emit': 'Emitir',
1388
1496
  'praxis.pageBuilder.connections.trace.phase.condition': 'Condição',
1389
1497
  'praxis.pageBuilder.connections.trace.phase.transform': 'Transformar',
@@ -1688,6 +1796,46 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
1688
1796
  'praxis.pageBuilder.agentic.errors.componentManifestUnavailable': 'Este componente não disponibiliza um manifesto de autoria por IA compatível com a alteração solicitada.',
1689
1797
  'praxis.pageBuilder.agentic.errors.componentPlanInvalid': 'A alteração proposta não passou pelas validações do componente. Revise o pedido e tente novamente.',
1690
1798
  'praxis.pageBuilder.agentic.errors.generic': 'Falha na autoria com IA.',
1799
+ 'praxis.pageBuilder.connections.draft.new': 'Nova conexão',
1800
+ 'praxis.pageBuilder.connections.draft.backToEdit': 'Voltar à edição',
1801
+ 'praxis.pageBuilder.connections.draft.present': 'Apresentar diagrama',
1802
+ 'praxis.pageBuilder.connections.draft.title': 'Rascunho da conexão',
1803
+ 'praxis.pageBuilder.connections.draft.prerequisites': 'Habilite o evento nas configurações do componente de origem. Para seleção de linhas, habilite a seleção única; configure o detalhe como somente leitura quando necessário.',
1804
+ 'praxis.pageBuilder.connections.draft.help': 'Escolha a origem e o destino. Revise o percurso e o valor entregue antes de aplicar.',
1805
+ 'praxis.pageBuilder.connections.draft.source': '1 · Origem / evento',
1806
+ 'praxis.pageBuilder.connections.draft.target': '2 · Destino / entrada',
1807
+ 'praxis.pageBuilder.connections.draft.chooseSource': 'Selecione a origem',
1808
+ 'praxis.pageBuilder.connections.draft.chooseTarget': 'Selecione o destino',
1809
+ 'praxis.pageBuilder.connections.draft.review': 'Revise o percurso',
1810
+ 'praxis.pageBuilder.connections.draft.valuePath': 'Valor entregue · caminho no payload',
1811
+ 'praxis.pageBuilder.connections.draft.nullFallback': 'Quando o valor estiver ausente, entregar null para limpar o destino.',
1812
+ 'praxis.pageBuilder.connections.draft.existingFallback': 'Valores ausentes seguem a transformação e a política de entrega configuradas abaixo.',
1813
+ 'praxis.pageBuilder.connections.draft.wholePayload': 'O valor de origem será entregue inteiro. Use as opções de transformação para extrair um campo.',
1814
+ 'praxis.pageBuilder.connections.draft.conflict': 'A página mudou durante a edição. Cancele este rascunho e reabra a conexão para usar a versão atual.',
1815
+ 'praxis.pageBuilder.connections.draft.pending': 'Rascunho · ainda não aplicado à página',
1816
+ 'praxis.pageBuilder.connections.draft.apply': 'Aplicar conexão',
1817
+ 'praxis.pageBuilder.connections.draft.cancel': 'Cancelar',
1818
+ 'praxis.pageBuilder.connections.draft.pageSave': 'As conexões são aplicadas à página no builder. Salve a página para persistir as alterações.',
1819
+ 'praxis.pageBuilder.connections.draft.edit': 'Editar conexão',
1820
+ 'praxis.pageBuilder.connections.draft.advanced': 'Transformação, condições e entrega',
1821
+ 'praxis.pageBuilder.connections.mode.simulation': 'Percurso',
1822
+ 'praxis.pageBuilder.connections.mode.all': 'Todas',
1823
+ 'praxis.pageBuilder.connections.mode.data': 'Dados',
1824
+ 'praxis.pageBuilder.connections.mode.event': 'Eventos',
1825
+ 'praxis.pageBuilder.connections.mode.capability': 'Ações',
1826
+ 'praxis.pageBuilder.connections.draft.extract': 'Extrair um campo',
1827
+ 'praxis.pageBuilder.connections.draft.clearMissing': 'Limpar quando ausente',
1828
+ 'praxis.pageBuilder.connections.draft.keepMissing': 'Manter destino quando ausente',
1829
+ 'praxis.pageBuilder.connections.editor.segmentKindLegend': 'Tipos de porta',
1830
+ 'praxis.pageBuilder.connections.closeAria': 'Fechar editor de conexões',
1831
+ 'praxis.pageBuilder.connections.mode.showFullSchemaAria': 'Mostrar portas e detalhes técnicos',
1832
+ 'praxis.pageBuilder.connections.mode.showGuidedViewAria': 'Mostrar visão guiada',
1833
+ 'praxis.pageBuilder.connections.mode.statusGuided': 'Visão guiada',
1834
+ 'praxis.pageBuilder.connections.mode.statusAll': 'Portas e detalhes',
1835
+ 'praxis.pageBuilder.connections.mode.statusSimulation': 'Percurso da configuração',
1836
+ 'praxis.pageBuilder.connections.mode.aria': 'Filtrar conexões por tipo',
1837
+ 'praxis.pageBuilder.connections.editor.segmentKinds': 'Tipos de porta',
1838
+ 'praxis.pageBuilder.connections.draft.duplicate': 'Já existe uma conexão idêntica. Edite a conexão existente ou altere este rascunho.',
1691
1839
  };
1692
1840
 
1693
1841
  function createPraxisPageBuilderI18nConfig(options = {}) {
@@ -2011,7 +2159,7 @@ class ComponentPaletteDialogComponent {
2011
2159
  <div mat-dialog-actions align="end">
2012
2160
  <button mat-stroked-button color="primary" mat-dialog-close>{{ tx('palette.cancel', 'Cancelar') }}</button>
2013
2161
  </div>
2014
- `, isInline: true, styles: [":host{display:block}h2[mat-dialog-title]{margin:0;padding:12px 24px 10px;border-bottom:1px solid var(--md-sys-color-outline-variant)}.dlg-title{display:flex;align-items:center;gap:10px}.dlg-icon{color:var(--md-sys-color-primary)}.dlg-head{font-size:18px;font-weight:600;line-height:1.2}.dlg-sub{font-size:12px;opacity:.85;color:var(--md-sys-color-on-surface-variant)}.pdx-palette-content{min-width:480px;max-width:920px;padding-top:8px}.pdx-palette-search{width:100%;margin-bottom:12px}.pdx-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}.pdx-grid.dense{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.pdx-grid.roomy{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px}.pdx-card{appearance:none;position:relative;display:grid;grid-template-columns:44px 1fr;grid-template-rows:auto auto;gap:10px;padding:14px;border:1px solid transparent;border-radius:14px;cursor:pointer;outline:none;color:inherit;font:inherit;min-height:92px;text-align:left;background:var(--md-sys-color-surface);border-color:var(--md-sys-color-outline-variant);transition:box-shadow .2s ease,background .25s ease,transform .06s ease}.pdx-grid.dense .pdx-card{padding:10px;border-radius:12px;min-height:84px}.pdx-grid.roomy .pdx-card{padding:16px;border-radius:16px;min-height:100px}.pdx-card:focus,.pdx-card:hover{box-shadow:var(--mat-elevation-level4);background:var(--md-sys-color-surface-container-low);border-color:var(--md-sys-color-primary)}.pdx-card:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-card:active{transform:translateY(1px)}.pdx-card-icon{grid-row:span 2;display:flex;align-items:center;justify-content:center;color:var(--md-sys-color-primary)}.pdx-card-title{font-weight:600;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pdx-card-desc{color:var(--md-sys-color-on-surface-variant);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}.pdx-card-badges{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.pdx-card-badge{display:inline-flex;align-items:center;gap:4px;min-height:22px;padding:2px 8px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container-highest);font-size:11px;line-height:1.2;font-weight:600;letter-spacing:.01em}.pdx-card-badge mat-icon{width:14px;height:14px;font-size:14px}.pdx-card-badge.ready{color:var(--md-sys-color-primary);border-color:color-mix(in srgb,var(--md-sys-color-primary) 45%,transparent);background:color-mix(in srgb,var(--md-sys-color-primary-container) 72%,transparent)}.pdx-card-badge.info{color:var(--md-sys-color-tertiary);border-color:color-mix(in srgb,var(--md-sys-color-tertiary) 38%,transparent);background:color-mix(in srgb,var(--md-sys-color-tertiary-container) 68%,transparent)}.pdx-card-badge.warning{color:var(--md-sys-color-error);border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,transparent);background:color-mix(in srgb,var(--md-sys-color-error-container) 62%,transparent)}.pdx-empty{padding:24px;display:grid;place-items:center;color:var(--md-sys-color-on-surface-variant);gap:8px}@media(max-width:600px){.pdx-palette-content{min-width:320px}.pdx-grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2162
+ `, isInline: true, styles: [":host{display:block}h2[mat-dialog-title]{margin:0;padding:12px 24px 10px;border-bottom:1px solid var(--md-sys-color-outline-variant)}.dlg-title{display:flex;align-items:center;gap:10px}.dlg-icon{color:var(--md-sys-color-primary)}.dlg-head{font-size:18px;font-weight:600;line-height:1.2}.dlg-sub{font-size:12px;opacity:.85;color:var(--md-sys-color-on-surface-variant)}.pdx-palette-content{min-width:480px;max-width:920px;padding-top:8px}.pdx-palette-search{width:100%;margin-bottom:12px}.pdx-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}.pdx-grid.dense{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.pdx-grid.roomy{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px}.pdx-card{appearance:none;position:relative;display:grid;grid-template-columns:44px 1fr;grid-template-rows:auto auto;gap:10px;padding:14px;border:1px solid transparent;border-radius:14px;cursor:pointer;outline:none;color:inherit;font:inherit;min-height:92px;text-align:left;background:var(--md-sys-color-surface);border-color:var(--md-sys-color-outline-variant);transition:box-shadow .2s ease,background .25s ease,transform .06s ease}.pdx-grid.dense .pdx-card{padding:10px;border-radius:12px;min-height:84px}.pdx-grid.roomy .pdx-card{padding:16px;border-radius:16px;min-height:100px}.pdx-card:focus,.pdx-card:hover{box-shadow:var(--mat-elevation-level4);background:var(--md-sys-color-surface-container-low);border-color:var(--md-sys-color-primary)}.pdx-card:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-card:active{transform:translateY(1px)}.pdx-card-icon{grid-row:span 2;display:flex;align-items:center;justify-content:center;color:var(--md-sys-color-primary)}.pdx-card-title{font-weight:600;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pdx-card-desc{color:var(--md-sys-color-on-surface-variant);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}.pdx-card-badges{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.pdx-card-badge{display:inline-flex;align-items:center;gap:4px;min-height:22px;padding:2px 8px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container-highest);font-size:11px;line-height:1.2;font-weight:600;letter-spacing:.01em}.pdx-card-badge mat-icon{width:14px;height:14px;font-size:14px}.pdx-card-badge.ready{color:var(--md-sys-color-primary);border-color:color-mix(in srgb,var(--md-sys-color-primary) 45%,transparent);background:color-mix(in srgb,var(--md-sys-color-primary-container) 72%,transparent)}.pdx-card-badge.info{color:var(--md-sys-color-tertiary);border-color:color-mix(in srgb,var(--md-sys-color-tertiary) 38%,transparent);background:color-mix(in srgb,var(--md-sys-color-tertiary-container) 68%,transparent)}.pdx-card-badge.warning{color:var(--md-sys-color-error);border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,transparent);background:color-mix(in srgb,var(--md-sys-color-error-container) 62%,transparent)}.pdx-empty{padding:24px;display:grid;place-items:center;color:var(--md-sys-color-on-surface-variant);gap:8px}@media(max-width:600px){.pdx-palette-content{min-width:320px}.pdx-grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2015
2163
  }
2016
2164
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ComponentPaletteDialogComponent, decorators: [{
2017
2165
  type: Component,
@@ -2091,6 +2239,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
2091
2239
  args: [MAT_DIALOG_DATA]
2092
2240
  }] }] });
2093
2241
 
2242
+ /** Browser syntax validation only; token resolution and URL policy belong to the host. */
2243
+ function cssDeclarationValidator(property) {
2244
+ return (control) => {
2245
+ const value = control.value;
2246
+ if (value == null || value === '')
2247
+ return null;
2248
+ if (typeof value !== 'string')
2249
+ return { cssDeclaration: true };
2250
+ if (!value.trim())
2251
+ return null;
2252
+ // SSR has no CSS parser or interactive authoring. Validate on browser hydration.
2253
+ if (typeof CSS === 'undefined' || typeof CSS.supports !== 'function')
2254
+ return null;
2255
+ return CSS.supports(property, value.trim()) ? null : { cssDeclaration: true };
2256
+ };
2257
+ }
2258
+
2094
2259
  const BUILTIN_PRESET_LABELS = {
2095
2260
  'dark-glass': 'Dark Glass',
2096
2261
  'light-neutral': 'Light Neutral',
@@ -2316,6 +2481,14 @@ const SET_INPUT_PRESETS = {
2316
2481
  };
2317
2482
  class WidgetShellEditorComponent {
2318
2483
  registry;
2484
+ i18n = inject(PraxisI18nService);
2485
+ menuPlacementLabel() { return this.i18n.t('praxis.pageBuilder.shell.menuPlacement'); }
2486
+ appearanceError() {
2487
+ return this.i18n.t('praxis.pageBuilder.shell.invalidCss');
2488
+ }
2489
+ stickyHeaderLabel() {
2490
+ return this.i18n.t('praxis.pageBuilder.shell.stickyHeader');
2491
+ }
2319
2492
  form = new FormGroup({
2320
2493
  kind: new FormControl('dashboard-card', {
2321
2494
  nonNullable: true,
@@ -2328,6 +2501,7 @@ class WidgetShellEditorComponent {
2328
2501
  showHeader: new FormControl('auto', {
2329
2502
  nonNullable: true,
2330
2503
  }),
2504
+ stickyHeader: new FormControl(false, { nonNullable: true }),
2331
2505
  collapsible: new FormControl(true, { nonNullable: true }),
2332
2506
  cardBg: new FormControl('', { nonNullable: true }),
2333
2507
  cardBorder: new FormControl('', { nonNullable: true }),
@@ -2379,6 +2553,18 @@ class WidgetShellEditorComponent {
2379
2553
  constructor(data, registry) {
2380
2554
  this.registry = registry;
2381
2555
  this.authoringCapabilities = data?.authoringCapabilities ?? null;
2556
+ if (this.canEditShellAppearance()) {
2557
+ const properties = {
2558
+ cardBg: 'background', cardBorder: 'border-color', cardRadius: 'border-radius',
2559
+ cardShadow: 'box-shadow', headerBg: 'background', headerBorder: 'border-color',
2560
+ titleColor: 'color', subtitleColor: 'color', iconColor: 'color',
2561
+ bodyBg: 'background', bodyColor: 'color', bodyPadding: 'padding',
2562
+ titleSize: 'font-size', titleWeight: 'font-weight', subtitleSize: 'font-size',
2563
+ };
2564
+ for (const name of Object.keys(properties)) {
2565
+ this.form.controls[name].addValidators(cssDeclarationValidator(properties[name]));
2566
+ }
2567
+ }
2382
2568
  const customPresets = data?.presets || {};
2383
2569
  const customOptions = Object.keys(customPresets).map((id) => ({
2384
2570
  id,
@@ -2441,10 +2627,14 @@ class WidgetShellEditorComponent {
2441
2627
  this.actions.removeAt(index);
2442
2628
  }
2443
2629
  getSettingsValue() {
2630
+ if (this.form.invalid) {
2631
+ this.form.markAllAsTouched();
2632
+ throw new Error(this.i18n.t('praxis.pageBuilder.editor.status.review'));
2633
+ }
2444
2634
  return this.buildResult();
2445
2635
  }
2446
2636
  onSave() {
2447
- return this.buildResult();
2637
+ return this.getSettingsValue();
2448
2638
  }
2449
2639
  reset() {
2450
2640
  const shell = this.initialShell.value;
@@ -2469,6 +2659,7 @@ class WidgetShellEditorComponent {
2469
2659
  subtitle: this.editableText(shell?.subtitle),
2470
2660
  icon: shell?.icon || '',
2471
2661
  showHeader,
2662
+ stickyHeader: shell?.stickyHeader === true,
2472
2663
  collapsible: window.collapsible !== false,
2473
2664
  cardBg: card.background || '',
2474
2665
  cardBorder: card.borderColor || '',
@@ -2570,6 +2761,7 @@ class WidgetShellEditorComponent {
2570
2761
  delete shell.subtitle;
2571
2762
  delete shell.icon;
2572
2763
  delete shell.showHeader;
2764
+ delete shell.stickyHeader;
2573
2765
  if (raw.preset)
2574
2766
  shell.preset = raw.preset;
2575
2767
  const title = this.preserveTextDescriptor(this.initialTitle, raw.title);
@@ -2582,6 +2774,8 @@ class WidgetShellEditorComponent {
2582
2774
  shell.icon = raw.icon.trim();
2583
2775
  if (raw.showHeader !== 'auto')
2584
2776
  shell.showHeader = raw.showHeader === 'true';
2777
+ if (raw.stickyHeader)
2778
+ shell.stickyHeader = true;
2585
2779
  const windowActions = {
2586
2780
  ...(initial?.windowActions || {}),
2587
2781
  };
@@ -2766,7 +2960,7 @@ class WidgetShellEditorComponent {
2766
2960
  }
2767
2961
  previewOverflowCount() {
2768
2962
  const actions = this.previewActions().filter((a) => (a.placement || 'header') === 'header');
2769
- return Math.max(0, actions.length - 3);
2963
+ return Math.max(0, actions.length - 3) + this.previewActions().filter(a => a.placement === 'menu').length;
2770
2964
  }
2771
2965
  previewWindowActions() {
2772
2966
  return this.previewActions().filter((a) => (a.placement || 'header') === 'window');
@@ -2985,7 +3179,7 @@ class WidgetShellEditorComponent {
2985
3179
  }
2986
3180
  }
2987
3181
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: WidgetShellEditorComponent, deps: [{ token: SETTINGS_PANEL_DATA, optional: true }, { token: i2$2.ComponentMetadataRegistry, optional: true }], target: i0.ɵɵFactoryTarget.Component });
2988
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: WidgetShellEditorComponent, isStandalone: true, selector: "praxis-widget-shell-editor", ngImport: i0, template: `
3182
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: WidgetShellEditorComponent, isStandalone: true, selector: "praxis-widget-shell-editor", providers: [providePraxisPageBuilderI18n()], ngImport: i0, template: `
2989
3183
  <div class="shell-editor">
2990
3184
  @if (
2991
3185
  canEditShellIdentity() &&
@@ -3054,6 +3248,9 @@ class WidgetShellEditorComponent {
3054
3248
  <mat-option value="false">Nunca</mat-option>
3055
3249
  </mat-select>
3056
3250
  </mat-form-field>
3251
+ <mat-checkbox [formControl]="form.controls.stickyHeader">
3252
+ {{ stickyHeaderLabel() }}
3253
+ </mat-checkbox>
3057
3254
  </div>
3058
3255
 
3059
3256
  <div class="shell-section">Controles de janela</div>
@@ -3069,125 +3266,170 @@ class WidgetShellEditorComponent {
3069
3266
 
3070
3267
  <div class="shell-section">Aparência</div>
3071
3268
  <div class="shell-grid">
3072
- <mat-form-field appearance="outline">
3269
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3073
3270
  <mat-label>Fundo do card</mat-label>
3074
3271
  <input
3075
3272
  matInput
3076
3273
  [formControl]="form.controls.cardBg"
3077
3274
  placeholder="ex: #0b1220"
3078
3275
  />
3276
+ @if (form.controls.cardBg.hasError('cssDeclaration')) {
3277
+ <mat-error>{{ appearanceError() }}</mat-error>
3278
+ }
3079
3279
  </mat-form-field>
3080
- <mat-form-field appearance="outline">
3280
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3081
3281
  <mat-label>Cor da borda</mat-label>
3082
3282
  <input
3083
3283
  matInput
3084
3284
  [formControl]="form.controls.cardBorder"
3085
3285
  placeholder="ex: rgba(255,255,255,0.12)"
3086
3286
  />
3287
+ @if (form.controls.cardBorder.hasError('cssDeclaration')) {
3288
+ <mat-error>{{ appearanceError() }}</mat-error>
3289
+ }
3087
3290
  </mat-form-field>
3088
- <mat-form-field appearance="outline">
3291
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3089
3292
  <mat-label>Raio da borda</mat-label>
3090
3293
  <input
3091
3294
  matInput
3092
3295
  [formControl]="form.controls.cardRadius"
3093
3296
  placeholder="ex: 14px"
3094
3297
  />
3298
+ @if (form.controls.cardRadius.hasError('cssDeclaration')) {
3299
+ <mat-error>{{ appearanceError() }}</mat-error>
3300
+ }
3095
3301
  </mat-form-field>
3096
- <mat-form-field appearance="outline">
3302
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3097
3303
  <mat-label>Sombra</mat-label>
3098
3304
  <input
3099
3305
  matInput
3100
3306
  [formControl]="form.controls.cardShadow"
3101
3307
  placeholder="ex: 0 10px 30px rgba(0,0,0,0.2)"
3102
3308
  />
3309
+ @if (form.controls.cardShadow.hasError('cssDeclaration')) {
3310
+ <mat-error>{{ appearanceError() }}</mat-error>
3311
+ }
3103
3312
  </mat-form-field>
3104
- <mat-form-field appearance="outline">
3313
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3105
3314
  <mat-label>Fundo do cabeçalho</mat-label>
3106
3315
  <input
3107
3316
  matInput
3108
3317
  [formControl]="form.controls.headerBg"
3109
3318
  placeholder="ex: linear-gradient(...)"
3110
3319
  />
3320
+ @if (form.controls.headerBg.hasError('cssDeclaration')) {
3321
+ <mat-error>{{ appearanceError() }}</mat-error>
3322
+ }
3111
3323
  </mat-form-field>
3112
- <mat-form-field appearance="outline">
3324
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3113
3325
  <mat-label>Borda do cabeçalho</mat-label>
3114
3326
  <input
3115
3327
  matInput
3116
3328
  [formControl]="form.controls.headerBorder"
3117
3329
  placeholder="ex: rgba(255,255,255,0.08)"
3118
3330
  />
3331
+ @if (form.controls.headerBorder.hasError('cssDeclaration')) {
3332
+ <mat-error>{{ appearanceError() }}</mat-error>
3333
+ }
3119
3334
  </mat-form-field>
3120
- <mat-form-field appearance="outline">
3335
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3121
3336
  <mat-label>Cor do título</mat-label>
3122
3337
  <input
3123
3338
  matInput
3124
3339
  [formControl]="form.controls.titleColor"
3125
3340
  placeholder="ex: #e6edf3"
3126
3341
  />
3342
+ @if (form.controls.titleColor.hasError('cssDeclaration')) {
3343
+ <mat-error>{{ appearanceError() }}</mat-error>
3344
+ }
3127
3345
  </mat-form-field>
3128
- <mat-form-field appearance="outline">
3346
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3129
3347
  <mat-label>Cor do subtítulo</mat-label>
3130
3348
  <input
3131
3349
  matInput
3132
3350
  [formControl]="form.controls.subtitleColor"
3133
3351
  placeholder="ex: #9fb0c4"
3134
3352
  />
3353
+ @if (form.controls.subtitleColor.hasError('cssDeclaration')) {
3354
+ <mat-error>{{ appearanceError() }}</mat-error>
3355
+ }
3135
3356
  </mat-form-field>
3136
- <mat-form-field appearance="outline">
3357
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3137
3358
  <mat-label>Cor do ícone</mat-label>
3138
3359
  <input
3139
3360
  matInput
3140
3361
  [formControl]="form.controls.iconColor"
3141
3362
  placeholder="ex: #8ab4ff"
3142
3363
  />
3364
+ @if (form.controls.iconColor.hasError('cssDeclaration')) {
3365
+ <mat-error>{{ appearanceError() }}</mat-error>
3366
+ }
3143
3367
  </mat-form-field>
3144
- <mat-form-field appearance="outline">
3368
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3145
3369
  <mat-label>Fundo do corpo</mat-label>
3146
3370
  <input
3147
3371
  matInput
3148
3372
  [formControl]="form.controls.bodyBg"
3149
3373
  placeholder="ex: transparent"
3150
3374
  />
3375
+ @if (form.controls.bodyBg.hasError('cssDeclaration')) {
3376
+ <mat-error>{{ appearanceError() }}</mat-error>
3377
+ }
3151
3378
  </mat-form-field>
3152
- <mat-form-field appearance="outline">
3379
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3153
3380
  <mat-label>Cor do texto do corpo</mat-label>
3154
3381
  <input
3155
3382
  matInput
3156
3383
  [formControl]="form.controls.bodyColor"
3157
3384
  placeholder="ex: #e6edf3"
3158
3385
  />
3386
+ @if (form.controls.bodyColor.hasError('cssDeclaration')) {
3387
+ <mat-error>{{ appearanceError() }}</mat-error>
3388
+ }
3159
3389
  </mat-form-field>
3160
- <mat-form-field appearance="outline">
3390
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3161
3391
  <mat-label>Padding do corpo</mat-label>
3162
3392
  <input
3163
3393
  matInput
3164
3394
  [formControl]="form.controls.bodyPadding"
3165
3395
  placeholder="ex: 10px 12px 12px"
3166
3396
  />
3397
+ @if (form.controls.bodyPadding.hasError('cssDeclaration')) {
3398
+ <mat-error>{{ appearanceError() }}</mat-error>
3399
+ }
3167
3400
  </mat-form-field>
3168
- <mat-form-field appearance="outline">
3401
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3169
3402
  <mat-label>Tamanho do título</mat-label>
3170
3403
  <input
3171
3404
  matInput
3172
3405
  [formControl]="form.controls.titleSize"
3173
3406
  placeholder="ex: 14px"
3174
3407
  />
3408
+ @if (form.controls.titleSize.hasError('cssDeclaration')) {
3409
+ <mat-error>{{ appearanceError() }}</mat-error>
3410
+ }
3175
3411
  </mat-form-field>
3176
- <mat-form-field appearance="outline">
3412
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3177
3413
  <mat-label>Peso do título</mat-label>
3178
3414
  <input
3179
3415
  matInput
3180
3416
  [formControl]="form.controls.titleWeight"
3181
3417
  placeholder="ex: 600"
3182
3418
  />
3419
+ @if (form.controls.titleWeight.hasError('cssDeclaration')) {
3420
+ <mat-error>{{ appearanceError() }}</mat-error>
3421
+ }
3183
3422
  </mat-form-field>
3184
- <mat-form-field appearance="outline">
3423
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3185
3424
  <mat-label>Tamanho do subtítulo</mat-label>
3186
3425
  <input
3187
3426
  matInput
3188
3427
  [formControl]="form.controls.subtitleSize"
3189
3428
  placeholder="ex: 12px"
3190
3429
  />
3430
+ @if (form.controls.subtitleSize.hasError('cssDeclaration')) {
3431
+ <mat-error>{{ appearanceError() }}</mat-error>
3432
+ }
3191
3433
  </mat-form-field>
3192
3434
  </div>
3193
3435
  }
@@ -3472,6 +3714,7 @@ class WidgetShellEditorComponent {
3472
3714
  <mat-select formControlName="placement">
3473
3715
  <mat-option value="header">Cabeçalho</mat-option>
3474
3716
  <mat-option value="window">Janela</mat-option>
3717
+ <mat-option value="menu">{{ menuPlacementLabel() }}</mat-option>
3475
3718
  </mat-select>
3476
3719
  </mat-form-field>
3477
3720
  <mat-form-field appearance="outline">
@@ -3533,11 +3776,11 @@ class WidgetShellEditorComponent {
3533
3776
  <div class="shell-empty">Nenhuma ação configurada.</div>
3534
3777
  }
3535
3778
  </div>
3536
- `, isInline: true, styles: [".shell-editor{display:grid;gap:20px;padding:12px 4px 20px}.shell-intro{margin:0;max-width:68ch;color:var(--md-sys-color-on-surface-variant);font-size:14px;line-height:1.5}.shell-section{margin:4px 0 -8px;color:var(--md-sys-color-on-surface);font-size:14px;font-weight:650;line-height:1.4}.shell-grid{display:grid;gap:12px 16px;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}.shell-field--wide{grid-column:1 / -1}.shell-flags{display:flex;gap:16px;align-items:center}.shell-actions-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.shell-available{display:grid;gap:12px}.shell-available-toolbar{display:grid;gap:12px;grid-template-columns:minmax(200px,1fr)}.shell-available-filters{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.shell-available-list{display:grid;gap:10px}.shell-available-item{display:grid;gap:8px;grid-template-columns:1fr auto;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;background:var(--md-sys-color-surface-container-low)}.shell-available-title{display:flex;align-items:center;gap:10px}.shell-available-text{display:grid;gap:4px}.shell-available-label{font-weight:600}.shell-available-meta{display:flex;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-desc{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-available-payload{font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-warning{font-size:11px;color:var(--md-sys-color-error)}.shell-available-actions{display:flex;align-items:flex-start;justify-content:flex-end}.shell-badge{padding:2px 6px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);font-size:10px}.shell-code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.shell-actions{display:grid;gap:12px}.shell-action{border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;display:grid;gap:8px}.shell-action-row{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.shell-action-actions{display:flex;justify-content:flex-end}.shell-action-hint,.shell-empty{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-hint{align-self:center;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-quick-inputs{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));align-items:end}.shell-input-path{font-size:12px;color:var(--md-sys-color-on-surface-variant);align-self:center}.shell-editor .mat-mdc-form-field{width:100%}.shell-preview{border:1px dashed var(--md-sys-color-outline-variant);border-radius:12px;padding:12px}.shell-preview-card{background:var(--pdx-shell-card-bg, var(--md-sys-color-surface));border:1px solid var(--pdx-shell-card-border, var(--md-sys-color-outline-variant));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.shell-preview-card.no-shell{background:transparent;border:none;box-shadow:none}.shell-preview-card.expanded{box-shadow:var( --pdx-shell-card-shadow, 0 16px 34px rgba(0, 0, 0, .32) );transform:scale(1.01)}.shell-preview-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var( --pdx-shell-header-bg, var(--md-sys-color-surface-container) )}.shell-preview-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.shell-preview-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.shell-preview-text{min-width:0}.shell-preview-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-actions{display:flex;align-items:center;gap:6px}.pdx-preview-text{padding:0 8px}.pdx-preview-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-preview-label{font-size:12px;font-weight:500}.shell-preview-body{padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit);font-size:12px}.shell-preview-body.hidden{display:none}@media(max-width:640px){.shell-editor{padding-inline:0}.shell-grid{grid-template-columns:minmax(0,1fr)}.shell-field--wide{grid-column:auto}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i5$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i6.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3779
+ `, isInline: true, styles: [".shell-editor{display:grid;grid-template-columns:minmax(0,1fr);min-width:0;gap:20px;padding:12px 4px 20px}.shell-intro{margin:0;max-width:68ch;color:var(--md-sys-color-on-surface-variant);font-size:14px;line-height:1.5}.shell-section{margin:4px 0 -8px;color:var(--md-sys-color-on-surface);font-size:14px;font-weight:650;line-height:1.4}.shell-grid{display:grid;gap:12px 16px;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}.shell-field--wide{grid-column:1 / -1}.shell-flags{display:flex;gap:16px;align-items:center}.shell-actions-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.shell-available{display:grid;gap:12px}.shell-available-toolbar{display:grid;gap:12px;grid-template-columns:minmax(200px,1fr)}.shell-available-filters{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.shell-available-list{display:grid;gap:10px}.shell-available-item{display:grid;gap:8px;grid-template-columns:minmax(0,1fr) auto;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;background:var(--md-sys-color-surface-container-low)}.shell-available-title{display:flex;align-items:center;gap:10px}.shell-available-text{display:grid;gap:4px}.shell-available-label{font-weight:600}.shell-available-meta{display:flex;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-desc{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-available-payload{font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-warning{font-size:11px;color:var(--md-sys-color-error)}.shell-available-actions{display:flex;align-items:flex-start;justify-content:flex-end}.shell-badge{padding:2px 6px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);font-size:10px}.shell-code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.shell-actions{display:grid;gap:12px}.shell-action{border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;display:grid;gap:8px}.shell-action-row{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.shell-action-actions{display:flex;justify-content:flex-end}.shell-action-hint,.shell-empty{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-hint{align-self:center;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-quick-inputs{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));align-items:end}.shell-input-path{font-size:12px;color:var(--md-sys-color-on-surface-variant);align-self:center}.shell-editor .mat-mdc-form-field{width:100%;min-width:0}:host{display:block;min-width:0}.shell-editor>*,.shell-action>*{min-width:0}.shell-available-text,.shell-input-path{min-width:0;overflow-wrap:anywhere}.shell-preview{border:1px dashed var(--md-sys-color-outline-variant);border-radius:12px;padding:12px}.shell-preview-card{background:var(--pdx-shell-card-bg, var(--md-sys-color-surface));border:1px solid var(--pdx-shell-card-border, var(--md-sys-color-outline-variant));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.shell-preview-card.no-shell{background:transparent;border:none;box-shadow:none}.shell-preview-card.expanded{box-shadow:var( --pdx-shell-card-shadow, 0 16px 34px rgba(0, 0, 0, .32) );transform:scale(1.01)}.shell-preview-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var( --pdx-shell-header-bg, var(--md-sys-color-surface-container) )}.shell-preview-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.shell-preview-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.shell-preview-text{min-width:0}.shell-preview-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-actions{display:flex;align-items:center;gap:6px}.pdx-preview-text{padding:0 8px}.pdx-preview-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-preview-label{font-size:12px;font-weight:500}.shell-preview-body{padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit);font-size:12px}.shell-preview-body.hidden{display:none}@media(max-width:640px){.shell-editor{padding-inline:0}.shell-grid,.shell-action-row,.shell-quick-inputs,.shell-available-toolbar{grid-template-columns:minmax(0,1fr)}.shell-actions-head,.shell-flags{flex-wrap:wrap}.shell-field--wide{grid-column:auto}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i5$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i6.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i10.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i10.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3537
3780
  }
3538
3781
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: WidgetShellEditorComponent, decorators: [{
3539
3782
  type: Component,
3540
- args: [{ selector: 'praxis-widget-shell-editor', standalone: true, imports: [
3783
+ args: [{ selector: 'praxis-widget-shell-editor', providers: [providePraxisPageBuilderI18n()], standalone: true, imports: [
3541
3784
  CommonModule,
3542
3785
  FormsModule,
3543
3786
  ReactiveFormsModule,
@@ -3619,6 +3862,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3619
3862
  <mat-option value="false">Nunca</mat-option>
3620
3863
  </mat-select>
3621
3864
  </mat-form-field>
3865
+ <mat-checkbox [formControl]="form.controls.stickyHeader">
3866
+ {{ stickyHeaderLabel() }}
3867
+ </mat-checkbox>
3622
3868
  </div>
3623
3869
 
3624
3870
  <div class="shell-section">Controles de janela</div>
@@ -3634,125 +3880,170 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3634
3880
 
3635
3881
  <div class="shell-section">Aparência</div>
3636
3882
  <div class="shell-grid">
3637
- <mat-form-field appearance="outline">
3883
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3638
3884
  <mat-label>Fundo do card</mat-label>
3639
3885
  <input
3640
3886
  matInput
3641
3887
  [formControl]="form.controls.cardBg"
3642
3888
  placeholder="ex: #0b1220"
3643
3889
  />
3890
+ @if (form.controls.cardBg.hasError('cssDeclaration')) {
3891
+ <mat-error>{{ appearanceError() }}</mat-error>
3892
+ }
3644
3893
  </mat-form-field>
3645
- <mat-form-field appearance="outline">
3894
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3646
3895
  <mat-label>Cor da borda</mat-label>
3647
3896
  <input
3648
3897
  matInput
3649
3898
  [formControl]="form.controls.cardBorder"
3650
3899
  placeholder="ex: rgba(255,255,255,0.12)"
3651
3900
  />
3901
+ @if (form.controls.cardBorder.hasError('cssDeclaration')) {
3902
+ <mat-error>{{ appearanceError() }}</mat-error>
3903
+ }
3652
3904
  </mat-form-field>
3653
- <mat-form-field appearance="outline">
3905
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3654
3906
  <mat-label>Raio da borda</mat-label>
3655
3907
  <input
3656
3908
  matInput
3657
3909
  [formControl]="form.controls.cardRadius"
3658
3910
  placeholder="ex: 14px"
3659
3911
  />
3912
+ @if (form.controls.cardRadius.hasError('cssDeclaration')) {
3913
+ <mat-error>{{ appearanceError() }}</mat-error>
3914
+ }
3660
3915
  </mat-form-field>
3661
- <mat-form-field appearance="outline">
3916
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3662
3917
  <mat-label>Sombra</mat-label>
3663
3918
  <input
3664
3919
  matInput
3665
3920
  [formControl]="form.controls.cardShadow"
3666
3921
  placeholder="ex: 0 10px 30px rgba(0,0,0,0.2)"
3667
3922
  />
3923
+ @if (form.controls.cardShadow.hasError('cssDeclaration')) {
3924
+ <mat-error>{{ appearanceError() }}</mat-error>
3925
+ }
3668
3926
  </mat-form-field>
3669
- <mat-form-field appearance="outline">
3927
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3670
3928
  <mat-label>Fundo do cabeçalho</mat-label>
3671
3929
  <input
3672
3930
  matInput
3673
3931
  [formControl]="form.controls.headerBg"
3674
3932
  placeholder="ex: linear-gradient(...)"
3675
3933
  />
3934
+ @if (form.controls.headerBg.hasError('cssDeclaration')) {
3935
+ <mat-error>{{ appearanceError() }}</mat-error>
3936
+ }
3676
3937
  </mat-form-field>
3677
- <mat-form-field appearance="outline">
3938
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3678
3939
  <mat-label>Borda do cabeçalho</mat-label>
3679
3940
  <input
3680
3941
  matInput
3681
3942
  [formControl]="form.controls.headerBorder"
3682
3943
  placeholder="ex: rgba(255,255,255,0.08)"
3683
3944
  />
3945
+ @if (form.controls.headerBorder.hasError('cssDeclaration')) {
3946
+ <mat-error>{{ appearanceError() }}</mat-error>
3947
+ }
3684
3948
  </mat-form-field>
3685
- <mat-form-field appearance="outline">
3949
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3686
3950
  <mat-label>Cor do título</mat-label>
3687
3951
  <input
3688
3952
  matInput
3689
3953
  [formControl]="form.controls.titleColor"
3690
3954
  placeholder="ex: #e6edf3"
3691
3955
  />
3956
+ @if (form.controls.titleColor.hasError('cssDeclaration')) {
3957
+ <mat-error>{{ appearanceError() }}</mat-error>
3958
+ }
3692
3959
  </mat-form-field>
3693
- <mat-form-field appearance="outline">
3960
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3694
3961
  <mat-label>Cor do subtítulo</mat-label>
3695
3962
  <input
3696
3963
  matInput
3697
3964
  [formControl]="form.controls.subtitleColor"
3698
3965
  placeholder="ex: #9fb0c4"
3699
3966
  />
3967
+ @if (form.controls.subtitleColor.hasError('cssDeclaration')) {
3968
+ <mat-error>{{ appearanceError() }}</mat-error>
3969
+ }
3700
3970
  </mat-form-field>
3701
- <mat-form-field appearance="outline">
3971
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3702
3972
  <mat-label>Cor do ícone</mat-label>
3703
3973
  <input
3704
3974
  matInput
3705
3975
  [formControl]="form.controls.iconColor"
3706
3976
  placeholder="ex: #8ab4ff"
3707
3977
  />
3978
+ @if (form.controls.iconColor.hasError('cssDeclaration')) {
3979
+ <mat-error>{{ appearanceError() }}</mat-error>
3980
+ }
3708
3981
  </mat-form-field>
3709
- <mat-form-field appearance="outline">
3982
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3710
3983
  <mat-label>Fundo do corpo</mat-label>
3711
3984
  <input
3712
3985
  matInput
3713
3986
  [formControl]="form.controls.bodyBg"
3714
3987
  placeholder="ex: transparent"
3715
3988
  />
3989
+ @if (form.controls.bodyBg.hasError('cssDeclaration')) {
3990
+ <mat-error>{{ appearanceError() }}</mat-error>
3991
+ }
3716
3992
  </mat-form-field>
3717
- <mat-form-field appearance="outline">
3993
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3718
3994
  <mat-label>Cor do texto do corpo</mat-label>
3719
3995
  <input
3720
3996
  matInput
3721
3997
  [formControl]="form.controls.bodyColor"
3722
3998
  placeholder="ex: #e6edf3"
3723
3999
  />
4000
+ @if (form.controls.bodyColor.hasError('cssDeclaration')) {
4001
+ <mat-error>{{ appearanceError() }}</mat-error>
4002
+ }
3724
4003
  </mat-form-field>
3725
- <mat-form-field appearance="outline">
4004
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3726
4005
  <mat-label>Padding do corpo</mat-label>
3727
4006
  <input
3728
4007
  matInput
3729
4008
  [formControl]="form.controls.bodyPadding"
3730
4009
  placeholder="ex: 10px 12px 12px"
3731
4010
  />
4011
+ @if (form.controls.bodyPadding.hasError('cssDeclaration')) {
4012
+ <mat-error>{{ appearanceError() }}</mat-error>
4013
+ }
3732
4014
  </mat-form-field>
3733
- <mat-form-field appearance="outline">
4015
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3734
4016
  <mat-label>Tamanho do título</mat-label>
3735
4017
  <input
3736
4018
  matInput
3737
4019
  [formControl]="form.controls.titleSize"
3738
4020
  placeholder="ex: 14px"
3739
4021
  />
4022
+ @if (form.controls.titleSize.hasError('cssDeclaration')) {
4023
+ <mat-error>{{ appearanceError() }}</mat-error>
4024
+ }
3740
4025
  </mat-form-field>
3741
- <mat-form-field appearance="outline">
4026
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3742
4027
  <mat-label>Peso do título</mat-label>
3743
4028
  <input
3744
4029
  matInput
3745
4030
  [formControl]="form.controls.titleWeight"
3746
4031
  placeholder="ex: 600"
3747
4032
  />
4033
+ @if (form.controls.titleWeight.hasError('cssDeclaration')) {
4034
+ <mat-error>{{ appearanceError() }}</mat-error>
4035
+ }
3748
4036
  </mat-form-field>
3749
- <mat-form-field appearance="outline">
4037
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
3750
4038
  <mat-label>Tamanho do subtítulo</mat-label>
3751
4039
  <input
3752
4040
  matInput
3753
4041
  [formControl]="form.controls.subtitleSize"
3754
4042
  placeholder="ex: 12px"
3755
4043
  />
4044
+ @if (form.controls.subtitleSize.hasError('cssDeclaration')) {
4045
+ <mat-error>{{ appearanceError() }}</mat-error>
4046
+ }
3756
4047
  </mat-form-field>
3757
4048
  </div>
3758
4049
  }
@@ -4037,6 +4328,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
4037
4328
  <mat-select formControlName="placement">
4038
4329
  <mat-option value="header">Cabeçalho</mat-option>
4039
4330
  <mat-option value="window">Janela</mat-option>
4331
+ <mat-option value="menu">{{ menuPlacementLabel() }}</mat-option>
4040
4332
  </mat-select>
4041
4333
  </mat-form-field>
4042
4334
  <mat-form-field appearance="outline">
@@ -4098,7 +4390,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
4098
4390
  <div class="shell-empty">Nenhuma ação configurada.</div>
4099
4391
  }
4100
4392
  </div>
4101
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".shell-editor{display:grid;gap:20px;padding:12px 4px 20px}.shell-intro{margin:0;max-width:68ch;color:var(--md-sys-color-on-surface-variant);font-size:14px;line-height:1.5}.shell-section{margin:4px 0 -8px;color:var(--md-sys-color-on-surface);font-size:14px;font-weight:650;line-height:1.4}.shell-grid{display:grid;gap:12px 16px;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}.shell-field--wide{grid-column:1 / -1}.shell-flags{display:flex;gap:16px;align-items:center}.shell-actions-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.shell-available{display:grid;gap:12px}.shell-available-toolbar{display:grid;gap:12px;grid-template-columns:minmax(200px,1fr)}.shell-available-filters{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.shell-available-list{display:grid;gap:10px}.shell-available-item{display:grid;gap:8px;grid-template-columns:1fr auto;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;background:var(--md-sys-color-surface-container-low)}.shell-available-title{display:flex;align-items:center;gap:10px}.shell-available-text{display:grid;gap:4px}.shell-available-label{font-weight:600}.shell-available-meta{display:flex;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-desc{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-available-payload{font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-warning{font-size:11px;color:var(--md-sys-color-error)}.shell-available-actions{display:flex;align-items:flex-start;justify-content:flex-end}.shell-badge{padding:2px 6px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);font-size:10px}.shell-code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.shell-actions{display:grid;gap:12px}.shell-action{border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;display:grid;gap:8px}.shell-action-row{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.shell-action-actions{display:flex;justify-content:flex-end}.shell-action-hint,.shell-empty{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-hint{align-self:center;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-quick-inputs{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));align-items:end}.shell-input-path{font-size:12px;color:var(--md-sys-color-on-surface-variant);align-self:center}.shell-editor .mat-mdc-form-field{width:100%}.shell-preview{border:1px dashed var(--md-sys-color-outline-variant);border-radius:12px;padding:12px}.shell-preview-card{background:var(--pdx-shell-card-bg, var(--md-sys-color-surface));border:1px solid var(--pdx-shell-card-border, var(--md-sys-color-outline-variant));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.shell-preview-card.no-shell{background:transparent;border:none;box-shadow:none}.shell-preview-card.expanded{box-shadow:var( --pdx-shell-card-shadow, 0 16px 34px rgba(0, 0, 0, .32) );transform:scale(1.01)}.shell-preview-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var( --pdx-shell-header-bg, var(--md-sys-color-surface-container) )}.shell-preview-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.shell-preview-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.shell-preview-text{min-width:0}.shell-preview-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-actions{display:flex;align-items:center;gap:6px}.pdx-preview-text{padding:0 8px}.pdx-preview-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-preview-label{font-size:12px;font-weight:500}.shell-preview-body{padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit);font-size:12px}.shell-preview-body.hidden{display:none}@media(max-width:640px){.shell-editor{padding-inline:0}.shell-grid{grid-template-columns:minmax(0,1fr)}.shell-field--wide{grid-column:auto}}\n"] }]
4393
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".shell-editor{display:grid;grid-template-columns:minmax(0,1fr);min-width:0;gap:20px;padding:12px 4px 20px}.shell-intro{margin:0;max-width:68ch;color:var(--md-sys-color-on-surface-variant);font-size:14px;line-height:1.5}.shell-section{margin:4px 0 -8px;color:var(--md-sys-color-on-surface);font-size:14px;font-weight:650;line-height:1.4}.shell-grid{display:grid;gap:12px 16px;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}.shell-field--wide{grid-column:1 / -1}.shell-flags{display:flex;gap:16px;align-items:center}.shell-actions-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.shell-available{display:grid;gap:12px}.shell-available-toolbar{display:grid;gap:12px;grid-template-columns:minmax(200px,1fr)}.shell-available-filters{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.shell-available-list{display:grid;gap:10px}.shell-available-item{display:grid;gap:8px;grid-template-columns:minmax(0,1fr) auto;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;background:var(--md-sys-color-surface-container-low)}.shell-available-title{display:flex;align-items:center;gap:10px}.shell-available-text{display:grid;gap:4px}.shell-available-label{font-weight:600}.shell-available-meta{display:flex;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-desc{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-available-payload{font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-warning{font-size:11px;color:var(--md-sys-color-error)}.shell-available-actions{display:flex;align-items:flex-start;justify-content:flex-end}.shell-badge{padding:2px 6px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);font-size:10px}.shell-code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.shell-actions{display:grid;gap:12px}.shell-action{border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;display:grid;gap:8px}.shell-action-row{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.shell-action-actions{display:flex;justify-content:flex-end}.shell-action-hint,.shell-empty{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-hint{align-self:center;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-quick-inputs{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));align-items:end}.shell-input-path{font-size:12px;color:var(--md-sys-color-on-surface-variant);align-self:center}.shell-editor .mat-mdc-form-field{width:100%;min-width:0}:host{display:block;min-width:0}.shell-editor>*,.shell-action>*{min-width:0}.shell-available-text,.shell-input-path{min-width:0;overflow-wrap:anywhere}.shell-preview{border:1px dashed var(--md-sys-color-outline-variant);border-radius:12px;padding:12px}.shell-preview-card{background:var(--pdx-shell-card-bg, var(--md-sys-color-surface));border:1px solid var(--pdx-shell-card-border, var(--md-sys-color-outline-variant));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.shell-preview-card.no-shell{background:transparent;border:none;box-shadow:none}.shell-preview-card.expanded{box-shadow:var( --pdx-shell-card-shadow, 0 16px 34px rgba(0, 0, 0, .32) );transform:scale(1.01)}.shell-preview-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var( --pdx-shell-header-bg, var(--md-sys-color-surface-container) )}.shell-preview-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.shell-preview-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.shell-preview-text{min-width:0}.shell-preview-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-actions{display:flex;align-items:center;gap:6px}.pdx-preview-text{padding:0 8px}.pdx-preview-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-preview-label{font-size:12px;font-weight:500}.shell-preview-body{padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit);font-size:12px}.shell-preview-body.hidden{display:none}@media(max-width:640px){.shell-editor{padding-inline:0}.shell-grid,.shell-action-row,.shell-quick-inputs,.shell-available-toolbar{grid-template-columns:minmax(0,1fr)}.shell-actions-head,.shell-flags{flex-wrap:wrap}.shell-field--wide{grid-column:auto}}\n"] }]
4102
4394
  }], ctorParameters: () => [{ type: undefined, decorators: [{
4103
4395
  type: Optional
4104
4396
  }, {
@@ -4230,13 +4522,24 @@ function deriveConnectionEditorGraph(page, options = {}) {
4230
4522
  const angle = (index / count) * Math.PI * 2 - Math.PI / 2;
4231
4523
  const widgetId = widget.key;
4232
4524
  const componentType = widget.definition?.id ?? 'unknown';
4233
- const inputs = Object.keys(widget.definition?.inputs ?? {});
4525
+ const metadata = options.componentMetadata?.(componentType);
4526
+ const contracts = (metadata?.ports ?? []).filter(port => port.exposure?.public !== false && !port.exposure?.deprecated);
4527
+ const inputs = uniqueStrings([
4528
+ ...contracts.filter(port => port.direction === 'input').map(port => port.id),
4529
+ ...(contracts.length ? [] : metadata?.inputs ?? []).map(input => input.name),
4530
+ ...Object.keys(widget.definition?.inputs ?? {}),
4531
+ ]);
4234
4532
  const outputs = uniqueStrings([
4533
+ ...contracts.filter(port => port.direction === 'output').map(port => port.id),
4534
+ ...(contracts.length ? [] : metadata?.outputs ?? []).map(output => output.name),
4235
4535
  ...Object.keys(widget.definition?.outputs ?? {}),
4236
4536
  ...configuredWidgetEventOutputs(widget),
4237
4537
  ]);
4238
- const outputPorts = (outputs.length ? outputs : ['change']).map((port) => componentPort(widgetId, port, 'output', componentType));
4239
- const inputPorts = prioritizedInputPorts(inputs).map((port) => componentPort(widgetId, port, 'input', componentType));
4538
+ const outputPorts = (outputs.length || metadata ? outputs : ['change']).map((port) => componentPort(widgetId, port, 'output', componentType));
4539
+ const inputPorts = (metadata ? inputs : prioritizedInputPorts(inputs)).map((port) => componentPort(widgetId, port, 'input', componentType));
4540
+ for (const port of [...inputPorts, ...outputPorts]) {
4541
+ port.contract = contracts.find(contract => contract.id === port.label && contract.direction === port.kind);
4542
+ }
4240
4543
  for (const nested of collectNestedWidgets(widget)) {
4241
4544
  const nestedComponentType = nested.definition.id ?? nested.path.find((segment) => segment.kind === 'widget')?.componentType;
4242
4545
  const nestedPrefix = nested.definition.childWidgetKey
@@ -4251,7 +4554,7 @@ function deriveConnectionEditorGraph(page, options = {}) {
4251
4554
  }
4252
4555
  return {
4253
4556
  id: widgetId,
4254
- title: editableText(widget.shell?.title) || widgetId,
4557
+ title: options.widgetTitle?.(widget) || editableText(widget.shell?.title) || widgetId,
4255
4558
  subtitle: componentType,
4256
4559
  icon: widget.shell?.icon ?? iconForComponentType(componentType),
4257
4560
  x: center.x + Math.cos(angle) * radiusX,
@@ -4350,7 +4653,7 @@ function deriveConnectionEditorGraph(page, options = {}) {
4350
4653
  }
4351
4654
  function editableText(value) {
4352
4655
  if (typeof value === 'string')
4353
- return value;
4656
+ return value.includes('${') ? '' : value;
4354
4657
  return value?.text ?? value?.key ?? '';
4355
4658
  }
4356
4659
  function feedbackDiagnosticsForLinks(links) {
@@ -4530,11 +4833,23 @@ function buildCompositionLinkFromPorts(source, target, links) {
4530
4833
  };
4531
4834
  }
4532
4835
  function inferCanonicalTransform(source, target) {
4836
+ if (source.kind === 'output' && target.kind === 'input'
4837
+ && source.contract?.resourceQueryFieldSource && target.contract?.semanticKind === 'query-context') {
4838
+ return {
4839
+ id: `${source.nodeId}-${target.nodeId}-filter-query-projection`,
4840
+ version: '2.0', phase: 'link-propagation', mode: 'object-fragment',
4841
+ sourceBindings: [{ source: 'event' }],
4842
+ steps: [{ id: 'shape-filter-query', kind: 'object-template', phase: 'link-propagation',
4843
+ input: { source: 'event' }, config: { template: { filters: '${payload}' } } }],
4844
+ output: { semanticKind: 'query-context', cardinality: 'one', stableShape: true, materializationHint: 'replace' },
4845
+ };
4846
+ }
4533
4847
  if (!isTableRowSelectionToFormResourceId(source, target)) {
4534
4848
  return undefined;
4535
4849
  }
4536
4850
  return {
4537
4851
  id: `${source.nodeId}-${target.nodeId}-row-id-projection`,
4852
+ fallbackValue: null,
4538
4853
  version: '2.0',
4539
4854
  phase: 'link-propagation',
4540
4855
  mode: 'single-value',
@@ -4543,7 +4858,8 @@ function inferCanonicalTransform(source, target) {
4543
4858
  id: 'pick-selected-row-id',
4544
4859
  kind: 'pick-path',
4545
4860
  phase: 'link-propagation',
4546
- config: { path: 'payload.row.id' },
4861
+ config: { path: source.endpoint.kind === 'component-port' && source.endpoint.ref.port === 'selectionChange'
4862
+ ? 'payload.selectedRows.0.id' : 'payload.row.id' },
4547
4863
  output: {
4548
4864
  semanticKind: 'selection',
4549
4865
  cardinality: 'one',
@@ -4571,9 +4887,9 @@ function isTableRowSelectionToFormResourceId(source, target) {
4571
4887
  const targetComponent = String(target.endpoint.ref.componentType ?? '').toLowerCase();
4572
4888
  const sourcePort = source.endpoint.ref.port.toLowerCase();
4573
4889
  const targetPort = target.endpoint.ref.port.toLowerCase();
4574
- return sourceComponent.includes('table')
4575
- && targetComponent.includes('dynamic-form')
4576
- && /^(rowclick|rowselection|selectionchange)$/.test(sourcePort)
4890
+ return sourceComponent === 'praxis-table'
4891
+ && targetComponent === 'praxis-dynamic-form'
4892
+ && ['rowclick', 'selectionchange'].includes(sourcePort)
4577
4893
  && targetPort === 'resourceid';
4578
4894
  }
4579
4895
  function deriveConnectionEditorSuggestions(graph, limit = 5) {
@@ -4593,6 +4909,7 @@ function buildConnectionEditorSuggestions(graph) {
4593
4909
  const targets = uniquePorts$1(graph.nodes.flatMap((node) => [...node.inputs, ...node.states]));
4594
4910
  return sources.flatMap((source) => targets
4595
4911
  .filter((target) => canConnectPorts(source, target))
4912
+ .filter((target) => !graph.connections.some(connection => connection.toPort && endpointKey(connection.toPort.endpoint) === endpointKey(target.endpoint)))
4596
4913
  .filter((target) => !connectedPairs.has(`${endpointKey(source.endpoint)}->${endpointKey(target.endpoint)}`))
4597
4914
  .map((target) => {
4598
4915
  const score = suggestionScore(source, target);
@@ -4607,6 +4924,7 @@ function buildConnectionEditorSuggestions(graph) {
4607
4924
  score,
4608
4925
  };
4609
4926
  }))
4927
+ .filter(suggestion => suggestion.score > 0)
4610
4928
  .sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
4611
4929
  }
4612
4930
  function uniqueConnectionLinkId(source, target, links) {
@@ -4622,35 +4940,16 @@ function uniqueConnectionLinkId(source, target, links) {
4622
4940
  }
4623
4941
  return candidate;
4624
4942
  }
4943
+ // Rank only explicit endpoint structure and the existing table/detail projection recipe.
4944
+ // This is a deterministic wiring shortcut, never primary user-intent resolution or AI advice.
4625
4945
  function suggestionScore(source, target) {
4626
- let score = source.nodeId === target.nodeId ? 6 : 18;
4627
- const sourceLabel = source.label.toLowerCase();
4628
- const targetLabel = target.label.toLowerCase();
4629
- if (source.kind === 'state' && target.kind === 'input')
4630
- score += 6;
4631
- if (source.kind === 'output' && target.kind === 'state')
4632
- score += 2;
4633
- if (source.kind === 'output' && target.kind === 'input')
4634
- score += 10;
4635
- if (/(row|select|selection|click|change)/.test(sourceLabel))
4636
- score += 8;
4637
- if (/(id|resourceid|formid|query|context|filter)/.test(targetLabel))
4638
- score += 8;
4639
- if (source.kind === 'output' && /(row|select|selection|click)/.test(sourceLabel) && /(id|resourceid|formid)/.test(targetLabel))
4640
- score += 30;
4641
- if (target.endpoint.kind === 'global-action' && target.endpoint.ref.actionId === 'surface.open' && /(pointclick|selectionchange|crossfilter)/.test(sourceLabel)) {
4642
- score += 70;
4643
- }
4644
- return score;
4946
+ return isTableRowSelectionToFormResourceId(source, target) ? 40 : 0;
4645
4947
  }
4646
- function suggestionReason(source, target, score) {
4948
+ function suggestionReason(source, target, _score) {
4949
+ if (isTableRowSelectionToFormResourceId(source, target))
4950
+ return 'row-selection-to-id';
4647
4951
  if (source.kind === 'state' && target.kind === 'input')
4648
4952
  return 'state-to-input';
4649
- const sourceLabel = source.label.toLowerCase();
4650
- const targetLabel = target.label.toLowerCase();
4651
- if (score >= 40 && /(row|select|selection|click)/.test(sourceLabel) && /(id|resourceid|formid)/.test(targetLabel)) {
4652
- return 'row-selection-to-id';
4653
- }
4654
4953
  return 'compatible-port';
4655
4954
  }
4656
4955
  function componentPort(widgetId, port, direction, componentType, nestedPath, label = port) {
@@ -5149,7 +5448,7 @@ const CONNECTION_EDITOR_CANVAS_WIDTH = 900;
5149
5448
  const CONNECTION_EDITOR_CANVAS_HEIGHT = 540;
5150
5449
  const CENTER = { x: CONNECTION_EDITOR_CANVAS_WIDTH / 2, y: CONNECTION_EDITOR_CANVAS_HEIGHT / 2 };
5151
5450
  const RADIUS_X = 310;
5152
- const RADIUS_Y = 190;
5451
+ const RADIUS_Y = 250;
5153
5452
  function autoArrangeConnectionEditorGraph(graph) {
5154
5453
  const stateNode = graph.nodes.find((node) => node.id === CONNECTION_EDITOR_STATE_NODE_ID) ?? null;
5155
5454
  const widgetNodes = graph.nodes.filter((node) => node.id !== CONNECTION_EDITOR_STATE_NODE_ID);
@@ -5539,6 +5838,71 @@ class ConnectionEditorComponent {
5539
5838
  focusWidget = output();
5540
5839
  openPageSettings = output();
5541
5840
  close = output();
5841
+ componentRegistry = inject(ComponentMetadataRegistry);
5842
+ compositionValidator = new CompositionValidatorService();
5843
+ draftBase = signal(null, ...(ngDevMode ? [{ debugName: "draftBase" }] : /* istanbul ignore next */ []));
5844
+ draftPage = signal(null, ...(ngDevMode ? [{ debugName: "draftPage" }] : /* istanbul ignore next */ []));
5845
+ workingPage = computed(() => this.draftPage() ?? this.page(), ...(ngDevMode ? [{ debugName: "workingPage" }] : /* istanbul ignore next */ []));
5846
+ draftSourceId = signal('', ...(ngDevMode ? [{ debugName: "draftSourceId" }] : /* istanbul ignore next */ []));
5847
+ draftTargetId = signal('', ...(ngDevMode ? [{ debugName: "draftTargetId" }] : /* istanbul ignore next */ []));
5848
+ draftLinkId = signal(null, ...(ngDevMode ? [{ debugName: "draftLinkId" }] : /* istanbul ignore next */ []));
5849
+ draftIsNew = signal(false, ...(ngDevMode ? [{ debugName: "draftIsNew" }] : /* istanbul ignore next */ []));
5850
+ draftSelectionBefore = null;
5851
+ presentationMode = signal(false, ...(ngDevMode ? [{ debugName: "presentationMode" }] : /* istanbul ignore next */ []));
5852
+ draftConflict = computed(() => !!this.draftBase() && !this.pagesEqual(this.draftBase(), this.page()), ...(ngDevMode ? [{ debugName: "draftConflict" }] : /* istanbul ignore next */ []));
5853
+ draftPorts = computed(() => {
5854
+ const ports = new Map(this.editorGraph().portById);
5855
+ for (const widget of this.workingPage()?.widgets ?? []) {
5856
+ for (const contract of this.componentRegistry.get(widget.definition.id)?.ports ?? []) {
5857
+ if (contract.exposure?.public === false || contract.exposure?.deprecated)
5858
+ continue;
5859
+ const existing = [...ports.values()].find(port => port.endpoint.kind === 'component-port'
5860
+ && port.endpoint.ref.widget === widget.key && !port.endpoint.ref.nestedPath?.length
5861
+ && port.endpoint.ref.port === contract.id && port.kind === contract.direction);
5862
+ if (existing)
5863
+ continue;
5864
+ const id = `${widget.key}-${contract.direction}-${contract.id.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'port'}`;
5865
+ ports.set(id, { id, nodeId: widget.key, label: contract.id, kind: contract.direction,
5866
+ endpoint: { kind: 'component-port', ref: { widget: widget.key, port: contract.id,
5867
+ direction: contract.direction, componentType: widget.definition.id } } });
5868
+ }
5869
+ }
5870
+ return [...ports.values()].filter(port => {
5871
+ if (port.endpoint.kind !== 'component-port')
5872
+ return true;
5873
+ const ref = port.endpoint.ref;
5874
+ const metadata = this.componentRegistry.get(ref.componentType ?? '');
5875
+ if (!metadata || port.id === this.draftSourceId() || port.id === this.draftTargetId())
5876
+ return true;
5877
+ return metadata.ports?.some(contract => contract.id === ref.port && contract.direction === ref.direction
5878
+ && contract.exposure?.public !== false && !contract.exposure?.deprecated) ?? false;
5879
+ });
5880
+ }, ...(ngDevMode ? [{ debugName: "draftPorts" }] : /* istanbul ignore next */ []));
5881
+ draftSources = computed(() => this.draftPorts().filter(port => port.kind === 'output' || port.kind === 'state'), ...(ngDevMode ? [{ debugName: "draftSources" }] : /* istanbul ignore next */ []));
5882
+ draftTargets = computed(() => {
5883
+ const source = this.draftPorts().find(port => port.id === this.draftSourceId());
5884
+ return source ? this.draftPorts().filter(port => canConnectPorts(source, port)) : [];
5885
+ }, ...(ngDevMode ? [{ debugName: "draftTargets" }] : /* istanbul ignore next */ []));
5886
+ draftDiagnostics = computed(() => {
5887
+ const page = this.draftPage();
5888
+ if (!page)
5889
+ return [];
5890
+ const links = page.composition?.links ?? [];
5891
+ const baseline = new Set(this.compositionValidator.validateLinks(this.draftBase()?.composition?.links ?? [], { page: this.draftBase(), registry: this.componentRegistry }).map(diagnostic => diagnostic.id));
5892
+ return this.compositionValidator.validateLinks(links, { page, registry: this.componentRegistry })
5893
+ .filter(diagnostic => diagnostic.subject.linkId === this.draftLinkId() || !baseline.has(diagnostic.id));
5894
+ }, ...(ngDevMode ? [{ debugName: "draftDiagnostics" }] : /* istanbul ignore next */ []));
5895
+ draftDuplicate = computed(() => {
5896
+ const links = this.draftPage()?.composition?.links ?? [];
5897
+ const draft = links.find(link => link.id === this.draftLinkId());
5898
+ if (!draft)
5899
+ return false;
5900
+ const signature = (link) => JSON.stringify([link.from, link.to, link.condition, link.transform, link.policy]);
5901
+ return links.some(link => link.id !== draft.id && signature(link) === signature(draft));
5902
+ }, ...(ngDevMode ? [{ debugName: "draftDuplicate" }] : /* istanbul ignore next */ []));
5903
+ canApplyDraft = computed(() => !!this.draftSourceId() && !!this.draftTargetId() && !!this.draftLinkId()
5904
+ && !this.draftConflict() && !this.draftDuplicate() && !this.globalActionPayloadError()
5905
+ && !this.draftDiagnostics().some(diagnostic => diagnostic.severity === 'error' || diagnostic.severity === 'fatal'), ...(ngDevMode ? [{ debugName: "canApplyDraft" }] : /* istanbul ignore next */ []));
5542
5906
  activeMode = signal('all', ...(ngDevMode ? [{ debugName: "activeMode" }] : /* istanbul ignore next */ []));
5543
5907
  activeFilter = signal('all', ...(ngDevMode ? [{ debugName: "activeFilter" }] : /* istanbul ignore next */ []));
5544
5908
  selectedWidgetId = signal(null, ...(ngDevMode ? [{ debugName: "selectedWidgetId" }] : /* istanbul ignore next */ []));
@@ -5574,17 +5938,24 @@ class ConnectionEditorComponent {
5574
5938
  recentLinkFeedbackTimer = null;
5575
5939
  minimapPointerCaptureTarget = null;
5576
5940
  suppressedNodeClickId = null;
5577
- fitViewportOnOpen = effect(() => {
5941
+ fitViewportOnOpen = effect((onCleanup) => {
5578
5942
  if (!this.open())
5579
5943
  return;
5580
- window.setTimeout(() => {
5944
+ let observer;
5945
+ const timer = window.setTimeout(() => {
5581
5946
  this.resetViewport();
5582
5947
  this.focusEditorSurface();
5948
+ const stage = this.hostElement.nativeElement.querySelector('.connection-editor__stage');
5949
+ if (stage) {
5950
+ observer = new ResizeObserver(() => this.resetViewport());
5951
+ observer.observe(stage);
5952
+ }
5583
5953
  if (this.open() && this.visualDensity() === 'guided') {
5584
5954
  this.expandedNodeId.set(null);
5585
5955
  this.selectedPort.set(null);
5586
5956
  }
5587
5957
  }, 0);
5958
+ onCleanup(() => { window.clearTimeout(timer); observer?.disconnect(); });
5588
5959
  }, ...(ngDevMode ? [{ debugName: "fitViewportOnOpen" }] : /* istanbul ignore next */ []));
5589
5960
  filters = [
5590
5961
  { id: 'all', label: this.tx('connections.filters.all', 'All') },
@@ -5606,7 +5977,7 @@ class ConnectionEditorComponent {
5606
5977
  { id: 'derived', label: this.tx('connections.dock.derived', 'Derived'), glyph: '⌘' },
5607
5978
  { id: 'export', label: this.tx('connections.dock.export', 'Export'), glyph: '⇩' },
5608
5979
  ];
5609
- model = computed(() => buildConnectionsViewerModel(this.page()), ...(ngDevMode ? [{ debugName: "model" }] : /* istanbul ignore next */ []));
5980
+ model = computed(() => buildConnectionsViewerModel(this.workingPage()), ...(ngDevMode ? [{ debugName: "model" }] : /* istanbul ignore next */ []));
5610
5981
  selectedNode = computed(() => this.model().nodes.find((node) => node.widgetId === this.selectedWidgetId()) ?? null, ...(ngDevMode ? [{ debugName: "selectedNode" }] : /* istanbul ignore next */ []));
5611
5982
  visibleEdges = computed(() => {
5612
5983
  const widgetId = this.selectedWidgetId();
@@ -5631,9 +6002,20 @@ class ConnectionEditorComponent {
5631
6002
  return edges;
5632
6003
  }, ...(ngDevMode ? [{ debugName: "visibleEdges" }] : /* istanbul ignore next */ []));
5633
6004
  selectedEdge = computed(() => this.model().edges.find((edge) => edge.id === this.selectedLinkId()) ?? null, ...(ngDevMode ? [{ debugName: "selectedEdge" }] : /* istanbul ignore next */ []));
5634
- editorGraph = computed(() => deriveConnectionEditorGraph(this.page(), {
6005
+ editorGraph = computed(() => deriveConnectionEditorGraph(this.workingPage(), {
6006
+ componentMetadata: componentType => this.componentRegistry.get(componentType),
5635
6007
  stateNodeTitle: this.tx('connections.editor.stateNode', 'Page state'),
5636
6008
  globalActionIds: this.availableGlobalActionIds(),
6009
+ widgetTitle: widget => {
6010
+ const title = this.i18n.resolve(widget.shell?.title, '').trim();
6011
+ if (title && !title.includes('${'))
6012
+ return title;
6013
+ const metadata = this.componentRegistry.get(widget.definition.id);
6014
+ if (!metadata)
6015
+ return widget.key;
6016
+ const peers = (this.workingPage()?.widgets ?? []).filter(peer => peer.definition.id === widget.definition.id);
6017
+ return peers.length > 1 ? `${metadata.friendlyName} ${peers.findIndex(peer => peer.key === widget.key) + 1}` : metadata.friendlyName;
6018
+ },
5637
6019
  }), ...(ngDevMode ? [{ debugName: "editorGraph" }] : /* istanbul ignore next */ []));
5638
6020
  connectedNodeIds = computed(() => {
5639
6021
  const ids = new Set();
@@ -5656,14 +6038,10 @@ class ConnectionEditorComponent {
5656
6038
  return ids;
5657
6039
  }, ...(ngDevMode ? [{ debugName: "connectedPortIds" }] : /* istanbul ignore next */ []));
5658
6040
  editorNodes = computed(() => {
5659
- const arranged = autoArrangeConnectionEditorGraph(this.editorGraph()).nodes.map((node) => this.applyNodeOffset(this.applyNodeRotation(applyConnectionEditorPortOrder(node, this.portOrder()))));
5660
- if (this.visualDensity() === 'full')
5661
- return arranged;
5662
- const connectedNodeIds = this.connectedNodeIds();
5663
- return arranged.filter((node) => node.id !== CONNECTION_EDITOR_GLOBAL_ACTION_NODE_ID
5664
- || connectedNodeIds.has(node.id)
5665
- || this.selectedWidgetId() === node.id
5666
- || this.hoveredNodeId() === node.id);
6041
+ const graph = this.editorGraph();
6042
+ const nodes = this.visualDensity() === 'full' ? graph.nodes : graph.nodes.filter(node => node.id !== CONNECTION_EDITOR_GLOBAL_ACTION_NODE_ID || this.connectedNodeIds().has(node.id)
6043
+ || this.selectedWidgetId() === node.id || this.hoveredNodeId() === node.id);
6044
+ return autoArrangeConnectionEditorGraph({ ...graph, nodes }).nodes.map(node => this.applyNodeOffset(this.applyNodeRotation(applyConnectionEditorPortOrder(node, this.portOrder()))));
5667
6045
  }, ...(ngDevMode ? [{ debugName: "editorNodes" }] : /* istanbul ignore next */ []));
5668
6046
  navigationBounds = computed(() => this.connectionEditorNavigationBounds(), ...(ngDevMode ? [{ debugName: "navigationBounds" }] : /* istanbul ignore next */ []));
5669
6047
  minimapViewBox = computed(() => {
@@ -5710,8 +6088,8 @@ class ConnectionEditorComponent {
5710
6088
  const nodes = this.editorNodes();
5711
6089
  const hasSelectionOutput = nodes.some((node) => node.outputs.some((port) => this.isTableSelectionOutput(port)));
5712
6090
  const hasResourceIdInput = nodes.some((node) => node.inputs.some((port) => this.isFormResourceIdInput(port)));
5713
- const hasTable = nodes.some((node) => [...node.outputs, ...node.inputs, ...node.states].some((port) => this.portComponentType(port).includes('table')));
5714
- const hasForm = nodes.some((node) => [...node.outputs, ...node.inputs, ...node.states].some((port) => this.portComponentType(port).includes('dynamic-form')));
6091
+ const hasTable = nodes.some((node) => [...node.outputs, ...node.inputs, ...node.states].some((port) => this.portComponentType(port) === 'praxis-table'));
6092
+ const hasForm = nodes.some((node) => [...node.outputs, ...node.inputs, ...node.states].some((port) => this.portComponentType(port) === 'praxis-dynamic-form'));
5715
6093
  if (hasTable && hasForm && !hasSelectionOutput && !hasResourceIdInput) {
5716
6094
  return this.tx('connections.suggestions.diagnostics.masterDetailMissingBoth', 'To create master-detail, the table needs a row selection output and the form needs a resourceId input.');
5717
6095
  }
@@ -5799,8 +6177,8 @@ class ConnectionEditorComponent {
5799
6177
  || this.rotationState()), ...(ngDevMode ? [{ debugName: "canvasInteractionActive" }] : /* istanbul ignore next */ []));
5800
6178
  visibleConnectionTooltip = computed(() => (this.canvasInteractionActive() ? null : this.connectionTooltip()), ...(ngDevMode ? [{ debugName: "visibleConnectionTooltip" }] : /* istanbul ignore next */ []));
5801
6179
  visibleExpandedNodeId = computed(() => (this.canvasInteractionActive() ? null : this.expandedNodeId()), ...(ngDevMode ? [{ debugName: "visibleExpandedNodeId" }] : /* istanbul ignore next */ []));
5802
- canUndo = computed(() => this.undoStack().length > 0, ...(ngDevMode ? [{ debugName: "canUndo" }] : /* istanbul ignore next */ []));
5803
- canRedo = computed(() => this.redoStack().length > 0, ...(ngDevMode ? [{ debugName: "canRedo" }] : /* istanbul ignore next */ []));
6180
+ canUndo = computed(() => !this.draftBase() && !this.presentationMode() && this.undoStack().length > 0, ...(ngDevMode ? [{ debugName: "canUndo" }] : /* istanbul ignore next */ []));
6181
+ canRedo = computed(() => !this.draftBase() && !this.presentationMode() && this.redoStack().length > 0, ...(ngDevMode ? [{ debugName: "canRedo" }] : /* istanbul ignore next */ []));
5804
6182
  hasVisualPortOrder = computed(() => Object.keys(this.portOrder()).length > 0, ...(ngDevMode ? [{ debugName: "hasVisualPortOrder" }] : /* istanbul ignore next */ []));
5805
6183
  dragPreviewPath = computed(() => {
5806
6184
  const drag = this.dragState();
@@ -5894,6 +6272,8 @@ class ConnectionEditorComponent {
5894
6272
  };
5895
6273
  }, ...(ngDevMode ? [{ debugName: "minimapViewport" }] : /* istanbul ignore next */ []));
5896
6274
  selectNode(nodeId) {
6275
+ if (this.draftBase())
6276
+ return;
5897
6277
  if (this.suppressedNodeClickId === nodeId) {
5898
6278
  this.suppressedNodeClickId = null;
5899
6279
  return;
@@ -5909,6 +6289,8 @@ class ConnectionEditorComponent {
5909
6289
  this.selectedLinkId.set(null);
5910
6290
  }
5911
6291
  selectSegment(port, event) {
6292
+ if (this.draftBase())
6293
+ return;
5912
6294
  event.preventDefault();
5913
6295
  event.stopPropagation();
5914
6296
  this.selectedWidgetId.set(port.nodeId === this.stateNodeId ? null : port.nodeId);
@@ -5918,16 +6300,22 @@ class ConnectionEditorComponent {
5918
6300
  this.traceSourceNodeId.set(null);
5919
6301
  }
5920
6302
  selectWidget(node) {
6303
+ if (this.draftBase())
6304
+ return;
5921
6305
  this.selectedPort.set(null);
5922
6306
  this.selectedWidgetId.update((current) => (current === node.widgetId ? null : node.widgetId));
5923
6307
  this.selectedLinkId.set(null);
5924
6308
  }
5925
6309
  selectLink(edge) {
6310
+ if (this.draftBase())
6311
+ return;
5926
6312
  this.selectedPort.set(null);
5927
6313
  this.selectedLinkId.update((current) => (current === edge.id ? null : edge.id));
5928
6314
  this.selectedWidgetId.set(null);
5929
6315
  }
5930
6316
  selectConnection(connection) {
6317
+ if (this.draftBase())
6318
+ return;
5931
6319
  this.selectedLinkId.set(connection.id);
5932
6320
  this.selectedWidgetId.set(null);
5933
6321
  }
@@ -5956,12 +6344,27 @@ class ConnectionEditorComponent {
5956
6344
  }
5957
6345
  }
5958
6346
  handleEditorKeydown(event) {
5959
- if (!this.open() || this.shouldIgnoreEditorShortcut(event))
6347
+ if (!this.open())
6348
+ return;
6349
+ if (event.key === 'Escape' && this.draftBase()) {
6350
+ event.preventDefault();
6351
+ this.cancelDraft();
6352
+ return;
6353
+ }
6354
+ if (this.shouldIgnoreEditorShortcut(event))
5960
6355
  return;
5961
6356
  const key = event.key.toLowerCase();
5962
6357
  const hasCommandModifier = event.ctrlKey || event.metaKey;
5963
6358
  if (key === 'escape') {
5964
6359
  event.preventDefault();
6360
+ if (this.draftBase()) {
6361
+ this.cancelDraft();
6362
+ return;
6363
+ }
6364
+ if (this.presentationMode()) {
6365
+ this.togglePresentation();
6366
+ return;
6367
+ }
5965
6368
  const hasTransientEditorState = !!(this.pendingSegmentState()
5966
6369
  || this.dragState()
5967
6370
  || this.nodeDragState()
@@ -6003,10 +6406,14 @@ class ConnectionEditorComponent {
6003
6406
  }
6004
6407
  }
6005
6408
  setFilter(filter) {
6409
+ if (this.draftBase())
6410
+ return;
6006
6411
  this.activeFilter.set(filter);
6007
6412
  this.selectedLinkId.set(null);
6008
6413
  }
6009
6414
  setMode(mode) {
6415
+ if (this.draftBase())
6416
+ return;
6010
6417
  this.activeMode.set(mode);
6011
6418
  this.selectedLinkId.set(null);
6012
6419
  this.traceEnabled.set(mode === 'simulation');
@@ -6079,6 +6486,8 @@ class ConnectionEditorComponent {
6079
6486
  this.dockCollapsed.update((collapsed) => !collapsed);
6080
6487
  }
6081
6488
  toggleTrace(event) {
6489
+ if (this.draftBase())
6490
+ return;
6082
6491
  event.preventDefault();
6083
6492
  event.stopPropagation();
6084
6493
  this.traceEnabled.update((enabled) => {
@@ -6092,6 +6501,8 @@ class ConnectionEditorComponent {
6092
6501
  });
6093
6502
  }
6094
6503
  selectTraceSource(nodeId, event) {
6504
+ if (this.draftBase())
6505
+ return;
6095
6506
  event.preventDefault();
6096
6507
  event.stopPropagation();
6097
6508
  this.traceSourceNodeId.set(nodeId);
@@ -6123,6 +6534,8 @@ class ConnectionEditorComponent {
6123
6534
  this.addLink(suggestion.source, suggestion.target);
6124
6535
  }
6125
6536
  startDrag(port, event) {
6537
+ if (this.presentationMode() || this.draftBase())
6538
+ return;
6126
6539
  event.preventDefault();
6127
6540
  event.stopPropagation();
6128
6541
  this.clearInvalidDropFeedback();
@@ -6137,6 +6550,8 @@ class ConnectionEditorComponent {
6137
6550
  });
6138
6551
  }
6139
6552
  startSegmentDrag(port, event) {
6553
+ if (this.presentationMode() || this.draftBase())
6554
+ return;
6140
6555
  if (event.shiftKey) {
6141
6556
  this.startNodeRotation(port.nodeId, event);
6142
6557
  return;
@@ -6313,6 +6728,9 @@ class ConnectionEditorComponent {
6313
6728
  event.stopPropagation();
6314
6729
  const source = this.dragState()?.port;
6315
6730
  this.dragState.set(null);
6731
+ // Segment pointerup stops propagation, so finish gestures normally closed by the document.
6732
+ this.cancelPendingSegment();
6733
+ this.stopRotation();
6316
6734
  if (!source)
6317
6735
  return;
6318
6736
  if (!canConnectPorts(source, target)) {
@@ -6501,7 +6919,11 @@ class ConnectionEditorComponent {
6501
6919
  return this.invalidDropFeedback()?.target?.id === target.id;
6502
6920
  }
6503
6921
  removeLink(linkId) {
6504
- const source = this.page();
6922
+ if (this.presentationMode())
6923
+ return;
6924
+ if (this.draftBase())
6925
+ return;
6926
+ const source = this.workingPage();
6505
6927
  if (!source)
6506
6928
  return;
6507
6929
  const next = this.clonePage(source);
@@ -6516,6 +6938,10 @@ class ConnectionEditorComponent {
6516
6938
  }
6517
6939
  }
6518
6940
  undoHistory(event) {
6941
+ if (this.presentationMode())
6942
+ return;
6943
+ if (this.draftBase())
6944
+ return;
6519
6945
  event.preventDefault();
6520
6946
  event.stopPropagation();
6521
6947
  const undoStack = this.undoStack();
@@ -6529,6 +6955,10 @@ class ConnectionEditorComponent {
6529
6955
  this.pageChange.emit(this.clonePage(entry.before));
6530
6956
  }
6531
6957
  redoHistory(event) {
6958
+ if (this.presentationMode())
6959
+ return;
6960
+ if (this.draftBase())
6961
+ return;
6532
6962
  event.preventDefault();
6533
6963
  event.stopPropagation();
6534
6964
  const redoStack = this.redoStack();
@@ -6727,8 +7157,7 @@ class ConnectionEditorComponent {
6727
7157
  if (this.dragState() || this.pendingSegmentState() || this.nodeHasCompatiblePreviewPort(node)) {
6728
7158
  return true;
6729
7159
  }
6730
- const connectedPortIds = this.connectedPortIds();
6731
- return [...node.inputs, ...node.outputs, ...node.states].some((port) => connectedPortIds.has(port.id));
7160
+ return node.id === this.selectedWidgetId() || this.isNodeFocusedByConnection(node);
6732
7161
  }
6733
7162
  isGuidedPortVisible(port) {
6734
7163
  if (this.visualDensity() === 'full')
@@ -6739,7 +7168,7 @@ class ConnectionEditorComponent {
6739
7168
  if (this.isDragSource(port) || this.isCompatibleDrop(port) || this.isDropCandidate(port) || this.isPortFocusedByConnection(port)) {
6740
7169
  return true;
6741
7170
  }
6742
- return this.connectedPortIds().has(port.id);
7171
+ return false;
6743
7172
  }
6744
7173
  isGuidedSegmentLabelVisible(port) {
6745
7174
  if (this.visualDensity() === 'full')
@@ -6906,7 +7335,8 @@ class ConnectionEditorComponent {
6906
7335
  }
6907
7336
  suggestionPreviewText(suggestion) {
6908
7337
  if (this.isSuggestedMasterDetail(suggestion)) {
6909
- return this.tx('connections.suggestions.preview.rowIdToDetail', 'Will deliver the selected row id (payload.row.id) to the form.');
7338
+ const link = buildCompositionLinkFromPorts(suggestion.source, suggestion.target, []);
7339
+ return `${this.tx('connections.preview.deliversPath', 'Delivered value')}: ${link.transform?.steps[0]?.config?.['path']}.`;
6910
7340
  }
6911
7341
  if (suggestion.source.kind === 'state') {
6912
7342
  return this.tx('connections.suggestions.preview.stateToInput', 'Will deliver the current page state value.');
@@ -7010,7 +7440,7 @@ class ConnectionEditorComponent {
7010
7440
  return false;
7011
7441
  if (target.label !== 'resourceId')
7012
7442
  return false;
7013
- if (!/^(rowClick|rowSelection|selectionChange)$/i.test(source.label))
7443
+ if (!['rowClick', 'selectionChange'].includes(source.label))
7014
7444
  return false;
7015
7445
  const sourceComponent = source.endpoint.kind === 'component-port'
7016
7446
  ? String(source.endpoint.ref.componentType ?? '').toLowerCase()
@@ -7018,17 +7448,17 @@ class ConnectionEditorComponent {
7018
7448
  const targetComponent = target.endpoint.kind === 'component-port'
7019
7449
  ? String(target.endpoint.ref.componentType ?? '').toLowerCase()
7020
7450
  : '';
7021
- return sourceComponent.includes('table') && targetComponent.includes('dynamic-form');
7451
+ return sourceComponent === 'praxis-table' && targetComponent === 'praxis-dynamic-form';
7022
7452
  }
7023
7453
  isTableSelectionOutput(port) {
7024
7454
  return port.kind === 'output'
7025
- && /^(rowClick|rowSelection|selectionChange)$/i.test(port.label)
7026
- && this.portComponentType(port).includes('table');
7455
+ && ['rowClick', 'selectionChange'].includes(port.label)
7456
+ && this.portComponentType(port) === 'praxis-table';
7027
7457
  }
7028
7458
  isFormResourceIdInput(port) {
7029
7459
  return port.kind === 'input'
7030
7460
  && port.label === 'resourceId'
7031
- && this.portComponentType(port).includes('dynamic-form');
7461
+ && this.portComponentType(port) === 'praxis-dynamic-form';
7032
7462
  }
7033
7463
  portComponentType(port) {
7034
7464
  return port.endpoint.kind === 'component-port'
@@ -7118,7 +7548,7 @@ class ConnectionEditorComponent {
7118
7548
  return this.tx('connections.tooltip.dataFlow', 'Data Flow');
7119
7549
  }
7120
7550
  dockJson() {
7121
- return this.page();
7551
+ return this.workingPage();
7122
7552
  }
7123
7553
  dockLinks() {
7124
7554
  return this.editorConnections().map((connection) => ({
@@ -7163,7 +7593,7 @@ class ConnectionEditorComponent {
7163
7593
  return {
7164
7594
  exportedAt: 'runtime',
7165
7595
  source: '@praxisui/page-builder/connection-editor',
7166
- composition: this.page()?.composition ?? { version: '1.0.0', links: [] },
7596
+ composition: this.workingPage()?.composition ?? { version: '1.0.0', links: [] },
7167
7597
  derived: this.dockDerived(),
7168
7598
  };
7169
7599
  }
@@ -7182,17 +7612,21 @@ class ConnectionEditorComponent {
7182
7612
  ...graphDiagnostics,
7183
7613
  ];
7184
7614
  }
7615
+ edgeRouteTitle(edge) {
7616
+ const connection = this.editorConnections().find(item => item.id === edge.id);
7617
+ return connection ? `${this.readableEndpointLabel(connection, 'from')} → ${this.readableEndpointLabel(connection, 'to')}` : this.describeEdge(edge);
7618
+ }
7185
7619
  describeSource(edge) {
7186
7620
  if (edge.fromKind === 'component-port') {
7187
7621
  return `${edge.fromWidgetId}.${edge.fromPort}`;
7188
7622
  }
7189
- return edge.fromKind;
7623
+ return edge.link.from.kind === 'state' ? `state.${edge.link.from.ref.path}` : edge.fromKind;
7190
7624
  }
7191
7625
  describeTarget(edge) {
7192
7626
  if (edge.toKind === 'component-port') {
7193
7627
  return `${edge.toWidgetId}.${edge.toPort}`;
7194
7628
  }
7195
- return `state.${edge.toStatePath}`;
7629
+ return edge.link.to.kind === 'global-action' ? edge.link.to.ref.actionId : `state.${edge.toStatePath}`;
7196
7630
  }
7197
7631
  globalActionPayloadText(connection) {
7198
7632
  const payload = connection.link.to.kind === 'global-action'
@@ -7282,6 +7716,161 @@ class ConnectionEditorComponent {
7282
7716
  };
7283
7717
  });
7284
7718
  }
7719
+ beginNewConnection() {
7720
+ if (this.presentationMode())
7721
+ return;
7722
+ if (this.draftBase() || !this.page())
7723
+ return;
7724
+ this.draftSelectionBefore = this.selectedLinkId();
7725
+ this.draftBase.set(this.clonePage(this.page()));
7726
+ this.draftPage.set(this.clonePage(this.page()));
7727
+ this.draftIsNew.set(true);
7728
+ this.draftSourceId.set('');
7729
+ this.draftTargetId.set('');
7730
+ this.draftLinkId.set(null);
7731
+ this.selectedLinkId.set(null);
7732
+ this.selectedWidgetId.set(null);
7733
+ this.scrollConnectionSidePanelToTop();
7734
+ window.setTimeout(() => this.hostElement.nativeElement.querySelector('[data-testid="page-builder-connections-draft-source"]')?.focus(), 0);
7735
+ }
7736
+ beginEditConnection(linkId) {
7737
+ if (this.presentationMode())
7738
+ return;
7739
+ if (this.draftBase())
7740
+ return;
7741
+ const connection = this.editorConnections().find(item => item.id === linkId);
7742
+ if (!connection || !this.page())
7743
+ return;
7744
+ this.beginNewConnection();
7745
+ this.draftIsNew.set(false);
7746
+ this.draftLinkId.set(linkId);
7747
+ this.selectedLinkId.set(linkId);
7748
+ this.draftSourceId.set(connection.fromPort?.id ?? '');
7749
+ this.draftTargetId.set(connection.toPort?.id ?? '');
7750
+ }
7751
+ changeDraftEndpoint(side, id) {
7752
+ if (!this.draftBase())
7753
+ return;
7754
+ if (side === 'source')
7755
+ this.draftSourceId.set(id);
7756
+ else
7757
+ this.draftTargetId.set(id);
7758
+ const source = this.draftPorts().find(port => port.id === this.draftSourceId());
7759
+ const target = this.draftPorts().find(port => port.id === this.draftTargetId());
7760
+ if (!source || !target || !canConnectPorts(source, target)) {
7761
+ if (side === 'source')
7762
+ this.draftTargetId.set('');
7763
+ return;
7764
+ }
7765
+ const next = this.clonePage(this.draftPage());
7766
+ next.widgets = this.clonePage(this.draftBase()).widgets;
7767
+ const links = next.composition?.links ?? [];
7768
+ const previous = links.find(link => link.id === this.draftLinkId());
7769
+ const created = buildCompositionLinkFromPorts(source, target, links);
7770
+ // Editing one endpoint must not replace payload, bindingPath or nested metadata on the other.
7771
+ const previousConnection = this.editorConnections().find(item => item.id === previous?.id);
7772
+ const previousDefaultTransform = previousConnection?.fromPort && previousConnection.toPort
7773
+ ? buildCompositionLinkFromPorts(previousConnection.fromPort, previousConnection.toPort, []).transform : undefined;
7774
+ const replaceDefaultTransform = this.draftIsNew() && previous
7775
+ && JSON.stringify(previous.transform) === JSON.stringify(previousDefaultTransform);
7776
+ const link = previous
7777
+ ? { ...previous,
7778
+ from: source.id === previousConnection?.fromPort?.id ? previous.from : created.from,
7779
+ to: target.id === previousConnection?.toPort?.id ? previous.to : created.to,
7780
+ intent: created.intent,
7781
+ ...(replaceDefaultTransform ? { transform: created.transform } : {}),
7782
+ }
7783
+ : created;
7784
+ this.ensureSourceOutputDeclared(next, source);
7785
+ next.composition = { ...(next.composition ?? { version: '1.0.0' }),
7786
+ links: [...links.filter(item => item.id !== this.draftLinkId()), link] };
7787
+ this.draftLinkId.set(link.id);
7788
+ this.selectedLinkId.set(link.id);
7789
+ this.draftPage.set(next);
7790
+ }
7791
+ projectDraftValue() {
7792
+ const connection = this.selectedConnection();
7793
+ if (!connection || !this.draftBase())
7794
+ return;
7795
+ this.setConnectionTransform(connection.id, { ...this.defaultTransformPipeline(connection),
7796
+ steps: [this.createTransformStep('pick-path', 0, connection)],
7797
+ output: { semanticKind: 'value', cardinality: 'one', stableShape: false },
7798
+ });
7799
+ }
7800
+ setDraftMissingValue(missingValuePolicy) {
7801
+ const id = this.draftLinkId();
7802
+ if (!id)
7803
+ return;
7804
+ this.updateConnectionLink(id, link => ({ ...link,
7805
+ policy: { ...link.policy, missingValuePolicy },
7806
+ transform: link.transform ? { ...link.transform, fallbackValue: null } : undefined,
7807
+ }));
7808
+ }
7809
+ changeDraftPickPath(index, path) {
7810
+ const id = this.draftLinkId();
7811
+ if (!id)
7812
+ return;
7813
+ this.updateConnectionLink(id, link => ({ ...link, transform: link.transform ? { ...link.transform,
7814
+ steps: link.transform.steps.map((step, stepIndex) => stepIndex === index
7815
+ ? { ...step, config: { ...step.config, path: path.trim() } } : step) } : undefined }));
7816
+ }
7817
+ draftPortLabel(port) {
7818
+ const title = this.editorGraph().nodes.find(node => node.id === port.nodeId)?.title ?? port.nodeId;
7819
+ if (port.endpoint.kind !== 'component-port')
7820
+ return `${title} · ${port.label}`;
7821
+ const ref = port.endpoint.ref;
7822
+ const contract = this.componentRegistry.get(ref.componentType ?? '')?.ports?.find(item => item.id === ref.port && item.direction === ref.direction);
7823
+ return `${title} · ${contract?.label ?? port.label}${contract?.label && contract.label !== port.label ? ` (${port.label})` : ''}`;
7824
+ }
7825
+ draftEndpointLabel(id) {
7826
+ const port = this.draftPorts().find(item => item.id === id);
7827
+ return port ? this.draftPortLabel(port) : id;
7828
+ }
7829
+ applyDraft() {
7830
+ if (!this.canApplyDraft())
7831
+ return;
7832
+ const next = this.draftPage();
7833
+ const id = this.draftLinkId();
7834
+ const created = this.draftIsNew();
7835
+ this.clearDraft();
7836
+ this.selectedLinkId.set(this.draftSelectionBefore);
7837
+ this.commitPageChange(next, id);
7838
+ if (created && id) {
7839
+ this.lastCreatedLinkId.set(id);
7840
+ this.markRecentLink(id);
7841
+ }
7842
+ this.focusEditorSurface();
7843
+ }
7844
+ cancelDraft() {
7845
+ const id = this.draftIsNew() ? null : this.draftLinkId();
7846
+ this.clearDraft();
7847
+ this.selectedLinkId.set(id);
7848
+ this.focusEditorSurface();
7849
+ }
7850
+ clearDraft() {
7851
+ this.draftBase.set(null);
7852
+ this.draftPage.set(null);
7853
+ this.draftLinkId.set(null);
7854
+ this.draftSourceId.set('');
7855
+ this.draftTargetId.set('');
7856
+ this.globalActionPayloadError.set(null);
7857
+ }
7858
+ closeEditor() {
7859
+ if (this.draftBase()) {
7860
+ this.cancelDraft();
7861
+ return;
7862
+ }
7863
+ this.close.emit();
7864
+ }
7865
+ nodeCaptionWidth() {
7866
+ return Math.min(180, this.stageClientSize().width / 3) / this.viewport().scale;
7867
+ }
7868
+ togglePresentation() {
7869
+ if (this.draftBase())
7870
+ return;
7871
+ this.presentationMode.update(value => !value);
7872
+ window.setTimeout(() => this.resetViewport(), 0);
7873
+ }
7285
7874
  tx(key, fallback) {
7286
7875
  return resolvePraxisPageBuilderText(this.i18n, key, fallback);
7287
7876
  }
@@ -7293,7 +7882,12 @@ class ConnectionEditorComponent {
7293
7882
  return this.availableGlobalActions().map((entry) => entry.id);
7294
7883
  }
7295
7884
  addLink(source, target) {
7296
- const page = this.page();
7885
+ if (this.presentationMode())
7886
+ return;
7887
+ if (this.draftBase())
7888
+ return;
7889
+ this.beginNewConnection();
7890
+ const page = this.workingPage();
7297
7891
  if (!page)
7298
7892
  return;
7299
7893
  const next = this.clonePage(page);
@@ -7304,9 +7898,11 @@ class ConnectionEditorComponent {
7304
7898
  ...(next.composition ?? { version: '1.0.0' }),
7305
7899
  links: [...links, link],
7306
7900
  };
7307
- this.commitPageChange(next, link.id);
7308
- this.lastCreatedLinkId.set(link.id);
7309
- this.markRecentLink(link.id);
7901
+ this.draftSourceId.set(source.id);
7902
+ this.draftTargetId.set(target.id);
7903
+ this.draftLinkId.set(link.id);
7904
+ this.selectedLinkId.set(link.id);
7905
+ this.draftPage.set(next);
7310
7906
  this.scrollConnectionSidePanelToTop();
7311
7907
  }
7312
7908
  ensureSourceOutputDeclared(page, source) {
@@ -7366,7 +7962,9 @@ class ConnectionEditorComponent {
7366
7962
  });
7367
7963
  }
7368
7964
  updateConnectionLink(linkId, update) {
7369
- const source = this.page();
7965
+ if (!this.draftBase())
7966
+ this.beginEditConnection(linkId);
7967
+ const source = this.workingPage();
7370
7968
  if (!source)
7371
7969
  return;
7372
7970
  const next = this.clonePage(source);
@@ -7378,6 +7976,11 @@ class ConnectionEditorComponent {
7378
7976
  this.commitPageChange(next, linkId);
7379
7977
  }
7380
7978
  commitPageChange(next, selectedAfter) {
7979
+ if (this.draftBase()) {
7980
+ this.draftPage.set(next);
7981
+ this.selectedLinkId.set(selectedAfter);
7982
+ return;
7983
+ }
7381
7984
  const current = this.page();
7382
7985
  if (!current)
7383
7986
  return;
@@ -7411,7 +8014,7 @@ class ConnectionEditorComponent {
7411
8014
  kind,
7412
8015
  phase: 'link-propagation',
7413
8016
  ...(kind === 'pick-path'
7414
- ? { config: { path: connection.fromLabel } }
8017
+ ? { config: { path: connection.link.from.kind === 'state' ? 'current' : 'payload' } }
7415
8018
  : {}),
7416
8019
  };
7417
8020
  }
@@ -7683,7 +8286,7 @@ class ConnectionEditorComponent {
7683
8286
  fitViewportState() {
7684
8287
  const stage = this.stageClientSize();
7685
8288
  const bounds = this.navigationBounds();
7686
- const scale = this.clamp(Math.min(stage.width / bounds.width, stage.height / bounds.height) * 0.94, 0.42, 1);
8289
+ const scale = this.clamp(Math.min(stage.width / bounds.width, stage.height / bounds.height) * 0.94, 0.2, 1);
7687
8290
  return this.clampViewport({
7688
8291
  x: ((stage.width - bounds.width * scale) / 2) - bounds.x * scale,
7689
8292
  y: ((stage.height - bounds.height * scale) / 2) - bounds.y * scale,
@@ -7760,6 +8363,8 @@ class ConnectionEditorComponent {
7760
8363
  @if (open()) {
7761
8364
  <aside
7762
8365
  class="connection-editor"
8366
+ [class.connection-editor--presentation]="presentationMode()"
8367
+ [class.connection-editor--draft]="!!draftBase()"
7763
8368
  [class.connection-editor--guided]="visualDensity() === 'guided'"
7764
8369
  [class.connection-editor--full]="visualDensity() === 'full'"
7765
8370
  [class.connection-editor--dock-collapsed]="dockCollapsed()"
@@ -7780,7 +8385,7 @@ class ConnectionEditorComponent {
7780
8385
  @for (mode of flowModes; track mode.id) {
7781
8386
  <button
7782
8387
  type="button"
7783
- class="connection-editor__mode"
8388
+ class="connection-editor__mode" [disabled]="!!draftBase()"
7784
8389
  role="tab"
7785
8390
  [class.connection-editor__mode--active]="activeMode() === mode.id"
7786
8391
  [attr.aria-selected]="activeMode() === mode.id"
@@ -7796,6 +8401,16 @@ class ConnectionEditorComponent {
7796
8401
  }
7797
8402
  </div>
7798
8403
  </div>
8404
+ <div class="connection-editor__header-actions">
8405
+ <button type="button" class="connection-editor__action connection-editor__action--primary"
8406
+ data-testid="page-builder-connections-new" [disabled]="!!draftBase() || presentationMode()"
8407
+ (click)="beginNewConnection()">+ {{ tx('connections.draft.new', 'New connection') }}</button>
8408
+ <button type="button" class="connection-editor__action"
8409
+ data-testid="page-builder-connections-presentation" [attr.aria-pressed]="presentationMode()"
8410
+ [disabled]="!!draftBase()" (click)="togglePresentation()">
8411
+ {{ presentationMode() ? tx('connections.draft.backToEdit', 'Back to editing') : tx('connections.draft.present', 'Present diagram') }}
8412
+ </button>
8413
+ </div>
7799
8414
  <div class="connection-editor__subtitle">
7800
8415
  <button
7801
8416
  type="button"
@@ -7814,7 +8429,7 @@ class ConnectionEditorComponent {
7814
8429
  class="connection-editor__close"
7815
8430
  data-testid="page-builder-connections-close"
7816
8431
  [attr.aria-label]="tx('connections.closeAria', 'Close connection editor')"
7817
- (click)="close.emit()"
8432
+ (click)="closeEditor()"
7818
8433
  >
7819
8434
  ×
7820
8435
  </button>
@@ -7832,6 +8447,9 @@ class ConnectionEditorComponent {
7832
8447
  <div class="connection-editor__viewport" [style.transform]="viewportTransform()">
7833
8448
  <svg class="connection-editor__wires" [attr.viewBox]="canvasViewBox" aria-hidden="true">
7834
8449
  <defs>
8450
+ <marker id="connection-direction" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse" markerUnits="userSpaceOnUse">
8451
+ <path d="M 1 1 L 9 5 L 1 9" fill="none" stroke="context-stroke" stroke-width="2" />
8452
+ </marker>
7835
8453
  <filter id="connection-glow" x="-20%" y="-20%" width="140%" height="140%">
7836
8454
  <feGaussianBlur stdDeviation="3" result="blur" />
7837
8455
  <feMerge>
@@ -7862,11 +8480,11 @@ class ConnectionEditorComponent {
7862
8480
  [class.connection-editor__wire--trace]="isTracedConnection(connection)"
7863
8481
  [class.connection-editor__wire--active]="selectedLinkId() === connection.id"
7864
8482
  [class.connection-editor__wire--new]="recentLinkId() === connection.id"
7865
- [class.connection-editor__wire--mode-dimmed]="isModeDimmedConnection(connection)"
8483
+ [class.connection-editor__wire--mode-dimmed]="isModeDimmedConnection(connection) || (!!selectedLinkId() && selectedLinkId() !== connection.id)"
7866
8484
  [attr.d]="wirePath(connection)"
7867
8485
  />
7868
8486
  <path
7869
- class="connection-editor__wire"
8487
+ class="connection-editor__wire" marker-end="url(#connection-direction)"
7870
8488
  [class.connection-editor__wire--active]="selectedLinkId() === connection.id"
7871
8489
  [class.connection-editor__wire--state]="connection.intent === 'state-write'"
7872
8490
  [class.connection-editor__wire--state-read]="connection.intent === 'state-read'"
@@ -7877,7 +8495,7 @@ class ConnectionEditorComponent {
7877
8495
  [class.connection-editor__wire--error]="connectionHasErrors(connection)"
7878
8496
  [class.connection-editor__wire--trace]="isTracedConnection(connection)"
7879
8497
  [class.connection-editor__wire--new]="recentLinkId() === connection.id"
7880
- [class.connection-editor__wire--mode-dimmed]="isModeDimmedConnection(connection)"
8498
+ [class.connection-editor__wire--mode-dimmed]="isModeDimmedConnection(connection) || (!!selectedLinkId() && selectedLinkId() !== connection.id)"
7881
8499
  [attr.d]="wirePath(connection)"
7882
8500
  (pointerenter)="previewConnection(connection)"
7883
8501
  (pointerleave)="clearPreviewConnection(connection)"
@@ -8159,9 +8777,11 @@ class ConnectionEditorComponent {
8159
8777
  </svg>
8160
8778
  <span class="connection-editor__node-core">
8161
8779
  <span class="connection-editor__node-icon" aria-hidden="true">{{ nodeIcon(node) }}</span>
8162
- <span class="connection-editor__node-title">{{ node.title }}</span>
8780
+
8163
8781
  <span class="connection-editor__node-subtitle">{{ node.subtitle }}</span>
8164
8782
  </span>
8783
+ <span class="connection-editor__node-caption" [style.font-size.px]="13 / viewport().scale"
8784
+ [style.width.px]="nodeCaptionWidth()">{{ node.title }}</span>
8165
8785
  </button>
8166
8786
 
8167
8787
  @if (visibleExpandedNodeId() === node.id) {
@@ -8239,6 +8859,8 @@ class ConnectionEditorComponent {
8239
8859
  <div
8240
8860
  class="connection-editor__ports connection-editor__ports--outputs"
8241
8861
  [class.connection-editor__ports--visible]="areNodePortsVisible(node)"
8862
+ [style.font-size.px]="11 / viewport().scale"
8863
+ [style.max-width.px]="230 / viewport().scale"
8242
8864
  [style.left.px]="node.x + 132"
8243
8865
  [style.top.px]="node.y + 24"
8244
8866
  (pointerenter)="hoveredNodeId.set(node.id)"
@@ -8263,6 +8885,8 @@ class ConnectionEditorComponent {
8263
8885
  <div
8264
8886
  class="connection-editor__ports connection-editor__ports--inputs"
8265
8887
  [class.connection-editor__ports--visible]="areNodePortsVisible(node)"
8888
+ [style.font-size.px]="11 / viewport().scale"
8889
+ [style.max-width.px]="230 / viewport().scale"
8266
8890
  [style.left.px]="node.x - 104"
8267
8891
  [style.top.px]="node.y + 24"
8268
8892
  (pointerenter)="hoveredNodeId.set(node.id)"
@@ -8520,6 +9144,76 @@ class ConnectionEditorComponent {
8520
9144
  [class.connection-editor__side-panel--contextual]="!!selectedConnection() || !!selectedEditorNode()"
8521
9145
  data-testid="page-builder-connections-side-panel"
8522
9146
  >
9147
+ @if (draftBase()) {
9148
+ <section class="connection-editor__draft" data-testid="page-builder-connections-draft">
9149
+ <div class="connection-editor__section-title">{{ tx('connections.draft.title', 'Connection draft') }}</div>
9150
+ <p>{{ tx('connections.draft.help', 'Choose the source and destination, then review the connection before applying it.') }}</p>
9151
+ <label>
9152
+ <span>{{ tx('connections.draft.source', '1 · Source / event') }}</span>
9153
+ <select #sourceSelect [value]="draftSourceId()" (change)="changeDraftEndpoint('source', sourceSelect.value)"
9154
+ data-testid="page-builder-connections-draft-source">
9155
+ <option value="">{{ tx('connections.draft.chooseSource', 'Choose a source') }}</option>
9156
+ @for (port of draftSources(); track port.id) { <option [value]="port.id">{{ draftPortLabel(port) }}</option> }
9157
+ </select>
9158
+ </label>
9159
+ <label>
9160
+ <span>{{ tx('connections.draft.target', '2 · Destination / input') }}</span>
9161
+ <select #targetSelect [value]="draftTargetId()" [disabled]="!draftSourceId()"
9162
+ (change)="changeDraftEndpoint('target', targetSelect.value)" data-testid="page-builder-connections-draft-target">
9163
+ <option value="">{{ tx('connections.draft.chooseTarget', 'Choose a destination') }}</option>
9164
+ @for (port of draftTargets(); track port.id) { <option [value]="port.id">{{ draftPortLabel(port) }}</option> }
9165
+ </select>
9166
+ </label>
9167
+ <p>{{ tx('connections.draft.prerequisites', 'Enable the source event in its component settings. For row selection, enable single selection; configure the detail as read-only when needed.') }}</p>
9168
+ @if (selectedConnection(); as connection) {
9169
+ <div class="connection-editor__draft-review">
9170
+ <span>{{ tx('connections.draft.review', 'Review the route') }}</span>
9171
+ <strong>{{ draftEndpointLabel(draftSourceId()) }}</strong>
9172
+ <span aria-hidden="true">↓</span>
9173
+ <strong>{{ draftEndpointLabel(draftTargetId()) }}</strong>
9174
+ </div>
9175
+ @for (step of connection.link.transform?.steps; track step.id ?? $index) {
9176
+ @if (step.kind === 'pick-path') {
9177
+ <label>
9178
+ <span>{{ tx('connections.draft.valuePath', 'Value to deliver · payload path') }}</span>
9179
+ <input #valuePath [value]="step.config?.['path'] ?? ''" (change)="changeDraftPickPath($index, valuePath.value)"
9180
+ data-testid="page-builder-connections-draft-path" />
9181
+ </label>
9182
+ <p>{{ connection.link.transform?.fallbackValue === null && connection.link.policy?.missingValuePolicy !== 'skip' ? tx('connections.draft.nullFallback', 'When the value is absent, deliver null (clear the destination).') : tx('connections.draft.existingFallback', 'Missing values follow the configured transform and delivery policy below.') }}</p>
9183
+ <div class="connection-editor__actions">
9184
+ <button type="button" class="connection-editor__action"
9185
+ [attr.aria-pressed]="connection.link.transform?.fallbackValue === null && connection.link.policy?.missingValuePolicy !== 'skip'"
9186
+ (click)="setDraftMissingValue('use-default')">{{ tx('connections.draft.clearMissing', 'Clear when absent') }}</button>
9187
+ <button type="button" class="connection-editor__action" [attr.aria-pressed]="connection.link.policy?.missingValuePolicy === 'skip'"
9188
+ (click)="setDraftMissingValue('skip')">{{ tx('connections.draft.keepMissing', 'Keep destination when absent') }}</button>
9189
+ </div>
9190
+ }
9191
+ }
9192
+ @if (!connection.link.transform?.steps?.length) {
9193
+ <p>{{ tx('connections.draft.wholePayload', 'The source value is delivered without a transformation. Use the advanced settings to project a field.') }}</p>
9194
+ <button type="button" class="connection-editor__action" (click)="projectDraftValue()">{{ tx('connections.draft.extract', 'Extract a field') }}</button>
9195
+ }
9196
+ }
9197
+ @for (diagnostic of draftDiagnostics(); track diagnostic.id) {
9198
+ <p class="connection-editor__diagnostic" [class.connection-editor__diagnostic--error]="diagnostic.severity === 'error' || diagnostic.severity === 'fatal'"
9199
+ role="status">{{ diagnostic.message }}</p>
9200
+ }
9201
+ @if (draftDuplicate()) { <p role="alert">{{ tx('connections.draft.duplicate', 'An identical connection already exists. Edit the existing connection or change this draft.') }}</p> }
9202
+ @if (draftConflict()) { <p role="alert">{{ tx('connections.draft.conflict', 'The page changed during editing. Cancel this draft and reopen the connection to use the latest version.') }}</p> }
9203
+ <p class="connection-editor__draft-status" role="status">{{ tx('connections.draft.pending', 'Draft · not yet applied to the page') }}</p>
9204
+ <div class="connection-editor__actions connection-editor__draft-actions">
9205
+ <button type="button" class="connection-editor__action connection-editor__action--primary"
9206
+ data-testid="page-builder-connections-draft-apply" [disabled]="!canApplyDraft()" (click)="applyDraft()">
9207
+ {{ tx('connections.draft.apply', 'Apply connection') }}
9208
+ </button>
9209
+ <button type="button" class="connection-editor__action" data-testid="page-builder-connections-draft-cancel"
9210
+ (click)="cancelDraft()">{{ tx('connections.draft.cancel', 'Cancel') }}</button>
9211
+ </div>
9212
+ </section>
9213
+ } @else {
9214
+ <p class="connection-editor__working-note" role="status">{{ tx('connections.draft.pageSave', 'Connections apply to the page in the builder. Save the page to persist your changes.') }}</p>
9215
+ }
9216
+ @if (visualDensity() === 'full' && !draftBase()) {
8523
9217
  @if (recommendedMasterDetailSuggestion(); as suggestion) {
8524
9218
  <section
8525
9219
  class="connection-editor__section connection-editor__recommended"
@@ -8540,6 +9234,7 @@ class ConnectionEditorComponent {
8540
9234
  </button>
8541
9235
  </section>
8542
9236
  }
9237
+ }
8543
9238
  <section class="connection-editor__section connection-editor__filters-section">
8544
9239
  <div class="connection-editor__section-title">{{ tx('connections.filters.title', 'Filters') }}</div>
8545
9240
  <div class="connection-editor__filters">
@@ -8622,6 +9317,14 @@ class ConnectionEditorComponent {
8622
9317
  <pre>{{ connection.link.to | json }}</pre>
8623
9318
  </div>
8624
9319
  </details>
9320
+ @if (!draftBase()) {
9321
+ <button type="button" class="connection-editor__action connection-editor__action--primary"
9322
+ data-testid="page-builder-connections-edit" (click)="beginEditConnection(connection.id)">
9323
+ {{ tx('connections.draft.edit', 'Edit connection') }}
9324
+ </button>
9325
+ }
9326
+ <details class="connection-editor__advanced" [open]="!!draftBase()">
9327
+ <summary>{{ tx('connections.draft.advanced', 'Transformation, conditions and delivery') }}</summary>
8625
9328
  @if (connection.link.to.kind === 'global-action') {
8626
9329
  <div class="connection-editor__details-block" data-testid="page-builder-connections-global-action-payload-editor">
8627
9330
  <div class="connection-editor__details-label">{{ tx('connections.globalAction.payloadTitle', 'Action payload') }}</div>
@@ -8808,6 +9511,7 @@ class ConnectionEditorComponent {
8808
9511
  }
8809
9512
  </div>
8810
9513
  </div>
9514
+ </details>
8811
9515
  @if (connection.diagnostics.length) {
8812
9516
  <div class="connection-editor__details-block" data-testid="page-builder-connections-inspector-diagnostics">
8813
9517
  <div class="connection-editor__details-label">{{ tx('connections.validation.title', 'Validation') }}</div>
@@ -8849,7 +9553,7 @@ class ConnectionEditorComponent {
8849
9553
  <button
8850
9554
  type="button"
8851
9555
  class="connection-editor__action connection-editor__action--danger"
8852
- data-testid="page-builder-connections-inspector-remove-link"
9556
+ data-testid="page-builder-connections-inspector-remove-link" [disabled]="!!draftBase()"
8853
9557
  (click)="removeLink(connection.id)"
8854
9558
  >
8855
9559
  {{ tx('connections.editor.removeLink', 'Remove connection') }}
@@ -8924,6 +9628,7 @@ class ConnectionEditorComponent {
8924
9628
  </div>
8925
9629
  </div>
8926
9630
  }
9631
+ @if (visualDensity() === 'full' && !draftBase()) {
8927
9632
  @if (smartSuggestions().length) {
8928
9633
  <div class="connection-editor__suggestions" data-testid="page-builder-connections-suggestions">
8929
9634
  <div class="connection-editor__section-title">{{ tx('connections.suggestions.title', 'Smart suggestions') }}</div>
@@ -8971,6 +9676,7 @@ class ConnectionEditorComponent {
8971
9676
  </div>
8972
9677
  </div>
8973
9678
  }
9679
+ }
8974
9680
  @if (traceEnabled()) {
8975
9681
  <div class="connection-editor__trace" data-testid="page-builder-connections-trace">
8976
9682
  <div class="connection-editor__section-title">{{ tx('connections.trace.title', 'Simulation trace') }}</div>
@@ -8993,7 +9699,7 @@ class ConnectionEditorComponent {
8993
9699
  }
8994
9700
  </section>
8995
9701
 
8996
- <section class="connection-editor__section">
9702
+ <section class="connection-editor__section connection-editor__widget-list">
8997
9703
  <div class="connection-editor__section-title">{{ tx('connections.widgets.title', 'Widgets') }}</div>
8998
9704
  @for (node of model().nodes; track node.widgetId) {
8999
9705
  <button
@@ -9028,7 +9734,7 @@ class ConnectionEditorComponent {
9028
9734
  </section>
9029
9735
  }
9030
9736
 
9031
- <section class="connection-editor__section">
9737
+ <section class="connection-editor__section connection-editor__link-list">
9032
9738
  <div class="connection-editor__section-title">{{ tx('connections.links.title', 'Links') }}</div>
9033
9739
  @for (edge of visibleEdges(); track edge.id) {
9034
9740
  <button
@@ -9038,8 +9744,8 @@ class ConnectionEditorComponent {
9038
9744
  [class.connection-editor__item--active]="selectedLinkId() === edge.id"
9039
9745
  (click)="selectLink(edge)"
9040
9746
  >
9041
- <div class="connection-editor__item-title">{{ edge.id }}</div>
9042
- <div class="connection-editor__item-copy">{{ describeEdge(edge) }}</div>
9747
+ <div class="connection-editor__item-title">{{ edgeRouteTitle(edge) }}</div>
9748
+ <div class="connection-editor__item-copy">{{ edge.id }}</div>
9043
9749
  <div class="connection-editor__badges">
9044
9750
  @if (edge.hasCondition) {
9045
9751
  <span class="connection-editor__badge">{{ tx('connections.badges.condition', 'Condition') }}</span>
@@ -9279,7 +9985,7 @@ class ConnectionEditorComponent {
9279
9985
  </section>
9280
9986
  </aside>
9281
9987
  }
9282
- `, isInline: true, styles: [":host{position:fixed;inset:0;z-index:1000;pointer-events:none}.connection-editor{--pce-fg: var(--md-sys-color-on-surface, #dce6f4);--pce-strong: var(--md-sys-color-on-surface, #f8fafc);--pce-muted: var(--md-sys-color-on-surface-variant, #94a3b8);--pce-primary: var(--md-sys-color-primary, #19c8e8);--pce-secondary: var(--md-sys-color-secondary, #b46cff);--pce-tertiary: var(--md-sys-color-tertiary, #ff9f2f);--pce-success: var(--md-sys-color-tertiary, #10b981);--pce-warning: var(--md-sys-color-error, #ff9f2f);--pce-error: var(--md-sys-color-error, #ef4444);--pce-surface-base: color-mix(in srgb, var(--md-sys-color-surface, #07111f) 80%, #020617);--pce-surface: color-mix(in srgb, var(--md-sys-color-surface-container, #08101d) 78%, transparent);--pce-surface-strong: color-mix(in srgb, var(--md-sys-color-surface-container-high, #0f172a) 88%, transparent);--pce-surface-soft: color-mix(in srgb, var(--md-sys-color-surface-container-low, #080d18) 64%, transparent);--pce-outline: color-mix(in srgb, var(--md-sys-color-outline-variant, #899ab7) 46%, transparent);--pce-outline-strong: color-mix(in srgb, var(--md-sys-color-outline, #94a3b8) 58%, transparent);--pce-grid: color-mix(in srgb, var(--md-sys-color-outline-variant, #7d8fa9) 24%, transparent);--pce-glass-highlight: color-mix(in srgb, var(--md-sys-color-on-surface, #ffffff) 8%, transparent);--pce-shadow: color-mix(in srgb, #000 56%, transparent);--pce-shadow-soft: color-mix(in srgb, #000 30%, transparent);--pce-stage-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest, #040a13) 42%, transparent);--pce-dock-bg: color-mix(in srgb, var(--md-sys-color-surface-container-high, #050912) 94%, transparent);position:absolute;inset:12px;height:calc(100vh - 24px);box-sizing:border-box;overflow:hidden;padding:16px;border-radius:18px;color:var(--pce-fg);background:radial-gradient(circle at 72% 20%,color-mix(in srgb,var(--pce-secondary) 16%,transparent),transparent 28%),radial-gradient(circle at 26% 74%,color-mix(in srgb,var(--pce-primary) 10%,transparent),transparent 34%),radial-gradient(circle at 48% 52%,color-mix(in srgb,var(--pce-tertiary) 5%,transparent),transparent 35%),linear-gradient(var(--pce-grid) 1px,transparent 1px),linear-gradient(90deg,var(--pce-grid) 1px,transparent 1px),var(--pce-surface-base);background-size:auto,auto,auto,38px 38px,38px 38px,auto;border:1px solid var(--pce-outline);box-shadow:0 28px 90px var(--pce-shadow);pointer-events:auto;display:grid;grid-template-columns:minmax(0,1fr) minmax(300px,380px);grid-template-rows:auto minmax(0,1fr) minmax(44px,118px);gap:14px}:host-context(.theme-light) .connection-editor{--pce-surface-base: color-mix(in srgb, var(--md-sys-color-surface, #ffffff) 94%, var(--md-sys-color-primary, #2563eb) 6%);--pce-surface: color-mix(in srgb, var(--md-sys-color-surface-container, #f8fafc) 82%, transparent);--pce-surface-strong: color-mix(in srgb, var(--md-sys-color-surface-container-high, #eef2ff) 88%, transparent);--pce-surface-soft: color-mix(in srgb, var(--md-sys-color-surface-container-low, #f8fafc) 70%, transparent);--pce-outline: color-mix(in srgb, var(--md-sys-color-outline-variant, #cbd5e1) 68%, transparent);--pce-outline-strong: color-mix(in srgb, var(--md-sys-color-outline, #64748b) 62%, transparent);--pce-grid: color-mix(in srgb, var(--md-sys-color-outline-variant, #cbd5e1) 34%, transparent);--pce-glass-highlight: color-mix(in srgb, #fff 64%, transparent);--pce-shadow: color-mix(in srgb, var(--md-sys-color-shadow, #64748b) 22%, transparent);--pce-shadow-soft: color-mix(in srgb, var(--md-sys-color-shadow, #64748b) 14%, transparent);--pce-stage-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest, #ffffff) 58%, transparent);--pce-dock-bg: color-mix(in srgb, var(--md-sys-color-surface-container, #f8fafc) 92%, transparent)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(0,1fr) 48px}.connection-editor__header{grid-column:1 / -1;display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:42px;padding:8px 14px;border-radius:999px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__mode-frame{position:relative;min-width:0;flex:1 1 auto;display:flex;justify-content:center}.connection-editor__close{width:32px;height:32px;flex:0 0 auto;display:inline-grid;place-items:center;border:1px solid var(--pce-outline);border-radius:999px;color:var(--pce-text);background:var(--pce-surface-strong);box-shadow:inset 0 1px var(--pce-glass-highlight);cursor:pointer;font:inherit;font-size:1.2rem;line-height:1}.connection-editor__close:hover,.connection-editor__close:focus-visible{border-color:var(--pce-outline-strong);background:color-mix(in srgb,var(--pce-accent) 16%,var(--pce-surface-strong));outline:none}.connection-editor__mode-shell{display:inline-flex;align-items:center;gap:4px;min-width:0;padding:4px;border-radius:999px;border:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-surface-strong),var(--pce-surface));box-shadow:inset 0 1px var(--pce-glass-highlight),0 10px 26px var(--pce-shadow-soft);scrollbar-width:none}.connection-editor__mode-shell::-webkit-scrollbar{display:none}.connection-editor__mode{min-height:30px;display:inline-flex;align-items:center;gap:7px;padding:0 12px;border:1px solid transparent;border-radius:999px;background:transparent;color:var(--pce-muted);cursor:pointer;font-size:.76rem;font-weight:800;white-space:nowrap;transition:background .16s ease,border-color .16s ease,color .16s ease,box-shadow .16s ease}.connection-editor__mode:hover,.connection-editor__mode--active{color:var(--pce-strong);border-color:color-mix(in srgb,var(--pce-primary) 28%,transparent);background:color-mix(in srgb,var(--pce-surface-strong) 82%,transparent);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 12%,transparent)}.connection-editor__mode-glyph{color:var(--pce-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.72rem;font-weight:900}.connection-editor__mode--active .connection-editor__mode-glyph{color:var(--pce-primary)}.connection-editor__mode-dot{width:6px;height:6px;border-radius:999px;background:var(--pce-success);box-shadow:0 0 12px color-mix(in srgb,var(--pce-success) 70%,transparent)}.connection-editor__status-pill{display:inline-flex;align-items:center;gap:8px;min-height:24px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--pce-primary) 36%,transparent);background:var(--pce-surface-soft);color:var(--pce-muted);font-size:.72rem;font-weight:800;white-space:nowrap}.connection-editor__status-pill--button{cursor:pointer;transition:border-color .16s ease,background .16s ease,color .16s ease,box-shadow .16s ease}.connection-editor__status-pill--button:hover,.connection-editor__status-pill--button:focus-visible{border-color:color-mix(in srgb,var(--pce-primary) 62%,transparent);background:color-mix(in srgb,var(--pce-primary) 12%,var(--pce-surface-soft));color:var(--pce-strong);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 14%,transparent);outline:none}.connection-editor__status-pill span{width:6px;height:6px;border-radius:999px;background:var(--pce-primary);box-shadow:0 0 12px color-mix(in srgb,var(--pce-primary) 70%,transparent)}.connection-editor__section{display:grid;gap:10px}.connection-editor__dock{grid-column:1 / -1;grid-row:3;position:relative;z-index:4;min-height:0;display:grid;border-radius:14px;border:1px solid var(--pce-outline);background:radial-gradient(circle at 16% 0%,color-mix(in srgb,var(--pce-primary) 22%,transparent),transparent 34%),radial-gradient(circle at 78% 0%,color-mix(in srgb,var(--pce-secondary) 25%,transparent),transparent 38%),linear-gradient(90deg,var(--pce-dock-bg),var(--pce-surface-strong) 46%,color-mix(in srgb,var(--pce-secondary) 14%,var(--pce-dock-bg))),var(--pce-dock-bg);box-shadow:0 18px 58px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);overflow:hidden;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__dock--collapsed .connection-editor__dock-tabs{border-block-end:0}.connection-editor__dock-tabs{display:flex;align-items:center;gap:10px;min-height:52px;padding:9px 14px;border-block-end:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-glass-highlight),transparent),color-mix(in srgb,var(--pce-surface-soft) 34%,transparent)}.connection-editor__dock-tablist{display:flex;align-items:center;gap:10px;min-width:0}.connection-editor__dock-tab,.connection-editor__dock-toggle{min-height:30px;border-radius:999px;border:1px solid transparent;background:transparent;color:var(--pce-muted);cursor:pointer;font-size:.72rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__dock-tab{display:inline-flex;align-items:center;gap:8px;padding:0 13px;white-space:nowrap}.connection-editor__dock-tab-glyph{color:var(--pce-muted);font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:.8rem;letter-spacing:0;text-transform:none}.connection-editor__dock-tab--active{color:var(--pce-strong);border-color:var(--pce-outline-strong);background:linear-gradient(180deg,color-mix(in srgb,var(--pce-strong) 14%,transparent),color-mix(in srgb,var(--pce-strong) 6%,transparent));box-shadow:0 0 0 1px color-mix(in srgb,var(--pce-shadow) 72%,transparent),0 0 22px color-mix(in srgb,var(--pce-strong) 8%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor__dock-tab--active .connection-editor__dock-tab-glyph{color:var(--pce-strong)}.connection-editor__dock-count{margin-inline-start:auto;color:color-mix(in srgb,var(--pce-muted) 62%,transparent);font-size:.7rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase;white-space:nowrap}.connection-editor__dock-toggle{width:32px;padding:0;border-color:var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-muted);font-size:.88rem;letter-spacing:0}.connection-editor__dock-body{max-height:154px;overflow:auto;padding:12px 12px 14px;border-block-start:1px solid var(--pce-outline);background:linear-gradient(90deg,color-mix(in srgb,var(--pce-primary) 9%,transparent),color-mix(in srgb,var(--pce-secondary) 10%,transparent) 62%,color-mix(in srgb,var(--pce-surface-strong) 18%,transparent)),color-mix(in srgb,var(--pce-surface-soft) 30%,transparent)}.connection-editor__dock-body pre{max-height:60px}.connection-editor__dock-links{display:grid;gap:8px}.connection-editor__dock-link-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:10px;width:100%;padding:7px 10px;border-radius:10px;border:1px solid var(--pce-outline);background:linear-gradient(90deg,var(--pce-surface-soft),color-mix(in srgb,var(--pce-secondary) 10%,var(--pce-surface-soft)) 72%,var(--pce-surface-soft)),var(--pce-surface-soft);color:var(--pce-fg);transition:border-color .15s ease,background .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__dock-link{display:grid;grid-template-columns:minmax(116px,.52fr) minmax(300px,1.72fr) minmax(160px,.9fr) auto;align-items:center;gap:12px;width:100%;min-height:42px;padding:0;border:0;border-radius:8px;background:transparent;color:inherit;cursor:pointer;text-align:start}.connection-editor__dock-link-row:hover,.connection-editor__dock-link--active{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 42%,transparent);background:linear-gradient(90deg,color-mix(in srgb,var(--pce-primary) 13%,transparent),color-mix(in srgb,var(--pce-secondary) 16%,transparent),var(--pce-surface-strong)),var(--pce-surface-strong);box-shadow:0 12px 32px var(--pce-shadow-soft),inset 0 0 18px color-mix(in srgb,var(--pce-primary) 6%,transparent)}.connection-editor__dock-link--active{border-color:#facc1575;box-shadow:0 12px 32px var(--pce-shadow-soft),0 0 24px #facc151f}.connection-editor__dock-link--new{border-color:#fb923ca3;background:linear-gradient(90deg,#fb923c2e,#a855f729,#0ea5e91f),#0f172ac2;box-shadow:0 12px 34px #02061752,0 0 30px #fb923c2e;animation:new-dock-link-flash 1.35s ease-out forwards}.connection-editor__dock-link--error{border-color:#ef444461}.connection-editor__dock-jump{min-width:62px;min-height:30px;padding:0 10px;border-radius:999px;border:1px solid color-mix(in srgb,var(--pce-primary) 28%,transparent);background:color-mix(in srgb,var(--pce-surface-soft) 54%,transparent);color:var(--pce-primary);cursor:pointer;font-size:.66rem;font-weight:900;letter-spacing:.06em;text-transform:uppercase;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease}.connection-editor__dock-jump:hover,.connection-editor__dock-jump:focus-visible{border-color:color-mix(in srgb,var(--pce-primary) 56%,transparent);background:color-mix(in srgb,var(--pce-primary) 16%,transparent);color:var(--pce-strong);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 18%,transparent);outline:none}.connection-editor__dock-link-kind{display:inline-flex;align-items:center;gap:7px;min-width:0;color:var(--pce-muted);font-size:.66rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase;white-space:nowrap}.connection-editor__dock-link-kind span{width:8px;height:8px;border-radius:999px;background:#b46cff;box-shadow:0 0 14px #b46cff7a}.connection-editor__dock-link--state .connection-editor__dock-link-kind span{background:#ff9f2f;box-shadow:0 0 16px #ff9f2f94}.connection-editor__dock-link--state-read .connection-editor__dock-link-kind span{background:#8b7dff;box-shadow:0 0 13px #8b7dff75}.connection-editor__dock-link--projection .connection-editor__dock-link-kind span{background:#19c8e8;box-shadow:0 0 10px #19c8e857}.connection-editor__dock-link--event-propagation .connection-editor__dock-link-kind span{background:#ffb454;box-shadow:0 0 15px #ffb45485}.connection-editor__dock-link--policy .connection-editor__dock-link-kind span{outline:1px solid rgba(34,197,94,.76);outline-offset:2px;box-shadow:0 0 16px #22c55e61}.connection-editor__dock-link--policy:not(.connection-editor__dock-link--state,.connection-editor__dock-link--state-read,.connection-editor__dock-link--projection,.connection-editor__dock-link--event-propagation) .connection-editor__dock-link-kind span{background:#22c55e}.connection-editor__dock-link-route{display:inline-flex;align-items:center;gap:8px;min-width:0}.connection-editor__dock-link-chip{min-width:0;overflow:hidden;padding:4px 8px;border-radius:999px;border:1px solid var(--pce-outline);background:color-mix(in srgb,var(--pce-surface-soft) 72%,transparent);color:var(--pce-fg);font-size:.72rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__dock-link-chip--from{border-color:#ff9f2f66;color:#ffbd62}.connection-editor__dock-link-chip--to{border-color:#19c8e83d;color:#7dddeb}:host-context(.theme-light) .connection-editor__dock-link-chip--from{border-color:color-mix(in srgb,var(--pce-tertiary) 46%,transparent);color:#8a4600}:host-context(.theme-light) .connection-editor__dock-link-chip--to{border-color:color-mix(in srgb,var(--pce-primary) 34%,transparent);color:#075f73}.connection-editor__dock-link-arrow{flex:0 0 auto;color:var(--pce-muted);font-weight:900}.connection-editor__dock-link-id{min-width:0;overflow:hidden;color:var(--pce-muted);font-size:.66rem;font-weight:800;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-overflow:ellipsis;white-space:nowrap}.connection-editor__dock-link-badges{display:inline-flex;justify-content:flex-end;gap:5px;min-width:70px}.connection-editor__dock-link-badges span{padding:3px 7px;border-radius:999px;background:#a855f724;color:#d8b4fe;font-size:.62rem;font-weight:900;text-transform:uppercase;white-space:nowrap}:host-context(.theme-light) .connection-editor__dock-link-badges span{background:color-mix(in srgb,var(--pce-secondary) 14%,var(--pce-surface-soft));color:#6d2a9f}.connection-editor__dock-empty{padding:12px;border-radius:12px;border:1px dashed var(--pce-outline);color:var(--pce-muted);font-size:.76rem;font-weight:700}.connection-editor__inspector{border-color:color-mix(in srgb,var(--pce-primary) 26%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 10%,transparent),color-mix(in srgb,var(--pce-secondary) 8%,transparent)),var(--pce-surface)}.connection-editor__eyebrow,.connection-editor__section-title{font-size:.72rem;text-transform:uppercase;letter-spacing:.16em;color:var(--pce-muted);font-weight:700}.connection-editor__title{font-size:1.02rem;font-weight:800}.connection-editor__subtitle,.connection-editor__item-copy,.connection-editor__item-meta,.connection-editor__details-row span,.connection-editor__details-label,.connection-editor__empty{color:var(--pce-muted);font-size:.84rem}.connection-editor__inspector-heading{display:grid;gap:4px;padding:10px 12px;border-radius:14px;background:var(--pce-surface-soft);border:1px solid var(--pce-outline)}.connection-editor__inspector-heading strong{color:var(--pce-strong);font-size:.96rem;overflow-wrap:anywhere}.connection-editor__inspector-heading span{color:var(--pce-primary);font-size:.76rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em}.connection-editor__human-summary{display:grid;gap:6px;padding:12px;border-radius:14px;border:1px solid color-mix(in srgb,var(--pce-primary) 24%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 12%,transparent),color-mix(in srgb,var(--pce-secondary) 8%,transparent)),var(--pce-surface-soft);color:var(--pce-fg);line-height:1.35}.connection-editor__nested-summary{display:grid;gap:6px;padding:10px;border-radius:12px;border:1px solid color-mix(in srgb,var(--pce-primary) 22%,transparent);background:color-mix(in srgb,var(--pce-primary) 8%,var(--pce-surface-soft))}.connection-editor__nested-summary strong{color:var(--pce-strong);font-size:.82rem}.connection-editor__nested-summary span:last-child{color:var(--pce-primary);font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:.72rem;overflow-wrap:anywhere}.connection-editor__recommended{border-color:color-mix(in srgb,var(--pce-secondary) 32%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 14%,transparent),color-mix(in srgb,var(--pce-primary) 10%,transparent)),var(--pce-surface)}.connection-editor__recommended-card{display:grid;gap:8px;width:100%;padding:13px;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-secondary) 36%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 16%,transparent),color-mix(in srgb,var(--pce-primary) 10%,transparent)),var(--pce-surface-strong);color:var(--pce-fg);text-align:start;cursor:pointer;transition:transform .16s ease,border-color .16s ease,background .16s ease}.connection-editor__recommended-card:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 52%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 22%,transparent),color-mix(in srgb,var(--pce-primary) 14%,transparent)),var(--pce-surface-strong)}.connection-editor__recommended-card strong{color:var(--pce-strong);font-size:.92rem;line-height:1.28}.connection-editor__recommended-card span:not(.connection-editor__human-summary-label):not(.connection-editor__suggestion-action),.connection-editor__recommended-card small{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__success{display:grid;gap:4px;padding:10px 12px;border-radius:14px;border:1px solid rgba(34,197,94,.34);background:#22c55e1f;color:var(--pce-fg);line-height:1.35}.connection-editor__success strong{color:#bbf7d0;font-size:.78rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__success span{color:var(--pce-strong);font-size:.82rem}.connection-editor__human-summary-label,.connection-editor__suggestion-intent{justify-self:start;padding:3px 8px;border-radius:999px;background:color-mix(in srgb,var(--pce-primary) 16%,transparent);color:#bae6fd;font-size:.66rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.connection-editor__human-summary strong{color:var(--pce-strong);font-size:.88rem}.connection-editor__human-summary span:last-child,.connection-editor__suggestion-preview{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__technical-details{display:grid;gap:10px;padding:10px 12px;border-radius:14px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft)}.connection-editor__technical-details summary{cursor:pointer;color:var(--pce-muted);font-size:.78rem;font-weight:800}.connection-editor__technical-details[open] summary{margin-bottom:10px;color:var(--pce-strong)}.connection-editor__inspector-ports{display:flex;flex-wrap:wrap;gap:6px}.connection-editor__inspector-port{max-width:100%;padding:4px 8px;border-radius:999px;border:1px solid rgba(148,163,184,.16);background:#0f172a94;color:#cbd5e1;font-size:.68rem;font-weight:800;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.connection-editor__inspector-port--input{border-color:#22d3ee57;color:#67e8f9}.connection-editor__inspector-port--output{border-color:#f59e0b57;color:#fbbf24}.connection-editor__inspector-port--state{border-color:#a855f761;color:#d8b4fe}.connection-editor__segment-kind-legend{display:grid;gap:10px;padding:12px;border-radius:16px;border:1px solid rgba(148,163,184,.14);background:linear-gradient(135deg,#0f172aa8,#080d1885),#02061757}.connection-editor__segment-kind-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.connection-editor__segment-kind-pill{min-width:0;display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:28px;padding:0 8px;border-radius:999px;border:1px solid rgba(148,163,184,.14);background:#0f172a94;color:#cbd5e1;font-size:.68rem;font-weight:900;overflow:hidden;white-space:nowrap}.connection-editor__segment-kind-pill span{width:8px;height:8px;flex:0 0 auto;border-radius:999px;background:currentColor;box-shadow:0 0 12px currentColor}.connection-editor__segment-kind-pill--input{border-color:#19c8e83d;color:#7dddeb}.connection-editor__segment-kind-pill--output{border-color:#ff9f2f61;color:#ffbd62}.connection-editor__segment-kind-pill--state{border-color:#b46cff61;color:#d9b8ff}:host-context(.theme-light) .connection-editor__segment-kind-pill{border-color:var(--pce-outline);background:color-mix(in srgb,var(--pce-surface-strong) 88%,transparent)}:host-context(.theme-light) .connection-editor__segment-kind-pill--input{color:#075f73}:host-context(.theme-light) .connection-editor__segment-kind-pill--output{color:#8a4600}:host-context(.theme-light) .connection-editor__segment-kind-pill--state{color:#6d2a9f}.connection-editor__stage{grid-column:1;grid-row:2;position:relative;min-height:0;height:100%;overflow:visible;border-radius:16px;border:1px solid var(--pce-outline);background:radial-gradient(circle at center,color-mix(in srgb,var(--pce-secondary) 7%,transparent),transparent 38%),radial-gradient(circle,var(--pce-grid) 1px,transparent 1.5px),var(--pce-stage-bg);background-size:auto,31px 31px,auto;box-shadow:inset 0 0 0 1px var(--pce-glass-highlight);cursor:grab;touch-action:none}.connection-editor__stage--panning,.connection-editor__stage:active{cursor:grabbing}.connection-editor__stage--dragging{cursor:crosshair}.connection-editor__stage--rotating{cursor:grabbing}.connection-editor__viewport{position:absolute;inset-block-start:0;inset-inline-start:0;width:900px;height:540px;overflow:visible;transform-origin:0 0;transition:transform .14s ease}.connection-editor__wires{position:absolute;inset:0;width:100%;height:100%;overflow:visible}.connection-editor__wire,.connection-editor__wire-hit{fill:none;stroke-linecap:round}.connection-editor__wire{stroke:#b46cff;stroke-width:1.45;stroke-opacity:.48;pointer-events:none;filter:url(#connection-glow);stroke-dasharray:10 8;animation:connection-flow 1.65s linear infinite}.connection-editor__wire-hit{stroke:transparent;stroke-width:18;cursor:pointer;pointer-events:stroke}.connection-editor__wire-hit--active{stroke-width:28}.connection-editor__wire--halo{stroke:#b46cff1f;stroke-width:7;stroke-dasharray:none;animation:none}.connection-editor__wire--state{stroke:#ff9f2f;stroke-width:2.25;stroke-dasharray:8 7;animation:connection-flow .92s linear infinite}.connection-editor__wire--state.connection-editor__wire--halo{stroke:#ff9f2f29;stroke-width:11}.connection-editor__wire--projection{stroke:#15b8d8;stroke-width:1.75;stroke-dasharray:20 8;animation:connection-flow 1.9s linear infinite}.connection-editor__wire--projection.connection-editor__wire--halo{stroke:#15b8d81a;stroke-width:7}.connection-editor__wire--state-read{stroke:#8b7dff;stroke-width:1.65;stroke-dasharray:3 11;animation:connection-flow 2.25s linear infinite reverse}.connection-editor__wire--state-read.connection-editor__wire--halo{stroke:#8b7dff21;stroke-width:8}.connection-editor__wire--event-propagation{stroke:#ffb454;stroke-width:2.05;stroke-dasharray:5 7;animation:connection-flow .78s linear infinite}.connection-editor__wire--event-propagation.connection-editor__wire--halo{stroke:#ffb4542e;stroke-width:10}.connection-editor__wire--policy{filter:drop-shadow(0 0 6px rgba(34,197,94,.2)) url(#connection-glow)}.connection-editor__wire--policy.connection-editor__wire--halo{stroke:#22c55e1f;stroke-width:13}.connection-editor__wire--warning{stroke:#ff9f2f}.connection-editor__wire--warning.connection-editor__wire--halo{stroke:#ff9f2f2e}.connection-editor__wire--error{stroke:#ef4444}.connection-editor__wire--error.connection-editor__wire--halo{stroke:#ef44442e}.connection-editor__wire--active{stroke:#d9b8ff;stroke-width:3;stroke-opacity:1;stroke-dasharray:5 7;animation-duration:.58s}.connection-editor__wire--halo.connection-editor__wire--active{stroke-width:16;stroke-opacity:.8;animation:selected-wire-aura 1.35s ease-in-out infinite}.connection-editor__wire--new{stroke-width:3.4;stroke-opacity:1;stroke-dasharray:640;stroke-dashoffset:640;animation:wire-draw-in .62s cubic-bezier(.16,1,.3,1) forwards,connection-flow 1.05s linear .62s infinite}.connection-editor__wire--halo.connection-editor__wire--new{stroke-width:22;stroke-opacity:0;animation:new-wire-halo 1.45s ease-out forwards}.connection-editor__wire--trace{stroke:#9b5cff;stroke-width:3.2;stroke-opacity:1;stroke-dasharray:7 6;animation:connection-flow .72s linear infinite,trace-wire-pulse 1.18s ease-in-out infinite}.connection-editor__wire--halo.connection-editor__wire--trace{stroke:#9b5cff57;stroke-width:18;stroke-opacity:.86;animation:trace-halo-pulse 1.18s ease-in-out infinite}.connection-editor__wire--mode-dimmed{stroke-opacity:.14;stroke-width:1.05;filter:none;animation-duration:2.6s}.connection-editor--guided .connection-editor__wire:not(.connection-editor__wire--active,.connection-editor__wire--new,.connection-editor__wire--trace){stroke-opacity:.34;filter:none;animation:none}.connection-editor--guided .connection-editor__wire--halo:not(.connection-editor__wire--active,.connection-editor__wire--new,.connection-editor__wire--trace){stroke-opacity:.08;animation:none}.connection-editor__wire--halo.connection-editor__wire--mode-dimmed{stroke-opacity:.05;stroke-width:5;animation:none}.connection-editor__wire-hit--mode-dimmed{stroke-width:12}.connection-editor__wire-endpoint{fill:#f8fafc;stroke:#0f172ae6;stroke-width:1.5;filter:drop-shadow(0 0 8px rgba(216,180,254,.8));pointer-events:none;animation:endpoint-pulse 1.2s ease-in-out infinite}.connection-editor__wire-endpoint--source{fill:#ff9f2f}.connection-editor__wire-endpoint--target{fill:#19c8e8}.connection-editor__connection-tooltip{position:absolute;z-index:6;width:min(312px,calc(100% - 24px));display:grid;gap:8px;padding:12px 14px;border-radius:14px;border:1px solid var(--pce-outline);background:linear-gradient(135deg,var(--pce-surface-strong),var(--pce-surface)),var(--pce-surface-strong);box-shadow:0 18px 44px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);color:var(--pce-fg);pointer-events:none;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);animation:tooltip-rise .14s ease-out}.connection-editor__connection-tooltip-callout{position:absolute;inset-block-start:100%;inset-inline-start:var(--connection-tooltip-callout-x, 42px);width:2px;height:28px;border-radius:999px;background:linear-gradient(180deg,#e2e8f0bd,#19c8e800);box-shadow:0 0 12px #19c8e852;transform:translate(-1px);pointer-events:none}.connection-editor__connection-tooltip-callout:before,.connection-editor__connection-tooltip-callout:after{content:\"\";position:absolute;border-radius:999px;pointer-events:none}.connection-editor__connection-tooltip-callout:before{inset-block-start:-5px;inset-inline-start:-5px;width:12px;height:12px;border:1px solid rgba(148,163,184,.24);background:#080d18f5;box-shadow:inset 0 1px #ffffff14}.connection-editor__connection-tooltip-callout:after{inset-block-end:-3px;inset-inline-start:-4px;width:10px;height:10px;background:#19c8e8;box-shadow:0 0 16px #19c8e88f}.connection-editor__connection-tooltip--projection{border-color:#19c8e833;background:linear-gradient(135deg,#15b8d81f,#0f172ae0),#070c16eb}.connection-editor__connection-tooltip--state{border-color:#ff9f2f66;background:linear-gradient(135deg,#ff9f2f3d,#0f172ae0),#070c16eb}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-callout{background:linear-gradient(180deg,#ffbd62d1,#ff9f2f00);box-shadow:0 0 14px #ff9f2f61}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-callout:after{background:#ff9f2f;box-shadow:0 0 18px #ff9f2fad}.connection-editor__connection-tooltip--error{border-color:#f871716b}.connection-editor__connection-tooltip--error .connection-editor__connection-tooltip-callout:after{background:#fb7185;box-shadow:0 0 18px #fb7185ad}.connection-editor__connection-tooltip-kind{display:inline-flex;align-items:center;gap:8px;color:#94a3b8;font-size:.62rem;font-weight:900;letter-spacing:.16em;text-transform:uppercase}.connection-editor__connection-tooltip-kind span{width:8px;height:8px;border-radius:999px;background:#19c8e8;box-shadow:0 0 12px #19c8e880}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-kind span{background:#ff9f2f;box-shadow:0 0 16px #ff9f2fc7}.connection-editor__connection-tooltip-row{min-width:0;display:grid;grid-template-columns:54px minmax(0,1fr);align-items:center;gap:8px;min-height:28px;padding:0 10px;border-radius:999px;background:#0f172a94}.connection-editor__connection-tooltip-row span{color:#8d9bb0;font-size:.62rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.connection-editor__connection-tooltip-row strong{min-width:0;overflow:hidden;color:#e2e8f0;font-size:.75rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__connection-tooltip-badges{display:flex;flex-wrap:wrap;gap:6px}.connection-editor__connection-tooltip-badges span{padding:3px 8px;border-radius:999px;background:#02061799;color:#fbbf24;font-size:.66rem;font-weight:900}.connection-editor__trace-overlay{position:absolute;z-index:6;inset-block-start:16px;inset-inline-start:50%;width:min(560px,calc(100% - 360px));min-width:320px;padding:12px 14px;border-radius:16px;border:1px solid rgba(34,197,94,.26);background:linear-gradient(135deg,#22c55e1f,#0ea5e91a),#080d18f0;box-shadow:0 22px 56px #02061775,0 0 42px #22c55e1f;transform:translate(-50%);pointer-events:auto;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);animation:tooltip-rise .16s ease-out}.connection-editor__trace-overlay-head,.connection-editor__trace-overlay-route{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0}.connection-editor__trace-overlay-head span,.connection-editor__trace-overlay-route span{color:#86efac;font-size:.66rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase;white-space:nowrap}.connection-editor__trace-overlay-head strong{color:#cbd5e1;font-size:.7rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace}.connection-editor__trace-overlay-route{margin-top:6px}.connection-editor__trace-overlay-controls{display:grid;grid-template-columns:28px minmax(0,1fr) 28px;align-items:center;gap:8px;margin-top:10px}.connection-editor__trace-overlay-button{width:28px;height:24px;display:inline-grid;place-items:center;border:1px solid rgba(148,163,184,.18);border-radius:999px;background:#0206176b;color:#e2e8f0;cursor:pointer;font-size:1rem;font-weight:900;line-height:1;transition:border-color .15s ease,background .15s ease,color .15s ease,opacity .15s ease}.connection-editor__trace-overlay-button:hover:not(:disabled){border-color:#22d3ee6b;background:#0ea5e929;color:#f8fafc}.connection-editor__trace-overlay-button:disabled{cursor:default;opacity:.36}.connection-editor__trace-overlay-steps{min-width:0;display:flex;align-items:center;gap:6px;overflow-x:auto;padding:2px;scrollbar-width:none}.connection-editor__trace-overlay-steps::-webkit-scrollbar{display:none}.connection-editor__trace-overlay-step{position:relative;flex:1 0 28px;min-width:28px;height:22px;border:1px solid rgba(148,163,184,.16);border-radius:999px;background:#0f172a9e;color:#94a3b8;cursor:pointer;font-size:.62rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease}.connection-editor__trace-overlay-step--active{border-color:#22d3eea3;background:linear-gradient(90deg,#22d3ee3d,#9b5cff33);color:#f8fafc;box-shadow:0 0 18px #22d3ee33}.connection-editor__trace-overlay-step--blocked{border-color:#fb71855c;color:#fecdd3}.connection-editor__trace-overlay-route strong{min-width:0;overflow:hidden;color:#f8fafc;font-size:.82rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__trace-overlay-bar{position:relative;height:5px;margin-top:10px;overflow:hidden;border-radius:999px;background:#94a3b829}.connection-editor__trace-overlay-bar span{position:absolute;inset-block:0;inset-inline-start:0;border-radius:inherit;background:linear-gradient(90deg,#22c55e,#22d3ee);box-shadow:0 0 18px #22c55e61;min-width:8%}.connection-editor__drag-wire{fill:none;stroke:#94a3b8;stroke-width:2.4;stroke-linecap:round;stroke-dasharray:6 7;pointer-events:none;filter:url(#connection-glow);animation:connection-flow 1.05s linear infinite,drag-wire-pulse 1.2s ease-in-out infinite}.connection-editor__drag-wire--halo{stroke:#94a3b829;stroke-width:14;stroke-dasharray:none;animation:drag-halo-pulse 1.2s ease-in-out infinite}.connection-editor__drag-wire--valid{stroke:#22c55e}.connection-editor__drag-wire--halo.connection-editor__drag-wire--valid{stroke:#22c55e47}.connection-editor__drag-endpoint{fill:#94a3b8;stroke:#020617eb;stroke-width:2;pointer-events:none;filter:url(#connection-glow);transform-box:fill-box;transform-origin:center;animation:endpoint-pulse 1s ease-in-out infinite}.connection-editor__drag-endpoint--source{fill:#ff9f2f}.connection-editor__drag-endpoint--target{fill:#7dddeb}.connection-editor__drag-endpoint--valid{fill:#22c55e;stroke:#bbf7d0e0}.connection-editor__wire-handle-line{stroke-width:2;stroke-linecap:round;stroke-dasharray:4 5;pointer-events:none;opacity:.88;filter:url(#connection-glow)}.connection-editor__wire-handle-line--source{stroke:#ff9f2ff0}.connection-editor__wire-handle-line--target{stroke:#19c8e8c2}.connection-editor__wire-control{fill:#0f172af5;stroke:#cbd5e1c7;stroke-width:1.8;pointer-events:none;filter:url(#connection-glow)}.connection-editor__wire-label{pointer-events:none;filter:drop-shadow(0 0 10px rgba(2,6,23,.44));animation:tooltip-rise .14s ease-out}.connection-editor__wire-label rect{fill:#080d18eb;stroke-width:1}.connection-editor__wire-label text{fill:#e2e8f0;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:8px;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.connection-editor__wire-label--source rect{stroke:#ff9f2fad}.connection-editor__wire-label--target rect{stroke:#19c8e86b}.connection-editor__zoom-controls{position:absolute;inset-inline-start:14px;inset-block-start:14px;z-index:4;display:grid;grid-template-columns:max-content;gap:6px;width:46px;pointer-events:auto}.connection-editor__tool-group,.connection-editor__trace-source{display:grid;gap:6px;padding:6px;border-radius:12px;border:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-surface-strong),var(--pce-surface));box-shadow:0 16px 36px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.connection-editor__tool-group{grid-template-columns:30px;width:max-content}.connection-editor__tool-group:nth-of-type(2){grid-template-columns:30px}.connection-editor__arrange-button{width:34px;min-height:34px;display:inline-flex;align-items:center;justify-content:center;gap:0;padding:0;border-radius:999px;border:1px solid rgba(168,85,247,.34);background:linear-gradient(90deg,#a855f72e,#22d3ee1f);color:#e9d5ff;font-size:0;font-weight:800;letter-spacing:0;cursor:pointer;opacity:1;transform:scale(1);transition:opacity .14s ease,transform .14s ease,border-color .14s ease,background .14s ease}.connection-editor--guided .connection-editor__arrange-button{opacity:.34;transform:scale(.9);border-color:color-mix(in srgb,var(--pce-outline) 82%,transparent);background:color-mix(in srgb,var(--pce-surface-soft) 84%,transparent)}.connection-editor--guided .connection-editor__zoom-controls:hover .connection-editor__arrange-button,.connection-editor--guided .connection-editor__zoom-controls:focus-within .connection-editor__arrange-button,.connection-editor--guided .connection-editor__arrange-button:hover,.connection-editor--guided .connection-editor__arrange-button:focus-visible,.connection-editor--guided .connection-editor__arrange-button--active{opacity:1;transform:scale(1);border-color:#a855f757;background:linear-gradient(90deg,#a855f72e,#22d3ee1f)}.connection-editor__arrange-button span{color:#c084fc;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.78rem;font-weight:900}.connection-editor__tool-button{width:30px;height:30px;border-radius:999px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-fg);font-size:.9rem;font-weight:800;letter-spacing:.02em;cursor:pointer;line-height:1}.connection-editor__tool-button:hover:not(:disabled){border-color:color-mix(in srgb,var(--pce-primary) 42%,transparent);color:var(--pce-primary);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 16%,transparent)}.connection-editor__history-button:disabled{cursor:not-allowed;opacity:.42}.connection-editor__arrange-button--active{border-color:#22d3ee94;background:linear-gradient(90deg,#0ea5e93d,#22d3ee2e);color:#cffafe}.connection-editor__trace-source{gap:4px;max-height:230px;overflow:auto}.connection-editor__trace-source-title{padding:2px 4px 6px;color:var(--pce-muted);font-size:.64rem;font-weight:900;letter-spacing:.18em;text-transform:uppercase}.connection-editor__trace-source-item{min-width:0;min-height:30px;display:flex;align-items:center;gap:8px;padding:0 8px;border:0;border-radius:8px;background:transparent;color:var(--pce-fg);cursor:pointer;font-size:.74rem;font-weight:800;text-align:start}.connection-editor__trace-source-item span{color:#f59e0b;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.72rem;font-weight:900}.connection-editor__trace-source-item:hover,.connection-editor__trace-source-item--active{background:var(--pce-surface-strong);color:var(--pce-strong);box-shadow:inset 3px 0 #f59e0bd1}.connection-editor__minimap{position:absolute;inset-inline-end:14px;inset-block-end:14px;z-index:4;width:184px;height:124px;padding:18px 10px 10px;overflow:hidden;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-primary) 30%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 12%,transparent),var(--pce-surface-strong)),var(--pce-surface);box-shadow:0 20px 48px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);cursor:pointer;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);opacity:1;transform:scale(1);transform-origin:100% 100%;transition:opacity .16s ease,transform .16s ease,border-color .16s ease,box-shadow .16s ease}.connection-editor--guided .connection-editor__minimap{opacity:.54;transform:scale(.88);border-color:color-mix(in srgb,var(--pce-outline) 64%,transparent);box-shadow:0 14px 34px color-mix(in srgb,var(--pce-shadow-soft) 70%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor--guided .connection-editor__minimap:hover,.connection-editor--guided .connection-editor__minimap:focus-visible,.connection-editor--guided .connection-editor__minimap--panning{opacity:1;transform:scale(1)}.connection-editor__minimap:hover,.connection-editor__minimap--panning{border-color:#22d3ee75;box-shadow:0 20px 48px var(--pce-shadow-soft),0 0 24px color-mix(in srgb,var(--pce-primary) 14%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor__minimap--panning{cursor:grabbing}.connection-editor__minimap:before{content:\"\";position:absolute;inset:22px 10px 10px;border-radius:10px;background-image:radial-gradient(circle,rgba(125,211,252,.18) 1px,transparent 1px);background-size:22px 22px;opacity:.58;pointer-events:none}.connection-editor__minimap-title{position:absolute;inset-block-start:7px;inset-inline-start:12px;color:var(--pce-muted);font-size:.6rem;font-weight:900;letter-spacing:.16em;text-transform:uppercase}.connection-editor__minimap svg{position:relative;width:100%;height:100%;display:block;overflow:visible}.connection-editor__minimap-wire{fill:none;stroke:#b46cff8f;stroke-width:4;stroke-linecap:round;opacity:.86}.connection-editor__minimap-wire--state{stroke:#ff9f2fdb}.connection-editor__minimap-wire--state-read{stroke:#8b7dffb8;stroke-dasharray:4 9}.connection-editor__minimap-wire--projection{stroke:#19c8e88a;stroke-dasharray:18 8}.connection-editor__minimap-wire--event-propagation{stroke:#ffb454cc;stroke-dasharray:6 7}.connection-editor__minimap-wire--policy{filter:drop-shadow(0 0 6px rgba(34,197,94,.28))}.connection-editor__minimap-wire--trace{stroke:#9b5cffe0;stroke-width:5;stroke-dasharray:12 9;animation:connection-flow 1s linear infinite}.connection-editor__minimap-wire--active{stroke:#facc15eb;stroke-width:6;filter:drop-shadow(0 0 8px rgba(250,204,21,.38))}.connection-editor__minimap-wire--mode-dimmed{opacity:.2;stroke-width:2.6;filter:none}.connection-editor__minimap-node{fill:#f59e0be0;stroke:#020617db;stroke-width:4;filter:drop-shadow(0 0 7px rgba(245,158,11,.24))}.connection-editor__minimap-node--state{fill:#a855f7e6;filter:drop-shadow(0 0 8px rgba(168,85,247,.34))}.connection-editor__minimap-node--selected{fill:#facc15f5;stroke:#fef08aeb;filter:drop-shadow(0 0 10px rgba(250,204,21,.42))}.connection-editor__minimap-node--trace-source{fill:#facc15f5;filter:drop-shadow(0 0 10px rgba(250,204,21,.44))}.connection-editor__minimap-node--trace-reacting{fill:#22c55eeb;filter:drop-shadow(0 0 10px rgba(34,197,94,.42))}.connection-editor__minimap-node--mode-dimmed{opacity:.28;filter:none}.connection-editor__minimap-viewport{fill:#7dd3fc1f;stroke:#7dd3fce6;stroke-width:5;stroke-dasharray:18 10;filter:drop-shadow(0 0 10px rgba(125,211,252,.3));transition:x .12s ease,y .12s ease,width .12s ease,height .12s ease}.connection-editor__minimap--panning .connection-editor__minimap-viewport{fill:#22d3ee2e;stroke:#22d3eef5;stroke-dasharray:none}.connection-editor__node{position:absolute;z-index:10;width:168px;height:168px;transform:translate(-84px,-84px);border:0;padding:0;background:transparent;color:inherit;cursor:pointer;transition:filter .16s ease}.connection-editor__node--dragging{cursor:grabbing;filter:drop-shadow(0 0 22px rgba(34,211,238,.26));z-index:12}.connection-editor__node--connection-focus{filter:drop-shadow(0 0 16px rgba(34,211,238,.2))}.connection-editor__node--compat-target .connection-editor__node-ring{box-shadow:0 0 0 2px #22c55e66,0 0 0 18px #22c55e0b,0 0 32px #22c55e29}.connection-editor__node--mode-dimmed{opacity:.42;filter:grayscale(.22) saturate(.56)}.connection-editor__node--quiet{opacity:.58;filter:saturate(.68) brightness(.86)}.connection-editor__node--mode-dimmed .connection-editor__node-ring{animation:none;box-shadow:0 0 0 1px #2dd4bf14,0 0 14px #0ea5e90f}.connection-editor__node--mode-dimmed .connection-editor__node-halo{stroke-opacity:.28}.connection-editor__node-ring,.connection-editor__node-core,.connection-editor__node-svg{position:absolute;inset:0}.connection-editor__node-ring,.connection-editor__node-core{display:grid;place-items:center;clip-path:polygon(30% 0,70% 0,100% 30%,100% 70%,70% 100%,30% 100%,0 70%,0 30%)}.connection-editor__node-ring{background:radial-gradient(circle at center,transparent 0 44%,rgba(7,17,31,.96) 45% 57%,transparent 58%),conic-gradient(from -28deg,#06b6d4b3 0 28%,#f59e0b9e 28% 48%,#a855f780 48% 68%,#06b6d4a3 68% 100%);box-shadow:0 0 0 1px #2dd4bf2e,0 0 26px #0ea5e929;animation:node-breathe 2.8s ease-in-out infinite}.connection-editor__node-svg{overflow:visible;filter:drop-shadow(0 0 16px rgba(14,165,233,.16))}.connection-editor__node-halo{fill:transparent;stroke:#38bdf838;stroke-width:1;stroke-dasharray:0;pointer-events:none}.connection-editor__node-halo:nth-child(2){stroke:#38bdf824}.connection-editor__node-halo:nth-child(3){stroke:#94a3b81a}.connection-editor__node-segment{fill-opacity:.28;stroke-opacity:.72;stroke-width:1.15;transition:fill-opacity .16s ease,stroke-opacity .16s ease,stroke-width .16s ease;cursor:grab}.connection-editor__node:hover .connection-editor__node-segment,.connection-editor__node--selected .connection-editor__node-segment,.connection-editor__node--connection-focus .connection-editor__node-segment{fill-opacity:.54;stroke-opacity:.95;stroke-width:1.55}.connection-editor__node-segment--connection-focus{fill-opacity:.76;stroke-opacity:1;stroke-width:2;filter:drop-shadow(0 0 8px rgba(34,211,238,.76));animation:segment-focus-sweep 1.45s ease-in-out infinite}.connection-editor__node-segment--source{fill-opacity:.86;stroke-opacity:1;stroke-width:2.45;filter:drop-shadow(0 0 12px rgba(251,146,60,.82))}.connection-editor__node-segment--compatible{fill-opacity:.74;stroke-opacity:1;stroke-width:2;filter:drop-shadow(0 0 10px rgba(34,197,94,.72));animation:snap-pulse 1s ease-in-out infinite}.connection-editor__node-segment--compat-preview{fill-opacity:.86;stroke-opacity:1;stroke-width:2.45;filter:drop-shadow(0 0 13px rgba(245,158,11,.78))}.connection-editor__node-segment--candidate{fill-opacity:.92;stroke-opacity:1;stroke-width:2.8;filter:drop-shadow(0 0 16px rgba(34,197,94,.86))}.connection-editor__node-segment--incompatible{fill-opacity:.12;stroke-opacity:.28;cursor:not-allowed}.connection-editor__node-segment--invalid-drop{fill-opacity:.72;stroke:#fb7185;stroke-opacity:1;stroke-width:3;filter:drop-shadow(0 0 16px rgba(244,63,94,.84));animation:invalid-drop-jolt .36s ease-out}.connection-editor__node-segment--state{fill-opacity:.44;filter:drop-shadow(0 0 8px rgba(180,108,255,.2))}.connection-editor__node-segment--mode-dimmed{fill-opacity:.08;stroke-opacity:.2;stroke-width:.9;filter:none}.connection-editor__node-anchor{opacity:.95;filter:drop-shadow(0 0 6px currentColor)}.connection-editor__node-rotate-handle{cursor:grab;opacity:0;pointer-events:none;transition:opacity .16s ease,filter .16s ease;filter:drop-shadow(0 0 8px rgba(34,211,238,.28))}.connection-editor__node:hover .connection-editor__node-rotate-handle,.connection-editor__node--selected .connection-editor__node-rotate-handle,.connection-editor__node--rotating .connection-editor__node-rotate-handle{opacity:1;pointer-events:auto}.connection-editor__node--rotating .connection-editor__node-rotate-handle{cursor:grabbing;filter:drop-shadow(0 0 12px rgba(34,211,238,.54))}.connection-editor__node-rotate-hit{fill:transparent}.connection-editor__node-rotate-dot{fill:var(--pce-surface-strong);stroke:var(--pce-primary);stroke-opacity:.88;stroke-width:1.4}.connection-editor__node-rotate-arc,.connection-editor__node-rotate-arrow{fill:none;stroke:var(--pce-primary);stroke-linecap:round;stroke-linejoin:round;stroke-opacity:.96;stroke-width:1.2;pointer-events:none}.connection-editor__segment-icon,.connection-editor__segment-label{pointer-events:none;fill:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-weight:800;paint-order:stroke;stroke:#020617c2;stroke-width:3px;stroke-linejoin:round}.connection-editor__segment-icon{font-size:12px;fill:#19c8e8;opacity:1;transition:opacity .14s ease}.connection-editor__segment-icon--output{fill:#ff9f2f}.connection-editor--guided .connection-editor__segment-icon:not(.connection-editor__segment-icon--guided-visible){opacity:0}.connection-editor--guided .connection-editor__node--rotating .connection-editor__segment-icon{opacity:0}.connection-editor__segment-label{font-size:9.5px;letter-spacing:0;fill:#e2e8f0e0;opacity:1;transition:opacity .14s ease}.connection-editor--guided .connection-editor__segment-label:not(.connection-editor__segment-label--guided-visible){opacity:0}.connection-editor--guided .connection-editor__node--rotating .connection-editor__segment-label{opacity:0}.connection-editor__node-core{inset:43px;padding:12px;border-radius:999px;clip-path:none;display:flex;flex-direction:column;align-items:center;justify-content:center;background:radial-gradient(circle at center,#111827 0 58%,#060b14);border:1px solid rgba(226,232,240,.1);box-shadow:0 12px 28px #00000073,inset 0 1px #ffffff0d;color:#f8fafc;text-shadow:0 1px 2px rgba(0,0,0,.62)}.connection-editor__node-icon{display:block;max-width:44px;overflow:hidden;color:#f8fafc;font-family:Material Symbols Outlined,Material Icons,sans-serif;font-size:1.22rem;font-weight:400;line-height:1;text-align:center;text-transform:none;letter-spacing:0;white-space:nowrap;filter:drop-shadow(0 0 10px rgba(226,232,240,.18))}.connection-editor__node--state .connection-editor__node-ring{background:radial-gradient(circle at center,transparent 0 44%,rgba(7,17,31,.96) 45% 57%,transparent 58%),conic-gradient(from 14deg,#a855f7c7 0 42%,#7e3af26b 42% 78%,#f59e0b47 78% 100%);box-shadow:0 0 0 1px #a855f738,0 0 30px #a855f733}.connection-editor__node--selected .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15bd,0 0 34px #facc153d}.connection-editor__node--connection-focus .connection-editor__node-ring{box-shadow:0 0 0 2px #22d3eead,0 0 38px #22d3ee42;animation:node-focus-breathe 1.7s ease-in-out infinite}.connection-editor__node--trace-source .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15c7,0 0 0 18px #facc150f,0 0 54px #facc154d;animation:trace-source-pulse 1.38s ease-in-out infinite}.connection-editor__node--trace-source:before{content:\"\";position:absolute;z-index:0;inset:-44px;border-radius:999px;border:2px solid rgba(250,204,21,.36);background:radial-gradient(circle at center,transparent 0 64%,rgba(250,204,21,.05) 65% 66%,transparent 67%),conic-gradient(from -20deg,#facc1500,#facc1552,#facc1500);box-shadow:0 0 36px #facc151f;opacity:.92;pointer-events:none;animation:trace-source-orbit 2.8s linear infinite}.connection-editor__node--trace-reacting .connection-editor__node-ring{box-shadow:0 0 0 2px #22c55e94,0 0 46px #22c55e38;animation:trace-reacting-pulse 1.38s ease-in-out infinite}.connection-editor__node--trace-source:after,.connection-editor__node--trace-reacting:after{position:absolute;z-index:3;inset-inline-start:50%;inset-block-start:-42px;padding:0;border:0;background:transparent;color:#facc15;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.68rem;font-weight:900;letter-spacing:.18em;text-transform:uppercase;transform:translate(-50%);text-shadow:0 0 12px rgba(250,204,21,.52);pointer-events:none}.connection-editor__node--trace-source:after{content:attr(data-trace-label)}.connection-editor__node--trace-reacting:after{content:attr(data-trace-label);color:#86efac;text-shadow:0 0 12px rgba(34,197,94,.44)}.connection-editor__node--trace-source .connection-editor__node-segment--output{fill-opacity:.66;stroke-width:2.2;filter:drop-shadow(0 0 14px rgba(255,159,47,.42))}.connection-editor__node--expanded .connection-editor__node-ring{box-shadow:0 0 0 2px #22d3eebd,0 0 34px #22d3ee3d}.connection-editor__node--rotating .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15c7,0 0 42px #facc1547;animation:node-focus-breathe 1.1s ease-in-out infinite}.connection-editor__node--rotating .connection-editor__node-segment{fill-opacity:.68;stroke-opacity:1;stroke-width:1.9;cursor:grabbing}.connection-editor__node-title,.connection-editor__node-subtitle{display:block;max-width:92px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center}.connection-editor__node-title{color:#f8fafc;font-size:.82rem;font-weight:800}.connection-editor__node-subtitle{margin-top:4px;color:#a7b4c8;font-size:.62rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__ports{position:absolute;z-index:30;display:grid;gap:6px;width:104px;opacity:0;pointer-events:none;transform:translateY(3px);transition:opacity .16s ease,transform .16s ease}.connection-editor__ports--visible{opacity:1;pointer-events:auto;transform:translateY(0)}.connection-editor__ports--outputs{z-index:42}.connection-editor__ports--inputs{z-index:36}.connection-editor__segment-popover{position:absolute;z-index:80;width:292px;max-height:320px;overflow:auto;display:grid;gap:10px;padding:14px;border-radius:16px;border:1px solid rgba(148,163,184,.18);background:linear-gradient(135deg,#0f172af5,#110c20f0),#090c16eb;box-shadow:0 28px 72px #0000007a,0 0 48px #a855f71a,inset 0 1px #ffffff0d;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__segment-popover-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding-bottom:2px}.connection-editor__segment-popover-title{color:#94a3b8;font-size:.68rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase}.connection-editor__segment-popover-hint{margin-top:5px;color:#64748b;font-size:.66rem;font-weight:700}.connection-editor__segment-rotation{flex:0 0 auto;min-width:54px;padding:5px 8px;border-radius:999px;border:1px solid rgba(34,211,238,.28);background:#082f4957;color:#7dd3fc;font-size:.66rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-align:center;box-shadow:inset 0 0 16px #0ea5e914}.connection-editor__segment-row{display:grid;grid-template-columns:28px 72px minmax(0,1fr) auto;align-items:center;gap:8px;min-height:40px;padding:7px;border-radius:12px;border:1px solid rgba(148,163,184,.12);background:linear-gradient(90deg,#0f172ac7,#0f172a75),#0f172a99;transition:border-color .15s ease,background .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__segment-row--input{border-color:#22d3ee42}.connection-editor__segment-row--output{border-color:#f59e0b47}.connection-editor__segment-row--state{border-color:#a855f74d}.connection-editor__segment-row--source,.connection-editor__segment-row--candidate{transform:translate(2px);box-shadow:0 0 22px #f59e0b24,inset 0 0 20px #f59e0b14}.connection-editor__segment-row--compatible{border-color:#22d3ee6b;box-shadow:inset 0 0 18px #22d3ee14}.connection-editor__segment-row--compat-preview{border-color:#f59e0b94;background:linear-gradient(90deg,#f59e0b1f,#0f172a8a),#0f172ab3;box-shadow:0 0 22px #f59e0b1f,inset 0 0 18px #f59e0b14}.connection-editor__segment-row--invalid-drop{border-color:#fb7185b3;background:linear-gradient(90deg,#f43f5e24,#0f172a94),#0f172ab3;box-shadow:0 0 24px #f43f5e2e,inset 0 0 18px #f43f5e1a;animation:invalid-drop-jolt .36s ease-out}.connection-editor__segment-index{color:#64748b;font-size:.64rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;letter-spacing:.08em}.connection-editor__segment-kind{display:inline-flex;align-items:center;gap:6px;color:#94a3b8;font-size:.62rem;font-weight:800;text-transform:uppercase;min-width:0;white-space:nowrap}.connection-editor__segment-kind-dot{width:7px;height:7px;border-radius:999px;background:#94a3b8;box-shadow:0 0 12px #94a3b847}.connection-editor__segment-row--input .connection-editor__segment-kind-dot{background:#22d3ee;box-shadow:0 0 12px #22d3ee61}.connection-editor__segment-row--output .connection-editor__segment-kind-dot{background:#fb923c;box-shadow:0 0 12px #fb923c66}.connection-editor__segment-row--state .connection-editor__segment-kind-dot{background:#a855f7;box-shadow:0 0 12px #a855f76b}.connection-editor__segment-label{display:inline-flex;align-items:center;gap:5px;min-width:0;overflow:hidden;color:#e2e8f0;font-size:.74rem;font-weight:700;text-overflow:ellipsis;white-space:nowrap}.connection-editor__segment-badge{flex:0 0 auto;padding:2px 5px;border-radius:999px;border:1px solid rgba(56,189,248,.28);background:#0ea5e91a;color:#bae6fd;font-size:.55rem;font-weight:900;text-transform:uppercase}.connection-editor__segment-path{grid-column:3 / 4;min-width:0;margin-top:-5px;overflow:hidden;color:#93c5fd;font-size:.62rem;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-overflow:ellipsis;white-space:nowrap}.connection-editor__segment-row--nested{min-height:56px;border-color:#38bdf84d;background:linear-gradient(90deg,#0ea5e91a,#0f172a80),#0f172aa3}.connection-editor__segment-actions{display:inline-flex;gap:4px}.connection-editor__segment-action{width:24px;height:24px;border-radius:999px;border:1px solid rgba(148,163,184,.18);background:#060c16b8;color:#dbeafe;cursor:pointer;font-weight:900;line-height:1}.connection-editor__segment-action:hover{border-color:#22d3ee75;box-shadow:0 0 14px #22d3ee29}.connection-editor__port{min-height:24px;border-radius:999px;border:1px solid rgba(148,163,184,.14);background:#080d18c2;color:#cbd5e1;font-size:.64rem;font-weight:700;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:crosshair;box-shadow:0 8px 20px #00000038}.connection-editor--guided .connection-editor__ports--visible .connection-editor__port:not(.connection-editor__port--guided-visible){display:none}.connection-editor__port--output{border-color:#f59e0b75;color:#fbbf24}.connection-editor__port--input{border-color:#06b6d475;color:#22d3ee}.connection-editor__port--state{border-color:#a855f785;color:#c084fc}.connection-editor__port--compatible{background:#22c55e29;box-shadow:0 0 0 1px #22c55e61,0 0 18px #22c55e2e;animation:snap-pulse 1s ease-in-out infinite}.connection-editor__port--source{border-color:#fb923cb8;background:#fb923c29;color:#fed7aa;box-shadow:0 0 0 1px #fb923c73,0 0 20px #fb923c2e}.connection-editor__port--candidate{background:#22c55e3d;box-shadow:0 0 0 2px #22c55e94,0 0 24px #22c55e3d}.connection-editor__port--incompatible{opacity:.42;cursor:not-allowed;filter:grayscale(.45)}.connection-editor__port--invalid-drop{opacity:1;border-color:#fb7185c7;color:#fecdd3;background:#f43f5e2e;box-shadow:0 0 0 2px #f43f5e75,0 0 24px #f43f5e38;filter:none;animation:invalid-drop-jolt .36s ease-out}.connection-editor__port--connection-focus{border-color:#facc15ad;color:#fde68a;background:#facc151f;box-shadow:0 0 0 1px #facc156b,0 0 20px #facc152e;animation:port-focus-pulse 1.25s ease-in-out infinite}.connection-editor__drag-tip{position:absolute;z-index:6;display:grid;gap:4px;width:min(330px,calc(100% - 28px));padding:10px 12px;border-radius:14px;border:1px solid rgba(125,211,252,.26);background:linear-gradient(135deg,#0ea5e91f,#0f172aeb),#080d18f0;box-shadow:0 18px 44px #0206176b,0 0 28px #0ea5e91a;font-size:.76rem;pointer-events:none;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);animation:tooltip-rise .14s ease-out}.connection-editor__drag-tip--valid{border-color:#22c55e66;background:linear-gradient(135deg,#22c55e29,#0f172aeb),#080d18f0;box-shadow:0 18px 44px #0206176b,0 0 30px #22c55e24}.connection-editor__drag-tip--invalid{z-index:7;border-color:#fb71858a;background:linear-gradient(135deg,#f43f5e2e,#0f172af0),#080d18f5;box-shadow:0 18px 44px #0206176b,0 0 34px #f43f5e29;animation:tooltip-rise .14s ease-out,invalid-drop-jolt .36s ease-out}.connection-editor__invalid-drop-pulse{position:absolute;z-index:6;width:16px;height:16px;border-radius:999px;border:2px solid rgba(251,113,133,.88);background:#f43f5e38;box-shadow:0 0 0 8px #f43f5e1a,0 0 28px #f43f5e3d;pointer-events:none;transform:translate(-50%,-50%);animation:invalid-drop-ping 1.2s ease-out both}.connection-editor__drag-tip span{color:#7dd3fc;text-transform:uppercase;letter-spacing:.12em;font-size:.62rem;font-weight:800}.connection-editor__drag-tip strong{min-width:0;overflow:hidden;color:#f8fafc;font-size:.78rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__drag-tip small{color:#94a3b8;font-size:.68rem;font-weight:700}.connection-editor__drag-tip--valid small{color:#86efac}.connection-editor__drag-tip--invalid span,.connection-editor__drag-tip--invalid small{color:#fda4af}.connection-editor__summary{grid-column:1;grid-row:2;align-self:end;justify-self:start;z-index:6;display:flex;flex-wrap:wrap;gap:6px;padding:0 0 12px 12px;pointer-events:none}.connection-editor__side-panel{grid-column:2;grid-row:2 / 3;position:relative;z-index:8;min-width:0;min-height:0;display:grid;align-content:start;gap:14px;overflow:auto;padding-inline-end:4px;scrollbar-color:var(--pce-outline-strong) var(--pce-surface-soft)}.connection-editor__side-panel--contextual .connection-editor__inspector{order:-3}.connection-editor__side-panel--contextual .connection-editor__recommended{order:2}.connection-editor__side-panel--contextual .connection-editor__filters-section{order:3}.connection-editor__side-panel--contextual .connection-editor__recommended,.connection-editor__side-panel--contextual .connection-editor__filters-section{opacity:.82}.connection-editor__metric,.connection-editor__item{border-radius:16px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.connection-editor__metric{min-width:auto;display:inline-flex;align-items:baseline;gap:7px;padding:6px 9px;border-radius:999px;background:color-mix(in srgb,var(--pce-surface-strong) 86%,transparent);box-shadow:0 10px 26px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight)}.connection-editor__metric span{color:var(--pce-muted);font-size:.62rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__metric strong,.connection-editor__details-row strong{color:var(--pce-strong);font-size:.78rem}.connection-editor__filters,.connection-editor__actions,.connection-editor__badges{display:flex;gap:8px;flex-wrap:wrap}.connection-editor__filter,.connection-editor__action{border:1px solid var(--pce-outline-strong);background:var(--pce-surface-soft);color:var(--pce-fg);border-radius:999px;padding:6px 10px;font-size:.75rem;font-weight:700;cursor:pointer}.connection-editor__filter--active,.connection-editor__action:hover{border-color:color-mix(in srgb,var(--pce-secondary) 62%,transparent);background:color-mix(in srgb,var(--pce-secondary) 16%,var(--pce-surface-soft));color:var(--pce-strong)}.connection-editor__action--danger{border-color:#f8717161;color:#fecaca}.connection-editor__item{width:100%;text-align:start;padding:12px;display:grid;gap:6px;cursor:pointer;color:inherit}.connection-editor__item--active{border-color:#a855f7b8;background:linear-gradient(90deg,#0ea5e91a,#a855f721)}.connection-editor__item-title{font-weight:800;color:#f8fafc;word-break:break-word}.connection-editor__badge{padding:3px 8px;border-radius:999px;background:#0ea5e92e;color:#bae6fd;font-size:.72rem;font-weight:800}.connection-editor__section{min-width:0;padding:14px;border-radius:18px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight),0 18px 50px var(--pce-shadow-soft);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__badge--warning{background:#facc1524;color:#fde68a}.connection-editor__badge--error{background:#ef444424;color:#fecaca}.connection-editor__diagnostics{display:grid;gap:8px}.connection-editor__diagnostic{display:grid;gap:4px;padding:10px;border-radius:12px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-fg);font-size:.78rem;line-height:1.35}.connection-editor__diagnostic--warning{border-color:#f59e0b47;background:#f59e0b1a}.connection-editor__diagnostic--error{border-color:#ef44445c;background:#ef44441f}.connection-editor__diagnostic-severity{font-size:.68rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase;color:#f8fafc}.connection-editor__suggestions{display:grid;gap:10px;margin-top:14px;padding-top:14px;border-top:1px solid var(--pce-outline)}.connection-editor__suggestion{display:grid;gap:8px;width:100%;padding:12px;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-secondary) 26%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 12%,transparent),color-mix(in srgb,var(--pce-primary) 8%,transparent)),var(--pce-surface-strong);color:var(--pce-fg);text-align:start;cursor:pointer;transition:transform .16s ease,border-color .16s ease,background .16s ease}.connection-editor__suggestion:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 46%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 18%,transparent),color-mix(in srgb,var(--pce-primary) 12%,transparent)),var(--pce-surface-strong)}.connection-editor__suggestion-route{display:grid;gap:4px;font-size:.78rem}.connection-editor__suggestion-route strong{color:var(--pce-strong);font-size:.82rem}.connection-editor__suggestion-reason{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__suggestion-diagnostic{padding:10px 12px;border-radius:14px;border:1px solid rgba(245,158,11,.28);background:#f59e0b1a;color:var(--pce-strong);font-size:.78rem;line-height:1.35}.connection-editor__suggestion-outcome{color:var(--pce-strong);font-size:.84rem;line-height:1.3}.connection-editor__suggestion-action{justify-self:start;padding:3px 8px;border-radius:999px;background:#22d3ee24;color:#67e8f9;font-size:.68rem;font-weight:900;letter-spacing:.06em;text-transform:uppercase}:host-context(.theme-light) .connection-editor__suggestion-action{background:color-mix(in srgb,var(--pce-primary) 12%,var(--pce-surface-soft));color:#075f73}.connection-editor__trace{display:grid;gap:10px;margin-top:14px;padding-top:14px;border-top:1px solid var(--pce-outline)}.connection-editor__trace-summary{color:var(--pce-muted);font-size:.78rem;line-height:1.35}.connection-editor__trace-step{display:grid;grid-template-columns:24px minmax(76px,auto) 1fr;gap:8px;align-items:center;padding:9px;border-radius:12px;border:1px solid rgba(34,211,238,.18);background:var(--pce-surface-soft);font-size:.76rem}.connection-editor__trace-step--blocked{border-color:#ef444457;background:#ef44441a}.connection-editor__trace-order{display:inline-grid;place-items:center;width:22px;height:22px;border-radius:999px;background:#22d3ee29;color:#67e8f9;font-weight:900}.connection-editor__trace-phase{color:var(--pce-strong);font-weight:900;text-transform:uppercase;letter-spacing:.06em;font-size:.66rem}.connection-editor__trace-copy{min-width:0;color:var(--pce-fg);word-break:break-word}.connection-editor__transform-steps{display:grid;gap:8px}.connection-editor__transform-step{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:8px;align-items:center;padding:9px;border-radius:12px;border:1px solid rgba(168,85,247,.22);background:var(--pce-surface-soft)}.connection-editor__transform-kind{min-width:0;color:var(--pce-strong);font-weight:900;word-break:break-word}.connection-editor__transform-phase{color:var(--pce-muted);font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}.connection-editor__details-row,.connection-editor__details-block{display:grid;gap:6px}.connection-editor__payload-input{width:100%;min-height:128px;box-sizing:border-box;resize:vertical;border:1px solid var(--pce-outline);border-radius:8px;background:color-mix(in srgb,var(--pce-surface) 92%,black);color:var(--pce-fg);padding:10px;font:.76rem/1.45 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.connection-editor__surface-action-editor{display:block;max-height:min(58vh,620px);overflow:auto;border:1px solid var(--pce-outline);border-radius:8px;background:color-mix(in srgb,var(--pce-surface) 96%,black);padding:10px;margin-bottom:10px}.connection-editor__rule-grid{display:grid;gap:10px}.connection-editor__rule-card{display:grid;gap:8px;padding:10px;border-radius:12px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft)}.connection-editor__rule-heading{color:var(--pce-fg);font-size:.72rem;font-weight:900;text-transform:uppercase;letter-spacing:.06em}pre{margin:0;padding:12px;border-radius:12px;background:color-mix(in srgb,var(--pce-surface-strong) 82%,#020617);color:var(--pce-fg);overflow:auto;font-size:.75rem}@keyframes connection-flow{to{stroke-dashoffset:-38}}@keyframes drag-wire-pulse{0%,to{stroke-opacity:.76}50%{stroke-opacity:1}}@keyframes drag-halo-pulse{0%,to{stroke-opacity:.48}50%{stroke-opacity:.82}}@keyframes endpoint-pulse{0%,to{opacity:.78;transform:scale(1)}50%{opacity:1;transform:scale(1.25)}}@keyframes selected-wire-aura{0%,to{stroke-opacity:.46}50%{stroke-opacity:.92}}@keyframes wire-draw-in{0%{stroke-dashoffset:640;opacity:.32}70%{opacity:1}to{stroke-dashoffset:0;opacity:1}}@keyframes new-wire-halo{0%{stroke-opacity:0;stroke-width:30}28%{stroke-opacity:.78}to{stroke-opacity:0;stroke-width:12}}@keyframes new-dock-link-flash{0%{transform:translateY(-2px);box-shadow:0 16px 40px #0206175c,0 0 42px #fb923c47}to{transform:translateY(0)}}@keyframes trace-wire-pulse{0%,to{stroke-opacity:.78}50%{stroke-opacity:1}}@keyframes trace-halo-pulse{0%,to{stroke-opacity:.34}50%{stroke-opacity:.86}}@keyframes tooltip-rise{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes invalid-drop-jolt{0%,to{transform:translate(0)}28%{transform:translate(-3px)}56%{transform:translate(3px)}}@keyframes invalid-drop-ping{0%{opacity:1;transform:translate(-50%,-50%) scale(.72)}72%{opacity:.74;transform:translate(-50%,-50%) scale(1.35)}to{opacity:0;transform:translate(-50%,-50%) scale(1.8)}}@keyframes node-breathe{50%{transform:scale(1.035)}}@keyframes node-focus-breathe{0%,to{transform:scale(1)}50%{transform:scale(1.055)}}@keyframes trace-source-pulse{50%{transform:scale(1.04);box-shadow:0 0 0 3px #facc15db,0 0 0 22px #facc1514,0 0 64px #facc1557}}@keyframes trace-source-orbit{to{transform:rotate(360deg)}}@keyframes trace-reacting-pulse{50%{transform:scale(1.045);box-shadow:0 0 0 3px #22c55ead,0 0 62px #22c55e4d}}@keyframes segment-focus-sweep{0%,to{stroke-opacity:.82}50%{stroke-opacity:1}}@keyframes snap-pulse{50%{filter:brightness(1.16)}}@keyframes port-focus-pulse{50%{transform:translateY(-1px);box-shadow:0 0 0 2px #facc158f,0 0 26px #facc153d}}@media(prefers-reduced-motion:reduce){.connection-editor__viewport,.connection-editor__node,.connection-editor__node-ring,.connection-editor__node-core,.connection-editor__node-segment,.connection-editor__dock-link,.connection-editor__port{transition-duration:1ms}.connection-editor__wire,.connection-editor__wire--state,.connection-editor__wire--projection,.connection-editor__wire--state-read,.connection-editor__wire--event-propagation,.connection-editor__wire--halo.connection-editor__wire--active,.connection-editor__wire--trace,.connection-editor__wire--halo.connection-editor__wire--trace,.connection-editor__wire-endpoint,.connection-editor__connection-tooltip,.connection-editor__dock-link--new,.connection-editor__trace-overlay,.connection-editor__trace-overlay-step,.connection-editor__drag-wire,.connection-editor__drag-wire--halo,.connection-editor__drag-endpoint,.connection-editor__drag-tip,.connection-editor__node-ring,.connection-editor__node--connection-focus .connection-editor__node-ring,.connection-editor__node--trace-source .connection-editor__node-ring,.connection-editor__node--trace-source:before,.connection-editor__node--trace-reacting .connection-editor__node-ring,.connection-editor__node--rotating .connection-editor__node-ring,.connection-editor__node-segment--connection-focus,.connection-editor__node-segment--compatible,.connection-editor__node-segment--invalid-drop,.connection-editor__segment-popover,.connection-editor__port--compatible,.connection-editor__port--invalid-drop,.connection-editor__port--connection-focus,.connection-editor__invalid-drop,.connection-editor__invalid-drop-pulse{animation:none}.connection-editor__wire{stroke-dashoffset:0}.connection-editor__wire--new{stroke-dasharray:10 8;stroke-dashoffset:0;opacity:1}.connection-editor__wire--halo.connection-editor__wire--new{stroke-width:12;stroke-opacity:.16}.connection-editor__wire-endpoint,.connection-editor__drag-endpoint,.connection-editor__node-ring,.connection-editor__node--trace-source:before{transform:none}.connection-editor__invalid-drop-pulse{opacity:0}}@media(max-width:900px){:host{inset:0}.connection-editor{inset:8px;height:calc(100vh - 16px);padding:12px;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(260px,1fr) minmax(150px,30vh) minmax(76px,112px)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(260px,1fr) minmax(150px,30vh) 48px}.connection-editor__header,.connection-editor__stage,.connection-editor__summary,.connection-editor__side-panel,.connection-editor__dock{grid-column:1}.connection-editor__header{align-items:stretch;flex-wrap:wrap;border-radius:18px}.connection-editor__mode-frame{order:3;width:100%;max-width:100%}.connection-editor__close{margin-inline-start:auto}.connection-editor__mode-frame:before,.connection-editor__mode-frame:after{content:\"\";position:absolute;z-index:2;inset-block:4px;width:24px;border-radius:999px;pointer-events:none}.connection-editor__mode-frame:before{inset-inline-start:1px;background:linear-gradient(90deg,#080d18f0,#080d1800)}.connection-editor__mode-frame:after{inset-inline-end:1px;background:linear-gradient(270deg,#080d18f0,#080d1800)}.connection-editor__mode-shell{width:100%;max-width:100%;overflow-x:auto;scroll-padding-inline:28px;-webkit-overflow-scrolling:touch}.connection-editor__subtitle{margin-inline-start:auto}.connection-editor__stage{grid-row:2;min-height:260px}.connection-editor__summary{grid-row:2;max-width:calc(100% - 24px)}.connection-editor__side-panel{grid-row:3;max-height:30vh}.connection-editor__dock{grid-row:4;min-height:0}.connection-editor__connection-tooltip{width:min(620px,calc(100% - 24px));max-height:min(260px,calc(100% - 24px));overflow:auto}.connection-editor__dock-tabs{width:100%;min-width:0;min-height:0;flex-wrap:wrap;align-items:flex-start}.connection-editor__dock-tablist{width:100%;flex-wrap:wrap}.connection-editor__dock-count{flex:1 1 100%;margin-inline-start:0}.connection-editor__dock-body{width:100%;min-width:0;max-height:76px;box-sizing:border-box}.connection-editor__dock-body pre{white-space:pre-wrap;overflow-wrap:anywhere}.connection-editor__dock-link{grid-template-columns:1fr;align-items:start}.connection-editor__dock-link-route{flex-wrap:wrap}.connection-editor__dock-link-badges{justify-content:flex-start}.connection-editor__trace-overlay{inset-inline-start:14px;width:min(420px,calc(100% - 28px));min-width:0;transform:none}.connection-editor__drag-tip{width:min(300px,calc(100% - 28px))}.connection-editor__segment-popover{width:min(280px,calc(100% - 28px));max-height:min(280px,calc(100% - 28px))}.connection-editor__minimap{inset-inline-end:10px;inset-block-end:10px;width:132px;height:94px;padding:16px 8px 8px;border-radius:14px}.connection-editor__minimap:before{inset:20px 8px 8px;background-size:18px 18px}.connection-editor__minimap-title{inset-block-start:6px;inset-inline-start:10px;font-size:.54rem}}@media(max-width:640px){.connection-editor{inset:4px;height:calc(100vh - 8px);padding:8px;gap:8px;grid-template-rows:auto minmax(240px,1fr) minmax(140px,28vh) minmax(72px,104px)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(240px,1fr) minmax(140px,28vh) 46px}.connection-editor__header{gap:8px;padding:10px}.connection-editor__mode-shell{padding:4px;gap:2px}.connection-editor__mode{min-height:28px;gap:4px;padding:0 7px;font-size:.68rem}.connection-editor__mode-glyph{font-size:.64rem}.connection-editor__stage{min-height:240px}.connection-editor__zoom-controls{inset-inline-start:8px;inset-block-start:8px;gap:6px;width:42px}.connection-editor__tool-group,.connection-editor__trace-source{gap:6px;padding:8px;border-radius:12px}.connection-editor__tool-group{grid-template-columns:28px}.connection-editor__tool-group:nth-of-type(2){grid-template-columns:28px}.connection-editor__tool-button{width:28px;height:28px}.connection-editor__arrange-button,.connection-editor__simulate-button{width:30px;min-height:30px}.connection-editor__summary{gap:6px;padding-inline-end:8px}.connection-editor__side-panel{max-height:28vh}.connection-editor__dock{border-radius:16px}.connection-editor__dock-tabs,.connection-editor__dock-tablist{gap:4px}.connection-editor__dock-tab{padding:0 9px}.connection-editor__dock-count{font-size:.62rem}.connection-editor__dock-body{max-height:66px;padding:8px}.connection-editor__minimap{width:116px;height:84px}.connection-editor__segment-row{grid-template-columns:24px minmax(54px,.55fr) minmax(0,1fr) auto}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: SurfaceOpenActionEditorComponent, selector: "praxis-surface-open-action-editor", inputs: ["value", "hostKind"], outputs: ["valueChange"] }, { kind: "pipe", type: i2$3.JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9988
+ `, isInline: true, styles: [":host{position:fixed;inset:0;z-index:1000;pointer-events:none}.connection-editor{--pce-fg: var(--md-sys-color-on-surface, #dce6f4);--pce-strong: var(--md-sys-color-on-surface, #f8fafc);--pce-muted: var(--md-sys-color-on-surface-variant, #94a3b8);--pce-primary: var(--md-sys-color-primary, #19c8e8);--pce-secondary: var(--md-sys-color-secondary, #b46cff);--pce-tertiary: var(--md-sys-color-tertiary, #ff9f2f);--pce-success: var(--md-sys-color-tertiary, #10b981);--pce-warning: var(--md-sys-color-error, #ff9f2f);--pce-error: var(--md-sys-color-error, #ef4444);--pce-surface-base: color-mix(in srgb, var(--md-sys-color-surface, #07111f) 80%, #020617);--pce-surface: color-mix(in srgb, var(--md-sys-color-surface-container, #08101d) 78%, transparent);--pce-surface-strong: color-mix(in srgb, var(--md-sys-color-surface-container-high, #0f172a) 88%, transparent);--pce-surface-soft: color-mix(in srgb, var(--md-sys-color-surface-container-low, #080d18) 64%, transparent);--pce-outline: color-mix(in srgb, var(--md-sys-color-outline-variant, #899ab7) 46%, transparent);--pce-outline-strong: color-mix(in srgb, var(--md-sys-color-outline, #94a3b8) 58%, transparent);--pce-grid: color-mix(in srgb, var(--md-sys-color-outline-variant, #7d8fa9) 24%, transparent);--pce-glass-highlight: color-mix(in srgb, var(--md-sys-color-on-surface, #ffffff) 8%, transparent);--pce-shadow: color-mix(in srgb, #000 56%, transparent);--pce-shadow-soft: color-mix(in srgb, #000 30%, transparent);--pce-stage-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest, #040a13) 42%, transparent);--pce-dock-bg: color-mix(in srgb, var(--md-sys-color-surface-container-high, #050912) 94%, transparent);position:absolute;inset:12px;height:calc(100vh - 24px);box-sizing:border-box;overflow:hidden;padding:16px;border-radius:18px;color:var(--pce-fg);background:radial-gradient(circle at 72% 20%,color-mix(in srgb,var(--pce-secondary) 16%,transparent),transparent 28%),radial-gradient(circle at 26% 74%,color-mix(in srgb,var(--pce-primary) 10%,transparent),transparent 34%),radial-gradient(circle at 48% 52%,color-mix(in srgb,var(--pce-tertiary) 5%,transparent),transparent 35%),linear-gradient(var(--pce-grid) 1px,transparent 1px),linear-gradient(90deg,var(--pce-grid) 1px,transparent 1px),var(--pce-surface-base);background-size:auto,auto,auto,38px 38px,38px 38px,auto;border:1px solid var(--pce-outline);box-shadow:0 28px 90px var(--pce-shadow);pointer-events:auto;display:grid;grid-template-columns:minmax(0,1fr) minmax(300px,380px);grid-template-rows:auto minmax(0,1fr) minmax(44px,118px);gap:14px}:host-context(.theme-light) .connection-editor{--pce-surface-base: color-mix(in srgb, var(--md-sys-color-surface, #ffffff) 94%, var(--md-sys-color-primary, #2563eb) 6%);--pce-surface: color-mix(in srgb, var(--md-sys-color-surface-container, #f8fafc) 82%, transparent);--pce-surface-strong: color-mix(in srgb, var(--md-sys-color-surface-container-high, #eef2ff) 88%, transparent);--pce-surface-soft: color-mix(in srgb, var(--md-sys-color-surface-container-low, #f8fafc) 70%, transparent);--pce-outline: color-mix(in srgb, var(--md-sys-color-outline-variant, #cbd5e1) 68%, transparent);--pce-outline-strong: color-mix(in srgb, var(--md-sys-color-outline, #64748b) 62%, transparent);--pce-grid: color-mix(in srgb, var(--md-sys-color-outline-variant, #cbd5e1) 34%, transparent);--pce-glass-highlight: color-mix(in srgb, #fff 64%, transparent);--pce-shadow: color-mix(in srgb, var(--md-sys-color-shadow, #64748b) 22%, transparent);--pce-shadow-soft: color-mix(in srgb, var(--md-sys-color-shadow, #64748b) 14%, transparent);--pce-stage-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest, #ffffff) 58%, transparent);--pce-dock-bg: color-mix(in srgb, var(--md-sys-color-surface-container, #f8fafc) 92%, transparent)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(0,1fr) 48px}.connection-editor__header{grid-column:1 / -1;display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:42px;padding:8px 14px;border-radius:999px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__mode-frame{position:relative;min-width:0;flex:1 1 auto;display:flex;justify-content:center}.connection-editor__close{width:32px;height:32px;flex:0 0 auto;display:inline-grid;place-items:center;border:1px solid var(--pce-outline);border-radius:999px;color:var(--pce-text);background:var(--pce-surface-strong);box-shadow:inset 0 1px var(--pce-glass-highlight);cursor:pointer;font:inherit;font-size:1.2rem;line-height:1}.connection-editor__close:hover,.connection-editor__close:focus-visible{border-color:var(--pce-outline-strong);background:color-mix(in srgb,var(--pce-accent) 16%,var(--pce-surface-strong));outline:none}.connection-editor__mode-shell{display:inline-flex;align-items:center;gap:4px;min-width:0;padding:4px;border-radius:999px;border:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-surface-strong),var(--pce-surface));box-shadow:inset 0 1px var(--pce-glass-highlight),0 10px 26px var(--pce-shadow-soft);scrollbar-width:none}.connection-editor__mode-shell::-webkit-scrollbar{display:none}.connection-editor__mode{min-height:30px;display:inline-flex;align-items:center;gap:7px;padding:0 12px;border:1px solid transparent;border-radius:999px;background:transparent;color:var(--pce-muted);cursor:pointer;font-size:.76rem;font-weight:800;white-space:nowrap;transition:background .16s ease,border-color .16s ease,color .16s ease,box-shadow .16s ease}.connection-editor__mode:hover,.connection-editor__mode--active{color:var(--pce-strong);border-color:color-mix(in srgb,var(--pce-primary) 28%,transparent);background:color-mix(in srgb,var(--pce-surface-strong) 82%,transparent);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 12%,transparent)}.connection-editor__mode-glyph{color:var(--pce-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.72rem;font-weight:900}.connection-editor__mode--active .connection-editor__mode-glyph{color:var(--pce-primary)}.connection-editor__mode-dot{width:6px;height:6px;border-radius:999px;background:var(--pce-success);box-shadow:0 0 12px color-mix(in srgb,var(--pce-success) 70%,transparent)}.connection-editor__status-pill{display:inline-flex;align-items:center;gap:8px;min-height:24px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--pce-primary) 36%,transparent);background:var(--pce-surface-soft);color:var(--pce-muted);font-size:.72rem;font-weight:800;white-space:nowrap}.connection-editor__status-pill--button{cursor:pointer;transition:border-color .16s ease,background .16s ease,color .16s ease,box-shadow .16s ease}.connection-editor__status-pill--button:hover,.connection-editor__status-pill--button:focus-visible{border-color:color-mix(in srgb,var(--pce-primary) 62%,transparent);background:color-mix(in srgb,var(--pce-primary) 12%,var(--pce-surface-soft));color:var(--pce-strong);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 14%,transparent);outline:none}.connection-editor__status-pill span{width:6px;height:6px;border-radius:999px;background:var(--pce-primary);box-shadow:0 0 12px color-mix(in srgb,var(--pce-primary) 70%,transparent)}.connection-editor__section{display:grid;gap:10px}.connection-editor__dock{grid-column:1 / -1;grid-row:3;position:relative;z-index:4;min-height:0;display:grid;border-radius:14px;border:1px solid var(--pce-outline);background:radial-gradient(circle at 16% 0%,color-mix(in srgb,var(--pce-primary) 22%,transparent),transparent 34%),radial-gradient(circle at 78% 0%,color-mix(in srgb,var(--pce-secondary) 25%,transparent),transparent 38%),linear-gradient(90deg,var(--pce-dock-bg),var(--pce-surface-strong) 46%,color-mix(in srgb,var(--pce-secondary) 14%,var(--pce-dock-bg))),var(--pce-dock-bg);box-shadow:0 18px 58px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);overflow:hidden;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__dock--collapsed .connection-editor__dock-tabs{border-block-end:0}.connection-editor__dock-tabs{display:flex;align-items:center;gap:10px;min-height:52px;padding:9px 14px;border-block-end:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-glass-highlight),transparent),color-mix(in srgb,var(--pce-surface-soft) 34%,transparent)}.connection-editor__dock-tablist{display:flex;align-items:center;gap:10px;min-width:0}.connection-editor__dock-tab,.connection-editor__dock-toggle{min-height:30px;border-radius:999px;border:1px solid transparent;background:transparent;color:var(--pce-muted);cursor:pointer;font-size:.72rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__dock-tab{display:inline-flex;align-items:center;gap:8px;padding:0 13px;white-space:nowrap}.connection-editor__dock-tab-glyph{color:var(--pce-muted);font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:.8rem;letter-spacing:0;text-transform:none}.connection-editor__dock-tab--active{color:var(--pce-strong);border-color:var(--pce-outline-strong);background:linear-gradient(180deg,color-mix(in srgb,var(--pce-strong) 14%,transparent),color-mix(in srgb,var(--pce-strong) 6%,transparent));box-shadow:0 0 0 1px color-mix(in srgb,var(--pce-shadow) 72%,transparent),0 0 22px color-mix(in srgb,var(--pce-strong) 8%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor__dock-tab--active .connection-editor__dock-tab-glyph{color:var(--pce-strong)}.connection-editor__dock-count{margin-inline-start:auto;color:color-mix(in srgb,var(--pce-muted) 62%,transparent);font-size:.7rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase;white-space:nowrap}.connection-editor__dock-toggle{width:32px;padding:0;border-color:var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-muted);font-size:.88rem;letter-spacing:0}.connection-editor__dock-body{max-height:154px;overflow:auto;padding:12px 12px 14px;border-block-start:1px solid var(--pce-outline);background:linear-gradient(90deg,color-mix(in srgb,var(--pce-primary) 9%,transparent),color-mix(in srgb,var(--pce-secondary) 10%,transparent) 62%,color-mix(in srgb,var(--pce-surface-strong) 18%,transparent)),color-mix(in srgb,var(--pce-surface-soft) 30%,transparent)}.connection-editor__dock-body pre{max-height:60px}.connection-editor__dock-links{display:grid;gap:8px}.connection-editor__dock-link-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:10px;width:100%;padding:7px 10px;border-radius:10px;border:1px solid var(--pce-outline);background:linear-gradient(90deg,var(--pce-surface-soft),color-mix(in srgb,var(--pce-secondary) 10%,var(--pce-surface-soft)) 72%,var(--pce-surface-soft)),var(--pce-surface-soft);color:var(--pce-fg);transition:border-color .15s ease,background .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__dock-link{display:grid;grid-template-columns:minmax(116px,.52fr) minmax(300px,1.72fr) minmax(160px,.9fr) auto;align-items:center;gap:12px;width:100%;min-height:42px;padding:0;border:0;border-radius:8px;background:transparent;color:inherit;cursor:pointer;text-align:start}.connection-editor__dock-link-row:hover,.connection-editor__dock-link--active{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 42%,transparent);background:linear-gradient(90deg,color-mix(in srgb,var(--pce-primary) 13%,transparent),color-mix(in srgb,var(--pce-secondary) 16%,transparent),var(--pce-surface-strong)),var(--pce-surface-strong);box-shadow:0 12px 32px var(--pce-shadow-soft),inset 0 0 18px color-mix(in srgb,var(--pce-primary) 6%,transparent)}.connection-editor__dock-link--active{border-color:#facc1575;box-shadow:0 12px 32px var(--pce-shadow-soft),0 0 24px #facc151f}.connection-editor__dock-link--new{border-color:#fb923ca3;background:linear-gradient(90deg,#fb923c2e,#a855f729,#0ea5e91f),#0f172ac2;box-shadow:0 12px 34px #02061752,0 0 30px #fb923c2e;animation:new-dock-link-flash 1.35s ease-out forwards}.connection-editor__dock-link--error{border-color:#ef444461}.connection-editor__dock-jump{min-width:62px;min-height:30px;padding:0 10px;border-radius:999px;border:1px solid color-mix(in srgb,var(--pce-primary) 28%,transparent);background:color-mix(in srgb,var(--pce-surface-soft) 54%,transparent);color:var(--pce-primary);cursor:pointer;font-size:.66rem;font-weight:900;letter-spacing:.06em;text-transform:uppercase;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease}.connection-editor__dock-jump:hover,.connection-editor__dock-jump:focus-visible{border-color:color-mix(in srgb,var(--pce-primary) 56%,transparent);background:color-mix(in srgb,var(--pce-primary) 16%,transparent);color:var(--pce-strong);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 18%,transparent);outline:none}.connection-editor__dock-link-kind{display:inline-flex;align-items:center;gap:7px;min-width:0;color:var(--pce-muted);font-size:.66rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase;white-space:nowrap}.connection-editor__dock-link-kind span{width:8px;height:8px;border-radius:999px;background:#b46cff;box-shadow:0 0 14px #b46cff7a}.connection-editor__dock-link--state .connection-editor__dock-link-kind span{background:#ff9f2f;box-shadow:0 0 16px #ff9f2f94}.connection-editor__dock-link--state-read .connection-editor__dock-link-kind span{background:#8b7dff;box-shadow:0 0 13px #8b7dff75}.connection-editor__dock-link--projection .connection-editor__dock-link-kind span{background:#19c8e8;box-shadow:0 0 10px #19c8e857}.connection-editor__dock-link--event-propagation .connection-editor__dock-link-kind span{background:#ffb454;box-shadow:0 0 15px #ffb45485}.connection-editor__dock-link--policy .connection-editor__dock-link-kind span{outline:1px solid rgba(34,197,94,.76);outline-offset:2px;box-shadow:0 0 16px #22c55e61}.connection-editor__dock-link--policy:not(.connection-editor__dock-link--state,.connection-editor__dock-link--state-read,.connection-editor__dock-link--projection,.connection-editor__dock-link--event-propagation) .connection-editor__dock-link-kind span{background:#22c55e}.connection-editor__dock-link-route{display:inline-flex;align-items:center;gap:8px;min-width:0}.connection-editor__dock-link-chip{min-width:0;overflow:hidden;padding:4px 8px;border-radius:999px;border:1px solid var(--pce-outline);background:color-mix(in srgb,var(--pce-surface-soft) 72%,transparent);color:var(--pce-fg);font-size:.72rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__dock-link-chip--from{border-color:#ff9f2f66;color:#ffbd62}.connection-editor__dock-link-chip--to{border-color:#19c8e83d;color:#7dddeb}:host-context(.theme-light) .connection-editor__dock-link-chip--from{border-color:color-mix(in srgb,var(--pce-tertiary) 46%,transparent);color:#8a4600}:host-context(.theme-light) .connection-editor__dock-link-chip--to{border-color:color-mix(in srgb,var(--pce-primary) 34%,transparent);color:#075f73}.connection-editor__dock-link-arrow{flex:0 0 auto;color:var(--pce-muted);font-weight:900}.connection-editor__dock-link-id{min-width:0;overflow:hidden;color:var(--pce-muted);font-size:.66rem;font-weight:800;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-overflow:ellipsis;white-space:nowrap}.connection-editor__dock-link-badges{display:inline-flex;justify-content:flex-end;gap:5px;min-width:70px}.connection-editor__dock-link-badges span{padding:3px 7px;border-radius:999px;background:#a855f724;color:#d8b4fe;font-size:.62rem;font-weight:900;text-transform:uppercase;white-space:nowrap}:host-context(.theme-light) .connection-editor__dock-link-badges span{background:color-mix(in srgb,var(--pce-secondary) 14%,var(--pce-surface-soft));color:#6d2a9f}.connection-editor__dock-empty{padding:12px;border-radius:12px;border:1px dashed var(--pce-outline);color:var(--pce-muted);font-size:.76rem;font-weight:700}.connection-editor__inspector{border-color:color-mix(in srgb,var(--pce-primary) 26%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 10%,transparent),color-mix(in srgb,var(--pce-secondary) 8%,transparent)),var(--pce-surface)}.connection-editor__eyebrow,.connection-editor__section-title{font-size:.72rem;text-transform:uppercase;letter-spacing:.16em;color:var(--pce-muted);font-weight:700}.connection-editor__title{font-size:1.02rem;font-weight:800}.connection-editor__subtitle,.connection-editor__item-copy,.connection-editor__item-meta,.connection-editor__details-row span,.connection-editor__details-label,.connection-editor__empty{color:var(--pce-muted);font-size:.84rem}.connection-editor__inspector-heading{display:grid;gap:4px;padding:10px 12px;border-radius:14px;background:var(--pce-surface-soft);border:1px solid var(--pce-outline)}.connection-editor__inspector-heading strong{color:var(--pce-strong);font-size:.96rem;overflow-wrap:anywhere}.connection-editor__inspector-heading span{color:var(--pce-primary);font-size:.76rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em}.connection-editor__human-summary{display:grid;gap:6px;padding:12px;border-radius:14px;border:1px solid color-mix(in srgb,var(--pce-primary) 24%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 12%,transparent),color-mix(in srgb,var(--pce-secondary) 8%,transparent)),var(--pce-surface-soft);color:var(--pce-fg);line-height:1.35}.connection-editor__nested-summary{display:grid;gap:6px;padding:10px;border-radius:12px;border:1px solid color-mix(in srgb,var(--pce-primary) 22%,transparent);background:color-mix(in srgb,var(--pce-primary) 8%,var(--pce-surface-soft))}.connection-editor__nested-summary strong{color:var(--pce-strong);font-size:.82rem}.connection-editor__nested-summary span:last-child{color:var(--pce-primary);font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:.72rem;overflow-wrap:anywhere}.connection-editor__recommended{border-color:color-mix(in srgb,var(--pce-secondary) 32%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 14%,transparent),color-mix(in srgb,var(--pce-primary) 10%,transparent)),var(--pce-surface)}.connection-editor__recommended-card{display:grid;gap:8px;width:100%;padding:13px;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-secondary) 36%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 16%,transparent),color-mix(in srgb,var(--pce-primary) 10%,transparent)),var(--pce-surface-strong);color:var(--pce-fg);text-align:start;cursor:pointer;transition:transform .16s ease,border-color .16s ease,background .16s ease}.connection-editor__recommended-card:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 52%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 22%,transparent),color-mix(in srgb,var(--pce-primary) 14%,transparent)),var(--pce-surface-strong)}.connection-editor__recommended-card strong{color:var(--pce-strong);font-size:.92rem;line-height:1.28}.connection-editor__recommended-card span:not(.connection-editor__human-summary-label):not(.connection-editor__suggestion-action),.connection-editor__recommended-card small{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__success{display:grid;gap:4px;padding:10px 12px;border-radius:14px;border:1px solid rgba(34,197,94,.34);background:#22c55e1f;color:var(--pce-fg);line-height:1.35}.connection-editor__success strong{color:#bbf7d0;font-size:.78rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__success span{color:var(--pce-strong);font-size:.82rem}.connection-editor__human-summary-label,.connection-editor__suggestion-intent{justify-self:start;padding:3px 8px;border-radius:999px;background:color-mix(in srgb,var(--pce-primary) 16%,transparent);color:#bae6fd;font-size:.66rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.connection-editor__human-summary strong{color:var(--pce-strong);font-size:.88rem}.connection-editor__human-summary span:last-child,.connection-editor__suggestion-preview{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__technical-details{display:grid;gap:10px;padding:10px 12px;border-radius:14px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft)}.connection-editor__technical-details summary{cursor:pointer;color:var(--pce-muted);font-size:.78rem;font-weight:800}.connection-editor__technical-details[open] summary{margin-bottom:10px;color:var(--pce-strong)}.connection-editor__inspector-ports{display:flex;flex-wrap:wrap;gap:6px}.connection-editor__inspector-port{max-width:100%;padding:4px 8px;border-radius:999px;border:1px solid rgba(148,163,184,.16);background:#0f172a94;color:#cbd5e1;font-size:.68rem;font-weight:800;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.connection-editor__inspector-port--input{border-color:#22d3ee57;color:#67e8f9}.connection-editor__inspector-port--output{border-color:#f59e0b57;color:#fbbf24}.connection-editor__inspector-port--state{border-color:#a855f761;color:#d8b4fe}.connection-editor__segment-kind-legend{display:grid;gap:10px;padding:12px;border-radius:16px;border:1px solid rgba(148,163,184,.14);background:linear-gradient(135deg,#0f172aa8,#080d1885),#02061757}.connection-editor__segment-kind-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.connection-editor__segment-kind-pill{min-width:0;display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:28px;padding:0 8px;border-radius:999px;border:1px solid rgba(148,163,184,.14);background:#0f172a94;color:#cbd5e1;font-size:.68rem;font-weight:900;overflow:hidden;white-space:nowrap}.connection-editor__segment-kind-pill span{width:8px;height:8px;flex:0 0 auto;border-radius:999px;background:currentColor;box-shadow:0 0 12px currentColor}.connection-editor__segment-kind-pill--input{border-color:#19c8e83d;color:#7dddeb}.connection-editor__segment-kind-pill--output{border-color:#ff9f2f61;color:#ffbd62}.connection-editor__segment-kind-pill--state{border-color:#b46cff61;color:#d9b8ff}:host-context(.theme-light) .connection-editor__segment-kind-pill{border-color:var(--pce-outline);background:color-mix(in srgb,var(--pce-surface-strong) 88%,transparent)}:host-context(.theme-light) .connection-editor__segment-kind-pill--input{color:#075f73}:host-context(.theme-light) .connection-editor__segment-kind-pill--output{color:#8a4600}:host-context(.theme-light) .connection-editor__segment-kind-pill--state{color:#6d2a9f}.connection-editor__stage{grid-column:1;grid-row:2;position:relative;min-height:0;height:100%;overflow:visible;border-radius:16px;border:1px solid var(--pce-outline);background:radial-gradient(circle at center,color-mix(in srgb,var(--pce-secondary) 7%,transparent),transparent 38%),radial-gradient(circle,var(--pce-grid) 1px,transparent 1.5px),var(--pce-stage-bg);background-size:auto,31px 31px,auto;box-shadow:inset 0 0 0 1px var(--pce-glass-highlight);cursor:grab;touch-action:none}.connection-editor__stage--panning,.connection-editor__stage:active{cursor:grabbing}.connection-editor__stage--dragging{cursor:crosshair}.connection-editor__stage--rotating{cursor:grabbing}.connection-editor__viewport{position:absolute;inset-block-start:0;inset-inline-start:0;width:900px;height:540px;overflow:visible;transform-origin:0 0;transition:transform .14s ease}.connection-editor__wires{position:absolute;inset:0;width:100%;height:100%;overflow:visible}.connection-editor__wire,.connection-editor__wire-hit{fill:none;stroke-linecap:round}.connection-editor__wire{stroke:#b46cff;stroke-width:1.45;stroke-opacity:.48;pointer-events:none;filter:url(#connection-glow);stroke-dasharray:10 8;animation:connection-flow 1.65s linear infinite}.connection-editor__wire-hit{stroke:transparent;stroke-width:18;cursor:pointer;pointer-events:stroke}.connection-editor__wire-hit--active{stroke-width:28}.connection-editor__wire--halo{stroke:#b46cff1f;stroke-width:7;stroke-dasharray:none;animation:none}.connection-editor__wire--state{stroke:#ff9f2f;stroke-width:2.25;stroke-dasharray:8 7;animation:connection-flow .92s linear infinite}.connection-editor__wire--state.connection-editor__wire--halo{stroke:#ff9f2f29;stroke-width:11}.connection-editor__wire--projection{stroke:#15b8d8;stroke-width:1.75;stroke-dasharray:20 8;animation:connection-flow 1.9s linear infinite}.connection-editor__wire--projection.connection-editor__wire--halo{stroke:#15b8d81a;stroke-width:7}.connection-editor__wire--state-read{stroke:#8b7dff;stroke-width:1.65;stroke-dasharray:3 11;animation:connection-flow 2.25s linear infinite reverse}.connection-editor__wire--state-read.connection-editor__wire--halo{stroke:#8b7dff21;stroke-width:8}.connection-editor__wire--event-propagation{stroke:#ffb454;stroke-width:2.05;stroke-dasharray:5 7;animation:connection-flow .78s linear infinite}.connection-editor__wire--event-propagation.connection-editor__wire--halo{stroke:#ffb4542e;stroke-width:10}.connection-editor__wire--policy{filter:drop-shadow(0 0 6px rgba(34,197,94,.2)) url(#connection-glow)}.connection-editor__wire--policy.connection-editor__wire--halo{stroke:#22c55e1f;stroke-width:13}.connection-editor__wire--warning{stroke:#ff9f2f}.connection-editor__wire--warning.connection-editor__wire--halo{stroke:#ff9f2f2e}.connection-editor__wire--error{stroke:#ef4444}.connection-editor__wire--error.connection-editor__wire--halo{stroke:#ef44442e}.connection-editor__wire--active{stroke:#d9b8ff;stroke-width:3;stroke-opacity:1;stroke-dasharray:5 7;animation-duration:.58s}.connection-editor__wire--halo.connection-editor__wire--active{stroke-width:16;stroke-opacity:.8;animation:selected-wire-aura 1.35s ease-in-out infinite}.connection-editor__wire--new{stroke-width:3.4;stroke-opacity:1;stroke-dasharray:640;stroke-dashoffset:640;animation:wire-draw-in .62s cubic-bezier(.16,1,.3,1) forwards,connection-flow 1.05s linear .62s infinite}.connection-editor__wire--halo.connection-editor__wire--new{stroke-width:22;stroke-opacity:0;animation:new-wire-halo 1.45s ease-out forwards}.connection-editor__wire--trace{stroke:#9b5cff;stroke-width:3.2;stroke-opacity:1;stroke-dasharray:7 6;animation:connection-flow .72s linear infinite,trace-wire-pulse 1.18s ease-in-out infinite}.connection-editor__wire--halo.connection-editor__wire--trace{stroke:#9b5cff57;stroke-width:18;stroke-opacity:.86;animation:trace-halo-pulse 1.18s ease-in-out infinite}.connection-editor__wire--mode-dimmed{stroke-opacity:.14;stroke-width:1.05;filter:none;animation-duration:2.6s}.connection-editor--guided .connection-editor__wire:not(.connection-editor__wire--active,.connection-editor__wire--new,.connection-editor__wire--trace){stroke-opacity:.85;filter:none;animation:none}.connection-editor--guided .connection-editor__wire--halo:not(.connection-editor__wire--active,.connection-editor__wire--new,.connection-editor__wire--trace){stroke-opacity:.08;animation:none}.connection-editor__wire--halo.connection-editor__wire--mode-dimmed{stroke-opacity:.05;stroke-width:5;animation:none}.connection-editor__wire-hit--mode-dimmed{stroke-width:12}.connection-editor__wire-endpoint{fill:#f8fafc;stroke:#0f172ae6;stroke-width:1.5;filter:drop-shadow(0 0 8px rgba(216,180,254,.8));pointer-events:none;animation:endpoint-pulse 1.2s ease-in-out infinite}.connection-editor__wire-endpoint--source{fill:#ff9f2f}.connection-editor__wire-endpoint--target{fill:#19c8e8}.connection-editor__connection-tooltip{position:absolute;z-index:6;width:min(312px,calc(100% - 24px));display:grid;gap:8px;padding:12px 14px;border-radius:14px;border:1px solid var(--pce-outline);background:linear-gradient(135deg,var(--pce-surface-strong),var(--pce-surface)),var(--pce-surface-strong);box-shadow:0 18px 44px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);color:var(--pce-fg);pointer-events:none;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);animation:tooltip-rise .14s ease-out}.connection-editor__connection-tooltip-callout{position:absolute;inset-block-start:100%;inset-inline-start:var(--connection-tooltip-callout-x, 42px);width:2px;height:28px;border-radius:999px;background:linear-gradient(180deg,#e2e8f0bd,#19c8e800);box-shadow:0 0 12px #19c8e852;transform:translate(-1px);pointer-events:none}.connection-editor__connection-tooltip-callout:before,.connection-editor__connection-tooltip-callout:after{content:\"\";position:absolute;border-radius:999px;pointer-events:none}.connection-editor__connection-tooltip-callout:before{inset-block-start:-5px;inset-inline-start:-5px;width:12px;height:12px;border:1px solid rgba(148,163,184,.24);background:#080d18f5;box-shadow:inset 0 1px #ffffff14}.connection-editor__connection-tooltip-callout:after{inset-block-end:-3px;inset-inline-start:-4px;width:10px;height:10px;background:#19c8e8;box-shadow:0 0 16px #19c8e88f}.connection-editor__connection-tooltip--projection{border-color:#19c8e833;background:linear-gradient(135deg,#15b8d81f,#0f172ae0),#070c16eb}.connection-editor__connection-tooltip--state{border-color:#ff9f2f66;background:linear-gradient(135deg,#ff9f2f3d,#0f172ae0),#070c16eb}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-callout{background:linear-gradient(180deg,#ffbd62d1,#ff9f2f00);box-shadow:0 0 14px #ff9f2f61}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-callout:after{background:#ff9f2f;box-shadow:0 0 18px #ff9f2fad}.connection-editor__connection-tooltip--error{border-color:#f871716b}.connection-editor__connection-tooltip--error .connection-editor__connection-tooltip-callout:after{background:#fb7185;box-shadow:0 0 18px #fb7185ad}.connection-editor__connection-tooltip-kind{display:inline-flex;align-items:center;gap:8px;color:#94a3b8;font-size:.62rem;font-weight:900;letter-spacing:.16em;text-transform:uppercase}.connection-editor__connection-tooltip-kind span{width:8px;height:8px;border-radius:999px;background:#19c8e8;box-shadow:0 0 12px #19c8e880}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-kind span{background:#ff9f2f;box-shadow:0 0 16px #ff9f2fc7}.connection-editor__connection-tooltip-row{min-width:0;display:grid;grid-template-columns:54px minmax(0,1fr);align-items:center;gap:8px;min-height:28px;padding:0 10px;border-radius:999px;background:#0f172a94}.connection-editor__connection-tooltip-row span{color:#8d9bb0;font-size:.62rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.connection-editor__connection-tooltip-row strong{min-width:0;overflow:hidden;color:#e2e8f0;font-size:.75rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__connection-tooltip-badges{display:flex;flex-wrap:wrap;gap:6px}.connection-editor__connection-tooltip-badges span{padding:3px 8px;border-radius:999px;background:#02061799;color:#fbbf24;font-size:.66rem;font-weight:900}.connection-editor__trace-overlay{position:absolute;z-index:6;inset-block-start:16px;inset-inline-start:50%;width:min(560px,calc(100% - 360px));min-width:320px;padding:12px 14px;border-radius:16px;border:1px solid rgba(34,197,94,.26);background:linear-gradient(135deg,#22c55e1f,#0ea5e91a),#080d18f0;box-shadow:0 22px 56px #02061775,0 0 42px #22c55e1f;transform:translate(-50%);pointer-events:auto;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);animation:tooltip-rise .16s ease-out}.connection-editor__trace-overlay-head,.connection-editor__trace-overlay-route{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0}.connection-editor__trace-overlay-head span,.connection-editor__trace-overlay-route span{color:#86efac;font-size:.66rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase;white-space:nowrap}.connection-editor__trace-overlay-head strong{color:#cbd5e1;font-size:.7rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace}.connection-editor__trace-overlay-route{margin-top:6px}.connection-editor__trace-overlay-controls{display:grid;grid-template-columns:28px minmax(0,1fr) 28px;align-items:center;gap:8px;margin-top:10px}.connection-editor__trace-overlay-button{width:28px;height:24px;display:inline-grid;place-items:center;border:1px solid rgba(148,163,184,.18);border-radius:999px;background:#0206176b;color:#e2e8f0;cursor:pointer;font-size:1rem;font-weight:900;line-height:1;transition:border-color .15s ease,background .15s ease,color .15s ease,opacity .15s ease}.connection-editor__trace-overlay-button:hover:not(:disabled){border-color:#22d3ee6b;background:#0ea5e929;color:#f8fafc}.connection-editor__trace-overlay-button:disabled{cursor:default;opacity:.36}.connection-editor__trace-overlay-steps{min-width:0;display:flex;align-items:center;gap:6px;overflow-x:auto;padding:2px;scrollbar-width:none}.connection-editor__trace-overlay-steps::-webkit-scrollbar{display:none}.connection-editor__trace-overlay-step{position:relative;flex:1 0 28px;min-width:28px;height:22px;border:1px solid rgba(148,163,184,.16);border-radius:999px;background:#0f172a9e;color:#94a3b8;cursor:pointer;font-size:.62rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease}.connection-editor__trace-overlay-step--active{border-color:#22d3eea3;background:linear-gradient(90deg,#22d3ee3d,#9b5cff33);color:#f8fafc;box-shadow:0 0 18px #22d3ee33}.connection-editor__trace-overlay-step--blocked{border-color:#fb71855c;color:#fecdd3}.connection-editor__trace-overlay-route strong{min-width:0;overflow:hidden;color:#f8fafc;font-size:.82rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__trace-overlay-bar{position:relative;height:5px;margin-top:10px;overflow:hidden;border-radius:999px;background:#94a3b829}.connection-editor__trace-overlay-bar span{position:absolute;inset-block:0;inset-inline-start:0;border-radius:inherit;background:linear-gradient(90deg,#22c55e,#22d3ee);box-shadow:0 0 18px #22c55e61;min-width:8%}.connection-editor__drag-wire{fill:none;stroke:#94a3b8;stroke-width:2.4;stroke-linecap:round;stroke-dasharray:6 7;pointer-events:none;filter:url(#connection-glow);animation:connection-flow 1.05s linear infinite,drag-wire-pulse 1.2s ease-in-out infinite}.connection-editor__drag-wire--halo{stroke:#94a3b829;stroke-width:14;stroke-dasharray:none;animation:drag-halo-pulse 1.2s ease-in-out infinite}.connection-editor__drag-wire--valid{stroke:#22c55e}.connection-editor__drag-wire--halo.connection-editor__drag-wire--valid{stroke:#22c55e47}.connection-editor__drag-endpoint{fill:#94a3b8;stroke:#020617eb;stroke-width:2;pointer-events:none;filter:url(#connection-glow);transform-box:fill-box;transform-origin:center;animation:endpoint-pulse 1s ease-in-out infinite}.connection-editor__drag-endpoint--source{fill:#ff9f2f}.connection-editor__drag-endpoint--target{fill:#7dddeb}.connection-editor__drag-endpoint--valid{fill:#22c55e;stroke:#bbf7d0e0}.connection-editor__wire-handle-line{stroke-width:2;stroke-linecap:round;stroke-dasharray:4 5;pointer-events:none;opacity:.88;filter:url(#connection-glow)}.connection-editor__wire-handle-line--source{stroke:#ff9f2ff0}.connection-editor__wire-handle-line--target{stroke:#19c8e8c2}.connection-editor__wire-control{fill:#0f172af5;stroke:#cbd5e1c7;stroke-width:1.8;pointer-events:none;filter:url(#connection-glow)}.connection-editor__wire-label{pointer-events:none;filter:drop-shadow(0 0 10px rgba(2,6,23,.44));animation:tooltip-rise .14s ease-out}.connection-editor__wire-label rect{fill:#080d18eb;stroke-width:1}.connection-editor__wire-label text{fill:#e2e8f0;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:8px;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.connection-editor__wire-label--source rect{stroke:#ff9f2fad}.connection-editor__wire-label--target rect{stroke:#19c8e86b}.connection-editor__zoom-controls{position:absolute;inset-inline-start:14px;inset-block-start:14px;z-index:4;display:grid;grid-template-columns:max-content;gap:6px;width:46px;pointer-events:auto}.connection-editor__tool-group,.connection-editor__trace-source{display:grid;gap:6px;padding:6px;border-radius:12px;border:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-surface-strong),var(--pce-surface));box-shadow:0 16px 36px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.connection-editor__tool-group{grid-template-columns:30px;width:max-content}.connection-editor__tool-group:nth-of-type(2){grid-template-columns:30px}.connection-editor__arrange-button{width:34px;min-height:34px;display:inline-flex;align-items:center;justify-content:center;gap:0;padding:0;border-radius:999px;border:1px solid rgba(168,85,247,.34);background:linear-gradient(90deg,#a855f72e,#22d3ee1f);color:#e9d5ff;font-size:0;font-weight:800;letter-spacing:0;cursor:pointer;opacity:1;transform:scale(1);transition:opacity .14s ease,transform .14s ease,border-color .14s ease,background .14s ease}.connection-editor--guided .connection-editor__arrange-button{opacity:.34;transform:scale(.9);border-color:color-mix(in srgb,var(--pce-outline) 82%,transparent);background:color-mix(in srgb,var(--pce-surface-soft) 84%,transparent)}.connection-editor--guided .connection-editor__zoom-controls:hover .connection-editor__arrange-button,.connection-editor--guided .connection-editor__zoom-controls:focus-within .connection-editor__arrange-button,.connection-editor--guided .connection-editor__arrange-button:hover,.connection-editor--guided .connection-editor__arrange-button:focus-visible,.connection-editor--guided .connection-editor__arrange-button--active{opacity:1;transform:scale(1);border-color:#a855f757;background:linear-gradient(90deg,#a855f72e,#22d3ee1f)}.connection-editor__arrange-button span{color:#c084fc;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.78rem;font-weight:900}.connection-editor__tool-button{width:30px;height:30px;border-radius:999px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-fg);font-size:.9rem;font-weight:800;letter-spacing:.02em;cursor:pointer;line-height:1}.connection-editor__tool-button:hover:not(:disabled){border-color:color-mix(in srgb,var(--pce-primary) 42%,transparent);color:var(--pce-primary);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 16%,transparent)}.connection-editor__history-button:disabled{cursor:not-allowed;opacity:.42}.connection-editor__arrange-button--active{border-color:#22d3ee94;background:linear-gradient(90deg,#0ea5e93d,#22d3ee2e);color:#cffafe}.connection-editor__trace-source{gap:4px;max-height:230px;overflow:auto}.connection-editor__trace-source-title{padding:2px 4px 6px;color:var(--pce-muted);font-size:.64rem;font-weight:900;letter-spacing:.18em;text-transform:uppercase}.connection-editor__trace-source-item{min-width:0;min-height:30px;display:flex;align-items:center;gap:8px;padding:0 8px;border:0;border-radius:8px;background:transparent;color:var(--pce-fg);cursor:pointer;font-size:.74rem;font-weight:800;text-align:start}.connection-editor__trace-source-item span{color:#f59e0b;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.72rem;font-weight:900}.connection-editor__trace-source-item:hover,.connection-editor__trace-source-item--active{background:var(--pce-surface-strong);color:var(--pce-strong);box-shadow:inset 3px 0 #f59e0bd1}.connection-editor__minimap{position:absolute;inset-inline-end:14px;inset-block-end:14px;z-index:4;width:184px;height:124px;padding:18px 10px 10px;overflow:hidden;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-primary) 30%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 12%,transparent),var(--pce-surface-strong)),var(--pce-surface);box-shadow:0 20px 48px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);cursor:pointer;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);opacity:1;transform:scale(1);transform-origin:100% 100%;transition:opacity .16s ease,transform .16s ease,border-color .16s ease,box-shadow .16s ease}.connection-editor--guided .connection-editor__minimap{opacity:.54;transform:scale(.88);border-color:color-mix(in srgb,var(--pce-outline) 64%,transparent);box-shadow:0 14px 34px color-mix(in srgb,var(--pce-shadow-soft) 70%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor--guided .connection-editor__minimap:hover,.connection-editor--guided .connection-editor__minimap:focus-visible,.connection-editor--guided .connection-editor__minimap--panning{opacity:1;transform:scale(1)}.connection-editor__minimap:hover,.connection-editor__minimap--panning{border-color:#22d3ee75;box-shadow:0 20px 48px var(--pce-shadow-soft),0 0 24px color-mix(in srgb,var(--pce-primary) 14%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor__minimap--panning{cursor:grabbing}.connection-editor__minimap:before{content:\"\";position:absolute;inset:22px 10px 10px;border-radius:10px;background-image:radial-gradient(circle,rgba(125,211,252,.18) 1px,transparent 1px);background-size:22px 22px;opacity:.58;pointer-events:none}.connection-editor__minimap-title{position:absolute;inset-block-start:7px;inset-inline-start:12px;color:var(--pce-muted);font-size:.6rem;font-weight:900;letter-spacing:.16em;text-transform:uppercase}.connection-editor__minimap svg{position:relative;width:100%;height:100%;display:block;overflow:visible}.connection-editor__minimap-wire{fill:none;stroke:#b46cff8f;stroke-width:4;stroke-linecap:round;opacity:.86}.connection-editor__minimap-wire--state{stroke:#ff9f2fdb}.connection-editor__minimap-wire--state-read{stroke:#8b7dffb8;stroke-dasharray:4 9}.connection-editor__minimap-wire--projection{stroke:#19c8e88a;stroke-dasharray:18 8}.connection-editor__minimap-wire--event-propagation{stroke:#ffb454cc;stroke-dasharray:6 7}.connection-editor__minimap-wire--policy{filter:drop-shadow(0 0 6px rgba(34,197,94,.28))}.connection-editor__minimap-wire--trace{stroke:#9b5cffe0;stroke-width:5;stroke-dasharray:12 9;animation:connection-flow 1s linear infinite}.connection-editor__minimap-wire--active{stroke:#facc15eb;stroke-width:6;filter:drop-shadow(0 0 8px rgba(250,204,21,.38))}.connection-editor__minimap-wire--mode-dimmed{opacity:.2;stroke-width:2.6;filter:none}.connection-editor__minimap-node{fill:#f59e0be0;stroke:#020617db;stroke-width:4;filter:drop-shadow(0 0 7px rgba(245,158,11,.24))}.connection-editor__minimap-node--state{fill:#a855f7e6;filter:drop-shadow(0 0 8px rgba(168,85,247,.34))}.connection-editor__minimap-node--selected{fill:#facc15f5;stroke:#fef08aeb;filter:drop-shadow(0 0 10px rgba(250,204,21,.42))}.connection-editor__minimap-node--trace-source{fill:#facc15f5;filter:drop-shadow(0 0 10px rgba(250,204,21,.44))}.connection-editor__minimap-node--trace-reacting{fill:#22c55eeb;filter:drop-shadow(0 0 10px rgba(34,197,94,.42))}.connection-editor__minimap-node--mode-dimmed{opacity:.28;filter:none}.connection-editor__minimap-viewport{fill:#7dd3fc1f;stroke:#7dd3fce6;stroke-width:5;stroke-dasharray:18 10;filter:drop-shadow(0 0 10px rgba(125,211,252,.3));transition:x .12s ease,y .12s ease,width .12s ease,height .12s ease}.connection-editor__minimap--panning .connection-editor__minimap-viewport{fill:#22d3ee2e;stroke:#22d3eef5;stroke-dasharray:none}.connection-editor__node{position:absolute;z-index:10;width:168px;height:168px;transform:translate(-84px,-84px);border:0;padding:0;background:transparent;color:inherit;cursor:pointer;transition:filter .16s ease}.connection-editor__node--dragging{cursor:grabbing;filter:drop-shadow(0 0 22px rgba(34,211,238,.26));z-index:12}.connection-editor__node--connection-focus{filter:drop-shadow(0 0 16px rgba(34,211,238,.2))}.connection-editor__node--compat-target .connection-editor__node-ring{box-shadow:0 0 0 2px #22c55e66,0 0 0 18px #22c55e0b,0 0 32px #22c55e29}.connection-editor__node--mode-dimmed{opacity:.42;filter:grayscale(.22) saturate(.56)}.connection-editor__node--quiet{opacity:.58;filter:saturate(.68) brightness(.86)}.connection-editor__node--mode-dimmed .connection-editor__node-ring{animation:none;box-shadow:0 0 0 1px #2dd4bf14,0 0 14px #0ea5e90f}.connection-editor__node--mode-dimmed .connection-editor__node-halo{stroke-opacity:.28}.connection-editor__node-ring,.connection-editor__node-core,.connection-editor__node-svg{position:absolute;inset:0}.connection-editor__node-ring,.connection-editor__node-core{display:grid;place-items:center;clip-path:polygon(30% 0,70% 0,100% 30%,100% 70%,70% 100%,30% 100%,0 70%,0 30%)}.connection-editor__node-ring{background:radial-gradient(circle at center,transparent 0 44%,rgba(7,17,31,.96) 45% 57%,transparent 58%),conic-gradient(from -28deg,#06b6d4b3 0 28%,#f59e0b9e 28% 48%,#a855f780 48% 68%,#06b6d4a3 68% 100%);box-shadow:0 0 0 1px #2dd4bf2e,0 0 26px #0ea5e929;animation:node-breathe 2.8s ease-in-out infinite}.connection-editor__node-svg{overflow:visible;filter:drop-shadow(0 0 16px rgba(14,165,233,.16))}.connection-editor__node-halo{fill:transparent;stroke:#38bdf838;stroke-width:1;stroke-dasharray:0;pointer-events:none}.connection-editor__node-halo:nth-child(2){stroke:#38bdf824}.connection-editor__node-halo:nth-child(3){stroke:#94a3b81a}.connection-editor__node-segment{fill-opacity:.28;stroke-opacity:.72;stroke-width:1.15;transition:fill-opacity .16s ease,stroke-opacity .16s ease,stroke-width .16s ease;cursor:grab}.connection-editor__node:hover .connection-editor__node-segment,.connection-editor__node--selected .connection-editor__node-segment,.connection-editor__node--connection-focus .connection-editor__node-segment{fill-opacity:.54;stroke-opacity:.95;stroke-width:1.55}.connection-editor__node-segment--connection-focus{fill-opacity:.76;stroke-opacity:1;stroke-width:2;filter:drop-shadow(0 0 8px rgba(34,211,238,.76));animation:segment-focus-sweep 1.45s ease-in-out infinite}.connection-editor__node-segment--source{fill-opacity:.86;stroke-opacity:1;stroke-width:2.45;filter:drop-shadow(0 0 12px rgba(251,146,60,.82))}.connection-editor__node-segment--compatible{fill-opacity:.74;stroke-opacity:1;stroke-width:2;filter:drop-shadow(0 0 10px rgba(34,197,94,.72));animation:snap-pulse 1s ease-in-out infinite}.connection-editor__node-segment--compat-preview{fill-opacity:.86;stroke-opacity:1;stroke-width:2.45;filter:drop-shadow(0 0 13px rgba(245,158,11,.78))}.connection-editor__node-segment--candidate{fill-opacity:.92;stroke-opacity:1;stroke-width:2.8;filter:drop-shadow(0 0 16px rgba(34,197,94,.86))}.connection-editor__node-segment--incompatible{fill-opacity:.12;stroke-opacity:.28;cursor:not-allowed}.connection-editor__node-segment--invalid-drop{fill-opacity:.72;stroke:#fb7185;stroke-opacity:1;stroke-width:3;filter:drop-shadow(0 0 16px rgba(244,63,94,.84));animation:invalid-drop-jolt .36s ease-out}.connection-editor__node-segment--state{fill-opacity:.44;filter:drop-shadow(0 0 8px rgba(180,108,255,.2))}.connection-editor__node-segment--mode-dimmed{fill-opacity:.08;stroke-opacity:.2;stroke-width:.9;filter:none}.connection-editor__node-anchor{opacity:.95;filter:drop-shadow(0 0 6px currentColor)}.connection-editor__node-rotate-handle{cursor:grab;opacity:0;pointer-events:none;transition:opacity .16s ease,filter .16s ease;filter:drop-shadow(0 0 8px rgba(34,211,238,.28))}.connection-editor__node:hover .connection-editor__node-rotate-handle,.connection-editor__node--selected .connection-editor__node-rotate-handle,.connection-editor__node--rotating .connection-editor__node-rotate-handle{opacity:1;pointer-events:auto}.connection-editor__node--rotating .connection-editor__node-rotate-handle{cursor:grabbing;filter:drop-shadow(0 0 12px rgba(34,211,238,.54))}.connection-editor__node-rotate-hit{fill:transparent}.connection-editor__node-rotate-dot{fill:var(--pce-surface-strong);stroke:var(--pce-primary);stroke-opacity:.88;stroke-width:1.4}.connection-editor__node-rotate-arc,.connection-editor__node-rotate-arrow{fill:none;stroke:var(--pce-primary);stroke-linecap:round;stroke-linejoin:round;stroke-opacity:.96;stroke-width:1.2;pointer-events:none}.connection-editor__segment-icon,.connection-editor__segment-label{pointer-events:none;fill:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-weight:800;paint-order:stroke;stroke:#020617c2;stroke-width:3px;stroke-linejoin:round}.connection-editor__segment-icon{font-size:12px;fill:#19c8e8;opacity:1;transition:opacity .14s ease}.connection-editor__segment-icon--output{fill:#ff9f2f}.connection-editor--guided .connection-editor__segment-icon:not(.connection-editor__segment-icon--guided-visible){opacity:0}.connection-editor--guided .connection-editor__node--rotating .connection-editor__segment-icon{opacity:0}.connection-editor__segment-label{font-size:9.5px;letter-spacing:0;fill:#e2e8f0e0;opacity:1;transition:opacity .14s ease}.connection-editor--guided .connection-editor__segment-label:not(.connection-editor__segment-label--guided-visible){opacity:0}.connection-editor--guided .connection-editor__node--rotating .connection-editor__segment-label{opacity:0}.connection-editor__node-core{inset:43px;padding:12px;border-radius:999px;clip-path:none;display:flex;flex-direction:column;align-items:center;justify-content:center;background:radial-gradient(circle at center,#111827 0 58%,#060b14);border:1px solid rgba(226,232,240,.1);box-shadow:0 12px 28px #00000073,inset 0 1px #ffffff0d;color:#f8fafc;text-shadow:0 1px 2px rgba(0,0,0,.62)}.connection-editor__node-icon{display:block;max-width:44px;overflow:hidden;color:#f8fafc;font-family:Material Symbols Outlined,Material Icons,sans-serif;font-size:1.22rem;font-weight:400;line-height:1;text-align:center;text-transform:none;letter-spacing:0;white-space:nowrap;filter:drop-shadow(0 0 10px rgba(226,232,240,.18))}.connection-editor__node--state .connection-editor__node-ring{background:radial-gradient(circle at center,transparent 0 44%,rgba(7,17,31,.96) 45% 57%,transparent 58%),conic-gradient(from 14deg,#a855f7c7 0 42%,#7e3af26b 42% 78%,#f59e0b47 78% 100%);box-shadow:0 0 0 1px #a855f738,0 0 30px #a855f733}.connection-editor__node--selected .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15bd,0 0 34px #facc153d}.connection-editor__node--connection-focus .connection-editor__node-ring{box-shadow:0 0 0 2px #22d3eead,0 0 38px #22d3ee42;animation:node-focus-breathe 1.7s ease-in-out infinite}.connection-editor__node--trace-source .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15c7,0 0 0 18px #facc150f,0 0 54px #facc154d;animation:trace-source-pulse 1.38s ease-in-out infinite}.connection-editor__node--trace-source:before{content:\"\";position:absolute;z-index:0;inset:-44px;border-radius:999px;border:2px solid rgba(250,204,21,.36);background:radial-gradient(circle at center,transparent 0 64%,rgba(250,204,21,.05) 65% 66%,transparent 67%),conic-gradient(from -20deg,#facc1500,#facc1552,#facc1500);box-shadow:0 0 36px #facc151f;opacity:.92;pointer-events:none;animation:trace-source-orbit 2.8s linear infinite}.connection-editor__node--trace-reacting .connection-editor__node-ring{box-shadow:0 0 0 2px #22c55e94,0 0 46px #22c55e38;animation:trace-reacting-pulse 1.38s ease-in-out infinite}.connection-editor__node--trace-source:after,.connection-editor__node--trace-reacting:after{position:absolute;z-index:3;inset-inline-start:50%;inset-block-start:-42px;padding:0;border:0;background:transparent;color:#facc15;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.68rem;font-weight:900;letter-spacing:.18em;text-transform:uppercase;transform:translate(-50%);text-shadow:0 0 12px rgba(250,204,21,.52);pointer-events:none}.connection-editor__node--trace-source:after{content:attr(data-trace-label)}.connection-editor__node--trace-reacting:after{content:attr(data-trace-label);color:#86efac;text-shadow:0 0 12px rgba(34,197,94,.44)}.connection-editor__node--trace-source .connection-editor__node-segment--output{fill-opacity:.66;stroke-width:2.2;filter:drop-shadow(0 0 14px rgba(255,159,47,.42))}.connection-editor__node--expanded .connection-editor__node-ring{box-shadow:0 0 0 2px #22d3eebd,0 0 34px #22d3ee3d}.connection-editor__node--rotating .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15c7,0 0 42px #facc1547;animation:node-focus-breathe 1.1s ease-in-out infinite}.connection-editor__node--rotating .connection-editor__node-segment{fill-opacity:.68;stroke-opacity:1;stroke-width:1.9;cursor:grabbing}.connection-editor__node-title,.connection-editor__node-subtitle{display:block;max-width:92px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center}.connection-editor__node-title{color:#f8fafc;font-size:.82rem;font-weight:800}.connection-editor__node-subtitle{margin-top:4px;color:#a7b4c8;font-size:.62rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__ports{position:absolute;z-index:30;display:grid;gap:6px;width:104px;opacity:0;pointer-events:none;transform:translateY(3px);transition:opacity .16s ease,transform .16s ease}.connection-editor__ports--visible{opacity:1;pointer-events:auto;transform:translateY(0)}.connection-editor__ports--outputs{z-index:42}.connection-editor__ports--inputs{z-index:36}.connection-editor__segment-popover{position:absolute;z-index:80;width:292px;max-height:320px;overflow:auto;display:grid;gap:10px;padding:14px;border-radius:16px;border:1px solid rgba(148,163,184,.18);background:linear-gradient(135deg,#0f172af5,#110c20f0),#090c16eb;box-shadow:0 28px 72px #0000007a,0 0 48px #a855f71a,inset 0 1px #ffffff0d;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__segment-popover-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding-bottom:2px}.connection-editor__segment-popover-title{color:#94a3b8;font-size:.68rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase}.connection-editor__segment-popover-hint{margin-top:5px;color:#64748b;font-size:.66rem;font-weight:700}.connection-editor__segment-rotation{flex:0 0 auto;min-width:54px;padding:5px 8px;border-radius:999px;border:1px solid rgba(34,211,238,.28);background:#082f4957;color:#7dd3fc;font-size:.66rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-align:center;box-shadow:inset 0 0 16px #0ea5e914}.connection-editor__segment-row{display:grid;grid-template-columns:28px 72px minmax(0,1fr) auto;align-items:center;gap:8px;min-height:40px;padding:7px;border-radius:12px;border:1px solid rgba(148,163,184,.12);background:linear-gradient(90deg,#0f172ac7,#0f172a75),#0f172a99;transition:border-color .15s ease,background .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__segment-row--input{border-color:#22d3ee42}.connection-editor__segment-row--output{border-color:#f59e0b47}.connection-editor__segment-row--state{border-color:#a855f74d}.connection-editor__segment-row--source,.connection-editor__segment-row--candidate{transform:translate(2px);box-shadow:0 0 22px #f59e0b24,inset 0 0 20px #f59e0b14}.connection-editor__segment-row--compatible{border-color:#22d3ee6b;box-shadow:inset 0 0 18px #22d3ee14}.connection-editor__segment-row--compat-preview{border-color:#f59e0b94;background:linear-gradient(90deg,#f59e0b1f,#0f172a8a),#0f172ab3;box-shadow:0 0 22px #f59e0b1f,inset 0 0 18px #f59e0b14}.connection-editor__segment-row--invalid-drop{border-color:#fb7185b3;background:linear-gradient(90deg,#f43f5e24,#0f172a94),#0f172ab3;box-shadow:0 0 24px #f43f5e2e,inset 0 0 18px #f43f5e1a;animation:invalid-drop-jolt .36s ease-out}.connection-editor__segment-index{color:#64748b;font-size:.64rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;letter-spacing:.08em}.connection-editor__segment-kind{display:inline-flex;align-items:center;gap:6px;color:#94a3b8;font-size:.62rem;font-weight:800;text-transform:uppercase;min-width:0;white-space:nowrap}.connection-editor__segment-kind-dot{width:7px;height:7px;border-radius:999px;background:#94a3b8;box-shadow:0 0 12px #94a3b847}.connection-editor__segment-row--input .connection-editor__segment-kind-dot{background:#22d3ee;box-shadow:0 0 12px #22d3ee61}.connection-editor__segment-row--output .connection-editor__segment-kind-dot{background:#fb923c;box-shadow:0 0 12px #fb923c66}.connection-editor__segment-row--state .connection-editor__segment-kind-dot{background:#a855f7;box-shadow:0 0 12px #a855f76b}.connection-editor__segment-label{display:inline-flex;align-items:center;gap:5px;min-width:0;overflow:hidden;color:#e2e8f0;font-size:.74rem;font-weight:700;text-overflow:ellipsis;white-space:nowrap}.connection-editor__segment-badge{flex:0 0 auto;padding:2px 5px;border-radius:999px;border:1px solid rgba(56,189,248,.28);background:#0ea5e91a;color:#bae6fd;font-size:.55rem;font-weight:900;text-transform:uppercase}.connection-editor__segment-path{grid-column:3 / 4;min-width:0;margin-top:-5px;overflow:hidden;color:#93c5fd;font-size:.62rem;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-overflow:ellipsis;white-space:nowrap}.connection-editor__segment-row--nested{min-height:56px;border-color:#38bdf84d;background:linear-gradient(90deg,#0ea5e91a,#0f172a80),#0f172aa3}.connection-editor__segment-actions{display:inline-flex;gap:4px}.connection-editor__segment-action{width:24px;height:24px;border-radius:999px;border:1px solid rgba(148,163,184,.18);background:#060c16b8;color:#dbeafe;cursor:pointer;font-weight:900;line-height:1}.connection-editor__segment-action:hover{border-color:#22d3ee75;box-shadow:0 0 14px #22d3ee29}.connection-editor__port{min-height:24px;border-radius:999px;border:1px solid rgba(148,163,184,.14);background:#080d18c2;color:#cbd5e1;font-size:.64rem;font-weight:700;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:crosshair;box-shadow:0 8px 20px #00000038}.connection-editor--guided .connection-editor__ports--visible .connection-editor__port:not(.connection-editor__port--guided-visible){display:none}.connection-editor__port--output{border-color:#f59e0b75;color:#fbbf24}.connection-editor__port--input{border-color:#06b6d475;color:#22d3ee}.connection-editor__port--state{border-color:#a855f785;color:#c084fc}.connection-editor__port--compatible{background:#22c55e29;box-shadow:0 0 0 1px #22c55e61,0 0 18px #22c55e2e;animation:snap-pulse 1s ease-in-out infinite}.connection-editor__port--source{border-color:#fb923cb8;background:#fb923c29;color:#fed7aa;box-shadow:0 0 0 1px #fb923c73,0 0 20px #fb923c2e}.connection-editor__port--candidate{background:#22c55e3d;box-shadow:0 0 0 2px #22c55e94,0 0 24px #22c55e3d}.connection-editor__port--incompatible{opacity:.42;cursor:not-allowed;filter:grayscale(.45)}.connection-editor__port--invalid-drop{opacity:1;border-color:#fb7185c7;color:#fecdd3;background:#f43f5e2e;box-shadow:0 0 0 2px #f43f5e75,0 0 24px #f43f5e38;filter:none;animation:invalid-drop-jolt .36s ease-out}.connection-editor__port--connection-focus{border-color:#facc15ad;color:#fde68a;background:#facc151f;box-shadow:0 0 0 1px #facc156b,0 0 20px #facc152e;animation:port-focus-pulse 1.25s ease-in-out infinite}:host-context(.theme-light) .connection-editor__port{color:var(--pce-fg)}.connection-editor__drag-tip{position:absolute;z-index:6;display:grid;gap:4px;width:min(330px,calc(100% - 28px));padding:10px 12px;border-radius:14px;border:1px solid rgba(125,211,252,.26);background:linear-gradient(135deg,#0ea5e91f,#0f172aeb),#080d18f0;box-shadow:0 18px 44px #0206176b,0 0 28px #0ea5e91a;font-size:.76rem;pointer-events:none;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);animation:tooltip-rise .14s ease-out}.connection-editor__drag-tip--valid{border-color:#22c55e66;background:linear-gradient(135deg,#22c55e29,#0f172aeb),#080d18f0;box-shadow:0 18px 44px #0206176b,0 0 30px #22c55e24}.connection-editor__drag-tip--invalid{z-index:7;border-color:#fb71858a;background:linear-gradient(135deg,#f43f5e2e,#0f172af0),#080d18f5;box-shadow:0 18px 44px #0206176b,0 0 34px #f43f5e29;animation:tooltip-rise .14s ease-out,invalid-drop-jolt .36s ease-out}.connection-editor__invalid-drop-pulse{position:absolute;z-index:6;width:16px;height:16px;border-radius:999px;border:2px solid rgba(251,113,133,.88);background:#f43f5e38;box-shadow:0 0 0 8px #f43f5e1a,0 0 28px #f43f5e3d;pointer-events:none;transform:translate(-50%,-50%);animation:invalid-drop-ping 1.2s ease-out both}.connection-editor__drag-tip span{color:#7dd3fc;text-transform:uppercase;letter-spacing:.12em;font-size:.62rem;font-weight:800}.connection-editor__drag-tip strong{min-width:0;overflow:hidden;color:#f8fafc;font-size:.78rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__drag-tip small{color:#94a3b8;font-size:.68rem;font-weight:700}.connection-editor__drag-tip--valid small{color:#86efac}.connection-editor__drag-tip--invalid span,.connection-editor__drag-tip--invalid small{color:#fda4af}.connection-editor__summary{grid-column:1;grid-row:2;align-self:end;justify-self:start;z-index:6;display:flex;flex-wrap:wrap;gap:6px;padding:0 0 12px 12px;pointer-events:none}.connection-editor__side-panel{grid-column:2;grid-row:2 / 3;position:relative;z-index:8;min-width:0;min-height:0;display:grid;align-content:start;gap:14px;overflow:auto;padding-inline-end:4px;scrollbar-color:var(--pce-outline-strong) var(--pce-surface-soft)}.connection-editor__side-panel--contextual .connection-editor__inspector{order:-3}.connection-editor__side-panel--contextual .connection-editor__recommended{order:2}.connection-editor__side-panel--contextual .connection-editor__filters-section{order:3}.connection-editor__side-panel--contextual .connection-editor__recommended,.connection-editor__side-panel--contextual .connection-editor__filters-section{opacity:.82}.connection-editor__metric,.connection-editor__item{border-radius:16px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.connection-editor__metric{min-width:auto;display:inline-flex;align-items:baseline;gap:7px;padding:6px 9px;border-radius:999px;background:color-mix(in srgb,var(--pce-surface-strong) 86%,transparent);box-shadow:0 10px 26px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight)}.connection-editor__metric span{color:var(--pce-muted);font-size:.62rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__metric strong,.connection-editor__details-row strong{color:var(--pce-strong);font-size:.78rem}.connection-editor__filters,.connection-editor__actions,.connection-editor__badges{display:flex;gap:8px;flex-wrap:wrap}.connection-editor__filter,.connection-editor__action{border:1px solid var(--pce-outline-strong);background:var(--pce-surface-soft);color:var(--pce-fg);border-radius:999px;padding:6px 10px;font-size:.75rem;font-weight:700;cursor:pointer}.connection-editor__filter--active,.connection-editor__action:hover{border-color:color-mix(in srgb,var(--pce-secondary) 62%,transparent);background:color-mix(in srgb,var(--pce-secondary) 16%,var(--pce-surface-soft));color:var(--pce-strong)}.connection-editor__action--danger{border-color:#f8717161;color:#fecaca}.connection-editor__item{width:100%;text-align:start;padding:12px;display:grid;gap:6px;cursor:pointer;color:inherit}.connection-editor__item--active{border-color:#a855f7b8;background:linear-gradient(90deg,#0ea5e91a,#a855f721)}.connection-editor__item-title{font-weight:800;color:#f8fafc;word-break:break-word}.connection-editor__badge{padding:3px 8px;border-radius:999px;background:color-mix(in srgb,var(--pce-primary) 12%,var(--pce-surface));color:var(--pce-fg);font-size:.72rem;font-weight:800}.connection-editor__section{min-width:0;padding:14px;border-radius:18px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight),0 18px 50px var(--pce-shadow-soft);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__badge--warning{background:color-mix(in srgb,var(--pce-warning) 14%,var(--pce-surface));color:var(--pce-fg)}.connection-editor__badge--error{background:color-mix(in srgb,var(--pce-error) 14%,var(--pce-surface));color:var(--pce-fg)}.connection-editor__diagnostics{display:grid;gap:8px}.connection-editor__diagnostic{display:grid;gap:4px;padding:10px;border-radius:12px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-fg);font-size:.78rem;line-height:1.35}.connection-editor__diagnostic--warning{border-color:#f59e0b47;background:#f59e0b1a}.connection-editor__diagnostic--error{border-color:#ef44445c;background:#ef44441f}.connection-editor__diagnostic-severity{font-size:.68rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase;color:#f8fafc}.connection-editor__suggestions{display:grid;gap:10px;margin-top:14px;padding-top:14px;border-top:1px solid var(--pce-outline)}.connection-editor__suggestion{display:grid;gap:8px;width:100%;padding:12px;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-secondary) 26%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 12%,transparent),color-mix(in srgb,var(--pce-primary) 8%,transparent)),var(--pce-surface-strong);color:var(--pce-fg);text-align:start;cursor:pointer;transition:transform .16s ease,border-color .16s ease,background .16s ease}.connection-editor__suggestion:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 46%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 18%,transparent),color-mix(in srgb,var(--pce-primary) 12%,transparent)),var(--pce-surface-strong)}.connection-editor__suggestion-route{display:grid;gap:4px;font-size:.78rem}.connection-editor__suggestion-route strong{color:var(--pce-strong);font-size:.82rem}.connection-editor__suggestion-reason{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__suggestion-diagnostic{padding:10px 12px;border-radius:14px;border:1px solid rgba(245,158,11,.28);background:#f59e0b1a;color:var(--pce-strong);font-size:.78rem;line-height:1.35}.connection-editor__suggestion-outcome{color:var(--pce-strong);font-size:.84rem;line-height:1.3}.connection-editor__suggestion-action{justify-self:start;padding:3px 8px;border-radius:999px;background:#22d3ee24;color:#67e8f9;font-size:.68rem;font-weight:900;letter-spacing:.06em;text-transform:uppercase}:host-context(.theme-light) .connection-editor__suggestion-action{background:color-mix(in srgb,var(--pce-primary) 12%,var(--pce-surface-soft));color:#075f73}.connection-editor__trace{display:grid;gap:10px;margin-top:14px;padding-top:14px;border-top:1px solid var(--pce-outline)}.connection-editor__trace-summary{color:var(--pce-muted);font-size:.78rem;line-height:1.35}.connection-editor__trace-step{display:grid;grid-template-columns:24px minmax(76px,auto) 1fr;gap:8px;align-items:center;padding:9px;border-radius:12px;border:1px solid rgba(34,211,238,.18);background:var(--pce-surface-soft);font-size:.76rem}.connection-editor__trace-step--blocked{border-color:#ef444457;background:#ef44441a}.connection-editor__trace-order{display:inline-grid;place-items:center;width:22px;height:22px;border-radius:999px;background:#22d3ee29;color:#67e8f9;font-weight:900}.connection-editor__trace-phase{color:var(--pce-strong);font-weight:900;text-transform:uppercase;letter-spacing:.06em;font-size:.66rem}.connection-editor__trace-copy{min-width:0;color:var(--pce-fg);word-break:break-word}.connection-editor__transform-steps{display:grid;gap:8px}.connection-editor__transform-step{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:8px;align-items:center;padding:9px;border-radius:12px;border:1px solid rgba(168,85,247,.22);background:var(--pce-surface-soft)}.connection-editor__transform-kind{min-width:0;color:var(--pce-strong);font-weight:900;word-break:break-word}.connection-editor__transform-phase{color:var(--pce-muted);font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}.connection-editor__details-row,.connection-editor__details-block{display:grid;gap:6px}.connection-editor__payload-input{width:100%;min-height:128px;box-sizing:border-box;resize:vertical;border:1px solid var(--pce-outline);border-radius:8px;background:color-mix(in srgb,var(--pce-surface) 92%,black);color:var(--pce-fg);padding:10px;font:.76rem/1.45 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.connection-editor__surface-action-editor{display:block;max-height:min(58vh,620px);overflow:auto;border:1px solid var(--pce-outline);border-radius:8px;background:color-mix(in srgb,var(--pce-surface) 96%,black);padding:10px;margin-bottom:10px}.connection-editor__rule-grid{display:grid;gap:10px}.connection-editor__rule-card{display:grid;gap:8px;padding:10px;border-radius:12px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft)}.connection-editor__rule-heading{color:var(--pce-fg);font-size:.72rem;font-weight:900;text-transform:uppercase;letter-spacing:.06em}pre{margin:0;padding:12px;border-radius:12px;background:color-mix(in srgb,var(--pce-surface-strong) 82%,#020617);color:var(--pce-fg);overflow:auto;font-size:.75rem}@keyframes connection-flow{to{stroke-dashoffset:-38}}@keyframes drag-wire-pulse{0%,to{stroke-opacity:.76}50%{stroke-opacity:1}}@keyframes drag-halo-pulse{0%,to{stroke-opacity:.48}50%{stroke-opacity:.82}}@keyframes endpoint-pulse{0%,to{opacity:.78;transform:scale(1)}50%{opacity:1;transform:scale(1.25)}}@keyframes selected-wire-aura{0%,to{stroke-opacity:.46}50%{stroke-opacity:.92}}@keyframes wire-draw-in{0%{stroke-dashoffset:640;opacity:.32}70%{opacity:1}to{stroke-dashoffset:0;opacity:1}}@keyframes new-wire-halo{0%{stroke-opacity:0;stroke-width:30}28%{stroke-opacity:.78}to{stroke-opacity:0;stroke-width:12}}@keyframes new-dock-link-flash{0%{transform:translateY(-2px);box-shadow:0 16px 40px #0206175c,0 0 42px #fb923c47}to{transform:translateY(0)}}@keyframes trace-wire-pulse{0%,to{stroke-opacity:.78}50%{stroke-opacity:1}}@keyframes trace-halo-pulse{0%,to{stroke-opacity:.34}50%{stroke-opacity:.86}}@keyframes tooltip-rise{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes invalid-drop-jolt{0%,to{transform:translate(0)}28%{transform:translate(-3px)}56%{transform:translate(3px)}}@keyframes invalid-drop-ping{0%{opacity:1;transform:translate(-50%,-50%) scale(.72)}72%{opacity:.74;transform:translate(-50%,-50%) scale(1.35)}to{opacity:0;transform:translate(-50%,-50%) scale(1.8)}}@keyframes node-breathe{50%{transform:scale(1.035)}}@keyframes node-focus-breathe{0%,to{transform:scale(1)}50%{transform:scale(1.055)}}@keyframes trace-source-pulse{50%{transform:scale(1.04);box-shadow:0 0 0 3px #facc15db,0 0 0 22px #facc1514,0 0 64px #facc1557}}@keyframes trace-source-orbit{to{transform:rotate(360deg)}}@keyframes trace-reacting-pulse{50%{transform:scale(1.045);box-shadow:0 0 0 3px #22c55ead,0 0 62px #22c55e4d}}@keyframes segment-focus-sweep{0%,to{stroke-opacity:.82}50%{stroke-opacity:1}}@keyframes snap-pulse{50%{filter:brightness(1.16)}}@keyframes port-focus-pulse{50%{transform:translateY(-1px);box-shadow:0 0 0 2px #facc158f,0 0 26px #facc153d}}.connection-editor__link-list{order:-2}.connection-editor__item-title{color:var(--pce-fg);font-weight:650;overflow-wrap:anywhere}.connection-editor__link-list .connection-editor__item{border:0;border-radius:6px;background:transparent;border-bottom:1px solid var(--pce-outline);box-shadow:none;padding:12px 4px;gap:4px}.connection-editor__link-list .connection-editor__item--active{background:var(--pce-surface-strong)}.connection-editor--guided .connection-editor__widget-list,.connection-editor--guided .connection-editor__link-list .connection-editor__item-copy{display:none}.connection-editor__filters-section,.connection-editor__side-panel--contextual .connection-editor__inspector{order:-3}.connection-editor__working-note{order:4}.connection-editor__draft{order:-5}.connection-editor--guided .connection-editor__segment-kind-legend{background:var(--pce-surface-strong);color:var(--pce-fg)}.connection-editor__node-caption{position:absolute;left:50%;top:-16px;transform:translate(-50%,-100%);color:var(--pce-fg);background:var(--pce-surface-base);border:1px solid var(--pce-outline);border-radius:8px;padding:6px 8px;line-height:1.3;font-weight:650;white-space:normal;overflow-wrap:anywhere}.connection-editor__node-core{padding:8px}.connection-editor__node-icon{font-size:30px}.connection-editor__node-subtitle{display:none}.connection-editor__wire:not(.connection-editor__wire--halo){stroke:var(--pce-primary);stroke-opacity:.85;stroke-width:2;vector-effect:non-scaling-stroke;stroke-dasharray:none;animation:none;filter:none}.connection-editor__wire--state:not(.connection-editor__wire--halo){stroke:var(--pce-tertiary)}.connection-editor__wire--state-read:not(.connection-editor__wire--halo){stroke:var(--pce-secondary)}.connection-editor__wire--error:not(.connection-editor__wire--halo){stroke:var(--pce-error)}.connection-editor__wire--active:not(.connection-editor__wire--halo),.connection-editor__wire--trace:not(.connection-editor__wire--halo){stroke-width:3;stroke-opacity:1;stroke-dasharray:12 6;animation:connection-flow 1.8s linear infinite}.connection-editor__wire--mode-dimmed:not(.connection-editor__wire--halo){stroke-opacity:.16}.connection-editor__header{gap:12px}.connection-editor__mode-frame{min-width:0;flex:1 1 300px}.connection-editor__header-actions{flex:0 1 auto}@media(max-width:1200px){.connection-editor__header{flex-wrap:wrap}}@media(prefers-reduced-motion:reduce){.connection-editor__viewport,.connection-editor__node,.connection-editor__node-ring,.connection-editor__node-core,.connection-editor__node-segment,.connection-editor__dock-link,.connection-editor__port{transition-duration:1ms}.connection-editor__wire,.connection-editor__wire--state,.connection-editor__wire--projection,.connection-editor__wire--state-read,.connection-editor__wire--event-propagation,.connection-editor__wire--halo.connection-editor__wire--active,.connection-editor__wire--trace,.connection-editor__wire--halo.connection-editor__wire--trace,.connection-editor__wire-endpoint,.connection-editor__connection-tooltip,.connection-editor__dock-link--new,.connection-editor__trace-overlay,.connection-editor__trace-overlay-step,.connection-editor__drag-wire,.connection-editor__drag-wire--halo,.connection-editor__drag-endpoint,.connection-editor__drag-tip,.connection-editor__node-ring,.connection-editor__node--connection-focus .connection-editor__node-ring,.connection-editor__node--trace-source .connection-editor__node-ring,.connection-editor__node--trace-source:before,.connection-editor__node--trace-reacting .connection-editor__node-ring,.connection-editor__node--rotating .connection-editor__node-ring,.connection-editor__node-segment--connection-focus,.connection-editor__node-segment--compatible,.connection-editor__node-segment--invalid-drop,.connection-editor__segment-popover,.connection-editor__port--compatible,.connection-editor__port--invalid-drop,.connection-editor__port--connection-focus,.connection-editor__invalid-drop,.connection-editor__invalid-drop-pulse{animation:none}.connection-editor__wire{stroke-dashoffset:0}.connection-editor__wire--new{stroke-dasharray:10 8;stroke-dashoffset:0;opacity:1}.connection-editor__wire--halo.connection-editor__wire--new{stroke-width:12;stroke-opacity:.16}.connection-editor__wire-endpoint,.connection-editor__drag-endpoint,.connection-editor__node-ring,.connection-editor__node--trace-source:before{transform:none}.connection-editor__invalid-drop-pulse{opacity:0}}@media(max-width:900px){:host{inset:0}.connection-editor{inset:8px;height:calc(100vh - 16px);padding:12px;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(260px,1fr) minmax(150px,30vh) minmax(76px,112px)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(260px,1fr) minmax(150px,30vh) 48px}.connection-editor__header,.connection-editor__stage,.connection-editor__summary,.connection-editor__side-panel,.connection-editor__dock{grid-column:1}.connection-editor__header{align-items:stretch;flex-wrap:wrap;border-radius:18px}.connection-editor__mode-frame{order:3;width:100%;max-width:100%}.connection-editor__close{margin-inline-start:auto}.connection-editor__mode-frame:before,.connection-editor__mode-frame:after{content:\"\";position:absolute;z-index:2;inset-block:4px;width:24px;border-radius:999px;pointer-events:none}.connection-editor__mode-frame:before{inset-inline-start:1px;background:linear-gradient(90deg,#080d18f0,#080d1800)}.connection-editor__mode-frame:after{inset-inline-end:1px;background:linear-gradient(270deg,#080d18f0,#080d1800)}.connection-editor__mode-shell{width:100%;max-width:100%;overflow-x:auto;scroll-padding-inline:28px;-webkit-overflow-scrolling:touch}.connection-editor__subtitle{margin-inline-start:auto}.connection-editor__stage{grid-row:2;min-height:260px}.connection-editor__summary{grid-row:2;max-width:calc(100% - 24px)}.connection-editor__side-panel{grid-row:3;max-height:30vh}.connection-editor__dock{grid-row:4;min-height:0}.connection-editor__connection-tooltip{width:min(620px,calc(100% - 24px));max-height:min(260px,calc(100% - 24px));overflow:auto}.connection-editor__dock-tabs{width:100%;min-width:0;min-height:0;flex-wrap:wrap;align-items:flex-start}.connection-editor__dock-tablist{width:100%;flex-wrap:wrap}.connection-editor__dock-count{flex:1 1 100%;margin-inline-start:0}.connection-editor__dock-body{width:100%;min-width:0;max-height:76px;box-sizing:border-box}.connection-editor__dock-body pre{white-space:pre-wrap;overflow-wrap:anywhere}.connection-editor__dock-link{grid-template-columns:1fr;align-items:start}.connection-editor__dock-link-route{flex-wrap:wrap}.connection-editor__dock-link-badges{justify-content:flex-start}.connection-editor__trace-overlay{inset-inline-start:14px;width:min(420px,calc(100% - 28px));min-width:0;transform:none}.connection-editor__drag-tip{width:min(300px,calc(100% - 28px))}.connection-editor__segment-popover{width:min(280px,calc(100% - 28px));max-height:min(280px,calc(100% - 28px))}.connection-editor__minimap{inset-inline-end:10px;inset-block-end:10px;width:132px;height:94px;padding:16px 8px 8px;border-radius:14px}.connection-editor__minimap:before{inset:20px 8px 8px;background-size:18px 18px}.connection-editor__minimap-title{inset-block-start:6px;inset-inline-start:10px;font-size:.54rem}}@media(max-width:640px){.connection-editor{inset:4px;height:calc(100vh - 8px);padding:8px;gap:8px;grid-template-rows:auto minmax(240px,1fr) minmax(140px,28vh) minmax(72px,104px)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(240px,1fr) minmax(140px,28vh) 46px}.connection-editor__header{gap:8px;padding:10px}.connection-editor__mode-shell{padding:4px;gap:2px}.connection-editor__mode{min-height:28px;gap:4px;padding:0 7px;font-size:.68rem}.connection-editor__mode-glyph{font-size:.64rem}.connection-editor__stage{min-height:240px}.connection-editor__zoom-controls{inset-inline-start:8px;inset-block-start:8px;gap:6px;width:42px}.connection-editor__tool-group,.connection-editor__trace-source{gap:6px;padding:8px;border-radius:12px}.connection-editor__tool-group{grid-template-columns:28px}.connection-editor__tool-group:nth-of-type(2){grid-template-columns:28px}.connection-editor__tool-button{width:28px;height:28px}.connection-editor__arrange-button,.connection-editor__simulate-button{width:30px;min-height:30px}.connection-editor__summary{gap:6px;padding-inline-end:8px}.connection-editor__side-panel{max-height:28vh}.connection-editor__dock{border-radius:16px}.connection-editor__dock-tabs,.connection-editor__dock-tablist{gap:4px}.connection-editor__dock-tab{padding:0 9px}.connection-editor__dock-count{font-size:.62rem}.connection-editor__dock-body{max-height:66px;padding:8px}.connection-editor__minimap{width:116px;height:84px}.connection-editor__segment-row{grid-template-columns:24px minmax(54px,.55fr) minmax(0,1fr) auto}}.connection-editor__ports{width:max-content}.connection-editor__port{font-size:inherit;white-space:normal;overflow:visible;overflow-wrap:anywhere;padding:4px 8px}.connection-editor__header-actions{display:flex;flex-wrap:wrap;gap:8px}.connection-editor__action--primary{background:var(--pce-primary);color:var(--pce-surface-base);font-weight:700}.connection-editor__action:disabled{opacity:.5;cursor:default}.connection-editor__draft{order:-4;padding:16px;border-bottom:1px solid var(--pce-outline)}.connection-editor__draft p,.connection-editor__working-note{font-size:.8rem;line-height:1.55;color:var(--pce-muted)}.connection-editor__working-note{margin:12px 16px}.connection-editor__draft label{display:grid;gap:6px;margin:14px 0;font-size:.82rem}.connection-editor__draft select,.connection-editor__draft input{box-sizing:border-box;width:100%;min-width:0;min-height:40px;padding:8px;color:var(--pce-fg);background:var(--pce-surface-base);border:1px solid var(--pce-outline-strong);border-radius:8px}.connection-editor__draft-review{display:grid;gap:6px;padding:12px 0;font-size:.85rem;overflow-wrap:anywhere}.connection-editor__draft-actions{position:sticky;bottom:0;z-index:4;padding:12px 0;background:var(--pce-surface-base);border-top:1px solid var(--pce-outline)}.connection-editor--draft .connection-editor__filters-section,.connection-editor--draft .connection-editor__link-list{display:none}.connection-editor__draft-status{color:var(--pce-primary)}.connection-editor__advanced{margin:16px 0}.connection-editor__advanced summary{cursor:pointer;font-size:.82rem;padding:10px 0}.connection-editor__node-title{white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:anywhere;line-height:1.25}.connection-editor--presentation{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}.connection-editor--presentation .connection-editor__side-panel,.connection-editor--presentation .connection-editor__summary,.connection-editor--presentation .connection-editor__dock,.connection-editor--presentation .connection-editor__subtitle,.connection-editor--presentation .connection-editor__mode-frame{display:none}.connection-editor--presentation .connection-editor__stage{grid-column:1;grid-row:2}@media(max-width:900px){.connection-editor__header{flex-wrap:wrap}.connection-editor__header-actions{order:2;width:100%}.connection-editor--draft{grid-template-rows:auto minmax(160px,.45fr) minmax(260px,1fr) 46px}.connection-editor--draft .connection-editor__stage{min-height:0}.connection-editor__side-panel{max-height:none}.connection-editor--draft .connection-editor__summary{display:none}}@media(max-width:640px){.connection-editor__eyebrow,.connection-editor__subtitle,.connection-editor__summary,.connection-editor__minimap{display:none}.connection-editor__header{position:relative;padding-right:42px}.connection-editor__close{position:absolute;top:8px;right:8px}.connection-editor__header-actions{flex-wrap:nowrap}.connection-editor__header-actions .connection-editor__action{min-height:36px}.connection-editor__mode-frame{flex-basis:auto}.connection-editor__side-panel{max-height:none}.connection-editor__draft{padding:10px}.connection-editor__stage{overflow:hidden}.connection-editor__connection-tooltip{display:none}.connection-editor__dock{min-width:0}.connection-editor__dock-tablist{min-width:0;overflow-x:auto;max-width:100%}.connection-editor__dock-meta{display:none}}@media(prefers-reduced-motion:reduce){.connection-editor *,.connection-editor *:before,.connection-editor *:after{animation:none!important;transition:none!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: SurfaceOpenActionEditorComponent, selector: "praxis-surface-open-action-editor", inputs: ["value", "hostKind"], outputs: ["valueChange"] }, { kind: "pipe", type: i2$3.JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9283
9989
  }
9284
9990
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ConnectionEditorComponent, decorators: [{
9285
9991
  type: Component,
@@ -9292,6 +9998,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9292
9998
  @if (open()) {
9293
9999
  <aside
9294
10000
  class="connection-editor"
10001
+ [class.connection-editor--presentation]="presentationMode()"
10002
+ [class.connection-editor--draft]="!!draftBase()"
9295
10003
  [class.connection-editor--guided]="visualDensity() === 'guided'"
9296
10004
  [class.connection-editor--full]="visualDensity() === 'full'"
9297
10005
  [class.connection-editor--dock-collapsed]="dockCollapsed()"
@@ -9312,7 +10020,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9312
10020
  @for (mode of flowModes; track mode.id) {
9313
10021
  <button
9314
10022
  type="button"
9315
- class="connection-editor__mode"
10023
+ class="connection-editor__mode" [disabled]="!!draftBase()"
9316
10024
  role="tab"
9317
10025
  [class.connection-editor__mode--active]="activeMode() === mode.id"
9318
10026
  [attr.aria-selected]="activeMode() === mode.id"
@@ -9328,6 +10036,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9328
10036
  }
9329
10037
  </div>
9330
10038
  </div>
10039
+ <div class="connection-editor__header-actions">
10040
+ <button type="button" class="connection-editor__action connection-editor__action--primary"
10041
+ data-testid="page-builder-connections-new" [disabled]="!!draftBase() || presentationMode()"
10042
+ (click)="beginNewConnection()">+ {{ tx('connections.draft.new', 'New connection') }}</button>
10043
+ <button type="button" class="connection-editor__action"
10044
+ data-testid="page-builder-connections-presentation" [attr.aria-pressed]="presentationMode()"
10045
+ [disabled]="!!draftBase()" (click)="togglePresentation()">
10046
+ {{ presentationMode() ? tx('connections.draft.backToEdit', 'Back to editing') : tx('connections.draft.present', 'Present diagram') }}
10047
+ </button>
10048
+ </div>
9331
10049
  <div class="connection-editor__subtitle">
9332
10050
  <button
9333
10051
  type="button"
@@ -9346,7 +10064,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9346
10064
  class="connection-editor__close"
9347
10065
  data-testid="page-builder-connections-close"
9348
10066
  [attr.aria-label]="tx('connections.closeAria', 'Close connection editor')"
9349
- (click)="close.emit()"
10067
+ (click)="closeEditor()"
9350
10068
  >
9351
10069
  ×
9352
10070
  </button>
@@ -9364,6 +10082,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9364
10082
  <div class="connection-editor__viewport" [style.transform]="viewportTransform()">
9365
10083
  <svg class="connection-editor__wires" [attr.viewBox]="canvasViewBox" aria-hidden="true">
9366
10084
  <defs>
10085
+ <marker id="connection-direction" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse" markerUnits="userSpaceOnUse">
10086
+ <path d="M 1 1 L 9 5 L 1 9" fill="none" stroke="context-stroke" stroke-width="2" />
10087
+ </marker>
9367
10088
  <filter id="connection-glow" x="-20%" y="-20%" width="140%" height="140%">
9368
10089
  <feGaussianBlur stdDeviation="3" result="blur" />
9369
10090
  <feMerge>
@@ -9394,11 +10115,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9394
10115
  [class.connection-editor__wire--trace]="isTracedConnection(connection)"
9395
10116
  [class.connection-editor__wire--active]="selectedLinkId() === connection.id"
9396
10117
  [class.connection-editor__wire--new]="recentLinkId() === connection.id"
9397
- [class.connection-editor__wire--mode-dimmed]="isModeDimmedConnection(connection)"
10118
+ [class.connection-editor__wire--mode-dimmed]="isModeDimmedConnection(connection) || (!!selectedLinkId() && selectedLinkId() !== connection.id)"
9398
10119
  [attr.d]="wirePath(connection)"
9399
10120
  />
9400
10121
  <path
9401
- class="connection-editor__wire"
10122
+ class="connection-editor__wire" marker-end="url(#connection-direction)"
9402
10123
  [class.connection-editor__wire--active]="selectedLinkId() === connection.id"
9403
10124
  [class.connection-editor__wire--state]="connection.intent === 'state-write'"
9404
10125
  [class.connection-editor__wire--state-read]="connection.intent === 'state-read'"
@@ -9409,7 +10130,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9409
10130
  [class.connection-editor__wire--error]="connectionHasErrors(connection)"
9410
10131
  [class.connection-editor__wire--trace]="isTracedConnection(connection)"
9411
10132
  [class.connection-editor__wire--new]="recentLinkId() === connection.id"
9412
- [class.connection-editor__wire--mode-dimmed]="isModeDimmedConnection(connection)"
10133
+ [class.connection-editor__wire--mode-dimmed]="isModeDimmedConnection(connection) || (!!selectedLinkId() && selectedLinkId() !== connection.id)"
9413
10134
  [attr.d]="wirePath(connection)"
9414
10135
  (pointerenter)="previewConnection(connection)"
9415
10136
  (pointerleave)="clearPreviewConnection(connection)"
@@ -9691,9 +10412,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9691
10412
  </svg>
9692
10413
  <span class="connection-editor__node-core">
9693
10414
  <span class="connection-editor__node-icon" aria-hidden="true">{{ nodeIcon(node) }}</span>
9694
- <span class="connection-editor__node-title">{{ node.title }}</span>
10415
+
9695
10416
  <span class="connection-editor__node-subtitle">{{ node.subtitle }}</span>
9696
10417
  </span>
10418
+ <span class="connection-editor__node-caption" [style.font-size.px]="13 / viewport().scale"
10419
+ [style.width.px]="nodeCaptionWidth()">{{ node.title }}</span>
9697
10420
  </button>
9698
10421
 
9699
10422
  @if (visibleExpandedNodeId() === node.id) {
@@ -9771,6 +10494,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9771
10494
  <div
9772
10495
  class="connection-editor__ports connection-editor__ports--outputs"
9773
10496
  [class.connection-editor__ports--visible]="areNodePortsVisible(node)"
10497
+ [style.font-size.px]="11 / viewport().scale"
10498
+ [style.max-width.px]="230 / viewport().scale"
9774
10499
  [style.left.px]="node.x + 132"
9775
10500
  [style.top.px]="node.y + 24"
9776
10501
  (pointerenter)="hoveredNodeId.set(node.id)"
@@ -9795,6 +10520,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9795
10520
  <div
9796
10521
  class="connection-editor__ports connection-editor__ports--inputs"
9797
10522
  [class.connection-editor__ports--visible]="areNodePortsVisible(node)"
10523
+ [style.font-size.px]="11 / viewport().scale"
10524
+ [style.max-width.px]="230 / viewport().scale"
9798
10525
  [style.left.px]="node.x - 104"
9799
10526
  [style.top.px]="node.y + 24"
9800
10527
  (pointerenter)="hoveredNodeId.set(node.id)"
@@ -10052,6 +10779,76 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10052
10779
  [class.connection-editor__side-panel--contextual]="!!selectedConnection() || !!selectedEditorNode()"
10053
10780
  data-testid="page-builder-connections-side-panel"
10054
10781
  >
10782
+ @if (draftBase()) {
10783
+ <section class="connection-editor__draft" data-testid="page-builder-connections-draft">
10784
+ <div class="connection-editor__section-title">{{ tx('connections.draft.title', 'Connection draft') }}</div>
10785
+ <p>{{ tx('connections.draft.help', 'Choose the source and destination, then review the connection before applying it.') }}</p>
10786
+ <label>
10787
+ <span>{{ tx('connections.draft.source', '1 · Source / event') }}</span>
10788
+ <select #sourceSelect [value]="draftSourceId()" (change)="changeDraftEndpoint('source', sourceSelect.value)"
10789
+ data-testid="page-builder-connections-draft-source">
10790
+ <option value="">{{ tx('connections.draft.chooseSource', 'Choose a source') }}</option>
10791
+ @for (port of draftSources(); track port.id) { <option [value]="port.id">{{ draftPortLabel(port) }}</option> }
10792
+ </select>
10793
+ </label>
10794
+ <label>
10795
+ <span>{{ tx('connections.draft.target', '2 · Destination / input') }}</span>
10796
+ <select #targetSelect [value]="draftTargetId()" [disabled]="!draftSourceId()"
10797
+ (change)="changeDraftEndpoint('target', targetSelect.value)" data-testid="page-builder-connections-draft-target">
10798
+ <option value="">{{ tx('connections.draft.chooseTarget', 'Choose a destination') }}</option>
10799
+ @for (port of draftTargets(); track port.id) { <option [value]="port.id">{{ draftPortLabel(port) }}</option> }
10800
+ </select>
10801
+ </label>
10802
+ <p>{{ tx('connections.draft.prerequisites', 'Enable the source event in its component settings. For row selection, enable single selection; configure the detail as read-only when needed.') }}</p>
10803
+ @if (selectedConnection(); as connection) {
10804
+ <div class="connection-editor__draft-review">
10805
+ <span>{{ tx('connections.draft.review', 'Review the route') }}</span>
10806
+ <strong>{{ draftEndpointLabel(draftSourceId()) }}</strong>
10807
+ <span aria-hidden="true">↓</span>
10808
+ <strong>{{ draftEndpointLabel(draftTargetId()) }}</strong>
10809
+ </div>
10810
+ @for (step of connection.link.transform?.steps; track step.id ?? $index) {
10811
+ @if (step.kind === 'pick-path') {
10812
+ <label>
10813
+ <span>{{ tx('connections.draft.valuePath', 'Value to deliver · payload path') }}</span>
10814
+ <input #valuePath [value]="step.config?.['path'] ?? ''" (change)="changeDraftPickPath($index, valuePath.value)"
10815
+ data-testid="page-builder-connections-draft-path" />
10816
+ </label>
10817
+ <p>{{ connection.link.transform?.fallbackValue === null && connection.link.policy?.missingValuePolicy !== 'skip' ? tx('connections.draft.nullFallback', 'When the value is absent, deliver null (clear the destination).') : tx('connections.draft.existingFallback', 'Missing values follow the configured transform and delivery policy below.') }}</p>
10818
+ <div class="connection-editor__actions">
10819
+ <button type="button" class="connection-editor__action"
10820
+ [attr.aria-pressed]="connection.link.transform?.fallbackValue === null && connection.link.policy?.missingValuePolicy !== 'skip'"
10821
+ (click)="setDraftMissingValue('use-default')">{{ tx('connections.draft.clearMissing', 'Clear when absent') }}</button>
10822
+ <button type="button" class="connection-editor__action" [attr.aria-pressed]="connection.link.policy?.missingValuePolicy === 'skip'"
10823
+ (click)="setDraftMissingValue('skip')">{{ tx('connections.draft.keepMissing', 'Keep destination when absent') }}</button>
10824
+ </div>
10825
+ }
10826
+ }
10827
+ @if (!connection.link.transform?.steps?.length) {
10828
+ <p>{{ tx('connections.draft.wholePayload', 'The source value is delivered without a transformation. Use the advanced settings to project a field.') }}</p>
10829
+ <button type="button" class="connection-editor__action" (click)="projectDraftValue()">{{ tx('connections.draft.extract', 'Extract a field') }}</button>
10830
+ }
10831
+ }
10832
+ @for (diagnostic of draftDiagnostics(); track diagnostic.id) {
10833
+ <p class="connection-editor__diagnostic" [class.connection-editor__diagnostic--error]="diagnostic.severity === 'error' || diagnostic.severity === 'fatal'"
10834
+ role="status">{{ diagnostic.message }}</p>
10835
+ }
10836
+ @if (draftDuplicate()) { <p role="alert">{{ tx('connections.draft.duplicate', 'An identical connection already exists. Edit the existing connection or change this draft.') }}</p> }
10837
+ @if (draftConflict()) { <p role="alert">{{ tx('connections.draft.conflict', 'The page changed during editing. Cancel this draft and reopen the connection to use the latest version.') }}</p> }
10838
+ <p class="connection-editor__draft-status" role="status">{{ tx('connections.draft.pending', 'Draft · not yet applied to the page') }}</p>
10839
+ <div class="connection-editor__actions connection-editor__draft-actions">
10840
+ <button type="button" class="connection-editor__action connection-editor__action--primary"
10841
+ data-testid="page-builder-connections-draft-apply" [disabled]="!canApplyDraft()" (click)="applyDraft()">
10842
+ {{ tx('connections.draft.apply', 'Apply connection') }}
10843
+ </button>
10844
+ <button type="button" class="connection-editor__action" data-testid="page-builder-connections-draft-cancel"
10845
+ (click)="cancelDraft()">{{ tx('connections.draft.cancel', 'Cancel') }}</button>
10846
+ </div>
10847
+ </section>
10848
+ } @else {
10849
+ <p class="connection-editor__working-note" role="status">{{ tx('connections.draft.pageSave', 'Connections apply to the page in the builder. Save the page to persist your changes.') }}</p>
10850
+ }
10851
+ @if (visualDensity() === 'full' && !draftBase()) {
10055
10852
  @if (recommendedMasterDetailSuggestion(); as suggestion) {
10056
10853
  <section
10057
10854
  class="connection-editor__section connection-editor__recommended"
@@ -10072,6 +10869,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10072
10869
  </button>
10073
10870
  </section>
10074
10871
  }
10872
+ }
10075
10873
  <section class="connection-editor__section connection-editor__filters-section">
10076
10874
  <div class="connection-editor__section-title">{{ tx('connections.filters.title', 'Filters') }}</div>
10077
10875
  <div class="connection-editor__filters">
@@ -10154,6 +10952,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10154
10952
  <pre>{{ connection.link.to | json }}</pre>
10155
10953
  </div>
10156
10954
  </details>
10955
+ @if (!draftBase()) {
10956
+ <button type="button" class="connection-editor__action connection-editor__action--primary"
10957
+ data-testid="page-builder-connections-edit" (click)="beginEditConnection(connection.id)">
10958
+ {{ tx('connections.draft.edit', 'Edit connection') }}
10959
+ </button>
10960
+ }
10961
+ <details class="connection-editor__advanced" [open]="!!draftBase()">
10962
+ <summary>{{ tx('connections.draft.advanced', 'Transformation, conditions and delivery') }}</summary>
10157
10963
  @if (connection.link.to.kind === 'global-action') {
10158
10964
  <div class="connection-editor__details-block" data-testid="page-builder-connections-global-action-payload-editor">
10159
10965
  <div class="connection-editor__details-label">{{ tx('connections.globalAction.payloadTitle', 'Action payload') }}</div>
@@ -10340,6 +11146,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10340
11146
  }
10341
11147
  </div>
10342
11148
  </div>
11149
+ </details>
10343
11150
  @if (connection.diagnostics.length) {
10344
11151
  <div class="connection-editor__details-block" data-testid="page-builder-connections-inspector-diagnostics">
10345
11152
  <div class="connection-editor__details-label">{{ tx('connections.validation.title', 'Validation') }}</div>
@@ -10381,7 +11188,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10381
11188
  <button
10382
11189
  type="button"
10383
11190
  class="connection-editor__action connection-editor__action--danger"
10384
- data-testid="page-builder-connections-inspector-remove-link"
11191
+ data-testid="page-builder-connections-inspector-remove-link" [disabled]="!!draftBase()"
10385
11192
  (click)="removeLink(connection.id)"
10386
11193
  >
10387
11194
  {{ tx('connections.editor.removeLink', 'Remove connection') }}
@@ -10456,6 +11263,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10456
11263
  </div>
10457
11264
  </div>
10458
11265
  }
11266
+ @if (visualDensity() === 'full' && !draftBase()) {
10459
11267
  @if (smartSuggestions().length) {
10460
11268
  <div class="connection-editor__suggestions" data-testid="page-builder-connections-suggestions">
10461
11269
  <div class="connection-editor__section-title">{{ tx('connections.suggestions.title', 'Smart suggestions') }}</div>
@@ -10503,6 +11311,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10503
11311
  </div>
10504
11312
  </div>
10505
11313
  }
11314
+ }
10506
11315
  @if (traceEnabled()) {
10507
11316
  <div class="connection-editor__trace" data-testid="page-builder-connections-trace">
10508
11317
  <div class="connection-editor__section-title">{{ tx('connections.trace.title', 'Simulation trace') }}</div>
@@ -10525,7 +11334,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10525
11334
  }
10526
11335
  </section>
10527
11336
 
10528
- <section class="connection-editor__section">
11337
+ <section class="connection-editor__section connection-editor__widget-list">
10529
11338
  <div class="connection-editor__section-title">{{ tx('connections.widgets.title', 'Widgets') }}</div>
10530
11339
  @for (node of model().nodes; track node.widgetId) {
10531
11340
  <button
@@ -10560,7 +11369,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10560
11369
  </section>
10561
11370
  }
10562
11371
 
10563
- <section class="connection-editor__section">
11372
+ <section class="connection-editor__section connection-editor__link-list">
10564
11373
  <div class="connection-editor__section-title">{{ tx('connections.links.title', 'Links') }}</div>
10565
11374
  @for (edge of visibleEdges(); track edge.id) {
10566
11375
  <button
@@ -10570,8 +11379,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10570
11379
  [class.connection-editor__item--active]="selectedLinkId() === edge.id"
10571
11380
  (click)="selectLink(edge)"
10572
11381
  >
10573
- <div class="connection-editor__item-title">{{ edge.id }}</div>
10574
- <div class="connection-editor__item-copy">{{ describeEdge(edge) }}</div>
11382
+ <div class="connection-editor__item-title">{{ edgeRouteTitle(edge) }}</div>
11383
+ <div class="connection-editor__item-copy">{{ edge.id }}</div>
10575
11384
  <div class="connection-editor__badges">
10576
11385
  @if (edge.hasCondition) {
10577
11386
  <span class="connection-editor__badge">{{ tx('connections.badges.condition', 'Condition') }}</span>
@@ -10811,9 +11620,177 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10811
11620
  </section>
10812
11621
  </aside>
10813
11622
  }
10814
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{position:fixed;inset:0;z-index:1000;pointer-events:none}.connection-editor{--pce-fg: var(--md-sys-color-on-surface, #dce6f4);--pce-strong: var(--md-sys-color-on-surface, #f8fafc);--pce-muted: var(--md-sys-color-on-surface-variant, #94a3b8);--pce-primary: var(--md-sys-color-primary, #19c8e8);--pce-secondary: var(--md-sys-color-secondary, #b46cff);--pce-tertiary: var(--md-sys-color-tertiary, #ff9f2f);--pce-success: var(--md-sys-color-tertiary, #10b981);--pce-warning: var(--md-sys-color-error, #ff9f2f);--pce-error: var(--md-sys-color-error, #ef4444);--pce-surface-base: color-mix(in srgb, var(--md-sys-color-surface, #07111f) 80%, #020617);--pce-surface: color-mix(in srgb, var(--md-sys-color-surface-container, #08101d) 78%, transparent);--pce-surface-strong: color-mix(in srgb, var(--md-sys-color-surface-container-high, #0f172a) 88%, transparent);--pce-surface-soft: color-mix(in srgb, var(--md-sys-color-surface-container-low, #080d18) 64%, transparent);--pce-outline: color-mix(in srgb, var(--md-sys-color-outline-variant, #899ab7) 46%, transparent);--pce-outline-strong: color-mix(in srgb, var(--md-sys-color-outline, #94a3b8) 58%, transparent);--pce-grid: color-mix(in srgb, var(--md-sys-color-outline-variant, #7d8fa9) 24%, transparent);--pce-glass-highlight: color-mix(in srgb, var(--md-sys-color-on-surface, #ffffff) 8%, transparent);--pce-shadow: color-mix(in srgb, #000 56%, transparent);--pce-shadow-soft: color-mix(in srgb, #000 30%, transparent);--pce-stage-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest, #040a13) 42%, transparent);--pce-dock-bg: color-mix(in srgb, var(--md-sys-color-surface-container-high, #050912) 94%, transparent);position:absolute;inset:12px;height:calc(100vh - 24px);box-sizing:border-box;overflow:hidden;padding:16px;border-radius:18px;color:var(--pce-fg);background:radial-gradient(circle at 72% 20%,color-mix(in srgb,var(--pce-secondary) 16%,transparent),transparent 28%),radial-gradient(circle at 26% 74%,color-mix(in srgb,var(--pce-primary) 10%,transparent),transparent 34%),radial-gradient(circle at 48% 52%,color-mix(in srgb,var(--pce-tertiary) 5%,transparent),transparent 35%),linear-gradient(var(--pce-grid) 1px,transparent 1px),linear-gradient(90deg,var(--pce-grid) 1px,transparent 1px),var(--pce-surface-base);background-size:auto,auto,auto,38px 38px,38px 38px,auto;border:1px solid var(--pce-outline);box-shadow:0 28px 90px var(--pce-shadow);pointer-events:auto;display:grid;grid-template-columns:minmax(0,1fr) minmax(300px,380px);grid-template-rows:auto minmax(0,1fr) minmax(44px,118px);gap:14px}:host-context(.theme-light) .connection-editor{--pce-surface-base: color-mix(in srgb, var(--md-sys-color-surface, #ffffff) 94%, var(--md-sys-color-primary, #2563eb) 6%);--pce-surface: color-mix(in srgb, var(--md-sys-color-surface-container, #f8fafc) 82%, transparent);--pce-surface-strong: color-mix(in srgb, var(--md-sys-color-surface-container-high, #eef2ff) 88%, transparent);--pce-surface-soft: color-mix(in srgb, var(--md-sys-color-surface-container-low, #f8fafc) 70%, transparent);--pce-outline: color-mix(in srgb, var(--md-sys-color-outline-variant, #cbd5e1) 68%, transparent);--pce-outline-strong: color-mix(in srgb, var(--md-sys-color-outline, #64748b) 62%, transparent);--pce-grid: color-mix(in srgb, var(--md-sys-color-outline-variant, #cbd5e1) 34%, transparent);--pce-glass-highlight: color-mix(in srgb, #fff 64%, transparent);--pce-shadow: color-mix(in srgb, var(--md-sys-color-shadow, #64748b) 22%, transparent);--pce-shadow-soft: color-mix(in srgb, var(--md-sys-color-shadow, #64748b) 14%, transparent);--pce-stage-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest, #ffffff) 58%, transparent);--pce-dock-bg: color-mix(in srgb, var(--md-sys-color-surface-container, #f8fafc) 92%, transparent)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(0,1fr) 48px}.connection-editor__header{grid-column:1 / -1;display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:42px;padding:8px 14px;border-radius:999px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__mode-frame{position:relative;min-width:0;flex:1 1 auto;display:flex;justify-content:center}.connection-editor__close{width:32px;height:32px;flex:0 0 auto;display:inline-grid;place-items:center;border:1px solid var(--pce-outline);border-radius:999px;color:var(--pce-text);background:var(--pce-surface-strong);box-shadow:inset 0 1px var(--pce-glass-highlight);cursor:pointer;font:inherit;font-size:1.2rem;line-height:1}.connection-editor__close:hover,.connection-editor__close:focus-visible{border-color:var(--pce-outline-strong);background:color-mix(in srgb,var(--pce-accent) 16%,var(--pce-surface-strong));outline:none}.connection-editor__mode-shell{display:inline-flex;align-items:center;gap:4px;min-width:0;padding:4px;border-radius:999px;border:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-surface-strong),var(--pce-surface));box-shadow:inset 0 1px var(--pce-glass-highlight),0 10px 26px var(--pce-shadow-soft);scrollbar-width:none}.connection-editor__mode-shell::-webkit-scrollbar{display:none}.connection-editor__mode{min-height:30px;display:inline-flex;align-items:center;gap:7px;padding:0 12px;border:1px solid transparent;border-radius:999px;background:transparent;color:var(--pce-muted);cursor:pointer;font-size:.76rem;font-weight:800;white-space:nowrap;transition:background .16s ease,border-color .16s ease,color .16s ease,box-shadow .16s ease}.connection-editor__mode:hover,.connection-editor__mode--active{color:var(--pce-strong);border-color:color-mix(in srgb,var(--pce-primary) 28%,transparent);background:color-mix(in srgb,var(--pce-surface-strong) 82%,transparent);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 12%,transparent)}.connection-editor__mode-glyph{color:var(--pce-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.72rem;font-weight:900}.connection-editor__mode--active .connection-editor__mode-glyph{color:var(--pce-primary)}.connection-editor__mode-dot{width:6px;height:6px;border-radius:999px;background:var(--pce-success);box-shadow:0 0 12px color-mix(in srgb,var(--pce-success) 70%,transparent)}.connection-editor__status-pill{display:inline-flex;align-items:center;gap:8px;min-height:24px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--pce-primary) 36%,transparent);background:var(--pce-surface-soft);color:var(--pce-muted);font-size:.72rem;font-weight:800;white-space:nowrap}.connection-editor__status-pill--button{cursor:pointer;transition:border-color .16s ease,background .16s ease,color .16s ease,box-shadow .16s ease}.connection-editor__status-pill--button:hover,.connection-editor__status-pill--button:focus-visible{border-color:color-mix(in srgb,var(--pce-primary) 62%,transparent);background:color-mix(in srgb,var(--pce-primary) 12%,var(--pce-surface-soft));color:var(--pce-strong);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 14%,transparent);outline:none}.connection-editor__status-pill span{width:6px;height:6px;border-radius:999px;background:var(--pce-primary);box-shadow:0 0 12px color-mix(in srgb,var(--pce-primary) 70%,transparent)}.connection-editor__section{display:grid;gap:10px}.connection-editor__dock{grid-column:1 / -1;grid-row:3;position:relative;z-index:4;min-height:0;display:grid;border-radius:14px;border:1px solid var(--pce-outline);background:radial-gradient(circle at 16% 0%,color-mix(in srgb,var(--pce-primary) 22%,transparent),transparent 34%),radial-gradient(circle at 78% 0%,color-mix(in srgb,var(--pce-secondary) 25%,transparent),transparent 38%),linear-gradient(90deg,var(--pce-dock-bg),var(--pce-surface-strong) 46%,color-mix(in srgb,var(--pce-secondary) 14%,var(--pce-dock-bg))),var(--pce-dock-bg);box-shadow:0 18px 58px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);overflow:hidden;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__dock--collapsed .connection-editor__dock-tabs{border-block-end:0}.connection-editor__dock-tabs{display:flex;align-items:center;gap:10px;min-height:52px;padding:9px 14px;border-block-end:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-glass-highlight),transparent),color-mix(in srgb,var(--pce-surface-soft) 34%,transparent)}.connection-editor__dock-tablist{display:flex;align-items:center;gap:10px;min-width:0}.connection-editor__dock-tab,.connection-editor__dock-toggle{min-height:30px;border-radius:999px;border:1px solid transparent;background:transparent;color:var(--pce-muted);cursor:pointer;font-size:.72rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__dock-tab{display:inline-flex;align-items:center;gap:8px;padding:0 13px;white-space:nowrap}.connection-editor__dock-tab-glyph{color:var(--pce-muted);font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:.8rem;letter-spacing:0;text-transform:none}.connection-editor__dock-tab--active{color:var(--pce-strong);border-color:var(--pce-outline-strong);background:linear-gradient(180deg,color-mix(in srgb,var(--pce-strong) 14%,transparent),color-mix(in srgb,var(--pce-strong) 6%,transparent));box-shadow:0 0 0 1px color-mix(in srgb,var(--pce-shadow) 72%,transparent),0 0 22px color-mix(in srgb,var(--pce-strong) 8%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor__dock-tab--active .connection-editor__dock-tab-glyph{color:var(--pce-strong)}.connection-editor__dock-count{margin-inline-start:auto;color:color-mix(in srgb,var(--pce-muted) 62%,transparent);font-size:.7rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase;white-space:nowrap}.connection-editor__dock-toggle{width:32px;padding:0;border-color:var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-muted);font-size:.88rem;letter-spacing:0}.connection-editor__dock-body{max-height:154px;overflow:auto;padding:12px 12px 14px;border-block-start:1px solid var(--pce-outline);background:linear-gradient(90deg,color-mix(in srgb,var(--pce-primary) 9%,transparent),color-mix(in srgb,var(--pce-secondary) 10%,transparent) 62%,color-mix(in srgb,var(--pce-surface-strong) 18%,transparent)),color-mix(in srgb,var(--pce-surface-soft) 30%,transparent)}.connection-editor__dock-body pre{max-height:60px}.connection-editor__dock-links{display:grid;gap:8px}.connection-editor__dock-link-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:10px;width:100%;padding:7px 10px;border-radius:10px;border:1px solid var(--pce-outline);background:linear-gradient(90deg,var(--pce-surface-soft),color-mix(in srgb,var(--pce-secondary) 10%,var(--pce-surface-soft)) 72%,var(--pce-surface-soft)),var(--pce-surface-soft);color:var(--pce-fg);transition:border-color .15s ease,background .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__dock-link{display:grid;grid-template-columns:minmax(116px,.52fr) minmax(300px,1.72fr) minmax(160px,.9fr) auto;align-items:center;gap:12px;width:100%;min-height:42px;padding:0;border:0;border-radius:8px;background:transparent;color:inherit;cursor:pointer;text-align:start}.connection-editor__dock-link-row:hover,.connection-editor__dock-link--active{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 42%,transparent);background:linear-gradient(90deg,color-mix(in srgb,var(--pce-primary) 13%,transparent),color-mix(in srgb,var(--pce-secondary) 16%,transparent),var(--pce-surface-strong)),var(--pce-surface-strong);box-shadow:0 12px 32px var(--pce-shadow-soft),inset 0 0 18px color-mix(in srgb,var(--pce-primary) 6%,transparent)}.connection-editor__dock-link--active{border-color:#facc1575;box-shadow:0 12px 32px var(--pce-shadow-soft),0 0 24px #facc151f}.connection-editor__dock-link--new{border-color:#fb923ca3;background:linear-gradient(90deg,#fb923c2e,#a855f729,#0ea5e91f),#0f172ac2;box-shadow:0 12px 34px #02061752,0 0 30px #fb923c2e;animation:new-dock-link-flash 1.35s ease-out forwards}.connection-editor__dock-link--error{border-color:#ef444461}.connection-editor__dock-jump{min-width:62px;min-height:30px;padding:0 10px;border-radius:999px;border:1px solid color-mix(in srgb,var(--pce-primary) 28%,transparent);background:color-mix(in srgb,var(--pce-surface-soft) 54%,transparent);color:var(--pce-primary);cursor:pointer;font-size:.66rem;font-weight:900;letter-spacing:.06em;text-transform:uppercase;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease}.connection-editor__dock-jump:hover,.connection-editor__dock-jump:focus-visible{border-color:color-mix(in srgb,var(--pce-primary) 56%,transparent);background:color-mix(in srgb,var(--pce-primary) 16%,transparent);color:var(--pce-strong);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 18%,transparent);outline:none}.connection-editor__dock-link-kind{display:inline-flex;align-items:center;gap:7px;min-width:0;color:var(--pce-muted);font-size:.66rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase;white-space:nowrap}.connection-editor__dock-link-kind span{width:8px;height:8px;border-radius:999px;background:#b46cff;box-shadow:0 0 14px #b46cff7a}.connection-editor__dock-link--state .connection-editor__dock-link-kind span{background:#ff9f2f;box-shadow:0 0 16px #ff9f2f94}.connection-editor__dock-link--state-read .connection-editor__dock-link-kind span{background:#8b7dff;box-shadow:0 0 13px #8b7dff75}.connection-editor__dock-link--projection .connection-editor__dock-link-kind span{background:#19c8e8;box-shadow:0 0 10px #19c8e857}.connection-editor__dock-link--event-propagation .connection-editor__dock-link-kind span{background:#ffb454;box-shadow:0 0 15px #ffb45485}.connection-editor__dock-link--policy .connection-editor__dock-link-kind span{outline:1px solid rgba(34,197,94,.76);outline-offset:2px;box-shadow:0 0 16px #22c55e61}.connection-editor__dock-link--policy:not(.connection-editor__dock-link--state,.connection-editor__dock-link--state-read,.connection-editor__dock-link--projection,.connection-editor__dock-link--event-propagation) .connection-editor__dock-link-kind span{background:#22c55e}.connection-editor__dock-link-route{display:inline-flex;align-items:center;gap:8px;min-width:0}.connection-editor__dock-link-chip{min-width:0;overflow:hidden;padding:4px 8px;border-radius:999px;border:1px solid var(--pce-outline);background:color-mix(in srgb,var(--pce-surface-soft) 72%,transparent);color:var(--pce-fg);font-size:.72rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__dock-link-chip--from{border-color:#ff9f2f66;color:#ffbd62}.connection-editor__dock-link-chip--to{border-color:#19c8e83d;color:#7dddeb}:host-context(.theme-light) .connection-editor__dock-link-chip--from{border-color:color-mix(in srgb,var(--pce-tertiary) 46%,transparent);color:#8a4600}:host-context(.theme-light) .connection-editor__dock-link-chip--to{border-color:color-mix(in srgb,var(--pce-primary) 34%,transparent);color:#075f73}.connection-editor__dock-link-arrow{flex:0 0 auto;color:var(--pce-muted);font-weight:900}.connection-editor__dock-link-id{min-width:0;overflow:hidden;color:var(--pce-muted);font-size:.66rem;font-weight:800;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-overflow:ellipsis;white-space:nowrap}.connection-editor__dock-link-badges{display:inline-flex;justify-content:flex-end;gap:5px;min-width:70px}.connection-editor__dock-link-badges span{padding:3px 7px;border-radius:999px;background:#a855f724;color:#d8b4fe;font-size:.62rem;font-weight:900;text-transform:uppercase;white-space:nowrap}:host-context(.theme-light) .connection-editor__dock-link-badges span{background:color-mix(in srgb,var(--pce-secondary) 14%,var(--pce-surface-soft));color:#6d2a9f}.connection-editor__dock-empty{padding:12px;border-radius:12px;border:1px dashed var(--pce-outline);color:var(--pce-muted);font-size:.76rem;font-weight:700}.connection-editor__inspector{border-color:color-mix(in srgb,var(--pce-primary) 26%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 10%,transparent),color-mix(in srgb,var(--pce-secondary) 8%,transparent)),var(--pce-surface)}.connection-editor__eyebrow,.connection-editor__section-title{font-size:.72rem;text-transform:uppercase;letter-spacing:.16em;color:var(--pce-muted);font-weight:700}.connection-editor__title{font-size:1.02rem;font-weight:800}.connection-editor__subtitle,.connection-editor__item-copy,.connection-editor__item-meta,.connection-editor__details-row span,.connection-editor__details-label,.connection-editor__empty{color:var(--pce-muted);font-size:.84rem}.connection-editor__inspector-heading{display:grid;gap:4px;padding:10px 12px;border-radius:14px;background:var(--pce-surface-soft);border:1px solid var(--pce-outline)}.connection-editor__inspector-heading strong{color:var(--pce-strong);font-size:.96rem;overflow-wrap:anywhere}.connection-editor__inspector-heading span{color:var(--pce-primary);font-size:.76rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em}.connection-editor__human-summary{display:grid;gap:6px;padding:12px;border-radius:14px;border:1px solid color-mix(in srgb,var(--pce-primary) 24%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 12%,transparent),color-mix(in srgb,var(--pce-secondary) 8%,transparent)),var(--pce-surface-soft);color:var(--pce-fg);line-height:1.35}.connection-editor__nested-summary{display:grid;gap:6px;padding:10px;border-radius:12px;border:1px solid color-mix(in srgb,var(--pce-primary) 22%,transparent);background:color-mix(in srgb,var(--pce-primary) 8%,var(--pce-surface-soft))}.connection-editor__nested-summary strong{color:var(--pce-strong);font-size:.82rem}.connection-editor__nested-summary span:last-child{color:var(--pce-primary);font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:.72rem;overflow-wrap:anywhere}.connection-editor__recommended{border-color:color-mix(in srgb,var(--pce-secondary) 32%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 14%,transparent),color-mix(in srgb,var(--pce-primary) 10%,transparent)),var(--pce-surface)}.connection-editor__recommended-card{display:grid;gap:8px;width:100%;padding:13px;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-secondary) 36%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 16%,transparent),color-mix(in srgb,var(--pce-primary) 10%,transparent)),var(--pce-surface-strong);color:var(--pce-fg);text-align:start;cursor:pointer;transition:transform .16s ease,border-color .16s ease,background .16s ease}.connection-editor__recommended-card:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 52%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 22%,transparent),color-mix(in srgb,var(--pce-primary) 14%,transparent)),var(--pce-surface-strong)}.connection-editor__recommended-card strong{color:var(--pce-strong);font-size:.92rem;line-height:1.28}.connection-editor__recommended-card span:not(.connection-editor__human-summary-label):not(.connection-editor__suggestion-action),.connection-editor__recommended-card small{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__success{display:grid;gap:4px;padding:10px 12px;border-radius:14px;border:1px solid rgba(34,197,94,.34);background:#22c55e1f;color:var(--pce-fg);line-height:1.35}.connection-editor__success strong{color:#bbf7d0;font-size:.78rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__success span{color:var(--pce-strong);font-size:.82rem}.connection-editor__human-summary-label,.connection-editor__suggestion-intent{justify-self:start;padding:3px 8px;border-radius:999px;background:color-mix(in srgb,var(--pce-primary) 16%,transparent);color:#bae6fd;font-size:.66rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.connection-editor__human-summary strong{color:var(--pce-strong);font-size:.88rem}.connection-editor__human-summary span:last-child,.connection-editor__suggestion-preview{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__technical-details{display:grid;gap:10px;padding:10px 12px;border-radius:14px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft)}.connection-editor__technical-details summary{cursor:pointer;color:var(--pce-muted);font-size:.78rem;font-weight:800}.connection-editor__technical-details[open] summary{margin-bottom:10px;color:var(--pce-strong)}.connection-editor__inspector-ports{display:flex;flex-wrap:wrap;gap:6px}.connection-editor__inspector-port{max-width:100%;padding:4px 8px;border-radius:999px;border:1px solid rgba(148,163,184,.16);background:#0f172a94;color:#cbd5e1;font-size:.68rem;font-weight:800;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.connection-editor__inspector-port--input{border-color:#22d3ee57;color:#67e8f9}.connection-editor__inspector-port--output{border-color:#f59e0b57;color:#fbbf24}.connection-editor__inspector-port--state{border-color:#a855f761;color:#d8b4fe}.connection-editor__segment-kind-legend{display:grid;gap:10px;padding:12px;border-radius:16px;border:1px solid rgba(148,163,184,.14);background:linear-gradient(135deg,#0f172aa8,#080d1885),#02061757}.connection-editor__segment-kind-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.connection-editor__segment-kind-pill{min-width:0;display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:28px;padding:0 8px;border-radius:999px;border:1px solid rgba(148,163,184,.14);background:#0f172a94;color:#cbd5e1;font-size:.68rem;font-weight:900;overflow:hidden;white-space:nowrap}.connection-editor__segment-kind-pill span{width:8px;height:8px;flex:0 0 auto;border-radius:999px;background:currentColor;box-shadow:0 0 12px currentColor}.connection-editor__segment-kind-pill--input{border-color:#19c8e83d;color:#7dddeb}.connection-editor__segment-kind-pill--output{border-color:#ff9f2f61;color:#ffbd62}.connection-editor__segment-kind-pill--state{border-color:#b46cff61;color:#d9b8ff}:host-context(.theme-light) .connection-editor__segment-kind-pill{border-color:var(--pce-outline);background:color-mix(in srgb,var(--pce-surface-strong) 88%,transparent)}:host-context(.theme-light) .connection-editor__segment-kind-pill--input{color:#075f73}:host-context(.theme-light) .connection-editor__segment-kind-pill--output{color:#8a4600}:host-context(.theme-light) .connection-editor__segment-kind-pill--state{color:#6d2a9f}.connection-editor__stage{grid-column:1;grid-row:2;position:relative;min-height:0;height:100%;overflow:visible;border-radius:16px;border:1px solid var(--pce-outline);background:radial-gradient(circle at center,color-mix(in srgb,var(--pce-secondary) 7%,transparent),transparent 38%),radial-gradient(circle,var(--pce-grid) 1px,transparent 1.5px),var(--pce-stage-bg);background-size:auto,31px 31px,auto;box-shadow:inset 0 0 0 1px var(--pce-glass-highlight);cursor:grab;touch-action:none}.connection-editor__stage--panning,.connection-editor__stage:active{cursor:grabbing}.connection-editor__stage--dragging{cursor:crosshair}.connection-editor__stage--rotating{cursor:grabbing}.connection-editor__viewport{position:absolute;inset-block-start:0;inset-inline-start:0;width:900px;height:540px;overflow:visible;transform-origin:0 0;transition:transform .14s ease}.connection-editor__wires{position:absolute;inset:0;width:100%;height:100%;overflow:visible}.connection-editor__wire,.connection-editor__wire-hit{fill:none;stroke-linecap:round}.connection-editor__wire{stroke:#b46cff;stroke-width:1.45;stroke-opacity:.48;pointer-events:none;filter:url(#connection-glow);stroke-dasharray:10 8;animation:connection-flow 1.65s linear infinite}.connection-editor__wire-hit{stroke:transparent;stroke-width:18;cursor:pointer;pointer-events:stroke}.connection-editor__wire-hit--active{stroke-width:28}.connection-editor__wire--halo{stroke:#b46cff1f;stroke-width:7;stroke-dasharray:none;animation:none}.connection-editor__wire--state{stroke:#ff9f2f;stroke-width:2.25;stroke-dasharray:8 7;animation:connection-flow .92s linear infinite}.connection-editor__wire--state.connection-editor__wire--halo{stroke:#ff9f2f29;stroke-width:11}.connection-editor__wire--projection{stroke:#15b8d8;stroke-width:1.75;stroke-dasharray:20 8;animation:connection-flow 1.9s linear infinite}.connection-editor__wire--projection.connection-editor__wire--halo{stroke:#15b8d81a;stroke-width:7}.connection-editor__wire--state-read{stroke:#8b7dff;stroke-width:1.65;stroke-dasharray:3 11;animation:connection-flow 2.25s linear infinite reverse}.connection-editor__wire--state-read.connection-editor__wire--halo{stroke:#8b7dff21;stroke-width:8}.connection-editor__wire--event-propagation{stroke:#ffb454;stroke-width:2.05;stroke-dasharray:5 7;animation:connection-flow .78s linear infinite}.connection-editor__wire--event-propagation.connection-editor__wire--halo{stroke:#ffb4542e;stroke-width:10}.connection-editor__wire--policy{filter:drop-shadow(0 0 6px rgba(34,197,94,.2)) url(#connection-glow)}.connection-editor__wire--policy.connection-editor__wire--halo{stroke:#22c55e1f;stroke-width:13}.connection-editor__wire--warning{stroke:#ff9f2f}.connection-editor__wire--warning.connection-editor__wire--halo{stroke:#ff9f2f2e}.connection-editor__wire--error{stroke:#ef4444}.connection-editor__wire--error.connection-editor__wire--halo{stroke:#ef44442e}.connection-editor__wire--active{stroke:#d9b8ff;stroke-width:3;stroke-opacity:1;stroke-dasharray:5 7;animation-duration:.58s}.connection-editor__wire--halo.connection-editor__wire--active{stroke-width:16;stroke-opacity:.8;animation:selected-wire-aura 1.35s ease-in-out infinite}.connection-editor__wire--new{stroke-width:3.4;stroke-opacity:1;stroke-dasharray:640;stroke-dashoffset:640;animation:wire-draw-in .62s cubic-bezier(.16,1,.3,1) forwards,connection-flow 1.05s linear .62s infinite}.connection-editor__wire--halo.connection-editor__wire--new{stroke-width:22;stroke-opacity:0;animation:new-wire-halo 1.45s ease-out forwards}.connection-editor__wire--trace{stroke:#9b5cff;stroke-width:3.2;stroke-opacity:1;stroke-dasharray:7 6;animation:connection-flow .72s linear infinite,trace-wire-pulse 1.18s ease-in-out infinite}.connection-editor__wire--halo.connection-editor__wire--trace{stroke:#9b5cff57;stroke-width:18;stroke-opacity:.86;animation:trace-halo-pulse 1.18s ease-in-out infinite}.connection-editor__wire--mode-dimmed{stroke-opacity:.14;stroke-width:1.05;filter:none;animation-duration:2.6s}.connection-editor--guided .connection-editor__wire:not(.connection-editor__wire--active,.connection-editor__wire--new,.connection-editor__wire--trace){stroke-opacity:.34;filter:none;animation:none}.connection-editor--guided .connection-editor__wire--halo:not(.connection-editor__wire--active,.connection-editor__wire--new,.connection-editor__wire--trace){stroke-opacity:.08;animation:none}.connection-editor__wire--halo.connection-editor__wire--mode-dimmed{stroke-opacity:.05;stroke-width:5;animation:none}.connection-editor__wire-hit--mode-dimmed{stroke-width:12}.connection-editor__wire-endpoint{fill:#f8fafc;stroke:#0f172ae6;stroke-width:1.5;filter:drop-shadow(0 0 8px rgba(216,180,254,.8));pointer-events:none;animation:endpoint-pulse 1.2s ease-in-out infinite}.connection-editor__wire-endpoint--source{fill:#ff9f2f}.connection-editor__wire-endpoint--target{fill:#19c8e8}.connection-editor__connection-tooltip{position:absolute;z-index:6;width:min(312px,calc(100% - 24px));display:grid;gap:8px;padding:12px 14px;border-radius:14px;border:1px solid var(--pce-outline);background:linear-gradient(135deg,var(--pce-surface-strong),var(--pce-surface)),var(--pce-surface-strong);box-shadow:0 18px 44px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);color:var(--pce-fg);pointer-events:none;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);animation:tooltip-rise .14s ease-out}.connection-editor__connection-tooltip-callout{position:absolute;inset-block-start:100%;inset-inline-start:var(--connection-tooltip-callout-x, 42px);width:2px;height:28px;border-radius:999px;background:linear-gradient(180deg,#e2e8f0bd,#19c8e800);box-shadow:0 0 12px #19c8e852;transform:translate(-1px);pointer-events:none}.connection-editor__connection-tooltip-callout:before,.connection-editor__connection-tooltip-callout:after{content:\"\";position:absolute;border-radius:999px;pointer-events:none}.connection-editor__connection-tooltip-callout:before{inset-block-start:-5px;inset-inline-start:-5px;width:12px;height:12px;border:1px solid rgba(148,163,184,.24);background:#080d18f5;box-shadow:inset 0 1px #ffffff14}.connection-editor__connection-tooltip-callout:after{inset-block-end:-3px;inset-inline-start:-4px;width:10px;height:10px;background:#19c8e8;box-shadow:0 0 16px #19c8e88f}.connection-editor__connection-tooltip--projection{border-color:#19c8e833;background:linear-gradient(135deg,#15b8d81f,#0f172ae0),#070c16eb}.connection-editor__connection-tooltip--state{border-color:#ff9f2f66;background:linear-gradient(135deg,#ff9f2f3d,#0f172ae0),#070c16eb}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-callout{background:linear-gradient(180deg,#ffbd62d1,#ff9f2f00);box-shadow:0 0 14px #ff9f2f61}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-callout:after{background:#ff9f2f;box-shadow:0 0 18px #ff9f2fad}.connection-editor__connection-tooltip--error{border-color:#f871716b}.connection-editor__connection-tooltip--error .connection-editor__connection-tooltip-callout:after{background:#fb7185;box-shadow:0 0 18px #fb7185ad}.connection-editor__connection-tooltip-kind{display:inline-flex;align-items:center;gap:8px;color:#94a3b8;font-size:.62rem;font-weight:900;letter-spacing:.16em;text-transform:uppercase}.connection-editor__connection-tooltip-kind span{width:8px;height:8px;border-radius:999px;background:#19c8e8;box-shadow:0 0 12px #19c8e880}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-kind span{background:#ff9f2f;box-shadow:0 0 16px #ff9f2fc7}.connection-editor__connection-tooltip-row{min-width:0;display:grid;grid-template-columns:54px minmax(0,1fr);align-items:center;gap:8px;min-height:28px;padding:0 10px;border-radius:999px;background:#0f172a94}.connection-editor__connection-tooltip-row span{color:#8d9bb0;font-size:.62rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.connection-editor__connection-tooltip-row strong{min-width:0;overflow:hidden;color:#e2e8f0;font-size:.75rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__connection-tooltip-badges{display:flex;flex-wrap:wrap;gap:6px}.connection-editor__connection-tooltip-badges span{padding:3px 8px;border-radius:999px;background:#02061799;color:#fbbf24;font-size:.66rem;font-weight:900}.connection-editor__trace-overlay{position:absolute;z-index:6;inset-block-start:16px;inset-inline-start:50%;width:min(560px,calc(100% - 360px));min-width:320px;padding:12px 14px;border-radius:16px;border:1px solid rgba(34,197,94,.26);background:linear-gradient(135deg,#22c55e1f,#0ea5e91a),#080d18f0;box-shadow:0 22px 56px #02061775,0 0 42px #22c55e1f;transform:translate(-50%);pointer-events:auto;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);animation:tooltip-rise .16s ease-out}.connection-editor__trace-overlay-head,.connection-editor__trace-overlay-route{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0}.connection-editor__trace-overlay-head span,.connection-editor__trace-overlay-route span{color:#86efac;font-size:.66rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase;white-space:nowrap}.connection-editor__trace-overlay-head strong{color:#cbd5e1;font-size:.7rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace}.connection-editor__trace-overlay-route{margin-top:6px}.connection-editor__trace-overlay-controls{display:grid;grid-template-columns:28px minmax(0,1fr) 28px;align-items:center;gap:8px;margin-top:10px}.connection-editor__trace-overlay-button{width:28px;height:24px;display:inline-grid;place-items:center;border:1px solid rgba(148,163,184,.18);border-radius:999px;background:#0206176b;color:#e2e8f0;cursor:pointer;font-size:1rem;font-weight:900;line-height:1;transition:border-color .15s ease,background .15s ease,color .15s ease,opacity .15s ease}.connection-editor__trace-overlay-button:hover:not(:disabled){border-color:#22d3ee6b;background:#0ea5e929;color:#f8fafc}.connection-editor__trace-overlay-button:disabled{cursor:default;opacity:.36}.connection-editor__trace-overlay-steps{min-width:0;display:flex;align-items:center;gap:6px;overflow-x:auto;padding:2px;scrollbar-width:none}.connection-editor__trace-overlay-steps::-webkit-scrollbar{display:none}.connection-editor__trace-overlay-step{position:relative;flex:1 0 28px;min-width:28px;height:22px;border:1px solid rgba(148,163,184,.16);border-radius:999px;background:#0f172a9e;color:#94a3b8;cursor:pointer;font-size:.62rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease}.connection-editor__trace-overlay-step--active{border-color:#22d3eea3;background:linear-gradient(90deg,#22d3ee3d,#9b5cff33);color:#f8fafc;box-shadow:0 0 18px #22d3ee33}.connection-editor__trace-overlay-step--blocked{border-color:#fb71855c;color:#fecdd3}.connection-editor__trace-overlay-route strong{min-width:0;overflow:hidden;color:#f8fafc;font-size:.82rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__trace-overlay-bar{position:relative;height:5px;margin-top:10px;overflow:hidden;border-radius:999px;background:#94a3b829}.connection-editor__trace-overlay-bar span{position:absolute;inset-block:0;inset-inline-start:0;border-radius:inherit;background:linear-gradient(90deg,#22c55e,#22d3ee);box-shadow:0 0 18px #22c55e61;min-width:8%}.connection-editor__drag-wire{fill:none;stroke:#94a3b8;stroke-width:2.4;stroke-linecap:round;stroke-dasharray:6 7;pointer-events:none;filter:url(#connection-glow);animation:connection-flow 1.05s linear infinite,drag-wire-pulse 1.2s ease-in-out infinite}.connection-editor__drag-wire--halo{stroke:#94a3b829;stroke-width:14;stroke-dasharray:none;animation:drag-halo-pulse 1.2s ease-in-out infinite}.connection-editor__drag-wire--valid{stroke:#22c55e}.connection-editor__drag-wire--halo.connection-editor__drag-wire--valid{stroke:#22c55e47}.connection-editor__drag-endpoint{fill:#94a3b8;stroke:#020617eb;stroke-width:2;pointer-events:none;filter:url(#connection-glow);transform-box:fill-box;transform-origin:center;animation:endpoint-pulse 1s ease-in-out infinite}.connection-editor__drag-endpoint--source{fill:#ff9f2f}.connection-editor__drag-endpoint--target{fill:#7dddeb}.connection-editor__drag-endpoint--valid{fill:#22c55e;stroke:#bbf7d0e0}.connection-editor__wire-handle-line{stroke-width:2;stroke-linecap:round;stroke-dasharray:4 5;pointer-events:none;opacity:.88;filter:url(#connection-glow)}.connection-editor__wire-handle-line--source{stroke:#ff9f2ff0}.connection-editor__wire-handle-line--target{stroke:#19c8e8c2}.connection-editor__wire-control{fill:#0f172af5;stroke:#cbd5e1c7;stroke-width:1.8;pointer-events:none;filter:url(#connection-glow)}.connection-editor__wire-label{pointer-events:none;filter:drop-shadow(0 0 10px rgba(2,6,23,.44));animation:tooltip-rise .14s ease-out}.connection-editor__wire-label rect{fill:#080d18eb;stroke-width:1}.connection-editor__wire-label text{fill:#e2e8f0;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:8px;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.connection-editor__wire-label--source rect{stroke:#ff9f2fad}.connection-editor__wire-label--target rect{stroke:#19c8e86b}.connection-editor__zoom-controls{position:absolute;inset-inline-start:14px;inset-block-start:14px;z-index:4;display:grid;grid-template-columns:max-content;gap:6px;width:46px;pointer-events:auto}.connection-editor__tool-group,.connection-editor__trace-source{display:grid;gap:6px;padding:6px;border-radius:12px;border:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-surface-strong),var(--pce-surface));box-shadow:0 16px 36px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.connection-editor__tool-group{grid-template-columns:30px;width:max-content}.connection-editor__tool-group:nth-of-type(2){grid-template-columns:30px}.connection-editor__arrange-button{width:34px;min-height:34px;display:inline-flex;align-items:center;justify-content:center;gap:0;padding:0;border-radius:999px;border:1px solid rgba(168,85,247,.34);background:linear-gradient(90deg,#a855f72e,#22d3ee1f);color:#e9d5ff;font-size:0;font-weight:800;letter-spacing:0;cursor:pointer;opacity:1;transform:scale(1);transition:opacity .14s ease,transform .14s ease,border-color .14s ease,background .14s ease}.connection-editor--guided .connection-editor__arrange-button{opacity:.34;transform:scale(.9);border-color:color-mix(in srgb,var(--pce-outline) 82%,transparent);background:color-mix(in srgb,var(--pce-surface-soft) 84%,transparent)}.connection-editor--guided .connection-editor__zoom-controls:hover .connection-editor__arrange-button,.connection-editor--guided .connection-editor__zoom-controls:focus-within .connection-editor__arrange-button,.connection-editor--guided .connection-editor__arrange-button:hover,.connection-editor--guided .connection-editor__arrange-button:focus-visible,.connection-editor--guided .connection-editor__arrange-button--active{opacity:1;transform:scale(1);border-color:#a855f757;background:linear-gradient(90deg,#a855f72e,#22d3ee1f)}.connection-editor__arrange-button span{color:#c084fc;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.78rem;font-weight:900}.connection-editor__tool-button{width:30px;height:30px;border-radius:999px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-fg);font-size:.9rem;font-weight:800;letter-spacing:.02em;cursor:pointer;line-height:1}.connection-editor__tool-button:hover:not(:disabled){border-color:color-mix(in srgb,var(--pce-primary) 42%,transparent);color:var(--pce-primary);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 16%,transparent)}.connection-editor__history-button:disabled{cursor:not-allowed;opacity:.42}.connection-editor__arrange-button--active{border-color:#22d3ee94;background:linear-gradient(90deg,#0ea5e93d,#22d3ee2e);color:#cffafe}.connection-editor__trace-source{gap:4px;max-height:230px;overflow:auto}.connection-editor__trace-source-title{padding:2px 4px 6px;color:var(--pce-muted);font-size:.64rem;font-weight:900;letter-spacing:.18em;text-transform:uppercase}.connection-editor__trace-source-item{min-width:0;min-height:30px;display:flex;align-items:center;gap:8px;padding:0 8px;border:0;border-radius:8px;background:transparent;color:var(--pce-fg);cursor:pointer;font-size:.74rem;font-weight:800;text-align:start}.connection-editor__trace-source-item span{color:#f59e0b;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.72rem;font-weight:900}.connection-editor__trace-source-item:hover,.connection-editor__trace-source-item--active{background:var(--pce-surface-strong);color:var(--pce-strong);box-shadow:inset 3px 0 #f59e0bd1}.connection-editor__minimap{position:absolute;inset-inline-end:14px;inset-block-end:14px;z-index:4;width:184px;height:124px;padding:18px 10px 10px;overflow:hidden;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-primary) 30%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 12%,transparent),var(--pce-surface-strong)),var(--pce-surface);box-shadow:0 20px 48px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);cursor:pointer;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);opacity:1;transform:scale(1);transform-origin:100% 100%;transition:opacity .16s ease,transform .16s ease,border-color .16s ease,box-shadow .16s ease}.connection-editor--guided .connection-editor__minimap{opacity:.54;transform:scale(.88);border-color:color-mix(in srgb,var(--pce-outline) 64%,transparent);box-shadow:0 14px 34px color-mix(in srgb,var(--pce-shadow-soft) 70%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor--guided .connection-editor__minimap:hover,.connection-editor--guided .connection-editor__minimap:focus-visible,.connection-editor--guided .connection-editor__minimap--panning{opacity:1;transform:scale(1)}.connection-editor__minimap:hover,.connection-editor__minimap--panning{border-color:#22d3ee75;box-shadow:0 20px 48px var(--pce-shadow-soft),0 0 24px color-mix(in srgb,var(--pce-primary) 14%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor__minimap--panning{cursor:grabbing}.connection-editor__minimap:before{content:\"\";position:absolute;inset:22px 10px 10px;border-radius:10px;background-image:radial-gradient(circle,rgba(125,211,252,.18) 1px,transparent 1px);background-size:22px 22px;opacity:.58;pointer-events:none}.connection-editor__minimap-title{position:absolute;inset-block-start:7px;inset-inline-start:12px;color:var(--pce-muted);font-size:.6rem;font-weight:900;letter-spacing:.16em;text-transform:uppercase}.connection-editor__minimap svg{position:relative;width:100%;height:100%;display:block;overflow:visible}.connection-editor__minimap-wire{fill:none;stroke:#b46cff8f;stroke-width:4;stroke-linecap:round;opacity:.86}.connection-editor__minimap-wire--state{stroke:#ff9f2fdb}.connection-editor__minimap-wire--state-read{stroke:#8b7dffb8;stroke-dasharray:4 9}.connection-editor__minimap-wire--projection{stroke:#19c8e88a;stroke-dasharray:18 8}.connection-editor__minimap-wire--event-propagation{stroke:#ffb454cc;stroke-dasharray:6 7}.connection-editor__minimap-wire--policy{filter:drop-shadow(0 0 6px rgba(34,197,94,.28))}.connection-editor__minimap-wire--trace{stroke:#9b5cffe0;stroke-width:5;stroke-dasharray:12 9;animation:connection-flow 1s linear infinite}.connection-editor__minimap-wire--active{stroke:#facc15eb;stroke-width:6;filter:drop-shadow(0 0 8px rgba(250,204,21,.38))}.connection-editor__minimap-wire--mode-dimmed{opacity:.2;stroke-width:2.6;filter:none}.connection-editor__minimap-node{fill:#f59e0be0;stroke:#020617db;stroke-width:4;filter:drop-shadow(0 0 7px rgba(245,158,11,.24))}.connection-editor__minimap-node--state{fill:#a855f7e6;filter:drop-shadow(0 0 8px rgba(168,85,247,.34))}.connection-editor__minimap-node--selected{fill:#facc15f5;stroke:#fef08aeb;filter:drop-shadow(0 0 10px rgba(250,204,21,.42))}.connection-editor__minimap-node--trace-source{fill:#facc15f5;filter:drop-shadow(0 0 10px rgba(250,204,21,.44))}.connection-editor__minimap-node--trace-reacting{fill:#22c55eeb;filter:drop-shadow(0 0 10px rgba(34,197,94,.42))}.connection-editor__minimap-node--mode-dimmed{opacity:.28;filter:none}.connection-editor__minimap-viewport{fill:#7dd3fc1f;stroke:#7dd3fce6;stroke-width:5;stroke-dasharray:18 10;filter:drop-shadow(0 0 10px rgba(125,211,252,.3));transition:x .12s ease,y .12s ease,width .12s ease,height .12s ease}.connection-editor__minimap--panning .connection-editor__minimap-viewport{fill:#22d3ee2e;stroke:#22d3eef5;stroke-dasharray:none}.connection-editor__node{position:absolute;z-index:10;width:168px;height:168px;transform:translate(-84px,-84px);border:0;padding:0;background:transparent;color:inherit;cursor:pointer;transition:filter .16s ease}.connection-editor__node--dragging{cursor:grabbing;filter:drop-shadow(0 0 22px rgba(34,211,238,.26));z-index:12}.connection-editor__node--connection-focus{filter:drop-shadow(0 0 16px rgba(34,211,238,.2))}.connection-editor__node--compat-target .connection-editor__node-ring{box-shadow:0 0 0 2px #22c55e66,0 0 0 18px #22c55e0b,0 0 32px #22c55e29}.connection-editor__node--mode-dimmed{opacity:.42;filter:grayscale(.22) saturate(.56)}.connection-editor__node--quiet{opacity:.58;filter:saturate(.68) brightness(.86)}.connection-editor__node--mode-dimmed .connection-editor__node-ring{animation:none;box-shadow:0 0 0 1px #2dd4bf14,0 0 14px #0ea5e90f}.connection-editor__node--mode-dimmed .connection-editor__node-halo{stroke-opacity:.28}.connection-editor__node-ring,.connection-editor__node-core,.connection-editor__node-svg{position:absolute;inset:0}.connection-editor__node-ring,.connection-editor__node-core{display:grid;place-items:center;clip-path:polygon(30% 0,70% 0,100% 30%,100% 70%,70% 100%,30% 100%,0 70%,0 30%)}.connection-editor__node-ring{background:radial-gradient(circle at center,transparent 0 44%,rgba(7,17,31,.96) 45% 57%,transparent 58%),conic-gradient(from -28deg,#06b6d4b3 0 28%,#f59e0b9e 28% 48%,#a855f780 48% 68%,#06b6d4a3 68% 100%);box-shadow:0 0 0 1px #2dd4bf2e,0 0 26px #0ea5e929;animation:node-breathe 2.8s ease-in-out infinite}.connection-editor__node-svg{overflow:visible;filter:drop-shadow(0 0 16px rgba(14,165,233,.16))}.connection-editor__node-halo{fill:transparent;stroke:#38bdf838;stroke-width:1;stroke-dasharray:0;pointer-events:none}.connection-editor__node-halo:nth-child(2){stroke:#38bdf824}.connection-editor__node-halo:nth-child(3){stroke:#94a3b81a}.connection-editor__node-segment{fill-opacity:.28;stroke-opacity:.72;stroke-width:1.15;transition:fill-opacity .16s ease,stroke-opacity .16s ease,stroke-width .16s ease;cursor:grab}.connection-editor__node:hover .connection-editor__node-segment,.connection-editor__node--selected .connection-editor__node-segment,.connection-editor__node--connection-focus .connection-editor__node-segment{fill-opacity:.54;stroke-opacity:.95;stroke-width:1.55}.connection-editor__node-segment--connection-focus{fill-opacity:.76;stroke-opacity:1;stroke-width:2;filter:drop-shadow(0 0 8px rgba(34,211,238,.76));animation:segment-focus-sweep 1.45s ease-in-out infinite}.connection-editor__node-segment--source{fill-opacity:.86;stroke-opacity:1;stroke-width:2.45;filter:drop-shadow(0 0 12px rgba(251,146,60,.82))}.connection-editor__node-segment--compatible{fill-opacity:.74;stroke-opacity:1;stroke-width:2;filter:drop-shadow(0 0 10px rgba(34,197,94,.72));animation:snap-pulse 1s ease-in-out infinite}.connection-editor__node-segment--compat-preview{fill-opacity:.86;stroke-opacity:1;stroke-width:2.45;filter:drop-shadow(0 0 13px rgba(245,158,11,.78))}.connection-editor__node-segment--candidate{fill-opacity:.92;stroke-opacity:1;stroke-width:2.8;filter:drop-shadow(0 0 16px rgba(34,197,94,.86))}.connection-editor__node-segment--incompatible{fill-opacity:.12;stroke-opacity:.28;cursor:not-allowed}.connection-editor__node-segment--invalid-drop{fill-opacity:.72;stroke:#fb7185;stroke-opacity:1;stroke-width:3;filter:drop-shadow(0 0 16px rgba(244,63,94,.84));animation:invalid-drop-jolt .36s ease-out}.connection-editor__node-segment--state{fill-opacity:.44;filter:drop-shadow(0 0 8px rgba(180,108,255,.2))}.connection-editor__node-segment--mode-dimmed{fill-opacity:.08;stroke-opacity:.2;stroke-width:.9;filter:none}.connection-editor__node-anchor{opacity:.95;filter:drop-shadow(0 0 6px currentColor)}.connection-editor__node-rotate-handle{cursor:grab;opacity:0;pointer-events:none;transition:opacity .16s ease,filter .16s ease;filter:drop-shadow(0 0 8px rgba(34,211,238,.28))}.connection-editor__node:hover .connection-editor__node-rotate-handle,.connection-editor__node--selected .connection-editor__node-rotate-handle,.connection-editor__node--rotating .connection-editor__node-rotate-handle{opacity:1;pointer-events:auto}.connection-editor__node--rotating .connection-editor__node-rotate-handle{cursor:grabbing;filter:drop-shadow(0 0 12px rgba(34,211,238,.54))}.connection-editor__node-rotate-hit{fill:transparent}.connection-editor__node-rotate-dot{fill:var(--pce-surface-strong);stroke:var(--pce-primary);stroke-opacity:.88;stroke-width:1.4}.connection-editor__node-rotate-arc,.connection-editor__node-rotate-arrow{fill:none;stroke:var(--pce-primary);stroke-linecap:round;stroke-linejoin:round;stroke-opacity:.96;stroke-width:1.2;pointer-events:none}.connection-editor__segment-icon,.connection-editor__segment-label{pointer-events:none;fill:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-weight:800;paint-order:stroke;stroke:#020617c2;stroke-width:3px;stroke-linejoin:round}.connection-editor__segment-icon{font-size:12px;fill:#19c8e8;opacity:1;transition:opacity .14s ease}.connection-editor__segment-icon--output{fill:#ff9f2f}.connection-editor--guided .connection-editor__segment-icon:not(.connection-editor__segment-icon--guided-visible){opacity:0}.connection-editor--guided .connection-editor__node--rotating .connection-editor__segment-icon{opacity:0}.connection-editor__segment-label{font-size:9.5px;letter-spacing:0;fill:#e2e8f0e0;opacity:1;transition:opacity .14s ease}.connection-editor--guided .connection-editor__segment-label:not(.connection-editor__segment-label--guided-visible){opacity:0}.connection-editor--guided .connection-editor__node--rotating .connection-editor__segment-label{opacity:0}.connection-editor__node-core{inset:43px;padding:12px;border-radius:999px;clip-path:none;display:flex;flex-direction:column;align-items:center;justify-content:center;background:radial-gradient(circle at center,#111827 0 58%,#060b14);border:1px solid rgba(226,232,240,.1);box-shadow:0 12px 28px #00000073,inset 0 1px #ffffff0d;color:#f8fafc;text-shadow:0 1px 2px rgba(0,0,0,.62)}.connection-editor__node-icon{display:block;max-width:44px;overflow:hidden;color:#f8fafc;font-family:Material Symbols Outlined,Material Icons,sans-serif;font-size:1.22rem;font-weight:400;line-height:1;text-align:center;text-transform:none;letter-spacing:0;white-space:nowrap;filter:drop-shadow(0 0 10px rgba(226,232,240,.18))}.connection-editor__node--state .connection-editor__node-ring{background:radial-gradient(circle at center,transparent 0 44%,rgba(7,17,31,.96) 45% 57%,transparent 58%),conic-gradient(from 14deg,#a855f7c7 0 42%,#7e3af26b 42% 78%,#f59e0b47 78% 100%);box-shadow:0 0 0 1px #a855f738,0 0 30px #a855f733}.connection-editor__node--selected .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15bd,0 0 34px #facc153d}.connection-editor__node--connection-focus .connection-editor__node-ring{box-shadow:0 0 0 2px #22d3eead,0 0 38px #22d3ee42;animation:node-focus-breathe 1.7s ease-in-out infinite}.connection-editor__node--trace-source .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15c7,0 0 0 18px #facc150f,0 0 54px #facc154d;animation:trace-source-pulse 1.38s ease-in-out infinite}.connection-editor__node--trace-source:before{content:\"\";position:absolute;z-index:0;inset:-44px;border-radius:999px;border:2px solid rgba(250,204,21,.36);background:radial-gradient(circle at center,transparent 0 64%,rgba(250,204,21,.05) 65% 66%,transparent 67%),conic-gradient(from -20deg,#facc1500,#facc1552,#facc1500);box-shadow:0 0 36px #facc151f;opacity:.92;pointer-events:none;animation:trace-source-orbit 2.8s linear infinite}.connection-editor__node--trace-reacting .connection-editor__node-ring{box-shadow:0 0 0 2px #22c55e94,0 0 46px #22c55e38;animation:trace-reacting-pulse 1.38s ease-in-out infinite}.connection-editor__node--trace-source:after,.connection-editor__node--trace-reacting:after{position:absolute;z-index:3;inset-inline-start:50%;inset-block-start:-42px;padding:0;border:0;background:transparent;color:#facc15;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.68rem;font-weight:900;letter-spacing:.18em;text-transform:uppercase;transform:translate(-50%);text-shadow:0 0 12px rgba(250,204,21,.52);pointer-events:none}.connection-editor__node--trace-source:after{content:attr(data-trace-label)}.connection-editor__node--trace-reacting:after{content:attr(data-trace-label);color:#86efac;text-shadow:0 0 12px rgba(34,197,94,.44)}.connection-editor__node--trace-source .connection-editor__node-segment--output{fill-opacity:.66;stroke-width:2.2;filter:drop-shadow(0 0 14px rgba(255,159,47,.42))}.connection-editor__node--expanded .connection-editor__node-ring{box-shadow:0 0 0 2px #22d3eebd,0 0 34px #22d3ee3d}.connection-editor__node--rotating .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15c7,0 0 42px #facc1547;animation:node-focus-breathe 1.1s ease-in-out infinite}.connection-editor__node--rotating .connection-editor__node-segment{fill-opacity:.68;stroke-opacity:1;stroke-width:1.9;cursor:grabbing}.connection-editor__node-title,.connection-editor__node-subtitle{display:block;max-width:92px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center}.connection-editor__node-title{color:#f8fafc;font-size:.82rem;font-weight:800}.connection-editor__node-subtitle{margin-top:4px;color:#a7b4c8;font-size:.62rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__ports{position:absolute;z-index:30;display:grid;gap:6px;width:104px;opacity:0;pointer-events:none;transform:translateY(3px);transition:opacity .16s ease,transform .16s ease}.connection-editor__ports--visible{opacity:1;pointer-events:auto;transform:translateY(0)}.connection-editor__ports--outputs{z-index:42}.connection-editor__ports--inputs{z-index:36}.connection-editor__segment-popover{position:absolute;z-index:80;width:292px;max-height:320px;overflow:auto;display:grid;gap:10px;padding:14px;border-radius:16px;border:1px solid rgba(148,163,184,.18);background:linear-gradient(135deg,#0f172af5,#110c20f0),#090c16eb;box-shadow:0 28px 72px #0000007a,0 0 48px #a855f71a,inset 0 1px #ffffff0d;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__segment-popover-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding-bottom:2px}.connection-editor__segment-popover-title{color:#94a3b8;font-size:.68rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase}.connection-editor__segment-popover-hint{margin-top:5px;color:#64748b;font-size:.66rem;font-weight:700}.connection-editor__segment-rotation{flex:0 0 auto;min-width:54px;padding:5px 8px;border-radius:999px;border:1px solid rgba(34,211,238,.28);background:#082f4957;color:#7dd3fc;font-size:.66rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-align:center;box-shadow:inset 0 0 16px #0ea5e914}.connection-editor__segment-row{display:grid;grid-template-columns:28px 72px minmax(0,1fr) auto;align-items:center;gap:8px;min-height:40px;padding:7px;border-radius:12px;border:1px solid rgba(148,163,184,.12);background:linear-gradient(90deg,#0f172ac7,#0f172a75),#0f172a99;transition:border-color .15s ease,background .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__segment-row--input{border-color:#22d3ee42}.connection-editor__segment-row--output{border-color:#f59e0b47}.connection-editor__segment-row--state{border-color:#a855f74d}.connection-editor__segment-row--source,.connection-editor__segment-row--candidate{transform:translate(2px);box-shadow:0 0 22px #f59e0b24,inset 0 0 20px #f59e0b14}.connection-editor__segment-row--compatible{border-color:#22d3ee6b;box-shadow:inset 0 0 18px #22d3ee14}.connection-editor__segment-row--compat-preview{border-color:#f59e0b94;background:linear-gradient(90deg,#f59e0b1f,#0f172a8a),#0f172ab3;box-shadow:0 0 22px #f59e0b1f,inset 0 0 18px #f59e0b14}.connection-editor__segment-row--invalid-drop{border-color:#fb7185b3;background:linear-gradient(90deg,#f43f5e24,#0f172a94),#0f172ab3;box-shadow:0 0 24px #f43f5e2e,inset 0 0 18px #f43f5e1a;animation:invalid-drop-jolt .36s ease-out}.connection-editor__segment-index{color:#64748b;font-size:.64rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;letter-spacing:.08em}.connection-editor__segment-kind{display:inline-flex;align-items:center;gap:6px;color:#94a3b8;font-size:.62rem;font-weight:800;text-transform:uppercase;min-width:0;white-space:nowrap}.connection-editor__segment-kind-dot{width:7px;height:7px;border-radius:999px;background:#94a3b8;box-shadow:0 0 12px #94a3b847}.connection-editor__segment-row--input .connection-editor__segment-kind-dot{background:#22d3ee;box-shadow:0 0 12px #22d3ee61}.connection-editor__segment-row--output .connection-editor__segment-kind-dot{background:#fb923c;box-shadow:0 0 12px #fb923c66}.connection-editor__segment-row--state .connection-editor__segment-kind-dot{background:#a855f7;box-shadow:0 0 12px #a855f76b}.connection-editor__segment-label{display:inline-flex;align-items:center;gap:5px;min-width:0;overflow:hidden;color:#e2e8f0;font-size:.74rem;font-weight:700;text-overflow:ellipsis;white-space:nowrap}.connection-editor__segment-badge{flex:0 0 auto;padding:2px 5px;border-radius:999px;border:1px solid rgba(56,189,248,.28);background:#0ea5e91a;color:#bae6fd;font-size:.55rem;font-weight:900;text-transform:uppercase}.connection-editor__segment-path{grid-column:3 / 4;min-width:0;margin-top:-5px;overflow:hidden;color:#93c5fd;font-size:.62rem;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-overflow:ellipsis;white-space:nowrap}.connection-editor__segment-row--nested{min-height:56px;border-color:#38bdf84d;background:linear-gradient(90deg,#0ea5e91a,#0f172a80),#0f172aa3}.connection-editor__segment-actions{display:inline-flex;gap:4px}.connection-editor__segment-action{width:24px;height:24px;border-radius:999px;border:1px solid rgba(148,163,184,.18);background:#060c16b8;color:#dbeafe;cursor:pointer;font-weight:900;line-height:1}.connection-editor__segment-action:hover{border-color:#22d3ee75;box-shadow:0 0 14px #22d3ee29}.connection-editor__port{min-height:24px;border-radius:999px;border:1px solid rgba(148,163,184,.14);background:#080d18c2;color:#cbd5e1;font-size:.64rem;font-weight:700;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:crosshair;box-shadow:0 8px 20px #00000038}.connection-editor--guided .connection-editor__ports--visible .connection-editor__port:not(.connection-editor__port--guided-visible){display:none}.connection-editor__port--output{border-color:#f59e0b75;color:#fbbf24}.connection-editor__port--input{border-color:#06b6d475;color:#22d3ee}.connection-editor__port--state{border-color:#a855f785;color:#c084fc}.connection-editor__port--compatible{background:#22c55e29;box-shadow:0 0 0 1px #22c55e61,0 0 18px #22c55e2e;animation:snap-pulse 1s ease-in-out infinite}.connection-editor__port--source{border-color:#fb923cb8;background:#fb923c29;color:#fed7aa;box-shadow:0 0 0 1px #fb923c73,0 0 20px #fb923c2e}.connection-editor__port--candidate{background:#22c55e3d;box-shadow:0 0 0 2px #22c55e94,0 0 24px #22c55e3d}.connection-editor__port--incompatible{opacity:.42;cursor:not-allowed;filter:grayscale(.45)}.connection-editor__port--invalid-drop{opacity:1;border-color:#fb7185c7;color:#fecdd3;background:#f43f5e2e;box-shadow:0 0 0 2px #f43f5e75,0 0 24px #f43f5e38;filter:none;animation:invalid-drop-jolt .36s ease-out}.connection-editor__port--connection-focus{border-color:#facc15ad;color:#fde68a;background:#facc151f;box-shadow:0 0 0 1px #facc156b,0 0 20px #facc152e;animation:port-focus-pulse 1.25s ease-in-out infinite}.connection-editor__drag-tip{position:absolute;z-index:6;display:grid;gap:4px;width:min(330px,calc(100% - 28px));padding:10px 12px;border-radius:14px;border:1px solid rgba(125,211,252,.26);background:linear-gradient(135deg,#0ea5e91f,#0f172aeb),#080d18f0;box-shadow:0 18px 44px #0206176b,0 0 28px #0ea5e91a;font-size:.76rem;pointer-events:none;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);animation:tooltip-rise .14s ease-out}.connection-editor__drag-tip--valid{border-color:#22c55e66;background:linear-gradient(135deg,#22c55e29,#0f172aeb),#080d18f0;box-shadow:0 18px 44px #0206176b,0 0 30px #22c55e24}.connection-editor__drag-tip--invalid{z-index:7;border-color:#fb71858a;background:linear-gradient(135deg,#f43f5e2e,#0f172af0),#080d18f5;box-shadow:0 18px 44px #0206176b,0 0 34px #f43f5e29;animation:tooltip-rise .14s ease-out,invalid-drop-jolt .36s ease-out}.connection-editor__invalid-drop-pulse{position:absolute;z-index:6;width:16px;height:16px;border-radius:999px;border:2px solid rgba(251,113,133,.88);background:#f43f5e38;box-shadow:0 0 0 8px #f43f5e1a,0 0 28px #f43f5e3d;pointer-events:none;transform:translate(-50%,-50%);animation:invalid-drop-ping 1.2s ease-out both}.connection-editor__drag-tip span{color:#7dd3fc;text-transform:uppercase;letter-spacing:.12em;font-size:.62rem;font-weight:800}.connection-editor__drag-tip strong{min-width:0;overflow:hidden;color:#f8fafc;font-size:.78rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__drag-tip small{color:#94a3b8;font-size:.68rem;font-weight:700}.connection-editor__drag-tip--valid small{color:#86efac}.connection-editor__drag-tip--invalid span,.connection-editor__drag-tip--invalid small{color:#fda4af}.connection-editor__summary{grid-column:1;grid-row:2;align-self:end;justify-self:start;z-index:6;display:flex;flex-wrap:wrap;gap:6px;padding:0 0 12px 12px;pointer-events:none}.connection-editor__side-panel{grid-column:2;grid-row:2 / 3;position:relative;z-index:8;min-width:0;min-height:0;display:grid;align-content:start;gap:14px;overflow:auto;padding-inline-end:4px;scrollbar-color:var(--pce-outline-strong) var(--pce-surface-soft)}.connection-editor__side-panel--contextual .connection-editor__inspector{order:-3}.connection-editor__side-panel--contextual .connection-editor__recommended{order:2}.connection-editor__side-panel--contextual .connection-editor__filters-section{order:3}.connection-editor__side-panel--contextual .connection-editor__recommended,.connection-editor__side-panel--contextual .connection-editor__filters-section{opacity:.82}.connection-editor__metric,.connection-editor__item{border-radius:16px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.connection-editor__metric{min-width:auto;display:inline-flex;align-items:baseline;gap:7px;padding:6px 9px;border-radius:999px;background:color-mix(in srgb,var(--pce-surface-strong) 86%,transparent);box-shadow:0 10px 26px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight)}.connection-editor__metric span{color:var(--pce-muted);font-size:.62rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__metric strong,.connection-editor__details-row strong{color:var(--pce-strong);font-size:.78rem}.connection-editor__filters,.connection-editor__actions,.connection-editor__badges{display:flex;gap:8px;flex-wrap:wrap}.connection-editor__filter,.connection-editor__action{border:1px solid var(--pce-outline-strong);background:var(--pce-surface-soft);color:var(--pce-fg);border-radius:999px;padding:6px 10px;font-size:.75rem;font-weight:700;cursor:pointer}.connection-editor__filter--active,.connection-editor__action:hover{border-color:color-mix(in srgb,var(--pce-secondary) 62%,transparent);background:color-mix(in srgb,var(--pce-secondary) 16%,var(--pce-surface-soft));color:var(--pce-strong)}.connection-editor__action--danger{border-color:#f8717161;color:#fecaca}.connection-editor__item{width:100%;text-align:start;padding:12px;display:grid;gap:6px;cursor:pointer;color:inherit}.connection-editor__item--active{border-color:#a855f7b8;background:linear-gradient(90deg,#0ea5e91a,#a855f721)}.connection-editor__item-title{font-weight:800;color:#f8fafc;word-break:break-word}.connection-editor__badge{padding:3px 8px;border-radius:999px;background:#0ea5e92e;color:#bae6fd;font-size:.72rem;font-weight:800}.connection-editor__section{min-width:0;padding:14px;border-radius:18px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight),0 18px 50px var(--pce-shadow-soft);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__badge--warning{background:#facc1524;color:#fde68a}.connection-editor__badge--error{background:#ef444424;color:#fecaca}.connection-editor__diagnostics{display:grid;gap:8px}.connection-editor__diagnostic{display:grid;gap:4px;padding:10px;border-radius:12px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-fg);font-size:.78rem;line-height:1.35}.connection-editor__diagnostic--warning{border-color:#f59e0b47;background:#f59e0b1a}.connection-editor__diagnostic--error{border-color:#ef44445c;background:#ef44441f}.connection-editor__diagnostic-severity{font-size:.68rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase;color:#f8fafc}.connection-editor__suggestions{display:grid;gap:10px;margin-top:14px;padding-top:14px;border-top:1px solid var(--pce-outline)}.connection-editor__suggestion{display:grid;gap:8px;width:100%;padding:12px;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-secondary) 26%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 12%,transparent),color-mix(in srgb,var(--pce-primary) 8%,transparent)),var(--pce-surface-strong);color:var(--pce-fg);text-align:start;cursor:pointer;transition:transform .16s ease,border-color .16s ease,background .16s ease}.connection-editor__suggestion:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 46%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 18%,transparent),color-mix(in srgb,var(--pce-primary) 12%,transparent)),var(--pce-surface-strong)}.connection-editor__suggestion-route{display:grid;gap:4px;font-size:.78rem}.connection-editor__suggestion-route strong{color:var(--pce-strong);font-size:.82rem}.connection-editor__suggestion-reason{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__suggestion-diagnostic{padding:10px 12px;border-radius:14px;border:1px solid rgba(245,158,11,.28);background:#f59e0b1a;color:var(--pce-strong);font-size:.78rem;line-height:1.35}.connection-editor__suggestion-outcome{color:var(--pce-strong);font-size:.84rem;line-height:1.3}.connection-editor__suggestion-action{justify-self:start;padding:3px 8px;border-radius:999px;background:#22d3ee24;color:#67e8f9;font-size:.68rem;font-weight:900;letter-spacing:.06em;text-transform:uppercase}:host-context(.theme-light) .connection-editor__suggestion-action{background:color-mix(in srgb,var(--pce-primary) 12%,var(--pce-surface-soft));color:#075f73}.connection-editor__trace{display:grid;gap:10px;margin-top:14px;padding-top:14px;border-top:1px solid var(--pce-outline)}.connection-editor__trace-summary{color:var(--pce-muted);font-size:.78rem;line-height:1.35}.connection-editor__trace-step{display:grid;grid-template-columns:24px minmax(76px,auto) 1fr;gap:8px;align-items:center;padding:9px;border-radius:12px;border:1px solid rgba(34,211,238,.18);background:var(--pce-surface-soft);font-size:.76rem}.connection-editor__trace-step--blocked{border-color:#ef444457;background:#ef44441a}.connection-editor__trace-order{display:inline-grid;place-items:center;width:22px;height:22px;border-radius:999px;background:#22d3ee29;color:#67e8f9;font-weight:900}.connection-editor__trace-phase{color:var(--pce-strong);font-weight:900;text-transform:uppercase;letter-spacing:.06em;font-size:.66rem}.connection-editor__trace-copy{min-width:0;color:var(--pce-fg);word-break:break-word}.connection-editor__transform-steps{display:grid;gap:8px}.connection-editor__transform-step{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:8px;align-items:center;padding:9px;border-radius:12px;border:1px solid rgba(168,85,247,.22);background:var(--pce-surface-soft)}.connection-editor__transform-kind{min-width:0;color:var(--pce-strong);font-weight:900;word-break:break-word}.connection-editor__transform-phase{color:var(--pce-muted);font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}.connection-editor__details-row,.connection-editor__details-block{display:grid;gap:6px}.connection-editor__payload-input{width:100%;min-height:128px;box-sizing:border-box;resize:vertical;border:1px solid var(--pce-outline);border-radius:8px;background:color-mix(in srgb,var(--pce-surface) 92%,black);color:var(--pce-fg);padding:10px;font:.76rem/1.45 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.connection-editor__surface-action-editor{display:block;max-height:min(58vh,620px);overflow:auto;border:1px solid var(--pce-outline);border-radius:8px;background:color-mix(in srgb,var(--pce-surface) 96%,black);padding:10px;margin-bottom:10px}.connection-editor__rule-grid{display:grid;gap:10px}.connection-editor__rule-card{display:grid;gap:8px;padding:10px;border-radius:12px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft)}.connection-editor__rule-heading{color:var(--pce-fg);font-size:.72rem;font-weight:900;text-transform:uppercase;letter-spacing:.06em}pre{margin:0;padding:12px;border-radius:12px;background:color-mix(in srgb,var(--pce-surface-strong) 82%,#020617);color:var(--pce-fg);overflow:auto;font-size:.75rem}@keyframes connection-flow{to{stroke-dashoffset:-38}}@keyframes drag-wire-pulse{0%,to{stroke-opacity:.76}50%{stroke-opacity:1}}@keyframes drag-halo-pulse{0%,to{stroke-opacity:.48}50%{stroke-opacity:.82}}@keyframes endpoint-pulse{0%,to{opacity:.78;transform:scale(1)}50%{opacity:1;transform:scale(1.25)}}@keyframes selected-wire-aura{0%,to{stroke-opacity:.46}50%{stroke-opacity:.92}}@keyframes wire-draw-in{0%{stroke-dashoffset:640;opacity:.32}70%{opacity:1}to{stroke-dashoffset:0;opacity:1}}@keyframes new-wire-halo{0%{stroke-opacity:0;stroke-width:30}28%{stroke-opacity:.78}to{stroke-opacity:0;stroke-width:12}}@keyframes new-dock-link-flash{0%{transform:translateY(-2px);box-shadow:0 16px 40px #0206175c,0 0 42px #fb923c47}to{transform:translateY(0)}}@keyframes trace-wire-pulse{0%,to{stroke-opacity:.78}50%{stroke-opacity:1}}@keyframes trace-halo-pulse{0%,to{stroke-opacity:.34}50%{stroke-opacity:.86}}@keyframes tooltip-rise{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes invalid-drop-jolt{0%,to{transform:translate(0)}28%{transform:translate(-3px)}56%{transform:translate(3px)}}@keyframes invalid-drop-ping{0%{opacity:1;transform:translate(-50%,-50%) scale(.72)}72%{opacity:.74;transform:translate(-50%,-50%) scale(1.35)}to{opacity:0;transform:translate(-50%,-50%) scale(1.8)}}@keyframes node-breathe{50%{transform:scale(1.035)}}@keyframes node-focus-breathe{0%,to{transform:scale(1)}50%{transform:scale(1.055)}}@keyframes trace-source-pulse{50%{transform:scale(1.04);box-shadow:0 0 0 3px #facc15db,0 0 0 22px #facc1514,0 0 64px #facc1557}}@keyframes trace-source-orbit{to{transform:rotate(360deg)}}@keyframes trace-reacting-pulse{50%{transform:scale(1.045);box-shadow:0 0 0 3px #22c55ead,0 0 62px #22c55e4d}}@keyframes segment-focus-sweep{0%,to{stroke-opacity:.82}50%{stroke-opacity:1}}@keyframes snap-pulse{50%{filter:brightness(1.16)}}@keyframes port-focus-pulse{50%{transform:translateY(-1px);box-shadow:0 0 0 2px #facc158f,0 0 26px #facc153d}}@media(prefers-reduced-motion:reduce){.connection-editor__viewport,.connection-editor__node,.connection-editor__node-ring,.connection-editor__node-core,.connection-editor__node-segment,.connection-editor__dock-link,.connection-editor__port{transition-duration:1ms}.connection-editor__wire,.connection-editor__wire--state,.connection-editor__wire--projection,.connection-editor__wire--state-read,.connection-editor__wire--event-propagation,.connection-editor__wire--halo.connection-editor__wire--active,.connection-editor__wire--trace,.connection-editor__wire--halo.connection-editor__wire--trace,.connection-editor__wire-endpoint,.connection-editor__connection-tooltip,.connection-editor__dock-link--new,.connection-editor__trace-overlay,.connection-editor__trace-overlay-step,.connection-editor__drag-wire,.connection-editor__drag-wire--halo,.connection-editor__drag-endpoint,.connection-editor__drag-tip,.connection-editor__node-ring,.connection-editor__node--connection-focus .connection-editor__node-ring,.connection-editor__node--trace-source .connection-editor__node-ring,.connection-editor__node--trace-source:before,.connection-editor__node--trace-reacting .connection-editor__node-ring,.connection-editor__node--rotating .connection-editor__node-ring,.connection-editor__node-segment--connection-focus,.connection-editor__node-segment--compatible,.connection-editor__node-segment--invalid-drop,.connection-editor__segment-popover,.connection-editor__port--compatible,.connection-editor__port--invalid-drop,.connection-editor__port--connection-focus,.connection-editor__invalid-drop,.connection-editor__invalid-drop-pulse{animation:none}.connection-editor__wire{stroke-dashoffset:0}.connection-editor__wire--new{stroke-dasharray:10 8;stroke-dashoffset:0;opacity:1}.connection-editor__wire--halo.connection-editor__wire--new{stroke-width:12;stroke-opacity:.16}.connection-editor__wire-endpoint,.connection-editor__drag-endpoint,.connection-editor__node-ring,.connection-editor__node--trace-source:before{transform:none}.connection-editor__invalid-drop-pulse{opacity:0}}@media(max-width:900px){:host{inset:0}.connection-editor{inset:8px;height:calc(100vh - 16px);padding:12px;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(260px,1fr) minmax(150px,30vh) minmax(76px,112px)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(260px,1fr) minmax(150px,30vh) 48px}.connection-editor__header,.connection-editor__stage,.connection-editor__summary,.connection-editor__side-panel,.connection-editor__dock{grid-column:1}.connection-editor__header{align-items:stretch;flex-wrap:wrap;border-radius:18px}.connection-editor__mode-frame{order:3;width:100%;max-width:100%}.connection-editor__close{margin-inline-start:auto}.connection-editor__mode-frame:before,.connection-editor__mode-frame:after{content:\"\";position:absolute;z-index:2;inset-block:4px;width:24px;border-radius:999px;pointer-events:none}.connection-editor__mode-frame:before{inset-inline-start:1px;background:linear-gradient(90deg,#080d18f0,#080d1800)}.connection-editor__mode-frame:after{inset-inline-end:1px;background:linear-gradient(270deg,#080d18f0,#080d1800)}.connection-editor__mode-shell{width:100%;max-width:100%;overflow-x:auto;scroll-padding-inline:28px;-webkit-overflow-scrolling:touch}.connection-editor__subtitle{margin-inline-start:auto}.connection-editor__stage{grid-row:2;min-height:260px}.connection-editor__summary{grid-row:2;max-width:calc(100% - 24px)}.connection-editor__side-panel{grid-row:3;max-height:30vh}.connection-editor__dock{grid-row:4;min-height:0}.connection-editor__connection-tooltip{width:min(620px,calc(100% - 24px));max-height:min(260px,calc(100% - 24px));overflow:auto}.connection-editor__dock-tabs{width:100%;min-width:0;min-height:0;flex-wrap:wrap;align-items:flex-start}.connection-editor__dock-tablist{width:100%;flex-wrap:wrap}.connection-editor__dock-count{flex:1 1 100%;margin-inline-start:0}.connection-editor__dock-body{width:100%;min-width:0;max-height:76px;box-sizing:border-box}.connection-editor__dock-body pre{white-space:pre-wrap;overflow-wrap:anywhere}.connection-editor__dock-link{grid-template-columns:1fr;align-items:start}.connection-editor__dock-link-route{flex-wrap:wrap}.connection-editor__dock-link-badges{justify-content:flex-start}.connection-editor__trace-overlay{inset-inline-start:14px;width:min(420px,calc(100% - 28px));min-width:0;transform:none}.connection-editor__drag-tip{width:min(300px,calc(100% - 28px))}.connection-editor__segment-popover{width:min(280px,calc(100% - 28px));max-height:min(280px,calc(100% - 28px))}.connection-editor__minimap{inset-inline-end:10px;inset-block-end:10px;width:132px;height:94px;padding:16px 8px 8px;border-radius:14px}.connection-editor__minimap:before{inset:20px 8px 8px;background-size:18px 18px}.connection-editor__minimap-title{inset-block-start:6px;inset-inline-start:10px;font-size:.54rem}}@media(max-width:640px){.connection-editor{inset:4px;height:calc(100vh - 8px);padding:8px;gap:8px;grid-template-rows:auto minmax(240px,1fr) minmax(140px,28vh) minmax(72px,104px)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(240px,1fr) minmax(140px,28vh) 46px}.connection-editor__header{gap:8px;padding:10px}.connection-editor__mode-shell{padding:4px;gap:2px}.connection-editor__mode{min-height:28px;gap:4px;padding:0 7px;font-size:.68rem}.connection-editor__mode-glyph{font-size:.64rem}.connection-editor__stage{min-height:240px}.connection-editor__zoom-controls{inset-inline-start:8px;inset-block-start:8px;gap:6px;width:42px}.connection-editor__tool-group,.connection-editor__trace-source{gap:6px;padding:8px;border-radius:12px}.connection-editor__tool-group{grid-template-columns:28px}.connection-editor__tool-group:nth-of-type(2){grid-template-columns:28px}.connection-editor__tool-button{width:28px;height:28px}.connection-editor__arrange-button,.connection-editor__simulate-button{width:30px;min-height:30px}.connection-editor__summary{gap:6px;padding-inline-end:8px}.connection-editor__side-panel{max-height:28vh}.connection-editor__dock{border-radius:16px}.connection-editor__dock-tabs,.connection-editor__dock-tablist{gap:4px}.connection-editor__dock-tab{padding:0 9px}.connection-editor__dock-count{font-size:.62rem}.connection-editor__dock-body{max-height:66px;padding:8px}.connection-editor__minimap{width:116px;height:84px}.connection-editor__segment-row{grid-template-columns:24px minmax(54px,.55fr) minmax(0,1fr) auto}}\n"] }]
11623
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{position:fixed;inset:0;z-index:1000;pointer-events:none}.connection-editor{--pce-fg: var(--md-sys-color-on-surface, #dce6f4);--pce-strong: var(--md-sys-color-on-surface, #f8fafc);--pce-muted: var(--md-sys-color-on-surface-variant, #94a3b8);--pce-primary: var(--md-sys-color-primary, #19c8e8);--pce-secondary: var(--md-sys-color-secondary, #b46cff);--pce-tertiary: var(--md-sys-color-tertiary, #ff9f2f);--pce-success: var(--md-sys-color-tertiary, #10b981);--pce-warning: var(--md-sys-color-error, #ff9f2f);--pce-error: var(--md-sys-color-error, #ef4444);--pce-surface-base: color-mix(in srgb, var(--md-sys-color-surface, #07111f) 80%, #020617);--pce-surface: color-mix(in srgb, var(--md-sys-color-surface-container, #08101d) 78%, transparent);--pce-surface-strong: color-mix(in srgb, var(--md-sys-color-surface-container-high, #0f172a) 88%, transparent);--pce-surface-soft: color-mix(in srgb, var(--md-sys-color-surface-container-low, #080d18) 64%, transparent);--pce-outline: color-mix(in srgb, var(--md-sys-color-outline-variant, #899ab7) 46%, transparent);--pce-outline-strong: color-mix(in srgb, var(--md-sys-color-outline, #94a3b8) 58%, transparent);--pce-grid: color-mix(in srgb, var(--md-sys-color-outline-variant, #7d8fa9) 24%, transparent);--pce-glass-highlight: color-mix(in srgb, var(--md-sys-color-on-surface, #ffffff) 8%, transparent);--pce-shadow: color-mix(in srgb, #000 56%, transparent);--pce-shadow-soft: color-mix(in srgb, #000 30%, transparent);--pce-stage-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest, #040a13) 42%, transparent);--pce-dock-bg: color-mix(in srgb, var(--md-sys-color-surface-container-high, #050912) 94%, transparent);position:absolute;inset:12px;height:calc(100vh - 24px);box-sizing:border-box;overflow:hidden;padding:16px;border-radius:18px;color:var(--pce-fg);background:radial-gradient(circle at 72% 20%,color-mix(in srgb,var(--pce-secondary) 16%,transparent),transparent 28%),radial-gradient(circle at 26% 74%,color-mix(in srgb,var(--pce-primary) 10%,transparent),transparent 34%),radial-gradient(circle at 48% 52%,color-mix(in srgb,var(--pce-tertiary) 5%,transparent),transparent 35%),linear-gradient(var(--pce-grid) 1px,transparent 1px),linear-gradient(90deg,var(--pce-grid) 1px,transparent 1px),var(--pce-surface-base);background-size:auto,auto,auto,38px 38px,38px 38px,auto;border:1px solid var(--pce-outline);box-shadow:0 28px 90px var(--pce-shadow);pointer-events:auto;display:grid;grid-template-columns:minmax(0,1fr) minmax(300px,380px);grid-template-rows:auto minmax(0,1fr) minmax(44px,118px);gap:14px}:host-context(.theme-light) .connection-editor{--pce-surface-base: color-mix(in srgb, var(--md-sys-color-surface, #ffffff) 94%, var(--md-sys-color-primary, #2563eb) 6%);--pce-surface: color-mix(in srgb, var(--md-sys-color-surface-container, #f8fafc) 82%, transparent);--pce-surface-strong: color-mix(in srgb, var(--md-sys-color-surface-container-high, #eef2ff) 88%, transparent);--pce-surface-soft: color-mix(in srgb, var(--md-sys-color-surface-container-low, #f8fafc) 70%, transparent);--pce-outline: color-mix(in srgb, var(--md-sys-color-outline-variant, #cbd5e1) 68%, transparent);--pce-outline-strong: color-mix(in srgb, var(--md-sys-color-outline, #64748b) 62%, transparent);--pce-grid: color-mix(in srgb, var(--md-sys-color-outline-variant, #cbd5e1) 34%, transparent);--pce-glass-highlight: color-mix(in srgb, #fff 64%, transparent);--pce-shadow: color-mix(in srgb, var(--md-sys-color-shadow, #64748b) 22%, transparent);--pce-shadow-soft: color-mix(in srgb, var(--md-sys-color-shadow, #64748b) 14%, transparent);--pce-stage-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest, #ffffff) 58%, transparent);--pce-dock-bg: color-mix(in srgb, var(--md-sys-color-surface-container, #f8fafc) 92%, transparent)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(0,1fr) 48px}.connection-editor__header{grid-column:1 / -1;display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:42px;padding:8px 14px;border-radius:999px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__mode-frame{position:relative;min-width:0;flex:1 1 auto;display:flex;justify-content:center}.connection-editor__close{width:32px;height:32px;flex:0 0 auto;display:inline-grid;place-items:center;border:1px solid var(--pce-outline);border-radius:999px;color:var(--pce-text);background:var(--pce-surface-strong);box-shadow:inset 0 1px var(--pce-glass-highlight);cursor:pointer;font:inherit;font-size:1.2rem;line-height:1}.connection-editor__close:hover,.connection-editor__close:focus-visible{border-color:var(--pce-outline-strong);background:color-mix(in srgb,var(--pce-accent) 16%,var(--pce-surface-strong));outline:none}.connection-editor__mode-shell{display:inline-flex;align-items:center;gap:4px;min-width:0;padding:4px;border-radius:999px;border:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-surface-strong),var(--pce-surface));box-shadow:inset 0 1px var(--pce-glass-highlight),0 10px 26px var(--pce-shadow-soft);scrollbar-width:none}.connection-editor__mode-shell::-webkit-scrollbar{display:none}.connection-editor__mode{min-height:30px;display:inline-flex;align-items:center;gap:7px;padding:0 12px;border:1px solid transparent;border-radius:999px;background:transparent;color:var(--pce-muted);cursor:pointer;font-size:.76rem;font-weight:800;white-space:nowrap;transition:background .16s ease,border-color .16s ease,color .16s ease,box-shadow .16s ease}.connection-editor__mode:hover,.connection-editor__mode--active{color:var(--pce-strong);border-color:color-mix(in srgb,var(--pce-primary) 28%,transparent);background:color-mix(in srgb,var(--pce-surface-strong) 82%,transparent);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 12%,transparent)}.connection-editor__mode-glyph{color:var(--pce-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.72rem;font-weight:900}.connection-editor__mode--active .connection-editor__mode-glyph{color:var(--pce-primary)}.connection-editor__mode-dot{width:6px;height:6px;border-radius:999px;background:var(--pce-success);box-shadow:0 0 12px color-mix(in srgb,var(--pce-success) 70%,transparent)}.connection-editor__status-pill{display:inline-flex;align-items:center;gap:8px;min-height:24px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--pce-primary) 36%,transparent);background:var(--pce-surface-soft);color:var(--pce-muted);font-size:.72rem;font-weight:800;white-space:nowrap}.connection-editor__status-pill--button{cursor:pointer;transition:border-color .16s ease,background .16s ease,color .16s ease,box-shadow .16s ease}.connection-editor__status-pill--button:hover,.connection-editor__status-pill--button:focus-visible{border-color:color-mix(in srgb,var(--pce-primary) 62%,transparent);background:color-mix(in srgb,var(--pce-primary) 12%,var(--pce-surface-soft));color:var(--pce-strong);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 14%,transparent);outline:none}.connection-editor__status-pill span{width:6px;height:6px;border-radius:999px;background:var(--pce-primary);box-shadow:0 0 12px color-mix(in srgb,var(--pce-primary) 70%,transparent)}.connection-editor__section{display:grid;gap:10px}.connection-editor__dock{grid-column:1 / -1;grid-row:3;position:relative;z-index:4;min-height:0;display:grid;border-radius:14px;border:1px solid var(--pce-outline);background:radial-gradient(circle at 16% 0%,color-mix(in srgb,var(--pce-primary) 22%,transparent),transparent 34%),radial-gradient(circle at 78% 0%,color-mix(in srgb,var(--pce-secondary) 25%,transparent),transparent 38%),linear-gradient(90deg,var(--pce-dock-bg),var(--pce-surface-strong) 46%,color-mix(in srgb,var(--pce-secondary) 14%,var(--pce-dock-bg))),var(--pce-dock-bg);box-shadow:0 18px 58px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);overflow:hidden;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__dock--collapsed .connection-editor__dock-tabs{border-block-end:0}.connection-editor__dock-tabs{display:flex;align-items:center;gap:10px;min-height:52px;padding:9px 14px;border-block-end:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-glass-highlight),transparent),color-mix(in srgb,var(--pce-surface-soft) 34%,transparent)}.connection-editor__dock-tablist{display:flex;align-items:center;gap:10px;min-width:0}.connection-editor__dock-tab,.connection-editor__dock-toggle{min-height:30px;border-radius:999px;border:1px solid transparent;background:transparent;color:var(--pce-muted);cursor:pointer;font-size:.72rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__dock-tab{display:inline-flex;align-items:center;gap:8px;padding:0 13px;white-space:nowrap}.connection-editor__dock-tab-glyph{color:var(--pce-muted);font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:.8rem;letter-spacing:0;text-transform:none}.connection-editor__dock-tab--active{color:var(--pce-strong);border-color:var(--pce-outline-strong);background:linear-gradient(180deg,color-mix(in srgb,var(--pce-strong) 14%,transparent),color-mix(in srgb,var(--pce-strong) 6%,transparent));box-shadow:0 0 0 1px color-mix(in srgb,var(--pce-shadow) 72%,transparent),0 0 22px color-mix(in srgb,var(--pce-strong) 8%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor__dock-tab--active .connection-editor__dock-tab-glyph{color:var(--pce-strong)}.connection-editor__dock-count{margin-inline-start:auto;color:color-mix(in srgb,var(--pce-muted) 62%,transparent);font-size:.7rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase;white-space:nowrap}.connection-editor__dock-toggle{width:32px;padding:0;border-color:var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-muted);font-size:.88rem;letter-spacing:0}.connection-editor__dock-body{max-height:154px;overflow:auto;padding:12px 12px 14px;border-block-start:1px solid var(--pce-outline);background:linear-gradient(90deg,color-mix(in srgb,var(--pce-primary) 9%,transparent),color-mix(in srgb,var(--pce-secondary) 10%,transparent) 62%,color-mix(in srgb,var(--pce-surface-strong) 18%,transparent)),color-mix(in srgb,var(--pce-surface-soft) 30%,transparent)}.connection-editor__dock-body pre{max-height:60px}.connection-editor__dock-links{display:grid;gap:8px}.connection-editor__dock-link-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:10px;width:100%;padding:7px 10px;border-radius:10px;border:1px solid var(--pce-outline);background:linear-gradient(90deg,var(--pce-surface-soft),color-mix(in srgb,var(--pce-secondary) 10%,var(--pce-surface-soft)) 72%,var(--pce-surface-soft)),var(--pce-surface-soft);color:var(--pce-fg);transition:border-color .15s ease,background .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__dock-link{display:grid;grid-template-columns:minmax(116px,.52fr) minmax(300px,1.72fr) minmax(160px,.9fr) auto;align-items:center;gap:12px;width:100%;min-height:42px;padding:0;border:0;border-radius:8px;background:transparent;color:inherit;cursor:pointer;text-align:start}.connection-editor__dock-link-row:hover,.connection-editor__dock-link--active{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 42%,transparent);background:linear-gradient(90deg,color-mix(in srgb,var(--pce-primary) 13%,transparent),color-mix(in srgb,var(--pce-secondary) 16%,transparent),var(--pce-surface-strong)),var(--pce-surface-strong);box-shadow:0 12px 32px var(--pce-shadow-soft),inset 0 0 18px color-mix(in srgb,var(--pce-primary) 6%,transparent)}.connection-editor__dock-link--active{border-color:#facc1575;box-shadow:0 12px 32px var(--pce-shadow-soft),0 0 24px #facc151f}.connection-editor__dock-link--new{border-color:#fb923ca3;background:linear-gradient(90deg,#fb923c2e,#a855f729,#0ea5e91f),#0f172ac2;box-shadow:0 12px 34px #02061752,0 0 30px #fb923c2e;animation:new-dock-link-flash 1.35s ease-out forwards}.connection-editor__dock-link--error{border-color:#ef444461}.connection-editor__dock-jump{min-width:62px;min-height:30px;padding:0 10px;border-radius:999px;border:1px solid color-mix(in srgb,var(--pce-primary) 28%,transparent);background:color-mix(in srgb,var(--pce-surface-soft) 54%,transparent);color:var(--pce-primary);cursor:pointer;font-size:.66rem;font-weight:900;letter-spacing:.06em;text-transform:uppercase;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease}.connection-editor__dock-jump:hover,.connection-editor__dock-jump:focus-visible{border-color:color-mix(in srgb,var(--pce-primary) 56%,transparent);background:color-mix(in srgb,var(--pce-primary) 16%,transparent);color:var(--pce-strong);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 18%,transparent);outline:none}.connection-editor__dock-link-kind{display:inline-flex;align-items:center;gap:7px;min-width:0;color:var(--pce-muted);font-size:.66rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase;white-space:nowrap}.connection-editor__dock-link-kind span{width:8px;height:8px;border-radius:999px;background:#b46cff;box-shadow:0 0 14px #b46cff7a}.connection-editor__dock-link--state .connection-editor__dock-link-kind span{background:#ff9f2f;box-shadow:0 0 16px #ff9f2f94}.connection-editor__dock-link--state-read .connection-editor__dock-link-kind span{background:#8b7dff;box-shadow:0 0 13px #8b7dff75}.connection-editor__dock-link--projection .connection-editor__dock-link-kind span{background:#19c8e8;box-shadow:0 0 10px #19c8e857}.connection-editor__dock-link--event-propagation .connection-editor__dock-link-kind span{background:#ffb454;box-shadow:0 0 15px #ffb45485}.connection-editor__dock-link--policy .connection-editor__dock-link-kind span{outline:1px solid rgba(34,197,94,.76);outline-offset:2px;box-shadow:0 0 16px #22c55e61}.connection-editor__dock-link--policy:not(.connection-editor__dock-link--state,.connection-editor__dock-link--state-read,.connection-editor__dock-link--projection,.connection-editor__dock-link--event-propagation) .connection-editor__dock-link-kind span{background:#22c55e}.connection-editor__dock-link-route{display:inline-flex;align-items:center;gap:8px;min-width:0}.connection-editor__dock-link-chip{min-width:0;overflow:hidden;padding:4px 8px;border-radius:999px;border:1px solid var(--pce-outline);background:color-mix(in srgb,var(--pce-surface-soft) 72%,transparent);color:var(--pce-fg);font-size:.72rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__dock-link-chip--from{border-color:#ff9f2f66;color:#ffbd62}.connection-editor__dock-link-chip--to{border-color:#19c8e83d;color:#7dddeb}:host-context(.theme-light) .connection-editor__dock-link-chip--from{border-color:color-mix(in srgb,var(--pce-tertiary) 46%,transparent);color:#8a4600}:host-context(.theme-light) .connection-editor__dock-link-chip--to{border-color:color-mix(in srgb,var(--pce-primary) 34%,transparent);color:#075f73}.connection-editor__dock-link-arrow{flex:0 0 auto;color:var(--pce-muted);font-weight:900}.connection-editor__dock-link-id{min-width:0;overflow:hidden;color:var(--pce-muted);font-size:.66rem;font-weight:800;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-overflow:ellipsis;white-space:nowrap}.connection-editor__dock-link-badges{display:inline-flex;justify-content:flex-end;gap:5px;min-width:70px}.connection-editor__dock-link-badges span{padding:3px 7px;border-radius:999px;background:#a855f724;color:#d8b4fe;font-size:.62rem;font-weight:900;text-transform:uppercase;white-space:nowrap}:host-context(.theme-light) .connection-editor__dock-link-badges span{background:color-mix(in srgb,var(--pce-secondary) 14%,var(--pce-surface-soft));color:#6d2a9f}.connection-editor__dock-empty{padding:12px;border-radius:12px;border:1px dashed var(--pce-outline);color:var(--pce-muted);font-size:.76rem;font-weight:700}.connection-editor__inspector{border-color:color-mix(in srgb,var(--pce-primary) 26%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 10%,transparent),color-mix(in srgb,var(--pce-secondary) 8%,transparent)),var(--pce-surface)}.connection-editor__eyebrow,.connection-editor__section-title{font-size:.72rem;text-transform:uppercase;letter-spacing:.16em;color:var(--pce-muted);font-weight:700}.connection-editor__title{font-size:1.02rem;font-weight:800}.connection-editor__subtitle,.connection-editor__item-copy,.connection-editor__item-meta,.connection-editor__details-row span,.connection-editor__details-label,.connection-editor__empty{color:var(--pce-muted);font-size:.84rem}.connection-editor__inspector-heading{display:grid;gap:4px;padding:10px 12px;border-radius:14px;background:var(--pce-surface-soft);border:1px solid var(--pce-outline)}.connection-editor__inspector-heading strong{color:var(--pce-strong);font-size:.96rem;overflow-wrap:anywhere}.connection-editor__inspector-heading span{color:var(--pce-primary);font-size:.76rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em}.connection-editor__human-summary{display:grid;gap:6px;padding:12px;border-radius:14px;border:1px solid color-mix(in srgb,var(--pce-primary) 24%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 12%,transparent),color-mix(in srgb,var(--pce-secondary) 8%,transparent)),var(--pce-surface-soft);color:var(--pce-fg);line-height:1.35}.connection-editor__nested-summary{display:grid;gap:6px;padding:10px;border-radius:12px;border:1px solid color-mix(in srgb,var(--pce-primary) 22%,transparent);background:color-mix(in srgb,var(--pce-primary) 8%,var(--pce-surface-soft))}.connection-editor__nested-summary strong{color:var(--pce-strong);font-size:.82rem}.connection-editor__nested-summary span:last-child{color:var(--pce-primary);font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:.72rem;overflow-wrap:anywhere}.connection-editor__recommended{border-color:color-mix(in srgb,var(--pce-secondary) 32%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 14%,transparent),color-mix(in srgb,var(--pce-primary) 10%,transparent)),var(--pce-surface)}.connection-editor__recommended-card{display:grid;gap:8px;width:100%;padding:13px;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-secondary) 36%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 16%,transparent),color-mix(in srgb,var(--pce-primary) 10%,transparent)),var(--pce-surface-strong);color:var(--pce-fg);text-align:start;cursor:pointer;transition:transform .16s ease,border-color .16s ease,background .16s ease}.connection-editor__recommended-card:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 52%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 22%,transparent),color-mix(in srgb,var(--pce-primary) 14%,transparent)),var(--pce-surface-strong)}.connection-editor__recommended-card strong{color:var(--pce-strong);font-size:.92rem;line-height:1.28}.connection-editor__recommended-card span:not(.connection-editor__human-summary-label):not(.connection-editor__suggestion-action),.connection-editor__recommended-card small{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__success{display:grid;gap:4px;padding:10px 12px;border-radius:14px;border:1px solid rgba(34,197,94,.34);background:#22c55e1f;color:var(--pce-fg);line-height:1.35}.connection-editor__success strong{color:#bbf7d0;font-size:.78rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__success span{color:var(--pce-strong);font-size:.82rem}.connection-editor__human-summary-label,.connection-editor__suggestion-intent{justify-self:start;padding:3px 8px;border-radius:999px;background:color-mix(in srgb,var(--pce-primary) 16%,transparent);color:#bae6fd;font-size:.66rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.connection-editor__human-summary strong{color:var(--pce-strong);font-size:.88rem}.connection-editor__human-summary span:last-child,.connection-editor__suggestion-preview{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__technical-details{display:grid;gap:10px;padding:10px 12px;border-radius:14px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft)}.connection-editor__technical-details summary{cursor:pointer;color:var(--pce-muted);font-size:.78rem;font-weight:800}.connection-editor__technical-details[open] summary{margin-bottom:10px;color:var(--pce-strong)}.connection-editor__inspector-ports{display:flex;flex-wrap:wrap;gap:6px}.connection-editor__inspector-port{max-width:100%;padding:4px 8px;border-radius:999px;border:1px solid rgba(148,163,184,.16);background:#0f172a94;color:#cbd5e1;font-size:.68rem;font-weight:800;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.connection-editor__inspector-port--input{border-color:#22d3ee57;color:#67e8f9}.connection-editor__inspector-port--output{border-color:#f59e0b57;color:#fbbf24}.connection-editor__inspector-port--state{border-color:#a855f761;color:#d8b4fe}.connection-editor__segment-kind-legend{display:grid;gap:10px;padding:12px;border-radius:16px;border:1px solid rgba(148,163,184,.14);background:linear-gradient(135deg,#0f172aa8,#080d1885),#02061757}.connection-editor__segment-kind-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.connection-editor__segment-kind-pill{min-width:0;display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:28px;padding:0 8px;border-radius:999px;border:1px solid rgba(148,163,184,.14);background:#0f172a94;color:#cbd5e1;font-size:.68rem;font-weight:900;overflow:hidden;white-space:nowrap}.connection-editor__segment-kind-pill span{width:8px;height:8px;flex:0 0 auto;border-radius:999px;background:currentColor;box-shadow:0 0 12px currentColor}.connection-editor__segment-kind-pill--input{border-color:#19c8e83d;color:#7dddeb}.connection-editor__segment-kind-pill--output{border-color:#ff9f2f61;color:#ffbd62}.connection-editor__segment-kind-pill--state{border-color:#b46cff61;color:#d9b8ff}:host-context(.theme-light) .connection-editor__segment-kind-pill{border-color:var(--pce-outline);background:color-mix(in srgb,var(--pce-surface-strong) 88%,transparent)}:host-context(.theme-light) .connection-editor__segment-kind-pill--input{color:#075f73}:host-context(.theme-light) .connection-editor__segment-kind-pill--output{color:#8a4600}:host-context(.theme-light) .connection-editor__segment-kind-pill--state{color:#6d2a9f}.connection-editor__stage{grid-column:1;grid-row:2;position:relative;min-height:0;height:100%;overflow:visible;border-radius:16px;border:1px solid var(--pce-outline);background:radial-gradient(circle at center,color-mix(in srgb,var(--pce-secondary) 7%,transparent),transparent 38%),radial-gradient(circle,var(--pce-grid) 1px,transparent 1.5px),var(--pce-stage-bg);background-size:auto,31px 31px,auto;box-shadow:inset 0 0 0 1px var(--pce-glass-highlight);cursor:grab;touch-action:none}.connection-editor__stage--panning,.connection-editor__stage:active{cursor:grabbing}.connection-editor__stage--dragging{cursor:crosshair}.connection-editor__stage--rotating{cursor:grabbing}.connection-editor__viewport{position:absolute;inset-block-start:0;inset-inline-start:0;width:900px;height:540px;overflow:visible;transform-origin:0 0;transition:transform .14s ease}.connection-editor__wires{position:absolute;inset:0;width:100%;height:100%;overflow:visible}.connection-editor__wire,.connection-editor__wire-hit{fill:none;stroke-linecap:round}.connection-editor__wire{stroke:#b46cff;stroke-width:1.45;stroke-opacity:.48;pointer-events:none;filter:url(#connection-glow);stroke-dasharray:10 8;animation:connection-flow 1.65s linear infinite}.connection-editor__wire-hit{stroke:transparent;stroke-width:18;cursor:pointer;pointer-events:stroke}.connection-editor__wire-hit--active{stroke-width:28}.connection-editor__wire--halo{stroke:#b46cff1f;stroke-width:7;stroke-dasharray:none;animation:none}.connection-editor__wire--state{stroke:#ff9f2f;stroke-width:2.25;stroke-dasharray:8 7;animation:connection-flow .92s linear infinite}.connection-editor__wire--state.connection-editor__wire--halo{stroke:#ff9f2f29;stroke-width:11}.connection-editor__wire--projection{stroke:#15b8d8;stroke-width:1.75;stroke-dasharray:20 8;animation:connection-flow 1.9s linear infinite}.connection-editor__wire--projection.connection-editor__wire--halo{stroke:#15b8d81a;stroke-width:7}.connection-editor__wire--state-read{stroke:#8b7dff;stroke-width:1.65;stroke-dasharray:3 11;animation:connection-flow 2.25s linear infinite reverse}.connection-editor__wire--state-read.connection-editor__wire--halo{stroke:#8b7dff21;stroke-width:8}.connection-editor__wire--event-propagation{stroke:#ffb454;stroke-width:2.05;stroke-dasharray:5 7;animation:connection-flow .78s linear infinite}.connection-editor__wire--event-propagation.connection-editor__wire--halo{stroke:#ffb4542e;stroke-width:10}.connection-editor__wire--policy{filter:drop-shadow(0 0 6px rgba(34,197,94,.2)) url(#connection-glow)}.connection-editor__wire--policy.connection-editor__wire--halo{stroke:#22c55e1f;stroke-width:13}.connection-editor__wire--warning{stroke:#ff9f2f}.connection-editor__wire--warning.connection-editor__wire--halo{stroke:#ff9f2f2e}.connection-editor__wire--error{stroke:#ef4444}.connection-editor__wire--error.connection-editor__wire--halo{stroke:#ef44442e}.connection-editor__wire--active{stroke:#d9b8ff;stroke-width:3;stroke-opacity:1;stroke-dasharray:5 7;animation-duration:.58s}.connection-editor__wire--halo.connection-editor__wire--active{stroke-width:16;stroke-opacity:.8;animation:selected-wire-aura 1.35s ease-in-out infinite}.connection-editor__wire--new{stroke-width:3.4;stroke-opacity:1;stroke-dasharray:640;stroke-dashoffset:640;animation:wire-draw-in .62s cubic-bezier(.16,1,.3,1) forwards,connection-flow 1.05s linear .62s infinite}.connection-editor__wire--halo.connection-editor__wire--new{stroke-width:22;stroke-opacity:0;animation:new-wire-halo 1.45s ease-out forwards}.connection-editor__wire--trace{stroke:#9b5cff;stroke-width:3.2;stroke-opacity:1;stroke-dasharray:7 6;animation:connection-flow .72s linear infinite,trace-wire-pulse 1.18s ease-in-out infinite}.connection-editor__wire--halo.connection-editor__wire--trace{stroke:#9b5cff57;stroke-width:18;stroke-opacity:.86;animation:trace-halo-pulse 1.18s ease-in-out infinite}.connection-editor__wire--mode-dimmed{stroke-opacity:.14;stroke-width:1.05;filter:none;animation-duration:2.6s}.connection-editor--guided .connection-editor__wire:not(.connection-editor__wire--active,.connection-editor__wire--new,.connection-editor__wire--trace){stroke-opacity:.85;filter:none;animation:none}.connection-editor--guided .connection-editor__wire--halo:not(.connection-editor__wire--active,.connection-editor__wire--new,.connection-editor__wire--trace){stroke-opacity:.08;animation:none}.connection-editor__wire--halo.connection-editor__wire--mode-dimmed{stroke-opacity:.05;stroke-width:5;animation:none}.connection-editor__wire-hit--mode-dimmed{stroke-width:12}.connection-editor__wire-endpoint{fill:#f8fafc;stroke:#0f172ae6;stroke-width:1.5;filter:drop-shadow(0 0 8px rgba(216,180,254,.8));pointer-events:none;animation:endpoint-pulse 1.2s ease-in-out infinite}.connection-editor__wire-endpoint--source{fill:#ff9f2f}.connection-editor__wire-endpoint--target{fill:#19c8e8}.connection-editor__connection-tooltip{position:absolute;z-index:6;width:min(312px,calc(100% - 24px));display:grid;gap:8px;padding:12px 14px;border-radius:14px;border:1px solid var(--pce-outline);background:linear-gradient(135deg,var(--pce-surface-strong),var(--pce-surface)),var(--pce-surface-strong);box-shadow:0 18px 44px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);color:var(--pce-fg);pointer-events:none;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);animation:tooltip-rise .14s ease-out}.connection-editor__connection-tooltip-callout{position:absolute;inset-block-start:100%;inset-inline-start:var(--connection-tooltip-callout-x, 42px);width:2px;height:28px;border-radius:999px;background:linear-gradient(180deg,#e2e8f0bd,#19c8e800);box-shadow:0 0 12px #19c8e852;transform:translate(-1px);pointer-events:none}.connection-editor__connection-tooltip-callout:before,.connection-editor__connection-tooltip-callout:after{content:\"\";position:absolute;border-radius:999px;pointer-events:none}.connection-editor__connection-tooltip-callout:before{inset-block-start:-5px;inset-inline-start:-5px;width:12px;height:12px;border:1px solid rgba(148,163,184,.24);background:#080d18f5;box-shadow:inset 0 1px #ffffff14}.connection-editor__connection-tooltip-callout:after{inset-block-end:-3px;inset-inline-start:-4px;width:10px;height:10px;background:#19c8e8;box-shadow:0 0 16px #19c8e88f}.connection-editor__connection-tooltip--projection{border-color:#19c8e833;background:linear-gradient(135deg,#15b8d81f,#0f172ae0),#070c16eb}.connection-editor__connection-tooltip--state{border-color:#ff9f2f66;background:linear-gradient(135deg,#ff9f2f3d,#0f172ae0),#070c16eb}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-callout{background:linear-gradient(180deg,#ffbd62d1,#ff9f2f00);box-shadow:0 0 14px #ff9f2f61}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-callout:after{background:#ff9f2f;box-shadow:0 0 18px #ff9f2fad}.connection-editor__connection-tooltip--error{border-color:#f871716b}.connection-editor__connection-tooltip--error .connection-editor__connection-tooltip-callout:after{background:#fb7185;box-shadow:0 0 18px #fb7185ad}.connection-editor__connection-tooltip-kind{display:inline-flex;align-items:center;gap:8px;color:#94a3b8;font-size:.62rem;font-weight:900;letter-spacing:.16em;text-transform:uppercase}.connection-editor__connection-tooltip-kind span{width:8px;height:8px;border-radius:999px;background:#19c8e8;box-shadow:0 0 12px #19c8e880}.connection-editor__connection-tooltip--state .connection-editor__connection-tooltip-kind span{background:#ff9f2f;box-shadow:0 0 16px #ff9f2fc7}.connection-editor__connection-tooltip-row{min-width:0;display:grid;grid-template-columns:54px minmax(0,1fr);align-items:center;gap:8px;min-height:28px;padding:0 10px;border-radius:999px;background:#0f172a94}.connection-editor__connection-tooltip-row span{color:#8d9bb0;font-size:.62rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.connection-editor__connection-tooltip-row strong{min-width:0;overflow:hidden;color:#e2e8f0;font-size:.75rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__connection-tooltip-badges{display:flex;flex-wrap:wrap;gap:6px}.connection-editor__connection-tooltip-badges span{padding:3px 8px;border-radius:999px;background:#02061799;color:#fbbf24;font-size:.66rem;font-weight:900}.connection-editor__trace-overlay{position:absolute;z-index:6;inset-block-start:16px;inset-inline-start:50%;width:min(560px,calc(100% - 360px));min-width:320px;padding:12px 14px;border-radius:16px;border:1px solid rgba(34,197,94,.26);background:linear-gradient(135deg,#22c55e1f,#0ea5e91a),#080d18f0;box-shadow:0 22px 56px #02061775,0 0 42px #22c55e1f;transform:translate(-50%);pointer-events:auto;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);animation:tooltip-rise .16s ease-out}.connection-editor__trace-overlay-head,.connection-editor__trace-overlay-route{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0}.connection-editor__trace-overlay-head span,.connection-editor__trace-overlay-route span{color:#86efac;font-size:.66rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase;white-space:nowrap}.connection-editor__trace-overlay-head strong{color:#cbd5e1;font-size:.7rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace}.connection-editor__trace-overlay-route{margin-top:6px}.connection-editor__trace-overlay-controls{display:grid;grid-template-columns:28px minmax(0,1fr) 28px;align-items:center;gap:8px;margin-top:10px}.connection-editor__trace-overlay-button{width:28px;height:24px;display:inline-grid;place-items:center;border:1px solid rgba(148,163,184,.18);border-radius:999px;background:#0206176b;color:#e2e8f0;cursor:pointer;font-size:1rem;font-weight:900;line-height:1;transition:border-color .15s ease,background .15s ease,color .15s ease,opacity .15s ease}.connection-editor__trace-overlay-button:hover:not(:disabled){border-color:#22d3ee6b;background:#0ea5e929;color:#f8fafc}.connection-editor__trace-overlay-button:disabled{cursor:default;opacity:.36}.connection-editor__trace-overlay-steps{min-width:0;display:flex;align-items:center;gap:6px;overflow-x:auto;padding:2px;scrollbar-width:none}.connection-editor__trace-overlay-steps::-webkit-scrollbar{display:none}.connection-editor__trace-overlay-step{position:relative;flex:1 0 28px;min-width:28px;height:22px;border:1px solid rgba(148,163,184,.16);border-radius:999px;background:#0f172a9e;color:#94a3b8;cursor:pointer;font-size:.62rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;transition:border-color .15s ease,background .15s ease,color .15s ease,box-shadow .15s ease}.connection-editor__trace-overlay-step--active{border-color:#22d3eea3;background:linear-gradient(90deg,#22d3ee3d,#9b5cff33);color:#f8fafc;box-shadow:0 0 18px #22d3ee33}.connection-editor__trace-overlay-step--blocked{border-color:#fb71855c;color:#fecdd3}.connection-editor__trace-overlay-route strong{min-width:0;overflow:hidden;color:#f8fafc;font-size:.82rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__trace-overlay-bar{position:relative;height:5px;margin-top:10px;overflow:hidden;border-radius:999px;background:#94a3b829}.connection-editor__trace-overlay-bar span{position:absolute;inset-block:0;inset-inline-start:0;border-radius:inherit;background:linear-gradient(90deg,#22c55e,#22d3ee);box-shadow:0 0 18px #22c55e61;min-width:8%}.connection-editor__drag-wire{fill:none;stroke:#94a3b8;stroke-width:2.4;stroke-linecap:round;stroke-dasharray:6 7;pointer-events:none;filter:url(#connection-glow);animation:connection-flow 1.05s linear infinite,drag-wire-pulse 1.2s ease-in-out infinite}.connection-editor__drag-wire--halo{stroke:#94a3b829;stroke-width:14;stroke-dasharray:none;animation:drag-halo-pulse 1.2s ease-in-out infinite}.connection-editor__drag-wire--valid{stroke:#22c55e}.connection-editor__drag-wire--halo.connection-editor__drag-wire--valid{stroke:#22c55e47}.connection-editor__drag-endpoint{fill:#94a3b8;stroke:#020617eb;stroke-width:2;pointer-events:none;filter:url(#connection-glow);transform-box:fill-box;transform-origin:center;animation:endpoint-pulse 1s ease-in-out infinite}.connection-editor__drag-endpoint--source{fill:#ff9f2f}.connection-editor__drag-endpoint--target{fill:#7dddeb}.connection-editor__drag-endpoint--valid{fill:#22c55e;stroke:#bbf7d0e0}.connection-editor__wire-handle-line{stroke-width:2;stroke-linecap:round;stroke-dasharray:4 5;pointer-events:none;opacity:.88;filter:url(#connection-glow)}.connection-editor__wire-handle-line--source{stroke:#ff9f2ff0}.connection-editor__wire-handle-line--target{stroke:#19c8e8c2}.connection-editor__wire-control{fill:#0f172af5;stroke:#cbd5e1c7;stroke-width:1.8;pointer-events:none;filter:url(#connection-glow)}.connection-editor__wire-label{pointer-events:none;filter:drop-shadow(0 0 10px rgba(2,6,23,.44));animation:tooltip-rise .14s ease-out}.connection-editor__wire-label rect{fill:#080d18eb;stroke-width:1}.connection-editor__wire-label text{fill:#e2e8f0;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:8px;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.connection-editor__wire-label--source rect{stroke:#ff9f2fad}.connection-editor__wire-label--target rect{stroke:#19c8e86b}.connection-editor__zoom-controls{position:absolute;inset-inline-start:14px;inset-block-start:14px;z-index:4;display:grid;grid-template-columns:max-content;gap:6px;width:46px;pointer-events:auto}.connection-editor__tool-group,.connection-editor__trace-source{display:grid;gap:6px;padding:6px;border-radius:12px;border:1px solid var(--pce-outline);background:linear-gradient(180deg,var(--pce-surface-strong),var(--pce-surface));box-shadow:0 16px 36px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.connection-editor__tool-group{grid-template-columns:30px;width:max-content}.connection-editor__tool-group:nth-of-type(2){grid-template-columns:30px}.connection-editor__arrange-button{width:34px;min-height:34px;display:inline-flex;align-items:center;justify-content:center;gap:0;padding:0;border-radius:999px;border:1px solid rgba(168,85,247,.34);background:linear-gradient(90deg,#a855f72e,#22d3ee1f);color:#e9d5ff;font-size:0;font-weight:800;letter-spacing:0;cursor:pointer;opacity:1;transform:scale(1);transition:opacity .14s ease,transform .14s ease,border-color .14s ease,background .14s ease}.connection-editor--guided .connection-editor__arrange-button{opacity:.34;transform:scale(.9);border-color:color-mix(in srgb,var(--pce-outline) 82%,transparent);background:color-mix(in srgb,var(--pce-surface-soft) 84%,transparent)}.connection-editor--guided .connection-editor__zoom-controls:hover .connection-editor__arrange-button,.connection-editor--guided .connection-editor__zoom-controls:focus-within .connection-editor__arrange-button,.connection-editor--guided .connection-editor__arrange-button:hover,.connection-editor--guided .connection-editor__arrange-button:focus-visible,.connection-editor--guided .connection-editor__arrange-button--active{opacity:1;transform:scale(1);border-color:#a855f757;background:linear-gradient(90deg,#a855f72e,#22d3ee1f)}.connection-editor__arrange-button span{color:#c084fc;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.78rem;font-weight:900}.connection-editor__tool-button{width:30px;height:30px;border-radius:999px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-fg);font-size:.9rem;font-weight:800;letter-spacing:.02em;cursor:pointer;line-height:1}.connection-editor__tool-button:hover:not(:disabled){border-color:color-mix(in srgb,var(--pce-primary) 42%,transparent);color:var(--pce-primary);box-shadow:0 0 18px color-mix(in srgb,var(--pce-primary) 16%,transparent)}.connection-editor__history-button:disabled{cursor:not-allowed;opacity:.42}.connection-editor__arrange-button--active{border-color:#22d3ee94;background:linear-gradient(90deg,#0ea5e93d,#22d3ee2e);color:#cffafe}.connection-editor__trace-source{gap:4px;max-height:230px;overflow:auto}.connection-editor__trace-source-title{padding:2px 4px 6px;color:var(--pce-muted);font-size:.64rem;font-weight:900;letter-spacing:.18em;text-transform:uppercase}.connection-editor__trace-source-item{min-width:0;min-height:30px;display:flex;align-items:center;gap:8px;padding:0 8px;border:0;border-radius:8px;background:transparent;color:var(--pce-fg);cursor:pointer;font-size:.74rem;font-weight:800;text-align:start}.connection-editor__trace-source-item span{color:#f59e0b;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.72rem;font-weight:900}.connection-editor__trace-source-item:hover,.connection-editor__trace-source-item--active{background:var(--pce-surface-strong);color:var(--pce-strong);box-shadow:inset 3px 0 #f59e0bd1}.connection-editor__minimap{position:absolute;inset-inline-end:14px;inset-block-end:14px;z-index:4;width:184px;height:124px;padding:18px 10px 10px;overflow:hidden;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-primary) 30%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-primary) 12%,transparent),var(--pce-surface-strong)),var(--pce-surface);box-shadow:0 20px 48px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight);cursor:pointer;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);opacity:1;transform:scale(1);transform-origin:100% 100%;transition:opacity .16s ease,transform .16s ease,border-color .16s ease,box-shadow .16s ease}.connection-editor--guided .connection-editor__minimap{opacity:.54;transform:scale(.88);border-color:color-mix(in srgb,var(--pce-outline) 64%,transparent);box-shadow:0 14px 34px color-mix(in srgb,var(--pce-shadow-soft) 70%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor--guided .connection-editor__minimap:hover,.connection-editor--guided .connection-editor__minimap:focus-visible,.connection-editor--guided .connection-editor__minimap--panning{opacity:1;transform:scale(1)}.connection-editor__minimap:hover,.connection-editor__minimap--panning{border-color:#22d3ee75;box-shadow:0 20px 48px var(--pce-shadow-soft),0 0 24px color-mix(in srgb,var(--pce-primary) 14%,transparent),inset 0 1px var(--pce-glass-highlight)}.connection-editor__minimap--panning{cursor:grabbing}.connection-editor__minimap:before{content:\"\";position:absolute;inset:22px 10px 10px;border-radius:10px;background-image:radial-gradient(circle,rgba(125,211,252,.18) 1px,transparent 1px);background-size:22px 22px;opacity:.58;pointer-events:none}.connection-editor__minimap-title{position:absolute;inset-block-start:7px;inset-inline-start:12px;color:var(--pce-muted);font-size:.6rem;font-weight:900;letter-spacing:.16em;text-transform:uppercase}.connection-editor__minimap svg{position:relative;width:100%;height:100%;display:block;overflow:visible}.connection-editor__minimap-wire{fill:none;stroke:#b46cff8f;stroke-width:4;stroke-linecap:round;opacity:.86}.connection-editor__minimap-wire--state{stroke:#ff9f2fdb}.connection-editor__minimap-wire--state-read{stroke:#8b7dffb8;stroke-dasharray:4 9}.connection-editor__minimap-wire--projection{stroke:#19c8e88a;stroke-dasharray:18 8}.connection-editor__minimap-wire--event-propagation{stroke:#ffb454cc;stroke-dasharray:6 7}.connection-editor__minimap-wire--policy{filter:drop-shadow(0 0 6px rgba(34,197,94,.28))}.connection-editor__minimap-wire--trace{stroke:#9b5cffe0;stroke-width:5;stroke-dasharray:12 9;animation:connection-flow 1s linear infinite}.connection-editor__minimap-wire--active{stroke:#facc15eb;stroke-width:6;filter:drop-shadow(0 0 8px rgba(250,204,21,.38))}.connection-editor__minimap-wire--mode-dimmed{opacity:.2;stroke-width:2.6;filter:none}.connection-editor__minimap-node{fill:#f59e0be0;stroke:#020617db;stroke-width:4;filter:drop-shadow(0 0 7px rgba(245,158,11,.24))}.connection-editor__minimap-node--state{fill:#a855f7e6;filter:drop-shadow(0 0 8px rgba(168,85,247,.34))}.connection-editor__minimap-node--selected{fill:#facc15f5;stroke:#fef08aeb;filter:drop-shadow(0 0 10px rgba(250,204,21,.42))}.connection-editor__minimap-node--trace-source{fill:#facc15f5;filter:drop-shadow(0 0 10px rgba(250,204,21,.44))}.connection-editor__minimap-node--trace-reacting{fill:#22c55eeb;filter:drop-shadow(0 0 10px rgba(34,197,94,.42))}.connection-editor__minimap-node--mode-dimmed{opacity:.28;filter:none}.connection-editor__minimap-viewport{fill:#7dd3fc1f;stroke:#7dd3fce6;stroke-width:5;stroke-dasharray:18 10;filter:drop-shadow(0 0 10px rgba(125,211,252,.3));transition:x .12s ease,y .12s ease,width .12s ease,height .12s ease}.connection-editor__minimap--panning .connection-editor__minimap-viewport{fill:#22d3ee2e;stroke:#22d3eef5;stroke-dasharray:none}.connection-editor__node{position:absolute;z-index:10;width:168px;height:168px;transform:translate(-84px,-84px);border:0;padding:0;background:transparent;color:inherit;cursor:pointer;transition:filter .16s ease}.connection-editor__node--dragging{cursor:grabbing;filter:drop-shadow(0 0 22px rgba(34,211,238,.26));z-index:12}.connection-editor__node--connection-focus{filter:drop-shadow(0 0 16px rgba(34,211,238,.2))}.connection-editor__node--compat-target .connection-editor__node-ring{box-shadow:0 0 0 2px #22c55e66,0 0 0 18px #22c55e0b,0 0 32px #22c55e29}.connection-editor__node--mode-dimmed{opacity:.42;filter:grayscale(.22) saturate(.56)}.connection-editor__node--quiet{opacity:.58;filter:saturate(.68) brightness(.86)}.connection-editor__node--mode-dimmed .connection-editor__node-ring{animation:none;box-shadow:0 0 0 1px #2dd4bf14,0 0 14px #0ea5e90f}.connection-editor__node--mode-dimmed .connection-editor__node-halo{stroke-opacity:.28}.connection-editor__node-ring,.connection-editor__node-core,.connection-editor__node-svg{position:absolute;inset:0}.connection-editor__node-ring,.connection-editor__node-core{display:grid;place-items:center;clip-path:polygon(30% 0,70% 0,100% 30%,100% 70%,70% 100%,30% 100%,0 70%,0 30%)}.connection-editor__node-ring{background:radial-gradient(circle at center,transparent 0 44%,rgba(7,17,31,.96) 45% 57%,transparent 58%),conic-gradient(from -28deg,#06b6d4b3 0 28%,#f59e0b9e 28% 48%,#a855f780 48% 68%,#06b6d4a3 68% 100%);box-shadow:0 0 0 1px #2dd4bf2e,0 0 26px #0ea5e929;animation:node-breathe 2.8s ease-in-out infinite}.connection-editor__node-svg{overflow:visible;filter:drop-shadow(0 0 16px rgba(14,165,233,.16))}.connection-editor__node-halo{fill:transparent;stroke:#38bdf838;stroke-width:1;stroke-dasharray:0;pointer-events:none}.connection-editor__node-halo:nth-child(2){stroke:#38bdf824}.connection-editor__node-halo:nth-child(3){stroke:#94a3b81a}.connection-editor__node-segment{fill-opacity:.28;stroke-opacity:.72;stroke-width:1.15;transition:fill-opacity .16s ease,stroke-opacity .16s ease,stroke-width .16s ease;cursor:grab}.connection-editor__node:hover .connection-editor__node-segment,.connection-editor__node--selected .connection-editor__node-segment,.connection-editor__node--connection-focus .connection-editor__node-segment{fill-opacity:.54;stroke-opacity:.95;stroke-width:1.55}.connection-editor__node-segment--connection-focus{fill-opacity:.76;stroke-opacity:1;stroke-width:2;filter:drop-shadow(0 0 8px rgba(34,211,238,.76));animation:segment-focus-sweep 1.45s ease-in-out infinite}.connection-editor__node-segment--source{fill-opacity:.86;stroke-opacity:1;stroke-width:2.45;filter:drop-shadow(0 0 12px rgba(251,146,60,.82))}.connection-editor__node-segment--compatible{fill-opacity:.74;stroke-opacity:1;stroke-width:2;filter:drop-shadow(0 0 10px rgba(34,197,94,.72));animation:snap-pulse 1s ease-in-out infinite}.connection-editor__node-segment--compat-preview{fill-opacity:.86;stroke-opacity:1;stroke-width:2.45;filter:drop-shadow(0 0 13px rgba(245,158,11,.78))}.connection-editor__node-segment--candidate{fill-opacity:.92;stroke-opacity:1;stroke-width:2.8;filter:drop-shadow(0 0 16px rgba(34,197,94,.86))}.connection-editor__node-segment--incompatible{fill-opacity:.12;stroke-opacity:.28;cursor:not-allowed}.connection-editor__node-segment--invalid-drop{fill-opacity:.72;stroke:#fb7185;stroke-opacity:1;stroke-width:3;filter:drop-shadow(0 0 16px rgba(244,63,94,.84));animation:invalid-drop-jolt .36s ease-out}.connection-editor__node-segment--state{fill-opacity:.44;filter:drop-shadow(0 0 8px rgba(180,108,255,.2))}.connection-editor__node-segment--mode-dimmed{fill-opacity:.08;stroke-opacity:.2;stroke-width:.9;filter:none}.connection-editor__node-anchor{opacity:.95;filter:drop-shadow(0 0 6px currentColor)}.connection-editor__node-rotate-handle{cursor:grab;opacity:0;pointer-events:none;transition:opacity .16s ease,filter .16s ease;filter:drop-shadow(0 0 8px rgba(34,211,238,.28))}.connection-editor__node:hover .connection-editor__node-rotate-handle,.connection-editor__node--selected .connection-editor__node-rotate-handle,.connection-editor__node--rotating .connection-editor__node-rotate-handle{opacity:1;pointer-events:auto}.connection-editor__node--rotating .connection-editor__node-rotate-handle{cursor:grabbing;filter:drop-shadow(0 0 12px rgba(34,211,238,.54))}.connection-editor__node-rotate-hit{fill:transparent}.connection-editor__node-rotate-dot{fill:var(--pce-surface-strong);stroke:var(--pce-primary);stroke-opacity:.88;stroke-width:1.4}.connection-editor__node-rotate-arc,.connection-editor__node-rotate-arrow{fill:none;stroke:var(--pce-primary);stroke-linecap:round;stroke-linejoin:round;stroke-opacity:.96;stroke-width:1.2;pointer-events:none}.connection-editor__segment-icon,.connection-editor__segment-label{pointer-events:none;fill:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-weight:800;paint-order:stroke;stroke:#020617c2;stroke-width:3px;stroke-linejoin:round}.connection-editor__segment-icon{font-size:12px;fill:#19c8e8;opacity:1;transition:opacity .14s ease}.connection-editor__segment-icon--output{fill:#ff9f2f}.connection-editor--guided .connection-editor__segment-icon:not(.connection-editor__segment-icon--guided-visible){opacity:0}.connection-editor--guided .connection-editor__node--rotating .connection-editor__segment-icon{opacity:0}.connection-editor__segment-label{font-size:9.5px;letter-spacing:0;fill:#e2e8f0e0;opacity:1;transition:opacity .14s ease}.connection-editor--guided .connection-editor__segment-label:not(.connection-editor__segment-label--guided-visible){opacity:0}.connection-editor--guided .connection-editor__node--rotating .connection-editor__segment-label{opacity:0}.connection-editor__node-core{inset:43px;padding:12px;border-radius:999px;clip-path:none;display:flex;flex-direction:column;align-items:center;justify-content:center;background:radial-gradient(circle at center,#111827 0 58%,#060b14);border:1px solid rgba(226,232,240,.1);box-shadow:0 12px 28px #00000073,inset 0 1px #ffffff0d;color:#f8fafc;text-shadow:0 1px 2px rgba(0,0,0,.62)}.connection-editor__node-icon{display:block;max-width:44px;overflow:hidden;color:#f8fafc;font-family:Material Symbols Outlined,Material Icons,sans-serif;font-size:1.22rem;font-weight:400;line-height:1;text-align:center;text-transform:none;letter-spacing:0;white-space:nowrap;filter:drop-shadow(0 0 10px rgba(226,232,240,.18))}.connection-editor__node--state .connection-editor__node-ring{background:radial-gradient(circle at center,transparent 0 44%,rgba(7,17,31,.96) 45% 57%,transparent 58%),conic-gradient(from 14deg,#a855f7c7 0 42%,#7e3af26b 42% 78%,#f59e0b47 78% 100%);box-shadow:0 0 0 1px #a855f738,0 0 30px #a855f733}.connection-editor__node--selected .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15bd,0 0 34px #facc153d}.connection-editor__node--connection-focus .connection-editor__node-ring{box-shadow:0 0 0 2px #22d3eead,0 0 38px #22d3ee42;animation:node-focus-breathe 1.7s ease-in-out infinite}.connection-editor__node--trace-source .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15c7,0 0 0 18px #facc150f,0 0 54px #facc154d;animation:trace-source-pulse 1.38s ease-in-out infinite}.connection-editor__node--trace-source:before{content:\"\";position:absolute;z-index:0;inset:-44px;border-radius:999px;border:2px solid rgba(250,204,21,.36);background:radial-gradient(circle at center,transparent 0 64%,rgba(250,204,21,.05) 65% 66%,transparent 67%),conic-gradient(from -20deg,#facc1500,#facc1552,#facc1500);box-shadow:0 0 36px #facc151f;opacity:.92;pointer-events:none;animation:trace-source-orbit 2.8s linear infinite}.connection-editor__node--trace-reacting .connection-editor__node-ring{box-shadow:0 0 0 2px #22c55e94,0 0 46px #22c55e38;animation:trace-reacting-pulse 1.38s ease-in-out infinite}.connection-editor__node--trace-source:after,.connection-editor__node--trace-reacting:after{position:absolute;z-index:3;inset-inline-start:50%;inset-block-start:-42px;padding:0;border:0;background:transparent;color:#facc15;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:.68rem;font-weight:900;letter-spacing:.18em;text-transform:uppercase;transform:translate(-50%);text-shadow:0 0 12px rgba(250,204,21,.52);pointer-events:none}.connection-editor__node--trace-source:after{content:attr(data-trace-label)}.connection-editor__node--trace-reacting:after{content:attr(data-trace-label);color:#86efac;text-shadow:0 0 12px rgba(34,197,94,.44)}.connection-editor__node--trace-source .connection-editor__node-segment--output{fill-opacity:.66;stroke-width:2.2;filter:drop-shadow(0 0 14px rgba(255,159,47,.42))}.connection-editor__node--expanded .connection-editor__node-ring{box-shadow:0 0 0 2px #22d3eebd,0 0 34px #22d3ee3d}.connection-editor__node--rotating .connection-editor__node-ring{box-shadow:0 0 0 2px #facc15c7,0 0 42px #facc1547;animation:node-focus-breathe 1.1s ease-in-out infinite}.connection-editor__node--rotating .connection-editor__node-segment{fill-opacity:.68;stroke-opacity:1;stroke-width:1.9;cursor:grabbing}.connection-editor__node-title,.connection-editor__node-subtitle{display:block;max-width:92px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center}.connection-editor__node-title{color:#f8fafc;font-size:.82rem;font-weight:800}.connection-editor__node-subtitle{margin-top:4px;color:#a7b4c8;font-size:.62rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__ports{position:absolute;z-index:30;display:grid;gap:6px;width:104px;opacity:0;pointer-events:none;transform:translateY(3px);transition:opacity .16s ease,transform .16s ease}.connection-editor__ports--visible{opacity:1;pointer-events:auto;transform:translateY(0)}.connection-editor__ports--outputs{z-index:42}.connection-editor__ports--inputs{z-index:36}.connection-editor__segment-popover{position:absolute;z-index:80;width:292px;max-height:320px;overflow:auto;display:grid;gap:10px;padding:14px;border-radius:16px;border:1px solid rgba(148,163,184,.18);background:linear-gradient(135deg,#0f172af5,#110c20f0),#090c16eb;box-shadow:0 28px 72px #0000007a,0 0 48px #a855f71a,inset 0 1px #ffffff0d;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__segment-popover-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding-bottom:2px}.connection-editor__segment-popover-title{color:#94a3b8;font-size:.68rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase}.connection-editor__segment-popover-hint{margin-top:5px;color:#64748b;font-size:.66rem;font-weight:700}.connection-editor__segment-rotation{flex:0 0 auto;min-width:54px;padding:5px 8px;border-radius:999px;border:1px solid rgba(34,211,238,.28);background:#082f4957;color:#7dd3fc;font-size:.66rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-align:center;box-shadow:inset 0 0 16px #0ea5e914}.connection-editor__segment-row{display:grid;grid-template-columns:28px 72px minmax(0,1fr) auto;align-items:center;gap:8px;min-height:40px;padding:7px;border-radius:12px;border:1px solid rgba(148,163,184,.12);background:linear-gradient(90deg,#0f172ac7,#0f172a75),#0f172a99;transition:border-color .15s ease,background .15s ease,box-shadow .15s ease,transform .15s ease}.connection-editor__segment-row--input{border-color:#22d3ee42}.connection-editor__segment-row--output{border-color:#f59e0b47}.connection-editor__segment-row--state{border-color:#a855f74d}.connection-editor__segment-row--source,.connection-editor__segment-row--candidate{transform:translate(2px);box-shadow:0 0 22px #f59e0b24,inset 0 0 20px #f59e0b14}.connection-editor__segment-row--compatible{border-color:#22d3ee6b;box-shadow:inset 0 0 18px #22d3ee14}.connection-editor__segment-row--compat-preview{border-color:#f59e0b94;background:linear-gradient(90deg,#f59e0b1f,#0f172a8a),#0f172ab3;box-shadow:0 0 22px #f59e0b1f,inset 0 0 18px #f59e0b14}.connection-editor__segment-row--invalid-drop{border-color:#fb7185b3;background:linear-gradient(90deg,#f43f5e24,#0f172a94),#0f172ab3;box-shadow:0 0 24px #f43f5e2e,inset 0 0 18px #f43f5e1a;animation:invalid-drop-jolt .36s ease-out}.connection-editor__segment-index{color:#64748b;font-size:.64rem;font-weight:900;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;letter-spacing:.08em}.connection-editor__segment-kind{display:inline-flex;align-items:center;gap:6px;color:#94a3b8;font-size:.62rem;font-weight:800;text-transform:uppercase;min-width:0;white-space:nowrap}.connection-editor__segment-kind-dot{width:7px;height:7px;border-radius:999px;background:#94a3b8;box-shadow:0 0 12px #94a3b847}.connection-editor__segment-row--input .connection-editor__segment-kind-dot{background:#22d3ee;box-shadow:0 0 12px #22d3ee61}.connection-editor__segment-row--output .connection-editor__segment-kind-dot{background:#fb923c;box-shadow:0 0 12px #fb923c66}.connection-editor__segment-row--state .connection-editor__segment-kind-dot{background:#a855f7;box-shadow:0 0 12px #a855f76b}.connection-editor__segment-label{display:inline-flex;align-items:center;gap:5px;min-width:0;overflow:hidden;color:#e2e8f0;font-size:.74rem;font-weight:700;text-overflow:ellipsis;white-space:nowrap}.connection-editor__segment-badge{flex:0 0 auto;padding:2px 5px;border-radius:999px;border:1px solid rgba(56,189,248,.28);background:#0ea5e91a;color:#bae6fd;font-size:.55rem;font-weight:900;text-transform:uppercase}.connection-editor__segment-path{grid-column:3 / 4;min-width:0;margin-top:-5px;overflow:hidden;color:#93c5fd;font-size:.62rem;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;text-overflow:ellipsis;white-space:nowrap}.connection-editor__segment-row--nested{min-height:56px;border-color:#38bdf84d;background:linear-gradient(90deg,#0ea5e91a,#0f172a80),#0f172aa3}.connection-editor__segment-actions{display:inline-flex;gap:4px}.connection-editor__segment-action{width:24px;height:24px;border-radius:999px;border:1px solid rgba(148,163,184,.18);background:#060c16b8;color:#dbeafe;cursor:pointer;font-weight:900;line-height:1}.connection-editor__segment-action:hover{border-color:#22d3ee75;box-shadow:0 0 14px #22d3ee29}.connection-editor__port{min-height:24px;border-radius:999px;border:1px solid rgba(148,163,184,.14);background:#080d18c2;color:#cbd5e1;font-size:.64rem;font-weight:700;font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:crosshair;box-shadow:0 8px 20px #00000038}.connection-editor--guided .connection-editor__ports--visible .connection-editor__port:not(.connection-editor__port--guided-visible){display:none}.connection-editor__port--output{border-color:#f59e0b75;color:#fbbf24}.connection-editor__port--input{border-color:#06b6d475;color:#22d3ee}.connection-editor__port--state{border-color:#a855f785;color:#c084fc}.connection-editor__port--compatible{background:#22c55e29;box-shadow:0 0 0 1px #22c55e61,0 0 18px #22c55e2e;animation:snap-pulse 1s ease-in-out infinite}.connection-editor__port--source{border-color:#fb923cb8;background:#fb923c29;color:#fed7aa;box-shadow:0 0 0 1px #fb923c73,0 0 20px #fb923c2e}.connection-editor__port--candidate{background:#22c55e3d;box-shadow:0 0 0 2px #22c55e94,0 0 24px #22c55e3d}.connection-editor__port--incompatible{opacity:.42;cursor:not-allowed;filter:grayscale(.45)}.connection-editor__port--invalid-drop{opacity:1;border-color:#fb7185c7;color:#fecdd3;background:#f43f5e2e;box-shadow:0 0 0 2px #f43f5e75,0 0 24px #f43f5e38;filter:none;animation:invalid-drop-jolt .36s ease-out}.connection-editor__port--connection-focus{border-color:#facc15ad;color:#fde68a;background:#facc151f;box-shadow:0 0 0 1px #facc156b,0 0 20px #facc152e;animation:port-focus-pulse 1.25s ease-in-out infinite}:host-context(.theme-light) .connection-editor__port{color:var(--pce-fg)}.connection-editor__drag-tip{position:absolute;z-index:6;display:grid;gap:4px;width:min(330px,calc(100% - 28px));padding:10px 12px;border-radius:14px;border:1px solid rgba(125,211,252,.26);background:linear-gradient(135deg,#0ea5e91f,#0f172aeb),#080d18f0;box-shadow:0 18px 44px #0206176b,0 0 28px #0ea5e91a;font-size:.76rem;pointer-events:none;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);animation:tooltip-rise .14s ease-out}.connection-editor__drag-tip--valid{border-color:#22c55e66;background:linear-gradient(135deg,#22c55e29,#0f172aeb),#080d18f0;box-shadow:0 18px 44px #0206176b,0 0 30px #22c55e24}.connection-editor__drag-tip--invalid{z-index:7;border-color:#fb71858a;background:linear-gradient(135deg,#f43f5e2e,#0f172af0),#080d18f5;box-shadow:0 18px 44px #0206176b,0 0 34px #f43f5e29;animation:tooltip-rise .14s ease-out,invalid-drop-jolt .36s ease-out}.connection-editor__invalid-drop-pulse{position:absolute;z-index:6;width:16px;height:16px;border-radius:999px;border:2px solid rgba(251,113,133,.88);background:#f43f5e38;box-shadow:0 0 0 8px #f43f5e1a,0 0 28px #f43f5e3d;pointer-events:none;transform:translate(-50%,-50%);animation:invalid-drop-ping 1.2s ease-out both}.connection-editor__drag-tip span{color:#7dd3fc;text-transform:uppercase;letter-spacing:.12em;font-size:.62rem;font-weight:800}.connection-editor__drag-tip strong{min-width:0;overflow:hidden;color:#f8fafc;font-size:.78rem;font-weight:900;text-overflow:ellipsis;white-space:nowrap}.connection-editor__drag-tip small{color:#94a3b8;font-size:.68rem;font-weight:700}.connection-editor__drag-tip--valid small{color:#86efac}.connection-editor__drag-tip--invalid span,.connection-editor__drag-tip--invalid small{color:#fda4af}.connection-editor__summary{grid-column:1;grid-row:2;align-self:end;justify-self:start;z-index:6;display:flex;flex-wrap:wrap;gap:6px;padding:0 0 12px 12px;pointer-events:none}.connection-editor__side-panel{grid-column:2;grid-row:2 / 3;position:relative;z-index:8;min-width:0;min-height:0;display:grid;align-content:start;gap:14px;overflow:auto;padding-inline-end:4px;scrollbar-color:var(--pce-outline-strong) var(--pce-surface-soft)}.connection-editor__side-panel--contextual .connection-editor__inspector{order:-3}.connection-editor__side-panel--contextual .connection-editor__recommended{order:2}.connection-editor__side-panel--contextual .connection-editor__filters-section{order:3}.connection-editor__side-panel--contextual .connection-editor__recommended,.connection-editor__side-panel--contextual .connection-editor__filters-section{opacity:.82}.connection-editor__metric,.connection-editor__item{border-radius:16px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.connection-editor__metric{min-width:auto;display:inline-flex;align-items:baseline;gap:7px;padding:6px 9px;border-radius:999px;background:color-mix(in srgb,var(--pce-surface-strong) 86%,transparent);box-shadow:0 10px 26px var(--pce-shadow-soft),inset 0 1px var(--pce-glass-highlight)}.connection-editor__metric span{color:var(--pce-muted);font-size:.62rem;text-transform:uppercase;letter-spacing:.08em}.connection-editor__metric strong,.connection-editor__details-row strong{color:var(--pce-strong);font-size:.78rem}.connection-editor__filters,.connection-editor__actions,.connection-editor__badges{display:flex;gap:8px;flex-wrap:wrap}.connection-editor__filter,.connection-editor__action{border:1px solid var(--pce-outline-strong);background:var(--pce-surface-soft);color:var(--pce-fg);border-radius:999px;padding:6px 10px;font-size:.75rem;font-weight:700;cursor:pointer}.connection-editor__filter--active,.connection-editor__action:hover{border-color:color-mix(in srgb,var(--pce-secondary) 62%,transparent);background:color-mix(in srgb,var(--pce-secondary) 16%,var(--pce-surface-soft));color:var(--pce-strong)}.connection-editor__action--danger{border-color:#f8717161;color:#fecaca}.connection-editor__item{width:100%;text-align:start;padding:12px;display:grid;gap:6px;cursor:pointer;color:inherit}.connection-editor__item--active{border-color:#a855f7b8;background:linear-gradient(90deg,#0ea5e91a,#a855f721)}.connection-editor__item-title{font-weight:800;color:#f8fafc;word-break:break-word}.connection-editor__badge{padding:3px 8px;border-radius:999px;background:color-mix(in srgb,var(--pce-primary) 12%,var(--pce-surface));color:var(--pce-fg);font-size:.72rem;font-weight:800}.connection-editor__section{min-width:0;padding:14px;border-radius:18px;background:var(--pce-surface);border:1px solid var(--pce-outline);box-shadow:inset 0 1px var(--pce-glass-highlight),0 18px 50px var(--pce-shadow-soft);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.connection-editor__badge--warning{background:color-mix(in srgb,var(--pce-warning) 14%,var(--pce-surface));color:var(--pce-fg)}.connection-editor__badge--error{background:color-mix(in srgb,var(--pce-error) 14%,var(--pce-surface));color:var(--pce-fg)}.connection-editor__diagnostics{display:grid;gap:8px}.connection-editor__diagnostic{display:grid;gap:4px;padding:10px;border-radius:12px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft);color:var(--pce-fg);font-size:.78rem;line-height:1.35}.connection-editor__diagnostic--warning{border-color:#f59e0b47;background:#f59e0b1a}.connection-editor__diagnostic--error{border-color:#ef44445c;background:#ef44441f}.connection-editor__diagnostic-severity{font-size:.68rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase;color:#f8fafc}.connection-editor__suggestions{display:grid;gap:10px;margin-top:14px;padding-top:14px;border-top:1px solid var(--pce-outline)}.connection-editor__suggestion{display:grid;gap:8px;width:100%;padding:12px;border-radius:16px;border:1px solid color-mix(in srgb,var(--pce-secondary) 26%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 12%,transparent),color-mix(in srgb,var(--pce-primary) 8%,transparent)),var(--pce-surface-strong);color:var(--pce-fg);text-align:start;cursor:pointer;transition:transform .16s ease,border-color .16s ease,background .16s ease}.connection-editor__suggestion:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--pce-primary) 46%,transparent);background:linear-gradient(135deg,color-mix(in srgb,var(--pce-secondary) 18%,transparent),color-mix(in srgb,var(--pce-primary) 12%,transparent)),var(--pce-surface-strong)}.connection-editor__suggestion-route{display:grid;gap:4px;font-size:.78rem}.connection-editor__suggestion-route strong{color:var(--pce-strong);font-size:.82rem}.connection-editor__suggestion-reason{color:var(--pce-muted);font-size:.76rem;line-height:1.35}.connection-editor__suggestion-diagnostic{padding:10px 12px;border-radius:14px;border:1px solid rgba(245,158,11,.28);background:#f59e0b1a;color:var(--pce-strong);font-size:.78rem;line-height:1.35}.connection-editor__suggestion-outcome{color:var(--pce-strong);font-size:.84rem;line-height:1.3}.connection-editor__suggestion-action{justify-self:start;padding:3px 8px;border-radius:999px;background:#22d3ee24;color:#67e8f9;font-size:.68rem;font-weight:900;letter-spacing:.06em;text-transform:uppercase}:host-context(.theme-light) .connection-editor__suggestion-action{background:color-mix(in srgb,var(--pce-primary) 12%,var(--pce-surface-soft));color:#075f73}.connection-editor__trace{display:grid;gap:10px;margin-top:14px;padding-top:14px;border-top:1px solid var(--pce-outline)}.connection-editor__trace-summary{color:var(--pce-muted);font-size:.78rem;line-height:1.35}.connection-editor__trace-step{display:grid;grid-template-columns:24px minmax(76px,auto) 1fr;gap:8px;align-items:center;padding:9px;border-radius:12px;border:1px solid rgba(34,211,238,.18);background:var(--pce-surface-soft);font-size:.76rem}.connection-editor__trace-step--blocked{border-color:#ef444457;background:#ef44441a}.connection-editor__trace-order{display:inline-grid;place-items:center;width:22px;height:22px;border-radius:999px;background:#22d3ee29;color:#67e8f9;font-weight:900}.connection-editor__trace-phase{color:var(--pce-strong);font-weight:900;text-transform:uppercase;letter-spacing:.06em;font-size:.66rem}.connection-editor__trace-copy{min-width:0;color:var(--pce-fg);word-break:break-word}.connection-editor__transform-steps{display:grid;gap:8px}.connection-editor__transform-step{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:8px;align-items:center;padding:9px;border-radius:12px;border:1px solid rgba(168,85,247,.22);background:var(--pce-surface-soft)}.connection-editor__transform-kind{min-width:0;color:var(--pce-strong);font-weight:900;word-break:break-word}.connection-editor__transform-phase{color:var(--pce-muted);font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}.connection-editor__details-row,.connection-editor__details-block{display:grid;gap:6px}.connection-editor__payload-input{width:100%;min-height:128px;box-sizing:border-box;resize:vertical;border:1px solid var(--pce-outline);border-radius:8px;background:color-mix(in srgb,var(--pce-surface) 92%,black);color:var(--pce-fg);padding:10px;font:.76rem/1.45 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.connection-editor__surface-action-editor{display:block;max-height:min(58vh,620px);overflow:auto;border:1px solid var(--pce-outline);border-radius:8px;background:color-mix(in srgb,var(--pce-surface) 96%,black);padding:10px;margin-bottom:10px}.connection-editor__rule-grid{display:grid;gap:10px}.connection-editor__rule-card{display:grid;gap:8px;padding:10px;border-radius:12px;border:1px solid var(--pce-outline);background:var(--pce-surface-soft)}.connection-editor__rule-heading{color:var(--pce-fg);font-size:.72rem;font-weight:900;text-transform:uppercase;letter-spacing:.06em}pre{margin:0;padding:12px;border-radius:12px;background:color-mix(in srgb,var(--pce-surface-strong) 82%,#020617);color:var(--pce-fg);overflow:auto;font-size:.75rem}@keyframes connection-flow{to{stroke-dashoffset:-38}}@keyframes drag-wire-pulse{0%,to{stroke-opacity:.76}50%{stroke-opacity:1}}@keyframes drag-halo-pulse{0%,to{stroke-opacity:.48}50%{stroke-opacity:.82}}@keyframes endpoint-pulse{0%,to{opacity:.78;transform:scale(1)}50%{opacity:1;transform:scale(1.25)}}@keyframes selected-wire-aura{0%,to{stroke-opacity:.46}50%{stroke-opacity:.92}}@keyframes wire-draw-in{0%{stroke-dashoffset:640;opacity:.32}70%{opacity:1}to{stroke-dashoffset:0;opacity:1}}@keyframes new-wire-halo{0%{stroke-opacity:0;stroke-width:30}28%{stroke-opacity:.78}to{stroke-opacity:0;stroke-width:12}}@keyframes new-dock-link-flash{0%{transform:translateY(-2px);box-shadow:0 16px 40px #0206175c,0 0 42px #fb923c47}to{transform:translateY(0)}}@keyframes trace-wire-pulse{0%,to{stroke-opacity:.78}50%{stroke-opacity:1}}@keyframes trace-halo-pulse{0%,to{stroke-opacity:.34}50%{stroke-opacity:.86}}@keyframes tooltip-rise{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes invalid-drop-jolt{0%,to{transform:translate(0)}28%{transform:translate(-3px)}56%{transform:translate(3px)}}@keyframes invalid-drop-ping{0%{opacity:1;transform:translate(-50%,-50%) scale(.72)}72%{opacity:.74;transform:translate(-50%,-50%) scale(1.35)}to{opacity:0;transform:translate(-50%,-50%) scale(1.8)}}@keyframes node-breathe{50%{transform:scale(1.035)}}@keyframes node-focus-breathe{0%,to{transform:scale(1)}50%{transform:scale(1.055)}}@keyframes trace-source-pulse{50%{transform:scale(1.04);box-shadow:0 0 0 3px #facc15db,0 0 0 22px #facc1514,0 0 64px #facc1557}}@keyframes trace-source-orbit{to{transform:rotate(360deg)}}@keyframes trace-reacting-pulse{50%{transform:scale(1.045);box-shadow:0 0 0 3px #22c55ead,0 0 62px #22c55e4d}}@keyframes segment-focus-sweep{0%,to{stroke-opacity:.82}50%{stroke-opacity:1}}@keyframes snap-pulse{50%{filter:brightness(1.16)}}@keyframes port-focus-pulse{50%{transform:translateY(-1px);box-shadow:0 0 0 2px #facc158f,0 0 26px #facc153d}}.connection-editor__link-list{order:-2}.connection-editor__item-title{color:var(--pce-fg);font-weight:650;overflow-wrap:anywhere}.connection-editor__link-list .connection-editor__item{border:0;border-radius:6px;background:transparent;border-bottom:1px solid var(--pce-outline);box-shadow:none;padding:12px 4px;gap:4px}.connection-editor__link-list .connection-editor__item--active{background:var(--pce-surface-strong)}.connection-editor--guided .connection-editor__widget-list,.connection-editor--guided .connection-editor__link-list .connection-editor__item-copy{display:none}.connection-editor__filters-section,.connection-editor__side-panel--contextual .connection-editor__inspector{order:-3}.connection-editor__working-note{order:4}.connection-editor__draft{order:-5}.connection-editor--guided .connection-editor__segment-kind-legend{background:var(--pce-surface-strong);color:var(--pce-fg)}.connection-editor__node-caption{position:absolute;left:50%;top:-16px;transform:translate(-50%,-100%);color:var(--pce-fg);background:var(--pce-surface-base);border:1px solid var(--pce-outline);border-radius:8px;padding:6px 8px;line-height:1.3;font-weight:650;white-space:normal;overflow-wrap:anywhere}.connection-editor__node-core{padding:8px}.connection-editor__node-icon{font-size:30px}.connection-editor__node-subtitle{display:none}.connection-editor__wire:not(.connection-editor__wire--halo){stroke:var(--pce-primary);stroke-opacity:.85;stroke-width:2;vector-effect:non-scaling-stroke;stroke-dasharray:none;animation:none;filter:none}.connection-editor__wire--state:not(.connection-editor__wire--halo){stroke:var(--pce-tertiary)}.connection-editor__wire--state-read:not(.connection-editor__wire--halo){stroke:var(--pce-secondary)}.connection-editor__wire--error:not(.connection-editor__wire--halo){stroke:var(--pce-error)}.connection-editor__wire--active:not(.connection-editor__wire--halo),.connection-editor__wire--trace:not(.connection-editor__wire--halo){stroke-width:3;stroke-opacity:1;stroke-dasharray:12 6;animation:connection-flow 1.8s linear infinite}.connection-editor__wire--mode-dimmed:not(.connection-editor__wire--halo){stroke-opacity:.16}.connection-editor__header{gap:12px}.connection-editor__mode-frame{min-width:0;flex:1 1 300px}.connection-editor__header-actions{flex:0 1 auto}@media(max-width:1200px){.connection-editor__header{flex-wrap:wrap}}@media(prefers-reduced-motion:reduce){.connection-editor__viewport,.connection-editor__node,.connection-editor__node-ring,.connection-editor__node-core,.connection-editor__node-segment,.connection-editor__dock-link,.connection-editor__port{transition-duration:1ms}.connection-editor__wire,.connection-editor__wire--state,.connection-editor__wire--projection,.connection-editor__wire--state-read,.connection-editor__wire--event-propagation,.connection-editor__wire--halo.connection-editor__wire--active,.connection-editor__wire--trace,.connection-editor__wire--halo.connection-editor__wire--trace,.connection-editor__wire-endpoint,.connection-editor__connection-tooltip,.connection-editor__dock-link--new,.connection-editor__trace-overlay,.connection-editor__trace-overlay-step,.connection-editor__drag-wire,.connection-editor__drag-wire--halo,.connection-editor__drag-endpoint,.connection-editor__drag-tip,.connection-editor__node-ring,.connection-editor__node--connection-focus .connection-editor__node-ring,.connection-editor__node--trace-source .connection-editor__node-ring,.connection-editor__node--trace-source:before,.connection-editor__node--trace-reacting .connection-editor__node-ring,.connection-editor__node--rotating .connection-editor__node-ring,.connection-editor__node-segment--connection-focus,.connection-editor__node-segment--compatible,.connection-editor__node-segment--invalid-drop,.connection-editor__segment-popover,.connection-editor__port--compatible,.connection-editor__port--invalid-drop,.connection-editor__port--connection-focus,.connection-editor__invalid-drop,.connection-editor__invalid-drop-pulse{animation:none}.connection-editor__wire{stroke-dashoffset:0}.connection-editor__wire--new{stroke-dasharray:10 8;stroke-dashoffset:0;opacity:1}.connection-editor__wire--halo.connection-editor__wire--new{stroke-width:12;stroke-opacity:.16}.connection-editor__wire-endpoint,.connection-editor__drag-endpoint,.connection-editor__node-ring,.connection-editor__node--trace-source:before{transform:none}.connection-editor__invalid-drop-pulse{opacity:0}}@media(max-width:900px){:host{inset:0}.connection-editor{inset:8px;height:calc(100vh - 16px);padding:12px;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(260px,1fr) minmax(150px,30vh) minmax(76px,112px)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(260px,1fr) minmax(150px,30vh) 48px}.connection-editor__header,.connection-editor__stage,.connection-editor__summary,.connection-editor__side-panel,.connection-editor__dock{grid-column:1}.connection-editor__header{align-items:stretch;flex-wrap:wrap;border-radius:18px}.connection-editor__mode-frame{order:3;width:100%;max-width:100%}.connection-editor__close{margin-inline-start:auto}.connection-editor__mode-frame:before,.connection-editor__mode-frame:after{content:\"\";position:absolute;z-index:2;inset-block:4px;width:24px;border-radius:999px;pointer-events:none}.connection-editor__mode-frame:before{inset-inline-start:1px;background:linear-gradient(90deg,#080d18f0,#080d1800)}.connection-editor__mode-frame:after{inset-inline-end:1px;background:linear-gradient(270deg,#080d18f0,#080d1800)}.connection-editor__mode-shell{width:100%;max-width:100%;overflow-x:auto;scroll-padding-inline:28px;-webkit-overflow-scrolling:touch}.connection-editor__subtitle{margin-inline-start:auto}.connection-editor__stage{grid-row:2;min-height:260px}.connection-editor__summary{grid-row:2;max-width:calc(100% - 24px)}.connection-editor__side-panel{grid-row:3;max-height:30vh}.connection-editor__dock{grid-row:4;min-height:0}.connection-editor__connection-tooltip{width:min(620px,calc(100% - 24px));max-height:min(260px,calc(100% - 24px));overflow:auto}.connection-editor__dock-tabs{width:100%;min-width:0;min-height:0;flex-wrap:wrap;align-items:flex-start}.connection-editor__dock-tablist{width:100%;flex-wrap:wrap}.connection-editor__dock-count{flex:1 1 100%;margin-inline-start:0}.connection-editor__dock-body{width:100%;min-width:0;max-height:76px;box-sizing:border-box}.connection-editor__dock-body pre{white-space:pre-wrap;overflow-wrap:anywhere}.connection-editor__dock-link{grid-template-columns:1fr;align-items:start}.connection-editor__dock-link-route{flex-wrap:wrap}.connection-editor__dock-link-badges{justify-content:flex-start}.connection-editor__trace-overlay{inset-inline-start:14px;width:min(420px,calc(100% - 28px));min-width:0;transform:none}.connection-editor__drag-tip{width:min(300px,calc(100% - 28px))}.connection-editor__segment-popover{width:min(280px,calc(100% - 28px));max-height:min(280px,calc(100% - 28px))}.connection-editor__minimap{inset-inline-end:10px;inset-block-end:10px;width:132px;height:94px;padding:16px 8px 8px;border-radius:14px}.connection-editor__minimap:before{inset:20px 8px 8px;background-size:18px 18px}.connection-editor__minimap-title{inset-block-start:6px;inset-inline-start:10px;font-size:.54rem}}@media(max-width:640px){.connection-editor{inset:4px;height:calc(100vh - 8px);padding:8px;gap:8px;grid-template-rows:auto minmax(240px,1fr) minmax(140px,28vh) minmax(72px,104px)}.connection-editor--dock-collapsed{grid-template-rows:auto minmax(240px,1fr) minmax(140px,28vh) 46px}.connection-editor__header{gap:8px;padding:10px}.connection-editor__mode-shell{padding:4px;gap:2px}.connection-editor__mode{min-height:28px;gap:4px;padding:0 7px;font-size:.68rem}.connection-editor__mode-glyph{font-size:.64rem}.connection-editor__stage{min-height:240px}.connection-editor__zoom-controls{inset-inline-start:8px;inset-block-start:8px;gap:6px;width:42px}.connection-editor__tool-group,.connection-editor__trace-source{gap:6px;padding:8px;border-radius:12px}.connection-editor__tool-group{grid-template-columns:28px}.connection-editor__tool-group:nth-of-type(2){grid-template-columns:28px}.connection-editor__tool-button{width:28px;height:28px}.connection-editor__arrange-button,.connection-editor__simulate-button{width:30px;min-height:30px}.connection-editor__summary{gap:6px;padding-inline-end:8px}.connection-editor__side-panel{max-height:28vh}.connection-editor__dock{border-radius:16px}.connection-editor__dock-tabs,.connection-editor__dock-tablist{gap:4px}.connection-editor__dock-tab{padding:0 9px}.connection-editor__dock-count{font-size:.62rem}.connection-editor__dock-body{max-height:66px;padding:8px}.connection-editor__minimap{width:116px;height:84px}.connection-editor__segment-row{grid-template-columns:24px minmax(54px,.55fr) minmax(0,1fr) auto}}.connection-editor__ports{width:max-content}.connection-editor__port{font-size:inherit;white-space:normal;overflow:visible;overflow-wrap:anywhere;padding:4px 8px}.connection-editor__header-actions{display:flex;flex-wrap:wrap;gap:8px}.connection-editor__action--primary{background:var(--pce-primary);color:var(--pce-surface-base);font-weight:700}.connection-editor__action:disabled{opacity:.5;cursor:default}.connection-editor__draft{order:-4;padding:16px;border-bottom:1px solid var(--pce-outline)}.connection-editor__draft p,.connection-editor__working-note{font-size:.8rem;line-height:1.55;color:var(--pce-muted)}.connection-editor__working-note{margin:12px 16px}.connection-editor__draft label{display:grid;gap:6px;margin:14px 0;font-size:.82rem}.connection-editor__draft select,.connection-editor__draft input{box-sizing:border-box;width:100%;min-width:0;min-height:40px;padding:8px;color:var(--pce-fg);background:var(--pce-surface-base);border:1px solid var(--pce-outline-strong);border-radius:8px}.connection-editor__draft-review{display:grid;gap:6px;padding:12px 0;font-size:.85rem;overflow-wrap:anywhere}.connection-editor__draft-actions{position:sticky;bottom:0;z-index:4;padding:12px 0;background:var(--pce-surface-base);border-top:1px solid var(--pce-outline)}.connection-editor--draft .connection-editor__filters-section,.connection-editor--draft .connection-editor__link-list{display:none}.connection-editor__draft-status{color:var(--pce-primary)}.connection-editor__advanced{margin:16px 0}.connection-editor__advanced summary{cursor:pointer;font-size:.82rem;padding:10px 0}.connection-editor__node-title{white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:anywhere;line-height:1.25}.connection-editor--presentation{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}.connection-editor--presentation .connection-editor__side-panel,.connection-editor--presentation .connection-editor__summary,.connection-editor--presentation .connection-editor__dock,.connection-editor--presentation .connection-editor__subtitle,.connection-editor--presentation .connection-editor__mode-frame{display:none}.connection-editor--presentation .connection-editor__stage{grid-column:1;grid-row:2}@media(max-width:900px){.connection-editor__header{flex-wrap:wrap}.connection-editor__header-actions{order:2;width:100%}.connection-editor--draft{grid-template-rows:auto minmax(160px,.45fr) minmax(260px,1fr) 46px}.connection-editor--draft .connection-editor__stage{min-height:0}.connection-editor__side-panel{max-height:none}.connection-editor--draft .connection-editor__summary{display:none}}@media(max-width:640px){.connection-editor__eyebrow,.connection-editor__subtitle,.connection-editor__summary,.connection-editor__minimap{display:none}.connection-editor__header{position:relative;padding-right:42px}.connection-editor__close{position:absolute;top:8px;right:8px}.connection-editor__header-actions{flex-wrap:nowrap}.connection-editor__header-actions .connection-editor__action{min-height:36px}.connection-editor__mode-frame{flex-basis:auto}.connection-editor__side-panel{max-height:none}.connection-editor__draft{padding:10px}.connection-editor__stage{overflow:hidden}.connection-editor__connection-tooltip{display:none}.connection-editor__dock{min-width:0}.connection-editor__dock-tablist{min-width:0;overflow-x:auto;max-width:100%}.connection-editor__dock-meta{display:none}}@media(prefers-reduced-motion:reduce){.connection-editor *,.connection-editor *:before,.connection-editor *:after{animation:none!important;transition:none!important}}\n"] }]
10815
11624
  }], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], focusWidget: [{ type: i0.Output, args: ["focusWidget"] }], openPageSettings: [{ type: i0.Output, args: ["openPageSettings"] }], close: [{ type: i0.Output, args: ["close"] }] } });
10816
11625
 
11626
+ const LIMIT_KEYS = ['minColSpan', 'maxColSpan', 'minRowSpan', 'maxRowSpan', 'minHeightPx', 'maxHeightPx'];
11627
+ /** Editor projection of Core canvas constraints; never a second persisted document. */
11628
+ class CanvasConstraintEditorModel {
11629
+ form = new FormArray([]);
11630
+ targets = [];
11631
+ baseline = { widgets: [] };
11632
+ hydrate(page) {
11633
+ this.form.clear({ emitEvent: false });
11634
+ this.targets = [];
11635
+ this.baseline = structuredClone(page);
11636
+ // Missing variants inherit Core's base canvas. Expose their override controls
11637
+ // without creating persisted blocks until a value is actually edited.
11638
+ const scopes = ['base', 'desktop', 'tablet', 'mobile'];
11639
+ for (const scope of scopes) {
11640
+ for (const [key, item] of Object.entries(page.canvas?.items ?? {})) {
11641
+ const widget = page.widgets.find(candidate => candidate.key === key);
11642
+ const override = scope === 'base' ? undefined : page.deviceLayouts?.[scope]?.canvas?.items?.[key];
11643
+ if (!widget || override?.hidden)
11644
+ continue;
11645
+ const constraints = scope === 'base' ? item.constraints : override?.constraints;
11646
+ const size = scope === 'base' ? item.contentSize : override?.contentSize;
11647
+ const seed = {
11648
+ minColSpan: constraints?.minColSpan ?? null, maxColSpan: constraints?.maxColSpan ?? null,
11649
+ minRowSpan: constraints?.minRowSpan ?? null, maxRowSpan: constraints?.maxRowSpan ?? null,
11650
+ minHeightPx: constraints?.minHeightPx ?? null, maxHeightPx: constraints?.maxHeightPx ?? null,
11651
+ heightMode: size === 'auto' ? 'auto' : size ? 'fixed' : 'inherit',
11652
+ heightPx: size && size !== 'auto' ? size.heightPx : null,
11653
+ offsetTopPx: size && size !== 'auto' ? size.offsetTopPx ?? 0 : null,
11654
+ };
11655
+ const authoredTitle = typeof widget.shell?.title === 'string' ? widget.shell.title : '';
11656
+ // This editor has no runtime state projection: identify templates by stable widget key.
11657
+ // Template detection is presentation only, never intent routing or expression evaluation.
11658
+ const title = authoredTitle.includes('${') ? key : authoredTitle;
11659
+ this.targets.push({ key, title: title || key, scope, seed });
11660
+ this.form.push(new FormGroup({
11661
+ minColSpan: new FormControl(seed.minColSpan), maxColSpan: new FormControl(seed.maxColSpan),
11662
+ minRowSpan: new FormControl(seed.minRowSpan), maxRowSpan: new FormControl(seed.maxRowSpan),
11663
+ minHeightPx: new FormControl(seed.minHeightPx), maxHeightPx: new FormControl(seed.maxHeightPx),
11664
+ heightMode: new FormControl(seed.heightMode, { nonNullable: true }),
11665
+ heightPx: new FormControl(seed.heightPx), offsetTopPx: new FormControl(seed.offsetTopPx),
11666
+ }), { emitEvent: false });
11667
+ }
11668
+ }
11669
+ }
11670
+ /** Mutates only the caller's cloned draft, preserving unknown fields and untouched overrides. */
11671
+ apply(page) {
11672
+ for (const group of this.form.controls) {
11673
+ for (const control of Object.values(group.controls)) {
11674
+ const errors = { ...control.errors };
11675
+ delete errors['canvasLimit'];
11676
+ control.setErrors(Object.keys(errors).length ? errors : null, { emitEvent: false });
11677
+ }
11678
+ }
11679
+ for (const [index, target] of this.targets.entries()) {
11680
+ const values = this.form.at(index).getRawValue();
11681
+ const invalidKeys = LIMIT_KEYS.filter(key => values[key] !== null && (!Number.isSafeInteger(values[key]) || values[key] < 1));
11682
+ if (invalidKeys.length) {
11683
+ return this.fail(index, 'integer', invalidKeys);
11684
+ }
11685
+ const heightChanged = values.heightMode !== target.seed.heightMode || values.heightPx !== target.seed.heightPx || values.offsetTopPx !== target.seed.offsetTopPx;
11686
+ if (!heightChanged && !LIMIT_KEYS.some(key => values[key] !== target.seed[key]))
11687
+ continue;
11688
+ const baseItem = page.canvas?.items[target.key];
11689
+ if (!baseItem)
11690
+ continue;
11691
+ const canvas = target.scope === 'base' ? page.canvas :
11692
+ (((page.deviceLayouts ??= {})[target.scope] ??= {}).canvas ??= {});
11693
+ const item = target.scope === 'base' ? baseItem :
11694
+ ((canvas.items ??= {})[target.key] ??= {});
11695
+ if (heightChanged) {
11696
+ if (values.heightMode === 'inherit')
11697
+ delete item.contentSize;
11698
+ else if (values.heightMode === 'auto')
11699
+ item.contentSize = 'auto';
11700
+ else {
11701
+ if (!Number.isSafeInteger(values.heightPx) || values.heightPx < 160 || !Number.isSafeInteger(values.offsetTopPx ?? 0) || (values.offsetTopPx ?? 0) < 0) {
11702
+ return this.fail(index, 'height', ['heightPx', 'offsetTopPx']);
11703
+ }
11704
+ item.contentSize = { heightPx: values.heightPx, offsetTopPx: values.offsetTopPx ?? 0 };
11705
+ }
11706
+ }
11707
+ const constraints = { ...item.constraints };
11708
+ for (const key of LIMIT_KEYS) {
11709
+ if (values[key] === target.seed[key])
11710
+ continue;
11711
+ if (values[key] === null)
11712
+ delete constraints[key];
11713
+ else
11714
+ constraints[key] = values[key];
11715
+ }
11716
+ if (Object.keys(constraints).length)
11717
+ item.constraints = constraints;
11718
+ else
11719
+ delete item.constraints;
11720
+ }
11721
+ for (const [index, target] of this.targets.entries()) {
11722
+ const base = page.canvas?.items[target.key];
11723
+ if (!base)
11724
+ continue;
11725
+ const canvas = target.scope === 'base' ? undefined : page.deviceLayouts?.[target.scope]?.canvas;
11726
+ const override = canvas?.items?.[target.key];
11727
+ const limits = { ...base.constraints, ...override?.constraints };
11728
+ const minHeight = limits.minHeightPx ?? 160;
11729
+ const maxHeight = limits.maxHeightPx ?? Number.MAX_SAFE_INTEGER;
11730
+ if (minHeight < 160 || maxHeight < minHeight)
11731
+ return this.fail(index, 'height', ['minHeightPx', 'maxHeightPx']);
11732
+ const size = override?.contentSize ?? base.contentSize;
11733
+ if (size && size !== 'auto' && (size.heightPx < minHeight || size.heightPx > maxHeight))
11734
+ return this.fail(index, 'height', ['heightPx', 'minHeightPx', 'maxHeightPx']);
11735
+ const columns = canvas?.columns ?? page.canvas.columns;
11736
+ const minCol = limits.minColSpan ?? 1;
11737
+ const maxCol = limits.maxColSpan ?? columns;
11738
+ const minRow = limits.minRowSpan ?? 1;
11739
+ const maxRow = limits.maxRowSpan ?? Number.MAX_SAFE_INTEGER;
11740
+ if (minCol > columns)
11741
+ return this.fail(index, 'columns', ['minColSpan']);
11742
+ if (minCol > maxCol)
11743
+ return this.fail(index, 'range', ['minColSpan', 'maxColSpan']);
11744
+ if (minRow > maxRow)
11745
+ return this.fail(index, 'range', ['minRowSpan', 'maxRowSpan']);
11746
+ const original = this.baseline.canvas?.items[target.key];
11747
+ const originalOverride = target.scope === 'base' ? undefined : this.baseline.deviceLayouts?.[target.scope]?.canvas?.items?.[target.key];
11748
+ const previous = { ...original?.constraints, ...originalOverride?.constraints };
11749
+ // Compare rendered spans under the original and proposed limits, using the
11750
+ // edited grid width in both. Preserve already-clamped source documents.
11751
+ const rawCol = override?.colSpan ?? base.colSpan;
11752
+ const rawRow = override?.rowSpan ?? base.rowSpan;
11753
+ const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
11754
+ const oldMinCol = Math.max(1, previous.minColSpan || 1);
11755
+ const oldMinRow = Math.max(1, previous.minRowSpan || 1);
11756
+ const oldCol = clamp(rawCol, oldMinCol, Math.min(columns, Math.max(oldMinCol, previous.maxColSpan || columns)));
11757
+ const oldRow = clamp(rawRow, oldMinRow, Math.max(oldMinRow, previous.maxRowSpan || Number.MAX_SAFE_INTEGER));
11758
+ const widthChanged = LIMIT_KEYS.slice(0, 2).some(key => limits[key] !== previous[key]);
11759
+ const heightChanged = LIMIT_KEYS.slice(2, 4).some(key => limits[key] !== previous[key]);
11760
+ const oldWidthValid = oldMinCol <= columns && oldMinCol <= (previous.maxColSpan ?? columns);
11761
+ const oldHeightValid = oldMinRow <= (previous.maxRowSpan ?? Number.MAX_SAFE_INTEGER);
11762
+ if (widthChanged && oldWidthValid && oldCol !== clamp(rawCol, minCol, Math.min(maxCol, columns))) {
11763
+ return this.fail(index, 'size', ['minColSpan', 'maxColSpan']);
11764
+ }
11765
+ if (heightChanged && oldHeightValid && oldRow !== clamp(rawRow, minRow, maxRow)) {
11766
+ return this.fail(index, 'size', ['minRowSpan', 'maxRowSpan']);
11767
+ }
11768
+ }
11769
+ return null;
11770
+ }
11771
+ fail(index, code, keys) {
11772
+ for (const key of keys) {
11773
+ const control = this.form.at(index).controls[key];
11774
+ control.setErrors({ ...control.errors, canvasLimit: code }, { emitEvent: false });
11775
+ }
11776
+ return { index, code };
11777
+ }
11778
+ displayedSize(index, columns) {
11779
+ const target = this.targets[index];
11780
+ const base = target && this.baseline.canvas?.items[target.key];
11781
+ if (!base)
11782
+ return { width: 1, height: 1 };
11783
+ const override = target.scope === 'base' ? undefined : this.baseline.deviceLayouts?.[target.scope]?.canvas?.items?.[target.key];
11784
+ const limits = { ...base.constraints, ...override?.constraints };
11785
+ const minCol = Math.max(1, limits.minColSpan || 1);
11786
+ const minRow = Math.max(1, limits.minRowSpan || 1);
11787
+ return {
11788
+ width: Math.max(minCol, Math.min(override?.colSpan ?? base.colSpan, columns, Math.max(minCol, limits.maxColSpan || columns))),
11789
+ height: Math.max(minRow, Math.min(override?.rowSpan ?? base.rowSpan, Math.max(minRow, limits.maxRowSpan || Number.MAX_SAFE_INTEGER))),
11790
+ };
11791
+ }
11792
+ }
11793
+
10817
11794
  const LAYOUT_PRESET_ENTRIES = Object.values(BUILTIN_PAGE_LAYOUT_PRESETS).map((preset) => ({
10818
11795
  id: preset.id,
10819
11796
  label: preset.label,
@@ -10870,23 +11847,46 @@ class DynamicPageConfigEditorComponent {
10870
11847
  contextError = signal(null, ...(ngDevMode ? [{ debugName: "contextError" }] : /* istanbul ignore next */ []));
10871
11848
  stateError = signal(null, ...(ngDevMode ? [{ debugName: "stateError" }] : /* istanbul ignore next */ []));
10872
11849
  deviceError = signal(null, ...(ngDevMode ? [{ debugName: "deviceError" }] : /* istanbul ignore next */ []));
11850
+ canvasLimits = new CanvasConstraintEditorModel();
11851
+ limitsTargetControl = new FormControl(0, { nonNullable: true });
11852
+ limitsError = signal(null, ...(ngDevMode ? [{ debugName: "limitsError" }] : /* istanbul ignore next */ []));
11853
+ limitsErrorMatcher = { isErrorState: (control) => !!control?.invalid };
10873
11854
  injectedData = inject(SETTINGS_PANEL_DATA$1, { optional: true });
11855
+ isHostedInSettingsPanel = this.injectedData != null;
10874
11856
  i18n = inject(PraxisI18nService);
10875
11857
  pageInput = null;
10876
11858
  snapshot = '';
11859
+ stackedDevices = new Set();
11860
+ activeDevice = signal('mobile', ...(ngDevMode ? [{ debugName: "activeDevice" }] : /* istanbul ignore next */ []));
10877
11861
  hydrating = false;
10878
- tabOrder = ['canvas', 'grouping', 'devices', 'context', 'state'];
11862
+ renderedCandidate = null;
11863
+ renderedValidationFingerprint = '';
11864
+ tabOrder = ['canvas', 'grouping', 'devices', 'context', 'state', 'limits'];
10879
11865
  constructor() {
10880
- merge(this.canvasColumnsControl.valueChanges, this.canvasRowUnitControl.valueChanges, this.canvasGapControl.valueChanges, this.canvasAutoRowsControl.valueChanges, this.layoutPresetControl.valueChanges, this.themePresetControl.valueChanges, this.shellPresetControl.valueChanges, this.contextControl.valueChanges, this.stateControl.valueChanges, this.groupingNodes.valueChanges, this.deviceForms.desktop.valueChanges, this.deviceForms.tablet.valueChanges, this.deviceForms.mobile.valueChanges).pipe(takeUntilDestroyed()).subscribe(() => { if (!this.hydrating)
11866
+ afterEveryRender({ read: () => this.refreshRenderedValidation() });
11867
+ merge(this.canvasLimits.form.valueChanges, this.canvasColumnsControl.valueChanges, this.canvasRowUnitControl.valueChanges, this.canvasGapControl.valueChanges, this.canvasAutoRowsControl.valueChanges, this.layoutPresetControl.valueChanges, this.themePresetControl.valueChanges, this.shellPresetControl.valueChanges, this.contextControl.valueChanges, this.stateControl.valueChanges, this.groupingNodes.valueChanges, this.deviceForms.desktop.valueChanges, this.deviceForms.tablet.valueChanges, this.deviceForms.mobile.valueChanges).pipe(takeUntilDestroyed()).subscribe(() => { if (!this.hydrating)
10881
11868
  this.refreshPanelState(); });
10882
11869
  }
10883
11870
  ngOnInit() { this.hydrate(); }
10884
11871
  get selectedTabIndex() { return this.tabOrder.indexOf(this.activeTab()); }
10885
11872
  get isConfigurationValid() { return this.isValid$.value; }
10886
11873
  get hasPendingChanges() { return this.isDirty$.value; }
10887
- get validationStateLabel() { const issue = this.canvasError() || this.groupingError() || this.deviceError() || this.contextError() || this.stateError(); if (issue)
11874
+ get validationStateLabel() { const issue = this.limitsError() || this.canvasError() || this.groupingError() || this.deviceError() || this.contextError() || this.stateError(); if (issue)
10888
11875
  return this.tx('editor.status.validationIssue', 'Review: {{issue}}').replace('{{issue}}', issue); return this.isValid$.value ? this.tx('editor.status.valid', 'Valid configuration') : this.tx('editor.status.review', 'Review errors before saving'); }
10889
11876
  get dirtyStateLabel() { return this.hasPendingChanges ? this.tx('editor.status.pending', 'Pending changes') : this.tx('editor.status.clean', 'No changes'); }
11877
+ get nonLimitError() {
11878
+ return this.canvasError() || this.groupingError() || this.deviceError() || this.contextError() || this.stateError();
11879
+ }
11880
+ get limitsSummaryError() {
11881
+ const message = this.limitsError();
11882
+ const form = this.canvasLimits.form.at(this.limitsTargetControl.value);
11883
+ // A rendered-height diagnostic is already attached to the visible height field.
11884
+ // Keep the summary for a different target or a field hidden by the current mode.
11885
+ return form?.controls.heightMode.value === 'fixed'
11886
+ && this.limitsUseContentHeight()
11887
+ && form.controls.heightPx.getError('canvasLimit') === 'rendered-height'
11888
+ ? null : message;
11889
+ }
10890
11890
  get selectedLayoutPresetLabel() { return this.tx('editor.summary.layout', 'Layout: {{label}}').replace('{{label}}', this.lookupPresetLabel(this.layoutPresetEntries, this.layoutPresetControl.value, this.tx('editor.fallback.manual', 'manual'))); }
10891
11891
  get selectedThemePresetLabel() { return this.tx('editor.summary.theme', 'Theme: {{label}}').replace('{{label}}', this.lookupPresetLabel(this.themePresetEntries, this.themePresetControl.value, this.tx('editor.layout.layoutDefault', 'Layout default'))); }
10892
11892
  get selectedLayoutPresetEntry() { return this.layoutPresetEntries.find((entry) => entry.id === this.layoutPresetControl.value) || null; }
@@ -10899,6 +11899,38 @@ class DynamicPageConfigEditorComponent {
10899
11899
  reset() { this.hydrate(); }
10900
11900
  pinRecommendedThemePreset() { const theme = this.recommendedThemePresetEntry; if (theme)
10901
11901
  this.themePresetControl.setValue(theme.id); }
11902
+ get hasCanvasWidgets() { return !!this.currentPageInput()?.widgets.length; }
11903
+ stackDevice(device) {
11904
+ this.hydrating = true;
11905
+ this.stackedDevices.add(device);
11906
+ this.deviceForms[device].patchValue({ columns: 1, autoRows: 'content' }, { emitEvent: false });
11907
+ for (const [index, target] of this.canvasLimits.targets.entries()) {
11908
+ if (target.scope !== device)
11909
+ continue;
11910
+ this.canvasLimits.form.at(index).patchValue({
11911
+ minColSpan: 1, maxColSpan: 1,
11912
+ heightMode: 'auto', heightPx: null, offsetTopPx: null,
11913
+ }, { emitEvent: false });
11914
+ }
11915
+ this.hydrating = false;
11916
+ this.refreshPanelState();
11917
+ }
11918
+ stackedCanvas(device, previous) {
11919
+ const page = this.currentPageInput() || { widgets: [] };
11920
+ const source = this.completeCanvasItems(page);
11921
+ const widgetOverrides = this.parseWidgetOverrides(this.deviceForms[device].controls.widgetOverrides.value);
11922
+ const items = this.clone(previous.items || {});
11923
+ let row = 1;
11924
+ for (const [key] of Object.entries(source).sort(([aKey, a], [bKey, b]) => a.row - b.row || a.col - b.col || aKey.localeCompare(bKey))) {
11925
+ if (!page.widgets.some(widget => widget.key === key) || items[key]?.hidden || widgetOverrides?.[key]?.hidden)
11926
+ continue;
11927
+ const rowSpan = items[key]?.rowSpan ?? source[key].rowSpan;
11928
+ items[key] = { ...items[key], col: 1, row, colSpan: 1, rowSpan, contentSize: 'auto',
11929
+ constraints: { ...items[key]?.constraints, minColSpan: 1, maxColSpan: 1 } };
11930
+ row += rowSpan;
11931
+ }
11932
+ return { ...previous, items };
11933
+ }
10902
11934
  addGroupingNode(seed) { this.groupingNodes.push(this.createGroupingNodeGroup(seed)); this.refreshPanelState(); }
10903
11935
  removeGroupingNode(index) { this.groupingNodes.removeAt(index); this.refreshPanelState(); }
10904
11936
  layoutPresetSummary(preset) { return this.tx('editor.preview.layoutSummary', '{{columns}} columns · {{gap}}').replace('{{columns}}', String(preset.defaultColumns)).replace('{{gap}}', preset.defaultGap || this.tx('editor.preview.defaultGap', 'default gap')); }
@@ -10915,9 +11947,104 @@ class DynamicPageConfigEditorComponent {
10915
11947
  inheritedThemeHelp(theme) { return this.themePresetControl.value ? theme.description : this.tx('editor.preview.inheritedThemeHelp', '{{description}} This theme is inherited automatically while the theme field stays on layout default.').replace('{{description}}', theme.description); }
10916
11948
  groupLabel(index) { return this.tx('editor.grouping.groupN', 'Group {{index}}').replace('{{index}}', String(index + 1)); }
10917
11949
  tx(key, fallback) { return resolvePraxisPageBuilderText(this.i18n, key, fallback); }
11950
+ limitsTargetLabel(index) {
11951
+ const target = this.canvasLimits.targets[index];
11952
+ if (!target)
11953
+ return '';
11954
+ const scope = target.scope === 'base' ? this.tx('editor.limits.base', 'Base layout') : this.deviceLabels[target.scope];
11955
+ return target.title + ' · ' + scope;
11956
+ }
11957
+ limitFieldError(control) {
11958
+ const code = control.getError('canvasLimit');
11959
+ if (code === 'rendered-height')
11960
+ return this.limitsError() || '';
11961
+ if (code === 'height')
11962
+ return this.tx('editor.height.error', 'Use a whole height of at least 160px, within the effective limits, and a non-negative top inset.');
11963
+ return code ? this.tx('editor.limits.errors.' + code, '') : '';
11964
+ }
11965
+ draftFingerprint() {
11966
+ return JSON.stringify([
11967
+ this.canvasColumnsControl.value, this.canvasRowUnitControl.value,
11968
+ this.canvasGapControl.value, this.canvasAutoRowsControl.value,
11969
+ this.layoutPresetControl.value, this.themePresetControl.value, this.shellPresetControl.value,
11970
+ this.contextControl.value, this.stateControl.value, this.groupingNodes.getRawValue(),
11971
+ this.deviceKeys.map(key => this.deviceForms[key].getRawValue()),
11972
+ this.canvasLimits.form.getRawValue(), [...this.stackedDevices],
11973
+ ]);
11974
+ }
11975
+ limitsGeometryLabel() {
11976
+ const target = this.canvasLimits.targets[this.limitsTargetControl.value];
11977
+ if (!target)
11978
+ return '';
11979
+ const columns = target.scope === 'base' ? this.canvasColumnsControl.value : this.deviceForms[target.scope].controls.columns.value ?? this.canvasColumnsControl.value;
11980
+ const size = this.canvasLimits.displayedSize(this.limitsTargetControl.value, columns);
11981
+ return this.tx('editor.limits.geometry', 'Current size: {{width}} columns × {{height}} rows. Grid: {{columns}} columns.')
11982
+ .replace('{{width}}', String(size.width))
11983
+ .replace('{{height}}', String(size.height))
11984
+ .replace('{{columns}}', String(columns));
11985
+ }
11986
+ limitsUseContentHeight() {
11987
+ const target = this.canvasLimits.targets[this.limitsTargetControl.value];
11988
+ const form = target && target.scope !== 'base' ? this.deviceForms[target.scope] : undefined;
11989
+ return (form?.controls.autoRows.value || this.canvasAutoRowsControl.value) === 'content';
11990
+ }
11991
+ applyCanvasLimits(page, reportErrors) {
11992
+ this.renderedCandidate = null;
11993
+ const issue = this.canvasLimits.apply(page);
11994
+ if (!issue) {
11995
+ this.limitsError.set(null);
11996
+ // Optional transient runtime validation supplied through SETTINGS_PANEL_DATA.
11997
+ // The canonical output remains the page only, never the validator/measurements.
11998
+ this.renderedCandidate = page;
11999
+ const renderedIssue = this.readRenderedIssue(page);
12000
+ this.renderedValidationFingerprint = JSON.stringify(renderedIssue);
12001
+ if (!renderedIssue)
12002
+ return page;
12003
+ const index = this.canvasLimits.targets.findIndex(target => target.key === renderedIssue.widgetKey && target.scope === renderedIssue.scope);
12004
+ this.limitsError.set((index >= 0 ? this.limitsTargetLabel(index) + ': ' : '') + renderedIssue.message);
12005
+ if (index >= 0) {
12006
+ const control = this.canvasLimits.form.at(index).controls.heightPx;
12007
+ control.setErrors({ ...control.errors, canvasLimit: 'rendered-height' }, { emitEvent: false });
12008
+ if (reportErrors)
12009
+ this.limitsTargetControl.setValue(index);
12010
+ }
12011
+ if (reportErrors) {
12012
+ this.activeTab.set('limits');
12013
+ this.isValid$.next(false);
12014
+ }
12015
+ return null;
12016
+ }
12017
+ const messages = {
12018
+ height: this.tx('editor.height.error', 'Use a whole height of at least 160px, within the effective limits, and a non-negative top inset.'),
12019
+ integer: this.tx('editor.limits.errors.integer', 'Use positive whole numbers or leave blank.'),
12020
+ range: this.tx('editor.limits.errors.range', 'The minimum cannot exceed the maximum, including inherited limits.'),
12021
+ columns: this.tx('editor.limits.errors.columns', 'The minimum width exceeds the grid columns.'),
12022
+ size: this.tx('editor.limits.errors.size', 'The current size is outside these limits. Resize the widget first, or review the limits.'),
12023
+ };
12024
+ this.limitsError.set(this.limitsTargetLabel(issue.index) + ': ' + messages[issue.code]);
12025
+ if (reportErrors) {
12026
+ this.limitsTargetControl.setValue(issue.index);
12027
+ this.activeTab.set('limits');
12028
+ this.isValid$.next(false);
12029
+ }
12030
+ return null;
12031
+ }
12032
+ readRenderedIssue(page) {
12033
+ const validate = this.injectedData?.validateRenderedCanvas;
12034
+ return typeof validate === 'function' ? validate(page) : null;
12035
+ }
12036
+ refreshRenderedValidation() {
12037
+ if (this.hydrating || !this.renderedCandidate)
12038
+ return;
12039
+ const fingerprint = JSON.stringify(this.readRenderedIssue(this.renderedCandidate));
12040
+ // Read after layout, but rebuild forms/status only when the diagnostic
12041
+ // changes. Measurements remain transient and never dirty the document.
12042
+ if (fingerprint !== this.renderedValidationFingerprint)
12043
+ this.refreshPanelState();
12044
+ }
10918
12045
  createDeviceVariantForm() { return new FormGroup({ columns: new FormControl(null, { validators: [Validators.min(1), integerColumnValidator] }), rowUnit: new FormControl('', { nonNullable: true }), gap: new FormControl('', { nonNullable: true }), autoRows: new FormControl('', { nonNullable: true }), groupingOverrides: new FormControl('', { nonNullable: true }), widgetOverrides: new FormControl('', { nonNullable: true }) }); }
10919
12046
  createGroupingNodeGroup(seed) { const label = seed && 'label' in seed && typeof seed.label === 'string' ? seed.label : ''; return new FormGroup({ kind: new FormControl(seed?.kind || 'section', { nonNullable: true }), id: new FormControl(seed?.id || '', { nonNullable: true, validators: [Validators.required] }), label: new FormControl(label, { nonNullable: true }), widgetKeysCsv: new FormControl(this.stringifyGroupingWidgetKeys(seed), { nonNullable: true }), layout: new FormControl(seed?.kind === 'section' ? seed.layout || '' : '', { nonNullable: true }), emphasis: new FormControl(seed?.kind === 'hero' ? seed.emphasis || '' : '', { nonNullable: true }), side: new FormControl(seed?.kind === 'rail' ? seed.side || 'right' : 'right', { nonNullable: true }), tabsJson: new FormControl(seed?.kind === 'tabs' ? JSON.stringify(seed.tabs || [], null, 2) : '', { nonNullable: true }) }); }
10920
- hydrate() { const page = this.currentPageInput() || { widgets: [] }; const canvas = page.canvas || this.defaultCanvas(); const context = this.clone(page.context || {}); this.hydrating = true; this.canvasColumnsControl.setValue(canvas.columns || 12, { emitEvent: false }); this.canvasRowUnitControl.setValue(canvas.rowUnit || '80px', { emitEvent: false }); this.canvasGapControl.setValue(canvas.gap || '16px', { emitEvent: false }); this.canvasAutoRowsControl.setValue(canvas.autoRows || 'fixed', { emitEvent: false }); this.layoutPresetControl.setValue(page.layoutPreset || '', { emitEvent: false }); this.themePresetControl.setValue(page.themePreset || '', { emitEvent: false }); this.shellPresetControl.setValue(this.extractShellPreset(context) || this.pagePreset || '', { emitEvent: false }); this.replaceGroupingNodes(page.grouping); this.contextControl.setValue(this.stringify(this.removeShellPresetFromContext(context)), { emitEvent: false }); this.stateControl.setValue(this.stringify(page.state || {}), { emitEvent: false }); for (const device of this.deviceKeys) {
12047
+ hydrate() { this.stackedDevices.clear(); const page = this.currentPageInput() || { widgets: [] }; const canvas = page.canvas || this.defaultCanvas(); const context = this.clone(page.context || {}); this.hydrating = true; this.canvasLimits.hydrate(page); this.limitsTargetControl.setValue(0, { emitEvent: false }); this.limitsError.set(null); this.canvasColumnsControl.setValue(canvas.columns || 12, { emitEvent: false }); this.canvasRowUnitControl.setValue(canvas.rowUnit || '80px', { emitEvent: false }); this.canvasGapControl.setValue(canvas.gap || '16px', { emitEvent: false }); this.canvasAutoRowsControl.setValue(canvas.autoRows || 'fixed', { emitEvent: false }); this.layoutPresetControl.setValue(page.layoutPreset || '', { emitEvent: false }); this.themePresetControl.setValue(page.themePreset || '', { emitEvent: false }); this.shellPresetControl.setValue(this.extractShellPreset(context) || this.pagePreset || '', { emitEvent: false }); this.replaceGroupingNodes(page.grouping); this.contextControl.setValue(this.stringify(this.removeShellPresetFromContext(context)), { emitEvent: false }); this.stateControl.setValue(this.stringify(page.state || {}), { emitEvent: false }); for (const device of this.deviceKeys) {
10921
12048
  const variant = page.deviceLayouts?.[device];
10922
12049
  this.deviceForms[device].setValue({ columns: variant?.canvas?.columns ?? null, rowUnit: variant?.canvas?.rowUnit || '', gap: variant?.canvas?.gap || '', autoRows: variant?.canvas?.autoRows || '', groupingOverrides: this.stringify(variant?.groupingOverrides || []), widgetOverrides: this.stringify(variant?.widgetOverrides || {}) }, { emitEvent: false });
10923
12050
  } this.canvasError.set(null); this.groupingError.set(null); this.deviceError.set(null); this.contextError.set(null); this.stateError.set(null); this.hydrating = false; this.refreshPanelState(true); }
@@ -10928,7 +12055,7 @@ class DynamicPageConfigEditorComponent {
10928
12055
  return null; this.groupingError.set(null); const context = this.parseRecordJson(this.contextControl.value, {}); if (context === null)
10929
12056
  return this.invalidate('context', this.contextError, reportErrors, this.tx('editor.errors.contextInvalid', 'Invalid context')); this.contextError.set(null); const state = this.parseRecordJson(this.stateControl.value, {}); if (state === null)
10930
12057
  return this.invalidate('state', this.stateError, reportErrors, this.tx('editor.errors.stateInvalid', 'Invalid state')); this.stateError.set(null); const deviceLayouts = this.buildDeviceLayouts(reportErrors, next.deviceLayouts); if (deviceLayouts === null)
10931
- return null; next.canvas = { ...(next.canvas || this.defaultCanvas()), mode: 'grid', columns: this.canvasColumnsControl.value, rowUnit: this.canvasRowUnitControl.value.trim() || '80px', gap: this.canvasGapControl.value.trim() || '16px', autoRows: this.canvasAutoRowsControl.value, items: next.canvas?.items || {} }; next.layoutPreset = this.layoutPresetControl.value.trim() || undefined; next.themePreset = this.themePresetControl.value.trim() || undefined; next.grouping = grouping.length ? grouping : undefined; next.deviceLayouts = Object.keys(deviceLayouts).length ? deviceLayouts : undefined; next.context = this.applyShellPresetToContext(context, this.shellPresetControl.value.trim()); next.state = Object.keys(state).length ? state : undefined; return next; }
12058
+ return null; next.canvas = { ...(next.canvas || this.defaultCanvas()), mode: 'grid', columns: this.canvasColumnsControl.value, rowUnit: this.canvasRowUnitControl.value.trim() || '80px', gap: this.canvasGapControl.value.trim() || '16px', autoRows: this.canvasAutoRowsControl.value, items: this.completeCanvasItems(next) }; next.layoutPreset = this.layoutPresetControl.value.trim() || undefined; next.themePreset = this.themePresetControl.value.trim() || undefined; next.grouping = grouping.length ? grouping : undefined; next.deviceLayouts = Object.keys(deviceLayouts).length ? deviceLayouts : undefined; next.context = this.applyShellPresetToContext(context, this.shellPresetControl.value.trim()); next.state = Object.keys(state).length ? state : undefined; return this.applyCanvasLimits(next, reportErrors); }
10932
12059
  buildGrouping(reportErrors) { const grouping = []; const seenIds = new Set(); for (const group of this.groupingNodes.controls) {
10933
12060
  const kind = group.controls.kind.value;
10934
12061
  const id = group.controls.id.value.trim();
@@ -10958,6 +12085,18 @@ class DynamicPageConfigEditorComponent {
10958
12085
  }
10959
12086
  grouping.push({ kind: 'rail', id, side: (group.controls.side.value || 'right'), widgetKeys });
10960
12087
  } return grouping; }
12088
+ /** A grid must place every existing widget before replacing the flow layout. */
12089
+ completeCanvasItems(page) {
12090
+ const items = { ...(page.canvas?.items || {}) };
12091
+ let row = Math.max(1, ...Object.values(items).map(item => item.row + item.rowSpan));
12092
+ for (const widget of page.widgets) {
12093
+ if (items[widget.key])
12094
+ continue;
12095
+ items[widget.key] = { col: 1, row, colSpan: this.canvasColumnsControl.value, rowSpan: 1 };
12096
+ row += 1;
12097
+ }
12098
+ return items;
12099
+ }
10961
12100
  buildDeviceLayouts(reportErrors, base) { const next = {}; for (const device of this.deviceKeys) {
10962
12101
  const form = this.deviceForms[device];
10963
12102
  if (form.controls.columns.invalid)
@@ -10971,7 +12110,7 @@ class DynamicPageConfigEditorComponent {
10971
12110
  delete baseVariant.canvas;
10972
12111
  delete baseVariant.groupingOverrides;
10973
12112
  delete baseVariant.widgetOverrides;
10974
- const canvas = { ...baseCanvas };
12113
+ const canvas = this.stackedDevices.has(device) ? this.stackedCanvas(device, baseCanvas) : { ...baseCanvas };
10975
12114
  if (form.controls.columns.value != null)
10976
12115
  canvas.columns = form.controls.columns.value;
10977
12116
  else
@@ -10997,11 +12136,15 @@ class DynamicPageConfigEditorComponent {
10997
12136
  this.activeTab.set(tab);
10998
12137
  this.isValid$.next(false);
10999
12138
  } return null; }
11000
- refreshPanelState(resetSnapshot = false) { const current = this.buildCurrentPage(false); const valid = !!current; this.isValid$.next(valid); if (resetSnapshot) {
11001
- this.snapshot = valid ? JSON.stringify(current) : '';
11002
- this.isDirty$.next(false);
11003
- return;
11004
- } this.isDirty$.next((valid ? JSON.stringify(current) : '') !== this.snapshot); }
12139
+ refreshPanelState(resetSnapshot = false) {
12140
+ this.renderedCandidate = null;
12141
+ const current = this.buildCurrentPage(false);
12142
+ this.isValid$.next(!!current);
12143
+ const fingerprint = this.draftFingerprint();
12144
+ if (resetSnapshot)
12145
+ this.snapshot = fingerprint;
12146
+ this.isDirty$.next(fingerprint !== this.snapshot);
12147
+ }
11005
12148
  currentPageInput() { return this.parsePage(this.pageInput) || this.parsePage(this.injectedData?.page || null); }
11006
12149
  parsePage(input) { if (!input)
11007
12150
  return null; if (typeof input === 'string') {
@@ -11055,57 +12198,293 @@ class DynamicPageConfigEditorComponent {
11055
12198
  next.ui.shell.preset = shellPreset;
11056
12199
  } return Object.keys(next).length ? next : undefined; }
11057
12200
  lookupPresetLabel(entries, value, fallback) { return entries.find((entry) => entry.id === value)?.label || fallback; }
11058
- defaultCanvas() { return { mode: 'grid', columns: 12, rowUnit: '80px', gap: '16px', autoRows: 'fixed', items: {} }; }
12201
+ defaultCanvas() { return { mode: 'grid', columns: 12, rowUnit: '80px', gap: '16px', autoRows: 'content', items: {} }; }
11059
12202
  clone(value) { if (value == null || typeof value !== 'object')
11060
12203
  return value; return JSON.parse(JSON.stringify(value)); }
11061
12204
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicPageConfigEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11062
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicPageConfigEditorComponent, isStandalone: true, selector: "praxis-dynamic-page-config-editor", inputs: { page: "page", layout: "layout", presets: "presets", pagePreset: "pagePreset" }, outputs: { pageChange: "pageChange" }, providers: [providePraxisPageBuilderI18n()], ngImport: i0, template: `
12205
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicPageConfigEditorComponent, isStandalone: true, selector: "praxis-dynamic-page-config-editor", inputs: { page: "page", layout: "layout", presets: "presets", pagePreset: "pagePreset" }, outputs: { pageChange: "pageChange" }, host: { listeners: { "window:resize": "refreshRenderedValidation()" } }, providers: [providePraxisPageBuilderI18n()], ngImport: i0, template: `
11063
12206
  <div class="editor">
11064
- <div class="head">
12207
+ @if (!isHostedInSettingsPanel) { <div class="head">
11065
12208
  <div>{{ tx('editor.title', 'Page settings') }}</div>
11066
12209
  <button mat-stroked-button type="button" (click)="reset()">{{ tx('editor.reload', 'Reload') }}</button>
11067
- </div>
11068
- <div class="summary" [attr.aria-label]="tx('editor.summaryAria', 'Configuration summary')">
11069
- <span>{{ validationStateLabel }}</span>
11070
- <span>{{ dirtyStateLabel }}</span>
12210
+ </div> }
12211
+ <details class="summary-details">
12212
+ <summary>{{ tx('editor.summaryAria', 'Configuration summary') }}</summary>
12213
+ <div class="summary">
12214
+ @if (!isHostedInSettingsPanel) {
12215
+ <span>{{ validationStateLabel }}</span>
12216
+ <span>{{ dirtyStateLabel }}</span>
12217
+ }
11071
12218
  <span>{{ selectedLayoutPresetLabel }}</span>
11072
12219
  <span>{{ selectedThemePresetLabel }}</span>
11073
12220
  <span>{{ canvasSummaryLabel }}</span>
11074
12221
  </div>
11075
- <mat-tab-group [selectedIndex]="selectedTabIndex" (selectedIndexChange)="onSelectedTabIndexChange($event)">
12222
+ </details>
12223
+ @if (nonLimitError) { <p class="limits-error" role="alert">{{ nonLimitError }}</p> }
12224
+ @if (activeTab() === 'limits' && canvasLimits.targets.length) {
12225
+ <div class="selection-identity" data-testid="canvas-limits-identity">
12226
+ <mat-icon aria-hidden="true">dashboard_customize</mat-icon>
12227
+ <strong>{{ limitsTargetLabel(limitsTargetControl.value) }}</strong>
12228
+ </div>
12229
+ }
12230
+ <mat-tab-group class="editor-tabs" [mat-stretch-tabs]="false" [selectedIndex]="selectedTabIndex" (selectedIndexChange)="onSelectedTabIndexChange($event)">
11076
12231
  <mat-tab [label]="tx('editor.tabs.layout', 'Layout')"><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.cols', 'Columns') }}</mat-label><input matInput type="number" min="1" [formControl]="canvasColumnsControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.rowUnit', 'Row unit') }}</mat-label><input matInput [formControl]="canvasRowUnitControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.gap', 'Gap') }}</mat-label><input matInput [formControl]="canvasGapControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.autoRows', 'Auto rows') }}</mat-label><mat-select [formControl]="canvasAutoRowsControl"><mat-option value="fixed">{{ tx('editor.layout.autoRowsFixed', 'Fixed height') }}</mat-option><mat-option value="content">{{ tx('editor.layout.autoRowsContent', 'Fit content') }}</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.layoutPreset', 'Layout preset') }}</mat-label><mat-select [formControl]="layoutPresetControl"><mat-option value="">{{ tx('editor.layout.none', 'None') }}</mat-option>@for (preset of layoutPresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.themePreset', 'Theme preset') }}</mat-label><mat-select [formControl]="themePresetControl"><mat-option value="">{{ tx('editor.layout.layoutDefault', 'Layout default') }}</mat-option>@for (preset of themePresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.shellPreset', 'Shell visual preset') }}</mat-label><mat-select [formControl]="shellPresetControl"><mat-option value="">{{ tx('editor.layout.none', 'None') }}</mat-option>@for (preset of shellPresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field></div>@if (selectedLayoutPresetEntry; as preset) {<div class="preset-preview"><div class="preset-preview__head"><div><span class="eyebrow">{{ tx('editor.preview.layoutEyebrow', 'Layout template') }} · {{ preset.category }}</span><strong>{{ preset.label }}</strong></div><span>{{ layoutPresetSummary(preset) }}</span></div><p>{{ preset.description || tx('editor.preview.layoutFallbackDescription', 'Page organization preset.') }}</p><div class="preset-preview__chips"><span>{{ slotsSummary(preset) }}</span><span>{{ requiredSlotsSummary(preset) }}</span><span>{{ devicePolicySummary(preset) }}</span></div>@if (recommendedThemePresetEntry; as theme) {<div class="preset-preview__recommendation"><div><span class="eyebrow">{{ tx('editor.preview.inheritedTheme', 'Inherited theme') }}</span><strong>{{ theme.label }}</strong><span>{{ inheritedThemeHelp(theme) }}</span></div><button mat-stroked-button type="button" [disabled]="themePresetControl.value === theme.id" (click)="pinRecommendedThemePreset()">{{ tx('editor.preview.pinTheme', 'Pin theme') }}</button></div>}</div>} @if (selectedThemePresetEntry; as theme) {<div class="preset-preview preset-preview--theme"><div class="preset-preview__head"><div><span class="eyebrow">{{ tx('editor.preview.explicitTheme', 'Explicit theme') }}</span><strong>{{ theme.label }}</strong></div><span>{{ theme.density }} · {{ theme.motion }}</span></div><p>{{ theme.description || tx('editor.preview.themeFallbackDescription', 'Page visual preset.') }}</p><div class="preset-preview__chips"><span>{{ tx('editor.preview.shellChip', 'Shell: {{value}}').replace('{{value}}', theme.shellPreset) }}</span><span>{{ tx('editor.preview.chartsChip', 'Charts: {{value}}').replace('{{value}}', theme.chartThemePreset) }}</span></div></div>}</mat-tab>
11077
- <mat-tab [label]="tx('editor.tabs.grouping', 'Grouping')"><div class="panel"><div class="head"><span>{{ tx('editor.grouping.title', 'Grouping') }}</span><button mat-stroked-button type="button" (click)="addGroupingNode()"><mat-icon>add</mat-icon>{{ tx('editor.grouping.add', 'Add group') }}</button></div>@for (group of groupingNodes.controls; track $index) {<div class="card" [formGroup]="group"><div class="head"><span>{{ groupLabel($index) }}</span><button praxisIconButton="delete" size="compact" type="button" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')" (click)="removeGroupingNode($index)"></button></div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.kind', 'Kind') }}</mat-label><mat-select formControlName="kind"><mat-option value="section">section</mat-option><mat-option value="tabs">tabs</mat-option><mat-option value="hero">hero</mat-option><mat-option value="rail">rail</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.id', 'Id') }}</mat-label><input matInput formControlName="id" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.label', 'Label') }}</mat-label><input matInput formControlName="label" /></mat-form-field>@if (group.controls.kind.value === 'section') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.layout', 'Layout') }}</mat-label><mat-select formControlName="layout"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="stack">stack</mat-option><mat-option value="grid">grid</mat-option><mat-option value="row">row</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'hero') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.emphasis', 'Emphasis') }}</mat-label><mat-select formControlName="emphasis"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="high">high</mat-option><mat-option value="medium">medium</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'rail') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.side', 'Side') }}</mat-label><mat-select formControlName="side"><mat-option value="left">left</mat-option><mat-option value="right">right</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'tabs') {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.tabsJson', 'Tabs (JSON)') }}</mat-label><textarea matInput rows="4" formControlName="tabsJson"></textarea></mat-form-field>} @else {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.widgetKeysCsv', 'Widget keys (comma-separated)') }}</mat-label><input matInput formControlName="widgetKeysCsv" /></mat-form-field>}</div></div>}</div></mat-tab>
11078
- <mat-tab [label]="tx('editor.tabs.devices', 'Device Layouts')"><div class="panel">@for (device of deviceKeys; track device) {<div class="card"><div>{{ deviceLabels[device] }}</div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.columns', 'Columns') }}</mat-label><input matInput type="number" min="1" [formControl]="deviceForms[device].controls.columns" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.rowUnit', 'Row unit') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.rowUnit" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.gap', 'Gap') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.gap" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.autoRows', 'Auto rows') }}</mat-label><mat-select [formControl]="deviceForms[device].controls.autoRows"><mat-option value="">{{ tx('editor.devices.orientationDefault', 'Default') }}</mat-option><mat-option value="fixed">{{ tx('editor.layout.autoRowsFixed', 'Fixed height') }}</mat-option><mat-option value="content">{{ tx('editor.layout.autoRowsContent', 'Fit content') }}</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.groupingOverrides"></textarea></mat-form-field><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.widgetOverrides"></textarea></mat-form-field></div><div class="hint">{{ tx('editor.preview.deviceCanvasHint', 'Existing canvas item overrides are preserved.') }}</div></div>}</div></mat-tab>
12232
+ <mat-tab [label]="tx('editor.tabs.grouping', 'Grouping')"><div class="panel">
12233
+ <p class="hint" data-testid="canvas-grouping-notice">{{ tx('editor.grouping.canvasNotice', 'Canvas positions control this page. Groups below are preserved for flow layouts and do not arrange widgets in the canvas. Use the widget move and resize controls to place them side by side.') }}</p><div class="head"><span>{{ tx('editor.grouping.title', 'Grouping') }}</span><button mat-stroked-button type="button" (click)="addGroupingNode()"><mat-icon>add</mat-icon>{{ tx('editor.grouping.add', 'Add group') }}</button></div>@for (group of groupingNodes.controls; track $index) {<div class="card" [formGroup]="group"><div class="head"><span>{{ groupLabel($index) }}</span><button praxisIconButton="delete" size="compact" type="button" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')" (click)="removeGroupingNode($index)"></button></div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.kind', 'Kind') }}</mat-label><mat-select formControlName="kind"><mat-option value="section">section</mat-option><mat-option value="tabs">tabs</mat-option><mat-option value="hero">hero</mat-option><mat-option value="rail">rail</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.id', 'Id') }}</mat-label><input matInput formControlName="id" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.label', 'Label') }}</mat-label><input matInput formControlName="label" /></mat-form-field>@if (group.controls.kind.value === 'section') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.layout', 'Layout') }}</mat-label><mat-select formControlName="layout"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="stack">stack</mat-option><mat-option value="grid">grid</mat-option><mat-option value="row">row</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'hero') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.emphasis', 'Emphasis') }}</mat-label><mat-select formControlName="emphasis"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="high">high</mat-option><mat-option value="medium">medium</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'rail') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.side', 'Side') }}</mat-label><mat-select formControlName="side"><mat-option value="left">left</mat-option><mat-option value="right">right</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'tabs') {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.tabsJson', 'Tabs (JSON)') }}</mat-label><textarea matInput rows="4" formControlName="tabsJson"></textarea></mat-form-field>} @else {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.widgetKeysCsv', 'Widget keys (comma-separated)') }}</mat-label><input matInput formControlName="widgetKeysCsv" /></mat-form-field>}</div></div>}</div></mat-tab>
12234
+ <mat-tab [label]="tx('editor.tabs.devices', 'Device Layouts')">
12235
+ <div class="panel">
12236
+ <mat-button-toggle-group [value]="activeDevice()" (change)="activeDevice.set($event.value)" [attr.aria-label]="tx('editor.devices.target', 'Device to configure')">
12237
+ @for (device of deviceKeys; track device) {
12238
+ <mat-button-toggle [value]="device">{{ deviceLabels[device] }}</mat-button-toggle>
12239
+ }
12240
+ </mat-button-toggle-group>
12241
+ @for (device of deviceKeys; track device) {
12242
+ @if (activeDevice() === device) {
12243
+ <div class="card">
12244
+ <div class="head"><strong>{{ deviceLabels[device] }}</strong><button mat-stroked-button type="button" [attr.data-testid]="'stack-device-' + device" [disabled]="!hasCanvasWidgets" (click)="stackDevice(device)"><mat-icon aria-hidden="true">view_agenda</mat-icon>{{ tx('editor.devices.stack', 'Stack widgets') }}</button></div>
12245
+ <p class="hint">{{ tx('editor.devices.stackHelp', 'Use full width and natural height, in reading order. This replaces positions and width limits only for this device; other layouts stay unchanged.') }}</p>
12246
+ @if (stackedDevices.has(device)) { <p class="hint" role="status">{{ tx('editor.devices.stacked', 'Single-column layout prepared. Save to apply; Reset undoes the changes.') }}</p> }
12247
+ <div class="grid">
12248
+ <mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.columns', 'Columns') }}</mat-label><input matInput type="number" min="1" [formControl]="deviceForms[device].controls.columns" /></mat-form-field>
12249
+ <mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.gap', 'Gap') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.gap" /></mat-form-field>
12250
+ <mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.autoRows', 'Auto rows') }}</mat-label><mat-select [formControl]="deviceForms[device].controls.autoRows"><mat-option value="">{{ tx('editor.devices.orientationDefault', 'Default') }}</mat-option><mat-option value="fixed">{{ tx('editor.layout.autoRowsFixed', 'Fixed height') }}</mat-option><mat-option value="content">{{ tx('editor.layout.autoRowsContent', 'Fit content') }}</mat-option></mat-select></mat-form-field>
12251
+ </div>
12252
+ <details><summary>{{ tx('editor.devices.advanced', 'Advanced overrides') }}</summary><div class="grid">
12253
+ <mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.rowUnit', 'Row unit') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.rowUnit" /></mat-form-field>
12254
+ <mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.groupingOverrides"></textarea></mat-form-field>
12255
+ <mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.widgetOverrides"></textarea></mat-form-field>
12256
+ </div></details>
12257
+ @if (!stackedDevices.has(device)) { <div class="hint">{{ tx('editor.preview.deviceCanvasHint', 'Existing canvas item overrides are preserved.') }}</div> }
12258
+ </div>
12259
+ }
12260
+ }
12261
+ </div>
12262
+ </mat-tab>
11079
12263
  <mat-tab [label]="tx('editor.tabs.context', 'Context')"><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.context.json', 'Context (JSON)') }}</mat-label><textarea matInput rows="12" [formControl]="contextControl"></textarea></mat-form-field></mat-tab>
11080
12264
  <mat-tab [label]="tx('editor.tabs.state', 'Page State')"><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.state.valuesJson', 'Primary values (JSON)') }}</mat-label><textarea matInput rows="14" [formControl]="stateControl"></textarea></mat-form-field></mat-tab>
12265
+ <mat-tab [label]="tx('editor.limits.tab', 'Widget limits')">
12266
+ <section class="limits-panel" data-testid="canvas-limits-editor">
12267
+ <p class="hint" id="canvas-limits-help">{{ tx('editor.limits.help', 'Limits use grid columns and rows, not pixels. Leave blank for defaults, or to inherit the base limits on a device.') }}</p>
12268
+ @if (canvasLimits.targets.length) {
12269
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12270
+ <mat-label>{{ tx('editor.limits.target', 'Widget and layout') }}</mat-label>
12271
+ <mat-select [formControl]="limitsTargetControl" data-testid="canvas-limits-target">
12272
+ @for (target of canvasLimits.targets; track target.scope + ':' + target.key; let index = $index) {
12273
+ <mat-option [value]="index">{{ limitsTargetLabel(index) }}</mat-option>
12274
+ }
12275
+ </mat-select>
12276
+ </mat-form-field>
12277
+ <p class="hint">{{ limitsGeometryLabel() }}</p>
12278
+ @if (canvasLimits.form.controls[limitsTargetControl.value]; as limitsForm) {
12279
+ <div class="limits-fields" aria-describedby="canvas-limits-help">
12280
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12281
+ <mat-label>{{ tx('editor.limits.minColSpan', 'Minimum width (columns)') }}</mat-label>
12282
+ <input matInput [errorStateMatcher]="limitsErrorMatcher" type="number" min="1" step="1" [formControl]="limitsForm.controls.minColSpan" data-testid="canvas-limit-minColSpan">
12283
+ <mat-error>{{ limitFieldError(limitsForm.controls.minColSpan) }}</mat-error>
12284
+ </mat-form-field>
12285
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12286
+ <mat-label>{{ tx('editor.limits.maxColSpan', 'Maximum width (columns)') }}</mat-label>
12287
+ <input matInput [errorStateMatcher]="limitsErrorMatcher" type="number" min="1" step="1" [formControl]="limitsForm.controls.maxColSpan" data-testid="canvas-limit-maxColSpan">
12288
+ <mat-error>{{ limitFieldError(limitsForm.controls.maxColSpan) }}</mat-error>
12289
+ </mat-form-field>
12290
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12291
+ <mat-label>{{ tx('editor.limits.minRowSpan', 'Minimum height (rows)') }}</mat-label>
12292
+ <input matInput [errorStateMatcher]="limitsErrorMatcher" type="number" min="1" step="1" [formControl]="limitsForm.controls.minRowSpan" data-testid="canvas-limit-minRowSpan">
12293
+ <mat-error>{{ limitFieldError(limitsForm.controls.minRowSpan) }}</mat-error>
12294
+ </mat-form-field>
12295
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12296
+ <mat-label>{{ tx('editor.limits.maxRowSpan', 'Maximum height (rows)') }}</mat-label>
12297
+ <input matInput [errorStateMatcher]="limitsErrorMatcher" type="number" min="1" step="1" [formControl]="limitsForm.controls.maxRowSpan" data-testid="canvas-limit-maxRowSpan">
12298
+ <mat-error>{{ limitFieldError(limitsForm.controls.maxRowSpan) }}</mat-error>
12299
+ </mat-form-field>
12300
+ </div>
12301
+ @if (limitsUseContentHeight()) {
12302
+ <p class="limits-notice"><mat-icon aria-hidden="true">height</mat-icon><span>{{ tx('editor.limits.autoHeight', 'Choose natural height or an adjustable height with internal scrolling.') }}</span></p>
12303
+ <mat-button-toggle-group class="height-modes" [formControl]="limitsForm.controls.heightMode" [attr.aria-label]="tx('editor.height.mode', 'Widget height')" data-testid="canvas-height-mode">
12304
+ <mat-button-toggle value="inherit">{{ tx('editor.height.inherit', 'Default') }}</mat-button-toggle>
12305
+ <mat-button-toggle value="auto">{{ tx('editor.height.auto', 'Automatic') }}</mat-button-toggle>
12306
+ <mat-button-toggle value="fixed">{{ tx('editor.height.fixed', 'Adjustable') }}</mat-button-toggle>
12307
+ </mat-button-toggle-group>
12308
+ @if (limitsForm.controls.heightMode.value === 'fixed') {
12309
+ <div class="limits-fields">
12310
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12311
+ <mat-label>{{ tx('editor.height.value', 'Height (px)') }}</mat-label>
12312
+ <input matInput type="number" min="160" step="1" [errorStateMatcher]="limitsErrorMatcher" [formControl]="limitsForm.controls.heightPx" data-testid="canvas-height-px">
12313
+ <mat-error>{{ limitFieldError(limitsForm.controls.heightPx) }}</mat-error>
12314
+ </mat-form-field>
12315
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12316
+ <mat-label>{{ tx('editor.height.top', 'Top inset (px)') }}</mat-label>
12317
+ <input matInput type="number" min="0" step="1" [errorStateMatcher]="limitsErrorMatcher" [formControl]="limitsForm.controls.offsetTopPx" data-testid="canvas-height-top">
12318
+ <mat-error>{{ limitFieldError(limitsForm.controls.offsetTopPx) }}</mat-error>
12319
+ </mat-form-field>
12320
+ </div>
12321
+ }
12322
+ <div class="limits-fields">
12323
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12324
+ <mat-label>{{ tx('editor.height.min', 'Minimum height (px)') }}</mat-label>
12325
+ <input matInput type="number" min="160" step="1" [errorStateMatcher]="limitsErrorMatcher" [formControl]="limitsForm.controls.minHeightPx" data-testid="canvas-limit-minHeightPx">
12326
+ <mat-error>{{ limitFieldError(limitsForm.controls.minHeightPx) }}</mat-error>
12327
+ </mat-form-field>
12328
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12329
+ <mat-label>{{ tx('editor.height.max', 'Maximum height (px)') }}</mat-label>
12330
+ <input matInput type="number" min="160" step="1" [errorStateMatcher]="limitsErrorMatcher" [formControl]="limitsForm.controls.maxHeightPx" data-testid="canvas-limit-maxHeightPx">
12331
+ <mat-error>{{ limitFieldError(limitsForm.controls.maxHeightPx) }}</mat-error>
12332
+ </mat-form-field>
12333
+ </div>
12334
+ }
12335
+ }
12336
+ @if (limitsSummaryError) { <p class="limits-error" role="alert">{{ limitsSummaryError }}</p> }
12337
+ } @else {
12338
+ <p>{{ tx('editor.limits.empty', 'Add widgets to the canvas to configure their limits.') }}</p>
12339
+ }
12340
+ </section>
12341
+ </mat-tab>
11081
12342
  </mat-tab-group>
11082
12343
  </div>
11083
- `, isInline: true, styles: [":host{display:block;min-width:0;max-width:100%}.editor,.panel{display:grid;gap:16px;min-width:0;max-width:100%;color:var(--md-sys-color-on-surface, currentColor)}mat-tab-group,mat-form-field{min-width:0;max-width:100%}.head,.summary{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.summary{color:var(--md-sys-color-on-surface-variant, currentColor)}.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr))}@media(max-width:600px){.grid{grid-template-columns:minmax(0,1fr)}.head,.summary,.preset-preview__head,.preset-preview__recommendation{align-items:stretch}.head>button,.preset-preview__recommendation button{width:100%}}.card{display:grid;gap:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface-container, transparent));color:var(--md-sys-color-on-surface, currentColor)}.wide{grid-column:1 / -1}.hint{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px}.preset-preview{display:grid;gap:12px;margin-top:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low, transparent) 88%,var(--md-sys-color-primary-container, transparent) 12%)}.preset-preview--theme{background:var(--md-sys-color-surface-container-low, transparent)}.preset-preview__head,.preset-preview__recommendation{display:flex;justify-content:space-between;align-items:center;gap:16px}.preset-preview__head>div,.preset-preview__recommendation>div{display:grid;gap:4px;min-width:0}.preset-preview p{margin:0;color:var(--md-sys-color-on-surface-variant, currentColor);line-height:1.45}.preset-preview__chips{display:flex;flex-wrap:wrap;gap:8px}.preset-preview__chips span{padding:5px 9px;border-radius:999px;background:var(--md-sys-color-surface-container, color-mix(in srgb, currentColor 8%, transparent));color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px;line-height:1.3}.preset-preview__recommendation{padding-top:12px;border-top:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 14%, transparent))}.preset-preview__recommendation span:last-child{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:13px;line-height:1.35}.eyebrow{color:var(--md-sys-color-primary, currentColor);font-size:11px;font-weight:700;letter-spacing:0;text-transform:uppercase}:host ::ng-deep .mat-mdc-tab-body-content{padding-top:12px;box-sizing:border-box}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i7.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
12344
+ `, isInline: true, styles: [":host{display:block;min-width:0;max-width:100%}.height-modes{width:fit-content;max-width:100%;flex-wrap:wrap;margin-bottom:16px}.editor,.panel{display:grid;gap:16px;min-width:0;max-width:100%;color:var(--md-sys-color-on-surface, currentColor)}mat-tab-group,mat-form-field{min-width:0;max-width:100%}.head,.summary{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.summary{color:var(--md-sys-color-on-surface-variant, currentColor)}.selection-identity{position:sticky;top:0;z-index:var(--praxis-layer-sticky-local, 100);display:flex;align-items:flex-start;gap:8px;padding-block:8px;background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface));overflow-wrap:anywhere}.selection-identity mat-icon{flex-shrink:0}:host ::ng-deep .editor-tabs>.mat-mdc-tab-header .mdc-tab{min-width:0;padding-inline:12px}:host ::ng-deep .editor-tabs>.mat-mdc-tab-header .mdc-tab__text-label{max-width:128px;white-space:normal;overflow-wrap:anywhere;text-align:center;letter-spacing:normal;line-height:1.25}.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr))}@media(max-width:600px){.grid{grid-template-columns:minmax(0,1fr)}.head,.summary,.preset-preview__head,.preset-preview__recommendation{align-items:stretch}.head>button,.preset-preview__recommendation button{width:100%}}.card{display:grid;gap:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface-container, transparent));color:var(--md-sys-color-on-surface, currentColor)}.limits-panel{display:grid;gap:8px;min-width:0}.limits-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:4px 16px}.limits-fields mat-form-field{min-width:0}.limits-notice{display:flex;gap:8px;align-items:flex-start;color:var(--md-sys-color-on-surface-variant, currentColor)}.limits-notice mat-icon{flex-shrink:0}.limits-error{color:var(--md-sys-color-error, #b3261e)}.wide{grid-column:1 / -1}.hint{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px}.preset-preview{display:grid;gap:12px;margin-top:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low, transparent) 88%,var(--md-sys-color-primary-container, transparent) 12%)}.preset-preview--theme{background:var(--md-sys-color-surface-container-low, transparent)}.preset-preview__head,.preset-preview__recommendation{display:flex;justify-content:space-between;align-items:center;gap:16px}.preset-preview__head>div,.preset-preview__recommendation>div{display:grid;gap:4px;min-width:0}.preset-preview p{margin:0;color:var(--md-sys-color-on-surface-variant, currentColor);line-height:1.45}.preset-preview__chips{display:flex;flex-wrap:wrap;gap:8px}.preset-preview__chips span{padding:5px 9px;border-radius:999px;background:var(--md-sys-color-surface-container, color-mix(in srgb, currentColor 8%, transparent));color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px;line-height:1.3}.preset-preview__recommendation{padding-top:12px;border-top:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 14%, transparent))}.preset-preview__recommendation span:last-child{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:13px;line-height:1.35}.eyebrow{color:var(--md-sys-color-primary, currentColor);font-size:11px;font-weight:700;letter-spacing:0;text-transform:uppercase}:host ::ng-deep .mat-mdc-tab-body-content{padding-top:12px;box-sizing:border-box}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i3$1.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i3$1.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i10.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i10.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i8.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i8.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11084
12345
  }
11085
12346
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicPageConfigEditorComponent, decorators: [{
11086
12347
  type: Component,
11087
- args: [{ selector: 'praxis-dynamic-page-config-editor', standalone: true, imports: [ReactiveFormsModule, MatButtonModule, MatFormFieldModule, MatIconModule, MatInputModule, MatSelectModule, MatTabsModule, PraxisIconButtonComponent], providers: [providePraxisPageBuilderI18n()], template: `
12348
+ args: [{ selector: 'praxis-dynamic-page-config-editor', standalone: true, imports: [ReactiveFormsModule, MatButtonModule, MatButtonToggleModule, MatFormFieldModule, MatIconModule, MatInputModule, MatSelectModule, MatTabsModule, PraxisIconButtonComponent], providers: [providePraxisPageBuilderI18n()], template: `
11088
12349
  <div class="editor">
11089
- <div class="head">
12350
+ @if (!isHostedInSettingsPanel) { <div class="head">
11090
12351
  <div>{{ tx('editor.title', 'Page settings') }}</div>
11091
12352
  <button mat-stroked-button type="button" (click)="reset()">{{ tx('editor.reload', 'Reload') }}</button>
11092
- </div>
11093
- <div class="summary" [attr.aria-label]="tx('editor.summaryAria', 'Configuration summary')">
11094
- <span>{{ validationStateLabel }}</span>
11095
- <span>{{ dirtyStateLabel }}</span>
12353
+ </div> }
12354
+ <details class="summary-details">
12355
+ <summary>{{ tx('editor.summaryAria', 'Configuration summary') }}</summary>
12356
+ <div class="summary">
12357
+ @if (!isHostedInSettingsPanel) {
12358
+ <span>{{ validationStateLabel }}</span>
12359
+ <span>{{ dirtyStateLabel }}</span>
12360
+ }
11096
12361
  <span>{{ selectedLayoutPresetLabel }}</span>
11097
12362
  <span>{{ selectedThemePresetLabel }}</span>
11098
12363
  <span>{{ canvasSummaryLabel }}</span>
11099
12364
  </div>
11100
- <mat-tab-group [selectedIndex]="selectedTabIndex" (selectedIndexChange)="onSelectedTabIndexChange($event)">
12365
+ </details>
12366
+ @if (nonLimitError) { <p class="limits-error" role="alert">{{ nonLimitError }}</p> }
12367
+ @if (activeTab() === 'limits' && canvasLimits.targets.length) {
12368
+ <div class="selection-identity" data-testid="canvas-limits-identity">
12369
+ <mat-icon aria-hidden="true">dashboard_customize</mat-icon>
12370
+ <strong>{{ limitsTargetLabel(limitsTargetControl.value) }}</strong>
12371
+ </div>
12372
+ }
12373
+ <mat-tab-group class="editor-tabs" [mat-stretch-tabs]="false" [selectedIndex]="selectedTabIndex" (selectedIndexChange)="onSelectedTabIndexChange($event)">
11101
12374
  <mat-tab [label]="tx('editor.tabs.layout', 'Layout')"><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.cols', 'Columns') }}</mat-label><input matInput type="number" min="1" [formControl]="canvasColumnsControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.rowUnit', 'Row unit') }}</mat-label><input matInput [formControl]="canvasRowUnitControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.gap', 'Gap') }}</mat-label><input matInput [formControl]="canvasGapControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.autoRows', 'Auto rows') }}</mat-label><mat-select [formControl]="canvasAutoRowsControl"><mat-option value="fixed">{{ tx('editor.layout.autoRowsFixed', 'Fixed height') }}</mat-option><mat-option value="content">{{ tx('editor.layout.autoRowsContent', 'Fit content') }}</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.layoutPreset', 'Layout preset') }}</mat-label><mat-select [formControl]="layoutPresetControl"><mat-option value="">{{ tx('editor.layout.none', 'None') }}</mat-option>@for (preset of layoutPresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.themePreset', 'Theme preset') }}</mat-label><mat-select [formControl]="themePresetControl"><mat-option value="">{{ tx('editor.layout.layoutDefault', 'Layout default') }}</mat-option>@for (preset of themePresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.shellPreset', 'Shell visual preset') }}</mat-label><mat-select [formControl]="shellPresetControl"><mat-option value="">{{ tx('editor.layout.none', 'None') }}</mat-option>@for (preset of shellPresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field></div>@if (selectedLayoutPresetEntry; as preset) {<div class="preset-preview"><div class="preset-preview__head"><div><span class="eyebrow">{{ tx('editor.preview.layoutEyebrow', 'Layout template') }} · {{ preset.category }}</span><strong>{{ preset.label }}</strong></div><span>{{ layoutPresetSummary(preset) }}</span></div><p>{{ preset.description || tx('editor.preview.layoutFallbackDescription', 'Page organization preset.') }}</p><div class="preset-preview__chips"><span>{{ slotsSummary(preset) }}</span><span>{{ requiredSlotsSummary(preset) }}</span><span>{{ devicePolicySummary(preset) }}</span></div>@if (recommendedThemePresetEntry; as theme) {<div class="preset-preview__recommendation"><div><span class="eyebrow">{{ tx('editor.preview.inheritedTheme', 'Inherited theme') }}</span><strong>{{ theme.label }}</strong><span>{{ inheritedThemeHelp(theme) }}</span></div><button mat-stroked-button type="button" [disabled]="themePresetControl.value === theme.id" (click)="pinRecommendedThemePreset()">{{ tx('editor.preview.pinTheme', 'Pin theme') }}</button></div>}</div>} @if (selectedThemePresetEntry; as theme) {<div class="preset-preview preset-preview--theme"><div class="preset-preview__head"><div><span class="eyebrow">{{ tx('editor.preview.explicitTheme', 'Explicit theme') }}</span><strong>{{ theme.label }}</strong></div><span>{{ theme.density }} · {{ theme.motion }}</span></div><p>{{ theme.description || tx('editor.preview.themeFallbackDescription', 'Page visual preset.') }}</p><div class="preset-preview__chips"><span>{{ tx('editor.preview.shellChip', 'Shell: {{value}}').replace('{{value}}', theme.shellPreset) }}</span><span>{{ tx('editor.preview.chartsChip', 'Charts: {{value}}').replace('{{value}}', theme.chartThemePreset) }}</span></div></div>}</mat-tab>
11102
- <mat-tab [label]="tx('editor.tabs.grouping', 'Grouping')"><div class="panel"><div class="head"><span>{{ tx('editor.grouping.title', 'Grouping') }}</span><button mat-stroked-button type="button" (click)="addGroupingNode()"><mat-icon>add</mat-icon>{{ tx('editor.grouping.add', 'Add group') }}</button></div>@for (group of groupingNodes.controls; track $index) {<div class="card" [formGroup]="group"><div class="head"><span>{{ groupLabel($index) }}</span><button praxisIconButton="delete" size="compact" type="button" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')" (click)="removeGroupingNode($index)"></button></div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.kind', 'Kind') }}</mat-label><mat-select formControlName="kind"><mat-option value="section">section</mat-option><mat-option value="tabs">tabs</mat-option><mat-option value="hero">hero</mat-option><mat-option value="rail">rail</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.id', 'Id') }}</mat-label><input matInput formControlName="id" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.label', 'Label') }}</mat-label><input matInput formControlName="label" /></mat-form-field>@if (group.controls.kind.value === 'section') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.layout', 'Layout') }}</mat-label><mat-select formControlName="layout"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="stack">stack</mat-option><mat-option value="grid">grid</mat-option><mat-option value="row">row</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'hero') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.emphasis', 'Emphasis') }}</mat-label><mat-select formControlName="emphasis"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="high">high</mat-option><mat-option value="medium">medium</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'rail') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.side', 'Side') }}</mat-label><mat-select formControlName="side"><mat-option value="left">left</mat-option><mat-option value="right">right</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'tabs') {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.tabsJson', 'Tabs (JSON)') }}</mat-label><textarea matInput rows="4" formControlName="tabsJson"></textarea></mat-form-field>} @else {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.widgetKeysCsv', 'Widget keys (comma-separated)') }}</mat-label><input matInput formControlName="widgetKeysCsv" /></mat-form-field>}</div></div>}</div></mat-tab>
11103
- <mat-tab [label]="tx('editor.tabs.devices', 'Device Layouts')"><div class="panel">@for (device of deviceKeys; track device) {<div class="card"><div>{{ deviceLabels[device] }}</div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.columns', 'Columns') }}</mat-label><input matInput type="number" min="1" [formControl]="deviceForms[device].controls.columns" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.rowUnit', 'Row unit') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.rowUnit" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.gap', 'Gap') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.gap" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.autoRows', 'Auto rows') }}</mat-label><mat-select [formControl]="deviceForms[device].controls.autoRows"><mat-option value="">{{ tx('editor.devices.orientationDefault', 'Default') }}</mat-option><mat-option value="fixed">{{ tx('editor.layout.autoRowsFixed', 'Fixed height') }}</mat-option><mat-option value="content">{{ tx('editor.layout.autoRowsContent', 'Fit content') }}</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.groupingOverrides"></textarea></mat-form-field><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.widgetOverrides"></textarea></mat-form-field></div><div class="hint">{{ tx('editor.preview.deviceCanvasHint', 'Existing canvas item overrides are preserved.') }}</div></div>}</div></mat-tab>
12375
+ <mat-tab [label]="tx('editor.tabs.grouping', 'Grouping')"><div class="panel">
12376
+ <p class="hint" data-testid="canvas-grouping-notice">{{ tx('editor.grouping.canvasNotice', 'Canvas positions control this page. Groups below are preserved for flow layouts and do not arrange widgets in the canvas. Use the widget move and resize controls to place them side by side.') }}</p><div class="head"><span>{{ tx('editor.grouping.title', 'Grouping') }}</span><button mat-stroked-button type="button" (click)="addGroupingNode()"><mat-icon>add</mat-icon>{{ tx('editor.grouping.add', 'Add group') }}</button></div>@for (group of groupingNodes.controls; track $index) {<div class="card" [formGroup]="group"><div class="head"><span>{{ groupLabel($index) }}</span><button praxisIconButton="delete" size="compact" type="button" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')" (click)="removeGroupingNode($index)"></button></div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.kind', 'Kind') }}</mat-label><mat-select formControlName="kind"><mat-option value="section">section</mat-option><mat-option value="tabs">tabs</mat-option><mat-option value="hero">hero</mat-option><mat-option value="rail">rail</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.id', 'Id') }}</mat-label><input matInput formControlName="id" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.label', 'Label') }}</mat-label><input matInput formControlName="label" /></mat-form-field>@if (group.controls.kind.value === 'section') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.layout', 'Layout') }}</mat-label><mat-select formControlName="layout"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="stack">stack</mat-option><mat-option value="grid">grid</mat-option><mat-option value="row">row</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'hero') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.emphasis', 'Emphasis') }}</mat-label><mat-select formControlName="emphasis"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="high">high</mat-option><mat-option value="medium">medium</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'rail') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.side', 'Side') }}</mat-label><mat-select formControlName="side"><mat-option value="left">left</mat-option><mat-option value="right">right</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'tabs') {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.tabsJson', 'Tabs (JSON)') }}</mat-label><textarea matInput rows="4" formControlName="tabsJson"></textarea></mat-form-field>} @else {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.widgetKeysCsv', 'Widget keys (comma-separated)') }}</mat-label><input matInput formControlName="widgetKeysCsv" /></mat-form-field>}</div></div>}</div></mat-tab>
12377
+ <mat-tab [label]="tx('editor.tabs.devices', 'Device Layouts')">
12378
+ <div class="panel">
12379
+ <mat-button-toggle-group [value]="activeDevice()" (change)="activeDevice.set($event.value)" [attr.aria-label]="tx('editor.devices.target', 'Device to configure')">
12380
+ @for (device of deviceKeys; track device) {
12381
+ <mat-button-toggle [value]="device">{{ deviceLabels[device] }}</mat-button-toggle>
12382
+ }
12383
+ </mat-button-toggle-group>
12384
+ @for (device of deviceKeys; track device) {
12385
+ @if (activeDevice() === device) {
12386
+ <div class="card">
12387
+ <div class="head"><strong>{{ deviceLabels[device] }}</strong><button mat-stroked-button type="button" [attr.data-testid]="'stack-device-' + device" [disabled]="!hasCanvasWidgets" (click)="stackDevice(device)"><mat-icon aria-hidden="true">view_agenda</mat-icon>{{ tx('editor.devices.stack', 'Stack widgets') }}</button></div>
12388
+ <p class="hint">{{ tx('editor.devices.stackHelp', 'Use full width and natural height, in reading order. This replaces positions and width limits only for this device; other layouts stay unchanged.') }}</p>
12389
+ @if (stackedDevices.has(device)) { <p class="hint" role="status">{{ tx('editor.devices.stacked', 'Single-column layout prepared. Save to apply; Reset undoes the changes.') }}</p> }
12390
+ <div class="grid">
12391
+ <mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.columns', 'Columns') }}</mat-label><input matInput type="number" min="1" [formControl]="deviceForms[device].controls.columns" /></mat-form-field>
12392
+ <mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.gap', 'Gap') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.gap" /></mat-form-field>
12393
+ <mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.autoRows', 'Auto rows') }}</mat-label><mat-select [formControl]="deviceForms[device].controls.autoRows"><mat-option value="">{{ tx('editor.devices.orientationDefault', 'Default') }}</mat-option><mat-option value="fixed">{{ tx('editor.layout.autoRowsFixed', 'Fixed height') }}</mat-option><mat-option value="content">{{ tx('editor.layout.autoRowsContent', 'Fit content') }}</mat-option></mat-select></mat-form-field>
12394
+ </div>
12395
+ <details><summary>{{ tx('editor.devices.advanced', 'Advanced overrides') }}</summary><div class="grid">
12396
+ <mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.rowUnit', 'Row unit') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.rowUnit" /></mat-form-field>
12397
+ <mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.groupingOverrides"></textarea></mat-form-field>
12398
+ <mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.widgetOverrides"></textarea></mat-form-field>
12399
+ </div></details>
12400
+ @if (!stackedDevices.has(device)) { <div class="hint">{{ tx('editor.preview.deviceCanvasHint', 'Existing canvas item overrides are preserved.') }}</div> }
12401
+ </div>
12402
+ }
12403
+ }
12404
+ </div>
12405
+ </mat-tab>
11104
12406
  <mat-tab [label]="tx('editor.tabs.context', 'Context')"><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.context.json', 'Context (JSON)') }}</mat-label><textarea matInput rows="12" [formControl]="contextControl"></textarea></mat-form-field></mat-tab>
11105
12407
  <mat-tab [label]="tx('editor.tabs.state', 'Page State')"><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.state.valuesJson', 'Primary values (JSON)') }}</mat-label><textarea matInput rows="14" [formControl]="stateControl"></textarea></mat-form-field></mat-tab>
12408
+ <mat-tab [label]="tx('editor.limits.tab', 'Widget limits')">
12409
+ <section class="limits-panel" data-testid="canvas-limits-editor">
12410
+ <p class="hint" id="canvas-limits-help">{{ tx('editor.limits.help', 'Limits use grid columns and rows, not pixels. Leave blank for defaults, or to inherit the base limits on a device.') }}</p>
12411
+ @if (canvasLimits.targets.length) {
12412
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12413
+ <mat-label>{{ tx('editor.limits.target', 'Widget and layout') }}</mat-label>
12414
+ <mat-select [formControl]="limitsTargetControl" data-testid="canvas-limits-target">
12415
+ @for (target of canvasLimits.targets; track target.scope + ':' + target.key; let index = $index) {
12416
+ <mat-option [value]="index">{{ limitsTargetLabel(index) }}</mat-option>
12417
+ }
12418
+ </mat-select>
12419
+ </mat-form-field>
12420
+ <p class="hint">{{ limitsGeometryLabel() }}</p>
12421
+ @if (canvasLimits.form.controls[limitsTargetControl.value]; as limitsForm) {
12422
+ <div class="limits-fields" aria-describedby="canvas-limits-help">
12423
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12424
+ <mat-label>{{ tx('editor.limits.minColSpan', 'Minimum width (columns)') }}</mat-label>
12425
+ <input matInput [errorStateMatcher]="limitsErrorMatcher" type="number" min="1" step="1" [formControl]="limitsForm.controls.minColSpan" data-testid="canvas-limit-minColSpan">
12426
+ <mat-error>{{ limitFieldError(limitsForm.controls.minColSpan) }}</mat-error>
12427
+ </mat-form-field>
12428
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12429
+ <mat-label>{{ tx('editor.limits.maxColSpan', 'Maximum width (columns)') }}</mat-label>
12430
+ <input matInput [errorStateMatcher]="limitsErrorMatcher" type="number" min="1" step="1" [formControl]="limitsForm.controls.maxColSpan" data-testid="canvas-limit-maxColSpan">
12431
+ <mat-error>{{ limitFieldError(limitsForm.controls.maxColSpan) }}</mat-error>
12432
+ </mat-form-field>
12433
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12434
+ <mat-label>{{ tx('editor.limits.minRowSpan', 'Minimum height (rows)') }}</mat-label>
12435
+ <input matInput [errorStateMatcher]="limitsErrorMatcher" type="number" min="1" step="1" [formControl]="limitsForm.controls.minRowSpan" data-testid="canvas-limit-minRowSpan">
12436
+ <mat-error>{{ limitFieldError(limitsForm.controls.minRowSpan) }}</mat-error>
12437
+ </mat-form-field>
12438
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12439
+ <mat-label>{{ tx('editor.limits.maxRowSpan', 'Maximum height (rows)') }}</mat-label>
12440
+ <input matInput [errorStateMatcher]="limitsErrorMatcher" type="number" min="1" step="1" [formControl]="limitsForm.controls.maxRowSpan" data-testid="canvas-limit-maxRowSpan">
12441
+ <mat-error>{{ limitFieldError(limitsForm.controls.maxRowSpan) }}</mat-error>
12442
+ </mat-form-field>
12443
+ </div>
12444
+ @if (limitsUseContentHeight()) {
12445
+ <p class="limits-notice"><mat-icon aria-hidden="true">height</mat-icon><span>{{ tx('editor.limits.autoHeight', 'Choose natural height or an adjustable height with internal scrolling.') }}</span></p>
12446
+ <mat-button-toggle-group class="height-modes" [formControl]="limitsForm.controls.heightMode" [attr.aria-label]="tx('editor.height.mode', 'Widget height')" data-testid="canvas-height-mode">
12447
+ <mat-button-toggle value="inherit">{{ tx('editor.height.inherit', 'Default') }}</mat-button-toggle>
12448
+ <mat-button-toggle value="auto">{{ tx('editor.height.auto', 'Automatic') }}</mat-button-toggle>
12449
+ <mat-button-toggle value="fixed">{{ tx('editor.height.fixed', 'Adjustable') }}</mat-button-toggle>
12450
+ </mat-button-toggle-group>
12451
+ @if (limitsForm.controls.heightMode.value === 'fixed') {
12452
+ <div class="limits-fields">
12453
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12454
+ <mat-label>{{ tx('editor.height.value', 'Height (px)') }}</mat-label>
12455
+ <input matInput type="number" min="160" step="1" [errorStateMatcher]="limitsErrorMatcher" [formControl]="limitsForm.controls.heightPx" data-testid="canvas-height-px">
12456
+ <mat-error>{{ limitFieldError(limitsForm.controls.heightPx) }}</mat-error>
12457
+ </mat-form-field>
12458
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12459
+ <mat-label>{{ tx('editor.height.top', 'Top inset (px)') }}</mat-label>
12460
+ <input matInput type="number" min="0" step="1" [errorStateMatcher]="limitsErrorMatcher" [formControl]="limitsForm.controls.offsetTopPx" data-testid="canvas-height-top">
12461
+ <mat-error>{{ limitFieldError(limitsForm.controls.offsetTopPx) }}</mat-error>
12462
+ </mat-form-field>
12463
+ </div>
12464
+ }
12465
+ <div class="limits-fields">
12466
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12467
+ <mat-label>{{ tx('editor.height.min', 'Minimum height (px)') }}</mat-label>
12468
+ <input matInput type="number" min="160" step="1" [errorStateMatcher]="limitsErrorMatcher" [formControl]="limitsForm.controls.minHeightPx" data-testid="canvas-limit-minHeightPx">
12469
+ <mat-error>{{ limitFieldError(limitsForm.controls.minHeightPx) }}</mat-error>
12470
+ </mat-form-field>
12471
+ <mat-form-field appearance="outline" subscriptSizing="dynamic">
12472
+ <mat-label>{{ tx('editor.height.max', 'Maximum height (px)') }}</mat-label>
12473
+ <input matInput type="number" min="160" step="1" [errorStateMatcher]="limitsErrorMatcher" [formControl]="limitsForm.controls.maxHeightPx" data-testid="canvas-limit-maxHeightPx">
12474
+ <mat-error>{{ limitFieldError(limitsForm.controls.maxHeightPx) }}</mat-error>
12475
+ </mat-form-field>
12476
+ </div>
12477
+ }
12478
+ }
12479
+ @if (limitsSummaryError) { <p class="limits-error" role="alert">{{ limitsSummaryError }}</p> }
12480
+ } @else {
12481
+ <p>{{ tx('editor.limits.empty', 'Add widgets to the canvas to configure their limits.') }}</p>
12482
+ }
12483
+ </section>
12484
+ </mat-tab>
11106
12485
  </mat-tab-group>
11107
12486
  </div>
11108
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;min-width:0;max-width:100%}.editor,.panel{display:grid;gap:16px;min-width:0;max-width:100%;color:var(--md-sys-color-on-surface, currentColor)}mat-tab-group,mat-form-field{min-width:0;max-width:100%}.head,.summary{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.summary{color:var(--md-sys-color-on-surface-variant, currentColor)}.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr))}@media(max-width:600px){.grid{grid-template-columns:minmax(0,1fr)}.head,.summary,.preset-preview__head,.preset-preview__recommendation{align-items:stretch}.head>button,.preset-preview__recommendation button{width:100%}}.card{display:grid;gap:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface-container, transparent));color:var(--md-sys-color-on-surface, currentColor)}.wide{grid-column:1 / -1}.hint{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px}.preset-preview{display:grid;gap:12px;margin-top:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low, transparent) 88%,var(--md-sys-color-primary-container, transparent) 12%)}.preset-preview--theme{background:var(--md-sys-color-surface-container-low, transparent)}.preset-preview__head,.preset-preview__recommendation{display:flex;justify-content:space-between;align-items:center;gap:16px}.preset-preview__head>div,.preset-preview__recommendation>div{display:grid;gap:4px;min-width:0}.preset-preview p{margin:0;color:var(--md-sys-color-on-surface-variant, currentColor);line-height:1.45}.preset-preview__chips{display:flex;flex-wrap:wrap;gap:8px}.preset-preview__chips span{padding:5px 9px;border-radius:999px;background:var(--md-sys-color-surface-container, color-mix(in srgb, currentColor 8%, transparent));color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px;line-height:1.3}.preset-preview__recommendation{padding-top:12px;border-top:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 14%, transparent))}.preset-preview__recommendation span:last-child{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:13px;line-height:1.35}.eyebrow{color:var(--md-sys-color-primary, currentColor);font-size:11px;font-weight:700;letter-spacing:0;text-transform:uppercase}:host ::ng-deep .mat-mdc-tab-body-content{padding-top:12px;box-sizing:border-box}\n"] }]
12487
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: { '(window:resize)': 'refreshRenderedValidation()' }, styles: [":host{display:block;min-width:0;max-width:100%}.height-modes{width:fit-content;max-width:100%;flex-wrap:wrap;margin-bottom:16px}.editor,.panel{display:grid;gap:16px;min-width:0;max-width:100%;color:var(--md-sys-color-on-surface, currentColor)}mat-tab-group,mat-form-field{min-width:0;max-width:100%}.head,.summary{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.summary{color:var(--md-sys-color-on-surface-variant, currentColor)}.selection-identity{position:sticky;top:0;z-index:var(--praxis-layer-sticky-local, 100);display:flex;align-items:flex-start;gap:8px;padding-block:8px;background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface));overflow-wrap:anywhere}.selection-identity mat-icon{flex-shrink:0}:host ::ng-deep .editor-tabs>.mat-mdc-tab-header .mdc-tab{min-width:0;padding-inline:12px}:host ::ng-deep .editor-tabs>.mat-mdc-tab-header .mdc-tab__text-label{max-width:128px;white-space:normal;overflow-wrap:anywhere;text-align:center;letter-spacing:normal;line-height:1.25}.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr))}@media(max-width:600px){.grid{grid-template-columns:minmax(0,1fr)}.head,.summary,.preset-preview__head,.preset-preview__recommendation{align-items:stretch}.head>button,.preset-preview__recommendation button{width:100%}}.card{display:grid;gap:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface-container, transparent));color:var(--md-sys-color-on-surface, currentColor)}.limits-panel{display:grid;gap:8px;min-width:0}.limits-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:4px 16px}.limits-fields mat-form-field{min-width:0}.limits-notice{display:flex;gap:8px;align-items:flex-start;color:var(--md-sys-color-on-surface-variant, currentColor)}.limits-notice mat-icon{flex-shrink:0}.limits-error{color:var(--md-sys-color-error, #b3261e)}.wide{grid-column:1 / -1}.hint{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px}.preset-preview{display:grid;gap:12px;margin-top:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low, transparent) 88%,var(--md-sys-color-primary-container, transparent) 12%)}.preset-preview--theme{background:var(--md-sys-color-surface-container-low, transparent)}.preset-preview__head,.preset-preview__recommendation{display:flex;justify-content:space-between;align-items:center;gap:16px}.preset-preview__head>div,.preset-preview__recommendation>div{display:grid;gap:4px;min-width:0}.preset-preview p{margin:0;color:var(--md-sys-color-on-surface-variant, currentColor);line-height:1.45}.preset-preview__chips{display:flex;flex-wrap:wrap;gap:8px}.preset-preview__chips span{padding:5px 9px;border-radius:999px;background:var(--md-sys-color-surface-container, color-mix(in srgb, currentColor 8%, transparent));color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px;line-height:1.3}.preset-preview__recommendation{padding-top:12px;border-top:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 14%, transparent))}.preset-preview__recommendation span:last-child{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:13px;line-height:1.35}.eyebrow{color:var(--md-sys-color-primary, currentColor);font-size:11px;font-weight:700;letter-spacing:0;text-transform:uppercase}:host ::ng-deep .mat-mdc-tab-body-content{padding-top:12px;box-sizing:border-box}\n"] }]
11109
12488
  }], ctorParameters: () => [], propDecorators: { page: [{
11110
12489
  type: Input
11111
12490
  }], layout: [{
@@ -12554,7 +13933,7 @@ class PageConfigEditorComponent {
12554
13933
  </div>
12555
13934
  }
12556
13935
  </div>
12557
- `, isInline: true, styles: [".page-config{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.page-head{display:flex;align-items:flex-start;gap:12px}.page-head__copy{display:grid;gap:4px}.page-title{font-weight:600;font-size:18px;line-height:1.2}.page-subtitle{max-width:72ch;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.spacer{flex:1}.page-summary{display:flex;flex-wrap:wrap;gap:8px}.page-tabs{border-radius:16px;overflow:hidden;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 78%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent)}.page-tabs ::ng-deep .mdc-tab{min-width:120px}.page-tabs ::ng-deep .mdc-tab__text-label{font-size:13px;font-weight:600}.page-tab-panel{display:grid;gap:12px;padding:16px 4px 4px}.page-chip{display:inline-flex;align-items:center;gap:8px;min-height:36px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 88%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent);color:var(--md-sys-color-on-surface-variant);font-size:12px;font-weight:500}.page-chip mat-icon{width:18px;height:18px;font-size:18px}.page-chip--accent{border-color:color-mix(in srgb,var(--md-sys-color-primary) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 30%,transparent)}.page-chip--warning{border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 35%,transparent)}.page-identity{display:grid;gap:4px;padding:12px 14px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 70%,transparent);font-size:13px;color:var(--md-sys-color-on-surface-variant)}.page-section{font-weight:600;margin-top:8px}.page-section-help{margin-top:-4px;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.grid-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.state-grid{display:grid;gap:16px}.state-values,.state-group{padding:14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:14px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-surface-container-low) 92%,transparent),transparent),var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface))}.state-group{display:grid;gap:12px}.state-group-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-group-title{font-weight:600}.state-group-help{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-list{display:grid;gap:12px}.state-card{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 80%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container) 84%,transparent)}.state-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-card-title{font-weight:600}.state-card-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.span-2{grid-column:span 2}.span-3{grid-column:1 / -1}.state-empty{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-operator-hint{display:flex;align-items:center;gap:8px;min-height:48px;padding:0 12px;border-radius:12px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-secondary-container) 35%,transparent)}.state-operator-hint mat-icon{color:var(--md-sys-color-secondary)}.page-actions{display:flex;justify-content:flex-end}.page-error{color:var(--md-sys-color-error);font-size:13px}.page-config .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.help-icon-button{margin-right:-4px}.mat-mdc-form-field-icon-suffix{align-self:center}@media(max-width:720px){.span-2,.span-3{grid-column:auto}.page-head{flex-direction:column}.state-group-head{align-items:stretch;flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i7.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13936
+ `, isInline: true, styles: [".page-config{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.page-head{display:flex;align-items:flex-start;gap:12px}.page-head__copy{display:grid;gap:4px}.page-title{font-weight:600;font-size:18px;line-height:1.2}.page-subtitle{max-width:72ch;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.spacer{flex:1}.page-summary{display:flex;flex-wrap:wrap;gap:8px}.page-tabs{border-radius:16px;overflow:hidden;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 78%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent)}.page-tabs ::ng-deep .mdc-tab{min-width:120px}.page-tabs ::ng-deep .mdc-tab__text-label{font-size:13px;font-weight:600}.page-tab-panel{display:grid;gap:12px;padding:16px 4px 4px}.page-chip{display:inline-flex;align-items:center;gap:8px;min-height:36px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 88%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent);color:var(--md-sys-color-on-surface-variant);font-size:12px;font-weight:500}.page-chip mat-icon{width:18px;height:18px;font-size:18px}.page-chip--accent{border-color:color-mix(in srgb,var(--md-sys-color-primary) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 30%,transparent)}.page-chip--warning{border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 35%,transparent)}.page-identity{display:grid;gap:4px;padding:12px 14px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 70%,transparent);font-size:13px;color:var(--md-sys-color-on-surface-variant)}.page-section{font-weight:600;margin-top:8px}.page-section-help{margin-top:-4px;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.grid-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.state-grid{display:grid;gap:16px}.state-values,.state-group{padding:14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:14px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-surface-container-low) 92%,transparent),transparent),var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface))}.state-group{display:grid;gap:12px}.state-group-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-group-title{font-weight:600}.state-group-help{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-list{display:grid;gap:12px}.state-card{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 80%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container) 84%,transparent)}.state-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-card-title{font-weight:600}.state-card-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.span-2{grid-column:span 2}.span-3{grid-column:1 / -1}.state-empty{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-operator-hint{display:flex;align-items:center;gap:8px;min-height:48px;padding:0 12px;border-radius:12px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-secondary-container) 35%,transparent)}.state-operator-hint mat-icon{color:var(--md-sys-color-secondary)}.page-actions{display:flex;justify-content:flex-end}.page-error{color:var(--md-sys-color-error);font-size:13px}.page-config .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.help-icon-button{margin-right:-4px}.mat-mdc-form-field-icon-suffix{align-self:center}@media(max-width:720px){.span-2,.span-3{grid-column:auto}.page-head{flex-direction:column}.state-group-head{align-items:stretch;flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i10.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i10.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i8.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i8.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
12558
13937
  }
12559
13938
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PageConfigEditorComponent, decorators: [{
12560
13939
  type: Component,
@@ -14352,7 +15731,7 @@ const PRAXIS_PAGE_BUILDER_AUTHORING_MANIFEST = {
14352
15731
  scope: 'templating',
14353
15732
  targetKind: 'widgetShell',
14354
15733
  target: { kind: 'widgetShell', resolver: 'widget-shell-by-widget-key', ambiguityPolicy: 'fail', required: true },
14355
- inputSchema: { type: 'object', required: ['widgetKey'], properties: { widgetKey: { type: 'string' }, shell: { type: 'object' } } },
15734
+ inputSchema: { type: 'object', required: ['widgetKey'], properties: { widgetKey: { type: 'string' }, shell: { type: 'object', properties: { stickyHeader: { type: 'boolean', description: 'Retain the existing header within the widget during page scrolling; suspended for drag authoring, collapsed/overlay states and scroll recovery.' } } } } },
14356
15735
  effects: [{ kind: 'compile-domain-patch', handler: 'page-builder-widget-shell-configure', handlerContract: {
14357
15736
  reads: ['WidgetPageDefinition.widgets', 'WidgetShellEditorComponent', 'SettingsPanelBridge'],
14358
15737
  writes: ['page.widgets[].shell'],
@@ -22725,6 +24104,10 @@ class DynamicPageBuilderComponent {
22725
24104
  this.agenticAuthoringLastEtag.set(this.authoringDocument.etag?.trim() || null);
22726
24105
  }
22727
24106
  else if (authoringDocumentChange && !this.authoringDocument) {
24107
+ // Clearing the persisted document hands ownership back to the page input.
24108
+ const parsed = this.parsePage(this.page) || { widgets: [] };
24109
+ this.currentPage.set(parsed);
24110
+ this.lastRuntimePageChangeFingerprint = JSON.stringify(parsed);
22728
24111
  this.persistedUiCompositionAuthoringSource.set(null);
22729
24112
  this.agenticAuthoringLastEtag.set(null);
22730
24113
  }