@foxeltech/angular-ui 0.7.108 → 0.7.131

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.
Files changed (31) hide show
  1. package/components.css +24 -5
  2. package/fesm2022/foxeltech-angular-ui-chart.mjs +25 -1
  3. package/fesm2022/foxeltech-angular-ui-chart.mjs.map +1 -1
  4. package/fesm2022/foxeltech-angular-ui-flow.mjs +42 -11
  5. package/fesm2022/foxeltech-angular-ui-flow.mjs.map +1 -1
  6. package/fesm2022/foxeltech-angular-ui.mjs +102 -28
  7. package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
  8. package/package.json +3 -2
  9. package/src/lib/styles/components.css +24 -5
  10. package/src/lib/styles/tokens.css +3 -0
  11. package/src/lib/styles/utilities.css +9 -4
  12. package/src/lib/ui/components/bar-list/bar-list.component.html +19 -18
  13. package/src/lib/ui/components/bar-list/bar-list.component.scss +63 -0
  14. package/src/lib/ui/components/breadcrumb/breadcrumb.component.css +31 -0
  15. package/src/lib/ui/components/breadcrumb/breadcrumb.component.html +6 -1
  16. package/src/lib/ui/components/button/button.component.html +37 -225
  17. package/src/lib/ui/components/button/button.component.scss +30 -0
  18. package/src/lib/ui/components/drawer/drawer.component.html +32 -7
  19. package/src/lib/ui/components/drawer/drawer.component.scss +114 -17
  20. package/src/lib/ui/components/filter-pills/filter-pills.component.html +4 -1
  21. package/src/lib/ui/components/filter-pills/filter-pills.component.scss +23 -0
  22. package/src/lib/ui/components/master-detail/master-detail.component.css +36 -8
  23. package/src/lib/ui/components/master-detail/master-detail.component.html +4 -2
  24. package/src/lib/ui/components/section-card/section-card.component.html +10 -5
  25. package/src/lib/ui/components/section-card/section-card.component.scss +28 -0
  26. package/src/lib/ui/components/stat-cards/stat-cards.component.css +48 -0
  27. package/src/lib/ui/components/stat-cards/stat-cards.component.html +16 -7
  28. package/tokens.css +3 -0
  29. package/types/foxeltech-angular-ui-flow.d.ts +24 -2
  30. package/types/foxeltech-angular-ui.d.ts +92 -5
  31. package/utilities.css +9 -4
@@ -28,8 +28,10 @@ import { MatTooltip } from '@angular/material/tooltip';
28
28
  import { MatFormField, MatSelect, MatOption } from '@angular/material/select';
29
29
  import { moveItemInArray, transferArrayItem, CdkDropList, CdkDrag, CdkDragPlaceholder } from '@angular/cdk/drag-drop';
30
30
  import { trigger, state, transition, style, animate } from '@angular/animations';
31
+ import * as i1$4 from 'lucide-angular';
32
+ import { Diamond, Rows3, SquarePlus, Grid2x2, Download, Radiation, Star, LucideAngularModule } from 'lucide-angular';
31
33
  import gsap from 'gsap';
32
- import * as i1$4 from '@angular/material/menu';
34
+ import * as i1$5 from '@angular/material/menu';
33
35
  import { MatMenuModule } from '@angular/material/menu';
34
36
 
35
37
  class BaseComponent {
@@ -2579,11 +2581,11 @@ class ButtonComponent {
2579
2581
  this.clicked.emit();
2580
2582
  }
2581
2583
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2582
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: ButtonComponent, isStandalone: true, selector: "fx-ui-button", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, buttonVariant: { classPropertyName: "buttonVariant", publicName: "buttonVariant", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: false, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<button\n [attr.type]=\"type()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-busy]=\"loading() || null\"\n [class]=\"class()\"\n [ngClass]=\"{\n 'fx-btn--icon': !!icon && !label,\n 'btn-default': buttonVariant() === 'default',\n 'btn-primary': buttonVariant() === 'primary',\n 'btn-success': buttonVariant() === 'success',\n 'btn-cta': buttonVariant() === 'cta',\n 'btn-alternative': buttonVariant() === 'alternative',\n }\"\n [disabled]=\"disabled() || loading()\"\n (click)=\"onClick()\"\n>\n @if (loading()) {\n <div class=\"flex w-full items-center justify-center\" aria-hidden=\"true\">\n <svg\n [attr.fill]=\"\n buttonVariant() !== 'alternative' ? 'rgb(var(--text-primary))' : 'rgb(var(--primary))'\n \"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <circle cx=\"4\" cy=\"12\" r=\"0\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_lo66\"\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_z0Or\"\n begin=\"spinner_lo66.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"4\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_JsnR\"\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_Aguh\"\n begin=\"spinner_JsnR.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"12\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_hSjk\"\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_UHR2\"\n begin=\"spinner_hSjk.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"20\" cy=\"12\" r=\"3\">\n <animate\n id=\"spinner_4v5M\"\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_OLMs\"\n begin=\"spinner_4v5M.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n </circle>\n </svg>\n </div>\n } @else {\n <ng-container>\n <div class=\"flex items-center justify-center gap-small\">\n @if (icon) {\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"16\"\n class=\"flex\"\n [ngClass]=\"{\n 'text-text-inverse': buttonVariant() === 'default',\n 'text-text-primary': buttonVariant() === 'alternative',\n 'text-accent-contrast': buttonVariant() === 'primary',\n 'text-surface': buttonVariant() === 'success',\n 'text-white': buttonVariant() === 'cta',\n }\"\n ></fx-ui-hero-icon>\n }\n @if (label) {\n <!-- text-box: optical centering \u2014 Archivo's leading floats ink up otherwise -->\n <div class=\"[text-box:trim-both_cap_alphabetic]\">{{ label }}</div>\n }\n </div>\n </ng-container>\n }\n</button>\n", styles: [""], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2584
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: ButtonComponent, isStandalone: true, selector: "fx-ui-button", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, buttonVariant: { classPropertyName: "buttonVariant", publicName: "buttonVariant", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: false, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<button\n [attr.type]=\"type()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-busy]=\"loading() || null\"\n [class]=\"class()\"\n [ngClass]=\"{\n 'fx-btn--icon': !!icon && !label,\n 'btn-default': buttonVariant() === 'default',\n 'btn-primary': buttonVariant() === 'primary',\n 'btn-success': buttonVariant() === 'success',\n 'btn-cta': buttonVariant() === 'cta',\n 'btn-alternative': buttonVariant() === 'alternative',\n }\"\n [disabled]=\"disabled() || loading()\"\n (click)=\"onClick()\"\n>\n <!-- Loading keeps the LABEL, as the sign-in button does: a button that\n swaps its text for a spinner collapses to a stub, and the row jumps.\n The ring is that button's `.li-spin`, in the foreground the variant\n already uses for its icon so it reads on a coloured ground. -->\n <!-- 6px, the reference button's own gap; `gap-small` is 4px. -->\n <div class=\"flex items-center justify-center gap-[6px]\">\n @if (loading()) {\n <span\n class=\"fx-btn__spin\"\n aria-hidden=\"true\"\n [ngClass]=\"{\n 'text-text-inverse': buttonVariant() === 'default',\n 'text-text-primary': buttonVariant() === 'alternative',\n 'text-accent-contrast': buttonVariant() === 'primary',\n 'text-surface': buttonVariant() === 'success',\n 'text-white': buttonVariant() === 'cta',\n }\"\n ></span>\n } @else if (icon) {\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"13\"\n class=\"flex\"\n [ngClass]=\"{\n 'text-text-inverse': buttonVariant() === 'default',\n 'text-text-primary': buttonVariant() === 'alternative',\n 'text-accent-contrast': buttonVariant() === 'primary',\n 'text-surface': buttonVariant() === 'success',\n 'text-white': buttonVariant() === 'cta',\n }\"\n ></fx-ui-hero-icon>\n }\n @if (label) {\n <!-- text-box: optical centering \u2014 Archivo's leading floats ink up otherwise -->\n <div class=\"[text-box:trim-both_cap_alphabetic]\">{{ label }}</div>\n }\n </div>\n</button>\n", styles: ["@charset \"UTF-8\";.fx-btn__spin{display:inline-block;width:12px;height:12px;flex:0 0 auto;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;opacity:.9;animation:fx-btn-spin .8s linear infinite}@keyframes fx-btn-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.fx-btn__spin{animation:none}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2583
2585
  }
2584
2586
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ButtonComponent, decorators: [{
2585
2587
  type: Component,
2586
- args: [{ selector: 'fx-ui-button', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgClass, HeroIconComponent], template: "<button\n [attr.type]=\"type()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-busy]=\"loading() || null\"\n [class]=\"class()\"\n [ngClass]=\"{\n 'fx-btn--icon': !!icon && !label,\n 'btn-default': buttonVariant() === 'default',\n 'btn-primary': buttonVariant() === 'primary',\n 'btn-success': buttonVariant() === 'success',\n 'btn-cta': buttonVariant() === 'cta',\n 'btn-alternative': buttonVariant() === 'alternative',\n }\"\n [disabled]=\"disabled() || loading()\"\n (click)=\"onClick()\"\n>\n @if (loading()) {\n <div class=\"flex w-full items-center justify-center\" aria-hidden=\"true\">\n <svg\n [attr.fill]=\"\n buttonVariant() !== 'alternative' ? 'rgb(var(--text-primary))' : 'rgb(var(--primary))'\n \"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <circle cx=\"4\" cy=\"12\" r=\"0\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_lo66\"\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_z0Or\"\n begin=\"spinner_lo66.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"4\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_JsnR\"\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_Aguh\"\n begin=\"spinner_JsnR.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"12\" cy=\"12\" r=\"3\">\n <animate\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_hSjk\"\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_UHR2\"\n begin=\"spinner_hSjk.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n </circle>\n <circle cx=\"20\" cy=\"12\" r=\"3\">\n <animate\n id=\"spinner_4v5M\"\n begin=\"0;spinner_z0Or.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"3;0\"\n fill=\"freeze\"\n />\n <animate\n id=\"spinner_OLMs\"\n begin=\"spinner_4v5M.end\"\n attributeName=\"cx\"\n dur=\"0.001s\"\n values=\"20;4\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_OLMs.end\"\n attributeName=\"r\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"0;3\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_UHR2.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"4;12\"\n fill=\"freeze\"\n />\n <animate\n begin=\"spinner_Aguh.end\"\n attributeName=\"cx\"\n calcMode=\"spline\"\n dur=\"0.5s\"\n keySplines=\".36,.6,.31,1\"\n values=\"12;20\"\n fill=\"freeze\"\n />\n </circle>\n </svg>\n </div>\n } @else {\n <ng-container>\n <div class=\"flex items-center justify-center gap-small\">\n @if (icon) {\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"16\"\n class=\"flex\"\n [ngClass]=\"{\n 'text-text-inverse': buttonVariant() === 'default',\n 'text-text-primary': buttonVariant() === 'alternative',\n 'text-accent-contrast': buttonVariant() === 'primary',\n 'text-surface': buttonVariant() === 'success',\n 'text-white': buttonVariant() === 'cta',\n }\"\n ></fx-ui-hero-icon>\n }\n @if (label) {\n <!-- text-box: optical centering \u2014 Archivo's leading floats ink up otherwise -->\n <div class=\"[text-box:trim-both_cap_alphabetic]\">{{ label }}</div>\n }\n </div>\n </ng-container>\n }\n</button>\n" }]
2588
+ args: [{ selector: 'fx-ui-button', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgClass, HeroIconComponent], template: "<button\n [attr.type]=\"type()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-busy]=\"loading() || null\"\n [class]=\"class()\"\n [ngClass]=\"{\n 'fx-btn--icon': !!icon && !label,\n 'btn-default': buttonVariant() === 'default',\n 'btn-primary': buttonVariant() === 'primary',\n 'btn-success': buttonVariant() === 'success',\n 'btn-cta': buttonVariant() === 'cta',\n 'btn-alternative': buttonVariant() === 'alternative',\n }\"\n [disabled]=\"disabled() || loading()\"\n (click)=\"onClick()\"\n>\n <!-- Loading keeps the LABEL, as the sign-in button does: a button that\n swaps its text for a spinner collapses to a stub, and the row jumps.\n The ring is that button's `.li-spin`, in the foreground the variant\n already uses for its icon so it reads on a coloured ground. -->\n <!-- 6px, the reference button's own gap; `gap-small` is 4px. -->\n <div class=\"flex items-center justify-center gap-[6px]\">\n @if (loading()) {\n <span\n class=\"fx-btn__spin\"\n aria-hidden=\"true\"\n [ngClass]=\"{\n 'text-text-inverse': buttonVariant() === 'default',\n 'text-text-primary': buttonVariant() === 'alternative',\n 'text-accent-contrast': buttonVariant() === 'primary',\n 'text-surface': buttonVariant() === 'success',\n 'text-white': buttonVariant() === 'cta',\n }\"\n ></span>\n } @else if (icon) {\n <fx-ui-hero-icon\n [icon]=\"icon\"\n [size]=\"13\"\n class=\"flex\"\n [ngClass]=\"{\n 'text-text-inverse': buttonVariant() === 'default',\n 'text-text-primary': buttonVariant() === 'alternative',\n 'text-accent-contrast': buttonVariant() === 'primary',\n 'text-surface': buttonVariant() === 'success',\n 'text-white': buttonVariant() === 'cta',\n }\"\n ></fx-ui-hero-icon>\n }\n @if (label) {\n <!-- text-box: optical centering \u2014 Archivo's leading floats ink up otherwise -->\n <div class=\"[text-box:trim-both_cap_alphabetic]\">{{ label }}</div>\n }\n </div>\n</button>\n", styles: ["@charset \"UTF-8\";.fx-btn__spin{display:inline-block;width:12px;height:12px;flex:0 0 auto;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;opacity:.9;animation:fx-btn-spin .8s linear infinite}@keyframes fx-btn-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.fx-btn__spin{animation:none}}\n"] }]
2587
2589
  }], propDecorators: { label: [{
2588
2590
  type: Input
2589
2591
  }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], buttonVariant: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonVariant", required: false }] }], icon: [{
@@ -3982,6 +3984,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
3982
3984
  type: Input
3983
3985
  }], columnChanged: [{ type: i0.Output, args: ["columnChanged"] }] } });
3984
3986
 
3987
+ /**
3988
+ * The lucide marks this design system draws, PICKED rather than pulled in whole:
3989
+ * `lucide-angular` ships ~2,100 icons and `pick()` is the only thing keeping the
3990
+ * bundle to the ones actually rendered.
3991
+ *
3992
+ * Heroicons remain the general-purpose set — `fx-ui-hero-icon` serves them from
3993
+ * the self-hosted sprite with no runtime icon library, and that does not change.
3994
+ * Lucide is here for the two marks the Binary Intelligence reference draws as
3995
+ * inline SVG of its own, which the sprite has no equivalent of:
3996
+ *
3997
+ * - `star` the GitHub star beside a PoC's star count
3998
+ * - `radiation` the proprietary-exploit mark (identified by matching the
3999
+ * reference's path data against lucide-static, 16/09)
4000
+ * - `download` the Download PoC action in the exploit detail footer
4001
+ *
4002
+ * And the four sidebar marks, chosen to match the reference's nav GLYPHS shape
4003
+ * for shape rather than by meaning (16/09):
4004
+ * `▦` → `grid-2x2` a square split by a cross
4005
+ * `⊞` → `square-plus` a square with a plus inside
4006
+ * `▤` → `rows-3` a square split into three bands
4007
+ * `❖` → `diamond` a diamond outline
4008
+ *
4009
+ * Usage: import `FxLucideModule`, then `<lucide-icon name="star" [size]="12" />`.
4010
+ * Names are kebab-case; the component pascal-cases them to look the icon up.
4011
+ */
4012
+ const FX_LUCIDE_ICONS = { Star, Radiation, Download, Grid2x2, SquarePlus, Rows3, Diamond };
4013
+ class FxLucideModule {
4014
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: FxLucideModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
4015
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.20", ngImport: i0, type: FxLucideModule, imports: [i1$4.LucideAngularModule], exports: [LucideAngularModule] });
4016
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: FxLucideModule, imports: [LucideAngularModule.pick(FX_LUCIDE_ICONS), LucideAngularModule] });
4017
+ }
4018
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: FxLucideModule, decorators: [{
4019
+ type: NgModule,
4020
+ args: [{
4021
+ imports: [LucideAngularModule.pick(FX_LUCIDE_ICONS)],
4022
+ exports: [LucideAngularModule],
4023
+ }]
4024
+ }] });
4025
+
3985
4026
  class DrawerComponent {
3986
4027
  router;
3987
4028
  cdr = inject(ChangeDetectorRef);
@@ -4022,7 +4063,18 @@ class DrawerComponent {
4022
4063
  this.destroy$.next();
4023
4064
  this.destroy$.complete();
4024
4065
  }
4066
+ /**
4067
+ * Ignores presses while the 300ms width animation is still running. The
4068
+ * button rides the drawer's edge, travelling ~190px as it animates, so a
4069
+ * second press lands on whatever the button has just slid away from — and a
4070
+ * mid-flight toggle leaves the animation and `isExpanded` out of step.
4071
+ */
4072
+ toggleLocked = false;
4025
4073
  toggleDrawer() {
4074
+ if (this.toggleLocked)
4075
+ return;
4076
+ this.toggleLocked = true;
4077
+ setTimeout(() => (this.toggleLocked = false), 320);
4026
4078
  this.isExpanded = !this.isExpanded;
4027
4079
  this.toggleExpanded.emit(this.isExpanded);
4028
4080
  }
@@ -4095,7 +4147,7 @@ class DrawerComponent {
4095
4147
  return this.isExpanded ? 'expanded' : 'collapsed';
4096
4148
  }
4097
4149
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DrawerComponent, deps: [{ token: i2$1.Router }], target: i0.ɵɵFactoryTarget.Component });
4098
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DrawerComponent, isStandalone: true, selector: "fx-ui-drawer", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, footerItems: { classPropertyName: "footerItems", publicName: "footerItems", isSignal: true, isRequired: false, transformFunction: null }, isExpanded: { classPropertyName: "isExpanded", publicName: "isExpanded", isSignal: false, isRequired: false, transformFunction: null }, logo: { classPropertyName: "logo", publicName: "logo", isSignal: false, isRequired: false, transformFunction: null }, logoText: { classPropertyName: "logoText", publicName: "logoText", isSignal: false, isRequired: false, transformFunction: null }, logoSubtext: { classPropertyName: "logoSubtext", publicName: "logoSubtext", isSignal: false, isRequired: false, transformFunction: null }, version: { classPropertyName: "version", publicName: "version", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { toggleExpanded: "toggleExpanded", itemClicked: "itemClicked" }, ngImport: i0, template: "<div class=\"drawer-host-wrapper\">\n <a href=\"/\" class=\"drawer-brand\" [class.is-collapsed]=\"!isExpanded\">\n @if (logo) {\n <img [src]=\"logo\" alt=\"Home\" class=\"drawer-brand-mark\" />\n } @else {\n <div class=\"drawer-brand-mark\"></div>\n }\n @if (isExpanded && (logoText || logoSubtext)) {\n <span class=\"drawer-brand-copy\">\n @if (logoText) {\n <span class=\"drawer-brand-text\">{{ logoText }}</span>\n }\n @if (logoSubtext) {\n <span class=\"drawer-brand-subtext\">{{ logoSubtext }}</span>\n }\n </span>\n }\n </a>\n <div class=\"drawer-container\" [@drawerExpand]=\"getDrawerState()\">\n <!-- Navigation Items -->\n <nav class=\"drawer-nav\">\n @for (item of items(); track item) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n }\n </nav>\n\n <!-- V2: bottom section (Administration group, user block, \u2026) -->\n <div class=\"mt-auto\">\n @if (footerItems().length) {\n <nav class=\"drawer-footer-nav\">\n @for (item of footerItems(); track item) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n }\n </nav>\n }\n <ng-content select=\"[drawerFooter]\"></ng-content>\n </div>\n\n <!-- Version -->\n @if (version) {\n <div class=\"drawer-version\" [matTooltip]=\"!isExpanded ? version : ''\" matTooltipPosition=\"right\">\n @if (isExpanded) {\n <span>{{ version }}</span>\n }\n @if (!isExpanded) {\n <span class=\"drawer-version-dot\"></span>\n }\n </div>\n }\n </div>\n\n <!-- Floating Toggle Button on right border -->\n <button\n type=\"button\"\n class=\"toggle-btn\"\n [attr.aria-label]=\"isExpanded ? 'Collapse navigation' : 'Expand navigation'\"\n [attr.aria-expanded]=\"isExpanded\"\n (click)=\"toggleDrawer()\"\n [matTooltip]=\"isExpanded ? 'Collapse' : 'Expand'\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n matTooltipPosition=\"right\"\n >\n <fx-ui-hero-icon\n [icon]=\"isExpanded ? 'chevron-left' : 'chevron-right'\"\n [size]=\"16\"\n class=\"text-text-primary\"\n ></fx-ui-hero-icon>\n </button>\n</div>\n\n<!-- Recursive Template for Drawer Items -->\n<ng-template #drawerItem let-item let-level=\"level\">\n @if (item.heading) {\n @if (isExpanded) {\n <div class=\"drawer-section-label\">{{ item.label }}</div>\n } @else {\n <div class=\"drawer-section-rule\"></div>\n }\n } @else {\n <div class=\"drawer-item-wrapper\">\n <!-- Item Button/Link -->\n @if (item.route && !item.children) {\n <a\n [routerLink]=\"item.route\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 8 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n @if (isExpanded) {\n <span class=\"item-label\">{{ item.label }}</span>\n }\n </div>\n </a>\n }\n\n @if (!item.route || item.children) {\n <button\n type=\"button\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 8 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-sm font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n @if (isExpanded) {\n <span class=\"item-label\">{{ item.label }}</span>\n }\n @if (isExpanded && item.children && item.children.length > 0) {\n <fx-ui-hero-icon\n [icon]=\"'chevron-down'\"\n [size]=\"16\"\n class=\"chevron-icon\"\n [class.rotated]=\"isAccordionExpanded(item.id)\"\n ></fx-ui-hero-icon>\n }\n </div>\n </button>\n }\n\n <!-- Accordion Children -->\n @if (item.children && item.children.length > 0) {\n <div\n class=\"accordion-children\"\n [@accordionExpand]=\"getAccordionState(item.id)\"\n >\n @for (child of item.children; track child) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: child, level: level + 1 }\"\n ></ng-container>\n }\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer properties;:host{display:block;height:100%}.drawer-host-wrapper{position:relative;display:flex;height:100%;flex-direction:column;background:rgb(var(--og-surface-sunken));border-right:1px solid rgb(var(--og-border-subtle))}.drawer-brand{display:flex;height:60px;flex-shrink:0;align-items:center;gap:calc(var(--spacing, .25rem) * 3);overflow:hidden;text-decoration-line:none;padding-left:16px;padding-right:8px;transition:padding .3s cubic-bezier(.4,0,.2,1);max-width:260px}.drawer-brand.is-collapsed{justify-content:center;padding-left:0;padding-right:0;max-width:70px}.drawer-brand-mark{height:20px;width:auto;flex-shrink:0}.drawer-brand-copy{display:flex;min-width:0px;flex-direction:column;--tw-leading: var(--leading-tight, 1.25);line-height:var(--leading-tight, 1.25)}.drawer-brand-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:15px;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgb(var(--text-primary));-webkit-user-select:none;user-select:none;font-family:var(--og-font-display);letter-spacing:-.01em}.drawer-brand-subtext{margin-top:1px;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;font-size:11px;color:rgb(var(--text-placeholder));-webkit-user-select:none;user-select:none;letter-spacing:var(--og-tracking-label)}.drawer-container{display:flex;min-height:0px;flex:1;flex-direction:column;overflow:hidden;padding-inline:calc(var(--spacing, .25rem) * 2);transition:width .3s cubic-bezier(.4,0,.2,1)}.toggle-btn{position:absolute;top:50%;z-index:50;display:flex;height:calc(var(--spacing, .25rem) * 6);width:calc(var(--spacing, .25rem) * 6);--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-default));background-color:rgb(var(--bg-primary));--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(var(--shadow-color) / .05));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);right:-12px;transition:all .2s ease}.toggle-btn:hover{background-color:rgb(var(--bg-primary-hover));--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.toggle-btn:active{transform:translateY(-50%) scale(.9)}.toggle-btn:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.drawer-footer-nav{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default));padding-inline:0px;padding-top:calc(var(--spacing, .25rem) * 2);padding-bottom:var(--spacing, .25rem)}.drawer-nav{flex:1;overflow-x:hidden;overflow-y:auto;padding-block:calc(var(--spacing, .25rem) * 2)}.drawer-nav::-webkit-scrollbar{width:calc(var(--spacing, .25rem) * 1.5)}.drawer-nav::-webkit-scrollbar-track{background-color:transparent}.drawer-nav::-webkit-scrollbar-thumb{border-radius:var(--og-radius-control);background-color:rgb(var(--border-default))}.drawer-nav::-webkit-scrollbar-thumb:hover{background-color:rgb(var(--text-placeholder))}.drawer-section-label{padding-inline:1rem;padding-top:calc(var(--spacing, .25rem) * 4);padding-bottom:var(--spacing, .25rem);font-size:10.5px;line-height:1.4;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgb(var(--og-text-faint));-webkit-user-select:none;user-select:none;letter-spacing:var(--og-tracking-label)}.drawer-section-rule{margin-inline:calc(var(--spacing, .25rem) * 3);margin-block:calc(var(--spacing, .25rem) * 3);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default))}.drawer-item-wrapper{position:relative}.drawer-item{position:relative;display:flex;width:100%;cursor:pointer;align-items:center;--tw-border-style: none;border-style:none;background-color:transparent;text-decoration-line:none;margin-top:1px;min-height:30px;padding:6px 8px;font-size:var(--og-fs-sm);color:rgb(var(--og-text-body));border-radius:var(--og-radius-control);transition:background-color .13s ease,color .13s ease}.drawer-item:hover{color:rgb(var(--og-text));background:rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item:hover{background:color-mix(in srgb,rgb(var(--og-text)) 8%,transparent)}}.drawer-item.active{color:rgb(var(--og-text));font-weight:500;background:rgb(var(--og-text));box-shadow:inset 0 0 0 1px rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item.active{background:color-mix(in srgb,rgb(var(--og-text)) 6%,transparent)}}@supports (color: color-mix(in lab,red,red)){.drawer-item.active{box-shadow:inset 0 0 0 1px color-mix(in srgb,rgb(var(--og-text)) 7%,transparent)}}.drawer-item.active:hover{background:rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item.active:hover{background:color-mix(in srgb,rgb(var(--og-text)) 9%,transparent)}}.drawer-item:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.drawer-item{-webkit-tap-highlight-color:transparent;--tw-outline-style: none;outline-style:none}.item-content{display:flex;width:100%;align-items:center;gap:calc(var(--spacing, .25rem) * 3);overflow:hidden}.item-icon{flex-shrink:0;color:inherit}.item-label{flex:1;overflow:hidden;text-align:left;--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);text-overflow:ellipsis;white-space:nowrap;font-size:inherit}.chevron-icon{margin-left:auto;flex-shrink:0;color:rgb(var(--text-primary));transition:transform .3s cubic-bezier(.4,0,.2,1)}.chevron-icon.rotated{transform:rotate(180deg)}.accordion-children{overflow:hidden}.drawer-version{display:flex;align-items:center;justify-content:center;border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);font-size:11px;color:rgb(var(--text-placeholder));-webkit-user-select:none;user-select:none;font-family:var(--og-font-mono)}.drawer-version-dot{display:block;height:calc(var(--spacing, .25rem) * 1.5);width:calc(var(--spacing, .25rem) * 1.5);border-radius:calc(infinity * 1px);background-color:rgb(var(--text-placeholder))}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-leading: initial;--tw-font-weight: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-outline-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], animations: [
4150
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DrawerComponent, isStandalone: true, selector: "fx-ui-drawer", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, footerItems: { classPropertyName: "footerItems", publicName: "footerItems", isSignal: true, isRequired: false, transformFunction: null }, isExpanded: { classPropertyName: "isExpanded", publicName: "isExpanded", isSignal: false, isRequired: false, transformFunction: null }, logo: { classPropertyName: "logo", publicName: "logo", isSignal: false, isRequired: false, transformFunction: null }, logoText: { classPropertyName: "logoText", publicName: "logoText", isSignal: false, isRequired: false, transformFunction: null }, logoSubtext: { classPropertyName: "logoSubtext", publicName: "logoSubtext", isSignal: false, isRequired: false, transformFunction: null }, version: { classPropertyName: "version", publicName: "version", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { toggleExpanded: "toggleExpanded", itemClicked: "itemClicked" }, ngImport: i0, template: "<div class=\"drawer-host-wrapper\">\n <a href=\"/\" class=\"drawer-brand\" [class.is-collapsed]=\"!isExpanded\">\n <!-- Logo sits in its own sunken tile (reference `.sb-logo`, measured 16/09):\n the tile is what carries the hover border, so the mark keeps its own\n size and nothing shifts by a pixel when that border appears. -->\n <span class=\"drawer-brand-logo\">\n @if (logo) {\n <img [src]=\"logo\" alt=\"Home\" class=\"drawer-brand-mark\" />\n } @else {\n <span class=\"drawer-brand-mark\"></span>\n }\n </span>\n @if (isExpanded && (logoText || logoSubtext)) {\n <span class=\"drawer-brand-copy\">\n @if (logoText) {\n <span class=\"drawer-brand-text\">{{ logoText }}</span>\n }\n @if (logoSubtext) {\n <span class=\"drawer-brand-subtext\">{{ logoSubtext }}</span>\n }\n </span>\n }\n </a>\n <div class=\"drawer-container\" [@drawerExpand]=\"getDrawerState()\">\n <!-- Navigation Items -->\n <nav class=\"drawer-nav\">\n @for (item of items(); track item) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n }\n </nav>\n\n <!-- V2: bottom section (Administration group, user block, \u2026) -->\n <div class=\"mt-auto\">\n @if (footerItems().length) {\n <nav class=\"drawer-footer-nav\">\n @for (item of footerItems(); track item) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n }\n </nav>\n }\n <ng-content select=\"[drawerFooter]\"></ng-content>\n </div>\n\n <!-- Version -->\n @if (version) {\n <div class=\"drawer-version\" [matTooltip]=\"!isExpanded ? version : ''\" matTooltipPosition=\"right\">\n @if (isExpanded) {\n <span>{{ version }}</span>\n }\n @if (!isExpanded) {\n <span class=\"drawer-version-dot\"></span>\n }\n </div>\n }\n </div>\n\n <!-- Floating Toggle Button on right border -->\n <button\n type=\"button\"\n class=\"toggle-btn\"\n [attr.aria-label]=\"isExpanded ? 'Collapse navigation' : 'Expand navigation'\"\n [attr.aria-expanded]=\"isExpanded\"\n (click)=\"toggleDrawer()\"\n [matTooltip]=\"isExpanded ? 'Collapse' : 'Expand'\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n matTooltipPosition=\"right\"\n >\n <fx-ui-hero-icon\n [icon]=\"isExpanded ? 'chevron-left' : 'chevron-right'\"\n [size]=\"16\"\n class=\"text-text-primary\"\n ></fx-ui-hero-icon>\n </button>\n</div>\n\n<!-- Recursive Template for Drawer Items -->\n<ng-template #drawerItem let-item let-level=\"level\">\n @if (item.heading) {\n @if (isExpanded) {\n <div class=\"drawer-section-label\">{{ item.label }}</div>\n } @else {\n <div class=\"drawer-section-rule\"></div>\n }\n } @else {\n <div class=\"drawer-item-wrapper\">\n <!-- Item Button/Link -->\n @if (item.route && !item.children) {\n <a\n [routerLink]=\"item.route\"\n class=\"drawer-item\"\n [class.is-collapsed]=\"!isExpanded\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 8 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n @if (item.iconData) {\n <lucide-icon\n [img]=\"item.iconData\"\n [size]=\"isExpanded ? 13 : 18\"\n class=\"item-icon\"\n [class.item-icon--solid]=\"item.iconSolid\"\n />\n } @else {\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"isExpanded ? 13 : 18\" class=\"item-icon\"></fx-ui-hero-icon>\n }\n @if (isExpanded) {\n <span class=\"item-label\">{{ item.label }}</span>\n }\n </div>\n </a>\n }\n\n @if (!item.route || item.children) {\n <button\n type=\"button\"\n class=\"drawer-item\"\n [class.is-collapsed]=\"!isExpanded\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 8 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-sm font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n @if (item.iconData) {\n <lucide-icon\n [img]=\"item.iconData\"\n [size]=\"isExpanded ? 13 : 18\"\n class=\"item-icon\"\n [class.item-icon--solid]=\"item.iconSolid\"\n />\n } @else {\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"isExpanded ? 13 : 18\" class=\"item-icon\"></fx-ui-hero-icon>\n }\n @if (isExpanded) {\n <span class=\"item-label\">{{ item.label }}</span>\n }\n @if (isExpanded && item.children && item.children.length > 0) {\n <fx-ui-hero-icon\n [icon]=\"'chevron-down'\"\n [size]=\"16\"\n class=\"chevron-icon\"\n [class.rotated]=\"isAccordionExpanded(item.id)\"\n ></fx-ui-hero-icon>\n }\n </div>\n </button>\n }\n\n <!-- Accordion Children -->\n @if (item.children && item.children.length > 0) {\n <div\n class=\"accordion-children\"\n [@accordionExpand]=\"getAccordionState(item.id)\"\n >\n @for (child of item.children; track child) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: child, level: level + 1 }\"\n ></ng-container>\n }\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer properties;:host{display:block;height:100%}.drawer-host-wrapper{position:relative;display:flex;height:100%;flex-direction:column;background:rgb(var(--og-surface-sunken));border-right:1px solid rgb(var(--og-border-subtle))}.drawer-brand{display:flex;height:60px;flex-shrink:0;align-items:center;gap:10px;overflow:hidden;text-decoration-line:none;padding-left:16px;padding-right:8px;transition:padding .3s cubic-bezier(.4,0,.2,1);max-width:260px}.drawer-brand.is-collapsed{justify-content:center;padding-left:0;padding-right:0;max-width:70px}.drawer-brand-logo{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;padding:6px;border-radius:var(--og-radius-control);background:rgb(var(--og-surface-sunken));border:1px solid transparent;transition:padding .22s cubic-bezier(.4,0,.2,1),border-color .15s ease}.drawer-brand:hover .drawer-brand-logo{border-color:rgb(var(--og-border))}.drawer-brand.is-collapsed .drawer-brand-logo{padding:4px}.drawer-brand-mark{height:21px;width:auto;flex-shrink:0}.drawer-brand.is-collapsed .drawer-brand-mark{height:19px}.drawer-brand-copy{display:flex;min-width:0px;flex-direction:column;--tw-leading: var(--leading-tight, 1.25);line-height:var(--leading-tight, 1.25)}.drawer-brand-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--og-fs-lg);--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgb(var(--text-primary));-webkit-user-select:none;user-select:none;font-family:var(--og-font-display);letter-spacing:-.015em;line-height:1}.drawer-brand-subtext{margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:9px;--tw-font-weight: var(--font-weight-normal, 400);font-weight:var(--font-weight-normal, 400);-webkit-user-select:none;user-select:none;color:rgb(var(--og-text-muted));letter-spacing:.02em;line-height:1.2}.drawer-container{display:flex;min-height:0px;flex:1;flex-direction:column;overflow:hidden;padding-inline:calc(var(--spacing, .25rem) * 2);transition:width .3s cubic-bezier(.4,0,.2,1)}.toggle-btn{position:absolute;top:50%;z-index:50;display:flex;height:calc(var(--spacing, .25rem) * 6);width:calc(var(--spacing, .25rem) * 6);--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-default));background-color:rgb(var(--bg-primary));--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(var(--shadow-color) / .05));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);right:-12px;transition:background-color .2s ease,box-shadow .2s ease}.toggle-btn:before{content:\"\";position:absolute;inset:-8px;border-radius:9999px}.toggle-btn:hover{background-color:rgb(var(--bg-primary-hover));--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.toggle-btn:active{background-color:rgb(var(--bg-primary-hover))}.toggle-btn:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.drawer-footer-nav{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default));padding-inline:0px;padding-top:calc(var(--spacing, .25rem) * 2);padding-bottom:var(--spacing, .25rem)}.drawer-nav{flex:1;overflow-x:hidden;overflow-y:auto;padding-block:calc(var(--spacing, .25rem) * 2)}.drawer-nav::-webkit-scrollbar{width:calc(var(--spacing, .25rem) * 1.5)}.drawer-nav::-webkit-scrollbar-track{background-color:transparent}.drawer-nav::-webkit-scrollbar-thumb{border-radius:var(--og-radius-control);background-color:rgb(var(--border-default))}.drawer-nav::-webkit-scrollbar-thumb:hover{background-color:rgb(var(--text-placeholder))}.drawer-section-label{padding-inline:1rem;padding-top:calc(var(--spacing, .25rem) * 4);padding-bottom:var(--spacing, .25rem);font-size:10.5px;line-height:1.4;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgb(var(--og-text-faint));-webkit-user-select:none;user-select:none;letter-spacing:var(--og-tracking-label)}.drawer-section-rule{margin-inline:calc(var(--spacing, .25rem) * 3);margin-block:calc(var(--spacing, .25rem) * 3);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default))}.drawer-item-wrapper{position:relative}.drawer-item{position:relative;display:flex;width:100%;cursor:pointer;align-items:center;--tw-border-style: none;border-style:none;background-color:transparent;text-decoration-line:none;margin-top:1px;min-height:30px;padding:6px 8px;font-size:var(--og-fs-sm);color:rgb(var(--og-text-body));border-radius:var(--og-radius-control);transition:background-color .13s ease,color .13s ease}.drawer-item:hover{color:rgb(var(--og-text));background:rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item:hover{background:color-mix(in srgb,rgb(var(--og-text)) 8%,transparent)}}.drawer-item.active{color:rgb(var(--og-text));font-weight:500;background:rgb(var(--og-text));box-shadow:inset 0 0 0 1px rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item.active{background:color-mix(in srgb,rgb(var(--og-text)) 6%,transparent)}}@supports (color: color-mix(in lab,red,red)){.drawer-item.active{box-shadow:inset 0 0 0 1px color-mix(in srgb,rgb(var(--og-text)) 7%,transparent)}}.drawer-item.active:hover{background:rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item.active:hover{background:color-mix(in srgb,rgb(var(--og-text)) 9%,transparent)}}.drawer-item:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.drawer-item{-webkit-tap-highlight-color:transparent;--tw-outline-style: none;outline-style:none}.item-content{display:flex;width:100%;align-items:center;gap:9px;overflow:hidden}.drawer-item.is-collapsed .item-icon{width:18px;height:18px;margin-right:0}.item-icon--solid{fill:currentColor}:host ::ng-deep .item-icon--solid svg,:host ::ng-deep .item-icon--solid path,:host ::ng-deep .item-icon--solid polygon{fill:currentColor}.item-icon{flex-shrink:0;color:inherit;width:13px;height:13px;margin-right:5px}.item-label{flex:1;overflow:hidden;text-align:left;--tw-leading: 18px;line-height:18px;text-overflow:ellipsis;white-space:nowrap;font-size:inherit}.chevron-icon{margin-left:auto;flex-shrink:0;color:rgb(var(--text-primary));transition:transform .3s cubic-bezier(.4,0,.2,1)}.chevron-icon.rotated{transform:rotate(180deg)}.accordion-children{overflow:hidden}.drawer-version{display:flex;align-items:center;justify-content:center;border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);font-size:11px;color:rgb(var(--text-placeholder));-webkit-user-select:none;user-select:none;font-family:var(--og-font-mono)}.drawer-version-dot{display:block;height:calc(var(--spacing, .25rem) * 1.5);width:calc(var(--spacing, .25rem) * 1.5);border-radius:calc(infinity * 1px);background-color:rgb(var(--text-placeholder))}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-leading: initial;--tw-font-weight: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-outline-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }, { kind: "ngmodule", type: FxLucideModule }, { kind: "component", type: i1$4.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], animations: [
4099
4151
  trigger('drawerExpand', [
4100
4152
  state('collapsed', style({ width: '70px' })),
4101
4153
  state('expanded', style({ width: '260px' })),
@@ -4125,8 +4177,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
4125
4177
  NgTemplateOutlet,
4126
4178
  MatTooltip,
4127
4179
  HeroIconComponent,
4180
+ FxLucideModule,
4128
4181
  RouterLink,
4129
- ], template: "<div class=\"drawer-host-wrapper\">\n <a href=\"/\" class=\"drawer-brand\" [class.is-collapsed]=\"!isExpanded\">\n @if (logo) {\n <img [src]=\"logo\" alt=\"Home\" class=\"drawer-brand-mark\" />\n } @else {\n <div class=\"drawer-brand-mark\"></div>\n }\n @if (isExpanded && (logoText || logoSubtext)) {\n <span class=\"drawer-brand-copy\">\n @if (logoText) {\n <span class=\"drawer-brand-text\">{{ logoText }}</span>\n }\n @if (logoSubtext) {\n <span class=\"drawer-brand-subtext\">{{ logoSubtext }}</span>\n }\n </span>\n }\n </a>\n <div class=\"drawer-container\" [@drawerExpand]=\"getDrawerState()\">\n <!-- Navigation Items -->\n <nav class=\"drawer-nav\">\n @for (item of items(); track item) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n }\n </nav>\n\n <!-- V2: bottom section (Administration group, user block, \u2026) -->\n <div class=\"mt-auto\">\n @if (footerItems().length) {\n <nav class=\"drawer-footer-nav\">\n @for (item of footerItems(); track item) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n }\n </nav>\n }\n <ng-content select=\"[drawerFooter]\"></ng-content>\n </div>\n\n <!-- Version -->\n @if (version) {\n <div class=\"drawer-version\" [matTooltip]=\"!isExpanded ? version : ''\" matTooltipPosition=\"right\">\n @if (isExpanded) {\n <span>{{ version }}</span>\n }\n @if (!isExpanded) {\n <span class=\"drawer-version-dot\"></span>\n }\n </div>\n }\n </div>\n\n <!-- Floating Toggle Button on right border -->\n <button\n type=\"button\"\n class=\"toggle-btn\"\n [attr.aria-label]=\"isExpanded ? 'Collapse navigation' : 'Expand navigation'\"\n [attr.aria-expanded]=\"isExpanded\"\n (click)=\"toggleDrawer()\"\n [matTooltip]=\"isExpanded ? 'Collapse' : 'Expand'\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n matTooltipPosition=\"right\"\n >\n <fx-ui-hero-icon\n [icon]=\"isExpanded ? 'chevron-left' : 'chevron-right'\"\n [size]=\"16\"\n class=\"text-text-primary\"\n ></fx-ui-hero-icon>\n </button>\n</div>\n\n<!-- Recursive Template for Drawer Items -->\n<ng-template #drawerItem let-item let-level=\"level\">\n @if (item.heading) {\n @if (isExpanded) {\n <div class=\"drawer-section-label\">{{ item.label }}</div>\n } @else {\n <div class=\"drawer-section-rule\"></div>\n }\n } @else {\n <div class=\"drawer-item-wrapper\">\n <!-- Item Button/Link -->\n @if (item.route && !item.children) {\n <a\n [routerLink]=\"item.route\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 8 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n @if (isExpanded) {\n <span class=\"item-label\">{{ item.label }}</span>\n }\n </div>\n </a>\n }\n\n @if (!item.route || item.children) {\n <button\n type=\"button\"\n class=\"drawer-item\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 8 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-sm font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"20\" class=\"item-icon\"></fx-ui-hero-icon>\n @if (isExpanded) {\n <span class=\"item-label\">{{ item.label }}</span>\n }\n @if (isExpanded && item.children && item.children.length > 0) {\n <fx-ui-hero-icon\n [icon]=\"'chevron-down'\"\n [size]=\"16\"\n class=\"chevron-icon\"\n [class.rotated]=\"isAccordionExpanded(item.id)\"\n ></fx-ui-hero-icon>\n }\n </div>\n </button>\n }\n\n <!-- Accordion Children -->\n @if (item.children && item.children.length > 0) {\n <div\n class=\"accordion-children\"\n [@accordionExpand]=\"getAccordionState(item.id)\"\n >\n @for (child of item.children; track child) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: child, level: level + 1 }\"\n ></ng-container>\n }\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer properties;:host{display:block;height:100%}.drawer-host-wrapper{position:relative;display:flex;height:100%;flex-direction:column;background:rgb(var(--og-surface-sunken));border-right:1px solid rgb(var(--og-border-subtle))}.drawer-brand{display:flex;height:60px;flex-shrink:0;align-items:center;gap:calc(var(--spacing, .25rem) * 3);overflow:hidden;text-decoration-line:none;padding-left:16px;padding-right:8px;transition:padding .3s cubic-bezier(.4,0,.2,1);max-width:260px}.drawer-brand.is-collapsed{justify-content:center;padding-left:0;padding-right:0;max-width:70px}.drawer-brand-mark{height:20px;width:auto;flex-shrink:0}.drawer-brand-copy{display:flex;min-width:0px;flex-direction:column;--tw-leading: var(--leading-tight, 1.25);line-height:var(--leading-tight, 1.25)}.drawer-brand-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:15px;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgb(var(--text-primary));-webkit-user-select:none;user-select:none;font-family:var(--og-font-display);letter-spacing:-.01em}.drawer-brand-subtext{margin-top:1px;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;font-size:11px;color:rgb(var(--text-placeholder));-webkit-user-select:none;user-select:none;letter-spacing:var(--og-tracking-label)}.drawer-container{display:flex;min-height:0px;flex:1;flex-direction:column;overflow:hidden;padding-inline:calc(var(--spacing, .25rem) * 2);transition:width .3s cubic-bezier(.4,0,.2,1)}.toggle-btn{position:absolute;top:50%;z-index:50;display:flex;height:calc(var(--spacing, .25rem) * 6);width:calc(var(--spacing, .25rem) * 6);--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-default));background-color:rgb(var(--bg-primary));--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(var(--shadow-color) / .05));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);right:-12px;transition:all .2s ease}.toggle-btn:hover{background-color:rgb(var(--bg-primary-hover));--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.toggle-btn:active{transform:translateY(-50%) scale(.9)}.toggle-btn:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.drawer-footer-nav{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default));padding-inline:0px;padding-top:calc(var(--spacing, .25rem) * 2);padding-bottom:var(--spacing, .25rem)}.drawer-nav{flex:1;overflow-x:hidden;overflow-y:auto;padding-block:calc(var(--spacing, .25rem) * 2)}.drawer-nav::-webkit-scrollbar{width:calc(var(--spacing, .25rem) * 1.5)}.drawer-nav::-webkit-scrollbar-track{background-color:transparent}.drawer-nav::-webkit-scrollbar-thumb{border-radius:var(--og-radius-control);background-color:rgb(var(--border-default))}.drawer-nav::-webkit-scrollbar-thumb:hover{background-color:rgb(var(--text-placeholder))}.drawer-section-label{padding-inline:1rem;padding-top:calc(var(--spacing, .25rem) * 4);padding-bottom:var(--spacing, .25rem);font-size:10.5px;line-height:1.4;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgb(var(--og-text-faint));-webkit-user-select:none;user-select:none;letter-spacing:var(--og-tracking-label)}.drawer-section-rule{margin-inline:calc(var(--spacing, .25rem) * 3);margin-block:calc(var(--spacing, .25rem) * 3);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default))}.drawer-item-wrapper{position:relative}.drawer-item{position:relative;display:flex;width:100%;cursor:pointer;align-items:center;--tw-border-style: none;border-style:none;background-color:transparent;text-decoration-line:none;margin-top:1px;min-height:30px;padding:6px 8px;font-size:var(--og-fs-sm);color:rgb(var(--og-text-body));border-radius:var(--og-radius-control);transition:background-color .13s ease,color .13s ease}.drawer-item:hover{color:rgb(var(--og-text));background:rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item:hover{background:color-mix(in srgb,rgb(var(--og-text)) 8%,transparent)}}.drawer-item.active{color:rgb(var(--og-text));font-weight:500;background:rgb(var(--og-text));box-shadow:inset 0 0 0 1px rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item.active{background:color-mix(in srgb,rgb(var(--og-text)) 6%,transparent)}}@supports (color: color-mix(in lab,red,red)){.drawer-item.active{box-shadow:inset 0 0 0 1px color-mix(in srgb,rgb(var(--og-text)) 7%,transparent)}}.drawer-item.active:hover{background:rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item.active:hover{background:color-mix(in srgb,rgb(var(--og-text)) 9%,transparent)}}.drawer-item:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.drawer-item{-webkit-tap-highlight-color:transparent;--tw-outline-style: none;outline-style:none}.item-content{display:flex;width:100%;align-items:center;gap:calc(var(--spacing, .25rem) * 3);overflow:hidden}.item-icon{flex-shrink:0;color:inherit}.item-label{flex:1;overflow:hidden;text-align:left;--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);text-overflow:ellipsis;white-space:nowrap;font-size:inherit}.chevron-icon{margin-left:auto;flex-shrink:0;color:rgb(var(--text-primary));transition:transform .3s cubic-bezier(.4,0,.2,1)}.chevron-icon.rotated{transform:rotate(180deg)}.accordion-children{overflow:hidden}.drawer-version{display:flex;align-items:center;justify-content:center;border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);font-size:11px;color:rgb(var(--text-placeholder));-webkit-user-select:none;user-select:none;font-family:var(--og-font-mono)}.drawer-version-dot{display:block;height:calc(var(--spacing, .25rem) * 1.5);width:calc(var(--spacing, .25rem) * 1.5);border-radius:calc(infinity * 1px);background-color:rgb(var(--text-placeholder))}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-leading: initial;--tw-font-weight: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-outline-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
4182
+ ], template: "<div class=\"drawer-host-wrapper\">\n <a href=\"/\" class=\"drawer-brand\" [class.is-collapsed]=\"!isExpanded\">\n <!-- Logo sits in its own sunken tile (reference `.sb-logo`, measured 16/09):\n the tile is what carries the hover border, so the mark keeps its own\n size and nothing shifts by a pixel when that border appears. -->\n <span class=\"drawer-brand-logo\">\n @if (logo) {\n <img [src]=\"logo\" alt=\"Home\" class=\"drawer-brand-mark\" />\n } @else {\n <span class=\"drawer-brand-mark\"></span>\n }\n </span>\n @if (isExpanded && (logoText || logoSubtext)) {\n <span class=\"drawer-brand-copy\">\n @if (logoText) {\n <span class=\"drawer-brand-text\">{{ logoText }}</span>\n }\n @if (logoSubtext) {\n <span class=\"drawer-brand-subtext\">{{ logoSubtext }}</span>\n }\n </span>\n }\n </a>\n <div class=\"drawer-container\" [@drawerExpand]=\"getDrawerState()\">\n <!-- Navigation Items -->\n <nav class=\"drawer-nav\">\n @for (item of items(); track item) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n }\n </nav>\n\n <!-- V2: bottom section (Administration group, user block, \u2026) -->\n <div class=\"mt-auto\">\n @if (footerItems().length) {\n <nav class=\"drawer-footer-nav\">\n @for (item of footerItems(); track item) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: item, level: 0 }\"\n ></ng-container>\n }\n </nav>\n }\n <ng-content select=\"[drawerFooter]\"></ng-content>\n </div>\n\n <!-- Version -->\n @if (version) {\n <div class=\"drawer-version\" [matTooltip]=\"!isExpanded ? version : ''\" matTooltipPosition=\"right\">\n @if (isExpanded) {\n <span>{{ version }}</span>\n }\n @if (!isExpanded) {\n <span class=\"drawer-version-dot\"></span>\n }\n </div>\n }\n </div>\n\n <!-- Floating Toggle Button on right border -->\n <button\n type=\"button\"\n class=\"toggle-btn\"\n [attr.aria-label]=\"isExpanded ? 'Collapse navigation' : 'Expand navigation'\"\n [attr.aria-expanded]=\"isExpanded\"\n (click)=\"toggleDrawer()\"\n [matTooltip]=\"isExpanded ? 'Collapse' : 'Expand'\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n matTooltipPosition=\"right\"\n >\n <fx-ui-hero-icon\n [icon]=\"isExpanded ? 'chevron-left' : 'chevron-right'\"\n [size]=\"16\"\n class=\"text-text-primary\"\n ></fx-ui-hero-icon>\n </button>\n</div>\n\n<!-- Recursive Template for Drawer Items -->\n<ng-template #drawerItem let-item let-level=\"level\">\n @if (item.heading) {\n @if (isExpanded) {\n <div class=\"drawer-section-label\">{{ item.label }}</div>\n } @else {\n <div class=\"drawer-section-rule\"></div>\n }\n } @else {\n <div class=\"drawer-item-wrapper\">\n <!-- Item Button/Link -->\n @if (item.route && !item.children) {\n <a\n [routerLink]=\"item.route\"\n class=\"drawer-item\"\n [class.is-collapsed]=\"!isExpanded\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 8 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-xs font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n @if (item.iconData) {\n <lucide-icon\n [img]=\"item.iconData\"\n [size]=\"isExpanded ? 13 : 18\"\n class=\"item-icon\"\n [class.item-icon--solid]=\"item.iconSolid\"\n />\n } @else {\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"isExpanded ? 13 : 18\" class=\"item-icon\"></fx-ui-hero-icon>\n }\n @if (isExpanded) {\n <span class=\"item-label\">{{ item.label }}</span>\n }\n </div>\n </a>\n }\n\n @if (!item.route || item.children) {\n <button\n type=\"button\"\n class=\"drawer-item\"\n [class.is-collapsed]=\"!isExpanded\"\n [class.active]=\"isActiveRoute(item.route)\"\n [class.has-children]=\"item.children && item.children.length > 0\"\n [style.paddingLeft.px]=\"isExpanded ? 8 + level * 16 : 16\"\n [matTooltip]=\"!isExpanded ? item.label : ''\"\n matTooltipPosition=\"right\"\n matTooltipClass=\"text-text-primary text-sm font-semibold\"\n (click)=\"onItemClick(item, $event)\"\n >\n <div class=\"item-content\">\n @if (item.iconData) {\n <lucide-icon\n [img]=\"item.iconData\"\n [size]=\"isExpanded ? 13 : 18\"\n class=\"item-icon\"\n [class.item-icon--solid]=\"item.iconSolid\"\n />\n } @else {\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"isExpanded ? 13 : 18\" class=\"item-icon\"></fx-ui-hero-icon>\n }\n @if (isExpanded) {\n <span class=\"item-label\">{{ item.label }}</span>\n }\n @if (isExpanded && item.children && item.children.length > 0) {\n <fx-ui-hero-icon\n [icon]=\"'chevron-down'\"\n [size]=\"16\"\n class=\"chevron-icon\"\n [class.rotated]=\"isAccordionExpanded(item.id)\"\n ></fx-ui-hero-icon>\n }\n </div>\n </button>\n }\n\n <!-- Accordion Children -->\n @if (item.children && item.children.length > 0) {\n <div\n class=\"accordion-children\"\n [@accordionExpand]=\"getAccordionState(item.id)\"\n >\n @for (child of item.children; track child) {\n <ng-container\n *ngTemplateOutlet=\"drawerItem; context: { $implicit: child, level: level + 1 }\"\n ></ng-container>\n }\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer properties;:host{display:block;height:100%}.drawer-host-wrapper{position:relative;display:flex;height:100%;flex-direction:column;background:rgb(var(--og-surface-sunken));border-right:1px solid rgb(var(--og-border-subtle))}.drawer-brand{display:flex;height:60px;flex-shrink:0;align-items:center;gap:10px;overflow:hidden;text-decoration-line:none;padding-left:16px;padding-right:8px;transition:padding .3s cubic-bezier(.4,0,.2,1);max-width:260px}.drawer-brand.is-collapsed{justify-content:center;padding-left:0;padding-right:0;max-width:70px}.drawer-brand-logo{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;padding:6px;border-radius:var(--og-radius-control);background:rgb(var(--og-surface-sunken));border:1px solid transparent;transition:padding .22s cubic-bezier(.4,0,.2,1),border-color .15s ease}.drawer-brand:hover .drawer-brand-logo{border-color:rgb(var(--og-border))}.drawer-brand.is-collapsed .drawer-brand-logo{padding:4px}.drawer-brand-mark{height:21px;width:auto;flex-shrink:0}.drawer-brand.is-collapsed .drawer-brand-mark{height:19px}.drawer-brand-copy{display:flex;min-width:0px;flex-direction:column;--tw-leading: var(--leading-tight, 1.25);line-height:var(--leading-tight, 1.25)}.drawer-brand-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--og-fs-lg);--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgb(var(--text-primary));-webkit-user-select:none;user-select:none;font-family:var(--og-font-display);letter-spacing:-.015em;line-height:1}.drawer-brand-subtext{margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:9px;--tw-font-weight: var(--font-weight-normal, 400);font-weight:var(--font-weight-normal, 400);-webkit-user-select:none;user-select:none;color:rgb(var(--og-text-muted));letter-spacing:.02em;line-height:1.2}.drawer-container{display:flex;min-height:0px;flex:1;flex-direction:column;overflow:hidden;padding-inline:calc(var(--spacing, .25rem) * 2);transition:width .3s cubic-bezier(.4,0,.2,1)}.toggle-btn{position:absolute;top:50%;z-index:50;display:flex;height:calc(var(--spacing, .25rem) * 6);width:calc(var(--spacing, .25rem) * 6);--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgb(var(--border-default));background-color:rgb(var(--bg-primary));--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(var(--shadow-color) / .05));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);right:-12px;transition:background-color .2s ease,box-shadow .2s ease}.toggle-btn:before{content:\"\";position:absolute;inset:-8px;border-radius:9999px}.toggle-btn:hover{background-color:rgb(var(--bg-primary-hover));--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(var(--shadow-color) / .1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(var(--shadow-color) / .1));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.toggle-btn:active{background-color:rgb(var(--bg-primary-hover))}.toggle-btn:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.drawer-footer-nav{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default));padding-inline:0px;padding-top:calc(var(--spacing, .25rem) * 2);padding-bottom:var(--spacing, .25rem)}.drawer-nav{flex:1;overflow-x:hidden;overflow-y:auto;padding-block:calc(var(--spacing, .25rem) * 2)}.drawer-nav::-webkit-scrollbar{width:calc(var(--spacing, .25rem) * 1.5)}.drawer-nav::-webkit-scrollbar-track{background-color:transparent}.drawer-nav::-webkit-scrollbar-thumb{border-radius:var(--og-radius-control);background-color:rgb(var(--border-default))}.drawer-nav::-webkit-scrollbar-thumb:hover{background-color:rgb(var(--text-placeholder))}.drawer-section-label{padding-inline:1rem;padding-top:calc(var(--spacing, .25rem) * 4);padding-bottom:var(--spacing, .25rem);font-size:10.5px;line-height:1.4;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgb(var(--og-text-faint));-webkit-user-select:none;user-select:none;letter-spacing:var(--og-tracking-label)}.drawer-section-rule{margin-inline:calc(var(--spacing, .25rem) * 3);margin-block:calc(var(--spacing, .25rem) * 3);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default))}.drawer-item-wrapper{position:relative}.drawer-item{position:relative;display:flex;width:100%;cursor:pointer;align-items:center;--tw-border-style: none;border-style:none;background-color:transparent;text-decoration-line:none;margin-top:1px;min-height:30px;padding:6px 8px;font-size:var(--og-fs-sm);color:rgb(var(--og-text-body));border-radius:var(--og-radius-control);transition:background-color .13s ease,color .13s ease}.drawer-item:hover{color:rgb(var(--og-text));background:rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item:hover{background:color-mix(in srgb,rgb(var(--og-text)) 8%,transparent)}}.drawer-item.active{color:rgb(var(--og-text));font-weight:500;background:rgb(var(--og-text));box-shadow:inset 0 0 0 1px rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item.active{background:color-mix(in srgb,rgb(var(--og-text)) 6%,transparent)}}@supports (color: color-mix(in lab,red,red)){.drawer-item.active{box-shadow:inset 0 0 0 1px color-mix(in srgb,rgb(var(--og-text)) 7%,transparent)}}.drawer-item.active:hover{background:rgb(var(--og-text))}@supports (color: color-mix(in lab,red,red)){.drawer-item.active:hover{background:color-mix(in srgb,rgb(var(--og-text)) 9%,transparent)}}.drawer-item:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgb(var(--primary))}.drawer-item{-webkit-tap-highlight-color:transparent;--tw-outline-style: none;outline-style:none}.item-content{display:flex;width:100%;align-items:center;gap:9px;overflow:hidden}.drawer-item.is-collapsed .item-icon{width:18px;height:18px;margin-right:0}.item-icon--solid{fill:currentColor}:host ::ng-deep .item-icon--solid svg,:host ::ng-deep .item-icon--solid path,:host ::ng-deep .item-icon--solid polygon{fill:currentColor}.item-icon{flex-shrink:0;color:inherit;width:13px;height:13px;margin-right:5px}.item-label{flex:1;overflow:hidden;text-align:left;--tw-leading: 18px;line-height:18px;text-overflow:ellipsis;white-space:nowrap;font-size:inherit}.chevron-icon{margin-left:auto;flex-shrink:0;color:rgb(var(--text-primary));transition:transform .3s cubic-bezier(.4,0,.2,1)}.chevron-icon.rotated{transform:rotate(180deg)}.accordion-children{overflow:hidden}.drawer-version{display:flex;align-items:center;justify-content:center;border-top-style:var(--tw-border-style);border-top-width:1px;border-color:rgb(var(--border-default));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);font-size:11px;color:rgb(var(--text-placeholder));-webkit-user-select:none;user-select:none;font-family:var(--og-font-mono)}.drawer-version-dot{display:block;height:calc(var(--spacing, .25rem) * 1.5);width:calc(var(--spacing, .25rem) * 1.5);border-radius:calc(infinity * 1px);background-color:rgb(var(--text-placeholder))}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-leading: initial;--tw-font-weight: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-outline-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
4130
4183
  }], ctorParameters: () => [{ type: i2$1.Router }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], footerItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerItems", required: false }] }], isExpanded: [{
4131
4184
  type: Input
4132
4185
  }], logo: [{
@@ -4314,7 +4367,7 @@ class StatCardsComponent {
4314
4367
  return card.subTone ? toneText(card.subTone) : 'text-text-secondary';
4315
4368
  }
4316
4369
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: StatCardsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4317
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: StatCardsComponent, isStandalone: true, selector: "fx-ui-stat-cards", inputs: { cards: { classPropertyName: "cards", publicName: "cards", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, cols: { classPropertyName: "cols", publicName: "cols", isSignal: false, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"fx-stat-grid grid gap-normal\" [ngStyle]=\"{ 'grid-template-columns': gridColumns }\">\n @for (card of cards(); track card.label) {\n @if (variant() === 'figure') {\n <div class=\"card-common flex flex-col gap-small min-w-0\">\n <span class=\"txt-utility-caption text-text-secondary truncate\">{{ card.label }}</span>\n <span\n class=\"txt-heading-03\"\n [ngClass]=\"numClass(card)\"\n style=\"font-family: var(--og-font-figure)\"\n ><span [fxCountUp]=\"card.value\"></span></span\n >\n @if (card.sub) {\n <span class=\"txt-utility-caption\" [ngClass]=\"subClass(card)\">{{ card.sub }}</span>\n }\n @if (card.spark?.length) {\n <fx-ui-sparkbar class=\"mt-xs\" [values]=\"card.spark!\" [height]=\"16\" [tone]=\"card.tone ?? null\" />\n }\n </div>\n } @else {\n <div class=\"card-common flex items-center gap-normal\">\n @if (card.icon) {\n <div\n class=\"w-10 h-10 rounded-lg flex items-center justify-center shrink-0\"\n [ngClass]=\"boxClass(card)\"\n >\n <fx-ui-hero-icon [icon]=\"card.icon\" [size]=\"20\"></fx-ui-hero-icon>\n </div>\n }\n <div class=\"flex flex-col min-w-0 flex-1\">\n <div class=\"flex items-baseline gap-small\">\n <span\n class=\"txt-heading-04\"\n [ngClass]=\"numClass(card)\"\n style=\"font-family: var(--og-font-figure)\"\n ><span [fxCountUp]=\"card.value\"></span></span\n >\n @if (card.delta) {\n <span class=\"text-xs font-semibold\" [ngClass]=\"deltaClass(card)\">\n {{ card.delta.direction === 'up' ? '\u25B2' : '\u25BC' }} {{ card.delta.value }}\n </span>\n }\n </div>\n <span class=\"txt-utility-caption text-text-secondary truncate\">{{ card.label }}</span>\n @if (card.spark?.length) {\n <fx-ui-sparkbar class=\"mt-xs\" [values]=\"card.spark!\" [height]=\"16\" [tone]=\"card.tone ?? null\" />\n }\n </div>\n </div>\n }\n }\n</div>\n", styles: [":host{display:block;container-type:inline-size}@container (width < 720px){.fx-stat-grid{grid-template-columns:repeat(2,minmax(0,1fr))!important}}@container (width < 360px){.fx-stat-grid{grid-template-columns:minmax(0,1fr)!important}}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }, { kind: "component", type: SparkbarComponent, selector: "fx-ui-sparkbar", inputs: ["values", "tone", "height"] }, { kind: "directive", type: FxCountUpDirective, selector: "[fxCountUp]", inputs: ["fxCountUp"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4370
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: StatCardsComponent, isStandalone: true, selector: "fx-ui-stat-cards", inputs: { cards: { classPropertyName: "cards", publicName: "cards", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, cols: { classPropertyName: "cols", publicName: "cols", isSignal: false, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<!-- variant=\"figure\" is ONE ruled panel, not N cards: the reference draws the\n KPI strip as a single bordered box divided by hairlines (`.og-statgrid\n --ruled`, measured 16/09). It was four separate `card-common` boxes with an\n 8px gap, which read as four objects where the design has one. The other\n variant keeps its own cards. -->\n<div\n class=\"fx-stat-grid grid\"\n [class.fx-stat-grid--ruled]=\"variant() === 'figure'\"\n [class.gap-normal]=\"variant() !== 'figure'\"\n [ngStyle]=\"{ 'grid-template-columns': gridColumns }\"\n>\n @for (card of cards(); track card.label; let last = $last) {\n @if (variant() === 'figure') {\n <div class=\"fx-stat-cell flex flex-col min-w-0\" [class.fx-stat-cell--last]=\"last\">\n <span class=\"fx-stat-cell__label truncate\">{{ card.label }}</span>\n <span\n class=\"fx-stat-cell__figure\"\n [ngClass]=\"numClass(card)\"\n ><span [fxCountUp]=\"card.value\"></span></span\n >\n @if (card.sub) {\n <span class=\"fx-stat-cell__sub\" [ngClass]=\"subClass(card)\">{{ card.sub }}</span>\n }\n @if (card.spark?.length) {\n <fx-ui-sparkbar class=\"mt-xs\" [values]=\"card.spark!\" [height]=\"16\" [tone]=\"card.tone ?? null\" />\n }\n </div>\n } @else {\n <div class=\"card-common flex items-center gap-normal\">\n @if (card.icon) {\n <div\n class=\"w-10 h-10 rounded-lg flex items-center justify-center shrink-0\"\n [ngClass]=\"boxClass(card)\"\n >\n <fx-ui-hero-icon [icon]=\"card.icon\" [size]=\"20\"></fx-ui-hero-icon>\n </div>\n }\n <div class=\"flex flex-col min-w-0 flex-1\">\n <div class=\"flex items-baseline gap-small\">\n <span\n class=\"txt-heading-04\"\n [ngClass]=\"numClass(card)\"\n style=\"font-family: var(--og-font-figure)\"\n ><span [fxCountUp]=\"card.value\"></span></span\n >\n @if (card.delta) {\n <span class=\"text-xs font-semibold\" [ngClass]=\"deltaClass(card)\">\n {{ card.delta.direction === 'up' ? '\u25B2' : '\u25BC' }} {{ card.delta.value }}\n </span>\n }\n </div>\n <span class=\"txt-utility-caption text-text-secondary truncate\">{{ card.label }}</span>\n @if (card.spark?.length) {\n <fx-ui-sparkbar class=\"mt-xs\" [values]=\"card.spark!\" [height]=\"16\" [tone]=\"card.tone ?? null\" />\n }\n </div>\n </div>\n }\n }\n</div>\n", styles: [":host{display:block;container-type:inline-size}@container (width < 720px){.fx-stat-grid{grid-template-columns:repeat(2,minmax(0,1fr))!important}}@container (width < 360px){.fx-stat-grid{grid-template-columns:minmax(0,1fr)!important}}.fx-stat-grid--ruled{gap:0;border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-control);background:rgb(var(--og-surface));box-shadow:0 1px 2px #0000000a,0 3px 8px #0000000d;overflow:hidden}.fx-stat-grid--ruled .fx-stat-cell{padding:10px 12px;border-right:1px solid rgb(var(--og-border))}.fx-stat-grid--ruled .fx-stat-cell--last{border-right:0}.fx-stat-cell__label{font-size:var(--og-fs-2xs);font-weight:400;color:rgb(var(--og-text-faint))}.fx-stat-cell__figure{font-family:var(--og-font-figure);font-size:32px;font-weight:400;line-height:1.1;color:rgb(var(--og-text));margin-top:6px}.fx-stat-cell__sub{font-size:var(--og-fs-xs);font-weight:400;color:rgb(var(--og-text-muted));margin-top:4px}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }, { kind: "component", type: SparkbarComponent, selector: "fx-ui-sparkbar", inputs: ["values", "tone", "height"] }, { kind: "directive", type: FxCountUpDirective, selector: "[fxCountUp]", inputs: ["fxCountUp"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4318
4371
  }
4319
4372
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: StatCardsComponent, decorators: [{
4320
4373
  type: Component,
@@ -4324,7 +4377,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
4324
4377
  HeroIconComponent,
4325
4378
  SparkbarComponent,
4326
4379
  FxCountUpDirective,
4327
- ], template: "<div class=\"fx-stat-grid grid gap-normal\" [ngStyle]=\"{ 'grid-template-columns': gridColumns }\">\n @for (card of cards(); track card.label) {\n @if (variant() === 'figure') {\n <div class=\"card-common flex flex-col gap-small min-w-0\">\n <span class=\"txt-utility-caption text-text-secondary truncate\">{{ card.label }}</span>\n <span\n class=\"txt-heading-03\"\n [ngClass]=\"numClass(card)\"\n style=\"font-family: var(--og-font-figure)\"\n ><span [fxCountUp]=\"card.value\"></span></span\n >\n @if (card.sub) {\n <span class=\"txt-utility-caption\" [ngClass]=\"subClass(card)\">{{ card.sub }}</span>\n }\n @if (card.spark?.length) {\n <fx-ui-sparkbar class=\"mt-xs\" [values]=\"card.spark!\" [height]=\"16\" [tone]=\"card.tone ?? null\" />\n }\n </div>\n } @else {\n <div class=\"card-common flex items-center gap-normal\">\n @if (card.icon) {\n <div\n class=\"w-10 h-10 rounded-lg flex items-center justify-center shrink-0\"\n [ngClass]=\"boxClass(card)\"\n >\n <fx-ui-hero-icon [icon]=\"card.icon\" [size]=\"20\"></fx-ui-hero-icon>\n </div>\n }\n <div class=\"flex flex-col min-w-0 flex-1\">\n <div class=\"flex items-baseline gap-small\">\n <span\n class=\"txt-heading-04\"\n [ngClass]=\"numClass(card)\"\n style=\"font-family: var(--og-font-figure)\"\n ><span [fxCountUp]=\"card.value\"></span></span\n >\n @if (card.delta) {\n <span class=\"text-xs font-semibold\" [ngClass]=\"deltaClass(card)\">\n {{ card.delta.direction === 'up' ? '\u25B2' : '\u25BC' }} {{ card.delta.value }}\n </span>\n }\n </div>\n <span class=\"txt-utility-caption text-text-secondary truncate\">{{ card.label }}</span>\n @if (card.spark?.length) {\n <fx-ui-sparkbar class=\"mt-xs\" [values]=\"card.spark!\" [height]=\"16\" [tone]=\"card.tone ?? null\" />\n }\n </div>\n </div>\n }\n }\n</div>\n", styles: [":host{display:block;container-type:inline-size}@container (width < 720px){.fx-stat-grid{grid-template-columns:repeat(2,minmax(0,1fr))!important}}@container (width < 360px){.fx-stat-grid{grid-template-columns:minmax(0,1fr)!important}}\n"] }]
4380
+ ], template: "<!-- variant=\"figure\" is ONE ruled panel, not N cards: the reference draws the\n KPI strip as a single bordered box divided by hairlines (`.og-statgrid\n --ruled`, measured 16/09). It was four separate `card-common` boxes with an\n 8px gap, which read as four objects where the design has one. The other\n variant keeps its own cards. -->\n<div\n class=\"fx-stat-grid grid\"\n [class.fx-stat-grid--ruled]=\"variant() === 'figure'\"\n [class.gap-normal]=\"variant() !== 'figure'\"\n [ngStyle]=\"{ 'grid-template-columns': gridColumns }\"\n>\n @for (card of cards(); track card.label; let last = $last) {\n @if (variant() === 'figure') {\n <div class=\"fx-stat-cell flex flex-col min-w-0\" [class.fx-stat-cell--last]=\"last\">\n <span class=\"fx-stat-cell__label truncate\">{{ card.label }}</span>\n <span\n class=\"fx-stat-cell__figure\"\n [ngClass]=\"numClass(card)\"\n ><span [fxCountUp]=\"card.value\"></span></span\n >\n @if (card.sub) {\n <span class=\"fx-stat-cell__sub\" [ngClass]=\"subClass(card)\">{{ card.sub }}</span>\n }\n @if (card.spark?.length) {\n <fx-ui-sparkbar class=\"mt-xs\" [values]=\"card.spark!\" [height]=\"16\" [tone]=\"card.tone ?? null\" />\n }\n </div>\n } @else {\n <div class=\"card-common flex items-center gap-normal\">\n @if (card.icon) {\n <div\n class=\"w-10 h-10 rounded-lg flex items-center justify-center shrink-0\"\n [ngClass]=\"boxClass(card)\"\n >\n <fx-ui-hero-icon [icon]=\"card.icon\" [size]=\"20\"></fx-ui-hero-icon>\n </div>\n }\n <div class=\"flex flex-col min-w-0 flex-1\">\n <div class=\"flex items-baseline gap-small\">\n <span\n class=\"txt-heading-04\"\n [ngClass]=\"numClass(card)\"\n style=\"font-family: var(--og-font-figure)\"\n ><span [fxCountUp]=\"card.value\"></span></span\n >\n @if (card.delta) {\n <span class=\"text-xs font-semibold\" [ngClass]=\"deltaClass(card)\">\n {{ card.delta.direction === 'up' ? '\u25B2' : '\u25BC' }} {{ card.delta.value }}\n </span>\n }\n </div>\n <span class=\"txt-utility-caption text-text-secondary truncate\">{{ card.label }}</span>\n @if (card.spark?.length) {\n <fx-ui-sparkbar class=\"mt-xs\" [values]=\"card.spark!\" [height]=\"16\" [tone]=\"card.tone ?? null\" />\n }\n </div>\n </div>\n }\n }\n</div>\n", styles: [":host{display:block;container-type:inline-size}@container (width < 720px){.fx-stat-grid{grid-template-columns:repeat(2,minmax(0,1fr))!important}}@container (width < 360px){.fx-stat-grid{grid-template-columns:minmax(0,1fr)!important}}.fx-stat-grid--ruled{gap:0;border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-control);background:rgb(var(--og-surface));box-shadow:0 1px 2px #0000000a,0 3px 8px #0000000d;overflow:hidden}.fx-stat-grid--ruled .fx-stat-cell{padding:10px 12px;border-right:1px solid rgb(var(--og-border))}.fx-stat-grid--ruled .fx-stat-cell--last{border-right:0}.fx-stat-cell__label{font-size:var(--og-fs-2xs);font-weight:400;color:rgb(var(--og-text-faint))}.fx-stat-cell__figure{font-family:var(--og-font-figure);font-size:32px;font-weight:400;line-height:1.1;color:rgb(var(--og-text));margin-top:6px}.fx-stat-cell__sub{font-size:var(--og-fs-xs);font-weight:400;color:rgb(var(--og-text-muted));margin-top:4px}\n"] }]
4328
4381
  }], propDecorators: { cards: [{ type: i0.Input, args: [{ isSignal: true, alias: "cards", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], cols: [{
4329
4382
  type: Input
4330
4383
  }] } });
@@ -4431,7 +4484,9 @@ class FilterPillsComponent {
4431
4484
  */
4432
4485
  pillClass(option) {
4433
4486
  if (!this.isActive(option)) {
4434
- return 'border-border-default text-text-secondary hover:border-border-strong';
4487
+ // White ground, not transparent: the reference's inactive pill sits on
4488
+ // --og-surface with a hairline, which is what separates it from the page.
4489
+ return 'bg-surface border-border-default text-text-secondary hover:border-border-strong';
4435
4490
  }
4436
4491
  return this.tone() === 'solid'
4437
4492
  ? 'bg-text-primary text-text-inverse border-text-primary'
@@ -4441,11 +4496,11 @@ class FilterPillsComponent {
4441
4496
  return this.isActive(option) ? 'opacity-70' : 'text-text-placeholder';
4442
4497
  }
4443
4498
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: FilterPillsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4444
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: FilterPillsComponent, isStandalone: true, selector: "fx-ui-filter-pills", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"flex flex-wrap items-center gap-small\">\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n [attr.aria-pressed]=\"isActive(option)\"\n class=\"flex items-center gap-small px-[11px] py-[4px] rounded-full border txt-utility-caption font-bold cursor-pointer transition-colors\"\n [ngClass]=\"pillClass(option)\"\n (click)=\"valueChange.emit(option.value)\"\n >\n {{ option.label }}\n @if (option.count !== null && option.count !== undefined) {\n <span [ngClass]=\"countClass(option)\" style=\"font-family: var(--og-font-figure)\">{{ option.count }}</span>\n }\n </button>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4499
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: FilterPillsComponent, isStandalone: true, selector: "fx-ui-filter-pills", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"flex flex-wrap items-center gap-small\">\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n [attr.aria-pressed]=\"isActive(option)\"\n class=\"fx-pill flex items-center rounded-full border cursor-pointer transition-colors\"\n [ngClass]=\"pillClass(option)\"\n (click)=\"valueChange.emit(option.value)\"\n >\n @if (option.icon) {\n <lucide-icon [img]=\"option.icon\" [size]=\"14\" class=\"flex shrink-0\" />\n }\n {{ option.label }}\n @if (option.count !== null && option.count !== undefined) {\n <span [ngClass]=\"countClass(option)\" style=\"font-family: var(--og-font-figure)\">{{ option.count }}</span>\n }\n </button>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.fx-pill{padding:6px 12px;gap:7px;font-size:var(--og-fs-xs);font-weight:600;line-height:15px}.fx-pill:hover{box-shadow:0 2px 7px #0000001f}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FxLucideModule }, { kind: "component", type: i1$4.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4445
4500
  }
4446
4501
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: FilterPillsComponent, decorators: [{
4447
4502
  type: Component,
4448
- args: [{ selector: 'fx-ui-filter-pills', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgClass], template: "<div class=\"flex flex-wrap items-center gap-small\">\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n [attr.aria-pressed]=\"isActive(option)\"\n class=\"flex items-center gap-small px-[11px] py-[4px] rounded-full border txt-utility-caption font-bold cursor-pointer transition-colors\"\n [ngClass]=\"pillClass(option)\"\n (click)=\"valueChange.emit(option.value)\"\n >\n {{ option.label }}\n @if (option.count !== null && option.count !== undefined) {\n <span [ngClass]=\"countClass(option)\" style=\"font-family: var(--og-font-figure)\">{{ option.count }}</span>\n }\n </button>\n }\n</div>\n" }]
4503
+ args: [{ selector: 'fx-ui-filter-pills', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgClass, FxLucideModule], template: "<div class=\"flex flex-wrap items-center gap-small\">\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n [attr.aria-pressed]=\"isActive(option)\"\n class=\"fx-pill flex items-center rounded-full border cursor-pointer transition-colors\"\n [ngClass]=\"pillClass(option)\"\n (click)=\"valueChange.emit(option.value)\"\n >\n @if (option.icon) {\n <lucide-icon [img]=\"option.icon\" [size]=\"14\" class=\"flex shrink-0\" />\n }\n {{ option.label }}\n @if (option.count !== null && option.count !== undefined) {\n <span [ngClass]=\"countClass(option)\" style=\"font-family: var(--og-font-figure)\">{{ option.count }}</span>\n }\n </button>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.fx-pill{padding:6px 12px;gap:7px;font-size:var(--og-fs-xs);font-weight:600;line-height:15px}.fx-pill:hover{box-shadow:0 2px 7px #0000001f}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
4449
4504
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
4450
4505
 
4451
4506
  /**
@@ -4586,11 +4641,11 @@ class SectionCardComponent {
4586
4641
  return iconTone ? toneText(iconTone) : 'text-text-secondary';
4587
4642
  }
4588
4643
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: SectionCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4589
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: SectionCardComponent, isStandalone: true, selector: "fx-ui-section-card", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: false, isRequired: false, transformFunction: null }, iconTone: { classPropertyName: "iconTone", publicName: "iconTone", isSignal: true, isRequired: false, transformFunction: null }, titleStyle: { classPropertyName: "titleStyle", publicName: "titleStyle", isSignal: true, isRequired: false, transformFunction: null }, aside: { classPropertyName: "aside", publicName: "aside", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "flex flex-col min-w-0" }, ngImport: i0, template: "<!-- flex column filling the host: cards in one grid row share a height and a\n projected footer can sit at the bottom with `mt-auto` (design 01). -->\n<div class=\"card-common bg-surface flex flex-col flex-1 min-h-0\">\n @if (titleStyle() === 'eyebrow') {\n <div class=\"flex items-baseline justify-between gap-normal mb-large\">\n <span class=\"txt-eyebrow\">{{ title() }}</span>\n @if (aside()) {\n <span class=\"txt-utility-description text-right\">{{ aside() }}</span>\n }\n </div>\n } @else {\n <div class=\"flex items-center gap-normal mb-large\">\n @if (icon) {\n <fx-ui-hero-icon [ngClass]=\"iconClass\" [icon]=\"icon\" [size]=\"18\"></fx-ui-hero-icon>\n }\n <span class=\"txt-heading-06\">{{ title() }}</span>\n </div>\n }\n <ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4644
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: SectionCardComponent, isStandalone: true, selector: "fx-ui-section-card", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: false, isRequired: false, transformFunction: null }, iconTone: { classPropertyName: "iconTone", publicName: "iconTone", isSignal: true, isRequired: false, transformFunction: null }, titleStyle: { classPropertyName: "titleStyle", publicName: "titleStyle", isSignal: true, isRequired: false, transformFunction: null }, aside: { classPropertyName: "aside", publicName: "aside", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "flex flex-col min-w-0" }, ngImport: i0, template: "<!-- flex column filling the host: cards in one grid row share a height and a\n projected footer can sit at the bottom with `mt-auto` (design 01). -->\n<!-- The card itself carries NO padding: the reference pads the header and the\n body separately (10px 12px 4px / 10px 12px, measured 16/09), which is what\n lets the header rule sit flush and the body start tight under it. -->\n<div class=\"card-common fx-section-card bg-surface flex flex-col flex-1 min-h-0\">\n @if (titleStyle() === 'eyebrow') {\n <div class=\"fx-section-card__head flex items-baseline justify-between gap-normal\">\n <span class=\"txt-eyebrow\">{{ title() }}</span>\n @if (aside()) {\n <span class=\"fx-section-card__aside text-right\">{{ aside() }}</span>\n }\n </div>\n } @else {\n <div class=\"fx-section-card__head flex items-center gap-normal\">\n @if (icon) {\n <fx-ui-hero-icon [ngClass]=\"iconClass\" [icon]=\"icon\" [size]=\"18\"></fx-ui-hero-icon>\n }\n <span class=\"txt-heading-06\">{{ title() }}</span>\n </div>\n }\n <div class=\"fx-section-card__body flex flex-col flex-1 min-h-0\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";.fx-section-card{padding:0}.fx-section-card__head{padding:10px 12px 4px}.fx-section-card__body{padding:10px 12px}.fx-section-card__aside{font-size:var(--og-fs-xs);font-weight:400;line-height:1.4;color:rgb(var(--og-text-faint))}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4590
4645
  }
4591
4646
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: SectionCardComponent, decorators: [{
4592
4647
  type: Component,
4593
- args: [{ selector: 'fx-ui-section-card', changeDetection: ChangeDetectionStrategy.OnPush, imports: [HeroIconComponent, NgClass], host: { class: 'flex flex-col min-w-0' }, template: "<!-- flex column filling the host: cards in one grid row share a height and a\n projected footer can sit at the bottom with `mt-auto` (design 01). -->\n<div class=\"card-common bg-surface flex flex-col flex-1 min-h-0\">\n @if (titleStyle() === 'eyebrow') {\n <div class=\"flex items-baseline justify-between gap-normal mb-large\">\n <span class=\"txt-eyebrow\">{{ title() }}</span>\n @if (aside()) {\n <span class=\"txt-utility-description text-right\">{{ aside() }}</span>\n }\n </div>\n } @else {\n <div class=\"flex items-center gap-normal mb-large\">\n @if (icon) {\n <fx-ui-hero-icon [ngClass]=\"iconClass\" [icon]=\"icon\" [size]=\"18\"></fx-ui-hero-icon>\n }\n <span class=\"txt-heading-06\">{{ title() }}</span>\n </div>\n }\n <ng-content></ng-content>\n</div>\n" }]
4648
+ args: [{ selector: 'fx-ui-section-card', changeDetection: ChangeDetectionStrategy.OnPush, imports: [HeroIconComponent, NgClass], host: { class: 'flex flex-col min-w-0' }, template: "<!-- flex column filling the host: cards in one grid row share a height and a\n projected footer can sit at the bottom with `mt-auto` (design 01). -->\n<!-- The card itself carries NO padding: the reference pads the header and the\n body separately (10px 12px 4px / 10px 12px, measured 16/09), which is what\n lets the header rule sit flush and the body start tight under it. -->\n<div class=\"card-common fx-section-card bg-surface flex flex-col flex-1 min-h-0\">\n @if (titleStyle() === 'eyebrow') {\n <div class=\"fx-section-card__head flex items-baseline justify-between gap-normal\">\n <span class=\"txt-eyebrow\">{{ title() }}</span>\n @if (aside()) {\n <span class=\"fx-section-card__aside text-right\">{{ aside() }}</span>\n }\n </div>\n } @else {\n <div class=\"fx-section-card__head flex items-center gap-normal\">\n @if (icon) {\n <fx-ui-hero-icon [ngClass]=\"iconClass\" [icon]=\"icon\" [size]=\"18\"></fx-ui-hero-icon>\n }\n <span class=\"txt-heading-06\">{{ title() }}</span>\n </div>\n }\n <div class=\"fx-section-card__body flex flex-col flex-1 min-h-0\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";.fx-section-card{padding:0}.fx-section-card__head{padding:10px 12px 4px}.fx-section-card__body{padding:10px 12px}.fx-section-card__aside{font-size:var(--og-fs-xs);font-weight:400;line-height:1.4;color:rgb(var(--og-text-faint))}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
4594
4649
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], icon: [{
4595
4650
  type: Input
4596
4651
  }], iconTone: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconTone", required: false }] }], titleStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleStyle", required: false }] }], aside: [{ type: i0.Input, args: [{ isSignal: true, alias: "aside", required: false }] }] } });
@@ -4703,8 +4758,30 @@ class MasterDetailComponent {
4703
4758
  /** True when the host projected a [mdFacets] block — switches to 3 columns. */
4704
4759
  hasFacets = false;
4705
4760
  table;
4706
- /** Fixed list-pane width on lg+ viewports (stacked full-width below). */
4707
- listWidth = input('420px', ...(ngDevMode ? [{ debugName: "listWidth" }] : /* istanbul ignore next */ []));
4761
+ /**
4762
+ * Optional CAP on the list pane.
4763
+ *
4764
+ * The list is the flexible track now (the design's `204px 1fr 340px`), so it
4765
+ * takes whatever the rail and the detail leave. Set this only for a screen
4766
+ * whose rows genuinely stop reading better past some width; left unset the
4767
+ * property is not emitted at all and the pane simply fills.
4768
+ */
4769
+ listWidth = input(null, ...(ngDevMode ? [{ debugName: "listWidth" }] : /* istanbul ignore next */ []));
4770
+ /**
4771
+ * The detail pane's fixed track — the design's own 340px, and 450px on the
4772
+ * screens anh Tú widened (17/09).
4773
+ *
4774
+ * Pass `'auto'` (or null) to give the detail the SPARE width instead and cap
4775
+ * the list at `listWidth` — the pre-0.7.130 behaviour, which Binaries and
4776
+ * Remediation keep because their panes carry header blocks, metric grids and
4777
+ * step lists that a narrow track cannot hold.
4778
+ */
4779
+ detailWidth = input('340px', ...(ngDevMode ? [{ debugName: "detailWidth" }] : /* istanbul ignore next */ []));
4780
+ /** true = detail takes the spare width; false = it is a fixed track. */
4781
+ detailFluid() {
4782
+ const w = this.detailWidth();
4783
+ return !w || w === 'auto';
4784
+ }
4708
4785
  /**
4709
4786
  * Chiều cao CHUNG của cả ba cụm (facets / list / detail). Mặc định trừ phần
4710
4787
  * chrome phía trên (topbar + tab strip + context line) khỏi viewport để
@@ -4806,7 +4883,7 @@ class MasterDetailComponent {
4806
4883
  el?.scrollIntoView({ block: 'nearest' });
4807
4884
  }
4808
4885
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: MasterDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4809
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: MasterDetailComponent, isStandalone: true, selector: "fx-ui-master-detail", inputs: { table: { classPropertyName: "table", publicName: "table", isSignal: false, isRequired: false, transformFunction: null }, listWidth: { classPropertyName: "listWidth", publicName: "listWidth", isSignal: true, isRequired: false, transformFunction: null }, paneHeight: { classPropertyName: "paneHeight", publicName: "paneHeight", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, listLabel: { classPropertyName: "listLabel", publicName: "listLabel", isSignal: true, isRequired: false, transformFunction: null }, skeletonCards: { classPropertyName: "skeletonCards", publicName: "skeletonCards", isSignal: true, isRequired: false, transformFunction: null }, paginationStyle: { classPropertyName: "paginationStyle", publicName: "paginationStyle", isSignal: true, isRequired: false, transformFunction: null }, prevLabel: { classPropertyName: "prevLabel", publicName: "prevLabel", isSignal: true, isRequired: false, transformFunction: null }, nextLabel: { classPropertyName: "nextLabel", publicName: "nextLabel", isSignal: true, isRequired: false, transformFunction: null }, pageLabel: { classPropertyName: "pageLabel", publicName: "pageLabel", isSignal: true, isRequired: false, transformFunction: null }, ofLabel: { classPropertyName: "ofLabel", publicName: "ofLabel", isSignal: true, isRequired: false, transformFunction: null }, facetsOpen: { classPropertyName: "facetsOpen", publicName: "facetsOpen", isSignal: true, isRequired: false, transformFunction: null }, facetsLabel: { classPropertyName: "facetsLabel", publicName: "facetsLabel", isSignal: true, isRequired: false, transformFunction: null }, expandFacetsLabel: { classPropertyName: "expandFacetsLabel", publicName: "expandFacetsLabel", isSignal: true, isRequired: false, transformFunction: null }, collapseFacetsLabel: { classPropertyName: "collapseFacetsLabel", publicName: "collapseFacetsLabel", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "rowTemplate", first: true, predicate: ["row"], descendants: true, isSignal: true }, { propertyName: "detailTemplate", first: true, predicate: ["detail"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- ONE frame, three columns (designs 11/14/17): facets rail \u00B7 list \u00B7 detail,\n separated by hairlines, sharing one height; only each pane's body scrolls.\n Row direction / height come from the CONTAINER QUERY in the CSS (the row's\n own width), never from `lg:*` \u2014 see the note at the top of the .css. -->\n<div\n class=\"fx-md-row flex flex-col items-stretch\"\n [class.fx-md-row--rail-open]=\"railOpen()\"\n [style.--fx-md-list-width]=\"listWidth()\"\n [style.--fx-md-pane-h]=\"paneHeight()\"\n>\n <!-- Facets rail \u2014 collapsible (\u00BB / \u00AB), collapsed = 40px strip with the label\n set vertically. The [mdFacets] projection is unconditional (hasFacets is\n detected by querying it) and only hidden while collapsed. -->\n <aside\n class=\"fx-md-facets flex flex-col min-h-0 min-w-0\"\n [class.hidden]=\"!hasFacets\"\n [class.fx-md-facets--open]=\"railOpen()\"\n >\n <div class=\"fx-md-rail-head flex items-center gap-small px-normal py-semi border-b border-border-subtle shrink-0\" [class.hidden]=\"!railOpen()\">\n <button\n type=\"button\"\n class=\"flex items-center justify-center w-6 h-6 rounded-control text-muted hover:bg-bg-hover shrink-0\"\n [attr.aria-label]=\"collapseFacetsLabel()\"\n (click)=\"railOpen.set(false)\"\n >\n <fx-ui-hero-icon icon=\"chevron-double-left\" [size]=\"14\" class=\"flex\" />\n </button>\n <span class=\"txt-eyebrow truncate\">{{ facetsLabel() }}</span>\n </div>\n <div class=\"flex-1 min-h-0 overflow-auto px-normal py-normal\" [class.hidden]=\"!railOpen()\">\n <ng-content select=\"[mdFacets]\"></ng-content>\n </div>\n <button\n type=\"button\"\n class=\"fx-md-rail-toggle flex items-center gap-normal text-muted hover:bg-bg-hover transition-colors\"\n [class.hidden]=\"railOpen()\"\n [attr.aria-label]=\"expandFacetsLabel()\"\n [attr.aria-expanded]=\"false\"\n (click)=\"railOpen.set(true)\"\n >\n <fx-ui-hero-icon icon=\"chevron-double-right\" [size]=\"14\" class=\"flex shrink-0\" />\n <span class=\"fx-md-rail-label txt-eyebrow whitespace-nowrap\">{{ facetsLabel() }}</span>\n </button>\n </aside>\n\n <!-- Master: list pane -->\n <section class=\"fx-md-list flex flex-col min-h-0 min-w-0\">\n <div class=\"fx-md-list-head px-large pt-large pb-small border-b border-border-subtle shrink-0\">\n <ng-content select=\"[mdHeader]\"></ng-content>\n <ng-content select=\"[mdFilters]\"></ng-content>\n </div>\n <div\n class=\"flex flex-col overflow-auto flex-1 min-h-0\"\n role=\"listbox\"\n [attr.aria-label]=\"listLabel() || null\"\n >\n @if (table.loading) {\n <div class=\"px-large py-normal\">\n <fx-ui-skeleton-list [rows]=\"table.pageSize\"></fx-ui-skeleton-list>\n </div>\n } @else {\n @for (row of table.dataSource.data; track table.idOf(row); let i = $index) {\n <!-- Hairline rows; the selected one is tinted with an accent bar on\n the left (design 11/14 selected finding / component). -->\n <button\n type=\"button\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(row)\"\n [tabindex]=\"isSelected(row) ? 0 : -1\"\n (click)=\"table.selectItem(row)\"\n (keydown)=\"onKeydown($event, i)\"\n class=\"text-left px-large py-semi border-l-2 border-b border-b-border-subtle transition-colors\"\n [ngClass]=\"\n isSelected(row)\n ? 'border-l-accent bg-accent-soft/60'\n : 'border-l-transparent hover:bg-surface-sunken'\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: row, selected: isSelected(row) }\"\n ></ng-container>\n </button>\n }\n @if (!table.dataSource.data.length) {\n <div class=\"px-large py-large\">\n <fx-ui-empty-state [message]=\"emptyMessage()\"></fx-ui-empty-state>\n </div>\n }\n }\n </div>\n <!-- No (page) binding: ngAfterViewInit hands this paginator to the table,\n whose own BaseTable subscription (running after this child's hook)\n wires page events \u2014 a binding here would double-fetch every change. -->\n <mat-paginator\n class=\"fx-md-paginator\"\n [class.fx-md-paginator-hidden]=\"paginationStyle() === 'prevnext'\"\n [length]=\"table.total\"\n [pageSize]=\"table.pageSize\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n ></mat-paginator>\n @if (paginationStyle() === 'prevnext') {\n <div class=\"flex items-center justify-between gap-normal px-large py-normal border-t border-border-subtle shrink-0\">\n <span class=\"txt-utility-description\" style=\"font-family: var(--og-font-figure)\">\n {{ rangeText() }}\n </span>\n <div class=\"flex gap-small\">\n <button\n type=\"button\"\n class=\"btn-alternative !h-8 !px-semi text-sm\"\n [disabled]=\"!paginator().hasPreviousPage()\"\n (click)=\"paginator().previousPage()\"\n >\n {{ prevLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"btn-alternative !h-8 !px-semi text-sm\"\n [disabled]=\"!paginator().hasNextPage()\"\n (click)=\"paginator().nextPage()\"\n >\n {{ nextLabel() }}\n </button>\n </div>\n </div>\n }\n </section>\n\n <!-- Detail pane -->\n <section class=\"fx-md-detail w-full min-w-0 flex flex-col min-h-0\">\n <div class=\"flex-1 min-h-0 overflow-auto px-large py-large\">\n @if (table.selected) {\n @if (table.detailBusy) {\n <fx-ui-skeleton-detail [cards]=\"skeletonCards()\"></fx-ui-skeleton-detail>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"detailTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: table.detail, selected: table.selected }\"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-center h-full min-h-[300px]\">\n <fx-ui-empty-state [message]=\"emptyMessage()\"></fx-ui-empty-state>\n </div>\n }\n </div>\n </section>\n</div>\n", styles: [":host{display:block;container-type:inline-size;container-name:fx-md}.fx-md-row{background:rgb(var(--og-surface));border:1px solid rgb(var(--border-default));border-radius:.5rem;overflow:hidden;box-shadow:0 1px 2px #0000000a,0 3px 8px #0000000d}.fx-md-facets,.fx-md-list{border-bottom:1px solid rgb(var(--og-border-subtle))}.fx-md-rail-toggle{width:100%;padding:10px 12px;flex-direction:row;justify-content:flex-start}.fx-md-list,.fx-md-facets{width:100%}@container fx-md (min-width: 720px){.fx-md-row{flex-direction:row;height:var(--fx-md-pane-h, calc(100vh - 300px) );min-height:420px}.fx-md-facets,.fx-md-list{border-bottom:0;border-right:1px solid rgb(var(--og-border-subtle))}.fx-md-facets{flex:0 0 40px;width:40px;min-width:40px;max-width:40px}.fx-md-facets--open{flex:0 1 230px;width:auto;min-width:200px;max-width:260px}.fx-md-rail-toggle{height:100%;padding:12px 0;flex-direction:column;justify-content:flex-start}.fx-md-rail-label{writing-mode:vertical-rl;transform:rotate(180deg)}.fx-md-list{width:auto;flex:0 1 var(--fx-md-list-width, 420px);min-width:260px;max-width:var(--fx-md-list-width, 420px)}.fx-md-detail{flex:1 1 360px;min-width:360px}}.fx-md-paginator-hidden{position:absolute!important;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}.fx-md-paginator{padding:0 12px;border-top:1px solid rgb(var(--og-border-subtle))}\n"], dependencies: [{ kind: "component", type: SkeletonListComponent, selector: "fx-ui-skeleton-list", inputs: ["rows"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: EmptyStateComponent, selector: "fx-ui-empty-state", inputs: ["message", "icon", "height"] }, { kind: "component", type: MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: SkeletonDetailComponent, selector: "fx-ui-skeleton-detail", inputs: ["cards", "rows"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
4886
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: MasterDetailComponent, isStandalone: true, selector: "fx-ui-master-detail", inputs: { table: { classPropertyName: "table", publicName: "table", isSignal: false, isRequired: false, transformFunction: null }, listWidth: { classPropertyName: "listWidth", publicName: "listWidth", isSignal: true, isRequired: false, transformFunction: null }, detailWidth: { classPropertyName: "detailWidth", publicName: "detailWidth", isSignal: true, isRequired: false, transformFunction: null }, paneHeight: { classPropertyName: "paneHeight", publicName: "paneHeight", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, listLabel: { classPropertyName: "listLabel", publicName: "listLabel", isSignal: true, isRequired: false, transformFunction: null }, skeletonCards: { classPropertyName: "skeletonCards", publicName: "skeletonCards", isSignal: true, isRequired: false, transformFunction: null }, paginationStyle: { classPropertyName: "paginationStyle", publicName: "paginationStyle", isSignal: true, isRequired: false, transformFunction: null }, prevLabel: { classPropertyName: "prevLabel", publicName: "prevLabel", isSignal: true, isRequired: false, transformFunction: null }, nextLabel: { classPropertyName: "nextLabel", publicName: "nextLabel", isSignal: true, isRequired: false, transformFunction: null }, pageLabel: { classPropertyName: "pageLabel", publicName: "pageLabel", isSignal: true, isRequired: false, transformFunction: null }, ofLabel: { classPropertyName: "ofLabel", publicName: "ofLabel", isSignal: true, isRequired: false, transformFunction: null }, facetsOpen: { classPropertyName: "facetsOpen", publicName: "facetsOpen", isSignal: true, isRequired: false, transformFunction: null }, facetsLabel: { classPropertyName: "facetsLabel", publicName: "facetsLabel", isSignal: true, isRequired: false, transformFunction: null }, expandFacetsLabel: { classPropertyName: "expandFacetsLabel", publicName: "expandFacetsLabel", isSignal: true, isRequired: false, transformFunction: null }, collapseFacetsLabel: { classPropertyName: "collapseFacetsLabel", publicName: "collapseFacetsLabel", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "rowTemplate", first: true, predicate: ["row"], descendants: true, isSignal: true }, { propertyName: "detailTemplate", first: true, predicate: ["detail"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- ONE frame, three columns (designs 11/14/17): facets rail \u00B7 list \u00B7 detail,\n separated by hairlines, sharing one height; only each pane's body scrolls.\n Row direction / height come from the CONTAINER QUERY in the CSS (the row's\n own width), never from `lg:*` \u2014 see the note at the top of the .css. -->\n<div\n class=\"fx-md-row flex flex-col items-stretch\"\n [class.fx-md-row--rail-open]=\"railOpen()\"\n [style.--fx-md-list-width]=\"listWidth()\"\n [class.fx-md-row--detail-fluid]=\"detailFluid()\"\n [style.--fx-md-detail-width]=\"detailFluid() ? null : detailWidth()\"\n [style.--fx-md-pane-h]=\"paneHeight()\"\n>\n <!-- Facets rail \u2014 collapsible (\u00BB / \u00AB), collapsed = 40px strip with the label\n set vertically. The [mdFacets] projection is unconditional (hasFacets is\n detected by querying it) and only hidden while collapsed. -->\n <aside\n class=\"fx-md-facets flex flex-col min-h-0 min-w-0\"\n [class.hidden]=\"!hasFacets\"\n [class.fx-md-facets--open]=\"railOpen()\"\n >\n <div class=\"fx-md-rail-head flex items-center gap-small px-normal py-semi border-b border-border-subtle shrink-0\" [class.hidden]=\"!railOpen()\">\n <button\n type=\"button\"\n class=\"flex items-center justify-center w-6 h-6 rounded-control text-muted hover:bg-bg-hover shrink-0\"\n [attr.aria-label]=\"collapseFacetsLabel()\"\n (click)=\"railOpen.set(false)\"\n >\n <fx-ui-hero-icon icon=\"chevron-double-left\" [size]=\"14\" class=\"flex\" />\n </button>\n <span class=\"txt-eyebrow truncate\">{{ facetsLabel() }}</span>\n </div>\n <div class=\"flex-1 min-h-0 overflow-auto px-normal py-normal\" [class.hidden]=\"!railOpen()\">\n <ng-content select=\"[mdFacets]\"></ng-content>\n </div>\n <button\n type=\"button\"\n class=\"fx-md-rail-toggle flex items-center gap-normal text-muted hover:bg-bg-hover transition-colors\"\n [class.hidden]=\"railOpen()\"\n [attr.aria-label]=\"expandFacetsLabel()\"\n [attr.aria-expanded]=\"false\"\n (click)=\"railOpen.set(true)\"\n >\n <fx-ui-hero-icon icon=\"chevron-double-right\" [size]=\"14\" class=\"flex shrink-0\" />\n <span class=\"fx-md-rail-label txt-eyebrow whitespace-nowrap\">{{ facetsLabel() }}</span>\n </button>\n </aside>\n\n <!-- Master: list pane -->\n <section class=\"fx-md-list flex flex-col min-h-0 min-w-0\">\n <div class=\"fx-md-list-head px-large pt-large pb-small border-b border-border-subtle shrink-0\">\n <ng-content select=\"[mdHeader]\"></ng-content>\n <ng-content select=\"[mdFilters]\"></ng-content>\n </div>\n <div\n class=\"flex flex-col overflow-auto flex-1 min-h-0\"\n role=\"listbox\"\n [attr.aria-label]=\"listLabel() || null\"\n >\n @if (table.loading) {\n <div class=\"px-large py-normal\">\n <fx-ui-skeleton-list [rows]=\"table.pageSize\"></fx-ui-skeleton-list>\n </div>\n } @else {\n @for (row of table.dataSource.data; track table.idOf(row); let i = $index) {\n <!-- Hairline rows; the selected one is tinted with an accent bar on\n the left (design 11/14 selected finding / component). -->\n <button\n type=\"button\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(row)\"\n [tabindex]=\"isSelected(row) ? 0 : -1\"\n (click)=\"table.selectItem(row)\"\n (keydown)=\"onKeydown($event, i)\"\n class=\"text-left px-large py-semi border-l-2 border-b border-b-border-subtle transition-colors\"\n [ngClass]=\"\n isSelected(row)\n ? 'border-l-accent bg-accent-soft/60'\n : 'border-l-transparent hover:bg-surface-sunken'\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: row, selected: isSelected(row) }\"\n ></ng-container>\n </button>\n }\n @if (!table.dataSource.data.length) {\n <div class=\"px-large py-large\">\n <fx-ui-empty-state [message]=\"emptyMessage()\"></fx-ui-empty-state>\n </div>\n }\n }\n </div>\n <!-- No (page) binding: ngAfterViewInit hands this paginator to the table,\n whose own BaseTable subscription (running after this child's hook)\n wires page events \u2014 a binding here would double-fetch every change. -->\n <mat-paginator\n class=\"fx-md-paginator\"\n [class.fx-md-paginator-hidden]=\"paginationStyle() === 'prevnext'\"\n [length]=\"table.total\"\n [pageSize]=\"table.pageSize\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n ></mat-paginator>\n @if (paginationStyle() === 'prevnext') {\n <div class=\"flex items-center justify-between gap-normal px-large py-normal border-t border-border-subtle shrink-0\">\n <span class=\"txt-utility-description\" style=\"font-family: var(--og-font-figure)\">\n {{ rangeText() }}\n </span>\n <div class=\"flex gap-small\">\n <button\n type=\"button\"\n class=\"btn-alternative\"\n [disabled]=\"!paginator().hasPreviousPage()\"\n (click)=\"paginator().previousPage()\"\n >\n {{ prevLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"btn-alternative\"\n [disabled]=\"!paginator().hasNextPage()\"\n (click)=\"paginator().nextPage()\"\n >\n {{ nextLabel() }}\n </button>\n </div>\n </div>\n }\n </section>\n\n <!-- Detail pane -->\n <section class=\"fx-md-detail w-full min-w-0 flex flex-col min-h-0\">\n <div class=\"flex-1 min-h-0 overflow-auto px-large py-large\">\n @if (table.selected) {\n @if (table.detailBusy) {\n <fx-ui-skeleton-detail [cards]=\"skeletonCards()\"></fx-ui-skeleton-detail>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"detailTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: table.detail, selected: table.selected }\"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-center h-full min-h-[300px]\">\n <fx-ui-empty-state [message]=\"emptyMessage()\"></fx-ui-empty-state>\n </div>\n }\n </div>\n </section>\n</div>\n", styles: [":host{display:block;container-type:inline-size;container-name:fx-md}.fx-md-row{background:rgb(var(--og-surface));border:1px solid rgb(var(--border-default));border-radius:.5rem;overflow:hidden;box-shadow:0 1px 2px #0000000a,0 3px 8px #0000000d}.fx-md-facets,.fx-md-list{border-bottom:1px solid rgb(var(--og-border-subtle))}.fx-md-rail-toggle{width:100%;padding:10px 12px;flex-direction:row;justify-content:flex-start}.fx-md-list,.fx-md-facets{width:100%}@container fx-md (min-width: 720px){.fx-md-row{flex-direction:row;height:var(--fx-md-pane-h, calc(100vh - 300px) );min-height:420px}.fx-md-facets,.fx-md-list{border-bottom:0;border-right:1px solid rgb(var(--og-border-subtle))}.fx-md-facets{flex:0 0 40px;width:40px;min-width:40px;max-width:40px}.fx-md-facets--open{flex:0 1 204px;width:auto;min-width:180px;max-width:204px}.fx-md-rail-toggle{height:100%;padding:12px 0;flex-direction:column;justify-content:flex-start}.fx-md-rail-label{writing-mode:vertical-rl;transform:rotate(180deg)}.fx-md-list{width:auto;flex:1 1 0;min-width:260px;max-width:var(--fx-md-list-width, none)}.fx-md-detail{width:auto;flex:0 1 var(--fx-md-detail-width, 340px);min-width:300px;max-width:var(--fx-md-detail-width, 340px)}.fx-md-row--detail-fluid .fx-md-list{flex:0 1 var(--fx-md-list-width, 420px);max-width:var(--fx-md-list-width, 420px)}.fx-md-row--detail-fluid .fx-md-detail{flex:1 1 360px;min-width:360px;max-width:none}}.fx-md-paginator-hidden{position:absolute!important;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}.fx-md-paginator{padding:0 12px;border-top:1px solid rgb(var(--og-border-subtle))}\n"], dependencies: [{ kind: "component", type: SkeletonListComponent, selector: "fx-ui-skeleton-list", inputs: ["rows"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: EmptyStateComponent, selector: "fx-ui-empty-state", inputs: ["message", "icon", "height"] }, { kind: "component", type: MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: SkeletonDetailComponent, selector: "fx-ui-skeleton-detail", inputs: ["cards", "rows"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }] });
4810
4887
  }
4811
4888
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: MasterDetailComponent, decorators: [{
4812
4889
  type: Component,
@@ -4818,10 +4895,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
4818
4895
  MatPaginator,
4819
4896
  SkeletonDetailComponent,
4820
4897
  HeroIconComponent,
4821
- ], template: "<!-- ONE frame, three columns (designs 11/14/17): facets rail \u00B7 list \u00B7 detail,\n separated by hairlines, sharing one height; only each pane's body scrolls.\n Row direction / height come from the CONTAINER QUERY in the CSS (the row's\n own width), never from `lg:*` \u2014 see the note at the top of the .css. -->\n<div\n class=\"fx-md-row flex flex-col items-stretch\"\n [class.fx-md-row--rail-open]=\"railOpen()\"\n [style.--fx-md-list-width]=\"listWidth()\"\n [style.--fx-md-pane-h]=\"paneHeight()\"\n>\n <!-- Facets rail \u2014 collapsible (\u00BB / \u00AB), collapsed = 40px strip with the label\n set vertically. The [mdFacets] projection is unconditional (hasFacets is\n detected by querying it) and only hidden while collapsed. -->\n <aside\n class=\"fx-md-facets flex flex-col min-h-0 min-w-0\"\n [class.hidden]=\"!hasFacets\"\n [class.fx-md-facets--open]=\"railOpen()\"\n >\n <div class=\"fx-md-rail-head flex items-center gap-small px-normal py-semi border-b border-border-subtle shrink-0\" [class.hidden]=\"!railOpen()\">\n <button\n type=\"button\"\n class=\"flex items-center justify-center w-6 h-6 rounded-control text-muted hover:bg-bg-hover shrink-0\"\n [attr.aria-label]=\"collapseFacetsLabel()\"\n (click)=\"railOpen.set(false)\"\n >\n <fx-ui-hero-icon icon=\"chevron-double-left\" [size]=\"14\" class=\"flex\" />\n </button>\n <span class=\"txt-eyebrow truncate\">{{ facetsLabel() }}</span>\n </div>\n <div class=\"flex-1 min-h-0 overflow-auto px-normal py-normal\" [class.hidden]=\"!railOpen()\">\n <ng-content select=\"[mdFacets]\"></ng-content>\n </div>\n <button\n type=\"button\"\n class=\"fx-md-rail-toggle flex items-center gap-normal text-muted hover:bg-bg-hover transition-colors\"\n [class.hidden]=\"railOpen()\"\n [attr.aria-label]=\"expandFacetsLabel()\"\n [attr.aria-expanded]=\"false\"\n (click)=\"railOpen.set(true)\"\n >\n <fx-ui-hero-icon icon=\"chevron-double-right\" [size]=\"14\" class=\"flex shrink-0\" />\n <span class=\"fx-md-rail-label txt-eyebrow whitespace-nowrap\">{{ facetsLabel() }}</span>\n </button>\n </aside>\n\n <!-- Master: list pane -->\n <section class=\"fx-md-list flex flex-col min-h-0 min-w-0\">\n <div class=\"fx-md-list-head px-large pt-large pb-small border-b border-border-subtle shrink-0\">\n <ng-content select=\"[mdHeader]\"></ng-content>\n <ng-content select=\"[mdFilters]\"></ng-content>\n </div>\n <div\n class=\"flex flex-col overflow-auto flex-1 min-h-0\"\n role=\"listbox\"\n [attr.aria-label]=\"listLabel() || null\"\n >\n @if (table.loading) {\n <div class=\"px-large py-normal\">\n <fx-ui-skeleton-list [rows]=\"table.pageSize\"></fx-ui-skeleton-list>\n </div>\n } @else {\n @for (row of table.dataSource.data; track table.idOf(row); let i = $index) {\n <!-- Hairline rows; the selected one is tinted with an accent bar on\n the left (design 11/14 selected finding / component). -->\n <button\n type=\"button\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(row)\"\n [tabindex]=\"isSelected(row) ? 0 : -1\"\n (click)=\"table.selectItem(row)\"\n (keydown)=\"onKeydown($event, i)\"\n class=\"text-left px-large py-semi border-l-2 border-b border-b-border-subtle transition-colors\"\n [ngClass]=\"\n isSelected(row)\n ? 'border-l-accent bg-accent-soft/60'\n : 'border-l-transparent hover:bg-surface-sunken'\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: row, selected: isSelected(row) }\"\n ></ng-container>\n </button>\n }\n @if (!table.dataSource.data.length) {\n <div class=\"px-large py-large\">\n <fx-ui-empty-state [message]=\"emptyMessage()\"></fx-ui-empty-state>\n </div>\n }\n }\n </div>\n <!-- No (page) binding: ngAfterViewInit hands this paginator to the table,\n whose own BaseTable subscription (running after this child's hook)\n wires page events \u2014 a binding here would double-fetch every change. -->\n <mat-paginator\n class=\"fx-md-paginator\"\n [class.fx-md-paginator-hidden]=\"paginationStyle() === 'prevnext'\"\n [length]=\"table.total\"\n [pageSize]=\"table.pageSize\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n ></mat-paginator>\n @if (paginationStyle() === 'prevnext') {\n <div class=\"flex items-center justify-between gap-normal px-large py-normal border-t border-border-subtle shrink-0\">\n <span class=\"txt-utility-description\" style=\"font-family: var(--og-font-figure)\">\n {{ rangeText() }}\n </span>\n <div class=\"flex gap-small\">\n <button\n type=\"button\"\n class=\"btn-alternative !h-8 !px-semi text-sm\"\n [disabled]=\"!paginator().hasPreviousPage()\"\n (click)=\"paginator().previousPage()\"\n >\n {{ prevLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"btn-alternative !h-8 !px-semi text-sm\"\n [disabled]=\"!paginator().hasNextPage()\"\n (click)=\"paginator().nextPage()\"\n >\n {{ nextLabel() }}\n </button>\n </div>\n </div>\n }\n </section>\n\n <!-- Detail pane -->\n <section class=\"fx-md-detail w-full min-w-0 flex flex-col min-h-0\">\n <div class=\"flex-1 min-h-0 overflow-auto px-large py-large\">\n @if (table.selected) {\n @if (table.detailBusy) {\n <fx-ui-skeleton-detail [cards]=\"skeletonCards()\"></fx-ui-skeleton-detail>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"detailTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: table.detail, selected: table.selected }\"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-center h-full min-h-[300px]\">\n <fx-ui-empty-state [message]=\"emptyMessage()\"></fx-ui-empty-state>\n </div>\n }\n </div>\n </section>\n</div>\n", styles: [":host{display:block;container-type:inline-size;container-name:fx-md}.fx-md-row{background:rgb(var(--og-surface));border:1px solid rgb(var(--border-default));border-radius:.5rem;overflow:hidden;box-shadow:0 1px 2px #0000000a,0 3px 8px #0000000d}.fx-md-facets,.fx-md-list{border-bottom:1px solid rgb(var(--og-border-subtle))}.fx-md-rail-toggle{width:100%;padding:10px 12px;flex-direction:row;justify-content:flex-start}.fx-md-list,.fx-md-facets{width:100%}@container fx-md (min-width: 720px){.fx-md-row{flex-direction:row;height:var(--fx-md-pane-h, calc(100vh - 300px) );min-height:420px}.fx-md-facets,.fx-md-list{border-bottom:0;border-right:1px solid rgb(var(--og-border-subtle))}.fx-md-facets{flex:0 0 40px;width:40px;min-width:40px;max-width:40px}.fx-md-facets--open{flex:0 1 230px;width:auto;min-width:200px;max-width:260px}.fx-md-rail-toggle{height:100%;padding:12px 0;flex-direction:column;justify-content:flex-start}.fx-md-rail-label{writing-mode:vertical-rl;transform:rotate(180deg)}.fx-md-list{width:auto;flex:0 1 var(--fx-md-list-width, 420px);min-width:260px;max-width:var(--fx-md-list-width, 420px)}.fx-md-detail{flex:1 1 360px;min-width:360px}}.fx-md-paginator-hidden{position:absolute!important;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}.fx-md-paginator{padding:0 12px;border-top:1px solid rgb(var(--og-border-subtle))}\n"] }]
4898
+ ], template: "<!-- ONE frame, three columns (designs 11/14/17): facets rail \u00B7 list \u00B7 detail,\n separated by hairlines, sharing one height; only each pane's body scrolls.\n Row direction / height come from the CONTAINER QUERY in the CSS (the row's\n own width), never from `lg:*` \u2014 see the note at the top of the .css. -->\n<div\n class=\"fx-md-row flex flex-col items-stretch\"\n [class.fx-md-row--rail-open]=\"railOpen()\"\n [style.--fx-md-list-width]=\"listWidth()\"\n [class.fx-md-row--detail-fluid]=\"detailFluid()\"\n [style.--fx-md-detail-width]=\"detailFluid() ? null : detailWidth()\"\n [style.--fx-md-pane-h]=\"paneHeight()\"\n>\n <!-- Facets rail \u2014 collapsible (\u00BB / \u00AB), collapsed = 40px strip with the label\n set vertically. The [mdFacets] projection is unconditional (hasFacets is\n detected by querying it) and only hidden while collapsed. -->\n <aside\n class=\"fx-md-facets flex flex-col min-h-0 min-w-0\"\n [class.hidden]=\"!hasFacets\"\n [class.fx-md-facets--open]=\"railOpen()\"\n >\n <div class=\"fx-md-rail-head flex items-center gap-small px-normal py-semi border-b border-border-subtle shrink-0\" [class.hidden]=\"!railOpen()\">\n <button\n type=\"button\"\n class=\"flex items-center justify-center w-6 h-6 rounded-control text-muted hover:bg-bg-hover shrink-0\"\n [attr.aria-label]=\"collapseFacetsLabel()\"\n (click)=\"railOpen.set(false)\"\n >\n <fx-ui-hero-icon icon=\"chevron-double-left\" [size]=\"14\" class=\"flex\" />\n </button>\n <span class=\"txt-eyebrow truncate\">{{ facetsLabel() }}</span>\n </div>\n <div class=\"flex-1 min-h-0 overflow-auto px-normal py-normal\" [class.hidden]=\"!railOpen()\">\n <ng-content select=\"[mdFacets]\"></ng-content>\n </div>\n <button\n type=\"button\"\n class=\"fx-md-rail-toggle flex items-center gap-normal text-muted hover:bg-bg-hover transition-colors\"\n [class.hidden]=\"railOpen()\"\n [attr.aria-label]=\"expandFacetsLabel()\"\n [attr.aria-expanded]=\"false\"\n (click)=\"railOpen.set(true)\"\n >\n <fx-ui-hero-icon icon=\"chevron-double-right\" [size]=\"14\" class=\"flex shrink-0\" />\n <span class=\"fx-md-rail-label txt-eyebrow whitespace-nowrap\">{{ facetsLabel() }}</span>\n </button>\n </aside>\n\n <!-- Master: list pane -->\n <section class=\"fx-md-list flex flex-col min-h-0 min-w-0\">\n <div class=\"fx-md-list-head px-large pt-large pb-small border-b border-border-subtle shrink-0\">\n <ng-content select=\"[mdHeader]\"></ng-content>\n <ng-content select=\"[mdFilters]\"></ng-content>\n </div>\n <div\n class=\"flex flex-col overflow-auto flex-1 min-h-0\"\n role=\"listbox\"\n [attr.aria-label]=\"listLabel() || null\"\n >\n @if (table.loading) {\n <div class=\"px-large py-normal\">\n <fx-ui-skeleton-list [rows]=\"table.pageSize\"></fx-ui-skeleton-list>\n </div>\n } @else {\n @for (row of table.dataSource.data; track table.idOf(row); let i = $index) {\n <!-- Hairline rows; the selected one is tinted with an accent bar on\n the left (design 11/14 selected finding / component). -->\n <button\n type=\"button\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(row)\"\n [tabindex]=\"isSelected(row) ? 0 : -1\"\n (click)=\"table.selectItem(row)\"\n (keydown)=\"onKeydown($event, i)\"\n class=\"text-left px-large py-semi border-l-2 border-b border-b-border-subtle transition-colors\"\n [ngClass]=\"\n isSelected(row)\n ? 'border-l-accent bg-accent-soft/60'\n : 'border-l-transparent hover:bg-surface-sunken'\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: row, selected: isSelected(row) }\"\n ></ng-container>\n </button>\n }\n @if (!table.dataSource.data.length) {\n <div class=\"px-large py-large\">\n <fx-ui-empty-state [message]=\"emptyMessage()\"></fx-ui-empty-state>\n </div>\n }\n }\n </div>\n <!-- No (page) binding: ngAfterViewInit hands this paginator to the table,\n whose own BaseTable subscription (running after this child's hook)\n wires page events \u2014 a binding here would double-fetch every change. -->\n <mat-paginator\n class=\"fx-md-paginator\"\n [class.fx-md-paginator-hidden]=\"paginationStyle() === 'prevnext'\"\n [length]=\"table.total\"\n [pageSize]=\"table.pageSize\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n ></mat-paginator>\n @if (paginationStyle() === 'prevnext') {\n <div class=\"flex items-center justify-between gap-normal px-large py-normal border-t border-border-subtle shrink-0\">\n <span class=\"txt-utility-description\" style=\"font-family: var(--og-font-figure)\">\n {{ rangeText() }}\n </span>\n <div class=\"flex gap-small\">\n <button\n type=\"button\"\n class=\"btn-alternative\"\n [disabled]=\"!paginator().hasPreviousPage()\"\n (click)=\"paginator().previousPage()\"\n >\n {{ prevLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"btn-alternative\"\n [disabled]=\"!paginator().hasNextPage()\"\n (click)=\"paginator().nextPage()\"\n >\n {{ nextLabel() }}\n </button>\n </div>\n </div>\n }\n </section>\n\n <!-- Detail pane -->\n <section class=\"fx-md-detail w-full min-w-0 flex flex-col min-h-0\">\n <div class=\"flex-1 min-h-0 overflow-auto px-large py-large\">\n @if (table.selected) {\n @if (table.detailBusy) {\n <fx-ui-skeleton-detail [cards]=\"skeletonCards()\"></fx-ui-skeleton-detail>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"detailTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: table.detail, selected: table.selected }\"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-center h-full min-h-[300px]\">\n <fx-ui-empty-state [message]=\"emptyMessage()\"></fx-ui-empty-state>\n </div>\n }\n </div>\n </section>\n</div>\n", styles: [":host{display:block;container-type:inline-size;container-name:fx-md}.fx-md-row{background:rgb(var(--og-surface));border:1px solid rgb(var(--border-default));border-radius:.5rem;overflow:hidden;box-shadow:0 1px 2px #0000000a,0 3px 8px #0000000d}.fx-md-facets,.fx-md-list{border-bottom:1px solid rgb(var(--og-border-subtle))}.fx-md-rail-toggle{width:100%;padding:10px 12px;flex-direction:row;justify-content:flex-start}.fx-md-list,.fx-md-facets{width:100%}@container fx-md (min-width: 720px){.fx-md-row{flex-direction:row;height:var(--fx-md-pane-h, calc(100vh - 300px) );min-height:420px}.fx-md-facets,.fx-md-list{border-bottom:0;border-right:1px solid rgb(var(--og-border-subtle))}.fx-md-facets{flex:0 0 40px;width:40px;min-width:40px;max-width:40px}.fx-md-facets--open{flex:0 1 204px;width:auto;min-width:180px;max-width:204px}.fx-md-rail-toggle{height:100%;padding:12px 0;flex-direction:column;justify-content:flex-start}.fx-md-rail-label{writing-mode:vertical-rl;transform:rotate(180deg)}.fx-md-list{width:auto;flex:1 1 0;min-width:260px;max-width:var(--fx-md-list-width, none)}.fx-md-detail{width:auto;flex:0 1 var(--fx-md-detail-width, 340px);min-width:300px;max-width:var(--fx-md-detail-width, 340px)}.fx-md-row--detail-fluid .fx-md-list{flex:0 1 var(--fx-md-list-width, 420px);max-width:var(--fx-md-list-width, 420px)}.fx-md-row--detail-fluid .fx-md-detail{flex:1 1 360px;min-width:360px;max-width:none}}.fx-md-paginator-hidden{position:absolute!important;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}.fx-md-paginator{padding:0 12px;border-top:1px solid rgb(var(--og-border-subtle))}\n"] }]
4822
4899
  }], propDecorators: { table: [{
4823
4900
  type: Input
4824
- }], listWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "listWidth", required: false }] }], paneHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "paneHeight", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], listLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "listLabel", required: false }] }], skeletonCards: [{ type: i0.Input, args: [{ isSignal: true, alias: "skeletonCards", required: false }] }], paginationStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginationStyle", required: false }] }], prevLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], pageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageLabel", required: false }] }], ofLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ofLabel", required: false }] }], facetsOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "facetsOpen", required: false }] }], facetsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "facetsLabel", required: false }] }], expandFacetsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandFacetsLabel", required: false }] }], collapseFacetsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseFacetsLabel", required: false }] }], rowTemplate: [{ type: i0.ContentChild, args: ['row', { isSignal: true }] }], detailTemplate: [{ type: i0.ContentChild, args: ['detail', { isSignal: true }] }], paginator: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatPaginator), { isSignal: true }] }] } });
4901
+ }], listWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "listWidth", required: false }] }], detailWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "detailWidth", required: false }] }], paneHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "paneHeight", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], listLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "listLabel", required: false }] }], skeletonCards: [{ type: i0.Input, args: [{ isSignal: true, alias: "skeletonCards", required: false }] }], paginationStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginationStyle", required: false }] }], prevLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], pageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageLabel", required: false }] }], ofLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ofLabel", required: false }] }], facetsOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "facetsOpen", required: false }] }], facetsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "facetsLabel", required: false }] }], expandFacetsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandFacetsLabel", required: false }] }], collapseFacetsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseFacetsLabel", required: false }] }], rowTemplate: [{ type: i0.ContentChild, args: ['row', { isSignal: true }] }], detailTemplate: [{ type: i0.ContentChild, args: ['detail', { isSignal: true }] }], paginator: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatPaginator), { isSignal: true }] }] } });
4825
4902
 
4826
4903
  /**
4827
4904
  * Row-action dropdown wrapper over mat-menu (behavior-layer rule, Playbook
@@ -4854,7 +4931,7 @@ class MenuComponent {
4854
4931
  }
4855
4932
  }
4856
4933
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: MenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4857
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: MenuComponent, isStandalone: true, selector: "fx-ui-menu", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { select: "select" }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-lg p-1 hover:bg-bg-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n [matMenuTriggerFor]=\"menu\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"ariaLabel()\"\n>\n <ng-content></ng-content>\n</button>\n\n<mat-menu #menu=\"matMenu\">\n @for (item of items(); track item.label) {\n <button\n mat-menu-item\n type=\"button\"\n [disabled]=\"item.disabled\"\n (click)=\"select.emit(item)\"\n >\n <span class=\"flex items-center gap-normal\" [class]=\"toneClass(item)\">\n @if (item.icon) {\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"18\"></fx-ui-hero-icon>\n }\n <span>{{ item.label }}</span>\n </span>\n </button>\n }\n</mat-menu>\n", dependencies: [{ kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i1$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i1$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4934
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: MenuComponent, isStandalone: true, selector: "fx-ui-menu", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { select: "select" }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-lg p-1 hover:bg-bg-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n [matMenuTriggerFor]=\"menu\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"ariaLabel()\"\n>\n <ng-content></ng-content>\n</button>\n\n<mat-menu #menu=\"matMenu\">\n @for (item of items(); track item.label) {\n <button\n mat-menu-item\n type=\"button\"\n [disabled]=\"item.disabled\"\n (click)=\"select.emit(item)\"\n >\n <span class=\"flex items-center gap-normal\" [class]=\"toneClass(item)\">\n @if (item.icon) {\n <fx-ui-hero-icon [icon]=\"item.icon\" [size]=\"18\"></fx-ui-hero-icon>\n }\n <span>{{ item.label }}</span>\n </span>\n </button>\n }\n</mat-menu>\n", dependencies: [{ kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1$5.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i1$5.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i1$5.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4858
4935
  }
4859
4936
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: MenuComponent, decorators: [{
4860
4937
  type: Component,
@@ -4885,7 +4962,7 @@ class ThemePickerComponent {
4885
4962
  this.themeService.setTheme(theme);
4886
4963
  }
4887
4964
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ThemePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4888
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: ThemePickerComponent, isStandalone: true, selector: "fx-ui-theme-picker", ngImport: i0, template: "<button\n type=\"button\"\n [matMenuTriggerFor]=\"themeMenu\"\n class=\"flex items-center gap-small px-normal py-small rounded-control border border-border-default bg-transparent cursor-pointer hover:bg-bg-hover transition-colors\"\n aria-label=\"Change theme\"\n>\n <span\n class=\"w-4 h-4 rounded-full border border-border-strong/30 shrink-0\"\n [style.background]=\"\n 'linear-gradient(135deg, ' +\n currentMeta().accent +\n ' 50%, ' +\n currentMeta().pageBg +\n ' 50%)'\n \"\n ></span>\n <span class=\"txt-body-01 hidden md:inline\">{{ currentMeta().label }}</span>\n <fx-ui-hero-icon icon=\"chevron-down\" [size]=\"14\" class=\"flex text-text-secondary\" />\n</button>\n\n<mat-menu #themeMenu=\"matMenu\">\n @for (theme of themes; track theme.name) {\n <button\n mat-menu-item\n type=\"button\"\n (click)=\"select(theme.name)\"\n [attr.aria-current]=\"theme.name === current() ? 'true' : null\"\n >\n <div class=\"flex items-center gap-normal min-w-40\">\n <span\n class=\"w-5 h-5 rounded-full border border-border-strong/30 shrink-0\"\n [style.background]=\"\n 'linear-gradient(135deg, ' + theme.accent + ' 50%, ' + theme.pageBg + ' 50%)'\n \"\n ></span>\n <span class=\"txt-body-01 flex-1\">{{ theme.label }}</span>\n <span class=\"txt-utility-description\">{{ theme.dark ? 'Dark' : 'Light' }}</span>\n @if (theme.name === current()) {\n <fx-ui-hero-icon icon=\"check\" [size]=\"16\" class=\"flex text-text-selected\" />\n }\n </div>\n </button>\n }\n</mat-menu>\n", dependencies: [{ kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i1$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i1$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4965
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: ThemePickerComponent, isStandalone: true, selector: "fx-ui-theme-picker", ngImport: i0, template: "<button\n type=\"button\"\n [matMenuTriggerFor]=\"themeMenu\"\n class=\"flex items-center gap-small px-normal py-small rounded-control border border-border-default bg-transparent cursor-pointer hover:bg-bg-hover transition-colors\"\n aria-label=\"Change theme\"\n>\n <span\n class=\"w-4 h-4 rounded-full border border-border-strong/30 shrink-0\"\n [style.background]=\"\n 'linear-gradient(135deg, ' +\n currentMeta().accent +\n ' 50%, ' +\n currentMeta().pageBg +\n ' 50%)'\n \"\n ></span>\n <span class=\"txt-body-01 hidden md:inline\">{{ currentMeta().label }}</span>\n <fx-ui-hero-icon icon=\"chevron-down\" [size]=\"14\" class=\"flex text-text-secondary\" />\n</button>\n\n<mat-menu #themeMenu=\"matMenu\">\n @for (theme of themes; track theme.name) {\n <button\n mat-menu-item\n type=\"button\"\n (click)=\"select(theme.name)\"\n [attr.aria-current]=\"theme.name === current() ? 'true' : null\"\n >\n <div class=\"flex items-center gap-normal min-w-40\">\n <span\n class=\"w-5 h-5 rounded-full border border-border-strong/30 shrink-0\"\n [style.background]=\"\n 'linear-gradient(135deg, ' + theme.accent + ' 50%, ' + theme.pageBg + ' 50%)'\n \"\n ></span>\n <span class=\"txt-body-01 flex-1\">{{ theme.label }}</span>\n <span class=\"txt-utility-description\">{{ theme.dark ? 'Dark' : 'Light' }}</span>\n @if (theme.name === current()) {\n <fx-ui-hero-icon icon=\"check\" [size]=\"16\" class=\"flex text-text-selected\" />\n }\n </div>\n </button>\n }\n</mat-menu>\n", dependencies: [{ kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1$5.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i1$5.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i1$5.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4889
4966
  }
4890
4967
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ThemePickerComponent, decorators: [{
4891
4968
  type: Component,
@@ -5034,11 +5111,11 @@ class BreadcrumbComponent {
5034
5111
  this.close();
5035
5112
  }
5036
5113
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: BreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5037
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: BreadcrumbComponent, isStandalone: true, selector: "fx-ui-breadcrumb", host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "<nav aria-label=\"Breadcrumb\" class=\"fx-crumbs\">\n @for (crumb of crumbs(); track $index; let last = $last; let i = $index) {\n @if (i > 0) {\n <span aria-hidden=\"true\" class=\"fx-crumb-sep\">/</span>\n }\n @if (isChip(crumb)) {\n <span class=\"relative inline-flex min-w-0\">\n <button\n type=\"button\"\n class=\"fx-crumb-chip\"\n [class.fx-crumb-chip--mono]=\"crumb.mono\"\n [attr.aria-haspopup]=\"crumb.switcher ? 'listbox' : null\"\n [attr.aria-expanded]=\"crumb.switcher ? openIndex() === i : null\"\n [attr.title]=\"crumb.switcher?.title || crumb.label\"\n (click)=\"onChipClick(crumb, i, $event)\"\n >\n @if (crumb.icon) {\n <fx-ui-hero-icon [icon]=\"crumb.icon\" [size]=\"13\" class=\"flex shrink-0 text-accent\" />\n }\n <span class=\"truncate\">{{ crumb.label }}</span>\n @if (crumb.switcher) {\n <fx-ui-hero-icon icon=\"chevron-down\" [size]=\"10\" class=\"flex shrink-0 text-faint\" />\n }\n </button>\n\n @if (crumb.switcher; as sw) {\n @if (openIndex() === i) {\n <div role=\"listbox\" class=\"fx-crumb-panel\" [class.fx-crumb-panel--mono]=\"sw.mono\">\n <div class=\"fx-crumb-panel__title\">{{ sw.title }}</div>\n @if (sw.filterPlaceholder) {\n <input\n type=\"search\"\n class=\"fx-crumb-panel__filter\"\n [placeholder]=\"sw.filterPlaceholder\"\n [attr.aria-label]=\"sw.filterPlaceholder\"\n [value]=\"keyword()\"\n (input)=\"onFilter(crumb, $any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n @if (loading() && options().length === 0) {\n <div class=\"fx-crumb-panel__skeleton\" aria-busy=\"true\">\n <span></span><span></span><span></span>\n </div>\n } @else if (options().length === 0) {\n <div class=\"fx-crumb-panel__empty\">{{ sw.emptyLabel || '\u2014' }}</div>\n }\n @for (opt of options(); track opt.value) {\n <button\n type=\"button\"\n role=\"option\"\n class=\"fx-crumb-panel__opt\"\n [class.fx-crumb-panel__opt--on]=\"opt.value === sw.selected\"\n [attr.aria-selected]=\"opt.value === sw.selected\"\n (click)=\"pick(opt, $event)\"\n >\n <span class=\"truncate\">{{ opt.label }}</span>\n @if (opt.meta) {\n <span class=\"fx-crumb-panel__meta\">{{ opt.meta }}</span>\n }\n @if (opt.value === sw.selected) {\n <fx-ui-hero-icon icon=\"check\" [size]=\"13\" class=\"flex shrink-0 text-strong\" />\n }\n </button>\n }\n @if (sw.viewAll || sw.create) {\n <div class=\"fx-crumb-panel__rule\"></div>\n }\n @if (sw.viewAll; as more) {\n <button type=\"button\" class=\"fx-crumb-panel__more\" (click)=\"go($event, more.url)\">\n <fx-ui-hero-icon icon=\"ellipsis-horizontal\" [size]=\"13\" class=\"flex shrink-0 text-faint\" />\n {{ more.label }}\n </button>\n }\n @if (sw.create; as create) {\n <button type=\"button\" class=\"fx-crumb-panel__create\" (click)=\"go($event, create.url)\">\n <fx-ui-hero-icon icon=\"plus\" [size]=\"14\" class=\"flex shrink-0\" />\n {{ create.label }}\n </button>\n }\n </div>\n }\n }\n </span>\n } @else if (!last && crumb.url) {\n <a class=\"fx-crumb-link\" [href]=\"crumb.url\" (click)=\"go($event, crumb.url)\">{{ crumb.label }}</a>\n } @else {\n <span class=\"fx-crumb-current\">{{ crumb.label }}</span>\n }\n }\n</nav>\n", styles: [":host{display:block;min-width:0}.fx-crumbs{display:flex;align-items:center;gap:7px;min-width:0;white-space:nowrap}.fx-crumb-sep{flex:0 0 auto;padding:0 2px;font-size:13px;color:rgb(var(--og-text-faint))}.fx-crumb-link{flex:0 0 auto;max-width:260px;overflow:hidden;text-overflow:ellipsis;font-size:var(--og-fs-sm);font-weight:500;letter-spacing:.005em;color:rgb(var(--og-text-muted));text-decoration:none;transition:color .15s}.fx-crumb-link:hover{color:rgb(var(--og-text))}.fx-crumb-current{min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:var(--og-fs-sm);font-weight:600;color:rgb(var(--og-text))}.fx-crumb-chip{appearance:none;display:inline-flex;align-items:center;gap:8px;min-width:0;max-width:320px;padding:4px 12px;line-height:1.5;border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-control);background:rgb(var(--og-surface));font-size:var(--og-fs-xs);font-weight:600;color:rgb(var(--og-text));box-shadow:0 1px 2px #0000000a;white-space:nowrap;cursor:pointer;transition:box-shadow .12s,border-color .12s}.fx-crumb-chip:hover,.fx-crumb-chip[aria-expanded=true]{box-shadow:0 2px 7px #0000001a}.fx-crumb-chip--mono{font-family:var(--og-font-mono)}.fx-crumb-panel{position:absolute;top:calc(100% + 8px);left:0;z-index:60;min-width:240px;max-width:min(380px,90vw);display:flex;flex-direction:column;padding:6px;background:rgb(var(--og-surface));border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-card);box-shadow:0 10px 28px #00000024;text-align:left;white-space:nowrap;animation:fx-crumb-panel-in .16s ease-out}@keyframes fx-crumb-panel-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.fx-crumb-panel{animation:none}}.fx-crumb-panel__title{padding:6px 12px 8px;margin-bottom:4px;font-size:var(--og-fs-2xs);letter-spacing:.02em;color:rgb(var(--og-text-faint));border-bottom:1px solid rgb(var(--og-border-subtle))}.fx-crumb-panel__filter{margin:2px 6px 6px;padding:6px 10px;border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-control);background:rgb(var(--og-surface-sunken));color:rgb(var(--og-text));font-family:var(--og-font-body);font-size:var(--og-fs-xs);outline:none}.fx-crumb-panel__filter:focus{border-color:rgb(var(--og-accent))}.fx-crumb-panel__opt{appearance:none;width:100%;display:flex;align-items:center;gap:8px;padding:8px 12px;border:0;border-radius:var(--og-radius-control);background:transparent;font-family:var(--og-font-body);font-size:var(--og-fs-sm);font-weight:400;color:rgb(var(--og-text-muted));text-align:left;cursor:pointer;transition:background .12s}.fx-crumb-panel--mono .fx-crumb-panel__opt{font-family:var(--og-font-mono);font-size:var(--og-fs-xs)}.fx-crumb-panel__opt:hover{background:rgb(var(--og-surface-sunken))}.fx-crumb-panel__opt--on{font-weight:600;color:rgb(var(--og-text))}.fx-crumb-panel__meta{margin-left:auto;font-family:var(--og-font-mono);font-size:var(--og-fs-2xs);color:rgb(var(--og-text-faint))}.fx-crumb-panel__opt>fx-ui-hero-icon:last-child{margin-left:auto}.fx-crumb-panel__opt>.fx-crumb-panel__meta+fx-ui-hero-icon{margin-left:0}.fx-crumb-panel__empty{padding:8px 12px;font-size:var(--og-fs-xs);color:rgb(var(--og-text-faint))}.fx-crumb-panel__skeleton{display:flex;flex-direction:column;gap:8px;padding:8px 12px}.fx-crumb-panel__skeleton span{display:block;height:10px;border-radius:4px;background:rgb(var(--og-border-subtle));animation:fx-crumb-pulse 1.2s ease-in-out infinite}.fx-crumb-panel__skeleton span:nth-child(1){width:62%}.fx-crumb-panel__skeleton span:nth-child(2){width:48%;animation-delay:.15s}.fx-crumb-panel__skeleton span:nth-child(3){width:55%;animation-delay:.3s}@keyframes fx-crumb-pulse{0%,to{opacity:1}50%{opacity:.45}}.fx-crumb-panel__rule{margin:4px 0;border-top:1px solid rgb(var(--og-border-subtle))}.fx-crumb-panel__more{appearance:none;width:100%;display:flex;align-items:center;gap:8px;padding:7px 12px;border:0;border-radius:var(--og-radius-control);background:transparent;font-family:var(--og-font-body);font-size:var(--og-fs-xs);color:rgb(var(--og-text-muted));text-align:left;cursor:pointer}.fx-crumb-panel__more:hover{background:rgb(var(--og-surface-sunken));color:rgb(var(--og-text))}.fx-crumb-panel__create{appearance:none;width:calc(100% - 12px);margin:4px 6px 2px;display:flex;align-items:center;gap:8px;padding:9px 12px;border:0;border-radius:var(--og-radius-control);background:rgb(var(--og-accent-soft-bg));color:rgb(var(--og-accent-soft-fg));font-family:var(--og-font-body);font-size:var(--og-fs-sm);font-weight:600;text-align:left;cursor:pointer;transition:background .15s,color .15s}.fx-crumb-panel__create:hover{background:rgb(var(--og-accent));color:rgb(var(--og-accent-contrast))}.text-accent{color:rgb(var(--og-accent))}.text-faint{color:rgb(var(--og-text-faint))}.text-strong{color:rgb(var(--og-text))}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}\n"], dependencies: [{ kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5114
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: BreadcrumbComponent, isStandalone: true, selector: "fx-ui-breadcrumb", host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "<nav aria-label=\"Breadcrumb\" class=\"fx-crumbs\">\n @for (crumb of crumbs(); track $index; let last = $last; let i = $index) {\n @if (i > 0) {\n <span aria-hidden=\"true\" class=\"fx-crumb-sep\">/</span>\n }\n @if (crumb.cta) {\n <button type=\"button\" class=\"fx-crumb-cta\" (click)=\"go($event, crumb.url || '')\">\n <fx-ui-hero-icon [icon]=\"crumb.icon || 'plus'\" [size]=\"13\" class=\"flex shrink-0\" />\n <span class=\"truncate\">{{ crumb.label }}</span>\n </button>\n } @else if (isChip(crumb)) {\n <span class=\"relative inline-flex min-w-0\">\n <button\n type=\"button\"\n class=\"fx-crumb-chip\"\n [class.fx-crumb-chip--mono]=\"crumb.mono\"\n [attr.aria-haspopup]=\"crumb.switcher ? 'listbox' : null\"\n [attr.aria-expanded]=\"crumb.switcher ? openIndex() === i : null\"\n [attr.title]=\"crumb.switcher?.title || crumb.label\"\n (click)=\"onChipClick(crumb, i, $event)\"\n >\n @if (crumb.icon) {\n <fx-ui-hero-icon [icon]=\"crumb.icon\" [size]=\"13\" class=\"flex shrink-0 text-accent\" />\n }\n <span class=\"truncate\">{{ crumb.label }}</span>\n @if (crumb.switcher) {\n <fx-ui-hero-icon icon=\"chevron-down\" [size]=\"10\" class=\"flex shrink-0 text-faint\" />\n }\n </button>\n\n @if (crumb.switcher; as sw) {\n @if (openIndex() === i) {\n <div role=\"listbox\" class=\"fx-crumb-panel\" [class.fx-crumb-panel--mono]=\"sw.mono\">\n <div class=\"fx-crumb-panel__title\">{{ sw.title }}</div>\n @if (sw.filterPlaceholder) {\n <input\n type=\"search\"\n class=\"fx-crumb-panel__filter\"\n [placeholder]=\"sw.filterPlaceholder\"\n [attr.aria-label]=\"sw.filterPlaceholder\"\n [value]=\"keyword()\"\n (input)=\"onFilter(crumb, $any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n @if (loading() && options().length === 0) {\n <div class=\"fx-crumb-panel__skeleton\" aria-busy=\"true\">\n <span></span><span></span><span></span>\n </div>\n } @else if (options().length === 0) {\n <div class=\"fx-crumb-panel__empty\">{{ sw.emptyLabel || '\u2014' }}</div>\n }\n @for (opt of options(); track opt.value) {\n <button\n type=\"button\"\n role=\"option\"\n class=\"fx-crumb-panel__opt\"\n [class.fx-crumb-panel__opt--on]=\"opt.value === sw.selected\"\n [attr.aria-selected]=\"opt.value === sw.selected\"\n (click)=\"pick(opt, $event)\"\n >\n <span class=\"truncate\">{{ opt.label }}</span>\n @if (opt.meta) {\n <span class=\"fx-crumb-panel__meta\">{{ opt.meta }}</span>\n }\n @if (opt.value === sw.selected) {\n <fx-ui-hero-icon icon=\"check\" [size]=\"13\" class=\"flex shrink-0 text-strong\" />\n }\n </button>\n }\n @if (sw.viewAll || sw.create) {\n <div class=\"fx-crumb-panel__rule\"></div>\n }\n @if (sw.viewAll; as more) {\n <button type=\"button\" class=\"fx-crumb-panel__more\" (click)=\"go($event, more.url)\">\n <fx-ui-hero-icon icon=\"ellipsis-horizontal\" [size]=\"13\" class=\"flex shrink-0 text-faint\" />\n {{ more.label }}\n </button>\n }\n @if (sw.create; as create) {\n <button type=\"button\" class=\"fx-crumb-panel__create\" (click)=\"go($event, create.url)\">\n <fx-ui-hero-icon icon=\"plus\" [size]=\"14\" class=\"flex shrink-0\" />\n {{ create.label }}\n </button>\n }\n </div>\n }\n }\n </span>\n } @else if (!last && crumb.url) {\n <a class=\"fx-crumb-link\" [href]=\"crumb.url\" (click)=\"go($event, crumb.url)\">{{ crumb.label }}</a>\n } @else {\n <span class=\"fx-crumb-current\">{{ crumb.label }}</span>\n }\n }\n</nav>\n", styles: [":host{display:block;min-width:0}.fx-crumbs{display:flex;align-items:center;gap:7px;min-width:0;white-space:nowrap}.fx-crumb-sep{flex:0 0 auto;padding:0 2px;font-size:13px;color:rgb(var(--og-text-faint))}.fx-crumb-link{flex:0 0 auto;max-width:260px;overflow:hidden;text-overflow:ellipsis;font-size:var(--og-fs-sm);font-weight:500;letter-spacing:.005em;color:rgb(var(--og-text-muted));text-decoration:none;transition:color .15s}.fx-crumb-link:hover{color:rgb(var(--og-text))}.fx-crumb-current{min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:var(--og-fs-sm);font-weight:600;color:rgb(var(--og-text))}.fx-crumb-chip{appearance:none;display:inline-flex;align-items:center;gap:8px;min-width:0;max-width:320px;padding:4px 12px;line-height:1.5;border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-control);background:rgb(var(--og-surface));font-size:var(--og-fs-xs);font-weight:600;color:rgb(var(--og-text));box-shadow:0 1px 2px #0000000a;white-space:nowrap;cursor:pointer;transition:box-shadow .12s,border-color .12s}.fx-crumb-chip:hover,.fx-crumb-chip[aria-expanded=true]{box-shadow:0 2px 7px #0000001a}.fx-crumb-chip--mono{font-family:var(--og-font-mono)}.fx-crumb-panel{position:absolute;top:calc(100% + 8px);left:0;z-index:60;min-width:240px;max-width:min(380px,90vw);display:flex;flex-direction:column;padding:6px;background:rgb(var(--og-surface));border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-card);box-shadow:0 10px 28px #00000024;text-align:left;white-space:nowrap;animation:fx-crumb-panel-in .16s ease-out}@keyframes fx-crumb-panel-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.fx-crumb-panel{animation:none}}.fx-crumb-panel__title{padding:6px 12px 8px;margin-bottom:4px;font-size:var(--og-fs-2xs);letter-spacing:.02em;color:rgb(var(--og-text-faint));border-bottom:1px solid rgb(var(--og-border-subtle))}.fx-crumb-panel__filter{margin:2px 6px 6px;padding:6px 10px;border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-control);background:rgb(var(--og-surface-sunken));color:rgb(var(--og-text));font-family:var(--og-font-body);font-size:var(--og-fs-xs);outline:none}.fx-crumb-panel__filter:focus{border-color:rgb(var(--og-accent))}.fx-crumb-panel__opt{appearance:none;width:100%;display:flex;align-items:center;gap:8px;padding:8px 12px;border:0;border-radius:var(--og-radius-control);background:transparent;font-family:var(--og-font-body);font-size:var(--og-fs-sm);font-weight:400;color:rgb(var(--og-text-muted));text-align:left;cursor:pointer;transition:background .12s}.fx-crumb-panel--mono .fx-crumb-panel__opt{font-family:var(--og-font-mono);font-size:var(--og-fs-xs)}.fx-crumb-panel__opt:hover{background:rgb(var(--og-surface-sunken))}.fx-crumb-panel__opt--on{font-weight:600;color:rgb(var(--og-text))}.fx-crumb-panel__meta{margin-left:auto;font-family:var(--og-font-mono);font-size:var(--og-fs-2xs);color:rgb(var(--og-text-faint))}.fx-crumb-panel__opt>fx-ui-hero-icon:last-child{margin-left:auto}.fx-crumb-panel__opt>.fx-crumb-panel__meta+fx-ui-hero-icon{margin-left:0}.fx-crumb-panel__empty{padding:8px 12px;font-size:var(--og-fs-xs);color:rgb(var(--og-text-faint))}.fx-crumb-panel__skeleton{display:flex;flex-direction:column;gap:8px;padding:8px 12px}.fx-crumb-panel__skeleton span{display:block;height:10px;border-radius:4px;background:rgb(var(--og-border-subtle));animation:fx-crumb-pulse 1.2s ease-in-out infinite}.fx-crumb-panel__skeleton span:nth-child(1){width:62%}.fx-crumb-panel__skeleton span:nth-child(2){width:48%;animation-delay:.15s}.fx-crumb-panel__skeleton span:nth-child(3){width:55%;animation-delay:.3s}@keyframes fx-crumb-pulse{0%,to{opacity:1}50%{opacity:.45}}.fx-crumb-panel__rule{margin:4px 0;border-top:1px solid rgb(var(--og-border-subtle))}.fx-crumb-panel__more{appearance:none;width:100%;display:flex;align-items:center;gap:8px;padding:7px 12px;border:0;border-radius:var(--og-radius-control);background:transparent;font-family:var(--og-font-body);font-size:var(--og-fs-xs);color:rgb(var(--og-text-muted));text-align:left;cursor:pointer}.fx-crumb-panel__more:hover{background:rgb(var(--og-surface-sunken));color:rgb(var(--og-text))}.fx-crumb-panel__create{appearance:none;width:calc(100% - 12px);margin:4px 6px 2px;display:flex;align-items:center;gap:8px;padding:9px 12px;border:0;border-radius:var(--og-radius-control);background:rgb(var(--og-accent-soft-bg));color:rgb(var(--og-accent-soft-fg));font-family:var(--og-font-body);font-size:var(--og-fs-sm);font-weight:600;text-align:left;cursor:pointer;transition:background .15s,color .15s}.fx-crumb-panel__create:hover{background:rgb(var(--og-accent));color:rgb(var(--og-accent-contrast))}.text-accent{color:rgb(var(--og-accent))}.text-faint{color:rgb(var(--og-text-faint))}.text-strong{color:rgb(var(--og-text))}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.fx-crumb-cta{appearance:none;display:inline-flex;align-items:center;gap:5px;min-width:0;padding:4px 10px 4px 8px;line-height:1.5;border:1px solid color-mix(in srgb,rgb(var(--og-accent)) 35%,transparent);border-radius:var(--og-radius-control);background:rgb(var(--og-accent-soft-bg));color:rgb(var(--og-accent-soft-fg));font-size:var(--og-fs-2xs);font-weight:600;white-space:nowrap;cursor:pointer;transition:background-color .13s ease,border-color .13s ease}.fx-crumb-cta:hover{background:color-mix(in srgb,rgb(var(--og-accent)) 16%,rgb(var(--og-accent-soft-bg)));border-color:color-mix(in srgb,rgb(var(--og-accent)) 55%,transparent)}\n"], dependencies: [{ kind: "component", type: HeroIconComponent, selector: "fx-ui-hero-icon", inputs: ["stroke", "icon", "solid", "outline", "size", "color", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5038
5115
  }
5039
5116
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: BreadcrumbComponent, decorators: [{
5040
5117
  type: Component,
5041
- args: [{ selector: 'fx-ui-breadcrumb', changeDetection: ChangeDetectionStrategy.OnPush, imports: [HeroIconComponent], template: "<nav aria-label=\"Breadcrumb\" class=\"fx-crumbs\">\n @for (crumb of crumbs(); track $index; let last = $last; let i = $index) {\n @if (i > 0) {\n <span aria-hidden=\"true\" class=\"fx-crumb-sep\">/</span>\n }\n @if (isChip(crumb)) {\n <span class=\"relative inline-flex min-w-0\">\n <button\n type=\"button\"\n class=\"fx-crumb-chip\"\n [class.fx-crumb-chip--mono]=\"crumb.mono\"\n [attr.aria-haspopup]=\"crumb.switcher ? 'listbox' : null\"\n [attr.aria-expanded]=\"crumb.switcher ? openIndex() === i : null\"\n [attr.title]=\"crumb.switcher?.title || crumb.label\"\n (click)=\"onChipClick(crumb, i, $event)\"\n >\n @if (crumb.icon) {\n <fx-ui-hero-icon [icon]=\"crumb.icon\" [size]=\"13\" class=\"flex shrink-0 text-accent\" />\n }\n <span class=\"truncate\">{{ crumb.label }}</span>\n @if (crumb.switcher) {\n <fx-ui-hero-icon icon=\"chevron-down\" [size]=\"10\" class=\"flex shrink-0 text-faint\" />\n }\n </button>\n\n @if (crumb.switcher; as sw) {\n @if (openIndex() === i) {\n <div role=\"listbox\" class=\"fx-crumb-panel\" [class.fx-crumb-panel--mono]=\"sw.mono\">\n <div class=\"fx-crumb-panel__title\">{{ sw.title }}</div>\n @if (sw.filterPlaceholder) {\n <input\n type=\"search\"\n class=\"fx-crumb-panel__filter\"\n [placeholder]=\"sw.filterPlaceholder\"\n [attr.aria-label]=\"sw.filterPlaceholder\"\n [value]=\"keyword()\"\n (input)=\"onFilter(crumb, $any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n @if (loading() && options().length === 0) {\n <div class=\"fx-crumb-panel__skeleton\" aria-busy=\"true\">\n <span></span><span></span><span></span>\n </div>\n } @else if (options().length === 0) {\n <div class=\"fx-crumb-panel__empty\">{{ sw.emptyLabel || '\u2014' }}</div>\n }\n @for (opt of options(); track opt.value) {\n <button\n type=\"button\"\n role=\"option\"\n class=\"fx-crumb-panel__opt\"\n [class.fx-crumb-panel__opt--on]=\"opt.value === sw.selected\"\n [attr.aria-selected]=\"opt.value === sw.selected\"\n (click)=\"pick(opt, $event)\"\n >\n <span class=\"truncate\">{{ opt.label }}</span>\n @if (opt.meta) {\n <span class=\"fx-crumb-panel__meta\">{{ opt.meta }}</span>\n }\n @if (opt.value === sw.selected) {\n <fx-ui-hero-icon icon=\"check\" [size]=\"13\" class=\"flex shrink-0 text-strong\" />\n }\n </button>\n }\n @if (sw.viewAll || sw.create) {\n <div class=\"fx-crumb-panel__rule\"></div>\n }\n @if (sw.viewAll; as more) {\n <button type=\"button\" class=\"fx-crumb-panel__more\" (click)=\"go($event, more.url)\">\n <fx-ui-hero-icon icon=\"ellipsis-horizontal\" [size]=\"13\" class=\"flex shrink-0 text-faint\" />\n {{ more.label }}\n </button>\n }\n @if (sw.create; as create) {\n <button type=\"button\" class=\"fx-crumb-panel__create\" (click)=\"go($event, create.url)\">\n <fx-ui-hero-icon icon=\"plus\" [size]=\"14\" class=\"flex shrink-0\" />\n {{ create.label }}\n </button>\n }\n </div>\n }\n }\n </span>\n } @else if (!last && crumb.url) {\n <a class=\"fx-crumb-link\" [href]=\"crumb.url\" (click)=\"go($event, crumb.url)\">{{ crumb.label }}</a>\n } @else {\n <span class=\"fx-crumb-current\">{{ crumb.label }}</span>\n }\n }\n</nav>\n", styles: [":host{display:block;min-width:0}.fx-crumbs{display:flex;align-items:center;gap:7px;min-width:0;white-space:nowrap}.fx-crumb-sep{flex:0 0 auto;padding:0 2px;font-size:13px;color:rgb(var(--og-text-faint))}.fx-crumb-link{flex:0 0 auto;max-width:260px;overflow:hidden;text-overflow:ellipsis;font-size:var(--og-fs-sm);font-weight:500;letter-spacing:.005em;color:rgb(var(--og-text-muted));text-decoration:none;transition:color .15s}.fx-crumb-link:hover{color:rgb(var(--og-text))}.fx-crumb-current{min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:var(--og-fs-sm);font-weight:600;color:rgb(var(--og-text))}.fx-crumb-chip{appearance:none;display:inline-flex;align-items:center;gap:8px;min-width:0;max-width:320px;padding:4px 12px;line-height:1.5;border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-control);background:rgb(var(--og-surface));font-size:var(--og-fs-xs);font-weight:600;color:rgb(var(--og-text));box-shadow:0 1px 2px #0000000a;white-space:nowrap;cursor:pointer;transition:box-shadow .12s,border-color .12s}.fx-crumb-chip:hover,.fx-crumb-chip[aria-expanded=true]{box-shadow:0 2px 7px #0000001a}.fx-crumb-chip--mono{font-family:var(--og-font-mono)}.fx-crumb-panel{position:absolute;top:calc(100% + 8px);left:0;z-index:60;min-width:240px;max-width:min(380px,90vw);display:flex;flex-direction:column;padding:6px;background:rgb(var(--og-surface));border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-card);box-shadow:0 10px 28px #00000024;text-align:left;white-space:nowrap;animation:fx-crumb-panel-in .16s ease-out}@keyframes fx-crumb-panel-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.fx-crumb-panel{animation:none}}.fx-crumb-panel__title{padding:6px 12px 8px;margin-bottom:4px;font-size:var(--og-fs-2xs);letter-spacing:.02em;color:rgb(var(--og-text-faint));border-bottom:1px solid rgb(var(--og-border-subtle))}.fx-crumb-panel__filter{margin:2px 6px 6px;padding:6px 10px;border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-control);background:rgb(var(--og-surface-sunken));color:rgb(var(--og-text));font-family:var(--og-font-body);font-size:var(--og-fs-xs);outline:none}.fx-crumb-panel__filter:focus{border-color:rgb(var(--og-accent))}.fx-crumb-panel__opt{appearance:none;width:100%;display:flex;align-items:center;gap:8px;padding:8px 12px;border:0;border-radius:var(--og-radius-control);background:transparent;font-family:var(--og-font-body);font-size:var(--og-fs-sm);font-weight:400;color:rgb(var(--og-text-muted));text-align:left;cursor:pointer;transition:background .12s}.fx-crumb-panel--mono .fx-crumb-panel__opt{font-family:var(--og-font-mono);font-size:var(--og-fs-xs)}.fx-crumb-panel__opt:hover{background:rgb(var(--og-surface-sunken))}.fx-crumb-panel__opt--on{font-weight:600;color:rgb(var(--og-text))}.fx-crumb-panel__meta{margin-left:auto;font-family:var(--og-font-mono);font-size:var(--og-fs-2xs);color:rgb(var(--og-text-faint))}.fx-crumb-panel__opt>fx-ui-hero-icon:last-child{margin-left:auto}.fx-crumb-panel__opt>.fx-crumb-panel__meta+fx-ui-hero-icon{margin-left:0}.fx-crumb-panel__empty{padding:8px 12px;font-size:var(--og-fs-xs);color:rgb(var(--og-text-faint))}.fx-crumb-panel__skeleton{display:flex;flex-direction:column;gap:8px;padding:8px 12px}.fx-crumb-panel__skeleton span{display:block;height:10px;border-radius:4px;background:rgb(var(--og-border-subtle));animation:fx-crumb-pulse 1.2s ease-in-out infinite}.fx-crumb-panel__skeleton span:nth-child(1){width:62%}.fx-crumb-panel__skeleton span:nth-child(2){width:48%;animation-delay:.15s}.fx-crumb-panel__skeleton span:nth-child(3){width:55%;animation-delay:.3s}@keyframes fx-crumb-pulse{0%,to{opacity:1}50%{opacity:.45}}.fx-crumb-panel__rule{margin:4px 0;border-top:1px solid rgb(var(--og-border-subtle))}.fx-crumb-panel__more{appearance:none;width:100%;display:flex;align-items:center;gap:8px;padding:7px 12px;border:0;border-radius:var(--og-radius-control);background:transparent;font-family:var(--og-font-body);font-size:var(--og-fs-xs);color:rgb(var(--og-text-muted));text-align:left;cursor:pointer}.fx-crumb-panel__more:hover{background:rgb(var(--og-surface-sunken));color:rgb(var(--og-text))}.fx-crumb-panel__create{appearance:none;width:calc(100% - 12px);margin:4px 6px 2px;display:flex;align-items:center;gap:8px;padding:9px 12px;border:0;border-radius:var(--og-radius-control);background:rgb(var(--og-accent-soft-bg));color:rgb(var(--og-accent-soft-fg));font-family:var(--og-font-body);font-size:var(--og-fs-sm);font-weight:600;text-align:left;cursor:pointer;transition:background .15s,color .15s}.fx-crumb-panel__create:hover{background:rgb(var(--og-accent));color:rgb(var(--og-accent-contrast))}.text-accent{color:rgb(var(--og-accent))}.text-faint{color:rgb(var(--og-text-faint))}.text-strong{color:rgb(var(--og-text))}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}\n"] }]
5118
+ args: [{ selector: 'fx-ui-breadcrumb', changeDetection: ChangeDetectionStrategy.OnPush, imports: [HeroIconComponent], template: "<nav aria-label=\"Breadcrumb\" class=\"fx-crumbs\">\n @for (crumb of crumbs(); track $index; let last = $last; let i = $index) {\n @if (i > 0) {\n <span aria-hidden=\"true\" class=\"fx-crumb-sep\">/</span>\n }\n @if (crumb.cta) {\n <button type=\"button\" class=\"fx-crumb-cta\" (click)=\"go($event, crumb.url || '')\">\n <fx-ui-hero-icon [icon]=\"crumb.icon || 'plus'\" [size]=\"13\" class=\"flex shrink-0\" />\n <span class=\"truncate\">{{ crumb.label }}</span>\n </button>\n } @else if (isChip(crumb)) {\n <span class=\"relative inline-flex min-w-0\">\n <button\n type=\"button\"\n class=\"fx-crumb-chip\"\n [class.fx-crumb-chip--mono]=\"crumb.mono\"\n [attr.aria-haspopup]=\"crumb.switcher ? 'listbox' : null\"\n [attr.aria-expanded]=\"crumb.switcher ? openIndex() === i : null\"\n [attr.title]=\"crumb.switcher?.title || crumb.label\"\n (click)=\"onChipClick(crumb, i, $event)\"\n >\n @if (crumb.icon) {\n <fx-ui-hero-icon [icon]=\"crumb.icon\" [size]=\"13\" class=\"flex shrink-0 text-accent\" />\n }\n <span class=\"truncate\">{{ crumb.label }}</span>\n @if (crumb.switcher) {\n <fx-ui-hero-icon icon=\"chevron-down\" [size]=\"10\" class=\"flex shrink-0 text-faint\" />\n }\n </button>\n\n @if (crumb.switcher; as sw) {\n @if (openIndex() === i) {\n <div role=\"listbox\" class=\"fx-crumb-panel\" [class.fx-crumb-panel--mono]=\"sw.mono\">\n <div class=\"fx-crumb-panel__title\">{{ sw.title }}</div>\n @if (sw.filterPlaceholder) {\n <input\n type=\"search\"\n class=\"fx-crumb-panel__filter\"\n [placeholder]=\"sw.filterPlaceholder\"\n [attr.aria-label]=\"sw.filterPlaceholder\"\n [value]=\"keyword()\"\n (input)=\"onFilter(crumb, $any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n @if (loading() && options().length === 0) {\n <div class=\"fx-crumb-panel__skeleton\" aria-busy=\"true\">\n <span></span><span></span><span></span>\n </div>\n } @else if (options().length === 0) {\n <div class=\"fx-crumb-panel__empty\">{{ sw.emptyLabel || '\u2014' }}</div>\n }\n @for (opt of options(); track opt.value) {\n <button\n type=\"button\"\n role=\"option\"\n class=\"fx-crumb-panel__opt\"\n [class.fx-crumb-panel__opt--on]=\"opt.value === sw.selected\"\n [attr.aria-selected]=\"opt.value === sw.selected\"\n (click)=\"pick(opt, $event)\"\n >\n <span class=\"truncate\">{{ opt.label }}</span>\n @if (opt.meta) {\n <span class=\"fx-crumb-panel__meta\">{{ opt.meta }}</span>\n }\n @if (opt.value === sw.selected) {\n <fx-ui-hero-icon icon=\"check\" [size]=\"13\" class=\"flex shrink-0 text-strong\" />\n }\n </button>\n }\n @if (sw.viewAll || sw.create) {\n <div class=\"fx-crumb-panel__rule\"></div>\n }\n @if (sw.viewAll; as more) {\n <button type=\"button\" class=\"fx-crumb-panel__more\" (click)=\"go($event, more.url)\">\n <fx-ui-hero-icon icon=\"ellipsis-horizontal\" [size]=\"13\" class=\"flex shrink-0 text-faint\" />\n {{ more.label }}\n </button>\n }\n @if (sw.create; as create) {\n <button type=\"button\" class=\"fx-crumb-panel__create\" (click)=\"go($event, create.url)\">\n <fx-ui-hero-icon icon=\"plus\" [size]=\"14\" class=\"flex shrink-0\" />\n {{ create.label }}\n </button>\n }\n </div>\n }\n }\n </span>\n } @else if (!last && crumb.url) {\n <a class=\"fx-crumb-link\" [href]=\"crumb.url\" (click)=\"go($event, crumb.url)\">{{ crumb.label }}</a>\n } @else {\n <span class=\"fx-crumb-current\">{{ crumb.label }}</span>\n }\n }\n</nav>\n", styles: [":host{display:block;min-width:0}.fx-crumbs{display:flex;align-items:center;gap:7px;min-width:0;white-space:nowrap}.fx-crumb-sep{flex:0 0 auto;padding:0 2px;font-size:13px;color:rgb(var(--og-text-faint))}.fx-crumb-link{flex:0 0 auto;max-width:260px;overflow:hidden;text-overflow:ellipsis;font-size:var(--og-fs-sm);font-weight:500;letter-spacing:.005em;color:rgb(var(--og-text-muted));text-decoration:none;transition:color .15s}.fx-crumb-link:hover{color:rgb(var(--og-text))}.fx-crumb-current{min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:var(--og-fs-sm);font-weight:600;color:rgb(var(--og-text))}.fx-crumb-chip{appearance:none;display:inline-flex;align-items:center;gap:8px;min-width:0;max-width:320px;padding:4px 12px;line-height:1.5;border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-control);background:rgb(var(--og-surface));font-size:var(--og-fs-xs);font-weight:600;color:rgb(var(--og-text));box-shadow:0 1px 2px #0000000a;white-space:nowrap;cursor:pointer;transition:box-shadow .12s,border-color .12s}.fx-crumb-chip:hover,.fx-crumb-chip[aria-expanded=true]{box-shadow:0 2px 7px #0000001a}.fx-crumb-chip--mono{font-family:var(--og-font-mono)}.fx-crumb-panel{position:absolute;top:calc(100% + 8px);left:0;z-index:60;min-width:240px;max-width:min(380px,90vw);display:flex;flex-direction:column;padding:6px;background:rgb(var(--og-surface));border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-card);box-shadow:0 10px 28px #00000024;text-align:left;white-space:nowrap;animation:fx-crumb-panel-in .16s ease-out}@keyframes fx-crumb-panel-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.fx-crumb-panel{animation:none}}.fx-crumb-panel__title{padding:6px 12px 8px;margin-bottom:4px;font-size:var(--og-fs-2xs);letter-spacing:.02em;color:rgb(var(--og-text-faint));border-bottom:1px solid rgb(var(--og-border-subtle))}.fx-crumb-panel__filter{margin:2px 6px 6px;padding:6px 10px;border:1px solid rgb(var(--og-border));border-radius:var(--og-radius-control);background:rgb(var(--og-surface-sunken));color:rgb(var(--og-text));font-family:var(--og-font-body);font-size:var(--og-fs-xs);outline:none}.fx-crumb-panel__filter:focus{border-color:rgb(var(--og-accent))}.fx-crumb-panel__opt{appearance:none;width:100%;display:flex;align-items:center;gap:8px;padding:8px 12px;border:0;border-radius:var(--og-radius-control);background:transparent;font-family:var(--og-font-body);font-size:var(--og-fs-sm);font-weight:400;color:rgb(var(--og-text-muted));text-align:left;cursor:pointer;transition:background .12s}.fx-crumb-panel--mono .fx-crumb-panel__opt{font-family:var(--og-font-mono);font-size:var(--og-fs-xs)}.fx-crumb-panel__opt:hover{background:rgb(var(--og-surface-sunken))}.fx-crumb-panel__opt--on{font-weight:600;color:rgb(var(--og-text))}.fx-crumb-panel__meta{margin-left:auto;font-family:var(--og-font-mono);font-size:var(--og-fs-2xs);color:rgb(var(--og-text-faint))}.fx-crumb-panel__opt>fx-ui-hero-icon:last-child{margin-left:auto}.fx-crumb-panel__opt>.fx-crumb-panel__meta+fx-ui-hero-icon{margin-left:0}.fx-crumb-panel__empty{padding:8px 12px;font-size:var(--og-fs-xs);color:rgb(var(--og-text-faint))}.fx-crumb-panel__skeleton{display:flex;flex-direction:column;gap:8px;padding:8px 12px}.fx-crumb-panel__skeleton span{display:block;height:10px;border-radius:4px;background:rgb(var(--og-border-subtle));animation:fx-crumb-pulse 1.2s ease-in-out infinite}.fx-crumb-panel__skeleton span:nth-child(1){width:62%}.fx-crumb-panel__skeleton span:nth-child(2){width:48%;animation-delay:.15s}.fx-crumb-panel__skeleton span:nth-child(3){width:55%;animation-delay:.3s}@keyframes fx-crumb-pulse{0%,to{opacity:1}50%{opacity:.45}}.fx-crumb-panel__rule{margin:4px 0;border-top:1px solid rgb(var(--og-border-subtle))}.fx-crumb-panel__more{appearance:none;width:100%;display:flex;align-items:center;gap:8px;padding:7px 12px;border:0;border-radius:var(--og-radius-control);background:transparent;font-family:var(--og-font-body);font-size:var(--og-fs-xs);color:rgb(var(--og-text-muted));text-align:left;cursor:pointer}.fx-crumb-panel__more:hover{background:rgb(var(--og-surface-sunken));color:rgb(var(--og-text))}.fx-crumb-panel__create{appearance:none;width:calc(100% - 12px);margin:4px 6px 2px;display:flex;align-items:center;gap:8px;padding:9px 12px;border:0;border-radius:var(--og-radius-control);background:rgb(var(--og-accent-soft-bg));color:rgb(var(--og-accent-soft-fg));font-family:var(--og-font-body);font-size:var(--og-fs-sm);font-weight:600;text-align:left;cursor:pointer;transition:background .15s,color .15s}.fx-crumb-panel__create:hover{background:rgb(var(--og-accent));color:rgb(var(--og-accent-contrast))}.text-accent{color:rgb(var(--og-accent))}.text-faint{color:rgb(var(--og-text-faint))}.text-strong{color:rgb(var(--og-text))}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.fx-crumb-cta{appearance:none;display:inline-flex;align-items:center;gap:5px;min-width:0;padding:4px 10px 4px 8px;line-height:1.5;border:1px solid color-mix(in srgb,rgb(var(--og-accent)) 35%,transparent);border-radius:var(--og-radius-control);background:rgb(var(--og-accent-soft-bg));color:rgb(var(--og-accent-soft-fg));font-size:var(--og-fs-2xs);font-weight:600;white-space:nowrap;cursor:pointer;transition:background-color .13s ease,border-color .13s ease}.fx-crumb-cta:hover{background:color-mix(in srgb,rgb(var(--og-accent)) 16%,rgb(var(--og-accent-soft-bg)));border-color:color-mix(in srgb,rgb(var(--og-accent)) 55%,transparent)}\n"] }]
5042
5119
  }], propDecorators: { onDocumentClick: [{
5043
5120
  type: HostListener,
5044
5121
  args: ['document:click', ['$event']]
@@ -5356,19 +5433,16 @@ class BarListComponent {
5356
5433
  widthOf(item) {
5357
5434
  return Math.max((item.value / this.max()) * 100, 2);
5358
5435
  }
5359
- barClass(item) {
5360
- return item.tone ? toneBg(item.tone) : 'bg-accent';
5361
- }
5362
5436
  onSelect(item) {
5363
5437
  if (this.interactive())
5364
5438
  this.select.emit(item);
5365
5439
  }
5366
5440
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: BarListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5367
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: BarListComponent, isStandalone: true, selector: "fx-ui-bar-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { select: "select" }, ngImport: i0, template: "<div class=\"flex flex-col gap-small\">\n @for (item of items(); track item.label) {\n <button\n type=\"button\"\n class=\"text-left rounded-control px-normal py-small -mx-normal transition-colors\"\n [class.hover:bg-surface-sunken]=\"interactive()\"\n [class.cursor-default]=\"!interactive()\"\n [tabindex]=\"interactive() ? 0 : -1\"\n (click)=\"onSelect(item)\"\n >\n <div class=\"flex items-baseline justify-between gap-normal\">\n <span class=\"txt-body-medium-01 truncate\">{{ item.label }}</span>\n <span class=\"text-sm font-semibold text-strong shrink-0\" style=\"font-family: var(--og-font-figure)\">\n {{ item.valueLabel ?? item.value }}\n </span>\n </div>\n @if (item.meta) {\n <div class=\"txt-utility-description truncate\">{{ item.meta }}</div>\n }\n <div class=\"mt-xs h-[var(--og-track-h)] rounded-pill bg-track overflow-hidden\">\n <div\n class=\"h-full rounded-pill transition-all duration-500\"\n [ngClass]=\"barClass(item)\"\n [style.width.%]=\"widthOf(item)\"\n ></div>\n </div>\n </button>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5441
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: BarListComponent, isStandalone: true, selector: "fx-ui-bar-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { select: "select" }, ngImport: i0, template: "<!-- Ranked list, to the reference's \"Top projects by risk\" (measured in the\n browser 16/09): the SCORE leads, right-aligned in a 70px column, then the\n label in a 150px column, then a 3px track taking the rest. It used to be\n label-left / score-right with a 5px full-width track underneath, which is a\n different object \u2014 two lines per row instead of one. -->\n<div class=\"fx-barlist flex flex-col\">\n @for (item of items(); track item.label; let first = $first) {\n <button\n type=\"button\"\n class=\"fx-barlist__row text-left flex items-center\"\n [class.hover:bg-surface-sunken]=\"interactive()\"\n [class.cursor-default]=\"!interactive()\"\n [tabindex]=\"interactive() ? 0 : -1\"\n (click)=\"onSelect(item)\"\n >\n <span class=\"fx-barlist__score shrink-0\">{{ item.valueLabel ?? item.value }}</span>\n <span class=\"fx-barlist__label shrink-0 truncate\">{{ item.label }}</span>\n <span class=\"fx-barlist__track flex-1\">\n <span\n class=\"fx-barlist__fill block\"\n [class.fx-barlist__fill--lead]=\"first\"\n [style.width.%]=\"widthOf(item)\"\n ></span>\n </span>\n </button>\n @if (item.meta) {\n <div class=\"fx-barlist__meta truncate\">{{ item.meta }}</div>\n }\n }\n</div>\n", styles: ["@charset \"UTF-8\";.fx-barlist{gap:8px}.fx-barlist__row{gap:10px;min-height:18px;padding:0 24px 0 0;background:transparent;border:0}.fx-barlist__score{width:70px;text-align:right;font-family:var(--og-font-mono);font-size:var(--og-fs-sm);font-weight:400;color:rgb(var(--og-text))}.fx-barlist__label{width:150px;font-size:var(--og-fs-sm);font-weight:400;color:rgb(var(--og-text-muted))}.fx-barlist__track{height:3px;background:rgb(var(--og-track));overflow:hidden}.fx-barlist__fill{height:100%;background:rgb(var(--og-text-muted));transition:width .5s ease}.fx-barlist__fill--lead{background:rgb(var(--og-accent))}.fx-barlist__meta{font-size:var(--og-fs-2xs);color:rgb(var(--og-text-faint));padding-left:80px}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5368
5442
  }
5369
5443
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: BarListComponent, decorators: [{
5370
5444
  type: Component,
5371
- args: [{ selector: 'fx-ui-bar-list', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgClass], template: "<div class=\"flex flex-col gap-small\">\n @for (item of items(); track item.label) {\n <button\n type=\"button\"\n class=\"text-left rounded-control px-normal py-small -mx-normal transition-colors\"\n [class.hover:bg-surface-sunken]=\"interactive()\"\n [class.cursor-default]=\"!interactive()\"\n [tabindex]=\"interactive() ? 0 : -1\"\n (click)=\"onSelect(item)\"\n >\n <div class=\"flex items-baseline justify-between gap-normal\">\n <span class=\"txt-body-medium-01 truncate\">{{ item.label }}</span>\n <span class=\"text-sm font-semibold text-strong shrink-0\" style=\"font-family: var(--og-font-figure)\">\n {{ item.valueLabel ?? item.value }}\n </span>\n </div>\n @if (item.meta) {\n <div class=\"txt-utility-description truncate\">{{ item.meta }}</div>\n }\n <div class=\"mt-xs h-[var(--og-track-h)] rounded-pill bg-track overflow-hidden\">\n <div\n class=\"h-full rounded-pill transition-all duration-500\"\n [ngClass]=\"barClass(item)\"\n [style.width.%]=\"widthOf(item)\"\n ></div>\n </div>\n </button>\n }\n</div>\n" }]
5445
+ args: [{ selector: 'fx-ui-bar-list', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Ranked list, to the reference's \"Top projects by risk\" (measured in the\n browser 16/09): the SCORE leads, right-aligned in a 70px column, then the\n label in a 150px column, then a 3px track taking the rest. It used to be\n label-left / score-right with a 5px full-width track underneath, which is a\n different object \u2014 two lines per row instead of one. -->\n<div class=\"fx-barlist flex flex-col\">\n @for (item of items(); track item.label; let first = $first) {\n <button\n type=\"button\"\n class=\"fx-barlist__row text-left flex items-center\"\n [class.hover:bg-surface-sunken]=\"interactive()\"\n [class.cursor-default]=\"!interactive()\"\n [tabindex]=\"interactive() ? 0 : -1\"\n (click)=\"onSelect(item)\"\n >\n <span class=\"fx-barlist__score shrink-0\">{{ item.valueLabel ?? item.value }}</span>\n <span class=\"fx-barlist__label shrink-0 truncate\">{{ item.label }}</span>\n <span class=\"fx-barlist__track flex-1\">\n <span\n class=\"fx-barlist__fill block\"\n [class.fx-barlist__fill--lead]=\"first\"\n [style.width.%]=\"widthOf(item)\"\n ></span>\n </span>\n </button>\n @if (item.meta) {\n <div class=\"fx-barlist__meta truncate\">{{ item.meta }}</div>\n }\n }\n</div>\n", styles: ["@charset \"UTF-8\";.fx-barlist{gap:8px}.fx-barlist__row{gap:10px;min-height:18px;padding:0 24px 0 0;background:transparent;border:0}.fx-barlist__score{width:70px;text-align:right;font-family:var(--og-font-mono);font-size:var(--og-fs-sm);font-weight:400;color:rgb(var(--og-text))}.fx-barlist__label{width:150px;font-size:var(--og-fs-sm);font-weight:400;color:rgb(var(--og-text-muted))}.fx-barlist__track{height:3px;background:rgb(var(--og-track));overflow:hidden}.fx-barlist__fill{height:100%;background:rgb(var(--og-text-muted));transition:width .5s ease}.fx-barlist__fill--lead{background:rgb(var(--og-accent))}.fx-barlist__meta{font-size:var(--og-fs-2xs);color:rgb(var(--og-text-faint));padding-left:80px}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
5372
5446
  }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], select: [{ type: i0.Output, args: ["select"] }] } });
5373
5447
 
5374
5448
  /**
@@ -5887,7 +5961,7 @@ class UiModule {
5887
5961
  TrimOnBlurDirective] });
5888
5962
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiModule, imports: [FormsModule,
5889
5963
  ReactiveFormsModule,
5890
- RouterModule, SelectComponent, DatetimePicker, MasterDetailComponent, MenuComponent, ThemePickerComponent, FormsModule,
5964
+ RouterModule, SelectComponent, DatetimePicker, DrawerComponent, FilterPillsComponent, MasterDetailComponent, MenuComponent, ThemePickerComponent, FormsModule,
5891
5965
  ReactiveFormsModule,
5892
5966
  RouterModule] });
5893
5967
  }
@@ -5925,5 +5999,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
5925
5999
  * Generated bundle index. Do not edit.
5926
6000
  */
5927
6001
 
5928
- export { AuthInterceptor, AuthStateService, BarListComponent, BaseComponent, BaseDialogComponent, BaseMasterDetailComponent, BaseTableComponent, BreadcrumbComponent, BreadcrumbService, ButtonComponent, CheckboxComponent, CircleProgressBar, CodeBlockComponent, ConfirmationDialogComponent, DatetimePicker, DndUploadComponent, DrawerComponent, EmptyStateComponent, FX_ICON_SPRITE_URL, FX_MOTION, FX_MOTION_DIRECTIVES, FX_SKELETON_DATA_KEY, FacetsRailComponent, FilterChipsComponent, FilterPillsComponent, FxComponent, FxCountUpDirective, FxDialogService, FxLoadStatusService, FxLoadingService, FxRevealDirective, FxRevealGroupDirective, FxRouteSkeletonService, FxStorageService, FxToastrService, FxUtils, FxValidators, HasPermissionDirective, HeroIconComponent, HttpLoaderFactory, HttpWrapper, InfoFieldComponent, InputComponent, KanbanBoardComponent, ListRowComponent, LoadStatusComponent, LoadingPanel, MasterDetailComponent, MenuComponent, MeterComponent, NotificationService, OG_LIGHT_THEMES, OG_PICKER_THEMES, OG_PICKER_THEME_META, OG_THEMES, OG_THEME_META, PendingBadgeComponent, PermissionGuard, PermissionService, PipelineStepperComponent, ProjectCardComponent, RadioButtonComponent, RadioButtonToggleComponent, RealtimeTableSync, RouteSkeletonComponent, SampleFrameComponent, ScoreBadgeComponent, ScreenshotFrameComponent, SearchBarComponent, SectionCardComponent, SegmentedComponent, SelectComponent, SeverityBadgesComponent, SkeletonDashboardComponent, SkeletonDetailComponent, SkeletonFacetsComponent, SkeletonFrameComponent, SkeletonListComponent, SliderComponent, SparkbarComponent, StageDotsComponent, StatCardsComponent, StatusDotComponent, SwitchComponent, TabComponent, TabGroupComponent, TableCell, TagComponent, ThemePickerComponent, ThemeService, ToastComponent, ToastContainerComponent, TranslationModule, TranslationService, TreeDiagram, TrimOnBlurDirective, UiModule, anyFeatureEnabled, countTone, createApi, featureEnabled, fxListResolver, fxMotionAllowed, fxRevealElement, scoreTone, severityTone, toneBadge, toneBadgeSolid, toneBg, toneBorder, toneColor, toneDot, toneDotChart, toneFg, toneScale, toneText, toneTint };
6002
+ export { AuthInterceptor, AuthStateService, BarListComponent, BaseComponent, BaseDialogComponent, BaseMasterDetailComponent, BaseTableComponent, BreadcrumbComponent, BreadcrumbService, ButtonComponent, CheckboxComponent, CircleProgressBar, CodeBlockComponent, ConfirmationDialogComponent, DatetimePicker, DndUploadComponent, DrawerComponent, EmptyStateComponent, FX_ICON_SPRITE_URL, FX_LUCIDE_ICONS, FX_MOTION, FX_MOTION_DIRECTIVES, FX_SKELETON_DATA_KEY, FacetsRailComponent, FilterChipsComponent, FilterPillsComponent, FxComponent, FxCountUpDirective, FxDialogService, FxLoadStatusService, FxLoadingService, FxLucideModule, FxRevealDirective, FxRevealGroupDirective, FxRouteSkeletonService, FxStorageService, FxToastrService, FxUtils, FxValidators, HasPermissionDirective, HeroIconComponent, HttpLoaderFactory, HttpWrapper, InfoFieldComponent, InputComponent, KanbanBoardComponent, ListRowComponent, LoadStatusComponent, LoadingPanel, MasterDetailComponent, MenuComponent, MeterComponent, NotificationService, OG_LIGHT_THEMES, OG_PICKER_THEMES, OG_PICKER_THEME_META, OG_THEMES, OG_THEME_META, PendingBadgeComponent, PermissionGuard, PermissionService, PipelineStepperComponent, ProjectCardComponent, RadioButtonComponent, RadioButtonToggleComponent, RealtimeTableSync, RouteSkeletonComponent, SampleFrameComponent, ScoreBadgeComponent, ScreenshotFrameComponent, SearchBarComponent, SectionCardComponent, SegmentedComponent, SelectComponent, SeverityBadgesComponent, SkeletonDashboardComponent, SkeletonDetailComponent, SkeletonFacetsComponent, SkeletonFrameComponent, SkeletonListComponent, SliderComponent, SparkbarComponent, StageDotsComponent, StatCardsComponent, StatusDotComponent, SwitchComponent, TabComponent, TabGroupComponent, TableCell, TagComponent, ThemePickerComponent, ThemeService, ToastComponent, ToastContainerComponent, TranslationModule, TranslationService, TreeDiagram, TrimOnBlurDirective, UiModule, anyFeatureEnabled, countTone, createApi, featureEnabled, fxListResolver, fxMotionAllowed, fxRevealElement, scoreTone, severityTone, toneBadge, toneBadgeSolid, toneBg, toneBorder, toneColor, toneDot, toneDotChart, toneFg, toneScale, toneText, toneTint };
5929
6003
  //# sourceMappingURL=foxeltech-angular-ui.mjs.map