@kaufmann-lab/ui 0.1.3 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, inject, computed, effect, Component, booleanAttribute, model, output, numberAttribute, contentChildren, signal, DestroyRef, viewChild, forwardRef, ElementRef, HostListener, ChangeDetectionStrategy, afterRenderEffect, afterNextRender, Injectable } from '@angular/core';
2
+ import { input, inject, computed, effect, Component, booleanAttribute, model, output, numberAttribute, contentChildren, signal, DestroyRef, viewChild, forwardRef, ViewContainerRef, HostListener, TemplateRef, contentChild, ElementRef, Directive, ChangeDetectionStrategy, afterRenderEffect, afterNextRender, Injectable } from '@angular/core';
3
3
  import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
4
4
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
5
+ import { Overlay } from '@angular/cdk/overlay';
6
+ import { TemplatePortal } from '@angular/cdk/portal';
5
7
 
6
8
  /** Símbolo de trazo "activity" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
7
9
  const activity = {
@@ -849,6 +851,55 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
849
851
  }, styles: [":host{display:inline-flex;align-items:center;gap:var(--kf-space-xs);padding:var(--kf-space-xs) var(--kf-space-sm);border-radius:var(--kf-radius-pill);background:var(--kf-color-warning-soft);border:1px solid var(--kf-color-warning);color:var(--kf-color-warning-strong);font-size:.75rem;font-weight:700;letter-spacing:.02em;text-transform:uppercase;line-height:1}:host(.kf-admin-badge--inline){background:transparent;border-color:#e2b93b80;color:var(--kf-color-warning)}\n"] }]
850
852
  }], propDecorators: { inline: [{ type: i0.Input, args: [{ isSignal: true, alias: "inline", required: false }] }] } });
851
853
 
854
+ /**
855
+ * Indicador de carga indeterminado: inline, dentro de un botón, o como único
856
+ * contenido de una sección mientras llega la data.
857
+ *
858
+ * Cuando se conoce la forma del contenido que viene, preferir `kf-skeleton`
859
+ * (dibuja la silueta). El spinner es para "algo está pasando" sin forma
860
+ * conocida — guardar, enviar, reintentar.
861
+ *
862
+ * Lleva `role="status"`, así que el lector de pantalla anuncia el `label`
863
+ * cuando aparece. Sin `label` cae en un texto accesible por defecto.
864
+ *
865
+ * <kf-spinner />
866
+ * <kf-spinner size="sm" label="Cargando…" />
867
+ * <kf-spinner onDark />
868
+ */
869
+ class Spinner {
870
+ size = input('md', /* @ts-ignore */
871
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
872
+ /** Texto al lado del anillo. Vacío deja solo el anillo (y el texto para lectores). */
873
+ label = input('', /* @ts-ignore */
874
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
875
+ /** Invierte los colores para fondos oscuros. */
876
+ onDark = input(false, { ...(ngDevMode ? { debugName: "onDark" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
877
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, deps: [], target: i0.ɵɵFactoryTarget.Component });
878
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Spinner, isStandalone: true, selector: "kf-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, onDark: { classPropertyName: "onDark", publicName: "onDark", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
879
+ <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
880
+ <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
881
+ @if (label()) {
882
+ <span class="kf-spinner__label">{{ label() }}</span>
883
+ } @else {
884
+ <span class="kf-spinner__sr">Cargando</span>
885
+ }
886
+ </span>
887
+ `, isInline: true, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] });
888
+ }
889
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, decorators: [{
890
+ type: Component,
891
+ args: [{ selector: 'kf-spinner', template: `
892
+ <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
893
+ <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
894
+ @if (label()) {
895
+ <span class="kf-spinner__label">{{ label() }}</span>
896
+ } @else {
897
+ <span class="kf-spinner__sr">Cargando</span>
898
+ }
899
+ </span>
900
+ `, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] }]
901
+ }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], onDark: [{ type: i0.Input, args: [{ isSignal: true, alias: "onDark", required: false }] }] } });
902
+
852
903
  /**
853
904
  * Wraps a native `<button>`, so keyboard handling, focus and form submission
854
905
  * keep working. The cost of the wrapper is forwarding `disabled` and `type` by
@@ -864,10 +915,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
864
915
  *
865
916
  * `(click)` is not forwarded — it bubbles from the inner button to the host.
866
917
  *
918
+ * `loading` replaces the projected content with `kf-spinner` and forces
919
+ * `disabled` — for the common "Ingresando…"/"Enviando…" submit pattern,
920
+ * which before this prop existed as plain text swaps with no visual
921
+ * indicator (see `web-portal-latam-frontend`'s login submit buttons).
922
+ *
867
923
  * <kf-button (click)="save()">Save</kf-button>
868
924
  * <kf-button variant="ghost" disabled>Cancel</kf-button>
869
925
  * <kf-button variant="danger" (click)="remove()">Delete</kf-button>
870
926
  * <kf-button size="sm" icon="calendar" block>Reservar hora</kf-button>
927
+ * <kf-button type="submit" [loading]="submitting()">Ingresar</kf-button>
871
928
  */
872
929
  class Button {
873
930
  variant = input('primary', /* @ts-ignore */
@@ -883,11 +940,19 @@ class Button {
883
940
  ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
884
941
  /** Stretches the button to the full width of its container. */
885
942
  block = input(false, { ...(ngDevMode ? { debugName: "block" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
943
+ /** Replaces the projected content with `kf-spinner` and forces `disabled`. */
944
+ loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
945
+ /** `ghost`/`outline` have no solid background — the default spinner ring already reads fine there. */
946
+ hasSolidFill = computed(() => {
947
+ const variant = this.variant();
948
+ return variant !== 'ghost' && variant !== 'outline';
949
+ }, /* @ts-ignore */
950
+ ...(ngDevMode ? [{ debugName: "hasSolidFill" }] : /* istanbul ignore next */ []));
886
951
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Button, deps: [], target: i0.ɵɵFactoryTarget.Component });
887
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Button, isStandalone: true, selector: "kf-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.block": "block() ? '' : null" } }, ngImport: i0, template: `
952
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Button, isStandalone: true, selector: "kf-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.block": "block() ? '' : null" } }, ngImport: i0, template: `
888
953
  <button
889
954
  [type]="type()"
890
- [disabled]="disabled()"
955
+ [disabled]="disabled() || loading()"
891
956
  [class.kf-button--secondary]="variant() === 'secondary'"
892
957
  [class.kf-button--ghost]="variant() === 'ghost'"
893
958
  [class.kf-button--outline]="variant() === 'outline'"
@@ -895,19 +960,27 @@ class Button {
895
960
  [class.kf-button--sm]="size() === 'sm'"
896
961
  [class.kf-button--lg]="size() === 'lg'"
897
962
  >
898
- @if (icon(); as iconName) {
899
- <kf-icon class="kf-button__icon" [name]="iconName" [size]="size() === 'lg' ? 'md' : 'sm'" />
963
+ @if (loading()) {
964
+ <kf-spinner size="sm" [onDark]="hasSolidFill()" />
965
+ } @else {
966
+ @if (icon(); as iconName) {
967
+ <kf-icon
968
+ class="kf-button__icon"
969
+ [name]="iconName"
970
+ [size]="size() === 'lg' ? 'md' : 'sm'"
971
+ />
972
+ }
973
+ <ng-content />
900
974
  }
901
- <ng-content />
902
975
  </button>
903
- `, isInline: true, styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
976
+ `, isInline: true, styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }, { kind: "component", type: Spinner, selector: "kf-spinner", inputs: ["size", "label", "onDark"] }] });
904
977
  }
905
978
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Button, decorators: [{
906
979
  type: Component,
907
- args: [{ selector: 'kf-button', imports: [Icon], template: `
980
+ args: [{ selector: 'kf-button', imports: [Icon, Spinner], template: `
908
981
  <button
909
982
  [type]="type()"
910
- [disabled]="disabled()"
983
+ [disabled]="disabled() || loading()"
911
984
  [class.kf-button--secondary]="variant() === 'secondary'"
912
985
  [class.kf-button--ghost]="variant() === 'ghost'"
913
986
  [class.kf-button--outline]="variant() === 'outline'"
@@ -915,15 +988,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
915
988
  [class.kf-button--sm]="size() === 'sm'"
916
989
  [class.kf-button--lg]="size() === 'lg'"
917
990
  >
918
- @if (icon(); as iconName) {
919
- <kf-icon class="kf-button__icon" [name]="iconName" [size]="size() === 'lg' ? 'md' : 'sm'" />
991
+ @if (loading()) {
992
+ <kf-spinner size="sm" [onDark]="hasSolidFill()" />
993
+ } @else {
994
+ @if (icon(); as iconName) {
995
+ <kf-icon
996
+ class="kf-button__icon"
997
+ [name]="iconName"
998
+ [size]="size() === 'lg' ? 'md' : 'sm'"
999
+ />
1000
+ }
1001
+ <ng-content />
920
1002
  }
921
- <ng-content />
922
1003
  </button>
923
1004
  `, host: {
924
1005
  '[attr.block]': "block() ? '' : null",
925
1006
  }, styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"] }]
926
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }] } });
1007
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
927
1008
 
928
1009
  /**
929
1010
  * Banner contextual que vive dentro del flujo: validaciones, avisos de
@@ -1463,7 +1544,7 @@ class Carousel {
1463
1544
  aria-label="Slide anterior"
1464
1545
  (click)="previous()"
1465
1546
  >
1466
- ‹
1547
+ <kf-icon name="chevron-left" size="sm" />
1467
1548
  </button>
1468
1549
  <button
1469
1550
  type="button"
@@ -1471,16 +1552,16 @@ class Carousel {
1471
1552
  aria-label="Slide siguiente"
1472
1553
  (click)="next()"
1473
1554
  >
1474
- ›
1555
+ <kf-icon name="chevron-right" size="sm" />
1475
1556
  </button>
1476
1557
  </div>
1477
1558
  }
1478
1559
  </div>
1479
- `, isInline: true, styles: [":host{display:block;height:100%;font-family:var(--kf-font-body)}.kf-carousel{position:relative;height:100%}.kf-carousel__viewport{height:100%;overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;height:100%;transition:transform .32s ease}.kf-carousel__controls{position:absolute;z-index:2;right:var(--kf-space-sm);bottom:var(--kf-space-sm);display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-carousel__arrow{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border:1px solid rgb(255 255 255 / 22%);border-radius:var(--kf-radius-pill);background:#ffffff1f;color:#fff;font-size:1rem;line-height:1;cursor:pointer;transition:background .14s}.kf-carousel__arrow:hover{background:#ffffff3d}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dots{display:flex;gap:var(--kf-space-xs)}.kf-carousel__dot{width:7px;height:7px;padding:0;border:none;border-radius:var(--kf-radius-pill);background:#ffffff59;cursor:pointer;transition:background .14s,width .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dot--active{width:1.125rem;border-radius:var(--kf-radius-xs);background:#fff}\n"] });
1560
+ `, isInline: true, styles: [":host{display:block;height:100%;font-family:var(--kf-font-body)}.kf-carousel{position:relative;height:100%}.kf-carousel__viewport{height:100%;overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;height:100%;transition:transform .32s ease}.kf-carousel__controls{position:absolute;z-index:2;right:var(--kf-space-sm);bottom:var(--kf-space-sm);display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-carousel__arrow{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border:1px solid rgb(255 255 255 / 22%);border-radius:var(--kf-radius-pill);background:#ffffff1f;color:#fff;cursor:pointer;transition:background .14s}.kf-carousel__arrow:hover{background:#ffffff3d}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dots{display:flex;gap:var(--kf-space-xs)}.kf-carousel__dot{width:7px;height:7px;padding:0;border:none;border-radius:var(--kf-radius-pill);background:#ffffff59;cursor:pointer;transition:background .14s,width .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dot--active{width:1.125rem;border-radius:var(--kf-radius-xs);background:#fff}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
1480
1561
  }
1481
1562
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Carousel, decorators: [{
1482
1563
  type: Component,
1483
- args: [{ selector: 'kf-carousel', template: `
1564
+ args: [{ selector: 'kf-carousel', imports: [Icon], template: `
1484
1565
  <div
1485
1566
  class="kf-carousel"
1486
1567
  (mouseenter)="pauseAutoplay()"
@@ -1518,7 +1599,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1518
1599
  aria-label="Slide anterior"
1519
1600
  (click)="previous()"
1520
1601
  >
1521
- ‹
1602
+ <kf-icon name="chevron-left" size="sm" />
1522
1603
  </button>
1523
1604
  <button
1524
1605
  type="button"
@@ -1526,12 +1607,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1526
1607
  aria-label="Slide siguiente"
1527
1608
  (click)="next()"
1528
1609
  >
1529
- ›
1610
+ <kf-icon name="chevron-right" size="sm" />
1530
1611
  </button>
1531
1612
  </div>
1532
1613
  }
1533
1614
  </div>
1534
- `, styles: [":host{display:block;height:100%;font-family:var(--kf-font-body)}.kf-carousel{position:relative;height:100%}.kf-carousel__viewport{height:100%;overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;height:100%;transition:transform .32s ease}.kf-carousel__controls{position:absolute;z-index:2;right:var(--kf-space-sm);bottom:var(--kf-space-sm);display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-carousel__arrow{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border:1px solid rgb(255 255 255 / 22%);border-radius:var(--kf-radius-pill);background:#ffffff1f;color:#fff;font-size:1rem;line-height:1;cursor:pointer;transition:background .14s}.kf-carousel__arrow:hover{background:#ffffff3d}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dots{display:flex;gap:var(--kf-space-xs)}.kf-carousel__dot{width:7px;height:7px;padding:0;border:none;border-radius:var(--kf-radius-pill);background:#ffffff59;cursor:pointer;transition:background .14s,width .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dot--active{width:1.125rem;border-radius:var(--kf-radius-xs);background:#fff}\n"] }]
1615
+ `, styles: [":host{display:block;height:100%;font-family:var(--kf-font-body)}.kf-carousel{position:relative;height:100%}.kf-carousel__viewport{height:100%;overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;height:100%;transition:transform .32s ease}.kf-carousel__controls{position:absolute;z-index:2;right:var(--kf-space-sm);bottom:var(--kf-space-sm);display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-carousel__arrow{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border:1px solid rgb(255 255 255 / 22%);border-radius:var(--kf-radius-pill);background:#ffffff1f;color:#fff;cursor:pointer;transition:background .14s}.kf-carousel__arrow:hover{background:#ffffff3d}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dots{display:flex;gap:var(--kf-space-xs)}.kf-carousel__dot{width:7px;height:7px;padding:0;border:none;border-radius:var(--kf-radius-pill);background:#ffffff59;cursor:pointer;transition:background .14s,width .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dot--active{width:1.125rem;border-radius:var(--kf-radius-xs);background:#fff}\n"] }]
1535
1616
  }], propDecorators: { autoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplay", required: false }] }], interval: [{ type: i0.Input, args: [{ isSignal: true, alias: "interval", required: false }] }], indexChange: [{ type: i0.Output, args: ["indexChange"] }], slides: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CarouselSlide), { isSignal: true }] }] } });
1536
1617
 
1537
1618
  /**
@@ -1700,10 +1781,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1700
1781
  }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
1701
1782
 
1702
1783
  /**
1703
- * Línea separadora decorativa entre bloques de contenido (ej. columnas de
1704
- * metadata en `kf-company-selector`). `vertical` necesita que el contenedor
1705
- * padre sea flex/grid con altura definida — se estira con `align-self:
1706
- * stretch`; sin eso no tiene alto propio y no se ve.
1784
+ * Separador visual simple entre bloques de contenido. `orientation`
1785
+ * controla si es una línea horizontal (para separar secciones apiladas) o
1786
+ * vertical (para separar elementos en una fila, ej. acciones en una toolbar,
1787
+ * o las columnas de metadata en `kf-company-selector`).
1788
+ *
1789
+ * En `vertical`, el divisor necesita una altura del contexto donde vive —
1790
+ * `:host { align-self: stretch }` lo hace ocupar la altura del contenedor
1791
+ * flex/grid padre sin que el consumidor tenga que fijarla a mano. `flex:
1792
+ * none` evita que el propio divisor se achique a 0 dentro de ese contenedor
1793
+ * flex cuando hay contenido hermano compitiendo por espacio.
1707
1794
  *
1708
1795
  * <kf-divider />
1709
1796
  * <kf-divider orientation="vertical" />
@@ -1712,15 +1799,15 @@ class Divider {
1712
1799
  orientation = input('horizontal', /* @ts-ignore */
1713
1800
  ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
1714
1801
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Divider, deps: [], target: i0.ɵɵFactoryTarget.Component });
1715
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: Divider, isStandalone: true, selector: "kf-divider", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "separator" }, properties: { "attr.aria-orientation": "orientation()", "class.kf-divider--vertical": "orientation() === 'vertical'" } }, ngImport: i0, template: '', isInline: true, styles: [":host{display:block;flex:none;background:var(--kf-color-border)}:host(.kf-divider--vertical){width:1px;align-self:stretch}:host(:not(.kf-divider--vertical)){width:100%;height:1px}\n"] });
1802
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: Divider, isStandalone: true, selector: "kf-divider", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "separator" }, properties: { "attr.orientation": "orientation()", "attr.aria-orientation": "orientation()" } }, ngImport: i0, template: '', isInline: true, styles: [":host{display:block;flex:none;border:none;background:var(--kf-color-border)}:host,:host([orientation=\"horizontal\"]){width:100%;height:1px}:host([orientation=\"vertical\"]){align-self:stretch;width:1px;height:auto}\n"] });
1716
1803
  }
1717
1804
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Divider, decorators: [{
1718
1805
  type: Component,
1719
1806
  args: [{ selector: 'kf-divider', template: '', host: {
1720
1807
  role: 'separator',
1808
+ '[attr.orientation]': 'orientation()',
1721
1809
  '[attr.aria-orientation]': 'orientation()',
1722
- '[class.kf-divider--vertical]': "orientation() === 'vertical'",
1723
- }, styles: [":host{display:block;flex:none;background:var(--kf-color-border)}:host(.kf-divider--vertical){width:1px;align-self:stretch}:host(:not(.kf-divider--vertical)){width:100%;height:1px}\n"] }]
1810
+ }, styles: [":host{display:block;flex:none;border:none;background:var(--kf-color-border)}:host,:host([orientation=\"horizontal\"]){width:100%;height:1px}:host([orientation=\"vertical\"]){align-self:stretch;width:1px;height:auto}\n"] }]
1724
1811
  }], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }] } });
1725
1812
 
1726
1813
  /**
@@ -1771,23 +1858,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1771
1858
  `, styles: [":host{display:contents}.kf-field-label{font-family:var(--kf-font-title);font-size:.75rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}.kf-field-label__required{color:var(--kf-color-error);margin-inline-start:2px}\n"] }]
1772
1859
  }], propDecorators: { for: [{ type: i0.Input, args: [{ isSignal: true, alias: "for", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }] } });
1773
1860
 
1774
- let nextId$2 = 0;
1861
+ let nextId$1 = 0;
1775
1862
  /**
1776
1863
  * Dropdown de selección simple con panel propio — NUNCA usa el `<select>`
1777
1864
  * nativo del navegador. Un `<select>` real siempre delega el popup de
1778
1865
  * opciones al sistema operativo, y ningún CSS puede controlar cómo se ve
1779
- * ese popup (ver el mismo problema ya resuelto en `SelectMenu`, del que
1780
- * este componente reutiliza el patrón completo: trigger + panel flotante
1781
- * propio, cerrado con click-afuera y Escape).
1866
+ * ese popup.
1867
+ *
1868
+ * Hasta la `0.1.5` existía además `kf-select-menu`, un segundo componente
1869
+ * prácticamente idéntico (mismo trigger, mismo panel, mismo CSS) que solo
1870
+ * se diferenciaba en aceptar `options` únicamente como `{ value, label }[]`
1871
+ * (sin el atajo `string[]` que `Select` ya tenía). Se fusionaron en este
1872
+ * componente en la `0.2.0` — mantener dos componentes con la misma
1873
+ * interacción y el mismo aspecto visual, cuya única diferencia real era la
1874
+ * forma de un input, no se sostenía como dos piezas separadas del catálogo.
1782
1875
  *
1783
- * Pensado para sets simples y cortos. Para listas más ricas o con más
1784
- * opciones, es el mismo componente que `SelectMenu` — la diferencia es
1785
- * solo de estilo (label uppercase, fondo gris que pasa a blanco al foco,
1786
- * a tono con `Input`) y que acepta `options` como `string[]` además de
1787
- * `{ value, label }[]`.
1876
+ * El panel flotante se conecta al trigger vía `@angular/cdk/overlay` — el
1877
+ * mismo mecanismo que `Dropdown` (ver `../dropdown/Dropdown.ts`).
1788
1878
  *
1789
1879
  * <kf-select label="Sede" [options]="['Lima', 'Arequipa', 'Trujillo']" placeholder="Selecciona…" />
1790
1880
  * <kf-select [options]="[{ value: 'pe', label: 'Perú' }]" (valueChange)="onCountry($event)" />
1881
+ *
1882
+ * Pendiente: el prop `icon` de otros componentes del design system requiere
1883
+ * un componente Icon / sprite SVG que esta librería todavía no tiene — este
1884
+ * componente no usa iconos, así que no aplica acá.
1791
1885
  */
1792
1886
  class Select {
1793
1887
  label = input(/* @ts-ignore */
@@ -1800,7 +1894,7 @@ class Select {
1800
1894
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1801
1895
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1802
1896
  valueChange = output();
1803
- instanceId = `kf-select-${nextId$2++}`;
1897
+ instanceId = `kf-select-${nextId$1++}`;
1804
1898
  triggerId = `${this.instanceId}-trigger`;
1805
1899
  listboxId = `${this.instanceId}-listbox`;
1806
1900
  isOpen = signal(false, /* @ts-ignore */
@@ -1810,7 +1904,34 @@ class Select {
1810
1904
  ...(ngDevMode ? [{ debugName: "normalizedOptions" }] : /* istanbul ignore next */ []));
1811
1905
  selectedOption = computed(() => this.normalizedOptions().find((option) => option.value === this.value()), /* @ts-ignore */
1812
1906
  ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
1813
- host = inject((ElementRef));
1907
+ triggerRef = viewChild.required('trigger', /* @ts-ignore */
1908
+ ...(ngDevMode ? [{ debugName: "triggerRef" }] : /* istanbul ignore next */ []));
1909
+ panelTemplate = viewChild.required('panelTemplate', /* @ts-ignore */
1910
+ ...(ngDevMode ? [{ debugName: "panelTemplate" }] : /* istanbul ignore next */ []));
1911
+ overlayService = inject(Overlay);
1912
+ viewContainerRef = inject(ViewContainerRef);
1913
+ destroyRef = inject(DestroyRef);
1914
+ overlayRef = null;
1915
+ constructor() {
1916
+ effect(() => {
1917
+ if (this.isOpen()) {
1918
+ this.attach();
1919
+ }
1920
+ else {
1921
+ this.detach();
1922
+ }
1923
+ });
1924
+ this.destroyRef.onDestroy(() => this.overlayRef?.dispose());
1925
+ }
1926
+ /**
1927
+ * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo ve
1928
+ * teclas presionadas con el foco DENTRO del pane del overlay. Si el foco
1929
+ * sigue en el trigger (o en cualquier otro lado de la página) mientras el
1930
+ * panel está abierto, `keydownEvents()` nunca dispara.
1931
+ */
1932
+ onDocumentEscape() {
1933
+ this.isOpen.set(false);
1934
+ }
1814
1935
  toggle() {
1815
1936
  if (this.disabled()) {
1816
1937
  return;
@@ -1823,16 +1944,53 @@ class Select {
1823
1944
  this.valueChange.emit(option.value);
1824
1945
  }
1825
1946
  }
1826
- onDocumentClick(event) {
1827
- if (this.isOpen() && !this.host.nativeElement.contains(event.target)) {
1828
- this.isOpen.set(false);
1947
+ attach() {
1948
+ if (this.overlayRef?.hasAttached()) {
1949
+ return;
1829
1950
  }
1951
+ const overlayRef = this.getOrCreateOverlayRef();
1952
+ // Se remide el ancho del trigger en cada apertura, no solo la primera —
1953
+ // ver la misma nota en SelectMenu: cachear el ancho en la creación del
1954
+ // overlay lo deja desincronizado si el trigger cambia de tamaño entre
1955
+ // una apertura y la siguiente.
1956
+ overlayRef.updateSize({ width: this.triggerRef().nativeElement.getBoundingClientRect().width });
1957
+ const portal = new TemplatePortal(this.panelTemplate(), this.viewContainerRef);
1958
+ overlayRef.attach(portal);
1959
+ overlayRef.outsidePointerEvents().subscribe(() => this.isOpen.set(false));
1960
+ overlayRef.keydownEvents().subscribe((event) => {
1961
+ if (event.key === 'Escape') {
1962
+ this.isOpen.set(false);
1963
+ }
1964
+ });
1830
1965
  }
1831
- onEscape() {
1832
- this.isOpen.set(false);
1966
+ detach() {
1967
+ this.overlayRef?.detach();
1968
+ }
1969
+ getOrCreateOverlayRef() {
1970
+ if (this.overlayRef) {
1971
+ return this.overlayRef;
1972
+ }
1973
+ const positionStrategy = this.overlayService
1974
+ .position()
1975
+ .flexibleConnectedTo(this.triggerRef())
1976
+ .withPositions([
1977
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
1978
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
1979
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
1980
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
1981
+ ])
1982
+ .withViewportMargin(8)
1983
+ .withPush(true);
1984
+ this.overlayRef = this.overlayService.create({
1985
+ positionStrategy,
1986
+ scrollStrategy: this.overlayService.scrollStrategies.reposition(),
1987
+ width: this.triggerRef().nativeElement.getBoundingClientRect().width,
1988
+ maxWidth: '90vw',
1989
+ });
1990
+ return this.overlayRef;
1833
1991
  }
1834
1992
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Select, deps: [], target: i0.ɵɵFactoryTarget.Component });
1835
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Select, isStandalone: true, selector: "kf-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: `
1993
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Select, isStandalone: true, selector: "kf-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:keydown.escape": "onDocumentEscape()" } }, viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "panelTemplate", first: true, predicate: ["panelTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: `
1836
1994
  @if (label()) {
1837
1995
  <kf-field-label [for]="triggerId">{{ label() }}</kf-field-label>
1838
1996
  }
@@ -1857,7 +2015,7 @@ class Select {
1857
2015
  aria-hidden="true"
1858
2016
  ></span>
1859
2017
  </button>
1860
- @if (isOpen()) {
2018
+ <ng-template #panelTemplate>
1861
2019
  <ul
1862
2020
  class="kf-select__panel"
1863
2021
  [id]="listboxId"
@@ -1881,8 +2039,8 @@ class Select {
1881
2039
  <li class="kf-select__empty">Sin opciones disponibles</li>
1882
2040
  }
1883
2041
  </ul>
1884
- }
1885
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
2042
+ </ng-template>
2043
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);width:240px;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{box-sizing:border-box;width:100%;margin:0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
1886
2044
  }
1887
2045
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Select, decorators: [{
1888
2046
  type: Component,
@@ -1911,7 +2069,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1911
2069
  aria-hidden="true"
1912
2070
  ></span>
1913
2071
  </button>
1914
- @if (isOpen()) {
2072
+ <ng-template #panelTemplate>
1915
2073
  <ul
1916
2074
  class="kf-select__panel"
1917
2075
  [id]="listboxId"
@@ -1935,12 +2093,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1935
2093
  <li class="kf-select__empty">Sin opciones disponibles</li>
1936
2094
  }
1937
2095
  </ul>
1938
- }
1939
- `, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
1940
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], onDocumentClick: [{
1941
- type: HostListener,
1942
- args: ['document:click', ['$event']]
1943
- }], onEscape: [{
2096
+ </ng-template>
2097
+ `, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);width:240px;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{box-sizing:border-box;width:100%;margin:0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
2098
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], panelTemplate: [{ type: i0.ViewChild, args: ['panelTemplate', { isSignal: true }] }], onDocumentEscape: [{
1944
2099
  type: HostListener,
1945
2100
  args: ['document:keydown.escape']
1946
2101
  }] } });
@@ -2179,55 +2334,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2179
2334
  `, styles: [":host{display:block}.kf-skeleton{display:block;background:linear-gradient(90deg,var(--kf-color-border) 25%,var(--kf-color-surface) 37%,var(--kf-color-border) 63%);background-size:400% 100%;animation:kf-skeleton-shimmer 1.4s ease infinite}.kf-skeleton--text{height:14px;border-radius:var(--kf-radius-xs)}.kf-skeleton--rect{height:80px;border-radius:var(--kf-radius-sm)}.kf-skeleton--circle{aspect-ratio:1 / 1;border-radius:var(--kf-radius-pill)}@keyframes kf-skeleton-shimmer{0%{background-position:100% 0}to{background-position:0 0}}@media(prefers-reduced-motion:reduce){.kf-skeleton{animation:none;background:var(--kf-color-border)}}\n"] }]
2180
2335
  }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }] } });
2181
2336
 
2182
- /**
2183
- * Indicador de carga indeterminado: inline, dentro de un botón, o como único
2184
- * contenido de una sección mientras llega la data.
2185
- *
2186
- * Cuando se conoce la forma del contenido que viene, preferir `kf-skeleton`
2187
- * (dibuja la silueta). El spinner es para "algo está pasando" sin forma
2188
- * conocida — guardar, enviar, reintentar.
2189
- *
2190
- * Lleva `role="status"`, así que el lector de pantalla anuncia el `label`
2191
- * cuando aparece. Sin `label` cae en un texto accesible por defecto.
2192
- *
2193
- * <kf-spinner />
2194
- * <kf-spinner size="sm" label="Cargando…" />
2195
- * <kf-spinner onDark />
2196
- */
2197
- class Spinner {
2198
- size = input('md', /* @ts-ignore */
2199
- ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
2200
- /** Texto al lado del anillo. Vacío deja solo el anillo (y el texto para lectores). */
2201
- label = input('', /* @ts-ignore */
2202
- ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
2203
- /** Invierte los colores para fondos oscuros. */
2204
- onDark = input(false, { ...(ngDevMode ? { debugName: "onDark" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
2205
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, deps: [], target: i0.ɵɵFactoryTarget.Component });
2206
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Spinner, isStandalone: true, selector: "kf-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, onDark: { classPropertyName: "onDark", publicName: "onDark", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2207
- <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
2208
- <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
2209
- @if (label()) {
2210
- <span class="kf-spinner__label">{{ label() }}</span>
2211
- } @else {
2212
- <span class="kf-spinner__sr">Cargando</span>
2213
- }
2214
- </span>
2215
- `, isInline: true, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] });
2216
- }
2217
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, decorators: [{
2218
- type: Component,
2219
- args: [{ selector: 'kf-spinner', template: `
2220
- <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
2221
- <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
2222
- @if (label()) {
2223
- <span class="kf-spinner__label">{{ label() }}</span>
2224
- } @else {
2225
- <span class="kf-spinner__sr">Cargando</span>
2226
- }
2227
- </span>
2228
- `, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] }]
2229
- }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], onDark: [{ type: i0.Input, args: [{ isSignal: true, alias: "onDark", required: false }] }] } });
2230
-
2231
2337
  /**
2232
2338
  * Wrapper de los cuatro estados que toda sección tiene: cargando, con datos,
2233
2339
  * vacía y con error. Un componente en vez de tres `@if` sueltos, porque es
@@ -2788,6 +2894,229 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2788
2894
  `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-data-table{background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-sm);overflow:hidden}.kf-data-table--flush{border:none;box-shadow:none;border-radius:0}.kf-data-table__toolbar{display:flex;justify-content:flex-end;padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border)}.kf-data-table__view-toggle{display:inline-flex;gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm)}.kf-data-table__view-option{font:inherit;font-size:.875em;padding:var(--kf-space-xs) var(--kf-space-sm);border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-xs);cursor:pointer;transition:background .14s,color .14s}.kf-data-table__view-option:hover:not(.kf-data-table__view-option--active){color:var(--kf-color-text)}.kf-data-table__view-option--active{background:var(--kf-color-white);color:var(--kf-color-ink);box-shadow:var(--kf-shadow-xs)}.kf-data-table__view-option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-data-table__scroll{overflow-x:auto}.kf-data-table__table{width:100%;border-collapse:collapse;text-align:left}.kf-data-table__table th,.kf-data-table__table td{padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border);white-space:nowrap}.kf-data-table__table th{font-family:var(--kf-font-title);font-size:.8125em;text-transform:uppercase;letter-spacing:.02em;color:var(--kf-color-text-muted);background:var(--kf-color-surface)}.kf-data-table__table td{color:var(--kf-color-text)}.kf-data-table__table tbody tr:last-child td{border-bottom:none}.kf-data-table__row{cursor:pointer;transition:background .14s}.kf-data-table__row:hover{background:var(--kf-color-primary-soft)}.kf-data-table__cell--right{text-align:right}.kf-data-table__empty{padding:var(--kf-space-md)}.kf-data-table__footer{padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border)}.kf-data-table__footer:empty{display:none}.kf-data-table__cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--kf-space-md);padding:var(--kf-space-md)}.kf-data-table__card{display:flex;flex-direction:column;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);overflow:hidden;transition:border-color .14s,box-shadow .14s}.kf-data-table__card--interactive{cursor:pointer}.kf-data-table__card:hover{border-color:var(--kf-color-border-strong);box-shadow:var(--kf-shadow-xs)}.kf-data-table__card--interactive:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-data-table__card-header{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-md)}.kf-data-table__card-media{flex:none}.kf-data-table__card-media img,.kf-data-table__thumb{display:block;width:48px;height:48px;border-radius:var(--kf-radius-xs);object-fit:cover;background:var(--kf-color-surface)}.kf-data-table__card-heading{display:flex;flex-direction:column;gap:var(--kf-space-xs);min-width:0}.kf-data-table__card-title{font-family:var(--kf-font-title);font-weight:600;color:var(--kf-color-ink);overflow-wrap:anywhere}.kf-data-table__card-statuses,.kf-data-table__card-actions{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-data-table__card-fields{display:flex;flex-direction:column;gap:var(--kf-space-xs);padding:var(--kf-space-md)}.kf-data-table__card-header+.kf-data-table__card-fields{padding-top:0}.kf-data-table__card-field{display:flex;justify-content:space-between;gap:var(--kf-space-sm)}.kf-data-table__card-actions{justify-content:flex-end;margin-top:auto;padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border);background:var(--kf-color-surface)}.kf-data-table__thumb{width:32px;height:32px}.kf-data-table__card-label{font-family:var(--kf-font-title);font-size:.8125em;text-transform:uppercase;letter-spacing:.02em;color:var(--kf-color-text-muted)}.kf-data-table__card-value{color:var(--kf-color-text);text-align:right}\n"] }]
2789
2895
  }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], flush: [{ type: i0.Input, args: [{ isSignal: true, alias: "flush", required: false }] }], cardable: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardable", required: false }] }], defaultView: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultView", required: false }] }], render: [{ type: i0.Input, args: [{ isSignal: true, alias: "render", required: false }] }], cellTemplates: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellTemplates", required: false }] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }] } });
2790
2896
 
2897
+ /**
2898
+ * Ítem de acción dentro de un `kf-dropdown-panel`. Es un `<button>` nativo
2899
+ * por dentro (no un `<a>`): las acciones de un menú de este tipo disparan
2900
+ * comportamiento en la pantalla actual (cerrar sesión, ejecutar un
2901
+ * comando), no navegan a otra URL.
2902
+ *
2903
+ * <kf-dropdown-item (click)="onLogout()" tone="error">Cerrar sesión</kf-dropdown-item>
2904
+ */
2905
+ class DropdownItem {
2906
+ tone = input('neutral', /* @ts-ignore */
2907
+ ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
2908
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
2909
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: DropdownItem, isStandalone: true, selector: "kf-dropdown-item", inputs: { tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2910
+ <button
2911
+ type="button"
2912
+ class="kf-dropdown-item"
2913
+ [class.kf-dropdown-item--error]="tone() === 'error'"
2914
+ role="menuitem"
2915
+ >
2916
+ <ng-content />
2917
+ </button>
2918
+ `, isInline: true, styles: [":host{display:block}.kf-dropdown-item{display:block;width:100%;padding:var(--kf-space-sm) var(--kf-space-md);border:none;background:transparent;font:inherit;text-align:left;color:var(--kf-color-text);cursor:pointer}.kf-dropdown-item:hover{background:var(--kf-color-surface)}.kf-dropdown-item:focus-visible{outline:none;box-shadow:inset var(--kf-shadow-focus)}.kf-dropdown-item--error{color:var(--kf-color-error-strong)}.kf-dropdown-item--error:hover{background:var(--kf-color-error-soft)}\n"] });
2919
+ }
2920
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownItem, decorators: [{
2921
+ type: Component,
2922
+ args: [{ selector: 'kf-dropdown-item', template: `
2923
+ <button
2924
+ type="button"
2925
+ class="kf-dropdown-item"
2926
+ [class.kf-dropdown-item--error]="tone() === 'error'"
2927
+ role="menuitem"
2928
+ >
2929
+ <ng-content />
2930
+ </button>
2931
+ `, styles: [":host{display:block}.kf-dropdown-item{display:block;width:100%;padding:var(--kf-space-sm) var(--kf-space-md);border:none;background:transparent;font:inherit;text-align:left;color:var(--kf-color-text);cursor:pointer}.kf-dropdown-item:hover{background:var(--kf-color-surface)}.kf-dropdown-item:focus-visible{outline:none;box-shadow:inset var(--kf-shadow-focus)}.kf-dropdown-item--error{color:var(--kf-color-error-strong)}.kf-dropdown-item--error:hover{background:var(--kf-color-error-soft)}\n"] }]
2932
+ }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
2933
+ /**
2934
+ * Contenido del panel flotante de un `kf-dropdown`. Es solo una plantilla
2935
+ * (`<ng-template>` por dentro, capturada vía `TemplateRef`) — `Dropdown` la
2936
+ * conecta al overlay real cuando `open` pasa a `true`, no la renderiza en su
2937
+ * propio lugar del DOM. Esto es lo que permite que el panel aparezca pegado
2938
+ * al host de `Dropdown` sin importar dónde esté ese host en la página.
2939
+ *
2940
+ * <kf-dropdown [(open)]="menuOpen">
2941
+ * <kf-dropdown-panel>
2942
+ * <kf-dropdown-item (click)="onLogout()">Cerrar sesión</kf-dropdown-item>
2943
+ * </kf-dropdown-panel>
2944
+ * </kf-dropdown>
2945
+ */
2946
+ class DropdownPanel {
2947
+ templateRef = viewChild.required(TemplateRef, /* @ts-ignore */
2948
+ ...(ngDevMode ? [{ debugName: "templateRef" }] : /* istanbul ignore next */ []));
2949
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
2950
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: DropdownPanel, isStandalone: true, selector: "kf-dropdown-panel", viewQueries: [{ propertyName: "templateRef", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: `
2951
+ <ng-template>
2952
+ <div class="kf-dropdown-panel">
2953
+ <div class="kf-dropdown-panel__inner" role="menu">
2954
+ <ng-content />
2955
+ </div>
2956
+ </div>
2957
+ </ng-template>
2958
+ `, isInline: true, styles: [".kf-dropdown-panel{min-width:220px;max-width:100%;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-lg);font-family:var(--kf-font-body);animation:kf-dropdown-panel-enter .14s ease-out}.kf-dropdown-panel__inner{padding:var(--kf-space-xs) 0;overflow:hidden;border-radius:inherit}@media(prefers-reduced-motion:reduce){.kf-dropdown-panel{animation:none}}@keyframes kf-dropdown-panel-enter{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}\n"] });
2959
+ }
2960
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownPanel, decorators: [{
2961
+ type: Component,
2962
+ args: [{ selector: 'kf-dropdown-panel', template: `
2963
+ <ng-template>
2964
+ <div class="kf-dropdown-panel">
2965
+ <div class="kf-dropdown-panel__inner" role="menu">
2966
+ <ng-content />
2967
+ </div>
2968
+ </div>
2969
+ </ng-template>
2970
+ `, styles: [".kf-dropdown-panel{min-width:220px;max-width:100%;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-lg);font-family:var(--kf-font-body);animation:kf-dropdown-panel-enter .14s ease-out}.kf-dropdown-panel__inner{padding:var(--kf-space-xs) 0;overflow:hidden;border-radius:inherit}@media(prefers-reduced-motion:reduce){.kf-dropdown-panel{animation:none}}@keyframes kf-dropdown-panel-enter{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}\n"] }]
2971
+ }], propDecorators: { templateRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
2972
+ /**
2973
+ * Menú de acciones flotante, conectado a un elemento disparador vía
2974
+ * `@angular/cdk/overlay` — la misma pieza que usa Angular Material para
2975
+ * `mat-menu`/`mat-select`. Resuelve el patrón que antes se reimplementaba a
2976
+ * mano en cada consumidor (ver el session-menu de `web-portal-latam-frontend`,
2977
+ * ~80 líneas de HTML/TS/SCSS para toggle + click-afuera + Escape + animación
2978
+ * de entrada, acopladas a una clase interna no pública de `PortalHeader`).
2979
+ *
2980
+ * Se apoya en el CDK en vez de un `position: absolute` casero (como sí hace
2981
+ * `SelectMenu`, que puede permitírselo porque su trigger vive siempre dentro
2982
+ * del propio componente) porque acá el disparador real puede vivir en OTRO
2983
+ * componente por completo — ej. el avatar dentro de `kf-portal-header` — y
2984
+ * el panel tiene que aparecer pegado a ESE elemento sin importar dónde el
2985
+ * consumidor ubique al `Dropdown` en su HTML. El Overlay CDK calcula esa
2986
+ * posición (con fallback si no entra en pantalla) en vez de que cada
2987
+ * consumidor lo recalcule a mano.
2988
+ *
2989
+ * `open` es two-way, igual que `Alert`/`Modal`: el consumidor decide qué
2990
+ * dispara la apertura (puede ser un `(click)` en un botón propio, o un
2991
+ * evento de otro componente como `PortalHeader.sessionClick`) — `Dropdown`
2992
+ * en sí no renderiza ningún trigger, su propio host es el punto de anclaje
2993
+ * visual del panel.
2994
+ *
2995
+ * <kf-dropdown [(open)]="menuOpen">
2996
+ * <kf-dropdown-panel>
2997
+ * <div class="mi-header">Sesión iniciada como {{ email() }}</div>
2998
+ * <kf-dropdown-item (click)="onLogout()" tone="error">Cerrar sesión</kf-dropdown-item>
2999
+ * </kf-dropdown-panel>
3000
+ * </kf-dropdown>
3001
+ */
3002
+ class Dropdown {
3003
+ /** Visibilidad del panel, two-way: `[(open)]="menuOpen"`. */
3004
+ open = model(false, /* @ts-ignore */
3005
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
3006
+ /**
3007
+ * Elemento al que se conecta el overlay, si es distinto del propio host
3008
+ * de `Dropdown`. Sin esto, `Dropdown` asume que ÉL MISMO es visualmente
3009
+ * el disparador (caso normal: el consumidor arma su propio botón como
3010
+ * contenido, y `Dropdown` envuelve ese contenido). Cuando el disparador
3011
+ * real vive dentro de OTRO componente que no se puede envolver sin
3012
+ * ensanchar el área de click a algo más grande que el botón (ej. el
3013
+ * avatar de sesión dentro de `PortalHeader`, que convive con logo y
3014
+ * navegación en el mismo host), `anchor` permite apuntar el overlay a la
3015
+ * referencia real de ese componente en vez de al host vacío de
3016
+ * `Dropdown`.
3017
+ *
3018
+ * <kf-dropdown [(open)]="menuOpen" [anchor]="headerRef()">
3019
+ */
3020
+ anchor = input(/* @ts-ignore */
3021
+ ...(ngDevMode ? [undefined, { debugName: "anchor" }] : /* istanbul ignore next */ []));
3022
+ panel = contentChild(DropdownPanel, /* @ts-ignore */
3023
+ ...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
3024
+ overlayService = inject(Overlay);
3025
+ host = inject((ElementRef));
3026
+ viewContainerRef = inject(ViewContainerRef);
3027
+ destroyRef = inject(DestroyRef);
3028
+ overlayRef = null;
3029
+ constructor() {
3030
+ effect(() => {
3031
+ if (this.open()) {
3032
+ this.attach();
3033
+ }
3034
+ else {
3035
+ this.detach();
3036
+ }
3037
+ });
3038
+ this.destroyRef.onDestroy(() => this.overlayRef?.dispose());
3039
+ }
3040
+ /**
3041
+ * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo
3042
+ * ve teclas presionadas con el foco DENTRO del pane del overlay. Si el
3043
+ * usuario abre el menú y presiona Escape con el foco en otro lado de la
3044
+ * página (caso común — el foco puede seguir en el botón disparador, que
3045
+ * vive fuera del overlay), `keydownEvents()` nunca dispara. Este listener
3046
+ * a nivel de documento cubre ese caso; el de `keydownEvents()` sigue
3047
+ * activo para cuando el foco sí está dentro del panel.
3048
+ */
3049
+ onDocumentEscape() {
3050
+ this.open.set(false);
3051
+ }
3052
+ attach() {
3053
+ const panel = this.panel();
3054
+ if (!panel || this.overlayRef?.hasAttached()) {
3055
+ return;
3056
+ }
3057
+ const overlayRef = this.getOrCreateOverlayRef();
3058
+ const portal = new TemplatePortal(panel.templateRef(), this.viewContainerRef);
3059
+ overlayRef.attach(portal);
3060
+ overlayRef.outsidePointerEvents().subscribe(() => this.open.set(false));
3061
+ overlayRef.keydownEvents().subscribe((event) => {
3062
+ if (event.key === 'Escape') {
3063
+ this.open.set(false);
3064
+ }
3065
+ });
3066
+ }
3067
+ detach() {
3068
+ this.overlayRef?.detach();
3069
+ }
3070
+ getOrCreateOverlayRef() {
3071
+ if (this.overlayRef) {
3072
+ return this.overlayRef;
3073
+ }
3074
+ const positionStrategy = this.overlayService
3075
+ .position()
3076
+ .flexibleConnectedTo(this.anchor() ?? this.host)
3077
+ // 4 posiciones, no 2: además de abajo/arriba (alineado al borde
3078
+ // derecho del anchor, el caso común), agrega el mismo par alineado al
3079
+ // borde IZQUIERDO. Con solo el par "end" — como estaba antes — un
3080
+ // trigger cerca del borde derecho de la pantalla no tenía ningún
3081
+ // fallback horizontal: el panel se salía del viewport en vez de
3082
+ // reacomodarse hacia la izquierda. El CDK prueba cada posición en
3083
+ // orden y usa la primera que entra completa en el viewport.
3084
+ .withPositions([
3085
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
3086
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
3087
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
3088
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
3089
+ ])
3090
+ // Margen de seguridad contra los bordes del viewport — sin esto, el
3091
+ // CDK puede considerar una posición "válida" aunque quede pegada al
3092
+ // borde con 0px de aire.
3093
+ .withViewportMargin(8)
3094
+ // Si ninguna de las 4 posiciones entra completa (viewport muy
3095
+ // angosto), la última se ajusta con push en vez de desbordarse fuera
3096
+ // de pantalla sin control.
3097
+ .withPush(true);
3098
+ this.overlayRef = this.overlayService.create({
3099
+ positionStrategy,
3100
+ scrollStrategy: this.overlayService.scrollStrategies.reposition(),
3101
+ // Sin esto, el panel puede quedar más angosto que su propio
3102
+ // `min-width` en pantallas chicas si el CDK decide recortar — fija un
3103
+ // techo razonable en vez de dejarlo crecer sin límite ni encogerse
3104
+ // por debajo de lo legible.
3105
+ maxWidth: '90vw',
3106
+ });
3107
+ return this.overlayRef;
3108
+ }
3109
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dropdown, deps: [], target: i0.ɵɵFactoryTarget.Component });
3110
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: Dropdown, isStandalone: true, selector: "kf-dropdown", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { listeners: { "document:keydown.escape": "onDocumentEscape()" } }, queries: [{ propertyName: "panel", first: true, predicate: DropdownPanel, descendants: true, isSignal: true }], ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:inline-block}\n"] });
3111
+ }
3112
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dropdown, decorators: [{
3113
+ type: Component,
3114
+ args: [{ selector: 'kf-dropdown', template: `<ng-content />`, styles: [":host{display:inline-block}\n"] }]
3115
+ }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchor", required: false }] }], panel: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownPanel), { isSignal: true }] }], onDocumentEscape: [{
3116
+ type: HostListener,
3117
+ args: ['document:keydown.escape']
3118
+ }] } });
3119
+
2791
3120
  /**
2792
3121
  * Bloque promocional sobre fondo oscuro de marca (página 37) — campañas,
2793
3122
  * accesos directos o el portal de autoatención. Usa `--kf-color-tertiary-deep`
@@ -3016,6 +3345,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3016
3345
  `, styles: [":host{display:block}.kf-filter-chips{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-filter-chips__chip{font:inherit;padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-pill);border:1px solid var(--kf-color-border);background:var(--kf-color-surface);color:var(--kf-color-text);cursor:pointer;transition:background .14s,border-color .14s,color .14s}.kf-filter-chips__chip:hover:not(.kf-filter-chips__chip--active){border-color:var(--kf-color-border-strong)}.kf-filter-chips__chip:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-filter-chips__chip--active{background:var(--kf-color-ink);border-color:var(--kf-color-ink);color:var(--kf-color-white)}\n"] }]
3017
3346
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
3018
3347
 
3348
+ /**
3349
+ * Marca el contenido que `kf-folder-tabs` debe proyectar dentro de su
3350
+ * panel. Sin lógica propia — existe solo como token de tipo para que
3351
+ * `contentChild(FolderTabsPanel)` pueda detectar si el consumidor proyectó
3352
+ * algo (mismo mecanismo que `kf-dropdown` usa con `contentChild(DropdownPanel)`;
3353
+ * acá no hace falta un componente completo, alcanza con una directiva de
3354
+ * atributo porque el panel no necesita comportamiento propio, solo marcar
3355
+ * su presencia).
3356
+ *
3357
+ * <div kf-folder-tabs-panel>...</div>
3358
+ */
3359
+ class FolderTabsPanel {
3360
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FolderTabsPanel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3361
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.0", type: FolderTabsPanel, isStandalone: true, selector: "[kf-folder-tabs-panel]", ngImport: i0 });
3362
+ }
3363
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FolderTabsPanel, decorators: [{
3364
+ type: Directive,
3365
+ args: [{ selector: '[kf-folder-tabs-panel]' }]
3366
+ }] });
3019
3367
  /**
3020
3368
  * Tabs con forma de "carpeta": el borde superior queda redondeado y el borde
3021
3369
  * inferior de la pestaña activa se funde visualmente con el panel de
@@ -3026,9 +3374,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3026
3374
  * `--active` se mueve de un botón a otro, y eso vuelve a disparar el keyframe
3027
3375
  * sin tocar JS.
3028
3376
  *
3029
- * El consumidor es responsable de renderizar el panel de contenido debajo;
3030
- * este componente solo resuelve la fila de pestañas y emite `valueChange`.
3377
+ * El panel de contenido es un `<ng-content>` OPCIONAL — si el consumidor no
3378
+ * proyecta nada, `kf-folder-tabs` renderiza solo la fila de pestañas (mismo
3379
+ * comportamiento que antes de que existiera este slot, detectado con
3380
+ * `contentChild()` sin `.required`, igual patrón que usa `kf-dropdown` para
3381
+ * saber si hay un `kf-dropdown-panel` proyectado). Si SÍ proyecta contenido,
3382
+ * se envuelve en un panel con la esquina superior-izquierda recta (nace ahí
3383
+ * la primera pestaña, siempre pegada al borde izquierdo de la fila — un
3384
+ * radio ahí competiría visualmente con el propio borde redondeado de esa
3385
+ * pestaña) y las otras tres redondeadas. El `border-bottom` de 2px de la
3386
+ * pestaña activa (ver `.kf-folder-tabs__tab--active` más abajo) es la señal
3387
+ * visual de conexión con el panel para CUALQUIER pestaña activa, sin
3388
+ * necesitar mover ni medir nada.
3389
+ *
3390
+ * El borde izquierdo de toda la fila (y por lo tanto de la esquina recta
3391
+ * del panel) depende de que ninguna pestaña se corra al cambiar de estado
3392
+ * — ver `transform-origin: bottom left` en `.kf-folder-tabs__tab`: con el
3393
+ * origen centrado (como estaba antes), el `scale()` de la pestaña inactiva
3394
+ * encogía el borde izquierdo unos px, y ese corrimiento sub-pixel es lo que
3395
+ * hacía que este mismo diseño (esquina fija) se viera "roto" cuando la
3396
+ * pestaña activa no era la primera. Con el origen anclado a la izquierda,
3397
+ * el corrimiento desaparece y la esquina fija vuelve a ser una solución
3398
+ * válida y simple — sin necesitar medir la pestaña activa por JS.
3399
+ *
3400
+ * Antes de este slot, el patrón era que el consumidor pusiera su propia
3401
+ * card como HERMANO de `kf-folder-tabs` y anulara a mano el radio superior
3402
+ * completo (`border-top-left-radius: 0`, `border-top-right-radius: 0` — ver
3403
+ * el caso original de `web-portal-latam-frontend`). Encapsular esto acá
3404
+ * evita que cada consumidor repita esas reglas a mano.
3031
3405
  *
3406
+ * <kf-folder-tabs [options]="tabs" [value]="active" (valueChange)="active = $event">
3407
+ * <div kf-folder-tabs-panel>contenido de la pestaña activa</div>
3408
+ * </kf-folder-tabs>
3409
+ *
3410
+ * <!-- o, sin contenido, como antes: -->
3032
3411
  * <kf-folder-tabs [options]="tabs" [value]="active" (valueChange)="active = $event" />
3033
3412
  */
3034
3413
  class FolderTabs {
@@ -3037,6 +3416,18 @@ class FolderTabs {
3037
3416
  value = input.required(/* @ts-ignore */
3038
3417
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
3039
3418
  valueChange = output();
3419
+ /**
3420
+ * Detecta si el consumidor proyectó contenido marcado con
3421
+ * `kf-folder-tabs-panel` — sin `.required`, así que es `undefined`
3422
+ * cuando no hay nada proyectado (mismo patrón que usa `kf-dropdown` con
3423
+ * `contentChild(DropdownPanel)` para saber si hay un panel que
3424
+ * attachear). `hasPanel()` decide si el wrapper del panel participa del
3425
+ * layout (`display: contents` si no).
3426
+ */
3427
+ projectedPanel = contentChild(FolderTabsPanel, /* @ts-ignore */
3428
+ ...(ngDevMode ? [{ debugName: "projectedPanel" }] : /* istanbul ignore next */ []));
3429
+ hasPanel = computed(() => this.projectedPanel() !== undefined, /* @ts-ignore */
3430
+ ...(ngDevMode ? [{ debugName: "hasPanel" }] : /* istanbul ignore next */ []));
3040
3431
  select(value) {
3041
3432
  if (value === this.value()) {
3042
3433
  return;
@@ -3073,7 +3464,7 @@ class FolderTabs {
3073
3464
  nextButton?.focus();
3074
3465
  }
3075
3466
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FolderTabs, deps: [], target: i0.ɵɵFactoryTarget.Component });
3076
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: FolderTabs, isStandalone: true, selector: "kf-folder-tabs", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: `
3467
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: FolderTabs, isStandalone: true, selector: "kf-folder-tabs", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { valueChange: "valueChange" }, queries: [{ propertyName: "projectedPanel", first: true, predicate: FolderTabsPanel, descendants: true, isSignal: true }], ngImport: i0, template: `
3077
3468
  <div class="kf-folder-tabs" role="tablist">
3078
3469
  @for (option of options(); track option.value) {
3079
3470
  <button
@@ -3095,7 +3486,11 @@ class FolderTabs {
3095
3486
  </button>
3096
3487
  }
3097
3488
  </div>
3098
- `, isInline: true, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-folder-tabs{display:flex;align-items:flex-end;gap:var(--kf-space-xs);perspective:900px}.kf-folder-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;font-size:.9rem;color:var(--kf-color-text-muted);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-bottom-color:var(--kf-color-border-strong);border-radius:var(--kf-radius-sm) var(--kf-radius-sm) 0 0;padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;transform-origin:bottom center;transform:translateY(5px) scale(.985);transition:background .14s,color .14s,border-color .14s,transform .22s cubic-bezier(.32,.72,0,1)}.kf-folder-tabs__tab:hover:not(.kf-folder-tabs__tab--active){color:var(--kf-color-text);background:var(--kf-color-white);transform:translateY(2px) scale(.99)}.kf-folder-tabs__tab:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);z-index:1}.kf-folder-tabs__tab--active{z-index:1;color:var(--kf-color-primary);font-weight:700;background:var(--kf-color-white);border-color:var(--kf-color-border);border-bottom:2px solid var(--kf-color-primary);box-shadow:0 -6px 14px -10px #10182866;transform:translateY(1px);animation:kf-folder-tabs-pull .34s cubic-bezier(.22,1.2,.36,1) both}@keyframes kf-folder-tabs-pull{0%{transform:translateY(7px) rotateX(-24deg) scale(.95)}60%{transform:translateY(-2px) rotateX(3deg) scale(1.02)}to{transform:translateY(1px) rotateX(0) scale(1)}}@media(prefers-reduced-motion:reduce){.kf-folder-tabs__tab,.kf-folder-tabs__tab--active{animation:none;transition:background .14s,color .14s,border-color .14s}}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
3489
+
3490
+ <div class="kf-folder-tabs__panel" [class.kf-folder-tabs__panel--empty]="!hasPanel()">
3491
+ <ng-content select="[kf-folder-tabs-panel]" />
3492
+ </div>
3493
+ `, isInline: true, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-folder-tabs{display:flex;align-items:flex-end;gap:var(--kf-space-xs);perspective:900px}.kf-folder-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;font-size:.9rem;color:var(--kf-color-text-muted);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-bottom-color:var(--kf-color-border-strong);border-radius:var(--kf-radius-sm) var(--kf-radius-sm) 0 0;padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;transform-origin:bottom left;transform:translateY(5px) scale(.985);transition:background .14s,color .14s,border-color .14s,transform .22s cubic-bezier(.32,.72,0,1)}.kf-folder-tabs__tab:hover:not(.kf-folder-tabs__tab--active){color:var(--kf-color-text);background:var(--kf-color-white);transform:translateY(2px) scale(.99)}.kf-folder-tabs__tab:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);z-index:1}.kf-folder-tabs__tab--active{z-index:1;color:var(--kf-color-primary);font-weight:700;background:var(--kf-color-white);border-color:var(--kf-color-border);border-bottom:2px solid var(--kf-color-primary);box-shadow:0 -6px 14px -10px #10182866;transform:translateY(1px);animation:kf-folder-tabs-pull .34s cubic-bezier(.22,1.2,.36,1) both}@keyframes kf-folder-tabs-pull{0%{transform:translateY(7px) rotateX(-10deg) scale(.97)}60%{transform:translateY(1px) rotateX(2deg) scale(1.01)}to{transform:translateY(1px) rotateX(0) scale(1)}}@media(prefers-reduced-motion:reduce){.kf-folder-tabs__tab,.kf-folder-tabs__tab--active{animation:none;transition:background .14s,color .14s,border-color .14s}}.kf-folder-tabs__panel--empty{display:contents}.kf-folder-tabs__panel:not(.kf-folder-tabs__panel--empty){background:var(--kf-color-white);border-right:1px solid var(--kf-color-border);border-bottom:1px solid var(--kf-color-border);border-left:1px solid var(--kf-color-border);border-top:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);border-top-left-radius:0;overflow:hidden}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
3099
3494
  }
3100
3495
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FolderTabs, decorators: [{
3101
3496
  type: Component,
@@ -3121,8 +3516,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3121
3516
  </button>
3122
3517
  }
3123
3518
  </div>
3124
- `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-folder-tabs{display:flex;align-items:flex-end;gap:var(--kf-space-xs);perspective:900px}.kf-folder-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;font-size:.9rem;color:var(--kf-color-text-muted);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-bottom-color:var(--kf-color-border-strong);border-radius:var(--kf-radius-sm) var(--kf-radius-sm) 0 0;padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;transform-origin:bottom center;transform:translateY(5px) scale(.985);transition:background .14s,color .14s,border-color .14s,transform .22s cubic-bezier(.32,.72,0,1)}.kf-folder-tabs__tab:hover:not(.kf-folder-tabs__tab--active){color:var(--kf-color-text);background:var(--kf-color-white);transform:translateY(2px) scale(.99)}.kf-folder-tabs__tab:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);z-index:1}.kf-folder-tabs__tab--active{z-index:1;color:var(--kf-color-primary);font-weight:700;background:var(--kf-color-white);border-color:var(--kf-color-border);border-bottom:2px solid var(--kf-color-primary);box-shadow:0 -6px 14px -10px #10182866;transform:translateY(1px);animation:kf-folder-tabs-pull .34s cubic-bezier(.22,1.2,.36,1) both}@keyframes kf-folder-tabs-pull{0%{transform:translateY(7px) rotateX(-24deg) scale(.95)}60%{transform:translateY(-2px) rotateX(3deg) scale(1.02)}to{transform:translateY(1px) rotateX(0) scale(1)}}@media(prefers-reduced-motion:reduce){.kf-folder-tabs__tab,.kf-folder-tabs__tab--active{animation:none;transition:background .14s,color .14s,border-color .14s}}\n"] }]
3125
- }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
3519
+
3520
+ <div class="kf-folder-tabs__panel" [class.kf-folder-tabs__panel--empty]="!hasPanel()">
3521
+ <ng-content select="[kf-folder-tabs-panel]" />
3522
+ </div>
3523
+ `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-folder-tabs{display:flex;align-items:flex-end;gap:var(--kf-space-xs);perspective:900px}.kf-folder-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;font-size:.9rem;color:var(--kf-color-text-muted);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-bottom-color:var(--kf-color-border-strong);border-radius:var(--kf-radius-sm) var(--kf-radius-sm) 0 0;padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;transform-origin:bottom left;transform:translateY(5px) scale(.985);transition:background .14s,color .14s,border-color .14s,transform .22s cubic-bezier(.32,.72,0,1)}.kf-folder-tabs__tab:hover:not(.kf-folder-tabs__tab--active){color:var(--kf-color-text);background:var(--kf-color-white);transform:translateY(2px) scale(.99)}.kf-folder-tabs__tab:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);z-index:1}.kf-folder-tabs__tab--active{z-index:1;color:var(--kf-color-primary);font-weight:700;background:var(--kf-color-white);border-color:var(--kf-color-border);border-bottom:2px solid var(--kf-color-primary);box-shadow:0 -6px 14px -10px #10182866;transform:translateY(1px);animation:kf-folder-tabs-pull .34s cubic-bezier(.22,1.2,.36,1) both}@keyframes kf-folder-tabs-pull{0%{transform:translateY(7px) rotateX(-10deg) scale(.97)}60%{transform:translateY(1px) rotateX(2deg) scale(1.01)}to{transform:translateY(1px) rotateX(0) scale(1)}}@media(prefers-reduced-motion:reduce){.kf-folder-tabs__tab,.kf-folder-tabs__tab--active{animation:none;transition:background .14s,color .14s,border-color .14s}}.kf-folder-tabs__panel--empty{display:contents}.kf-folder-tabs__panel:not(.kf-folder-tabs__panel--empty){background:var(--kf-color-white);border-right:1px solid var(--kf-color-border);border-bottom:1px solid var(--kf-color-border);border-left:1px solid var(--kf-color-border);border-top:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);border-top-left-radius:0;overflow:hidden}\n"] }]
3524
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], projectedPanel: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FolderTabsPanel), { isSignal: true }] }] } });
3126
3525
 
3127
3526
  /**
3128
3527
  * Input de formulario apilado: label arriba (en mayúsculas vía CSS), campo con
@@ -3369,6 +3768,58 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3369
3768
  `, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);font-family:var(--kf-font-body)}.kf-input__field{display:flex;align-items:center;gap:var(--kf-space-sm);padding:0 var(--kf-space-sm);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);transition:background .14s,border-color .14s,box-shadow .14s}.kf-input__field--focused{background:var(--kf-color-white);border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-input__field--invalid{border-color:var(--kf-color-error)}.kf-input__field--invalid.kf-input__field--focused{box-shadow:0 0 0 3px #eb575738}.kf-input__field--disabled{opacity:.5}.kf-input__icon{color:var(--kf-color-text-muted)}.kf-input__field--invalid .kf-input__icon{color:var(--kf-color-error)}.kf-input__control{flex:1;min-width:0;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) 0}.kf-input__control::placeholder{color:var(--kf-color-text-muted)}.kf-input__control:disabled{cursor:not-allowed}.kf-input__suffix{flex:none;color:var(--kf-color-text-muted);font-size:.875rem}.kf-input__toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:var(--kf-space-xs);margin:0 calc(var(--kf-space-xs) * -1);color:var(--kf-color-text-muted);cursor:pointer;border-radius:var(--kf-radius-xs);transition:color .14s}.kf-input__toggle:hover{color:var(--kf-color-text)}.kf-input__toggle:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-input__hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-input__error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}\n"] }]
3370
3769
  }], propDecorators: { nativeInput: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], toggleable: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleable", required: false }] }] } });
3371
3770
 
3771
+ /**
3772
+ * Acción de bajo énfasis con apariencia de texto — "¿Olvidaste tu
3773
+ * contraseña?", "Volver a iniciar sesión". Antes de este componente,
3774
+ * `web-portal-latam-frontend` reimplementaba esto dos veces (`.login__forgot`
3775
+ * como `<a href="#">` con `preventDefault()`, `.login__back` como
3776
+ * `<button>`), cada una con su propio `:focus-visible` copiado literal.
3777
+ *
3778
+ * Es siempre un `<button type="button">` por dentro, nunca un `<a>`: ninguno
3779
+ * de los dos casos reales navega a otra URL, ambos disparan una acción en la
3780
+ * misma pantalla (cambiar de vista, someter un flujo). Si en el futuro un
3781
+ * consumidor necesita navegación real con href, esa es una discusión de API
3782
+ * distinta (`kf-link` con `[routerLink]`/`href`), no se fuerza acá sin caso
3783
+ * de uso confirmado.
3784
+ *
3785
+ * `variant="default"` (color primario, subrayado en hover) es para la
3786
+ * acción de mayor jerarquía relativa de la pantalla (ej. "¿Olvidaste tu
3787
+ * contraseña?"); `variant="muted"` (gris que pasa a primario en hover) es
3788
+ * para acciones de retroceso/salida (ej. "← Volver a iniciar sesión").
3789
+ *
3790
+ * <kf-link (click)="onForgotPassword()">¿Olvidaste tu contraseña?</kf-link>
3791
+ * <kf-link variant="muted" (click)="onBack()">← Volver a iniciar sesión</kf-link>
3792
+ */
3793
+ class Link {
3794
+ variant = input('default', /* @ts-ignore */
3795
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3796
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3797
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Link, deps: [], target: i0.ɵɵFactoryTarget.Component });
3798
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: Link, isStandalone: true, selector: "kf-link", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
3799
+ <button
3800
+ type="button"
3801
+ class="kf-link"
3802
+ [class.kf-link--muted]="variant() === 'muted'"
3803
+ [disabled]="disabled()"
3804
+ >
3805
+ <ng-content />
3806
+ </button>
3807
+ `, isInline: true, styles: [":host{display:inline-block}.kf-link{padding:0;border:none;background:transparent;font:inherit;font-weight:500;color:var(--kf-color-primary);text-decoration:none;cursor:pointer}.kf-link:hover:not(:disabled){text-decoration:underline}.kf-link:focus-visible{outline:2px solid var(--kf-color-primary);outline-offset:2px;border-radius:var(--kf-radius-xs)}.kf-link:disabled{opacity:.5;cursor:not-allowed}.kf-link--muted{color:var(--kf-color-text-muted)}.kf-link--muted:hover:not(:disabled){color:var(--kf-color-primary);text-decoration:none}\n"] });
3808
+ }
3809
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Link, decorators: [{
3810
+ type: Component,
3811
+ args: [{ selector: 'kf-link', template: `
3812
+ <button
3813
+ type="button"
3814
+ class="kf-link"
3815
+ [class.kf-link--muted]="variant() === 'muted'"
3816
+ [disabled]="disabled()"
3817
+ >
3818
+ <ng-content />
3819
+ </button>
3820
+ `, styles: [":host{display:inline-block}.kf-link{padding:0;border:none;background:transparent;font:inherit;font-weight:500;color:var(--kf-color-primary);text-decoration:none;cursor:pointer}.kf-link:hover:not(:disabled){text-decoration:underline}.kf-link:focus-visible{outline:2px solid var(--kf-color-primary);outline-offset:2px;border-radius:var(--kf-radius-xs)}.kf-link:disabled{opacity:.5;cursor:not-allowed}.kf-link--muted{color:var(--kf-color-text-muted)}.kf-link--muted:hover:not(:disabled){color:var(--kf-color-primary);text-decoration:none}\n"] }]
3821
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
3822
+
3372
3823
  /**
3373
3824
  * Card de estadística/KPI: label, valor grande, ícono opcional en la esquina
3374
3825
  * superior derecha, badge de esquina opcional (pegado al valor) y una nota
@@ -3620,7 +4071,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3620
4071
  args: ['click', ['$event']]
3621
4072
  }] } });
3622
4073
 
3623
- let nextId$1 = 0;
4074
+ let nextId = 0;
3624
4075
  /**
3625
4076
  * Dropdown de selección múltiple con checkboxes internos, siguiendo el mismo
3626
4077
  * patrón de panel flotante que `SelectMenu` (panel con `position: absolute`
@@ -3657,7 +4108,7 @@ class MultiSelect {
3657
4108
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
3658
4109
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3659
4110
  valuesChange = output();
3660
- instanceId = `kf-multi-select-${nextId$1++}`;
4111
+ instanceId = `kf-multi-select-${nextId++}`;
3661
4112
  triggerId = `${this.instanceId}-trigger`;
3662
4113
  listboxId = `${this.instanceId}-listbox`;
3663
4114
  isOpen = signal(false, /* @ts-ignore */
@@ -3793,7 +4244,7 @@ class MultiSelect {
3793
4244
  }
3794
4245
  </ul>
3795
4246
  }
3796
- `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
4247
+ `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;width:240px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
3797
4248
  }
3798
4249
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MultiSelect, decorators: [{
3799
4250
  type: Component,
@@ -3875,7 +4326,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3875
4326
  }
3876
4327
  </ul>
3877
4328
  }
3878
- `, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
4329
+ `, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;width:240px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
3879
4330
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valuesChange: [{ type: i0.Output, args: ["valuesChange"] }], onDocumentClick: [{
3880
4331
  type: HostListener,
3881
4332
  args: ['document:click', ['$event']]
@@ -4171,12 +4622,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4171
4622
  * Es distinto de `kf-page-header`/`kf-page-head` (título de una vista
4172
4623
  * interna) — este es el chrome global del portal, se monta una sola vez.
4173
4624
  *
4625
+ * `:host` es `position: sticky; top: 0` — el header queda pegado arriba al
4626
+ * hacer scroll. No es `fixed`: como no se superpone al contenido en el
4627
+ * primer render, no hace falta que el consumidor compense con
4628
+ * `padding-top`.
4629
+ *
4630
+ * Bajo los 768px, el `<nav>` deja de mostrarse en línea junto al logo y
4631
+ * colapsa detrás de un botón de menú (ícono `menu`/`x`) que despliega un
4632
+ * drawer lateral (desde el borde derecho, `position: fixed` + overlay de
4633
+ * fondo) con los mismos `navItems` en columna. Ese drawer expone un slot
4634
+ * (`<ng-content select="[kf-portal-header-extra]">`) al final, pensado
4635
+ * para que el consumidor proyecte contenido propio que en escritorio vive
4636
+ * en otro lugar (ej. el menú de sesión) — `PortalHeader` no sabe qué es
4637
+ * ese contenido ni cómo se abre, solo le da un lugar dentro del drawer ya
4638
+ * desplegado. El overlay cierra el drawer al tocarlo, y `Escape` también.
4639
+ *
4174
4640
  * <kf-portal-header
4175
4641
  * [isAdmin]="true"
4176
4642
  * [adminActions]="[{ label: 'Consultar deuda clientes', href: '/deuda' }, { label: 'Cambiar DNI', href: '/dni' }]"
4177
4643
  * [navItems]="[{ label: 'Home', href: '/', active: true }, { label: 'Vehículos', href: '/vehiculos' }]"
4178
4644
  * userInitials="TS"
4179
- * />
4645
+ * >
4646
+ * <div kf-portal-header-extra>contenido propio, solo visible en el panel mobile</div>
4647
+ * </kf-portal-header>
4180
4648
  */
4181
4649
  class PortalHeader {
4182
4650
  brand = input('DIVEMOTOR', /* @ts-ignore */
@@ -4189,8 +4657,26 @@ class PortalHeader {
4189
4657
  userInitials = input.required(/* @ts-ignore */
4190
4658
  ...(ngDevMode ? [{ debugName: "userInitials" }] : /* istanbul ignore next */ []));
4191
4659
  sessionClick = output();
4660
+ /** Visibilidad del drawer de navegación mobile (bajo los 768px). */
4661
+ mobileMenuOpen = signal(false, /* @ts-ignore */
4662
+ ...(ngDevMode ? [{ debugName: "mobileMenuOpen" }] : /* istanbul ignore next */ []));
4663
+ /** Cierra el drawer con Escape — mismo patrón que cualquier drawer/modal. */
4664
+ onEscape() {
4665
+ this.mobileMenuOpen.set(false);
4666
+ }
4667
+ /**
4668
+ * `ElementRef` real del botón de sesión (el avatar en la esquina
4669
+ * derecha), no de todo el header. Pensado para anclar un `kf-dropdown`
4670
+ * externo — sin esto, el único `ElementRef` disponible desde afuera es el
4671
+ * del host de `PortalHeader`, que ocupa el ancho completo de la pantalla
4672
+ * (logo + nav + sesión), y el overlay del CDK termina "pegado" a la
4673
+ * esquina del header entero en vez de al botón que el usuario clickeó.
4674
+ *
4675
+ * <kf-dropdown [(open)]="menuOpen" [anchor]="header().sessionButtonRef()">
4676
+ */
4677
+ sessionButtonRef = viewChild.required('sessionButton', { ...(ngDevMode ? { debugName: "sessionButtonRef" } : /* istanbul ignore next */ {}), read: ElementRef });
4192
4678
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PortalHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
4193
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PortalHeader, isStandalone: true, selector: "kf-portal-header", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: false, transformFunction: null }, isAdmin: { classPropertyName: "isAdmin", publicName: "isAdmin", isSignal: true, isRequired: false, transformFunction: null }, adminActions: { classPropertyName: "adminActions", publicName: "adminActions", isSignal: true, isRequired: false, transformFunction: null }, navItems: { classPropertyName: "navItems", publicName: "navItems", isSignal: true, isRequired: false, transformFunction: null }, userInitials: { classPropertyName: "userInitials", publicName: "userInitials", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { sessionClick: "sessionClick" }, ngImport: i0, template: `
4679
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PortalHeader, isStandalone: true, selector: "kf-portal-header", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: false, transformFunction: null }, isAdmin: { classPropertyName: "isAdmin", publicName: "isAdmin", isSignal: true, isRequired: false, transformFunction: null }, adminActions: { classPropertyName: "adminActions", publicName: "adminActions", isSignal: true, isRequired: false, transformFunction: null }, navItems: { classPropertyName: "navItems", publicName: "navItems", isSignal: true, isRequired: false, transformFunction: null }, userInitials: { classPropertyName: "userInitials", publicName: "userInitials", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { sessionClick: "sessionClick" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, viewQueries: [{ propertyName: "sessionButtonRef", first: true, predicate: ["sessionButton"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: `
4194
4680
  @if (isAdmin()) {
4195
4681
  <div class="kf-portal-header__admin-bar">
4196
4682
  <kf-admin-badge inline />
@@ -4203,6 +4689,7 @@ class PortalHeader {
4203
4689
  }
4204
4690
  <div class="kf-portal-header__brand-bar">
4205
4691
  <span class="kf-portal-header__logo">{{ brand() }}</span>
4692
+
4206
4693
  <nav class="kf-portal-header__nav">
4207
4694
  @for (item of navItems(); track item.label) {
4208
4695
  <a
@@ -4214,15 +4701,54 @@ class PortalHeader {
4214
4701
  </a>
4215
4702
  }
4216
4703
  </nav>
4217
- <button type="button" class="kf-portal-header__session" (click)="sessionClick.emit()">
4704
+
4705
+ <button
4706
+ #sessionButton
4707
+ type="button"
4708
+ class="kf-portal-header__session"
4709
+ (click)="sessionClick.emit()"
4710
+ >
4218
4711
  <kf-avatar [initials]="userInitials()" pill />
4219
4712
  </button>
4713
+
4714
+ <button
4715
+ type="button"
4716
+ class="kf-portal-header__menu-toggle"
4717
+ [attr.aria-expanded]="mobileMenuOpen()"
4718
+ aria-label="Abrir menú de navegación"
4719
+ (click)="mobileMenuOpen.set(!mobileMenuOpen())"
4720
+ >
4721
+ <kf-icon [name]="mobileMenuOpen() ? 'x' : 'menu'" size="md" />
4722
+ </button>
4220
4723
  </div>
4221
- `, isInline: true, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover:not(.kf-portal-header__nav-item--active){color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}\n"], dependencies: [{ kind: "component", type: AdminBadge, selector: "kf-admin-badge", inputs: ["inline"] }, { kind: "component", type: Avatar, selector: "kf-avatar", inputs: ["initials", "src", "round", "pill"] }] });
4724
+
4725
+ @if (mobileMenuOpen()) {
4726
+ <div class="kf-portal-header__overlay" (click)="mobileMenuOpen.set(false)"></div>
4727
+ }
4728
+
4729
+ <nav
4730
+ class="kf-portal-header__drawer"
4731
+ [class.kf-portal-header__drawer--open]="mobileMenuOpen()"
4732
+ role="menu"
4733
+ [attr.aria-hidden]="!mobileMenuOpen()"
4734
+ >
4735
+ @for (item of navItems(); track item.label) {
4736
+ <a
4737
+ class="kf-portal-header__mobile-nav-item"
4738
+ [class.kf-portal-header__mobile-nav-item--active]="item.active"
4739
+ [href]="item.href"
4740
+ (click)="mobileMenuOpen.set(false)"
4741
+ >
4742
+ {{ item.label }}
4743
+ </a>
4744
+ }
4745
+ <ng-content select="[kf-portal-header-extra]" />
4746
+ </nav>
4747
+ `, isInline: true, styles: [":host{display:block;position:sticky;top:0;z-index:30;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase;flex:1 1 auto;min-width:0}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;white-space:nowrap;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover:not(.kf-portal-header__nav-item--active){color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}.kf-portal-header__menu-toggle{display:none;flex:none;align-items:center;justify-content:center;width:36px;height:36px;border:none;border-radius:var(--kf-radius-sm);background:transparent;color:var(--kf-color-white);cursor:pointer}.kf-portal-header__menu-toggle:hover{background:#ffffff1a}.kf-portal-header__overlay{position:fixed;inset:0;background:#10182866;z-index:39}.kf-portal-header__drawer{position:fixed;top:0;right:0;bottom:0;z-index:40;display:none;flex-direction:column;width:min(80vw,320px);padding:var(--kf-space-lg);background:var(--kf-color-tertiary-deep);box-shadow:-8px 0 24px -8px #1018284d;transform:translate(100%);visibility:hidden;transition:transform .24s cubic-bezier(.32,.72,0,1),visibility 0s linear .24s}.kf-portal-header__drawer--open{transform:translate(0);visibility:visible;transition:transform .24s cubic-bezier(.32,.72,0,1),visibility 0s linear 0s}.kf-portal-header__mobile-nav-item{padding:var(--kf-space-sm) var(--kf-space-xs);color:#ffffffd9;font-size:.9375rem;font-weight:600;text-decoration:none;border-radius:var(--kf-radius-sm)}.kf-portal-header__mobile-nav-item:hover{background:#ffffff14}.kf-portal-header__mobile-nav-item--active{color:var(--kf-color-white);background:#ffffff24}@media(max-width:768px){.kf-portal-header__nav,.kf-portal-header__session{display:none}.kf-portal-header__menu-toggle{display:inline-flex}.kf-portal-header__drawer{display:flex}}\n"], dependencies: [{ kind: "component", type: AdminBadge, selector: "kf-admin-badge", inputs: ["inline"] }, { kind: "component", type: Avatar, selector: "kf-avatar", inputs: ["initials", "src", "round", "pill"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
4222
4748
  }
4223
4749
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PortalHeader, decorators: [{
4224
4750
  type: Component,
4225
- args: [{ selector: 'kf-portal-header', imports: [AdminBadge, Avatar], template: `
4751
+ args: [{ selector: 'kf-portal-header', imports: [AdminBadge, Avatar, Icon], template: `
4226
4752
  @if (isAdmin()) {
4227
4753
  <div class="kf-portal-header__admin-bar">
4228
4754
  <kf-admin-badge inline />
@@ -4235,6 +4761,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4235
4761
  }
4236
4762
  <div class="kf-portal-header__brand-bar">
4237
4763
  <span class="kf-portal-header__logo">{{ brand() }}</span>
4764
+
4238
4765
  <nav class="kf-portal-header__nav">
4239
4766
  @for (item of navItems(); track item.label) {
4240
4767
  <a
@@ -4246,12 +4773,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4246
4773
  </a>
4247
4774
  }
4248
4775
  </nav>
4249
- <button type="button" class="kf-portal-header__session" (click)="sessionClick.emit()">
4776
+
4777
+ <button
4778
+ #sessionButton
4779
+ type="button"
4780
+ class="kf-portal-header__session"
4781
+ (click)="sessionClick.emit()"
4782
+ >
4250
4783
  <kf-avatar [initials]="userInitials()" pill />
4251
4784
  </button>
4785
+
4786
+ <button
4787
+ type="button"
4788
+ class="kf-portal-header__menu-toggle"
4789
+ [attr.aria-expanded]="mobileMenuOpen()"
4790
+ aria-label="Abrir menú de navegación"
4791
+ (click)="mobileMenuOpen.set(!mobileMenuOpen())"
4792
+ >
4793
+ <kf-icon [name]="mobileMenuOpen() ? 'x' : 'menu'" size="md" />
4794
+ </button>
4252
4795
  </div>
4253
- `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover:not(.kf-portal-header__nav-item--active){color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}\n"] }]
4254
- }], propDecorators: { brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: false }] }], isAdmin: [{ type: i0.Input, args: [{ isSignal: true, alias: "isAdmin", required: false }] }], adminActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "adminActions", required: false }] }], navItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navItems", required: false }] }], userInitials: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInitials", required: true }] }], sessionClick: [{ type: i0.Output, args: ["sessionClick"] }] } });
4796
+
4797
+ @if (mobileMenuOpen()) {
4798
+ <div class="kf-portal-header__overlay" (click)="mobileMenuOpen.set(false)"></div>
4799
+ }
4800
+
4801
+ <nav
4802
+ class="kf-portal-header__drawer"
4803
+ [class.kf-portal-header__drawer--open]="mobileMenuOpen()"
4804
+ role="menu"
4805
+ [attr.aria-hidden]="!mobileMenuOpen()"
4806
+ >
4807
+ @for (item of navItems(); track item.label) {
4808
+ <a
4809
+ class="kf-portal-header__mobile-nav-item"
4810
+ [class.kf-portal-header__mobile-nav-item--active]="item.active"
4811
+ [href]="item.href"
4812
+ (click)="mobileMenuOpen.set(false)"
4813
+ >
4814
+ {{ item.label }}
4815
+ </a>
4816
+ }
4817
+ <ng-content select="[kf-portal-header-extra]" />
4818
+ </nav>
4819
+ `, styles: [":host{display:block;position:sticky;top:0;z-index:30;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase;flex:1 1 auto;min-width:0}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;white-space:nowrap;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover:not(.kf-portal-header__nav-item--active){color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}.kf-portal-header__menu-toggle{display:none;flex:none;align-items:center;justify-content:center;width:36px;height:36px;border:none;border-radius:var(--kf-radius-sm);background:transparent;color:var(--kf-color-white);cursor:pointer}.kf-portal-header__menu-toggle:hover{background:#ffffff1a}.kf-portal-header__overlay{position:fixed;inset:0;background:#10182866;z-index:39}.kf-portal-header__drawer{position:fixed;top:0;right:0;bottom:0;z-index:40;display:none;flex-direction:column;width:min(80vw,320px);padding:var(--kf-space-lg);background:var(--kf-color-tertiary-deep);box-shadow:-8px 0 24px -8px #1018284d;transform:translate(100%);visibility:hidden;transition:transform .24s cubic-bezier(.32,.72,0,1),visibility 0s linear .24s}.kf-portal-header__drawer--open{transform:translate(0);visibility:visible;transition:transform .24s cubic-bezier(.32,.72,0,1),visibility 0s linear 0s}.kf-portal-header__mobile-nav-item{padding:var(--kf-space-sm) var(--kf-space-xs);color:#ffffffd9;font-size:.9375rem;font-weight:600;text-decoration:none;border-radius:var(--kf-radius-sm)}.kf-portal-header__mobile-nav-item:hover{background:#ffffff14}.kf-portal-header__mobile-nav-item--active{color:var(--kf-color-white);background:#ffffff24}@media(max-width:768px){.kf-portal-header__nav,.kf-portal-header__session{display:none}.kf-portal-header__menu-toggle{display:inline-flex}.kf-portal-header__drawer{display:flex}}\n"] }]
4820
+ }], propDecorators: { brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: false }] }], isAdmin: [{ type: i0.Input, args: [{ isSignal: true, alias: "isAdmin", required: false }] }], adminActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "adminActions", required: false }] }], navItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navItems", required: false }] }], userInitials: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInitials", required: true }] }], sessionClick: [{ type: i0.Output, args: ["sessionClick"] }], onEscape: [{
4821
+ type: HostListener,
4822
+ args: ['document:keydown.escape']
4823
+ }], sessionButtonRef: [{ type: i0.ViewChild, args: ['sessionButton', { ...{ read: ElementRef }, isSignal: true }] }] } });
4255
4824
 
4256
4825
  /**
4257
4826
  * Estado vacío de reservas en taller con llamada a agendar (página 36):
@@ -4347,10 +4916,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4347
4916
  * `<ng-content select="[actions]">` para no acoplar el componente a un set
4348
4917
  * fijo de botones.
4349
4918
  *
4350
- * `size="sm"` reduce tipografía y espaciado para usarse dentro de cards ya
4351
- * compactas. `solid` rellena el chip de ícono con el color primario sólido
4352
- * en vez del tratamiento "soft" (fondo tenue) — pensado para superficies que
4353
- * ya tienen un tinte de color propio, donde el chip "soft" perdería contraste.
4919
+ * `size` sigue la misma escala `'sm'|'md'|'lg'` que `Button`/`Icon`/`Spinner`
4920
+ * (`md` es el default): `sm` reduce tipografía y espaciado para usarse
4921
+ * dentro de cards ya compactas, `lg` los agranda para encabezar un bloque
4922
+ * más prominente. Antes de la `0.2.0`, `size` solo aceptaba `'sm'` —
4923
+ * placeholder incompleto de esta misma escala; los valores de `lg` son una
4924
+ * extrapolación proporcional de la escala existente (sin un caso de uso
4925
+ * real todavía que los valide, a diferencia de `sm`, que sí viene de un
4926
+ * patrón real de uso dentro de cards). `solid` rellena el chip de ícono con
4927
+ * el color primario sólido en vez del tratamiento "soft" (fondo tenue) —
4928
+ * pensado para superficies que ya tienen un tinte de color propio, donde el
4929
+ * chip "soft" perdería contraste.
4354
4930
  *
4355
4931
  * <kf-section-header icon="settings" title="Preferencias"
4356
4932
  * subtitle="Configuración general de la cuenta">
@@ -4358,6 +4934,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4358
4934
  * </kf-section-header>
4359
4935
  *
4360
4936
  * <kf-section-header title="Detalle" size="sm" [solid]="true" />
4937
+ * <kf-section-header title="Resumen de flota" size="lg" />
4361
4938
  */
4362
4939
  class SectionHeader {
4363
4940
  icon = input(/* @ts-ignore */
@@ -4366,14 +4943,24 @@ class SectionHeader {
4366
4943
  ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
4367
4944
  subtitle = input(/* @ts-ignore */
4368
4945
  ...(ngDevMode ? [undefined, { debugName: "subtitle" }] : /* istanbul ignore next */ []));
4369
- size = input(/* @ts-ignore */
4370
- ...(ngDevMode ? [undefined, { debugName: "size" }] : /* istanbul ignore next */ []));
4946
+ size = input('md', /* @ts-ignore */
4947
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
4371
4948
  solid = input(false, { ...(ngDevMode ? { debugName: "solid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
4949
+ iconSize = computed(() => {
4950
+ if (this.size() === 'sm') {
4951
+ return 'sm';
4952
+ }
4953
+ if (this.size() === 'lg') {
4954
+ return 'lg';
4955
+ }
4956
+ return 'md';
4957
+ }, /* @ts-ignore */
4958
+ ...(ngDevMode ? [{ debugName: "iconSize" }] : /* istanbul ignore next */ []));
4372
4959
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SectionHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
4373
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SectionHeader, isStandalone: true, selector: "kf-section-header", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, solid: { classPropertyName: "solid", publicName: "solid", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.kf-section-header--sm": "size() === 'sm'" } }, ngImport: i0, template: `
4960
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SectionHeader, isStandalone: true, selector: "kf-section-header", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, solid: { classPropertyName: "solid", publicName: "solid", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.kf-section-header--sm": "size() === 'sm'", "class.kf-section-header--lg": "size() === 'lg'" } }, ngImport: i0, template: `
4374
4961
  @if (icon(); as iconName) {
4375
4962
  <span class="kf-section-header__icon" [class.kf-section-header__icon--solid]="solid()">
4376
- <kf-icon [name]="iconName" [size]="size() === 'sm' ? 'sm' : 'md'" />
4963
+ <kf-icon [name]="iconName" [size]="iconSize()" />
4377
4964
  </span>
4378
4965
  }
4379
4966
 
@@ -4387,14 +4974,14 @@ class SectionHeader {
4387
4974
  <span class="kf-section-header__actions">
4388
4975
  <ng-content select="[actions]" />
4389
4976
  </span>
4390
- `, isInline: true, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
4977
+ `, isInline: true, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}:host(.kf-section-header--lg){gap:var(--kf-space-md)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}:host(.kf-section-header--lg) .kf-section-header__icon{inline-size:3.25rem;block-size:3.25rem;border-radius:var(--kf-radius-md)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}:host(.kf-section-header--lg) .kf-section-header__title{font-size:1.25rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}:host(.kf-section-header--lg) .kf-section-header__subtitle{font-size:.9375rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
4391
4978
  }
4392
4979
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SectionHeader, decorators: [{
4393
4980
  type: Component,
4394
4981
  args: [{ selector: 'kf-section-header', imports: [Icon], template: `
4395
4982
  @if (icon(); as iconName) {
4396
4983
  <span class="kf-section-header__icon" [class.kf-section-header__icon--solid]="solid()">
4397
- <kf-icon [name]="iconName" [size]="size() === 'sm' ? 'sm' : 'md'" />
4984
+ <kf-icon [name]="iconName" [size]="iconSize()" />
4398
4985
  </span>
4399
4986
  }
4400
4987
 
@@ -4410,7 +4997,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4410
4997
  </span>
4411
4998
  `, host: {
4412
4999
  '[class.kf-section-header--sm]': "size() === 'sm'",
4413
- }, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"] }]
5000
+ '[class.kf-section-header--lg]': "size() === 'lg'",
5001
+ }, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}:host(.kf-section-header--lg){gap:var(--kf-space-md)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}:host(.kf-section-header--lg) .kf-section-header__icon{inline-size:3.25rem;block-size:3.25rem;border-radius:var(--kf-radius-md)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}:host(.kf-section-header--lg) .kf-section-header__title{font-size:1.25rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}:host(.kf-section-header--lg) .kf-section-header__subtitle{font-size:.9375rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"] }]
4414
5002
  }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], solid: [{ type: i0.Input, args: [{ isSignal: true, alias: "solid", required: false }] }] } });
4415
5003
 
4416
5004
  /**
@@ -4484,184 +5072,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4484
5072
  `, styles: [":host{display:inline-block}.kf-segmented__track{position:relative;display:grid;grid-template-columns:repeat(var(--kf-segmented-count),1fr);gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md)}.kf-segmented__thumb{position:absolute;top:2px;left:2px;width:calc((100% - 4px) / var(--kf-segmented-count));height:calc(100% - 4px);background:var(--kf-color-white);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-xs);transform:translate(calc(var(--kf-segmented-index, 0) * 100%));transition:transform .2s ease}.kf-segmented__option{position:relative;z-index:1;font:inherit;padding:var(--kf-space-xs) var(--kf-space-md);border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-sm);cursor:pointer;transition:color .14s}.kf-segmented__option:hover:not(.kf-segmented__option--active){color:var(--kf-color-text)}.kf-segmented__option--active{color:var(--kf-color-ink)}.kf-segmented__option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}\n"] }]
4485
5073
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
4486
5074
 
4487
- let nextId = 0;
4488
- /**
4489
- * Dropdown de selección simple pero con panel propio (no usa `<select>` nativo),
4490
- * para cuando se necesita más control visual que un `<select>` estándar permite.
4491
- *
4492
- * El panel flotante se implementa con `position: absolute` sobre el host
4493
- * (sin Angular CDK Overlay, que no es dependencia del proyecto). Se abre y
4494
- * cierra por click en el disparador, y se cierra al hacer click afuera o al
4495
- * presionar Escape.
4496
- *
4497
- * <kf-select-menu
4498
- * label="País"
4499
- * [options]="countries"
4500
- * [value]="selectedCountry()"
4501
- * (valueChange)="selectedCountry.set($event)"
4502
- * />
4503
- *
4504
- * Pendiente: el prop `icon` de otros componentes del design system requiere
4505
- * un componente Icon / sprite SVG que esta librería todavía no tiene — este
4506
- * componente no usa iconos, así que no aplica acá.
4507
- */
4508
- class SelectMenu {
4509
- label = input(/* @ts-ignore */
4510
- ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
4511
- options = input([], /* @ts-ignore */
4512
- ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
4513
- value = input(/* @ts-ignore */
4514
- ...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
4515
- placeholder = input('Seleccionar', /* @ts-ignore */
4516
- ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
4517
- disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
4518
- valueChange = output();
4519
- instanceId = `kf-select-menu-${nextId++}`;
4520
- triggerId = `${this.instanceId}-trigger`;
4521
- listboxId = `${this.instanceId}-listbox`;
4522
- isOpen = signal(false, /* @ts-ignore */
4523
- ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
4524
- selectedOption = computed(() => this.options().find((option) => option.value === this.value()), /* @ts-ignore */
4525
- ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
4526
- host = inject((ElementRef));
4527
- toggle() {
4528
- if (this.disabled()) {
4529
- return;
4530
- }
4531
- this.isOpen.update((open) => !open);
4532
- }
4533
- select(option) {
4534
- this.isOpen.set(false);
4535
- if (option.value !== this.value()) {
4536
- this.valueChange.emit(option.value);
4537
- }
4538
- }
4539
- onDocumentClick(event) {
4540
- if (this.isOpen() && !this.host.nativeElement.contains(event.target)) {
4541
- this.isOpen.set(false);
4542
- }
4543
- }
4544
- onEscape() {
4545
- this.isOpen.set(false);
4546
- }
4547
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SelectMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
4548
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SelectMenu, isStandalone: true, selector: "kf-select-menu", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: `
4549
- @if (label()) {
4550
- <kf-field-label [for]="triggerId">{{ label() }}</kf-field-label>
4551
- }
4552
- <button
4553
- #trigger
4554
- type="button"
4555
- [id]="triggerId"
4556
- class="kf-select-menu__trigger"
4557
- [disabled]="disabled()"
4558
- [attr.aria-haspopup]="'listbox'"
4559
- [attr.aria-expanded]="isOpen()"
4560
- [attr.aria-controls]="isOpen() ? listboxId : null"
4561
- (click)="toggle()"
4562
- >
4563
- <span
4564
- class="kf-select-menu__value"
4565
- [class.kf-select-menu__value--placeholder]="!selectedOption()"
4566
- >
4567
- {{ selectedOption()?.label ?? placeholder() }}
4568
- </span>
4569
- <span
4570
- class="kf-select-menu__caret"
4571
- [class.kf-select-menu__caret--open]="isOpen()"
4572
- aria-hidden="true"
4573
- ></span>
4574
- </button>
4575
- @if (isOpen()) {
4576
- <ul
4577
- class="kf-select-menu__panel"
4578
- [id]="listboxId"
4579
- role="listbox"
4580
- [attr.aria-labelledby]="triggerId"
4581
- >
4582
- @for (option of options(); track option.value) {
4583
- <li
4584
- class="kf-select-menu__option"
4585
- [class.kf-select-menu__option--selected]="option.value === value()"
4586
- role="option"
4587
- tabindex="0"
4588
- [attr.aria-selected]="option.value === value()"
4589
- (click)="select(option)"
4590
- (keydown.enter)="select(option)"
4591
- (keydown.space)="$event.preventDefault(); select(option)"
4592
- >
4593
- {{ option.label }}
4594
- </li>
4595
- } @empty {
4596
- <li class="kf-select-menu__empty">Sin opciones disponibles</li>
4597
- }
4598
- </ul>
4599
- }
4600
- `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-select-menu__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select-menu__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select-menu__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-select-menu__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-select-menu__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select-menu__value--placeholder{color:var(--kf-color-text-muted)}.kf-select-menu__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select-menu__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select-menu__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select-menu__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select-menu__option:hover{background:var(--kf-color-primary-soft)}.kf-select-menu__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select-menu__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
4601
- }
4602
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SelectMenu, decorators: [{
4603
- type: Component,
4604
- args: [{ selector: 'kf-select-menu', imports: [FieldLabel], template: `
4605
- @if (label()) {
4606
- <kf-field-label [for]="triggerId">{{ label() }}</kf-field-label>
4607
- }
4608
- <button
4609
- #trigger
4610
- type="button"
4611
- [id]="triggerId"
4612
- class="kf-select-menu__trigger"
4613
- [disabled]="disabled()"
4614
- [attr.aria-haspopup]="'listbox'"
4615
- [attr.aria-expanded]="isOpen()"
4616
- [attr.aria-controls]="isOpen() ? listboxId : null"
4617
- (click)="toggle()"
4618
- >
4619
- <span
4620
- class="kf-select-menu__value"
4621
- [class.kf-select-menu__value--placeholder]="!selectedOption()"
4622
- >
4623
- {{ selectedOption()?.label ?? placeholder() }}
4624
- </span>
4625
- <span
4626
- class="kf-select-menu__caret"
4627
- [class.kf-select-menu__caret--open]="isOpen()"
4628
- aria-hidden="true"
4629
- ></span>
4630
- </button>
4631
- @if (isOpen()) {
4632
- <ul
4633
- class="kf-select-menu__panel"
4634
- [id]="listboxId"
4635
- role="listbox"
4636
- [attr.aria-labelledby]="triggerId"
4637
- >
4638
- @for (option of options(); track option.value) {
4639
- <li
4640
- class="kf-select-menu__option"
4641
- [class.kf-select-menu__option--selected]="option.value === value()"
4642
- role="option"
4643
- tabindex="0"
4644
- [attr.aria-selected]="option.value === value()"
4645
- (click)="select(option)"
4646
- (keydown.enter)="select(option)"
4647
- (keydown.space)="$event.preventDefault(); select(option)"
4648
- >
4649
- {{ option.label }}
4650
- </li>
4651
- } @empty {
4652
- <li class="kf-select-menu__empty">Sin opciones disponibles</li>
4653
- }
4654
- </ul>
4655
- }
4656
- `, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-select-menu__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select-menu__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select-menu__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-select-menu__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-select-menu__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select-menu__value--placeholder{color:var(--kf-color-text-muted)}.kf-select-menu__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select-menu__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select-menu__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select-menu__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select-menu__option:hover{background:var(--kf-color-primary-soft)}.kf-select-menu__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select-menu__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
4657
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], onDocumentClick: [{
4658
- type: HostListener,
4659
- args: ['document:click', ['$event']]
4660
- }], onEscape: [{
4661
- type: HostListener,
4662
- args: ['document:keydown.escape']
4663
- }] } });
4664
-
4665
5075
  /**
4666
5076
  * Indicador numérico compacto (página 34): label + valor grande, con un
4667
5077
  * ícono a la derecha del label — más chico y sin badge de esquina que
@@ -4930,6 +5340,109 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4930
5340
  `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-tabs{position:relative;display:flex;align-items:stretch;gap:var(--kf-space-md);border-bottom:1px solid var(--kf-color-border)}.kf-tabs__tab{display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;font-size:.9rem;color:var(--kf-color-text-muted);background:transparent;border:none;padding:var(--kf-space-sm) 0;cursor:pointer;transition:color .14s}.kf-tabs__tab:hover:not(.kf-tabs__tab--active){color:var(--kf-color-text)}.kf-tabs__tab:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);border-radius:var(--kf-radius-xs)}.kf-tabs__tab--active{color:var(--kf-color-primary);font-size:1rem;font-weight:700}.kf-tabs__indicator{position:absolute;bottom:-1px;left:0;height:2px;background:var(--kf-color-primary);border-radius:2px 2px 0 0;pointer-events:none;transition:transform .28s cubic-bezier(.32,.72,0,1),width .28s cubic-bezier(.32,.72,0,1),opacity .14s}@media(prefers-reduced-motion:reduce){.kf-tabs__indicator{transition:opacity .14s}}\n"] }]
4931
5341
  }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }] } });
4932
5342
 
5343
+ /**
5344
+ * Tipografía unificada — separa la etiqueta semántica (`as`, qué es el
5345
+ * texto para el DOM/accesibilidad) del estilo visual (`variant`, cómo se
5346
+ * ve). Antes de este componente, cada pantalla que consume la librería
5347
+ * definía su propio `h1`/`p` con `font-size` suelto para el mismo rol
5348
+ * (título de página, subtítulo, texto secundario) — auditoría sobre
5349
+ * `web-portal-latam-frontend` encontró 9 tamaños distintos entre login y
5350
+ * home sin ningún paso de escala en común. `variant` resuelve eso contra
5351
+ * la escala de `tokens/base.css` (`--kf-font-size-*`).
5352
+ *
5353
+ * `tone` sigue la misma convención cerrada que `Alert`/`Badge`/`Chip`: sin
5354
+ * escape hatch de color libre. Si un caso necesita un color que `tone` no
5355
+ * cubre, es una discusión de design system, no una prop nueva acá.
5356
+ *
5357
+ * <kf-text as="h1" variant="title-lg">Bienvenido de nuevo</kf-text>
5358
+ * <kf-text as="p" variant="body-muted">Ingresá con tu cuenta corporativa.</kf-text>
5359
+ * <kf-text as="span" variant="caption" tone="error">Campo obligatorio</kf-text>
5360
+ */
5361
+ class Text {
5362
+ /** Etiqueta semántica real que se renderiza — nunca es solo un `div`. */
5363
+ as = input('p', /* @ts-ignore */
5364
+ ...(ngDevMode ? [{ debugName: "as" }] : /* istanbul ignore next */ []));
5365
+ variant = input('body', /* @ts-ignore */
5366
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
5367
+ tone = input('neutral', /* @ts-ignore */
5368
+ ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
5369
+ /**
5370
+ * Copia de `as` para uso dentro del template: `as` es palabra reservada
5371
+ * en las expresiones de Angular (`@if (x; as y)`), así que `@switch (as())`
5372
+ * no parsea — NG5002. La prop pública sigue llamándose `as` (es solo un
5373
+ * atributo HTML ahí, `<kf-text as="h1">`, no una expresión).
5374
+ */
5375
+ tag = computed(() => this.as(), /* @ts-ignore */
5376
+ ...(ngDevMode ? [{ debugName: "tag" }] : /* istanbul ignore next */ []));
5377
+ hostClass() {
5378
+ const classes = [`kf-text--${this.variant()}`];
5379
+ if (this.tone() !== 'neutral') {
5380
+ classes.push(`kf-text--${this.tone()}`);
5381
+ }
5382
+ return classes.join(' ');
5383
+ }
5384
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Text, deps: [], target: i0.ɵɵFactoryTarget.Component });
5385
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Text, isStandalone: true, selector: "kf-text", inputs: { as: { classPropertyName: "as", publicName: "as", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
5386
+ <!--
5387
+ Una sola proyección compartida (ng-template + ngTemplateOutlet) en vez
5388
+ de un <ng-content> por cada rama del @switch: Angular solo conecta la
5389
+ proyección real del consumidor a la PRIMERA instancia de <ng-content>
5390
+ que encuentra en el árbol de compilación — con un <ng-content> por
5391
+ rama, todas menos una quedan vacías en runtime (bug detectado por
5392
+ Text.spec.ts, no algo documentado a simple vista en los docs de Angular).
5393
+ -->
5394
+ <ng-template #projected><ng-content /></ng-template>
5395
+ @switch (tag()) {
5396
+ @case ('h1') {
5397
+ <h1 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h1>
5398
+ }
5399
+ @case ('h2') {
5400
+ <h2 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h2>
5401
+ }
5402
+ @case ('h3') {
5403
+ <h3 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h3>
5404
+ }
5405
+ @case ('span') {
5406
+ <span [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></span>
5407
+ }
5408
+ @default {
5409
+ <p [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></p>
5410
+ }
5411
+ }
5412
+ `, isInline: true, styles: [":host{display:contents}h1,h2,h3,p,span{margin:0;font-family:var(--kf-font-body);color:var(--kf-color-text)}.kf-text--title-lg{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-lg);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-md{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-md);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-sm{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-sm);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--body{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body)}.kf-text--body-muted{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--caption{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--ok{color:var(--kf-color-success-strong)}.kf-text--warn{color:var(--kf-color-warning-strong)}.kf-text--error{color:var(--kf-color-error-strong)}.kf-text--info{color:var(--kf-color-info-strong)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
5413
+ }
5414
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Text, decorators: [{
5415
+ type: Component,
5416
+ args: [{ selector: 'kf-text', imports: [NgTemplateOutlet], template: `
5417
+ <!--
5418
+ Una sola proyección compartida (ng-template + ngTemplateOutlet) en vez
5419
+ de un <ng-content> por cada rama del @switch: Angular solo conecta la
5420
+ proyección real del consumidor a la PRIMERA instancia de <ng-content>
5421
+ que encuentra en el árbol de compilación — con un <ng-content> por
5422
+ rama, todas menos una quedan vacías en runtime (bug detectado por
5423
+ Text.spec.ts, no algo documentado a simple vista en los docs de Angular).
5424
+ -->
5425
+ <ng-template #projected><ng-content /></ng-template>
5426
+ @switch (tag()) {
5427
+ @case ('h1') {
5428
+ <h1 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h1>
5429
+ }
5430
+ @case ('h2') {
5431
+ <h2 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h2>
5432
+ }
5433
+ @case ('h3') {
5434
+ <h3 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h3>
5435
+ }
5436
+ @case ('span') {
5437
+ <span [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></span>
5438
+ }
5439
+ @default {
5440
+ <p [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></p>
5441
+ }
5442
+ }
5443
+ `, styles: [":host{display:contents}h1,h2,h3,p,span{margin:0;font-family:var(--kf-font-body);color:var(--kf-color-text)}.kf-text--title-lg{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-lg);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-md{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-md);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-sm{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-sm);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--body{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body)}.kf-text--body-muted{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--caption{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--ok{color:var(--kf-color-success-strong)}.kf-text--warn{color:var(--kf-color-warning-strong)}.kf-text--error{color:var(--kf-color-error-strong)}.kf-text--info{color:var(--kf-color-info-strong)}\n"] }]
5444
+ }], propDecorators: { as: [{ type: i0.Input, args: [{ isSignal: true, alias: "as", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
5445
+
4933
5446
  /** Fixed icon per tone (page 20 of the design system) — not configurable per call. */
4934
5447
  const TOAST_ICON = {
4935
5448
  success: 'check-circle',
@@ -5279,5 +5792,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
5279
5792
  * Generated bundle index. Do not edit.
5280
5793
  */
5281
5794
 
5282
- export { ActionTile, AdminBadge, Alert, Avatar, Badge, Breadcrumb, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, CompanySelector, ContentSlot, ContentState, DataTable, Divider, EmptyState, FeatureCard, Field, FieldLabel, FilterChips, FolderTabs, Icon, Input, MetricCard, Modal, MultiSelect, PageHeader, Pagination, Panel, PortalHeader, ReserveBlock, SectionHeader, Segmented, Select, SelectMenu, Skeleton, Spinner, StatTile, StatusPill, Tabs, ToastHost, ToastService, VehicleHeader, ViewToggle };
5795
+ export { ActionTile, AdminBadge, Alert, Avatar, Badge, Breadcrumb, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, CompanySelector, ContentSlot, ContentState, DataTable, Divider, Dropdown, DropdownItem, DropdownPanel, EmptyState, FeatureCard, Field, FieldLabel, FilterChips, FolderTabs, FolderTabsPanel, Icon, Input, Link, MetricCard, Modal, MultiSelect, PageHeader, Pagination, Panel, PortalHeader, ReserveBlock, SectionHeader, Segmented, Select, Skeleton, Spinner, StatTile, StatusPill, Tabs, Text, ToastHost, ToastService, VehicleHeader, ViewToggle };
5283
5796
  //# sourceMappingURL=kaufmann-lab-ui.mjs.map