@apliteni/apliteni-ui 0.68.0 → 0.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apliteni/apliteni-ui",
3
- "version": "0.68.0",
3
+ "version": "0.70.0",
4
4
  "workspaces": [
5
5
  "react"
6
6
  ],
package/react/README.md CHANGED
@@ -29,7 +29,7 @@ import '@apliteni/apliteni-ui/react/css'; // React components' shell styles (mo
29
29
  import { DataTable, Modal, Button } from '@apliteni/apliteni-ui/react';
30
30
  ```
31
31
 
32
- Components: `Success`, `SuccessPanel`, `SuccessCheck`, `Checkbox`, `Switch`, `EmptyState`, `DataTable`, `Pagination`, `StatBand`, `Modal`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Tooltip`, `Snippet`, `Tabs`, `Button`, `Badge`, `Pill`, `StatusDot`, `Card`, `Callout`, `Icon`.
32
+ Components: `Success`, `SuccessPanel`, `SuccessCheck`, `Checkbox`, `Switch`, `SearchField`, `EmptyState`, `DataTable`, `Pagination`, `StatBand`, `Modal`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Tooltip`, `Snippet`, `Tabs`, `Button`, `Badge`, `Pill`, `StatusDot`, `Card`, `Callout`, `Icon`.
33
33
 
34
34
  `Pill` renders metadata with the existing pill spacing. Omit `variant` for neutral
35
35
  metadata or use `live` or `soon`. Its children accept React content.
@@ -477,6 +477,27 @@ class and invalid styling as appropriate:
477
477
  The frame supplies a stable ID, required state, and linked hint/error messages.
478
478
  An optional `id` lets the caller choose the control ID; it must be unique.
479
479
 
480
+ ### Search
481
+
482
+ `SearchField` is the toolbar's search box: a search glyph, a native
483
+ `type="search"` input, and no visible label, so the row keeps the height of the
484
+ unlabelled controls beside it. `ariaLabel` is required and is the control's only
485
+ name. It forwards its ref and the native input props, and `className` joins
486
+ `.ui-input`. There is no clear button — see the specification for why.
487
+
488
+ ```tsx
489
+ <div className="ui-toolbar">
490
+ <SearchField ariaLabel="Search invoices" placeholder="Vendor or number"
491
+ value={query} onChange={e => setQuery(e.currentTarget.value)} />
492
+ {/* then the filters, then the view switch */}
493
+ </div>
494
+ ```
495
+
496
+ Put it first in a `.ui-toolbar`. Above 560px it is the control there that grows
497
+ into the slack the others leave; at 560px and below it takes a line of its own
498
+ and the rest of the row wraps under it. Use `TextField` instead wherever the
499
+ field stands in a form and a visible label belongs over it.
500
+
480
501
  ## Toast
481
502
 
482
503
  Wrap your app in `Toast`, then call `useToast()` inside it to add a notice.
@@ -420,6 +420,11 @@
420
420
  box-shadow: var(--ring);
421
421
  border-radius: 4px;
422
422
  }
423
+ .ui-table__selection input[type=checkbox]:focus-visible {
424
+ outline: 2px solid transparent;
425
+ box-shadow: var(--ring);
426
+ border-radius: var(--radius-xs);
427
+ }
423
428
  .rx-caret {
424
429
  margin-left: 2px;
425
430
  width: 16px;
@@ -602,6 +602,29 @@ type FileFieldProps = FieldMessage & Omit<InputHTMLAttributes<HTMLInputElement>,
602
602
  };
603
603
  declare function FileField({ label, hint, error, required, disabled, onFileChange, className, ...props }: FileFieldProps): react.JSX.Element;
604
604
 
605
+ type SearchFieldProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'aria-label'> & {
606
+ /** The control's name. A toolbar search box shows no label, so this is the
607
+ only name it has. */
608
+ ariaLabel: string;
609
+ };
610
+ /**
611
+ * The toolbar's search box: one `.ui-input-group` holding the `search` glyph and
612
+ * a native `type="search"` control on `.ui-input`. It has no visible label, so
613
+ * the row keeps the height of the unlabelled controls beside it, and no CSS of
614
+ * its own, so the toolbar's row rule and the field's focus ring reach it from
615
+ * the kit's stylesheet.
616
+ *
617
+ * No clear button. The browser paints its own near-black on the light field and
618
+ * white on the dark one, beside the kit's `--muted` magnifier — two glyphs, and
619
+ * only one answers to a token — so `input.css` suppresses it.
620
+ * why: docs/specification.md#react-search-field
621
+ */
622
+ declare const SearchField: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "aria-label"> & {
623
+ /** The control's name. A toolbar search box shows no label, so this is the
624
+ only name it has. */
625
+ ariaLabel: string;
626
+ } & react.RefAttributes<HTMLInputElement>>;
627
+
605
628
  type ToastNotice = {
606
629
  tone?: 'success' | 'danger' | 'warn' | 'info' | 'neutral';
607
630
  title: string;
@@ -799,4 +822,4 @@ declare const SidebarNav: react.ForwardRefExoticComponent<Omit<Omit<react.Detail
799
822
  declare const PAGE_SIZES: readonly number[];
800
823
  declare const DEFAULT_PAGE_SIZE: number;
801
824
 
802
- export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonLinkProps, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, Checkbox, type CheckboxProps, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, Field, type FieldControlProps, type FieldProps, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, type NativeButtonProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, Pill, type PillProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, SidebarNav, type SidebarNavBadge, type SidebarNavItem, type SidebarNavLeaf, type SidebarNavProps, type SidebarNavSection, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, StatusDot, type StatusDotProps, Success, SuccessCheck, type SuccessCheckProps, type SuccessCountdown, SuccessPanel, type SuccessPanelProps, type SuccessProps, Switch, type SwitchProps, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, Tooltip, type TooltipProps, sortTableRows, useToast };
825
+ export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonLinkProps, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, Checkbox, type CheckboxProps, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, Field, type FieldControlProps, type FieldProps, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, type NativeButtonProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, Pill, type PillProps, RowIdentity, type RowIdentityProps, SearchField, type SearchFieldProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, SidebarNav, type SidebarNavBadge, type SidebarNavItem, type SidebarNavLeaf, type SidebarNavProps, type SidebarNavSection, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, StatusDot, type StatusDotProps, Success, SuccessCheck, type SuccessCheckProps, type SuccessCountdown, SuccessPanel, type SuccessPanelProps, type SuccessProps, Switch, type SwitchProps, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, Tooltip, type TooltipProps, sortTableRows, useToast };
@@ -79,7 +79,7 @@ var cx = (...a) => a.filter(Boolean).join(" ");
79
79
  var Button = forwardRef3(function Button2({
80
80
  variant = "secondary",
81
81
  size = "md",
82
- icon: icon6,
82
+ icon: icon7,
83
83
  leading,
84
84
  iconRight,
85
85
  iconOnly,
@@ -143,7 +143,7 @@ var Button = forwardRef3(function Button2({
143
143
  className
144
144
  );
145
145
  const labelled = rest["aria-label"] != null || rest["aria-labelledby"] != null;
146
- const fallback = typeof children === "string" && children.trim() ? children.trim() : icon6;
146
+ const fallback = typeof children === "string" && children.trim() ? children.trim() : icon7;
147
147
  const named = iconOnly && !labelled && fallback ? { "aria-label": fallback, title: rest.title ?? fallback } : {};
148
148
  const Root = href === void 0 ? "button" : "a";
149
149
  return /* @__PURE__ */ jsxs3(
@@ -188,7 +188,7 @@ var Button = forwardRef3(function Button2({
188
188
  if (!blockKey(event)) onKeyUp?.(event);
189
189
  },
190
190
  children: [
191
- leading != null ? /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: leading }) : icon6 && /* @__PURE__ */ jsx4(Icon, { name: icon6 }),
191
+ leading != null ? /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: leading }) : icon7 && /* @__PURE__ */ jsx4(Icon, { name: icon7 }),
192
192
  !iconOnly && renderedChildren != null && /* @__PURE__ */ jsx4("span", { className: "ui-btn__label-slot", children: /* @__PURE__ */ jsx4("span", { className: "ui-btn__label", ref: labelRef, children: renderedChildren }) }),
193
193
  iconRight && /* @__PURE__ */ jsx4(Icon, { name: iconRight }),
194
194
  busy && /* @__PURE__ */ jsxs3("span", { className: "ui-btn__dots", "aria-hidden": "true", children: [
@@ -258,9 +258,9 @@ function Card({ title, sub, level = 2, children }) {
258
258
  // src/primitives/Callout.tsx
259
259
  import { calloutIcons, icon as kitIcon } from "@apliteni/apliteni-ui";
260
260
  import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
261
- function Callout({ variant, icon: icon6 = calloutIcons[variant ?? "neutral"], children, actions }) {
261
+ function Callout({ variant, icon: icon7 = calloutIcons[variant ?? "neutral"], children, actions }) {
262
262
  return /* @__PURE__ */ jsxs5("div", { className: `ui-callout${variant && variant !== "neutral" ? ` ui-callout--${variant}` : ""}`, role: variant === "danger" ? "alert" : void 0, children: [
263
- /* @__PURE__ */ jsx9("span", { className: "ui-callout__icon", dangerouslySetInnerHTML: { __html: kitIcon(icon6) } }),
263
+ /* @__PURE__ */ jsx9("span", { className: "ui-callout__icon", dangerouslySetInnerHTML: { __html: kitIcon(icon7) } }),
264
264
  /* @__PURE__ */ jsxs5("div", { className: "ui-callout__body", children: [
265
265
  children,
266
266
  actions != null && /* @__PURE__ */ jsx9("div", { className: "ui-toolbar ui-callout__actions", children: actions })
@@ -1429,12 +1429,12 @@ function Denied({
1429
1429
  title = "You don\u2019t have access",
1430
1430
  sub,
1431
1431
  need,
1432
- icon: icon6 = "lock",
1432
+ icon: icon7 = "lock",
1433
1433
  className,
1434
1434
  children
1435
1435
  }) {
1436
1436
  return /* @__PURE__ */ jsxs14("div", { className: cx4("ui-denied", className), children: [
1437
- /* @__PURE__ */ jsx18("div", { className: "ui-denied__seal", children: /* @__PURE__ */ jsx18(Icon, { name: icon6 }) }),
1437
+ /* @__PURE__ */ jsx18("div", { className: "ui-denied__seal", children: /* @__PURE__ */ jsx18(Icon, { name: icon7 }) }),
1438
1438
  /* @__PURE__ */ jsx18("div", { className: "ui-denied__title", children: title }),
1439
1439
  sub && /* @__PURE__ */ jsx18("div", { className: "ui-denied__sub", children: sub }),
1440
1440
  need && /* @__PURE__ */ jsxs14("div", { className: "ui-denied__need", children: [
@@ -2362,10 +2362,30 @@ function FileField({ label, hint, error, required, disabled, onFileChange, class
2362
2362
  ) });
2363
2363
  }
2364
2364
 
2365
+ // src/SearchField.tsx
2366
+ import { forwardRef as forwardRef7 } from "react";
2367
+ import { icon as icon5 } from "@apliteni/apliteni-ui";
2368
+ import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
2369
+ var SearchField = forwardRef7(function SearchField2({ ariaLabel, className, ...props }, ref) {
2370
+ return /* @__PURE__ */ jsxs24("div", { className: "ui-input-group", children: [
2371
+ /* @__PURE__ */ jsx29("span", { className: "ui-input-group__icon", dangerouslySetInnerHTML: { __html: icon5("search") } }),
2372
+ /* @__PURE__ */ jsx29(
2373
+ "input",
2374
+ {
2375
+ ref,
2376
+ ...props,
2377
+ type: "search",
2378
+ "aria-label": ariaLabel,
2379
+ className: ["ui-input", className].filter(Boolean).join(" ")
2380
+ }
2381
+ )
2382
+ ] });
2383
+ });
2384
+
2365
2385
  // src/Toast.tsx
2366
2386
  import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useEffect as useEffect11, useLayoutEffect as useLayoutEffect4, useRef as useRef16, useState as useState10 } from "react";
2367
2387
  import { createPortal as createPortal4 } from "react-dom";
2368
- import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
2388
+ import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
2369
2389
  var Context = createContext2(null);
2370
2390
  var glyphs = { success: "circleCheck", danger: "circleX", warn: "circleAlert", info: "info", neutral: "bolt" };
2371
2391
  function useToast() {
@@ -2411,7 +2431,7 @@ function Notice({ notice, remove }) {
2411
2431
  remaining.current = Math.max(0, remaining.current - (Date.now() - started));
2412
2432
  };
2413
2433
  }, [dismiss, leaving2, notice.action, paused]);
2414
- return /* @__PURE__ */ jsxs24(
2434
+ return /* @__PURE__ */ jsxs25(
2415
2435
  "div",
2416
2436
  {
2417
2437
  ref: root,
@@ -2425,12 +2445,12 @@ function Notice({ notice, remove }) {
2425
2445
  if (!event.currentTarget.contains(event.relatedTarget)) setFocused(false);
2426
2446
  },
2427
2447
  children: [
2428
- /* @__PURE__ */ jsx29("span", { className: "ui-toast__icon", children: /* @__PURE__ */ jsx29(Icon, { name: glyphs[tone] }) }),
2429
- /* @__PURE__ */ jsxs24("div", { className: "ui-toast__body", children: [
2430
- /* @__PURE__ */ jsx29("div", { className: "ui-toast__title", children: notice.title }),
2431
- notice.text && /* @__PURE__ */ jsx29("div", { className: "ui-toast__text", children: notice.text })
2448
+ /* @__PURE__ */ jsx30("span", { className: "ui-toast__icon", children: /* @__PURE__ */ jsx30(Icon, { name: glyphs[tone] }) }),
2449
+ /* @__PURE__ */ jsxs25("div", { className: "ui-toast__body", children: [
2450
+ /* @__PURE__ */ jsx30("div", { className: "ui-toast__title", children: notice.title }),
2451
+ notice.text && /* @__PURE__ */ jsx30("div", { className: "ui-toast__text", children: notice.text })
2432
2452
  ] }),
2433
- notice.action && /* @__PURE__ */ jsx29(
2453
+ notice.action && /* @__PURE__ */ jsx30(
2434
2454
  "button",
2435
2455
  {
2436
2456
  type: "button",
@@ -2445,8 +2465,8 @@ function Notice({ notice, remove }) {
2445
2465
  children: notice.action.label
2446
2466
  }
2447
2467
  ),
2448
- /* @__PURE__ */ jsx29("button", { type: "button", className: "ui-toast__close", "aria-label": "Dismiss", disabled: leaving2, onClick: dismiss, children: /* @__PURE__ */ jsx29(Icon, { name: "x" }) }),
2449
- !notice.action && /* @__PURE__ */ jsx29(
2468
+ /* @__PURE__ */ jsx30("button", { type: "button", className: "ui-toast__close", "aria-label": "Dismiss", disabled: leaving2, onClick: dismiss, children: /* @__PURE__ */ jsx30(Icon, { name: "x" }) }),
2469
+ !notice.action && /* @__PURE__ */ jsx30(
2450
2470
  "span",
2451
2471
  {
2452
2472
  className: "ui-toast__timer is-running",
@@ -2488,10 +2508,10 @@ function Toast({ children }) {
2488
2508
  setEntries((current) => [...current, { id, notice, remove }]);
2489
2509
  }, []);
2490
2510
  usePublishedReach(stack2, entries.length);
2491
- return /* @__PURE__ */ jsxs24(Context.Provider, { value: push, children: [
2511
+ return /* @__PURE__ */ jsxs25(Context.Provider, { value: push, children: [
2492
2512
  children,
2493
2513
  typeof document !== "undefined" && createPortal4(
2494
- /* @__PURE__ */ jsx29("div", { className: "ui-toast-stack rx-toast-stack", ref: stack2, children: entries.map((entry) => /* @__PURE__ */ jsx29(Notice, { notice: entry.notice, remove: entry.remove }, entry.id)) }),
2514
+ /* @__PURE__ */ jsx30("div", { className: "ui-toast-stack rx-toast-stack", ref: stack2, children: entries.map((entry) => /* @__PURE__ */ jsx30(Notice, { notice: entry.notice, remove: entry.remove }, entry.id)) }),
2495
2515
  document.body
2496
2516
  )
2497
2517
  ] });
@@ -2502,9 +2522,9 @@ import { useEffect as useEffect13, useId as useId12, useState as useState12 } fr
2502
2522
  import { applyTheme, paletteHotkey, prism, railCollapsed, themeIcon, themeName } from "@apliteni/apliteni-ui";
2503
2523
 
2504
2524
  // src/SidebarNav.tsx
2505
- import { forwardRef as forwardRef7, useEffect as useEffect12, useId as useId11, useState as useState11 } from "react";
2525
+ import { forwardRef as forwardRef8, useEffect as useEffect12, useId as useId11, useState as useState11 } from "react";
2506
2526
  import { ENTRANCE_FALLBACK_MS } from "@apliteni/apliteni-ui/motion";
2507
- import { Fragment as Fragment7, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
2527
+ import { Fragment as Fragment7, jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
2508
2528
  var badgeText = (badge) => typeof badge === "object" ? badge.text : badge;
2509
2529
  function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
2510
2530
  const uid = useId11();
@@ -2530,15 +2550,15 @@ function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
2530
2550
  item.disabled && "is-disabled",
2531
2551
  item.danger && "is-danger"
2532
2552
  ].filter(Boolean).join(" ");
2533
- const children = /* @__PURE__ */ jsxs25(Fragment7, { children: [
2534
- (item.leading ?? item.icon) && /* @__PURE__ */ jsx30("span", { className: "ui-nav__ic", children: item.leading ?? /* @__PURE__ */ jsx30(Icon, { name: item.icon }) }),
2535
- /* @__PURE__ */ jsx30("span", { className: "ui-nav__label", children: item.label }),
2536
- count != null && count !== "" && /* @__PURE__ */ jsx30("span", { className: `ui-nav__badge is-${typeof item.badge === "object" ? item.badge.tone ?? "neutral" : "neutral"}`, children: count }),
2537
- group && /* @__PURE__ */ jsx30("span", { className: "ui-nav__caret", "aria-hidden": "true" })
2553
+ const children = /* @__PURE__ */ jsxs26(Fragment7, { children: [
2554
+ (item.leading ?? item.icon) && /* @__PURE__ */ jsx31("span", { className: "ui-nav__ic", children: item.leading ?? /* @__PURE__ */ jsx31(Icon, { name: item.icon }) }),
2555
+ /* @__PURE__ */ jsx31("span", { className: "ui-nav__label", children: item.label }),
2556
+ count != null && count !== "" && /* @__PURE__ */ jsx31("span", { className: `ui-nav__badge is-${typeof item.badge === "object" ? item.badge.tone ?? "neutral" : "neutral"}`, children: count }),
2557
+ group && /* @__PURE__ */ jsx31("span", { className: "ui-nav__caret", "aria-hidden": "true" })
2538
2558
  ] });
2539
2559
  const common = { className, "aria-label": name, title: collapsed ? name : void 0, children };
2540
- if (group) return /* @__PURE__ */ jsxs25("li", { className: `ui-nav__group${open ? " is-open" : ""}`, children: [
2541
- /* @__PURE__ */ jsx30(
2560
+ if (group) return /* @__PURE__ */ jsxs26("li", { className: `ui-nav__group${open ? " is-open" : ""}`, children: [
2561
+ /* @__PURE__ */ jsx31(
2542
2562
  "button",
2543
2563
  {
2544
2564
  ...common,
@@ -2552,25 +2572,25 @@ function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
2552
2572
  }
2553
2573
  }
2554
2574
  ),
2555
- /* @__PURE__ */ jsx30("ul", { id: uid, className: `ui-nav__sub${entering ? " is-entering" : ""}`, hidden: !open, onAnimationEnd: () => setEntering(false), children: item.items.map((child) => /* @__PURE__ */ jsx30(Row, { item: child, active, activeIs, collapsed, renderLink, sub: true }, child.id)) })
2575
+ /* @__PURE__ */ jsx31("ul", { id: uid, className: `ui-nav__sub${entering ? " is-entering" : ""}`, hidden: !open, onAnimationEnd: () => setEntering(false), children: item.items.map((child) => /* @__PURE__ */ jsx31(Row, { item: child, active, activeIs, collapsed, renderLink, sub: true }, child.id)) })
2556
2576
  ] });
2557
- if (item.disabled) return /* @__PURE__ */ jsx30("li", { children: /* @__PURE__ */ jsx30("span", { ...common, role: "link", "aria-disabled": "true" }) });
2577
+ if (item.disabled) return /* @__PURE__ */ jsx31("li", { children: /* @__PURE__ */ jsx31("span", { ...common, role: "link", "aria-disabled": "true" }) });
2558
2578
  const props = {
2559
2579
  ...common,
2560
2580
  href: item.href ?? `#${item.id}`,
2561
2581
  target: item.target,
2562
2582
  "aria-current": isActive ? activeIs === "section" ? "true" : "page" : void 0
2563
2583
  };
2564
- return /* @__PURE__ */ jsx30("li", { children: renderLink ? renderLink(item, props) : /* @__PURE__ */ jsx30("a", { ...props }) });
2584
+ return /* @__PURE__ */ jsx31("li", { children: renderLink ? renderLink(item, props) : /* @__PURE__ */ jsx31("a", { ...props }) });
2565
2585
  }
2566
2586
  function Section({ section, ...props }) {
2567
2587
  const uid = useId11();
2568
- return /* @__PURE__ */ jsxs25("div", { className: "ui-nav__section", children: [
2569
- section.label && /* @__PURE__ */ jsx30("div", { className: "ui-nav__cap", id: uid, "aria-hidden": props.collapsed || void 0, children: section.label }),
2570
- /* @__PURE__ */ jsx30("ul", { className: "ui-nav__list", "aria-labelledby": section.label ? uid : void 0, children: section.items.map((item) => /* @__PURE__ */ jsx30(Row, { item, ...props }, item.id)) })
2588
+ return /* @__PURE__ */ jsxs26("div", { className: "ui-nav__section", children: [
2589
+ section.label && /* @__PURE__ */ jsx31("div", { className: "ui-nav__cap", id: uid, "aria-hidden": props.collapsed || void 0, children: section.label }),
2590
+ /* @__PURE__ */ jsx31("ul", { className: "ui-nav__list", "aria-labelledby": section.label ? uid : void 0, children: section.items.map((item) => /* @__PURE__ */ jsx31(Row, { item, ...props }, item.id)) })
2571
2591
  ] });
2572
2592
  }
2573
- var SidebarNav = forwardRef7(function SidebarNav2({
2593
+ var SidebarNav = forwardRef8(function SidebarNav2({
2574
2594
  items = [],
2575
2595
  sections,
2576
2596
  active,
@@ -2582,8 +2602,8 @@ var SidebarNav = forwardRef7(function SidebarNav2({
2582
2602
  "aria-label": label = "Sidebar",
2583
2603
  ...rest
2584
2604
  }, ref) {
2585
- return /* @__PURE__ */ jsxs25("nav", { ...rest, ref, "aria-label": label, className: ["ui-nav", "ui-nav--side", collapsed && "is-collapsed", className].filter(Boolean).join(" "), children: [
2586
- (sections?.length ? sections : [{ items }]).map((section, index) => /* @__PURE__ */ jsx30(
2605
+ return /* @__PURE__ */ jsxs26("nav", { ...rest, ref, "aria-label": label, className: ["ui-nav", "ui-nav--side", collapsed && "is-collapsed", className].filter(Boolean).join(" "), children: [
2606
+ (sections?.length ? sections : [{ items }]).map((section, index) => /* @__PURE__ */ jsx31(
2587
2607
  Section,
2588
2608
  {
2589
2609
  section,
@@ -2594,12 +2614,12 @@ var SidebarNav = forwardRef7(function SidebarNav2({
2594
2614
  },
2595
2615
  index
2596
2616
  )),
2597
- footer && /* @__PURE__ */ jsx30("div", { className: "ui-nav__foot", children: footer })
2617
+ footer && /* @__PURE__ */ jsx31("div", { className: "ui-nav__foot", children: footer })
2598
2618
  ] });
2599
2619
  });
2600
2620
 
2601
2621
  // src/AppShell.tsx
2602
- import { Fragment as Fragment8, jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
2622
+ import { Fragment as Fragment8, jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
2603
2623
  var path = (value) => value.split(/[?#]/)[0].replace(/\/+$/, "") || "/";
2604
2624
  function AppShell({
2605
2625
  sections,
@@ -2672,15 +2692,15 @@ function AppShell({
2672
2692
  onClick: (event) => {
2673
2693
  if (!event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) setMore(false);
2674
2694
  },
2675
- children: /* @__PURE__ */ jsxs26(Fragment8, { children: [
2676
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx31(Icon, { name: section.icon }) }),
2677
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__label", children: section.label }),
2678
- section.count != null && /* @__PURE__ */ jsx31("span", { className: "ui-nav__badge is-neutral", children: section.count })
2695
+ children: /* @__PURE__ */ jsxs27(Fragment8, { children: [
2696
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx32(Icon, { name: section.icon }) }),
2697
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__label", children: section.label }),
2698
+ section.count != null && /* @__PURE__ */ jsx32("span", { className: "ui-nav__badge is-neutral", children: section.count })
2679
2699
  ] })
2680
2700
  };
2681
- return /* @__PURE__ */ jsx31("li", { children: renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx31("a", { ...linkProps }) }, section.href);
2701
+ return /* @__PURE__ */ jsx32("li", { children: renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx32("a", { ...linkProps }) }, section.href);
2682
2702
  });
2683
- const sidebar = (items, label) => /* @__PURE__ */ jsx31(
2703
+ const sidebar = (items, label) => /* @__PURE__ */ jsx32(
2684
2704
  SidebarNav,
2685
2705
  {
2686
2706
  "aria-label": label,
@@ -2692,7 +2712,7 @@ function AppShell({
2692
2712
  const linkProps = { ...props, onClick: (event) => {
2693
2713
  if (!event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) setMore(false);
2694
2714
  } };
2695
- return renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx31("a", { ...linkProps });
2715
+ return renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx32("a", { ...linkProps });
2696
2716
  }
2697
2717
  }
2698
2718
  );
@@ -2700,33 +2720,33 @@ function AppShell({
2700
2720
  const visible = sections.length > 4 ? sections.slice(0, 3) : sections;
2701
2721
  const remaining = sections.slice(visible.length);
2702
2722
  const initials = account.name.trim().split(/\s+/).map((part) => part[0]).slice(0, 2).join("");
2703
- return /* @__PURE__ */ jsxs26("div", { className: `ui-app ui-app--topbar ui-react-app${collapsed ? " is-collapsed" : ""}`, children: [
2704
- /* @__PURE__ */ jsxs26("div", { className: "ui-app__rail", children: [
2705
- /* @__PURE__ */ jsx31("div", { className: "ui-app__head", children: /* @__PURE__ */ jsxs26("a", { className: "ui-app__brand", href: brandHref, "aria-label": word, children: [
2706
- brand ?? /* @__PURE__ */ jsx31("i", { "aria-hidden": "true", className: "ui-react-app__mark", dangerouslySetInnerHTML: { __html: prism(uid, 24) } }),
2707
- /* @__PURE__ */ jsx31("span", { children: word })
2723
+ return /* @__PURE__ */ jsxs27("div", { className: `ui-app ui-app--topbar ui-react-app${collapsed ? " is-collapsed" : ""}`, children: [
2724
+ /* @__PURE__ */ jsxs27("div", { className: "ui-app__rail", children: [
2725
+ /* @__PURE__ */ jsx32("div", { className: "ui-app__head", children: /* @__PURE__ */ jsxs27("a", { className: "ui-app__brand", href: brandHref, "aria-label": word, children: [
2726
+ brand ?? /* @__PURE__ */ jsx32("i", { "aria-hidden": "true", className: "ui-react-app__mark", dangerouslySetInnerHTML: { __html: prism(uid, 24) } }),
2727
+ /* @__PURE__ */ jsx32("span", { children: word })
2708
2728
  ] }) }),
2709
2729
  sidebar(sections, "Sections"),
2710
- /* @__PURE__ */ jsx31("div", { className: "ui-app__foot", children: /* @__PURE__ */ jsx31("div", { className: "ui-app__fold-row", children: /* @__PURE__ */ jsxs26("button", { type: "button", className: "ui-nav__item ui-app__fold", "aria-expanded": !collapsed, "aria-label": foldLabel, onClick: fold, children: [
2711
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsxs26("svg", { className: "ui-icon", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2712
- /* @__PURE__ */ jsx31("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
2713
- /* @__PURE__ */ jsx31("path", { className: "ui-app__fold-seam", d: "M9 3v18" }),
2714
- /* @__PURE__ */ jsx31("path", { className: "ui-app__fold-arrow", d: "m16 15-3-3 3-3" })
2730
+ /* @__PURE__ */ jsx32("div", { className: "ui-app__foot", children: /* @__PURE__ */ jsx32("div", { className: "ui-app__fold-row", children: /* @__PURE__ */ jsxs27("button", { type: "button", className: "ui-nav__item ui-app__fold", "aria-expanded": !collapsed, "aria-label": foldLabel, onClick: fold, children: [
2731
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsxs27("svg", { className: "ui-icon", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2732
+ /* @__PURE__ */ jsx32("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
2733
+ /* @__PURE__ */ jsx32("path", { className: "ui-app__fold-seam", d: "M9 3v18" }),
2734
+ /* @__PURE__ */ jsx32("path", { className: "ui-app__fold-arrow", d: "m16 15-3-3 3-3" })
2715
2735
  ] }) }),
2716
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__label", children: foldLabel })
2736
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__label", children: foldLabel })
2717
2737
  ] }) }) })
2718
2738
  ] }),
2719
- /* @__PURE__ */ jsxs26("div", { className: "ui-app__well", children: [
2720
- /* @__PURE__ */ jsxs26("header", { className: "ui-app__bar", children: [
2721
- /* @__PURE__ */ jsxs26("button", { type: "button", className: "ui-app__search", "aria-haspopup": "dialog", onClick: () => setSearch(true), children: [
2722
- /* @__PURE__ */ jsx31("span", { className: "ui-app__search-ic", children: /* @__PURE__ */ jsx31(Icon, { name: "search" }) }),
2723
- /* @__PURE__ */ jsx31("span", { className: "ui-app__search-txt", children: "Search or run a command\u2026" }),
2724
- /* @__PURE__ */ jsx31("kbd", { className: "ui-cmdk__key", children: paletteHotkey() })
2739
+ /* @__PURE__ */ jsxs27("div", { className: "ui-app__well", children: [
2740
+ /* @__PURE__ */ jsxs27("header", { className: "ui-app__bar", children: [
2741
+ /* @__PURE__ */ jsxs27("button", { type: "button", className: "ui-app__search", "aria-haspopup": "dialog", onClick: () => setSearch(true), children: [
2742
+ /* @__PURE__ */ jsx32("span", { className: "ui-app__search-ic", children: /* @__PURE__ */ jsx32(Icon, { name: "search" }) }),
2743
+ /* @__PURE__ */ jsx32("span", { className: "ui-app__search-txt", children: "Search or run a command\u2026" }),
2744
+ /* @__PURE__ */ jsx32("kbd", { className: "ui-cmdk__key", children: paletteHotkey() })
2725
2745
  ] }),
2726
- /* @__PURE__ */ jsx31("span", { className: "ui-app__bar-gap" }),
2746
+ /* @__PURE__ */ jsx32("span", { className: "ui-app__bar-gap" }),
2727
2747
  bandControl,
2728
- /* @__PURE__ */ jsx31("button", { type: "button", className: "toggle", "aria-label": themeName(theme), title: themeName(theme), onClick: () => applyTheme(theme === "light" ? "dark" : "light"), children: /* @__PURE__ */ jsx31("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: themeIcon(theme) } }) }),
2729
- /* @__PURE__ */ jsx31("div", { className: "ui-app__user ui-app__user--bar", children: /* @__PURE__ */ jsx31(
2748
+ /* @__PURE__ */ jsx32("button", { type: "button", className: "toggle", "aria-label": themeName(theme), title: themeName(theme), onClick: () => applyTheme(theme === "light" ? "dark" : "light"), children: /* @__PURE__ */ jsx32("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: themeIcon(theme) } }) }),
2749
+ /* @__PURE__ */ jsx32("div", { className: "ui-app__user ui-app__user--bar", children: /* @__PURE__ */ jsx32(
2730
2750
  Dropdown,
2731
2751
  {
2732
2752
  variant: "menu",
@@ -2735,27 +2755,27 @@ function AppShell({
2735
2755
  ariaLabel: `Signed in as ${account.name}, ${account.email}`,
2736
2756
  triggerClass: "ui-app__user-trigger",
2737
2757
  panelClass: "ui-app__user-panel",
2738
- triggerContent: /* @__PURE__ */ jsx31("span", { className: "ui-app__av", "aria-hidden": "true", children: initials }),
2739
- header: /* @__PURE__ */ jsxs26("div", { className: "ui-dropdown__head", children: [
2740
- /* @__PURE__ */ jsx31("b", { children: account.name }),
2741
- /* @__PURE__ */ jsx31("span", { children: account.email })
2758
+ triggerContent: /* @__PURE__ */ jsx32("span", { className: "ui-app__av", "aria-hidden": "true", children: initials }),
2759
+ header: /* @__PURE__ */ jsxs27("div", { className: "ui-dropdown__head", children: [
2760
+ /* @__PURE__ */ jsx32("b", { children: account.name }),
2761
+ /* @__PURE__ */ jsx32("span", { children: account.email })
2742
2762
  ] }),
2743
2763
  items: [{ label: "Sign out", icon: "logout", danger: true }],
2744
2764
  onSelect: onSignOut
2745
2765
  }
2746
2766
  ) })
2747
2767
  ] }),
2748
- /* @__PURE__ */ jsxs26("main", { className: `ui-app__main${width === "wide" ? " ui-app__main--wide" : ""}`, children: [
2749
- back && /* @__PURE__ */ jsx31(BackLink, { ...back }),
2750
- /* @__PURE__ */ jsx31("h1", { children: title }),
2751
- lede && /* @__PURE__ */ jsx31("p", { className: "ui-app__sub", children: lede }),
2752
- actions && /* @__PURE__ */ jsx31("div", { className: "ui-react-app__actions", children: actions }),
2753
- /* @__PURE__ */ jsx31("div", { className: "ui-app__body", children })
2768
+ /* @__PURE__ */ jsxs27("main", { className: `ui-app__main${width === "wide" ? " ui-app__main--wide" : ""}`, children: [
2769
+ back && /* @__PURE__ */ jsx32(BackLink, { ...back }),
2770
+ /* @__PURE__ */ jsx32("h1", { children: title }),
2771
+ lede && /* @__PURE__ */ jsx32("p", { className: "ui-app__sub", children: lede }),
2772
+ actions && /* @__PURE__ */ jsx32("div", { className: "ui-react-app__actions", children: actions }),
2773
+ /* @__PURE__ */ jsx32("div", { className: "ui-app__body", children })
2754
2774
  ] })
2755
2775
  ] }),
2756
- /* @__PURE__ */ jsx31("nav", { className: "ui-nav ui-react-app__bottom", "aria-label": "Sections on mobile", children: /* @__PURE__ */ jsxs26("ul", { className: "ui-nav__list", children: [
2776
+ /* @__PURE__ */ jsx32("nav", { className: "ui-nav ui-react-app__bottom", "aria-label": "Sections on mobile", children: /* @__PURE__ */ jsxs27("ul", { className: "ui-nav__list", children: [
2757
2777
  links(visible),
2758
- remaining.length > 0 && /* @__PURE__ */ jsx31("li", { children: /* @__PURE__ */ jsxs26(
2778
+ remaining.length > 0 && /* @__PURE__ */ jsx32("li", { children: /* @__PURE__ */ jsxs27(
2759
2779
  "button",
2760
2780
  {
2761
2781
  type: "button",
@@ -2764,43 +2784,43 @@ function AppShell({
2764
2784
  "aria-expanded": more,
2765
2785
  onClick: () => setMore(true),
2766
2786
  children: [
2767
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx31(Icon, { name: "moreHorizontal" }) }),
2768
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__label", children: "More" })
2787
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx32(Icon, { name: "moreHorizontal" }) }),
2788
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__label", children: "More" })
2769
2789
  ]
2770
2790
  }
2771
2791
  ) })
2772
2792
  ] }) }),
2773
- /* @__PURE__ */ jsx31(Drawer, { open: more, onClose: () => setMore(false), title: "More sections", side: "bottom", children: sidebar(remaining, "More sections") }),
2774
- /* @__PURE__ */ jsx31(CommandPalette, { ...palette ?? { groups: [{ items: sections.map((section) => ({ id: section.href, ...section })) }] }, open: search, onClose: () => setSearch(false) })
2793
+ /* @__PURE__ */ jsx32(Drawer, { open: more, onClose: () => setMore(false), title: "More sections", side: "bottom", children: sidebar(remaining, "More sections") }),
2794
+ /* @__PURE__ */ jsx32(CommandPalette, { ...palette ?? { groups: [{ items: sections.map((section) => ({ id: section.href, ...section })) }] }, open: search, onClose: () => setSearch(false) })
2775
2795
  ] });
2776
2796
  }
2777
2797
 
2778
2798
  // src/EmptyState.tsx
2779
2799
  import { icon as kitIcon2, illo } from "@apliteni/apliteni-ui";
2780
- import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
2800
+ import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
2781
2801
  var cases = {
2782
2802
  "first-run": { title: "Nothing needs you yet", sub: "New items will appear here when they need your attention.", icon: "check" },
2783
2803
  "no-matches": { title: "No results in this view", sub: "Try another search or clear your filters.", icon: "search" },
2784
2804
  "not-found": { title: "We can't find that page", sub: "Check the address or return to the home page.", icon: "folder" },
2785
2805
  "not-yet-built": { title: "This ships later", sub: "This screen is not available yet. Check back later.", icon: "clock" }
2786
2806
  };
2787
- function EmptyState({ variant = "first-run", title, sub, icon: icon6, art, actions }) {
2807
+ function EmptyState({ variant = "first-run", title, sub, icon: icon7, art, actions }) {
2788
2808
  const preset = cases[variant];
2789
2809
  const heading = title ?? preset.title;
2790
2810
  const guidance = sub ?? preset.sub;
2791
- const glyph = icon6 ?? preset.icon;
2792
- return /* @__PURE__ */ jsxs27("div", { className: "ui-empty", children: [
2793
- art ? typeof art === "string" ? /* @__PURE__ */ jsx32("div", { className: "ui-empty__art", dangerouslySetInnerHTML: { __html: /<svg/.test(art) ? art : illo(art) } }) : /* @__PURE__ */ jsx32("div", { className: "ui-empty__art", "aria-hidden": "true", children: art }) : glyph ? /* @__PURE__ */ jsx32("div", { className: "ui-empty__icon", dangerouslySetInnerHTML: { __html: kitIcon2(glyph) } }) : null,
2794
- heading && /* @__PURE__ */ jsx32("div", { className: "ui-empty__title", children: heading }),
2795
- guidance && /* @__PURE__ */ jsx32("div", { className: "ui-empty__sub", children: guidance }),
2796
- actions && /* @__PURE__ */ jsx32("div", { className: "ui-empty__actions", children: actions })
2811
+ const glyph = icon7 ?? preset.icon;
2812
+ return /* @__PURE__ */ jsxs28("div", { className: "ui-empty", children: [
2813
+ art ? typeof art === "string" ? /* @__PURE__ */ jsx33("div", { className: "ui-empty__art", dangerouslySetInnerHTML: { __html: /<svg/.test(art) ? art : illo(art) } }) : /* @__PURE__ */ jsx33("div", { className: "ui-empty__art", "aria-hidden": "true", children: art }) : glyph ? /* @__PURE__ */ jsx33("div", { className: "ui-empty__icon", dangerouslySetInnerHTML: { __html: kitIcon2(glyph) } }) : null,
2814
+ heading && /* @__PURE__ */ jsx33("div", { className: "ui-empty__title", children: heading }),
2815
+ guidance && /* @__PURE__ */ jsx33("div", { className: "ui-empty__sub", children: guidance }),
2816
+ actions && /* @__PURE__ */ jsx33("div", { className: "ui-empty__actions", children: actions })
2797
2817
  ] });
2798
2818
  }
2799
2819
 
2800
2820
  // src/ThemeToggle.tsx
2801
2821
  import { useSyncExternalStore } from "react";
2802
- import { icon as icon5 } from "@apliteni/apliteni-ui";
2803
- import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
2822
+ import { icon as icon6 } from "@apliteni/apliteni-ui";
2823
+ import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
2804
2824
  var STORAGE_KEY = "apliteni-strategy-theme";
2805
2825
  var CHOICE_EVENT = "apliteni-theme-choice";
2806
2826
  var THEME_INIT_SCRIPT = `(()=>{let t;try{t=localStorage.getItem('apliteni-strategy-theme')}catch{}document.documentElement.setAttribute('data-theme',t==='dark'||t==='light'?t:window.matchMedia?.('(prefers-color-scheme: light)').matches?'light':'dark')})()`;
@@ -2858,10 +2878,10 @@ function ThemeToggle({ labelled = false }) {
2858
2878
  }
2859
2879
  window.dispatchEvent(new Event(CHOICE_EVENT));
2860
2880
  };
2861
- const button = /* @__PURE__ */ jsx33("button", { type: "button", className: "toggle", "aria-label": name, title: name, onClick: toggle, children: /* @__PURE__ */ jsx33("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: icon5(theme === "auto" ? "monitor" : theme === "light" ? "sun" : "moon") } }) });
2862
- return labelled ? /* @__PURE__ */ jsxs28("span", { className: "ui-theme-toggle", children: [
2881
+ const button = /* @__PURE__ */ jsx34("button", { type: "button", className: "toggle", "aria-label": name, title: name, onClick: toggle, children: /* @__PURE__ */ jsx34("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: icon6(theme === "auto" ? "monitor" : theme === "light" ? "sun" : "moon") } }) });
2882
+ return labelled ? /* @__PURE__ */ jsxs29("span", { className: "ui-theme-toggle", children: [
2863
2883
  button,
2864
- /* @__PURE__ */ jsxs28("span", { children: [
2884
+ /* @__PURE__ */ jsxs29("span", { children: [
2865
2885
  label,
2866
2886
  " theme"
2867
2887
  ] })
@@ -2869,11 +2889,11 @@ function ThemeToggle({ labelled = false }) {
2869
2889
  }
2870
2890
 
2871
2891
  // src/Success.tsx
2872
- import { forwardRef as forwardRef8, useEffect as useEffect14, useRef as useRef17, useState as useState13 } from "react";
2873
- import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
2874
- var SuccessCheck = forwardRef8(function SuccessCheck2({ variant = "line", className, ...rest }, ref) {
2892
+ import { forwardRef as forwardRef9, useEffect as useEffect14, useRef as useRef17, useState as useState13 } from "react";
2893
+ import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
2894
+ var SuccessCheck = forwardRef9(function SuccessCheck2({ variant = "line", className, ...rest }, ref) {
2875
2895
  const circled = variant === "circled";
2876
- return /* @__PURE__ */ jsxs29(
2896
+ return /* @__PURE__ */ jsxs30(
2877
2897
  "svg",
2878
2898
  {
2879
2899
  ...rest,
@@ -2885,18 +2905,18 @@ var SuccessCheck = forwardRef8(function SuccessCheck2({ variant = "line", classN
2885
2905
  "aria-hidden": "true",
2886
2906
  focusable: "false",
2887
2907
  children: [
2888
- circled && /* @__PURE__ */ jsx34("path", { className: "ui-sx__circle", d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
2889
- /* @__PURE__ */ jsx34("path", { className: "ui-sx__tick", d: circled ? "M22 4L12 14.01l-3-3" : "M20 6L9 17l-5-5" })
2908
+ circled && /* @__PURE__ */ jsx35("path", { className: "ui-sx__circle", d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
2909
+ /* @__PURE__ */ jsx35("path", { className: "ui-sx__tick", d: circled ? "M22 4L12 14.01l-3-3" : "M20 6L9 17l-5-5" })
2890
2910
  ]
2891
2911
  }
2892
2912
  );
2893
2913
  });
2894
- var SuccessPanel = forwardRef8(function SuccessPanel2({ title = "Done", sub, check = "line", className, ...rest }, ref) {
2914
+ var SuccessPanel = forwardRef9(function SuccessPanel2({ title = "Done", sub, check = "line", className, ...rest }, ref) {
2895
2915
  const circled = check === "circled";
2896
- return /* @__PURE__ */ jsxs29("div", { ...rest, ref, className: ["ui-success", className].filter(Boolean).join(" "), children: [
2897
- /* @__PURE__ */ jsx34("div", { className: ["ui-success__check", circled && "ui-success__check--circled"].filter(Boolean).join(" "), children: /* @__PURE__ */ jsx34(SuccessCheck, { variant: circled ? "circled" : "line" }) }),
2898
- /* @__PURE__ */ jsx34("div", { className: "ui-success__title", children: title }),
2899
- sub && /* @__PURE__ */ jsx34("div", { className: "ui-success__sub", children: sub })
2916
+ return /* @__PURE__ */ jsxs30("div", { ...rest, ref, className: ["ui-success", className].filter(Boolean).join(" "), children: [
2917
+ /* @__PURE__ */ jsx35("div", { className: ["ui-success__check", circled && "ui-success__check--circled"].filter(Boolean).join(" "), children: /* @__PURE__ */ jsx35(SuccessCheck, { variant: circled ? "circled" : "line" }) }),
2918
+ /* @__PURE__ */ jsx35("div", { className: "ui-success__title", children: title }),
2919
+ sub && /* @__PURE__ */ jsx35("div", { className: "ui-success__sub", children: sub })
2900
2920
  ] });
2901
2921
  });
2902
2922
  var pieces = [
@@ -2933,17 +2953,17 @@ function Countdown({ seconds, label = "Redirecting", onDone }) {
2933
2953
  }, 1e3);
2934
2954
  return () => clearInterval(timer);
2935
2955
  }, [seconds]);
2936
- return /* @__PURE__ */ jsxs29("div", { className: "ui-sx__count", "data-sx-count": "", style: { "--sx-secs": `${seconds}s` }, children: [
2937
- /* @__PURE__ */ jsx34("span", { className: "ui-sx__count-ring", "aria-hidden": "true" }),
2938
- /* @__PURE__ */ jsxs29("span", { className: "ui-sx__count-text", children: [
2956
+ return /* @__PURE__ */ jsxs30("div", { className: "ui-sx__count", "data-sx-count": "", style: { "--sx-secs": `${seconds}s` }, children: [
2957
+ /* @__PURE__ */ jsx35("span", { className: "ui-sx__count-ring", "aria-hidden": "true" }),
2958
+ /* @__PURE__ */ jsxs30("span", { className: "ui-sx__count-text", children: [
2939
2959
  label,
2940
2960
  " in ",
2941
- /* @__PURE__ */ jsx34("b", { "data-sx-num": "", children: remaining }),
2961
+ /* @__PURE__ */ jsx35("b", { "data-sx-num": "", children: remaining }),
2942
2962
  "s"
2943
2963
  ] })
2944
2964
  ] });
2945
2965
  }
2946
- var Success = forwardRef8(function Success2({
2966
+ var Success = forwardRef9(function Success2({
2947
2967
  layout = "hero",
2948
2968
  level,
2949
2969
  check = "line",
@@ -2965,7 +2985,7 @@ var Success = forwardRef8(function Success2({
2965
2985
  const seconds = countdown?.seconds;
2966
2986
  const duration = seconds != null && Number.isFinite(seconds) && seconds >= 1 ? Math.floor(seconds) : 5;
2967
2987
  const mark = check === "circled" ? "circled" : "line";
2968
- return /* @__PURE__ */ jsxs29(
2988
+ return /* @__PURE__ */ jsxs30(
2969
2989
  "div",
2970
2990
  {
2971
2991
  role: "status",
@@ -2974,16 +2994,16 @@ var Success = forwardRef8(function Success2({
2974
2994
  ref,
2975
2995
  className: ["ui-sx", `ui-sx--${layout}`, `ui-sx--check-${mark}`, confetti && "ui-sx--confetti", className].filter(Boolean).join(" "),
2976
2996
  children: [
2977
- confetti && /* @__PURE__ */ jsx34("div", { className: "ui-sx__confetti", "aria-hidden": "true", children: pieces.map(
2978
- ([x, d, r, t, s], index) => /* @__PURE__ */ jsx34("i", { className: `ui-sx__piece ui-sx__piece--${t}`, style: { left: `${x}%`, "--sx-d": `${d}s`, "--sx-r": `${r}deg`, "--sx-s": s } }, index)
2997
+ confetti && /* @__PURE__ */ jsx35("div", { className: "ui-sx__confetti", "aria-hidden": "true", children: pieces.map(
2998
+ ([x, d, r, t, s], index) => /* @__PURE__ */ jsx35("i", { className: `ui-sx__piece ui-sx__piece--${t}`, style: { left: `${x}%`, "--sx-d": `${d}s`, "--sx-r": `${r}deg`, "--sx-s": s } }, index)
2979
2999
  ) }),
2980
- /* @__PURE__ */ jsxs29("div", { className: "ui-sx__inner", children: [
2981
- /* @__PURE__ */ jsx34("div", { className: "ui-sx__visual", children: /* @__PURE__ */ jsx34(SuccessCheck, { variant: mark }) }),
2982
- /* @__PURE__ */ jsxs29("div", { className: "ui-sx__content", children: [
2983
- /* @__PURE__ */ jsx34(Heading, { className: "ui-sx__title", children: title }),
2984
- body && /* @__PURE__ */ jsx34("p", { className: "ui-sx__body", children: body }),
2985
- hasActions && /* @__PURE__ */ jsx34("div", { className: "ui-sx__actions", children: actions }),
2986
- countdown && /* @__PURE__ */ jsx34(Countdown, { seconds: duration, label: countdown.label, onDone: onCountdownEnd }, duration)
3000
+ /* @__PURE__ */ jsxs30("div", { className: "ui-sx__inner", children: [
3001
+ /* @__PURE__ */ jsx35("div", { className: "ui-sx__visual", children: /* @__PURE__ */ jsx35(SuccessCheck, { variant: mark }) }),
3002
+ /* @__PURE__ */ jsxs30("div", { className: "ui-sx__content", children: [
3003
+ /* @__PURE__ */ jsx35(Heading, { className: "ui-sx__title", children: title }),
3004
+ body && /* @__PURE__ */ jsx35("p", { className: "ui-sx__body", children: body }),
3005
+ hasActions && /* @__PURE__ */ jsx35("div", { className: "ui-sx__actions", children: actions }),
3006
+ countdown && /* @__PURE__ */ jsx35(Countdown, { seconds: duration, label: countdown.label, onDone: onCountdownEnd }, duration)
2987
3007
  ] })
2988
3008
  ] })
2989
3009
  ]
@@ -3024,6 +3044,7 @@ export {
3024
3044
  Pagination,
3025
3045
  Pill,
3026
3046
  RowIdentity,
3047
+ SearchField,
3027
3048
  Segmented,
3028
3049
  SelectField,
3029
3050
  SidebarNav,
@@ -67,6 +67,7 @@
67
67
  /* Tone ink keeps actions readable on the neutral surface. */
68
68
  .ui-toast__action { flex: none; align-self: center; background: none; border: 0; padding: 4px 9px; border-radius: var(--radius-xs); font: inherit; font-weight: var(--weight-medium); color: var(--toast-action-ink); cursor: pointer; }
69
69
  .ui-toast__action:hover { background: color-mix(in srgb, var(--bg) 55%, transparent); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
70
+ .ui-toast__action:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
70
71
 
71
72
  .ui-toast__close { flex: none; align-self: flex-start; background: none; border: 0; /* muted-ink: glyph — dismiss icon */ color: var(--muted); cursor: pointer; padding: 2px; display: grid; place-items: center; position: relative; font: inherit; }
72
73
  .ui-toast__close svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2.5; }
@@ -90,6 +91,7 @@
90
91
  transform: translate(-50%, -50%);
91
92
  }
92
93
  .ui-toast__close:hover { color: var(--text); }
94
+ .ui-toast__close:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-radius: var(--radius-xs); }
93
95
 
94
96
  /* auto-dismiss progress bar — .is-running is added by wireToastStack() */
95
97
  .ui-toast__timer { position: absolute; left: 0; bottom: 0; height: 3px; width: 100%; background: var(--toast-accent); opacity: 0.85; transform-origin: left; }
@@ -58,6 +58,7 @@
58
58
  }
59
59
  /* The lift alone: the kit casts no shadow anywhere (#295), and cards are flat (#284). */
60
60
  .ui-card--interactive:hover { transform: translateY(-2px); }
61
+ .ui-card--interactive:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
61
62
 
62
63
  /* card() emits the title as an h2, so the text face is named here rather than
63
64
  left to the element. why: docs/specification.md#labels-and-titles */
@@ -51,6 +51,7 @@
51
51
  }
52
52
  .ui-snippet__copy:hover { color: var(--accent); }
53
53
  .ui-snippet--reveal .ui-snippet__copy:hover { color: var(--strong); }
54
+ .ui-snippet__copy:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-radius: var(--radius-xs); }
54
55
  .ui-snippet__copy svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 2.8; }
55
56
 
56
57
  /* Shell token highlighting (matches viz/account.mjs classes) */
@@ -95,6 +95,15 @@
95
95
  .ui-dropdown__panel.is-end { left: auto; right: 0; }
96
96
  .ui-dropdown__panel.is-scroll { max-height: 300px; overflow-y: auto; }
97
97
 
98
+ /* Chrome makes a scroll container a keyboard stop on its own — no tabindex, no
99
+ author rule — so `.is-scroll` took the browser's outline, black in light mode.
100
+ #457 refused that, and #487's review measured it here. Written for every panel
101
+ rather than the scrolling one, as the palette and the drawer write it, because
102
+ a panel that takes focus any other way has the same claim on the ring. The
103
+ ring is COMPOSED with the floating treatment rather than over it: a box-shadow
104
+ list replaces the whole list. why: docs/specification.md#elevation */
105
+ .ui-dropdown__panel:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring), inset 0 0 0 1px var(--elev-edge, var(--border)), var(--elev-drop); }
106
+
98
107
  /* Opening upward. The kit releases its own `top` here so that nothing outside
99
108
  the kit has to: an absolute box with both edges pinned is stretched between
100
109
  them, and a consumer who set `bottom` and left our `top` standing got a
@@ -78,6 +78,7 @@
78
78
  .ui-fbc__chip svg { width: 14px; height: 14px; flex: none; opacity: .85; stroke-width: 2.7; }
79
79
  .ui-fbc__x { margin-left: auto; background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ /* muted-ink: glyph — dismiss icon */ color: var(--dim); border: 0; width: 28px; height: 28px; border-radius: 8px; cursor: pointer; display: grid; place-items: center; flex: none; font: inherit; }
80
80
  .ui-fbc__x:hover { color: var(--strong); }
81
+ .ui-fbc__x:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
81
82
  .ui-fbc__x svg { width: 14px; height: 14px; stroke-width: 2.7; }
82
83
  .ui-fbc__quote { margin: 14px 20px 0; background: var(--surface); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ border-radius: 12px; padding: 12px 15px; display: flex; gap: 11px; }
83
84
  .ui-fbc__qm { color: var(--accent); font-size: var(--text-xl); font-weight: var(--weight-bold); line-height: .9; flex: none; opacity: .55; }
@@ -94,6 +95,7 @@
94
95
  .ui-fbbtn { font-family: var(--font-sans); font-size: calc(var(--text-sm) + 0.5px); font-weight: var(--weight-semibold); border-radius: 10px; padding: 9px 17px; cursor: pointer; border: 0; transition: filter var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
95
96
  .ui-fbbtn.ghost { background: transparent; color: var(--text); }
96
97
  .ui-fbbtn.ghost:hover { color: var(--strong); background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
98
+ .ui-fbbtn:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
97
99
  .ui-fbbtn.primary { background: var(--accent-strong); color: var(--accent-contrast); display: inline-flex; align-items: center; gap: 9px; }
98
100
  .ui-fbbtn.primary:hover { filter: brightness(1.08); }
99
101
  /* Same flat pair as .ui-btn:disabled, for the same reason (#220). */
@@ -62,6 +62,7 @@
62
62
  transform var(--dur-fast) var(--ease);
63
63
  }
64
64
  .ui-footer__social:hover { color: var(--accent); border-color: var(--accent); transform: translateY(-1px); }
65
+ .ui-footer__social:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
65
66
  .ui-footer__social svg { width: 17px; height: 17px; display: block; }
66
67
 
67
68
  /* Link columns */
@@ -85,6 +86,7 @@
85
86
  transition: color var(--dur-fast) var(--ease);
86
87
  }
87
88
  .ui-footer__links a:hover { color: var(--footer-link-hover); }
89
+ .ui-footer__links a:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-radius: var(--radius-xs); }
88
90
 
89
91
  /* Legal bar — shared by every variant */
90
92
  .ui-footer__bar {
@@ -102,6 +104,7 @@
102
104
  .ui-footer__legal-links { display: flex; gap: 22px; flex-wrap: wrap; }
103
105
  .ui-footer__legal-links a { color: var(--footer-link); text-decoration: none; transition: color var(--dur-fast) var(--ease); }
104
106
  .ui-footer__legal-links a:hover { color: var(--footer-link-hover); }
107
+ .ui-footer__legal-links a:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-radius: var(--radius-xs); }
105
108
  .ui-footer__switcher { display: inline-flex; align-items: center; gap: 12px; margin-left: auto; }
106
109
 
107
110
  /* slim — a single legal row, no divider above it. */
@@ -83,6 +83,7 @@
83
83
  /* Carries the shell's class as well as its own, for the same reason the rail
84
84
  rows do (see nav.css): a host stylesheet's `a:link` is (0,1,1). */
85
85
  .ui-app .ui-app__brand { color: var(--strong); }
86
+ .ui-app__brand:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-radius: var(--radius-xs); }
86
87
  .ui-app__brand svg { display: block; flex: none; }
87
88
 
88
89
  /* The head band: the product's mark, and the rail's own control at the far end of
@@ -441,6 +442,13 @@
441
442
  .ui-toolbar { display: flex; flex-wrap: wrap; gap: 10px; }
442
443
  .ui-toolbar > .ui-input,
443
444
  .ui-toolbar > .ui-input-group { flex: 1 1 6rem; min-width: 0; }
445
+ /* At one column the 6rem basis stops being a floor and becomes the field's size:
446
+ a row with two narrow chips beside it keeps all three on the line and cuts the
447
+ field's own placeholder off. why: docs/specification.md#a-toolbar-at-one-column */
448
+ @media (max-width: 560px) {
449
+ .ui-toolbar > .ui-input,
450
+ .ui-toolbar > .ui-input-group { flex-basis: 100%; }
451
+ }
444
452
 
445
453
  /* -- The narrow rail ------------------------------------------------------ */
446
454
  /* Below this width the rail folds to the icon strip instead of disappearing, and
@@ -41,6 +41,9 @@
41
41
  .brand svg { display: block; flex: none; }
42
42
  /* Topbar wordmark runs a touch smaller than the auth-card lockup. */
43
43
  .topbar .brand { font-size: var(--text-sm); }
44
+ /* The lockup is a link, so it takes focus: the shared ring, with a radius of its
45
+ own because the mark and the word have no box to borrow one from. #482 */
46
+ .brand:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-radius: var(--radius-xs); }
44
47
  .topbar .spacer { flex: 1; }
45
48
 
46
49
  /* Deck / Text (segmented, pill) */
@@ -67,6 +70,7 @@
67
70
  }
68
71
  .dtsw a:hover { color: var(--strong); }
69
72
  .dtsw a.cur { color: var(--strong); background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
73
+ .dtsw a:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
70
74
 
71
75
  /* Theme toggle — a single icon-only switch (no label). */
72
76
  .toggle {
@@ -86,6 +90,7 @@
86
90
  border-color var(--dur-fast) var(--ease);
87
91
  }
88
92
  .toggle:hover { transform: translateY(-1px); border-color: var(--border-strong); }
93
+ .toggle:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
89
94
  .toggle .ic { width: 17px; height: 17px; display: inline-block; color: var(--text); }
90
95
  .toggle .ic svg { display: block; width: 100%; height: 100%; stroke-width: 2.2; }
91
96
 
@@ -107,6 +112,7 @@
107
112
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
108
113
  }
109
114
  .vsw__btn:hover { border-color: var(--accent); }
115
+ .vsw__btn:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
110
116
  .vsw__btn .lbl { color: var(--text); }
111
117
  .vsw__btn .cur { color: var(--strong); font-weight: 600; letter-spacing: 0.01em; }
112
118
  .vsw__btn .car {
@@ -180,9 +186,14 @@
180
186
  transition: background var(--dur-fast) var(--ease);
181
187
  }
182
188
  /* Menu rows use the reading surface and an edge on hover — see
183
- src/styles/dropdown.css:155 `.ui-dropdown__item:hover`.
189
+ src/styles/dropdown.css:164 `.ui-dropdown__item:hover`.
184
190
  why: docs/specification.md#elevation */
185
191
  .vopt:hover { outline: 1px solid var(--control-edge); outline-offset: -1px; background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
192
+ /* The row is a keyboard stop: src/components/dropdown.js:554
193
+ `items[next < 0 ? 0 : next]?.focus()` moves focus onto a `[data-dd-item]` with
194
+ the arrow keys, and this is one. It takes the ring on its own 10px radius, the
195
+ same way `.amenu a` above does. #482 */
196
+ .vopt:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
186
197
  .vopt .vname { font-family: var(--font-sans); font-size: calc(var(--text-sm) - 0.5px); color: var(--strong); font-weight: 600; letter-spacing: 0.01em; }
187
198
  .vopt .vmeta { font-size: var(--text-xs); color: var(--text); margin-top: 2px; }
188
199
  .vopt .vbadge {
@@ -221,6 +232,7 @@
221
232
  transition: border-color var(--dur-fast) var(--ease);
222
233
  }
223
234
  .acct .avatar:hover { border-color: var(--accent); }
235
+ .avatar:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
224
236
  .amenu {
225
237
  position: absolute;
226
238
  right: 0; top: 44px;
@@ -259,6 +271,7 @@
259
271
  .amenu .ahead .aem { color: var(--text); font-size: calc(var(--text-xs) + 0.5px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
260
272
  .amenu a { display: flex; align-items: center; gap: 11px; padding: 10px 15px; color: var(--text); text-decoration: none; font-family: var(--font-sans); font-size: var(--text-sm); cursor: pointer; }
261
273
  .amenu a:hover { outline: 1px solid var(--control-edge); outline-offset: -1px; background: var(--surface); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
274
+ .amenu a:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); border-radius: var(--radius-xs); }
262
275
  .amenu a.cur { color: var(--accent); }
263
276
  .amenu a svg { width: 19px; height: 19px; flex: none; stroke: currentColor; fill: none; stroke-width: 2; opacity: 0.85; }
264
277
  .amenu .asep { height: 1px; background: var(--border); margin: 5px 0; }