gridsmith-ui 0.27.0 → 0.28.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +42 -1
  2. package/dist/charts.js +2 -2
  3. package/dist/charts.js.map +1 -1
  4. package/dist/chat.js +5 -5
  5. package/dist/chat.js.map +1 -1
  6. package/dist/{chunk-64TXZ4L7.js → chunk-2ROVDVLZ.js} +3 -3
  7. package/dist/{chunk-64TXZ4L7.js.map → chunk-2ROVDVLZ.js.map} +1 -1
  8. package/dist/chunk-66JJCQU3.js +46 -0
  9. package/dist/chunk-66JJCQU3.js.map +1 -0
  10. package/dist/chunk-7RKC5C7G.js +35 -0
  11. package/dist/chunk-7RKC5C7G.js.map +1 -0
  12. package/dist/{chunk-OTCEJN64.js → chunk-BRAXJEEP.js} +7 -6
  13. package/dist/chunk-BRAXJEEP.js.map +1 -0
  14. package/dist/{chunk-IXR4UEF4.js → chunk-DQCHCLEJ.js} +9 -8
  15. package/dist/chunk-DQCHCLEJ.js.map +1 -0
  16. package/dist/{chunk-5TUVX4B2.js → chunk-EIWXVICU.js} +3 -3
  17. package/dist/{chunk-5TUVX4B2.js.map → chunk-EIWXVICU.js.map} +1 -1
  18. package/dist/{chunk-SFY2KMHG.js → chunk-FD4YED7O.js} +5 -4
  19. package/dist/chunk-FD4YED7O.js.map +1 -0
  20. package/dist/{chunk-MD2EFMES.js → chunk-GXMTS3YD.js} +3 -3
  21. package/dist/{chunk-MD2EFMES.js.map → chunk-GXMTS3YD.js.map} +1 -1
  22. package/dist/chunk-HTXP5BMM.js +38 -0
  23. package/dist/chunk-HTXP5BMM.js.map +1 -0
  24. package/dist/{chunk-R5NFCEZ3.js → chunk-OPFJEL3Z.js} +52 -24
  25. package/dist/chunk-OPFJEL3Z.js.map +1 -0
  26. package/dist/{chunk-EKBWWA6P.js → chunk-R4GXKH4U.js} +7 -7
  27. package/dist/chunk-R4GXKH4U.js.map +1 -0
  28. package/dist/{chunk-PO65I3MQ.js → chunk-TRDXWHD2.js} +6 -6
  29. package/dist/chunk-TRDXWHD2.js.map +1 -0
  30. package/dist/{chunk-2CKSSWYG.js → chunk-YUTF2X6B.js} +39 -20
  31. package/dist/chunk-YUTF2X6B.js.map +1 -0
  32. package/dist/data-grid.js +18 -11
  33. package/dist/data-grid.js.map +1 -1
  34. package/dist/editor-mentions.js +2 -2
  35. package/dist/editor.js +44 -24
  36. package/dist/editor.js.map +1 -1
  37. package/dist/index.d.ts +39 -2
  38. package/dist/index.js +397 -279
  39. package/dist/index.js.map +1 -1
  40. package/dist/kanban.js +18 -11
  41. package/dist/kanban.js.map +1 -1
  42. package/dist/resizable.js +20 -15
  43. package/dist/resizable.js.map +1 -1
  44. package/dist/roots.css +91 -15
  45. package/dist/tokens.css +111 -26
  46. package/dist/utils.d.ts +47 -5
  47. package/dist/utils.js +15 -5
  48. package/dist/utils.js.map +1 -1
  49. package/dist/virtualized.js +17 -11
  50. package/dist/virtualized.js.map +1 -1
  51. package/dist-static/tokens.css +111 -26
  52. package/package.json +1 -1
  53. package/dist/chunk-2CKSSWYG.js.map +0 -1
  54. package/dist/chunk-EKBWWA6P.js.map +0 -1
  55. package/dist/chunk-IXR4UEF4.js.map +0 -1
  56. package/dist/chunk-M2QRNBDQ.js +0 -42
  57. package/dist/chunk-M2QRNBDQ.js.map +0 -1
  58. package/dist/chunk-OTCEJN64.js.map +0 -1
  59. package/dist/chunk-PO65I3MQ.js.map +0 -1
  60. package/dist/chunk-R5NFCEZ3.js.map +0 -1
  61. package/dist/chunk-SFY2KMHG.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,36 +1,40 @@
1
1
  "use client";
2
- import { useAppShell, REACT_VERSION_PROBLEM, Breadcrumbs, useCrumbFollow, crumbGoes, useOverlayMark, useDirtyGuard, useScrollbarInset, DrawerFooterCtx, OverlayMarkScope, OverlayCloseCtx, insetLessScrollbar, DiscardConfirm, OVERLAY_ROOT_ATTR, OverlayMarkCtx, useCompact, useElementWidth, DrawerPanel, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, defaultMaxWidth } from './chunk-R5NFCEZ3.js';
3
- export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-R5NFCEZ3.js';
4
- import { Skeleton, TITLE_TEXT, useFormatLocale, useHydrated, useToday, LocaleContext } from './chunk-64TXZ4L7.js';
5
- export { Skeleton, useFormatLocale, useHydrated } from './chunk-64TXZ4L7.js';
2
+ import { useAppShell, REACT_VERSION_PROBLEM, Breadcrumbs, useCrumbFollow, crumbGoes, useOverlayMark, useDirtyGuard, useScrollbarInset, DrawerFooterCtx, OverlayMarkScope, OverlayCloseCtx, insetLessScrollbar, DiscardConfirm, OVERLAY_ROOT_ATTR, OverlayMarkCtx, useCompact, useElementWidth, DrawerPanel, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, defaultMaxWidth } from './chunk-OPFJEL3Z.js';
3
+ export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-OPFJEL3Z.js';
4
+ import { Skeleton, TITLE_TEXT, useFormatLocale, useHydrated, useToday, LocaleContext } from './chunk-2ROVDVLZ.js';
5
+ export { Skeleton, useFormatLocale, useHydrated } from './chunk-2ROVDVLZ.js';
6
6
  import { useScrollEdges, EDGE_FADE } from './chunk-3BRQRKDO.js';
7
- import { Avatar, foldForSearch, useActiveOptionInView, shownMentions, matchMentions, mentionOptionId, MentionList, initialsOf } from './chunk-5TUVX4B2.js';
8
- export { Avatar } from './chunk-5TUVX4B2.js';
9
- import { Link } from './chunk-IXR4UEF4.js';
10
- export { CodeBlock, Divider, Link } from './chunk-IXR4UEF4.js';
11
- import { useFillHeight, FillContext, useKeyboardScroll, FOCUS_INSET } from './chunk-SFY2KMHG.js';
12
- import { Badge } from './chunk-PO65I3MQ.js';
13
- export { Badge } from './chunk-PO65I3MQ.js';
14
- import { inferColumnKinds, useCardsSwitch, CARDS_BELOW, kindOfRenderedText, CardValue } from './chunk-OTCEJN64.js';
15
- import { useWholeListCheck, pagerPart, Pagination, Input, CHECK_FILL, Dropdown, FilterRowContext, SWITCH_KNOB_ON, SWITCH_KNOB_OFF, SWITCH_KNOB_MOTION, SWITCH_TRACK_ON, SWITCH_TRACK_OFF, useFieldControl, useField, OutsideField, NumberInput, InlineFieldError, Label, AddAnotherRowCtx, Field } from './chunk-2CKSSWYG.js';
16
- export { Dropdown, Field, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, Textarea, Toggle, usePagination } from './chunk-2CKSSWYG.js';
17
- import { Button, ControlSizeContext, triggerType, Spinner, useShellPreferences, PREFERENCE_HOUSE_DEFAULTS, formatRelativeTime, thresholdOf, toMillis, formatFullTime, preferenceActionKey, DAY, HOUR, MINUTE } from './chunk-EKBWWA6P.js';
18
- export { Button, ButtonSizeContext, ControlSizeContext, PreferencesContext, Spinner, formatFullTime, formatRelativeTime, preferenceActionKey, usePreferences } from './chunk-EKBWWA6P.js';
7
+ import { Avatar, foldForSearch, useActiveOptionInView, shownMentions, matchMentions, mentionOptionId, MentionList, initialsOf } from './chunk-EIWXVICU.js';
8
+ export { Avatar } from './chunk-EIWXVICU.js';
9
+ import { Link } from './chunk-DQCHCLEJ.js';
10
+ export { CodeBlock, Divider, Link } from './chunk-DQCHCLEJ.js';
11
+ import { useFillHeight, FillContext, useKeyboardScroll } from './chunk-FD4YED7O.js';
12
+ import { Badge } from './chunk-TRDXWHD2.js';
13
+ export { Badge } from './chunk-TRDXWHD2.js';
14
+ import { inferColumnKinds, useCardsSwitch, CARDS_BELOW, kindOfRenderedText, CardValue } from './chunk-BRAXJEEP.js';
15
+ import { useWholeListCheck, pagerPart, Pagination, Input, CHECK_FILL, Dropdown, FilterRowContext, SWITCH_KNOB_ON, SWITCH_KNOB_OFF, SWITCH_KNOB_MOTION, SWITCH_TRACK_ON, SWITCH_TRACK_OFF, useFieldControl, useField, OutsideField, NumberInput, InlineFieldError, Label, AddAnotherRowCtx, Field } from './chunk-YUTF2X6B.js';
16
+ export { Dropdown, Field, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, Textarea, Toggle, usePagination } from './chunk-YUTF2X6B.js';
17
+ import { Button, ControlSizeContext, triggerType, Spinner, useShellPreferences, PREFERENCE_HOUSE_DEFAULTS, formatRelativeTime, thresholdOf, toMillis, formatFullTime, preferenceActionKey, DAY, HOUR, MINUTE } from './chunk-R4GXKH4U.js';
18
+ export { Button, ButtonSizeContext, ControlSizeContext, PreferencesContext, Spinner, formatFullTime, formatRelativeTime, preferenceActionKey, usePreferences } from './chunk-R4GXKH4U.js';
19
19
  import { elementType, formatLocale, localeChoice } from './chunk-OYNUU346.js';
20
+ import { useInputModality } from './chunk-7RKC5C7G.js';
21
+ export { INPUT_MODALITY_ATTR, inputModality, installInputModality, useInputModality } from './chunk-7RKC5C7G.js';
20
22
  import { useBreakpoint, readBreakpointPx } from './chunk-CRQ6KCRD.js';
21
23
  export { BreakpointWidthProvider, useBreakpoint } from './chunk-CRQ6KCRD.js';
22
24
  import { FormChangeCtx, useReportedChange, useOptionalFormContext, FormCtx } from './chunk-XVB55M3N.js';
23
25
  export { useFormContext } from './chunk-XVB55M3N.js';
24
26
  import { CardSurfaceCtx, useScrollsInside, SplitPaneCtx, RowsCtx, PageActionsCtx, SurfaceBoundary, useFramesItself, TableCardCtx } from './chunk-QM4SJKWV.js';
27
+ import { useRevealFocus } from './chunk-HTXP5BMM.js';
28
+ export { useRevealFocus } from './chunk-HTXP5BMM.js';
25
29
  import { FOCUSABLE, usePopupFocus, FIELD, ZOOM_KEYS, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberFocus, focusInitial, OVERLAY_CLOSE_ATTR, rovingTabStop, useGridKeyboard, focusAfterRemoval, useNonModalDialog, useReturnFocus } from './chunk-ZI3OQDET.js';
26
30
  export { useGridKeyboard } from './chunk-ZI3OQDET.js';
27
- import { useTextOverflow, Tooltip } from './chunk-MD2EFMES.js';
28
- export { Tooltip, useTextOverflow } from './chunk-MD2EFMES.js';
31
+ import { useTextOverflow, Tooltip } from './chunk-GXMTS3YD.js';
32
+ export { Tooltip, useTextOverflow } from './chunk-GXMTS3YD.js';
29
33
  import { mergeRefs, Slot } from './chunk-7Q46JXSK.js';
30
34
  import { Icon, slotIcon, warnOnce } from './chunk-TJEV7YAY.js';
31
35
  import { usePosition } from './chunk-HP7WUZPK.js';
32
36
  import { useLatest } from './chunk-WH2FUFAC.js';
33
- import { cn, GLASS_SURFACE, GLASS_BACKDROP, GLASS_DROPDOWN, lockBodyScroll, unlockBodyScroll, OVERLAY_SURFACE, GLASS_OVERLAY, ERROR_HAS_FOCUS_RING, HAS_FOCUS_RING, ACTIVE_RING, ERROR_ACTIVE_RING, ERROR_FOCUS_RING, FOCUS_RING, FOCUS_WITHIN_RING } from './chunk-M2QRNBDQ.js';
37
+ import { cn, FOCUS_RING, GLASS_SURFACE, FOCUS_INSET, PEER_FOCUS_RING, GLASS_BACKDROP, GLASS_DROPDOWN, lockBodyScroll, unlockBodyScroll, OVERLAY_SURFACE, GLASS_OVERLAY, ERROR_HAS_FOCUS_RING, FIELD_HAS_FOCUS_RING, GROUP_FOCUS_RING, ERROR_ACTIVE_RING, ACTIVE_RING, ERROR_FOCUS_WITHIN_RING, FIELD_FOCUS_WITHIN_RING, ERROR_FOCUS_RING, FIELD_FOCUS_RING } from './chunk-66JJCQU3.js';
34
38
  import { createContext, forwardRef, useState, useRef, useCallback, useLayoutEffect, useMemo, useContext, isValidElement, useId, memo, useEffect, useImperativeHandle, Children, Fragment as Fragment$1, cloneElement, useSyncExternalStore } from 'react';
35
39
  import { createPortal, flushSync } from 'react-dom';
36
40
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -114,7 +118,8 @@ function DropdownMenu({ trigger, items, align = "end", className, container }) {
114
118
  "data-gs": "menu",
115
119
  onKeyDown: handleMenuKeyDown,
116
120
  className: cn(
117
- "fixed z-[var(--ds-z-popover)] min-w-[180px] p-ds-1",
121
+ // the items' outline has its 4 px of room at any density (Sprint 30, P3)
122
+ "fixed z-[var(--ds-z-popover)] min-w-[180px] p-[max(var(--ds-space-1),4px)]",
118
123
  "bg-[var(--ds-bg-surface-elevated)]",
119
124
  GLASS_DROPDOWN,
120
125
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
@@ -145,8 +150,8 @@ function DropdownMenu({ trigger, items, align = "end", className, container }) {
145
150
  className: cn(
146
151
  "flex items-center gap-ds-2 w-full text-left px-ds-3 py-ds-2 text-sm cursor-pointer",
147
152
  "transition-colors rounded-[var(--ds-radius-sm)]",
148
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
149
- item.disabled ? "opacity-disabled cursor-not-allowed" : item.variant === "destructive" ? "text-[var(--ds-status-error)] hover:bg-[var(--ds-status-error-bg)] focus-visible:bg-[var(--ds-status-error-bg)]" : "text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-emphasis)] focus-visible:bg-[var(--ds-bg-emphasis)]"
153
+ FOCUS_RING,
154
+ item.disabled ? "opacity-disabled cursor-not-allowed" : item.variant === "destructive" ? "text-[var(--ds-status-error-text)] hover:bg-[var(--ds-status-error-bg)] focus-visible:bg-[var(--ds-status-error-bg)]" : "text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-emphasis)] focus-visible:bg-[var(--ds-bg-emphasis)]"
150
155
  ),
151
156
  children: [
152
157
  item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon, size: 16, className: "shrink-0" }),
@@ -176,7 +181,7 @@ function BulkActionBar({ selectedCount, actions, onClearSelection, className })
176
181
  {
177
182
  className: cn(
178
183
  "flex items-center gap-ds-3 rounded-[var(--ds-radius-md)]",
179
- "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-accent-primary)]",
184
+ "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-accent-ink)]",
180
185
  "shadow-[var(--ds-shadow-dropdown),var(--ds-glass-inset)]",
181
186
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
182
187
  className
@@ -185,7 +190,7 @@ function BulkActionBar({ selectedCount, actions, onClearSelection, className })
185
190
  role: "toolbar",
186
191
  "aria-label": `${selectedCount} items selected`,
187
192
  children: [
188
- /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-[var(--ds-accent-primary)] tabular-nums whitespace-nowrap", children: [
193
+ /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-[var(--ds-accent-ink)] tabular-nums whitespace-nowrap", children: [
189
194
  selectedCount,
190
195
  " selected"
191
196
  ] }),
@@ -213,7 +218,7 @@ function BulkActionBar({ selectedCount, actions, onClearSelection, className })
213
218
  {
214
219
  type: "button",
215
220
  onClick: onClearSelection,
216
- className: "ml-auto text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
221
+ className: cn("ml-auto text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer", FOCUS_RING),
217
222
  "aria-label": "Clear selection",
218
223
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
219
224
  }
@@ -234,6 +239,7 @@ var TEXT_SIZE = {
234
239
  };
235
240
  function Combobox({ value, onChange: onChangeProp, options, placeholder = "Search\u2026", disabled, loading = false, className, ref, container, error = false, size: sizeProp, "aria-label": ariaLabel, ...wiring }) {
236
241
  const onChange = useReportedChange(onChangeProp, value);
242
+ useInputModality();
237
243
  const contextSize = useContext(ControlSizeContext);
238
244
  const control = useFieldControl({ ...wiring, error: error || void 0 });
239
245
  const size = sizeProp ?? contextSize ?? "md";
@@ -352,7 +358,7 @@ function Combobox({ value, onChange: onChangeProp, options, placeholder = "Searc
352
358
  "shadow-[var(--ds-input-shadow)]",
353
359
  "transition-colors duration-[var(--ds-transition-speed)]",
354
360
  !control.invalid && !disabled && "hover:border-[var(--ds-input-border-hover)]",
355
- control.invalid ? ERROR_HAS_FOCUS_RING : HAS_FOCUS_RING,
361
+ control.invalid ? ERROR_HAS_FOCUS_RING : FIELD_HAS_FOCUS_RING,
356
362
  disabled && "opacity-disabled pointer-events-none"
357
363
  ),
358
364
  children: [
@@ -405,7 +411,7 @@ function Combobox({ value, onChange: onChangeProp, options, placeholder = "Searc
405
411
  onChange("");
406
412
  setQuery("");
407
413
  },
408
- className: "mr-ds-2 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] cursor-pointer",
414
+ className: cn("mr-ds-2 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] cursor-pointer", FOCUS_RING),
409
415
  tabIndex: -1,
410
416
  "aria-label": "Clear",
411
417
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 14 })
@@ -449,7 +455,7 @@ function Combobox({ value, onChange: onChangeProp, options, placeholder = "Searc
449
455
  ),
450
456
  children: [
451
457
  /* @__PURE__ */ jsx("span", { className: "flex-1", children: opt.label }),
452
- opt.value === value && /* @__PURE__ */ jsx(Icon, { name: "check", size: 14, className: "text-[var(--ds-accent-primary)]" })
458
+ opt.value === value && /* @__PURE__ */ jsx(Icon, { name: "check", size: 14, className: "text-[var(--ds-accent-ink)]" })
453
459
  ]
454
460
  },
455
461
  opt.value
@@ -620,7 +626,7 @@ function RelativeTimeAt({ at, hours, className }) {
620
626
  dateTime: new Date(at).toISOString(),
621
627
  "data-relative-time": "",
622
628
  suppressHydrationWarning: true,
623
- className: cn("rounded-[var(--ds-radius-sm)] focus-visible:outline-2 focus-visible:outline-[var(--ds-accent-primary)] focus-visible:outline-offset-2", className),
629
+ className: cn("rounded-[var(--ds-radius-sm)]", FOCUS_RING, className),
624
630
  children: [
625
631
  formatRelativeTime(at, { thresholdHours: hours, now, locale }),
626
632
  /* @__PURE__ */ jsx("span", { className: "sr-only", suppressHydrationWarning: true, children: `, ${full}` })
@@ -686,7 +692,7 @@ function Checkbox({ checked, onChange: onChangeProp, label, size = "md", disable
686
692
  "rounded-[calc(var(--ds-radius-sm)*0.6)]",
687
693
  "border-[2px]",
688
694
  "transition-all",
689
- "peer-focus-visible:shadow-[var(--ds-shadow-focus)]",
695
+ PEER_FOCUS_RING,
690
696
  filled ? cn(CHECK_FILL, "border-[var(--ds-check-border)]") : "bg-[var(--ds-input-bg)] border-[var(--ds-input-border)]",
691
697
  disabled && "pointer-events-none"
692
698
  ),
@@ -808,7 +814,8 @@ function RadioGroup({ value, onChange: onChangeProp, options, orientation = "ver
808
814
  "bg-[var(--ds-input-bg)]",
809
815
  "transition-colors",
810
816
  selected && "border-[var(--ds-check-border)]",
811
- "group-focus-visible:shadow-[var(--ds-shadow-focus)]"
817
+ // keyboard focus is drawn on the radio, not round its label: the button draws none of its own
818
+ GROUP_FOCUS_RING
812
819
  ),
813
820
  children: selected && // Sprint 25.14 A20: the checked colour, as a checkbox's fill (a preset whose accent falls short of 3:1 sets it)
814
821
  /* @__PURE__ */ jsx("span", { className: cn("h-2.5 w-2.5 rounded-[var(--ds-radius-full)]", CHECK_FILL) })
@@ -1320,6 +1327,7 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1320
1327
  const gridRef = useRef(null);
1321
1328
  const generatedId = useId();
1322
1329
  const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
1330
+ useInputModality();
1323
1331
  const inputId = control.id ?? generatedId;
1324
1332
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
1325
1333
  const year = viewDate.getFullYear();
@@ -1535,8 +1543,10 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1535
1543
  "shadow-[var(--ds-input-shadow)]",
1536
1544
  "transition-colors",
1537
1545
  "px-ds-3 py-ds-2",
1538
- !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)] focus-within:border-[var(--ds-input-border-hover)]",
1539
- open && ACTIVE_RING,
1546
+ !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
1547
+ open && (control.invalid || typedInvalid ? ERROR_ACTIVE_RING : ACTIVE_RING),
1548
+ // the segments mark which part has focus (their tint); the field draws the focus (a hover keeps its border)
1549
+ control.invalid || typedInvalid ? ERROR_FOCUS_WITHIN_RING : FIELD_FOCUS_WITHIN_RING,
1540
1550
  disabled && "opacity-disabled cursor-not-allowed",
1541
1551
  "text-[var(--ds-text-primary)]"
1542
1552
  ),
@@ -1562,7 +1572,7 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1562
1572
  "aria-valuetext": drawn[k] === null ? "empty" : String(drawn[k]),
1563
1573
  onKeyDown: (e) => onSegmentKeyDown(e, i, spec),
1564
1574
  onClick: (e) => e.stopPropagation(),
1565
- className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", drawn[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
1575
+ className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-ink)] cursor-text", drawn[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
1566
1576
  children: shown(spec)
1567
1577
  }
1568
1578
  )
@@ -1578,7 +1588,7 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1578
1588
  "aria-expanded": open,
1579
1589
  onClick: () => !disabled && setOpen(!open),
1580
1590
  disabled,
1581
- className: "shrink-0 -mr-ds-1 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] focus-visible:outline-2 focus-visible:outline-[var(--ds-accent-primary)] disabled:cursor-not-allowed",
1591
+ className: cn("shrink-0 -mr-ds-1 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] disabled:cursor-not-allowed", FOCUS_RING),
1582
1592
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: 14 })
1583
1593
  }
1584
1594
  )
@@ -1622,11 +1632,11 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1622
1632
  role: "grid",
1623
1633
  "aria-label": formatMonthYear(viewDate),
1624
1634
  onKeyDown: grid.onKeyDown,
1625
- className: "flex flex-col gap-ds-0.5 tabular-nums",
1635
+ className: "flex flex-col gap-[4px] tabular-nums",
1626
1636
  style: { padding: "var(--ds-space-2) var(--ds-space-3)" },
1627
1637
  children: [
1628
- /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5 mb-ds-0.5", children: weekdayNames(weekStartsOn, "narrow").map((d, i) => /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": weekdayNames(weekStartsOn, "long")[i], className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, i)) }),
1629
- weeks.map((week, w) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5", onMouseLeave: () => setHover(null), children: week.map((day, i) => {
1638
+ /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-[4px] mb-ds-0.5", children: weekdayNames(weekStartsOn, "narrow").map((d, i) => /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": weekdayNames(weekStartsOn, "long")[i], className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, i)) }),
1639
+ weeks.map((week, w) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-[4px]", onMouseLeave: () => setHover(null), children: week.map((day, i) => {
1630
1640
  if (day === null) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, `empty-${i}`);
1631
1641
  const dateObj = new Date(year, month, day);
1632
1642
  const isSelected = !!value && isSameDay2(dateObj, value);
@@ -1653,8 +1663,8 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1653
1663
  className: cn(
1654
1664
  "w-full aspect-square flex items-center justify-center text-xs",
1655
1665
  "transition-colors cursor-pointer",
1656
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
1657
- isSelected ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-bold rounded-[var(--ds-radius-sm)]" : isOtherEnd ? "bg-[var(--ds-accent-muted)] text-[var(--ds-accent-subtle-text)] font-semibold rounded-[var(--ds-radius-sm)]" : between ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-subtle-text)] rounded-none" : isToday ? "border-[length:var(--ds-border-width)] border-[var(--ds-accent-primary)] text-[var(--ds-accent-primary)] font-medium rounded-[var(--ds-radius-sm)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)] rounded-[var(--ds-radius-sm)]",
1666
+ FOCUS_RING,
1667
+ isSelected ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-bold rounded-[var(--ds-radius-sm)]" : isOtherEnd ? "bg-[var(--ds-accent-muted)] text-[var(--ds-accent-subtle-text)] font-semibold rounded-[var(--ds-radius-sm)]" : between ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-subtle-text)] rounded-none" : isToday ? "border-[length:var(--ds-border-width)] border-[var(--ds-accent-ink)] text-[var(--ds-accent-ink)] font-medium rounded-[var(--ds-radius-sm)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)] rounded-[var(--ds-radius-sm)]",
1658
1668
  dis && "opacity-disabled cursor-not-allowed hover:bg-transparent"
1659
1669
  ),
1660
1670
  children: day
@@ -1769,6 +1779,7 @@ function DateRangePicker({
1769
1779
  const panelRef = useRef(null);
1770
1780
  const generatedId = useId();
1771
1781
  const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
1782
+ useInputModality();
1772
1783
  const triggerId = control.id ?? generatedId;
1773
1784
  const labelledBy = control.labelId ? `${control.labelId} ${triggerId}` : void 0;
1774
1785
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
@@ -1881,7 +1892,7 @@ function DateRangePicker({
1881
1892
  "px-ds-3 py-ds-2",
1882
1893
  !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
1883
1894
  open && (control.invalid ? ERROR_ACTIVE_RING : ACTIVE_RING),
1884
- control.invalid ? ERROR_FOCUS_RING : FOCUS_RING,
1895
+ control.invalid ? ERROR_FOCUS_RING : FIELD_FOCUS_RING,
1885
1896
  disabled && "opacity-disabled cursor-not-allowed",
1886
1897
  summary ? "text-[var(--ds-text-primary)]" : "text-[var(--ds-text-muted)]"
1887
1898
  ),
@@ -1922,9 +1933,9 @@ function DateRangePicker({
1922
1933
  /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: formatMonthRange(months[0], months[1]) }),
1923
1934
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: () => pageMonths(1), "aria-label": "Next month", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14 }) })
1924
1935
  ] }),
1925
- /* @__PURE__ */ jsx("div", { ref: gridRef, onKeyDown: grid.onKeyDown, className: "flex gap-ds-4 px-ds-3 py-ds-2", onMouseLeave: () => setHover(null), children: months.map((m) => /* @__PURE__ */ jsxs("div", { role: "grid", "aria-label": formatMonthYear(m), className: "w-[224px] flex flex-col gap-ds-0.5", children: [
1926
- /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5 mb-ds-0.5", children: dayNames.map((d, i) => /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": dayNamesLong[i], className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, i)) }),
1927
- getMonthGrid(m.getFullYear(), m.getMonth(), weekStartsOn).map((week, w) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5", children: week.map((d) => {
1936
+ /* @__PURE__ */ jsx("div", { ref: gridRef, onKeyDown: grid.onKeyDown, className: "flex gap-ds-4 px-ds-3 py-ds-2", onMouseLeave: () => setHover(null), children: months.map((m) => /* @__PURE__ */ jsxs("div", { role: "grid", "aria-label": formatMonthYear(m), className: "w-[224px] flex flex-col gap-[4px]", children: [
1937
+ /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-[4px] mb-ds-0.5", children: dayNames.map((d, i) => /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": dayNamesLong[i], className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, i)) }),
1938
+ getMonthGrid(m.getFullYear(), m.getMonth(), weekStartsOn).map((week, w) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-[4px]", children: week.map((d) => {
1928
1939
  if (!isSameMonth(d, m)) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, d.toISOString());
1929
1940
  const isStart = !!rangeStart && isSameDay(d, rangeStart);
1930
1941
  const isEnd = !!rangeEnd2 && isSameDay(d, rangeEnd2);
@@ -1949,8 +1960,8 @@ function DateRangePicker({
1949
1960
  className: cn(
1950
1961
  "w-full aspect-square flex items-center justify-center text-xs",
1951
1962
  "transition-colors cursor-pointer",
1952
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
1953
- isStart || isEnd ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-bold rounded-[var(--ds-radius-sm)]" : between ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-subtle-text)] rounded-none" : today !== null && isSameDay(d, today) ? "border-[length:var(--ds-border-width)] border-[var(--ds-accent-primary)] text-[var(--ds-accent-primary)] font-medium rounded-[var(--ds-radius-sm)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)] rounded-[var(--ds-radius-sm)]",
1963
+ FOCUS_RING,
1964
+ isStart || isEnd ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-bold rounded-[var(--ds-radius-sm)]" : between ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-subtle-text)] rounded-none" : today !== null && isSameDay(d, today) ? "border-[length:var(--ds-border-width)] border-[var(--ds-accent-ink)] text-[var(--ds-accent-ink)] font-medium rounded-[var(--ds-radius-sm)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)] rounded-[var(--ds-radius-sm)]",
1954
1965
  dis && "opacity-disabled cursor-not-allowed hover:bg-transparent"
1955
1966
  ),
1956
1967
  children: d.getDate()
@@ -2641,8 +2652,8 @@ function RecurrencePicker({ value, onChange: onChangeProp, start, label, weekSta
2641
2652
  onClick: () => edit({ weekdays: (on ? (custom.weekdays ?? []).filter((x) => x !== d) : [...custom.weekdays ?? [], d]).sort((a, b) => a - b) }),
2642
2653
  className: cn(
2643
2654
  "inline-flex h-8 min-w-8 items-center justify-center px-ds-2 rounded-[var(--ds-radius-full)] text-xs font-medium",
2644
- "border-[length:var(--ds-border-width)] cursor-pointer select-none outline-none transition-colors",
2645
- "focus-visible:shadow-[var(--ds-shadow-focus)]",
2655
+ "border-[length:var(--ds-border-width)] cursor-pointer select-none transition-colors",
2656
+ FOCUS_RING,
2646
2657
  // chosen: the accent's readable pair, so its text reads 4.5:1 in every theme (the house's one way
2647
2658
  // to colour text on a colour, as the Calendar's accent events), and the checked border at 3:1
2648
2659
  // marking the state; the tick colour on the checked fill read 3.2:1 in three themes
@@ -2875,7 +2886,7 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
2875
2886
  type: "button",
2876
2887
  onClick: onClose,
2877
2888
  ...{ [OVERLAY_CLOSE_ATTR]: "" },
2878
- className: "shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
2889
+ className: cn("shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer", FOCUS_RING),
2879
2890
  "aria-label": "Close",
2880
2891
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 18 })
2881
2892
  }
@@ -3114,6 +3125,7 @@ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accep
3114
3125
  const uploadRef = useLatest(upload);
3115
3126
  const overlayMark = useContext(OverlayMarkCtx);
3116
3127
  const reportChange = useContext(FormChangeCtx);
3128
+ useInputModality();
3117
3129
  const field = useField();
3118
3130
  const ownId = useId();
3119
3131
  const inputId = id ?? `${ownId}-input`;
@@ -3309,7 +3321,7 @@ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accep
3309
3321
  "transition-colors",
3310
3322
  // The real file input is visually hidden inside; Tab lands on it and
3311
3323
  // Enter/Space open the picker natively, so the zone shows its focus.
3312
- invalid ? ERROR_HAS_FOCUS_RING : HAS_FOCUS_RING,
3324
+ invalid ? ERROR_HAS_FOCUS_RING : FIELD_HAS_FOCUS_RING,
3313
3325
  dragOver ? `${ACTIVE_RING} bg-[var(--ds-accent-whisper)]` : invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)] hover:border-[var(--ds-accent-primary)] hover:bg-[var(--ds-accent-whisper)]"
3314
3326
  ),
3315
3327
  style: { padding: "var(--ds-space-8) var(--ds-space-4)" },
@@ -3317,7 +3329,7 @@ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accep
3317
3329
  /* @__PURE__ */ jsx("div", { className: cn(
3318
3330
  "flex items-center justify-center w-10 h-10 rounded-[var(--ds-radius-full)] transition-colors",
3319
3331
  "bg-[var(--ds-bg-muted)] group-hover:bg-[var(--ds-accent-subtle)]"
3320
- ), children: /* @__PURE__ */ jsx(Icon, { name: "plus", size: 20, className: "text-[var(--ds-text-muted)] group-hover:text-[var(--ds-accent-primary)] transition-colors" }) }),
3332
+ ), children: /* @__PURE__ */ jsx(Icon, { name: "plus", size: 20, className: "text-[var(--ds-text-muted)] group-hover:text-[var(--ds-accent-subtle-text)] transition-colors" }) }),
3321
3333
  /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
3322
3334
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: multiple ? "Drop files here or browse" : "Drop a file here or browse" }),
3323
3335
  constraints && /* @__PURE__ */ jsx("p", { id: constraintsId, className: "text-xs text-[var(--ds-text-muted)] mt-ds-1", children: constraints })
@@ -3370,7 +3382,7 @@ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accep
3370
3382
  /* @__PURE__ */ jsx(TruncFileName, { className: "text-[var(--ds-text-primary)]", children: item.name }),
3371
3383
  (item.size != null || words) && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-muted)]", children: [item.size != null ? formatSize(item.size) : null, words].filter(Boolean).join(" \xB7 ") }),
3372
3384
  item.status === "uploading" && /* @__PURE__ */ jsx(Progress, { value: progress, size: "sm", label: `Uploading ${item.name}`, className: "mt-ds-1" }),
3373
- item.status === "error" && /* @__PURE__ */ jsx("p", { className: "mt-ds-1 text-xs text-[var(--ds-status-error)]", children: item.error ?? "The upload didn't work." })
3385
+ item.status === "error" && /* @__PURE__ */ jsx("p", { className: "mt-ds-1 text-xs text-[var(--ds-status-error-text)]", children: item.error ?? "The upload didn't work." })
3374
3386
  ] }),
3375
3387
  item.status === "done" && /* @__PURE__ */ jsx(Icon, { name: "check", size: 16, className: "text-[var(--ds-status-success)] shrink-0", "aria-hidden": "true" }),
3376
3388
  running(item) && canCancel && // the visible word starts the name, which says which file (WCAG 2.5.3)
@@ -3511,7 +3523,7 @@ function SliderRange({
3511
3523
  error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-check-border)]",
3512
3524
  "shadow-[var(--ds-shadow-sm)]",
3513
3525
  "transition-[shadow,transform] duration-200 ease-out",
3514
- "focus-visible:shadow-[var(--ds-shadow-focus)]",
3526
+ FOCUS_RING,
3515
3527
  !disabled && "cursor-grab active:cursor-grabbing hover:scale-125 active:scale-150"
3516
3528
  ),
3517
3529
  style: { left: `${pct(low)}%` }
@@ -3537,7 +3549,7 @@ function SliderRange({
3537
3549
  error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-check-border)]",
3538
3550
  "shadow-[var(--ds-shadow-sm)]",
3539
3551
  "transition-[shadow,transform] duration-200 ease-out",
3540
- "focus-visible:shadow-[var(--ds-shadow-focus)]",
3552
+ FOCUS_RING,
3541
3553
  !disabled && "cursor-grab active:cursor-grabbing hover:scale-125 active:scale-150"
3542
3554
  ),
3543
3555
  style: { left: `${pct(high)}%` }
@@ -3623,11 +3635,14 @@ function TimePicker({
3623
3635
  const onChange = useReportedChange(onChangeProp, value);
3624
3636
  const contextSize = useContext(ControlSizeContext);
3625
3637
  const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
3638
+ useInputModality();
3626
3639
  const size = sizeProp ?? contextSize ?? "md";
3627
3640
  const [open, setOpen] = useState(false);
3628
3641
  const triggerRef = useRef(null);
3629
3642
  const buttonRef = useRef(null);
3630
3643
  const panelRef = useRef(null);
3644
+ const [columns, setColumns] = useState(null);
3645
+ useRevealFocus(columns);
3631
3646
  const listboxId = useId();
3632
3647
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
3633
3648
  const parsed = parseTime(value);
@@ -3662,9 +3677,9 @@ function TimePicker({
3662
3677
  if (e.key === "ArrowDown") next = current.nextElementSibling;
3663
3678
  else if (e.key === "ArrowUp") next = current.previousElementSibling;
3664
3679
  else if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
3665
- const columns = Array.from(panelRef.current?.querySelectorAll("[data-column]") ?? []);
3666
- const idx = columns.indexOf(column);
3667
- const target = columns[idx + (e.key === "ArrowRight" ? 1 : -1)];
3680
+ const columns2 = Array.from(panelRef.current?.querySelectorAll("[data-column]") ?? []);
3681
+ const idx = columns2.indexOf(column);
3682
+ const target = columns2[idx + (e.key === "ArrowRight" ? 1 : -1)];
3668
3683
  next = target?.querySelector('[data-selected="true"]') ?? target?.querySelector("button") ?? null;
3669
3684
  } else return;
3670
3685
  if (next instanceof HTMLElement) {
@@ -3835,8 +3850,10 @@ function TimePicker({
3835
3850
  "shadow-[var(--ds-input-shadow)]",
3836
3851
  "transition-colors",
3837
3852
  "px-ds-3 py-ds-2",
3838
- !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)] focus-within:border-[var(--ds-input-border-hover)]",
3839
- open && ACTIVE_RING,
3853
+ !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
3854
+ open && (control.invalid ? ERROR_ACTIVE_RING : ACTIVE_RING),
3855
+ // the segments mark which part has focus (their tint); the field draws the focus (a hover keeps its border)
3856
+ control.invalid ? ERROR_FOCUS_WITHIN_RING : FIELD_FOCUS_WITHIN_RING,
3840
3857
  disabled && "opacity-disabled cursor-not-allowed",
3841
3858
  "text-[var(--ds-text-primary)]"
3842
3859
  ),
@@ -3863,7 +3880,7 @@ function TimePicker({
3863
3880
  "aria-valuetext": segs[k] === null ? "empty" : pad2(segs[k]),
3864
3881
  onKeyDown: (e) => onSegmentKeyDown(e, i, spec),
3865
3882
  onClick: (e) => e.stopPropagation(),
3866
- className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", segs[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
3883
+ className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-ink)] cursor-text", segs[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
3867
3884
  children: shown(k, spec)
3868
3885
  }
3869
3886
  )
@@ -3885,7 +3902,7 @@ function TimePicker({
3885
3902
  "aria-valuetext": segs.period ?? "empty",
3886
3903
  onKeyDown: (e) => onSegmentKeyDown(e, specs.length, null),
3887
3904
  onClick: (e) => e.stopPropagation(),
3888
- className: cn("ml-ds-1 rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", segs.period === null && "text-[var(--ds-text-muted)]"),
3905
+ className: cn("ml-ds-1 rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-ink)] cursor-text", segs.period === null && "text-[var(--ds-text-muted)]"),
3889
3906
  children: segs.period ?? "--"
3890
3907
  }
3891
3908
  )
@@ -3901,7 +3918,7 @@ function TimePicker({
3901
3918
  "aria-controls": open ? listboxId : void 0,
3902
3919
  onClick: () => !disabled && setOpen(!open),
3903
3920
  disabled,
3904
- className: "shrink-0 -mr-ds-1 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] focus-visible:outline-2 focus-visible:outline-[var(--ds-accent-primary)] disabled:cursor-not-allowed",
3921
+ className: cn("shrink-0 -mr-ds-1 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] disabled:cursor-not-allowed", FOCUS_RING),
3905
3922
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: 14 })
3906
3923
  }
3907
3924
  )
@@ -3926,8 +3943,8 @@ function TimePicker({
3926
3943
  "rounded-[var(--ds-radius-md)]"
3927
3944
  ),
3928
3945
  style: { top: pos.top, left: pos.left },
3929
- children: /* @__PURE__ */ jsxs("div", { className: "inline-flex p-ds-1 gap-ds-1 tabular-nums", children: [
3930
- /* @__PURE__ */ jsx("div", { "data-column": true, className: "max-h-48 overflow-y-auto w-ds-10", children: hours.map((h, i) => /* @__PURE__ */ jsx(
3946
+ children: /* @__PURE__ */ jsxs("div", { ref: setColumns, className: "inline-flex p-ds-1 gap-ds-1 tabular-nums", children: [
3947
+ /* @__PURE__ */ jsx("div", { "data-column": true, className: "flex max-h-48 w-[calc(var(--ds-space-10)+8px)] flex-col gap-[4px] overflow-y-auto p-[4px] scroll-p-[4px]", children: hours.map((h, i) => /* @__PURE__ */ jsx(
3931
3948
  "button",
3932
3949
  {
3933
3950
  type: "button",
@@ -3938,7 +3955,8 @@ function TimePicker({
3938
3955
  onClick: () => emitChange(h, parsed.minute, parsed.period),
3939
3956
  className: cn(
3940
3957
  "w-full text-center text-sm py-ds-1 rounded-[var(--ds-radius-sm)] cursor-pointer",
3941
- "transition-colors focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
3958
+ "transition-colors",
3959
+ FOCUS_RING,
3942
3960
  (use24Hour ? h === parseInt(value) : h === parsed.hour) ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)]"
3943
3961
  ),
3944
3962
  children: pad3(h)
@@ -3946,7 +3964,7 @@ function TimePicker({
3946
3964
  h
3947
3965
  )) }),
3948
3966
  /* @__PURE__ */ jsx("div", { className: "w-px bg-[var(--ds-border-subtle)]" }),
3949
- /* @__PURE__ */ jsx("div", { "data-column": true, className: "max-h-48 overflow-y-auto w-ds-10", children: minutes.map((m, i) => /* @__PURE__ */ jsx(
3967
+ /* @__PURE__ */ jsx("div", { "data-column": true, className: "flex max-h-48 w-[calc(var(--ds-space-10)+8px)] flex-col gap-[4px] overflow-y-auto p-[4px] scroll-p-[4px]", children: minutes.map((m, i) => /* @__PURE__ */ jsx(
3950
3968
  "button",
3951
3969
  {
3952
3970
  type: "button",
@@ -3957,7 +3975,8 @@ function TimePicker({
3957
3975
  onClick: () => emitChange(parsed.hour, m, parsed.period),
3958
3976
  className: cn(
3959
3977
  "w-full text-center text-sm py-ds-1 rounded-[var(--ds-radius-sm)] cursor-pointer",
3960
- "transition-colors focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
3978
+ "transition-colors",
3979
+ FOCUS_RING,
3961
3980
  m === parsed.minute ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)]"
3962
3981
  ),
3963
3982
  children: pad3(m)
@@ -3966,7 +3985,7 @@ function TimePicker({
3966
3985
  )) }),
3967
3986
  !use24Hour && /* @__PURE__ */ jsxs(Fragment, { children: [
3968
3987
  /* @__PURE__ */ jsx("div", { className: "w-px bg-[var(--ds-border-subtle)]" }),
3969
- /* @__PURE__ */ jsx("div", { "data-column": true, className: "flex flex-col gap-ds-1 w-ds-10", children: ["AM", "PM"].map((p, i) => /* @__PURE__ */ jsx(
3988
+ /* @__PURE__ */ jsx("div", { "data-column": true, className: "flex flex-col gap-[4px] w-ds-10", children: ["AM", "PM"].map((p, i) => /* @__PURE__ */ jsx(
3970
3989
  "button",
3971
3990
  {
3972
3991
  type: "button",
@@ -3977,7 +3996,8 @@ function TimePicker({
3977
3996
  onClick: () => emitChange(parsed.hour, parsed.minute, p),
3978
3997
  className: cn(
3979
3998
  "text-xs font-medium py-ds-1 rounded-[var(--ds-radius-sm)] cursor-pointer",
3980
- "transition-colors text-center focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
3999
+ "transition-colors text-center",
4000
+ FOCUS_RING,
3981
4001
  p === parsed.period ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)]"
3982
4002
  ),
3983
4003
  children: p
@@ -3995,9 +4015,9 @@ function TimePicker({
3995
4015
  var variantStyles = {
3996
4016
  default: "bg-[var(--ds-bg-emphasis)] text-[var(--ds-text-primary)]",
3997
4017
  subtle: "bg-[var(--ds-bg-muted)] text-[var(--ds-text-secondary)]",
3998
- success: "bg-[var(--ds-status-success-bg)] text-[var(--ds-status-success)] border-[length:var(--ds-border-width)] border-[var(--ds-status-success)]/[0.2]",
4018
+ success: "bg-[var(--ds-status-success-bg)] text-[var(--ds-status-success-text)] border-[length:var(--ds-border-width)] border-[var(--ds-status-success)]/[0.2]",
3999
4019
  warning: "bg-[var(--ds-status-warning-bg)] text-[var(--ds-status-warning-text)] border-[length:var(--ds-border-width)] border-[var(--ds-status-warning)]/[0.2]",
4000
- error: "bg-[var(--ds-status-error-bg)] text-[var(--ds-status-error)] border-[length:var(--ds-border-width)] border-[var(--ds-status-error)]/[0.2]",
4020
+ error: "bg-[var(--ds-status-error-bg)] text-[var(--ds-status-error-text)] border-[length:var(--ds-border-width)] border-[var(--ds-status-error)]/[0.2]",
4001
4021
  outline: "bg-transparent text-[var(--ds-text-primary)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]"
4002
4022
  };
4003
4023
  var sizeStyles = {
@@ -4071,7 +4091,7 @@ var Tag = forwardRef(function Tag2({
4071
4091
  "transition-colors duration-[var(--ds-transition-speed)]",
4072
4092
  sizeStyles[size],
4073
4093
  colorClasses,
4074
- onClick && !disabled && "cursor-pointer select-none hover:opacity-80",
4094
+ onClick && !disabled && cn("cursor-pointer select-none hover:opacity-80", FOCUS_RING),
4075
4095
  disabled && "opacity-disabled cursor-not-allowed",
4076
4096
  className
4077
4097
  ),
@@ -4090,7 +4110,7 @@ var Tag = forwardRef(function Tag2({
4090
4110
  "rounded-[var(--ds-radius-sm)]",
4091
4111
  "opacity-60 hover:opacity-100",
4092
4112
  "transition-opacity duration-[var(--ds-transition-speed)] cursor-pointer",
4093
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
4113
+ FOCUS_RING
4094
4114
  ),
4095
4115
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: size === "sm" ? 10 : 12 })
4096
4116
  }
@@ -4115,6 +4135,7 @@ function TagInput({
4115
4135
  "aria-label": ariaLabel
4116
4136
  }) {
4117
4137
  const onChange = useReportedChange(onChangeProp, value);
4138
+ useInputModality();
4118
4139
  const [input, setInput] = useState("");
4119
4140
  const inputRef = useRef(null);
4120
4141
  const addTag = useCallback(
@@ -4187,7 +4208,7 @@ function TagInput({
4187
4208
  "shadow-[var(--ds-input-shadow)]",
4188
4209
  "transition-colors duration-[var(--ds-transition-speed)] cursor-text",
4189
4210
  !error && !disabled && "hover:border-[var(--ds-input-border-hover)]",
4190
- error ? ERROR_HAS_FOCUS_RING : HAS_FOCUS_RING,
4211
+ error ? ERROR_HAS_FOCUS_RING : FIELD_HAS_FOCUS_RING,
4191
4212
  disabled && "opacity-disabled cursor-not-allowed",
4192
4213
  className
4193
4214
  ),
@@ -4250,6 +4271,7 @@ function PinInput({
4250
4271
  id
4251
4272
  }) {
4252
4273
  const onChange = useReportedChange(onChangeProp, value);
4274
+ useInputModality();
4253
4275
  const contextSize = useContext(ControlSizeContext);
4254
4276
  const size = sizeProp ?? contextSize ?? "md";
4255
4277
  const inputsRef = useRef([]);
@@ -4335,7 +4357,7 @@ function PinInput({
4335
4357
  "shadow-[var(--ds-input-shadow)]",
4336
4358
  "transition-colors",
4337
4359
  !error && !disabled && "hover:border-[var(--ds-input-border-hover)]",
4338
- error ? ERROR_FOCUS_RING : FOCUS_RING,
4360
+ error ? ERROR_FOCUS_RING : FIELD_FOCUS_RING,
4339
4361
  "placeholder:text-[var(--ds-text-muted)]",
4340
4362
  sizes[size],
4341
4363
  disabled && "opacity-disabled cursor-not-allowed"
@@ -4435,7 +4457,7 @@ function Rating({
4435
4457
  const starClass = cn(
4436
4458
  "inline-flex transition-colors",
4437
4459
  filled || half ? "text-[var(--ds-status-warning)]" : "text-[var(--ds-text-muted)]",
4438
- !readOnly && "hover:scale-110 transition-transform cursor-pointer rounded-[var(--ds-radius-sm)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
4460
+ !readOnly && ["hover:scale-110 transition-transform cursor-pointer rounded-[var(--ds-radius-sm)]", FOCUS_RING]
4439
4461
  );
4440
4462
  if (readOnly) {
4441
4463
  return /* @__PURE__ */ jsx("span", { className: starClass, children: /* @__PURE__ */ jsx(StarIcon, { filled, half, size: iconSize }) }, i);
@@ -4556,6 +4578,7 @@ function ColorPicker({
4556
4578
  error = false
4557
4579
  }) {
4558
4580
  const onChange = useReportedChange(onChangeProp, value);
4581
+ useInputModality();
4559
4582
  const [hsl, setHsl] = useState(() => hexToHsl(value));
4560
4583
  const [hexInput, setHexInput] = useState(value);
4561
4584
  const satLightRef = useRef(null);
@@ -4665,7 +4688,7 @@ function ColorPicker({
4665
4688
  max: 360,
4666
4689
  value: hsl.h,
4667
4690
  onChange: (e) => emitChange({ ...hsl, h: Number(e.target.value) }),
4668
- className: "w-full h-3.5 rounded-[var(--ds-radius-full)] cursor-pointer appearance-none touch-none [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:rounded-[var(--ds-radius-full)] [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-white [&::-webkit-slider-thumb]:shadow-md [&::-webkit-slider-thumb]:cursor-pointer [&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:rounded-[var(--ds-radius-full)] [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-white [&::-moz-range-thumb]:shadow-md [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:appearance-none",
4691
+ className: cn("w-full h-3.5 rounded-[var(--ds-radius-full)] cursor-pointer appearance-none touch-none [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:rounded-[var(--ds-radius-full)] [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-white [&::-webkit-slider-thumb]:shadow-md [&::-webkit-slider-thumb]:cursor-pointer [&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:rounded-[var(--ds-radius-full)] [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-white [&::-moz-range-thumb]:shadow-md [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:appearance-none", FOCUS_RING),
4669
4692
  style: {
4670
4693
  background: "linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)"
4671
4694
  }
@@ -4680,7 +4703,7 @@ function ColorPicker({
4680
4703
  max: 100,
4681
4704
  value: Math.round(hsl.a * 100),
4682
4705
  onChange: (e) => emitChange({ ...hsl, a: Number(e.target.value) / 100 }),
4683
- className: "w-full h-3.5 rounded-[var(--ds-radius-full)] cursor-pointer appearance-none touch-none [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:rounded-[var(--ds-radius-full)] [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-white [&::-webkit-slider-thumb]:shadow-md [&::-webkit-slider-thumb]:cursor-pointer [&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:rounded-[var(--ds-radius-full)] [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-white [&::-moz-range-thumb]:shadow-md [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:appearance-none",
4706
+ className: cn("w-full h-3.5 rounded-[var(--ds-radius-full)] cursor-pointer appearance-none touch-none [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:rounded-[var(--ds-radius-full)] [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-white [&::-webkit-slider-thumb]:shadow-md [&::-webkit-slider-thumb]:cursor-pointer [&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:rounded-[var(--ds-radius-full)] [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-white [&::-moz-range-thumb]:shadow-md [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:appearance-none", FOCUS_RING),
4684
4707
  style: {
4685
4708
  background: `linear-gradient(to right, transparent, ${hslToHex({ ...hsl, a: 1 })})`
4686
4709
  }
@@ -4707,7 +4730,7 @@ function ColorPicker({
4707
4730
  "border-[length:var(--ds-border-width)] border-[var(--ds-input-border)]",
4708
4731
  "rounded-[var(--ds-radius-sm)]",
4709
4732
  "hover:border-[var(--ds-input-border-hover)]",
4710
- FOCUS_RING
4733
+ FIELD_FOCUS_RING
4711
4734
  )
4712
4735
  }
4713
4736
  )
@@ -4724,8 +4747,9 @@ function ColorPicker({
4724
4747
  "w-7 h-7 rounded-[var(--ds-radius-sm)] cursor-pointer",
4725
4748
  "border-[length:var(--ds-border-width)]",
4726
4749
  "hover:scale-110 transition-transform",
4727
- "focus-visible:shadow-[var(--ds-shadow-focus)] outline-none",
4728
- swatch.toLowerCase() === value.toLowerCase() ? ACTIVE_RING : "border-[var(--ds-border-default)]"
4750
+ FOCUS_RING,
4751
+ // selected is a choice's look: the accent border, never the halo (that is active's) or an outline
4752
+ swatch.toLowerCase() === value.toLowerCase() ? "border-[var(--ds-accent-ink)]" : "border-[var(--ds-border-default)]"
4729
4753
  ),
4730
4754
  style: { backgroundColor: swatch },
4731
4755
  "aria-label": swatch
@@ -4749,10 +4773,11 @@ var indicators = {
4749
4773
  error: "bg-[var(--ds-status-error)]"
4750
4774
  };
4751
4775
  var actionColorsByVariant = {
4752
- info: "text-[var(--ds-accent-primary)] hover:bg-[var(--ds-accent-muted)]",
4753
- success: "text-[var(--ds-status-success)] hover:bg-[color-mix(in_srgb,var(--ds-status-success)_12%,transparent)]",
4754
- warning: "text-[var(--ds-status-warning)] hover:bg-[color-mix(in_srgb,var(--ds-status-warning)_15%,transparent)]",
4755
- error: "text-[var(--ds-status-error)] hover:bg-[color-mix(in_srgb,var(--ds-status-error)_12%,transparent)]"
4776
+ // words on the alert's ground: the accent's tint takes its pair's words (D5), a status's soft background its words
4777
+ info: "text-[var(--ds-accent-subtle-text)] hover:bg-[var(--ds-accent-muted)]",
4778
+ success: "text-[var(--ds-status-success-text)] hover:bg-[color-mix(in_srgb,var(--ds-status-success)_12%,transparent)]",
4779
+ warning: "text-[var(--ds-status-warning-text)] hover:bg-[color-mix(in_srgb,var(--ds-status-warning)_15%,transparent)]",
4780
+ error: "text-[var(--ds-status-error-text)] hover:bg-[color-mix(in_srgb,var(--ds-status-error)_12%,transparent)]"
4756
4781
  };
4757
4782
  var Alert = forwardRef(function Alert2({
4758
4783
  variant,
@@ -4801,7 +4826,8 @@ var Alert = forwardRef(function Alert2({
4801
4826
  "shrink-0 text-xs font-medium px-ds-2.5 py-ds-1 rounded-[var(--ds-radius-sm)]",
4802
4827
  "transition-colors cursor-pointer",
4803
4828
  "border-[length:var(--ds-border-width)] border-current",
4804
- actionColorsByVariant[variant]
4829
+ actionColorsByVariant[variant],
4830
+ FOCUS_RING
4805
4831
  ),
4806
4832
  children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-ds-1", children: [
4807
4833
  /* @__PURE__ */ jsx(Icon, { name: "more-vertical", size: 12 }),
@@ -4826,7 +4852,8 @@ var Alert = forwardRef(function Alert2({
4826
4852
  className: cn(
4827
4853
  "shrink-0 text-xs font-medium px-ds-2.5 py-ds-1 rounded-[var(--ds-radius-sm)]",
4828
4854
  "transition-colors cursor-pointer",
4829
- isAccent ? cn("border-[length:var(--ds-border-width)] border-current", actionColorsByVariant[variant]) : cn("border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]", "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-tab-hover-bg)]")
4855
+ isAccent ? cn("border-[length:var(--ds-border-width)] border-current", actionColorsByVariant[variant]) : cn("border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]", "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-tab-hover-bg)]"),
4856
+ FOCUS_RING
4830
4857
  ),
4831
4858
  children: [
4832
4859
  a.iconLeft && /* @__PURE__ */ jsx("span", { className: "inline-flex mr-ds-1", children: slotIcon(a.iconLeft, 14, "an Alert action's iconLeft") }),
@@ -4849,7 +4876,8 @@ var Alert = forwardRef(function Alert2({
4849
4876
  "inline-flex items-center gap-ds-1 text-xs font-medium cursor-pointer",
4850
4877
  "transition-colors duration-[var(--ds-transition-speed)]",
4851
4878
  actionColorsByVariant[variant],
4852
- "rounded-[var(--ds-radius-sm)] px-ds-1.5 py-ds-0.5"
4879
+ "rounded-[var(--ds-radius-sm)] px-ds-1.5 py-ds-0.5",
4880
+ FOCUS_RING
4853
4881
  ),
4854
4882
  children: [
4855
4883
  /* @__PURE__ */ jsx(
@@ -4872,7 +4900,7 @@ var Alert = forwardRef(function Alert2({
4872
4900
  {
4873
4901
  type: "button",
4874
4902
  onClick: onDismiss,
4875
- className: "shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
4903
+ className: cn("shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer", FOCUS_RING),
4876
4904
  "aria-label": "Dismiss",
4877
4905
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
4878
4906
  }
@@ -5066,13 +5094,13 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
5066
5094
  }
5067
5095
  };
5068
5096
  const summaryNode = errorSummary && summary.length >= 2 ? typeof errorSummary === "function" ? errorSummary(summary) : /* @__PURE__ */ jsx(Alert, { variant: "error", title: `${summary.length} fields need attention`, className: "mb-ds-4", children: /* @__PURE__ */ jsx("ul", { className: "list-disc pl-ds-5 flex flex-col gap-ds-1", children: summary.map((f) => /* @__PURE__ */ jsxs("li", { children: [
5069
- /* @__PURE__ */ jsx("a", { href: f.id ? `#${f.id}` : void 0, onClick: focusField(f.id), className: "underline underline-offset-2 text-[var(--ds-text-primary)]", children: f.label }),
5097
+ /* @__PURE__ */ jsx("a", { href: f.id ? `#${f.id}` : void 0, onClick: focusField(f.id), className: cn("underline underline-offset-2 text-[var(--ds-text-primary)]", FOCUS_RING), children: f.label }),
5070
5098
  ": ",
5071
5099
  f.message
5072
5100
  ] }, f.id || f.label)) }) }) : null;
5073
5101
  const actionsNode = actions && !drawerFooter ? /* @__PURE__ */ jsx("div", { "data-form-actions": "", className: cn("flex flex-wrap items-center gap-ds-2 pt-ds-2", actionsAlign === "end" ? "justify-end" : "justify-start"), children: actions }) : null;
5074
5102
  return /* @__PURE__ */ jsx(FormCtx.Provider, { value: { errors, setError, clearErrors, validationMode, submitted, register }, children: /* @__PURE__ */ jsx(FormChangeCtx.Provider, { value: reportChange, children: /* @__PURE__ */ jsxs("form", { ref: setRefs, id: drawerFooter ? formId : void 0, onSubmit: handleSubmit, onFocusCapture: userActed, onInputCapture: userActed, className: cn("flex flex-col gap-ds-5 max-w-[var(--ds-form-max,calc(var(--ds-bp-md)_-_2*var(--ds-space-6)))]", className), style: fullWidth ? { "--ds-form-max": "none" } : void 0, noValidate: true, children: [
5075
- summaryNode && /* @__PURE__ */ jsx("div", { ref: summaryRef, "data-form-error-summary": "", tabIndex: -1, className: "rounded-[var(--ds-radius-md)] outline-none focus-visible:shadow-[var(--ds-shadow-focus)]", children: summaryNode }),
5103
+ summaryNode && /* @__PURE__ */ jsx("div", { ref: summaryRef, "data-form-error-summary": "", tabIndex: -1, className: cn("rounded-[var(--ds-radius-md)] focus-visible:*:shadow-none", FOCUS_RING), children: summaryNode }),
5076
5104
  children,
5077
5105
  actionsNode
5078
5106
  ] }) }) });
@@ -5292,10 +5320,13 @@ var actionButtonClass = [
5292
5320
  "bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)]",
5293
5321
  "transition-colors cursor-pointer",
5294
5322
  "hover:bg-[var(--ds-bg-muted)]",
5295
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
5323
+ FOCUS_RING,
5296
5324
  "disabled:opacity-disabled disabled:pointer-events-none"
5297
5325
  ].join(" ");
5298
5326
  function Panel({ title, items, totalCount, checked, onToggle, onToggleAll, search, onSearchChange, stacked }) {
5327
+ useInputModality();
5328
+ const [list2, setList] = useState(null);
5329
+ useRevealFocus(list2);
5299
5330
  const enabledItems = items.filter((i) => !i.disabled);
5300
5331
  const allChecked = enabledItems.length > 0 && enabledItems.every((i) => checked.has(i.id));
5301
5332
  const someChecked = enabledItems.some((i) => checked.has(i.id));
@@ -5319,7 +5350,7 @@ function Panel({ title, items, totalCount, checked, onToggle, onToggleAll, searc
5319
5350
  totalCount
5320
5351
  ] })
5321
5352
  ] }),
5322
- onSearchChange && /* @__PURE__ */ jsx("div", { className: "px-ds-2 py-ds-1.5 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-1.5 px-ds-2 py-ds-1 bg-[var(--ds-bg-muted)] rounded-[var(--ds-radius-sm)]", children: [
5353
+ onSearchChange && /* @__PURE__ */ jsx("div", { className: "px-ds-2 py-ds-1.5 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]", children: /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-ds-1.5 px-ds-2 py-ds-1 bg-[var(--ds-bg-muted)] rounded-[var(--ds-radius-sm)]", FIELD_HAS_FOCUS_RING), children: [
5323
5354
  /* @__PURE__ */ jsx(Icon, { name: "search", size: 12, className: "shrink-0 text-[var(--ds-text-muted)]" }),
5324
5355
  /* @__PURE__ */ jsx(
5325
5356
  "input",
@@ -5332,7 +5363,7 @@ function Panel({ title, items, totalCount, checked, onToggle, onToggleAll, searc
5332
5363
  }
5333
5364
  )
5334
5365
  ] }) }),
5335
- /* @__PURE__ */ jsxs("div", { className: cn("flex-1 overflow-y-auto", stacked ? "max-h-[160px]" : "max-h-[240px]"), style: { padding: "var(--ds-space-1)" }, children: [
5366
+ /* @__PURE__ */ jsxs("div", { ref: setList, className: cn("flex-1 overflow-y-auto scroll-p-[4px]", stacked ? "max-h-[160px]" : "max-h-[240px]"), style: { padding: "max(var(--ds-space-1), 4px)" }, children: [
5336
5367
  items.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-muted)] text-center py-ds-6", children: "No items" }),
5337
5368
  items.map((item) => /* @__PURE__ */ jsxs(
5338
5369
  "label",
@@ -5375,7 +5406,8 @@ function PanelCheckbox({ checked, indeterminate, onClick, label }) {
5375
5406
  },
5376
5407
  className: cn(
5377
5408
  "flex items-center justify-center shrink-0 h-[18px] w-[18px] rounded-[calc(var(--ds-radius-sm)*0.6)] border-[2px] transition-all cursor-pointer",
5378
- filled ? "bg-[var(--ds-check-bg)] border-[var(--ds-check-border)]" : "bg-[var(--ds-input-bg)] border-[var(--ds-input-border)]"
5409
+ filled ? "bg-[var(--ds-check-bg)] border-[var(--ds-check-border)]" : "bg-[var(--ds-input-bg)] border-[var(--ds-input-border)]",
5410
+ FOCUS_RING
5379
5411
  ),
5380
5412
  children: /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 12 12", fill: "none", className: cn("text-[var(--ds-check-fg)] transition-opacity", filled ? "opacity-100" : "opacity-0"), children: indeterminate ? /* @__PURE__ */ jsx("path", { d: "M3 6H9", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }) : /* @__PURE__ */ jsx("path", { d: "M2.5 6L5 8.5L9.5 3.5", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) })
5381
5413
  }
@@ -5394,6 +5426,7 @@ function MentionInput({
5394
5426
  ...wiring
5395
5427
  }) {
5396
5428
  const control = useFieldControl({ ...wiring, error: error || void 0 });
5429
+ useInputModality();
5397
5430
  const [internalValue, setInternalValue] = useState("");
5398
5431
  const value = controlledValue ?? internalValue;
5399
5432
  const onChange = useReportedChange(onChangeProp, value);
@@ -5533,7 +5566,7 @@ function MentionInput({
5533
5566
  "rounded-[var(--ds-radius-md)]",
5534
5567
  "shadow-[var(--ds-input-shadow)]",
5535
5568
  "placeholder:text-[var(--ds-text-muted)]",
5536
- control.invalid ? ERROR_FOCUS_RING : FOCUS_RING,
5569
+ control.invalid ? ERROR_FOCUS_RING : FIELD_FOCUS_RING,
5537
5570
  "disabled:cursor-not-allowed disabled:opacity-disabled disabled:hover:border-[var(--ds-input-border)]",
5538
5571
  "transition-colors duration-[var(--ds-transition-speed)]"
5539
5572
  )
@@ -5777,7 +5810,8 @@ var Card = forwardRef(function Card2({
5777
5810
  ref: checkboxRef,
5778
5811
  "data-card-checkbox": "",
5779
5812
  className: cn(
5780
- "rounded-[calc(var(--ds-radius-sm)*0.6)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
5813
+ "rounded-[calc(var(--ds-radius-sm)*0.6)]",
5814
+ FOCUS_RING,
5781
5815
  disabled ? "cursor-not-allowed" : "cursor-pointer"
5782
5816
  ),
5783
5817
  onClick: handleCheckableClick,
@@ -5837,11 +5871,14 @@ var Card = forwardRef(function Card2({
5837
5871
  variantStyles3[variant],
5838
5872
  isSelectable && !disabled && "cursor-pointer select-none transition-all duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
5839
5873
  isSelectable && !disabled && !selected && "hover:border-[var(--ds-accent-primary)]",
5840
- isButton && "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
5841
- // Selected is the accent border, with the accent-subtle fill on a card with a selectType. No ring: a ring round the
5842
- // whole card is the focus state (focus-visible:shadow-[var(--ds-shadow-focus)] above), and the active halo other
5843
- // controls take (ACTIVE_RING, since Sprint 20.6) made a selected card look focused (the owner, 27 September 2026).
5844
- isSelectable && selected && "border-[var(--ds-border-focus)]",
5874
+ // keyboard focus: the shared outline round the card, its own shadow stepping aside while it shows (Sprint 30); a card
5875
+ // that holds controls draws it round itself for its action too (CardTitle), so its shadow steps aside for that
5876
+ isButton && FOCUS_RING,
5877
+ holdsControls && "has-[[data-card-action]:focus-visible]:shadow-none",
5878
+ // Selected is the accent border (--ds-accent-ink, Sprint 30), with the accent-subtle fill on a card with a
5879
+ // selectType. Never an outline or a halo: the outline round the whole card is keyboard focus (FOCUS_RING above),
5880
+ // and the active halo (ACTIVE_RING) made a selected card look focused (the owner, 27 September 2026).
5881
+ isSelectable && selected && "border-[var(--ds-accent-ink)]",
5845
5882
  hasSelectType && selected && "bg-[var(--ds-accent-subtle)]",
5846
5883
  disabled && "opacity-disabled cursor-not-allowed",
5847
5884
  className
@@ -5920,7 +5957,8 @@ var CardHeader = forwardRef(function CardHeader2(allProps, ref) {
5920
5957
  className: cn(
5921
5958
  "inline-flex items-center justify-center h-8 w-8 rounded-[var(--ds-radius-md)]",
5922
5959
  "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]",
5923
- "transition-colors duration-[var(--ds-transition-speed)]"
5960
+ "transition-colors duration-[var(--ds-transition-speed)]",
5961
+ FOCUS_RING
5924
5962
  ),
5925
5963
  onClick: (e) => e.stopPropagation(),
5926
5964
  children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: 16 })
@@ -5998,8 +6036,9 @@ var CardTitle = forwardRef(function CardTitle2({ className, children, lines: lin
5998
6036
  ...props,
5999
6037
  children: action ? (
6000
6038
  // The card's action when the card holds controls of its own (Sprint 25.7): a real button, which a click
6001
- // elsewhere on the card also runs. Its ::after spans the card to draw the card's focus ring, taking no clicks.
6002
- // The clamp moves inside, since a button lays out its own lines.
6039
+ // elsewhere on the card also runs. Its ::after spans the card, taking no clicks, and draws keyboard focus round the
6040
+ // whole card, which is what a click runs: FOCUS_RING's outline on that layer, the card's shadow stepping aside
6041
+ // (the card's has-[]). The clamp moves inside, since a button lays out its own lines.
6003
6042
  /* @__PURE__ */ jsx(
6004
6043
  "button",
6005
6044
  {
@@ -6009,7 +6048,8 @@ var CardTitle = forwardRef(function CardTitle2({ className, children, lines: lin
6009
6048
  onClick: action.activate,
6010
6049
  className: cn(
6011
6050
  "block w-full text-left cursor-pointer [text-overflow:inherit] focus-visible:outline-none disabled:cursor-not-allowed",
6012
- "after:absolute after:inset-0 after:rounded-[var(--ds-radius-md)] after:pointer-events-none focus-visible:after:shadow-[var(--ds-shadow-focus)]"
6051
+ "after:absolute after:inset-0 after:rounded-[var(--ds-radius-md)] after:pointer-events-none",
6052
+ "focus-visible:after:outline-2 focus-visible:after:outline-solid focus-visible:after:outline-offset-2 focus-visible:after:outline-[var(--ds-focus)]"
6013
6053
  ),
6014
6054
  children: /* @__PURE__ */ jsx("span", { ref: (node) => overflowRef(lines > 0 ? node : null), className: cn("block [text-overflow:inherit]", lines > 0 && [lineClampClass, WHOLE_ON_KEYBOARD]), children })
6015
6055
  }
@@ -6080,6 +6120,8 @@ function StatsCard({ value, label, trend, icon, layout = "card", interactive, on
6080
6120
  "hover:scale-[var(--ds-ambient-hover-scale)] hover:-translate-y-[var(--ds-ambient-hover-lift)]"
6081
6121
  ],
6082
6122
  GLASS_SURFACE,
6123
+ // a clickable card is a Tab stop
6124
+ onClick && FOCUS_RING,
6083
6125
  className
6084
6126
  ),
6085
6127
  onClick,
@@ -6109,8 +6151,8 @@ function StatsCard({ value, label, trend, icon, layout = "card", interactive, on
6109
6151
  }
6110
6152
  function TrendBadge({ trend }) {
6111
6153
  const colors = {
6112
- positive: "text-[var(--ds-status-success)] bg-[var(--ds-status-success-bg)]",
6113
- negative: "text-[var(--ds-status-error)] bg-[var(--ds-status-error-bg)]",
6154
+ positive: "text-[var(--ds-status-success-text)] bg-[var(--ds-status-success-bg)]",
6155
+ negative: "text-[var(--ds-status-error-text)] bg-[var(--ds-status-error-bg)]",
6114
6156
  neutral: "text-[var(--ds-text-muted)] bg-[var(--ds-bg-muted)]"
6115
6157
  };
6116
6158
  const defaultSentiment = { up: "positive", down: "negative", flat: "neutral" };
@@ -6452,8 +6494,9 @@ function Accordion({ items, type = "single", defaultOpen = [], open: controlledO
6452
6494
  className: cn(
6453
6495
  "flex w-full items-center justify-between text-left cursor-pointer",
6454
6496
  "text-sm font-medium text-[var(--ds-text-primary)]",
6455
- "hover:text-[var(--ds-accent-primary)] transition-colors",
6456
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)] focus-visible:rounded-[var(--ds-radius-sm)]"
6497
+ "hover:text-[var(--ds-accent-ink)] transition-colors",
6498
+ FOCUS_RING,
6499
+ "focus-visible:rounded-[var(--ds-radius-sm)]"
6457
6500
  ),
6458
6501
  style: { padding: "var(--ds-space-4) 0" },
6459
6502
  children: [
@@ -6545,6 +6588,10 @@ var FRAME_CLASSES = cn(
6545
6588
  GLASS_SURFACE,
6546
6589
  "bg-[var(--ds-bg-surface)]"
6547
6590
  );
6591
+ var FRAME_FOCUS_RING = "has-[>[data-table-scroll]:focus-visible]:outline-2 has-[>[data-table-scroll]:focus-visible]:outline-solid has-[>[data-table-scroll]:focus-visible]:outline-offset-2 has-[>[data-table-scroll]:focus-visible]:outline-[var(--ds-focus)] has-[>[data-table-scroll]:focus-visible]:shadow-none";
6592
+ var PIN_FOCUS = "[tr:focus-visible>&]:after:pointer-events-none [tr:focus-visible>&]:after:absolute [tr:focus-visible>&]:after:inset-x-0 [tr:focus-visible>&]:after:inset-y-[3px] [tr:focus-visible>&]:after:border-y-2 [tr:focus-visible>&]:after:border-[var(--ds-focus)] [tr:focus-visible>&]:after:content-['']";
6593
+ var PIN_FOCUS_START = "[tr:focus-visible>&]:after:left-[3px] [tr:focus-visible>&]:after:border-l-2";
6594
+ var PIN_FOCUS_END = "[tr:focus-visible>&]:after:right-[3px] [tr:focus-visible>&]:after:border-r-2";
6548
6595
  var PINNED_BACKGROUND = "linear-gradient(var(--gs-row-tint, transparent), var(--gs-row-tint, transparent)), linear-gradient(var(--gs-pinned-surface), var(--gs-pinned-surface)), var(--gs-pinned-canvas)";
6549
6596
  var PINNED_LAYERS = cn(
6550
6597
  "[--gs-pinned-surface:var(--ds-bg-surface)] [--gs-pinned-canvas:var(--ds-bg-canvas)]",
@@ -6823,6 +6870,7 @@ function Table({
6823
6870
  const mobileBreakpoint = mobileBreakpointProp ?? preferences.tableToCardsBreakpoint ?? "md";
6824
6871
  const narrowScreen = useBreakpoint(mobileBreakpoint);
6825
6872
  const { cards: isMobile, rootRef } = useCardsSwitch(narrowScreen, cardsBelow, title);
6873
+ useRevealFocus(rootRef, isMobile);
6826
6874
  const setRoot = useCallback((node) => {
6827
6875
  rootRef.current = node;
6828
6876
  if (typeof ref === "function") ref(node);
@@ -6888,6 +6936,11 @@ function Table({
6888
6936
  }
6889
6937
  return void 0;
6890
6938
  };
6939
+ const pinFocus = (position, total) => {
6940
+ if (!onRowClick || !pinStyle(position, total)) return void 0;
6941
+ const fromEnd = total - 1 - position;
6942
+ return cn(PIN_FOCUS, position === 0 && PIN_FOCUS_START, fromEnd === 0 && PIN_FOCUS_END);
6943
+ };
6891
6944
  const fade = fadeProps;
6892
6945
  const [renderedKinds, setRenderedKinds] = useState(() => /* @__PURE__ */ new Map());
6893
6946
  useLayoutEffect(() => {
@@ -6988,7 +7041,7 @@ function Table({
6988
7041
  if (title) return titledCards(/* @__PURE__ */ jsx("div", { className: "py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", children: emptyState }));
6989
7042
  return /* @__PURE__ */ jsx("div", { ref: setRoot, "data-ds-list": "", "data-gs": "table", className: cn("py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", className), children: emptyState });
6990
7043
  }
6991
- const cards = /* @__PURE__ */ jsx(TableCardCtx, { value: true, children: /* @__PURE__ */ jsx(RowsCtx, { value: true, children: /* @__PURE__ */ jsxs("div", { ref: title ? void 0 : listRoot, "data-ds-list": title ? void 0 : "", "data-gs": title ? void 0 : "table", className: cn("flex flex-col gap-ds-3 overflow-y-auto", title ? "min-h-0" : listClass), onFocus: title ? void 0 : noteFocus, onBlur: title ? void 0 : forgetFocus, children: [
7044
+ const cards = /* @__PURE__ */ jsx(TableCardCtx, { value: true, children: /* @__PURE__ */ jsx(RowsCtx, { value: true, children: /* @__PURE__ */ jsxs("div", { ref: title ? void 0 : listRoot, "data-ds-list": title ? void 0 : "", "data-gs": title ? void 0 : "table", className: cn("flex flex-col gap-ds-3 overflow-y-auto -m-[4px] p-[4px] scroll-p-[4px]", title ? "min-h-0" : listClass), onFocus: title ? void 0 : noteFocus, onBlur: title ? void 0 : forgetFocus, children: [
6992
7045
  selectable && /* @__PURE__ */ jsx("span", { hidden: true, id: `${headingId}-select`, children: "Select" }),
6993
7046
  drawn.map(({ row, originalIdx }) => {
6994
7047
  const isSelected = selectedSet.has(originalIdx);
@@ -7010,8 +7063,9 @@ function Table({
7010
7063
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
7011
7064
  "rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-surface)]",
7012
7065
  "p-ds-4 space-y-ds-3",
7013
- isSelected && ACTIVE_RING,
7014
- onRowClick && "cursor-pointer hover:bg-[var(--ds-bg-muted)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
7066
+ // selected is chosen, not active (D2): the accent border, never the halo
7067
+ isSelected && "border-[var(--ds-accent-ink)]",
7068
+ onRowClick && ["cursor-pointer hover:bg-[var(--ds-bg-muted)]", FOCUS_RING],
7015
7069
  // with no onRowClick a tap on the card opens and closes it, as a click on the row does; the toggle is the
7016
7070
  // keyboard's way
7017
7071
  !onRowClick && canOpenByClick && "cursor-pointer"
@@ -7054,9 +7108,9 @@ function Table({
7054
7108
  // at least 24 px tall: the card around it is a target too, so its own box must meet the size (WCAG 2.5.8)
7055
7109
  "inline-flex min-h-[24px] items-center gap-ds-1 text-xs font-medium cursor-pointer",
7056
7110
  "transition-colors duration-[var(--ds-transition-speed)]",
7057
- "text-[var(--ds-accent-primary)] hover:bg-[var(--ds-accent-muted)]",
7111
+ "text-[var(--ds-accent-ink)] hover:bg-[var(--ds-accent-muted)]",
7058
7112
  "rounded-[var(--ds-radius-sm)] px-ds-1.5 py-ds-0.5 -mx-ds-1.5",
7059
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
7113
+ FOCUS_RING
7060
7114
  ),
7061
7115
  children: [
7062
7116
  /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 12, className: cn("transition-transform duration-[var(--ds-transition-speed)]", isExpanded && "rotate-90") }),
@@ -7116,7 +7170,8 @@ function Table({
7116
7170
  className: cn(
7117
7171
  "inline-flex items-center gap-ds-1 uppercase tracking-wider cursor-pointer select-none",
7118
7172
  "[font:inherit] text-inherit hover:text-[var(--ds-text-primary)]",
7119
- "rounded-[var(--ds-radius-sm)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
7173
+ "rounded-[var(--ds-radius-sm)]",
7174
+ FOCUS_RING
7120
7175
  ),
7121
7176
  children: [
7122
7177
  col.header,
@@ -7173,9 +7228,9 @@ function Table({
7173
7228
  // the row's tint, as a variable too, so its pinned cells draw it over their solid ground (A35)
7174
7229
  isSelected ? "bg-[var(--ds-accent-subtle)] [--gs-row-tint:var(--ds-accent-subtle)]" : striped && i % 2 === 1 ? "bg-[var(--ds-bg-muted)]/50 [--gs-row-tint:color-mix(in_oklab,var(--ds-bg-muted)_50%,transparent)]" : "hover:bg-[var(--ds-bg-muted)] hover:[--gs-row-tint:var(--ds-bg-muted)]",
7175
7230
  clickable && "cursor-pointer",
7176
- // an outline inside the row: it shows over the pinned cells, whose solid ground hid the inset
7177
- // shadow the row drew before (the review of 30 September)
7178
- onRowClick && "focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[var(--ds-border-focus)]"
7231
+ // an outline inside the row, clear of the frame's rounded corners (D8): it shows over the pinned
7232
+ // cells, whose solid ground hid the inset shadow the row drew before (the review of 30 September)
7233
+ onRowClick && FOCUS_INSET
7179
7234
  ),
7180
7235
  onClick: clickable ? (e) => {
7181
7236
  if (!clickIsRows(e)) return;
@@ -7191,13 +7246,13 @@ function Table({
7191
7246
  }
7192
7247
  } : void 0,
7193
7248
  children: [
7194
- selectable && /* @__PURE__ */ jsx("td", { className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3"), style: pinStyle(0, cellCount), children: /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, onChange: () => toggleRow(originalIdx), "aria-labelledby": nameIndex >= 0 ? `${headingId}-select ${headingId}-name-${idPart(id)}` : `${headingId}-select` }) }),
7195
- expandable && /* @__PURE__ */ jsx("td", { className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3"), style: pinStyle(selectable ? 1 : 0, cellCount), children: /* @__PURE__ */ jsx(
7249
+ selectable && /* @__PURE__ */ jsx("td", { className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3", pinFocus(0, cellCount)), style: pinStyle(0, cellCount), children: /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, onChange: () => toggleRow(originalIdx), "aria-labelledby": nameIndex >= 0 ? `${headingId}-select ${headingId}-name-${idPart(id)}` : `${headingId}-select` }) }),
7250
+ expandable && /* @__PURE__ */ jsx("td", { className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3", pinFocus(selectable ? 1 : 0, cellCount)), style: pinStyle(selectable ? 1 : 0, cellCount), children: /* @__PURE__ */ jsx(
7196
7251
  "button",
7197
7252
  {
7198
7253
  type: "button",
7199
7254
  onClick: () => toggleExpand(id),
7200
- className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] cursor-pointer",
7255
+ className: cn("text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] cursor-pointer", FOCUS_RING),
7201
7256
  "aria-expanded": isExpanded,
7202
7257
  ...nameIndex >= 0 ? { "aria-labelledby": `${headingId}-details ${headingId}-name-${idPart(id)}` } : { "aria-label": "Details" },
7203
7258
  children: /* @__PURE__ */ jsx(
@@ -7240,7 +7295,8 @@ function Table({
7240
7295
  alignClass,
7241
7296
  shrink && "w-px whitespace-nowrap",
7242
7297
  whole2 && "whitespace-nowrap",
7243
- kind === "identifier" && "[overflow-wrap:anywhere]"
7298
+ kind === "identifier" && "[overflow-wrap:anywhere]",
7299
+ pinFocus(lead + c, cellCount)
7244
7300
  ),
7245
7301
  style: col.maxWidth || col.width || col.minWidth || c + lead < pinnedStart || c >= columns.length - endCount ? { maxWidth: col.maxWidth, width: col.width, minWidth: col.minWidth, ...pinStyle(lead + c, cellCount) } : void 0,
7246
7302
  children: inner
@@ -7279,7 +7335,7 @@ function Table({
7279
7335
  ref: listRoot,
7280
7336
  "data-ds-list": "",
7281
7337
  "data-gs": "table",
7282
- className: cn("w-full min-w-0 flex flex-col", FRAME_CLASSES, "has-[>[data-table-scroll]:focus-visible]:shadow-[var(--ds-shadow-focus)]", control && "min-h-0", listClass),
7338
+ className: cn("w-full min-w-0 flex flex-col", FRAME_CLASSES, FRAME_FOCUS_RING, control && "min-h-0", listClass),
7283
7339
  onFocus: noteFocus,
7284
7340
  onBlur: forgetFocus,
7285
7341
  children: [
@@ -7495,9 +7551,11 @@ function SegmentedControl({
7495
7551
  // The selected segment is a kind of button, so it takes a secondary button's surface and edge, as the frame
7496
7552
  // takes a button's edge (the owner, 3 October 2026: the 3:1 strong edge it kept from Sprint 25.14 drew the
7497
7553
  // darker outline 25.19 took off the fields). It shows its state as the raised surface on the muted track, with
7498
- // the default variant's shadow and its label's stronger colour
7554
+ // the default variant's shadow and its label's stronger colour. While its segment has keyboard focus the shadow
7555
+ // steps aside, as a button's does, so the segment's outline sits clear (Sprint 30, D7)
7499
7556
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
7500
7557
  variant === "default" && "shadow-[var(--ds-shadow-sm)]",
7558
+ "[[role=radiogroup]:has([aria-checked=true]:focus-visible)_&]:shadow-none",
7501
7559
  "transition-all motion-reduce:transition-none"
7502
7560
  ),
7503
7561
  "data-animate": animate ? "true" : "false",
@@ -7529,7 +7587,7 @@ function SegmentedControl({
7529
7587
  fullWidth && "flex-1",
7530
7588
  "rounded-[var(--ds-segment-radius)]",
7531
7589
  "transition-colors cursor-pointer",
7532
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
7590
+ FOCUS_RING,
7533
7591
  sizes2[size],
7534
7592
  opt.value === value ? "text-[var(--ds-text-primary)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-secondary)]",
7535
7593
  opt.disabled && "opacity-disabled cursor-not-allowed"
@@ -7599,7 +7657,7 @@ function EventBar({ event, category, compact, onOpen, inert, showTime = true, co
7599
7657
  {
7600
7658
  ref,
7601
7659
  type: "button",
7602
- className: cn("w-1.5 h-1.5 rounded-[var(--ds-radius-full)] shrink-0 cursor-pointer", className),
7660
+ className: cn("w-1.5 h-1.5 rounded-[var(--ds-radius-full)] shrink-0 cursor-pointer", FOCUS_RING, className),
7603
7661
  style: { backgroundColor: colours.edge },
7604
7662
  onClick: (e) => {
7605
7663
  e.stopPropagation();
@@ -7619,7 +7677,7 @@ function EventBar({ event, category, compact, onOpen, inert, showTime = true, co
7619
7677
  "w-full text-left text-xs leading-tight px-ds-1.5 py-ds-0.5",
7620
7678
  look,
7621
7679
  "truncate cursor-pointer transition-[filter] hover:brightness-95",
7622
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
7680
+ FOCUS_RING,
7623
7681
  className
7624
7682
  ),
7625
7683
  style: paint,
@@ -7751,7 +7809,7 @@ function EventCard({ event, category, anchor, actions, onClose, fallbackFocus, r
7751
7809
  /* @__PURE__ */ jsx("span", { className: "min-w-0 break-words", children: event.location })
7752
7810
  ] }),
7753
7811
  event.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-secondary)] whitespace-pre-line break-words", children: event.description }),
7754
- event.attendees && event.attendees.length > 0 && /* @__PURE__ */ jsx("div", { ref: setAttendeeScroll, className: cn("max-h-32 overflow-y-auto -mx-[2px] p-[2px]", FOCUS_INSET), children: /* @__PURE__ */ jsx("ul", { "aria-label": "Attendees", className: "space-y-ds-1", children: event.attendees.map((person, i) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-ds-2 text-xs text-[var(--ds-text-primary)]", children: [
7812
+ event.attendees && event.attendees.length > 0 && /* @__PURE__ */ jsx("div", { ref: setAttendeeScroll, className: cn("max-h-32 overflow-y-auto -mx-[calc(var(--ds-focus-inset)+1px)] p-[calc(var(--ds-focus-inset)+1px)]", FOCUS_INSET), children: /* @__PURE__ */ jsx("ul", { "aria-label": "Attendees", className: "space-y-ds-1", children: event.attendees.map((person, i) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-ds-2 text-xs text-[var(--ds-text-primary)]", children: [
7755
7813
  /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "shrink-0", children: /* @__PURE__ */ jsx(Avatar, { size: "sm", initials: person.initials ?? person.name }) }),
7756
7814
  /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: person.name })
7757
7815
  ] }, `${person.name}-${i}`)) }) }),
@@ -7904,7 +7962,8 @@ function MonthView({ date, events, categories, selectedDate, onDayClick, canStar
7904
7962
  "relative min-h-[80px] sm:min-h-[100px] border-[var(--ds-border-subtle)]",
7905
7963
  !lastRow && "border-b-[length:var(--ds-divider-width)]",
7906
7964
  di < 6 && "border-r-[length:var(--ds-divider-width)]",
7907
- "p-ds-1 flex flex-col cursor-pointer transition-colors",
7965
+ // the day's outline sits inside its edge (D8, --ds-focus-inset): its number and events keep clear of it
7966
+ "p-[max(var(--ds-space-1),calc(var(--ds-focus-inset)+1px))] flex flex-col cursor-pointer transition-colors",
7908
7967
  FOCUS_INSET,
7909
7968
  "hover:bg-[var(--ds-bg-muted)]",
7910
7969
  !isCurrentMonth && "[&>.gs-day-number]:opacity-40",
@@ -7947,7 +8006,7 @@ function MonthView({ date, events, categories, selectedDate, onDayClick, canStar
7947
8006
  type: "button",
7948
8007
  "aria-haspopup": "dialog",
7949
8008
  "aria-expanded": !!list2 && isSameDay(list2.day, day),
7950
- className: "absolute z-[1] text-2xs text-[var(--ds-accent-text,var(--ds-accent-primary))] font-medium cursor-pointer hover:underline px-ds-1.5 rounded-[var(--ds-radius-sm)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
8009
+ className: cn("absolute z-[1] text-2xs text-[var(--ds-accent-text,var(--ds-accent-primary))] font-medium cursor-pointer hover:underline px-ds-1.5 rounded-[var(--ds-radius-sm)]", FOCUS_RING),
7951
8010
  style: { top: NUMBER_ROW + (LANES - 1) * (LANE_H + LANE_GAP), left: INSET, height: LANE_H },
7952
8011
  onClick: (e) => {
7953
8012
  e.stopPropagation();
@@ -8036,7 +8095,9 @@ function TimedEvent({ placed, category, onOpen, onFocusEvent }) {
8036
8095
  "border-[length:var(--ds-border-width)] border-solid rounded-[var(--ds-radius-sm)]",
8037
8096
  cutTop && "rounded-t-none border-t-0",
8038
8097
  cutBottom && "rounded-b-none border-b-0",
8039
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)] focus-visible:z-[3]"
8098
+ // a week event: the outline inside its edge (P3), raised over the now line and its neighbours while it shows
8099
+ FOCUS_INSET,
8100
+ "focus-visible:z-[3]"
8040
8101
  ),
8041
8102
  style: {
8042
8103
  top: drawnTop,
@@ -8244,7 +8305,7 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8244
8305
  type: "button",
8245
8306
  "aria-haspopup": "dialog",
8246
8307
  "aria-expanded": !!list2 && isSameDay(list2.day, day),
8247
- className: "absolute z-[1] text-2xs text-[var(--ds-accent-text,var(--ds-accent-primary))] font-medium cursor-pointer hover:underline px-ds-1.5 rounded-[var(--ds-radius-sm)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
8308
+ className: cn("absolute z-[1] text-2xs text-[var(--ds-accent-text,var(--ds-accent-primary))] font-medium cursor-pointer hover:underline px-ds-1.5 rounded-[var(--ds-radius-sm)]", FOCUS_RING),
8248
8309
  style: { top: 2 + (ALL_DAY_LANES - 1) * (LANE_H2 + LANE_GAP2), left: 2, height: LANE_H2 },
8249
8310
  onClick: (e) => {
8250
8311
  e.stopPropagation();
@@ -8422,7 +8483,7 @@ function MonthRow({
8422
8483
  "aria-label": `${words.monthName(month, "short")} ${year}`,
8423
8484
  className: cn(
8424
8485
  "sticky left-0 z-10 flex items-center justify-center px-ds-2 text-xs font-semibold text-[var(--ds-text-primary)]",
8425
- "bg-[var(--ds-bg-muted)] cursor-pointer hover:text-[var(--ds-accent-primary)] transition-colors",
8486
+ "bg-[var(--ds-bg-muted)] cursor-pointer hover:text-[var(--ds-accent-ink)] transition-colors",
8426
8487
  "border-r-[length:var(--ds-divider-width)] border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]",
8427
8488
  FOCUS_INSET
8428
8489
  ),
@@ -8559,7 +8620,9 @@ function CompactMonthView({ date, events, categories, selectedDate, onDayClick,
8559
8620
  },
8560
8621
  className: cn(
8561
8622
  "flex flex-col items-center justify-center py-ds-1.5 rounded-[var(--ds-radius-sm)] cursor-pointer transition-colors",
8562
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
8623
+ // a day cell, edge to edge with its neighbours and never filled solid: its outline sits inside its
8624
+ // edge, as the month view's days do
8625
+ FOCUS_INSET,
8563
8626
  !isCurrentMonth && "opacity-30",
8564
8627
  selected && "bg-[var(--ds-accent-subtle)]",
8565
8628
  !selected && "hover:bg-[var(--ds-bg-muted)]"
@@ -8671,8 +8734,9 @@ function MonthDropdown({
8671
8734
  onClick: () => setOpen(!open),
8672
8735
  className: cn(
8673
8736
  "flex items-center gap-ds-1 text-sm font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] cursor-pointer",
8674
- "hover:text-[var(--ds-accent-primary)] transition-colors",
8675
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)] rounded-[var(--ds-radius-sm)]",
8737
+ "hover:text-[var(--ds-accent-ink)] transition-colors",
8738
+ "rounded-[var(--ds-radius-sm)]",
8739
+ FOCUS_RING,
8676
8740
  "pl-ds-3 pr-ds-2 py-ds-1"
8677
8741
  ),
8678
8742
  children: [
@@ -8701,7 +8765,7 @@ function MonthDropdown({
8701
8765
  /* @__PURE__ */ jsx(
8702
8766
  "button",
8703
8767
  {
8704
- className: "p-ds-1 rounded-[var(--ds-radius-sm)] hover:bg-[var(--ds-bg-emphasis)] cursor-pointer text-[var(--ds-text-secondary)]",
8768
+ className: cn("p-ds-1 rounded-[var(--ds-radius-sm)] hover:bg-[var(--ds-bg-emphasis)] cursor-pointer text-[var(--ds-text-secondary)]", FOCUS_RING),
8705
8769
  onClick: () => setBrowseYear(browseYear - 1),
8706
8770
  "aria-label": "Previous year",
8707
8771
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14, className: "rotate-180" })
@@ -8711,7 +8775,7 @@ function MonthDropdown({
8711
8775
  /* @__PURE__ */ jsx(
8712
8776
  "button",
8713
8777
  {
8714
- className: "p-ds-1 rounded-[var(--ds-radius-sm)] hover:bg-[var(--ds-bg-emphasis)] cursor-pointer text-[var(--ds-text-secondary)]",
8778
+ className: cn("p-ds-1 rounded-[var(--ds-radius-sm)] hover:bg-[var(--ds-bg-emphasis)] cursor-pointer text-[var(--ds-text-secondary)]", FOCUS_RING),
8715
8779
  onClick: () => setBrowseYear(browseYear + 1),
8716
8780
  "aria-label": "Next year",
8717
8781
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14 })
@@ -8725,7 +8789,7 @@ function MonthDropdown({
8725
8789
  {
8726
8790
  className: cn(
8727
8791
  "px-ds-2 py-ds-1.5 text-xs font-medium rounded-[var(--ds-radius-sm)] cursor-pointer transition-colors",
8728
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
8792
+ FOCUS_RING,
8729
8793
  isActive2 ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)] hover:text-[var(--ds-text-primary)]"
8730
8794
  ),
8731
8795
  "data-current": isActive2 || void 0,
@@ -8802,8 +8866,9 @@ function YearDropdown({
8802
8866
  onClick: () => setOpen(!open),
8803
8867
  className: cn(
8804
8868
  "flex items-center gap-ds-1 text-sm font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] cursor-pointer",
8805
- "hover:text-[var(--ds-accent-primary)] transition-colors",
8806
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)] rounded-[var(--ds-radius-sm)]",
8869
+ "hover:text-[var(--ds-accent-ink)] transition-colors",
8870
+ "rounded-[var(--ds-radius-sm)]",
8871
+ FOCUS_RING,
8807
8872
  "pl-ds-3 pr-ds-2 py-ds-1"
8808
8873
  ),
8809
8874
  children: [
@@ -8832,7 +8897,7 @@ function YearDropdown({
8832
8897
  /* @__PURE__ */ jsx(
8833
8898
  "button",
8834
8899
  {
8835
- className: "p-ds-1 rounded-[var(--ds-radius-sm)] hover:bg-[var(--ds-bg-emphasis)] cursor-pointer text-[var(--ds-text-secondary)]",
8900
+ className: cn("p-ds-1 rounded-[var(--ds-radius-sm)] hover:bg-[var(--ds-bg-emphasis)] cursor-pointer text-[var(--ds-text-secondary)]", FOCUS_RING),
8836
8901
  onClick: () => setRangeStart(rangeStart - 9),
8837
8902
  "aria-label": "Previous years",
8838
8903
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14, className: "rotate-180" })
@@ -8846,7 +8911,7 @@ function YearDropdown({
8846
8911
  /* @__PURE__ */ jsx(
8847
8912
  "button",
8848
8913
  {
8849
- className: "p-ds-1 rounded-[var(--ds-radius-sm)] hover:bg-[var(--ds-bg-emphasis)] cursor-pointer text-[var(--ds-text-secondary)]",
8914
+ className: cn("p-ds-1 rounded-[var(--ds-radius-sm)] hover:bg-[var(--ds-bg-emphasis)] cursor-pointer text-[var(--ds-text-secondary)]", FOCUS_RING),
8850
8915
  onClick: () => setRangeStart(rangeStart + 9),
8851
8916
  "aria-label": "Next years",
8852
8917
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14 })
@@ -8858,7 +8923,7 @@ function YearDropdown({
8858
8923
  {
8859
8924
  className: cn(
8860
8925
  "px-ds-2 py-ds-1.5 text-xs font-medium rounded-[var(--ds-radius-sm)] cursor-pointer transition-colors",
8861
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
8926
+ FOCUS_RING,
8862
8927
  y === currentYear ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)] hover:text-[var(--ds-text-primary)]"
8863
8928
  ),
8864
8929
  "data-current": y === currentYear || void 0,
@@ -9181,7 +9246,7 @@ function Calendar({
9181
9246
  "button",
9182
9247
  {
9183
9248
  onClick: () => navigate(-1),
9184
- className: "flex items-center justify-center w-8 h-8 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-surface)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
9249
+ className: cn("flex items-center justify-center w-8 h-8 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-surface)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer", FOCUS_RING),
9185
9250
  "aria-label": "Previous month",
9186
9251
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-left", size: 16 })
9187
9252
  }
@@ -9190,7 +9255,7 @@ function Calendar({
9190
9255
  "button",
9191
9256
  {
9192
9257
  onClick: goToToday,
9193
- className: "text-sm font-medium text-[var(--ds-text-primary)] hover:text-[var(--ds-accent-primary)] transition-colors cursor-pointer px-ds-2",
9258
+ className: cn("text-sm font-medium text-[var(--ds-text-primary)] hover:text-[var(--ds-accent-ink)] transition-colors cursor-pointer px-ds-2", FOCUS_RING),
9194
9259
  children: formatMonthYear(currentDate)
9195
9260
  }
9196
9261
  ),
@@ -9198,7 +9263,7 @@ function Calendar({
9198
9263
  "button",
9199
9264
  {
9200
9265
  onClick: () => navigate(1),
9201
- className: "flex items-center justify-center w-8 h-8 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-surface)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
9266
+ className: cn("flex items-center justify-center w-8 h-8 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-surface)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer", FOCUS_RING),
9202
9267
  "aria-label": "Next month",
9203
9268
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16 })
9204
9269
  }
@@ -9395,7 +9460,7 @@ function AvatarGroup({ items, max = 4, size = "sm", ring = "canvas", onOverflowC
9395
9460
  );
9396
9461
  return item.name ? /* @__PURE__ */ jsx(Tooltip, { content: item.name, children: avatar }, i) : avatar;
9397
9462
  }),
9398
- counter && (onOverflowClick ? /* @__PURE__ */ jsx("button", { type: "button", onClick: onOverflowClick, "aria-label": `Show ${hidden.length} more`, className: "inline-flex rounded-[var(--ds-radius-full)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]", children: hiddenNames ? /* @__PURE__ */ jsx(Tooltip, { content: hiddenNames, children: counter }) : counter }) : hiddenNames ? /* @__PURE__ */ jsx(Tooltip, { content: hiddenNames, children: counter }) : counter)
9463
+ counter && (onOverflowClick ? /* @__PURE__ */ jsx("button", { type: "button", onClick: onOverflowClick, "aria-label": `Show ${hidden.length} more`, className: cn("inline-flex rounded-[var(--ds-radius-full)]", FOCUS_RING), children: hiddenNames ? /* @__PURE__ */ jsx(Tooltip, { content: hiddenNames, children: counter }) : counter }) : hiddenNames ? /* @__PURE__ */ jsx(Tooltip, { content: hiddenNames, children: counter }) : counter)
9399
9464
  ] });
9400
9465
  }
9401
9466
  var defaultIcons = {
@@ -9615,6 +9680,8 @@ function ExpandableDescription({ children, className }) {
9615
9680
  "text-xs text-[var(--ds-text-muted)] mt-ds-0.5",
9616
9681
  !expanded && "line-clamp-2",
9617
9682
  isTruncated && !expanded && "cursor-pointer",
9683
+ // a button while it's clamped: a Tab stop that shows the outline
9684
+ FOCUS_RING,
9618
9685
  className
9619
9686
  ),
9620
9687
  onClick: isTruncated && !expanded ? () => setExpanded(true) : void 0,
@@ -9768,7 +9835,7 @@ function CollapsibleTrigger({ children, className }) {
9768
9835
  }
9769
9836
  });
9770
9837
  }
9771
- return /* @__PURE__ */ jsx("button", { type: "button", className: cn("text-left cursor-pointer", className), onClick: toggle, "aria-expanded": isOpen, children });
9838
+ return /* @__PURE__ */ jsx("button", { type: "button", className: cn("text-left cursor-pointer", FOCUS_RING, className), onClick: toggle, "aria-expanded": isOpen, children });
9772
9839
  }
9773
9840
  function CollapsibleContent({ children, className }) {
9774
9841
  const { isOpen } = useCollapsible();
@@ -10035,12 +10102,12 @@ function TreeNodeRow({
10035
10102
  tabIndex: 0,
10036
10103
  className: cn(
10037
10104
  "flex items-center gap-ds-2 py-ds-1.5 px-ds-2 rounded-[var(--ds-radius-md)] cursor-pointer transition-colors",
10038
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
10105
+ FOCUS_RING,
10039
10106
  // Active style comes from the theme's --ds-tree-* tokens (rail / tint;
10040
10107
  // pill degrades to tint). Every row carries the rail width as a
10041
10108
  // transparent placeholder so content never shifts on selection.
10042
10109
  "border-l-[length:var(--ds-tree-active-rail)]",
10043
- isSelected ? "bg-[var(--ds-tree-active-bg)] text-[var(--ds-tree-active-text)] font-medium border-[var(--ds-accent-primary)]" : "border-transparent text-[var(--ds-text-primary)] hover:bg-[var(--ds-tab-hover-bg)] hover:text-[var(--ds-text-primary)]",
10110
+ isSelected ? "bg-[var(--ds-tree-active-bg)] text-[var(--ds-tree-active-text)] font-medium border-[var(--ds-accent-ink)]" : "border-transparent text-[var(--ds-text-primary)] hover:bg-[var(--ds-tab-hover-bg)] hover:text-[var(--ds-text-primary)]",
10044
10111
  // A folder holding the selection, as SidebarNav draws a parent of the current page (Sprint 25.16 A18): no
10045
10112
  // fill, so it reads the plain tokens, never the active set (pill's on-primary text would vanish here).
10046
10113
  holdsSelection && "font-medium",
@@ -10070,7 +10137,7 @@ function TreeNodeRow({
10070
10137
  {
10071
10138
  name: "chevron-right",
10072
10139
  size: 14,
10073
- className: cn(isSelected ? "text-[var(--ds-tree-active-muted)]" : holdsSelection ? "text-[var(--ds-accent-primary)]" : "text-[var(--ds-text-muted)]", "transition-transform", isExpanded && "rotate-90")
10140
+ className: cn(isSelected ? "text-[var(--ds-tree-active-muted)]" : holdsSelection ? "text-[var(--ds-accent-ink)]" : "text-[var(--ds-text-muted)]", "transition-transform", isExpanded && "rotate-90")
10074
10141
  }
10075
10142
  )
10076
10143
  }
@@ -10085,7 +10152,7 @@ function TreeNodeRow({
10085
10152
  }
10086
10153
  }
10087
10154
  ),
10088
- node.icon && /* @__PURE__ */ jsx(Icon, { name: node.icon, size: 16, className: cn("shrink-0", isSelected ? "text-[var(--ds-tree-active-text)]" : holdsSelection ? "text-[var(--ds-accent-primary)]" : "text-[var(--ds-text-muted)]") }),
10155
+ node.icon && /* @__PURE__ */ jsx(Icon, { name: node.icon, size: 16, className: cn("shrink-0", isSelected ? "text-[var(--ds-tree-active-text)]" : holdsSelection ? "text-[var(--ds-accent-ink)]" : "text-[var(--ds-text-muted)]") }),
10089
10156
  /* @__PURE__ */ jsx(TruncLabel2, { children: node.label })
10090
10157
  ]
10091
10158
  }
@@ -10246,7 +10313,7 @@ function Carousel({
10246
10313
  "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
10247
10314
  "text-[var(--ds-text-primary)] shadow-[var(--ds-shadow-sm)]",
10248
10315
  "transition-opacity cursor-pointer hover:bg-[var(--ds-bg-muted)]",
10249
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
10316
+ FOCUS_RING,
10250
10317
  !canPrev && "opacity-0 pointer-events-none"
10251
10318
  ),
10252
10319
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16, className: "rotate-180" })
@@ -10266,7 +10333,7 @@ function Carousel({
10266
10333
  "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
10267
10334
  "text-[var(--ds-text-primary)] shadow-[var(--ds-shadow-sm)]",
10268
10335
  "transition-opacity cursor-pointer hover:bg-[var(--ds-bg-muted)]",
10269
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
10336
+ FOCUS_RING,
10270
10337
  !canNext && "opacity-0 pointer-events-none"
10271
10338
  ),
10272
10339
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16 })
@@ -10281,7 +10348,7 @@ function Carousel({
10281
10348
  "aria-label": `Go to slide ${i + 1}`,
10282
10349
  className: cn(
10283
10350
  "w-2 h-2 rounded-[var(--ds-radius-full)] transition-all cursor-pointer",
10284
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
10351
+ FOCUS_RING,
10285
10352
  i === current ? "bg-[var(--ds-accent-primary)] w-6" : "bg-[var(--ds-text-muted)] opacity-50 hover:opacity-75"
10286
10353
  )
10287
10354
  },
@@ -10370,7 +10437,7 @@ function SourcesRow({ sources }) {
10370
10437
  ] }, s.href + i))
10371
10438
  ] });
10372
10439
  }
10373
- var StreamingCaret = () => /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-ds-0.5 inline-block animate-pulse text-[var(--ds-accent-primary)]", children: "\u258D" });
10440
+ var StreamingCaret = () => /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-ds-0.5 inline-block animate-pulse text-[var(--ds-accent-ink)]", children: "\u258D" });
10374
10441
  var ChatMessageItem = memo(function ChatMessageItem2({ message: msg, grouped, spaced, layout, onAction, renderContent }) {
10375
10442
  const locale = useFormatLocale();
10376
10443
  const hydrated = useHydrated();
@@ -10440,6 +10507,7 @@ var ChatMessageItem = memo(function ChatMessageItem2({ message: msg, grouped, sp
10440
10507
  function ChatInputBar({ onSend, busy, onStop, onAttach, onVoice, placeholder }) {
10441
10508
  const [text, setText] = useState("");
10442
10509
  const isMobile = useBreakpoint("sm");
10510
+ useInputModality();
10443
10511
  const handleSend = () => {
10444
10512
  if (busy || !text.trim()) return;
10445
10513
  onSend?.(text.trim());
@@ -10463,7 +10531,7 @@ function ChatInputBar({ onSend, busy, onStop, onAttach, onVoice, placeholder })
10463
10531
  "border-[length:var(--ds-border-width)] border-[var(--ds-input-border)]",
10464
10532
  "bg-[var(--ds-input-bg)]",
10465
10533
  "shadow-[var(--ds-input-shadow)]",
10466
- FOCUS_WITHIN_RING,
10534
+ FIELD_FOCUS_WITHIN_RING,
10467
10535
  "transition-colors duration-[var(--ds-transition-speed)]"
10468
10536
  ),
10469
10537
  children: [
@@ -10543,6 +10611,12 @@ function ChatMessageList({
10543
10611
  }) {
10544
10612
  const scrollRef = useRef(null);
10545
10613
  const columnRef = useRef(null);
10614
+ const [scroller, setScroller] = useState(null);
10615
+ const setScroll = useCallback((node) => {
10616
+ scrollRef.current = node;
10617
+ setScroller(node);
10618
+ }, []);
10619
+ useRevealFocus(scroller);
10546
10620
  const atBottomRef = useRef(true);
10547
10621
  const [showJump, setShowJump] = useState(false);
10548
10622
  const [prevMessages, setPrevMessages] = useState(messages);
@@ -10586,7 +10660,7 @@ function ChatMessageList({
10586
10660
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col flex-1 min-h-0", className), children: [
10587
10661
  /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "sr-only", children: announcement }),
10588
10662
  /* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-h-0", children: [
10589
- /* @__PURE__ */ jsx("div", { ref: scrollRef, onScroll, className: "h-full overflow-y-auto", children: empty ? /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-ds-6 px-ds-4 py-ds-8 text-center", children: [
10663
+ /* @__PURE__ */ jsx("div", { ref: setScroll, onScroll, className: "h-full overflow-y-auto scroll-p-[4px]", children: empty ? /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-ds-6 px-ds-4 py-ds-8 text-center", children: [
10590
10664
  emptyState ?? /* @__PURE__ */ jsx(
10591
10665
  EmptyState,
10592
10666
  {
@@ -11119,7 +11193,9 @@ var StackedList = forwardRef(function StackedList2({
11119
11193
  inset,
11120
11194
  interactive && [
11121
11195
  "cursor-pointer [transition:var(--ds-transition-interactive)]",
11122
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
11196
+ // in a pane the rows run to its edges, which clip: the outline sits inside (D8), and on the selected
11197
+ // row in the row's own text colour, which reads on its fill, the pill style's solid one too (P1)
11198
+ inPane ? cn(FOCUS_INSET, isSelected && "[--ds-focus:var(--ds-nav-v-active-text)]") : FOCUS_RING
11123
11199
  ],
11124
11200
  // list: same colours as the vertical Tabs item — the theme's in-page vertical style decides rail, tint
11125
11201
  // or pill via the --ds-nav-v-* tokens — but a list row is a full-width band: no radius, ever, and a
@@ -11128,7 +11204,7 @@ var StackedList = forwardRef(function StackedList2({
11128
11204
  // articles: no rail, no tint; the title answers the pointer and the current row.
11129
11205
  // The placeholder exists only where a selection can appear, so a plain canvas list aligns exactly with its heading.
11130
11206
  !articles && selectable && "border-l-[length:calc(var(--ds-nav-v-active-rail)*1.5)]",
11131
- !articles && (isSelected ? "bg-[var(--ds-nav-v-active-bg)] text-[var(--ds-nav-v-active-text)] border-[var(--ds-accent-primary)]" : cn("border-transparent", interactive && "hover:bg-[var(--ds-tab-hover-bg)]")),
11207
+ !articles && (isSelected ? "bg-[var(--ds-nav-v-active-bg)] text-[var(--ds-nav-v-active-text)] border-[var(--ds-accent-ink)]" : cn("border-transparent", interactive && "hover:bg-[var(--ds-tab-hover-bg)]")),
11132
11208
  articles && interactive && "group/row"
11133
11209
  ),
11134
11210
  onClick: item.onClick,
@@ -11150,9 +11226,9 @@ var StackedList = forwardRef(function StackedList2({
11150
11226
  {
11151
11227
  className: cn(
11152
11228
  "text-sm",
11153
- isSelected ? articles ? "font-medium text-[var(--ds-accent-primary)]" : "font-medium text-[var(--ds-nav-v-active-text)]" : hasEmphasis && !item.unread ? "text-[var(--ds-text-secondary)]" : "font-medium text-[var(--ds-text-primary)]",
11229
+ isSelected ? articles ? "font-medium text-[var(--ds-accent-ink)]" : "font-medium text-[var(--ds-nav-v-active-text)]" : hasEmphasis && !item.unread ? "text-[var(--ds-text-secondary)]" : "font-medium text-[var(--ds-text-primary)]",
11154
11230
  // articles: the title answers the pointer (accent, underline) instead of the row's background
11155
- articles && interactive && !isSelected && "transition-colors group-hover/row:text-[var(--ds-accent-primary)] group-hover/row:underline underline-offset-2"
11231
+ articles && interactive && !isSelected && "transition-colors group-hover/row:text-[var(--ds-accent-ink)] group-hover/row:underline underline-offset-2"
11156
11232
  ),
11157
11233
  children: item.title
11158
11234
  }
@@ -11817,8 +11893,8 @@ var TimeViewport = forwardRef(function TimeViewport2({
11817
11893
  const { labelWidth: lw, headerHeight } = latest.current;
11818
11894
  const box = el.getBoundingClientRect();
11819
11895
  const r = target.getBoundingClientRect();
11820
- const top = box.top + headerHeight;
11821
- const bottom = box.top + el.clientHeight;
11896
+ const top = box.top + headerHeight + 4;
11897
+ const bottom = box.top + el.clientHeight - 4;
11822
11898
  if (r.top < top) el.scrollTop -= top - r.top;
11823
11899
  else if (r.bottom > bottom) el.scrollTop += Math.min(r.bottom - bottom, r.top - top);
11824
11900
  const x0 = r.left - box.left - lw + el.scrollLeft;
@@ -11994,7 +12070,7 @@ var TimeViewport = forwardRef(function TimeViewport2({
11994
12070
  tabIndex: tabStop ? 0 : void 0,
11995
12071
  onScroll,
11996
12072
  className: "peer h-full overflow-auto isolate outline-none",
11997
- style: { scrollPaddingInlineStart: pinnedWidth, scrollPaddingTop: frame.header },
12073
+ style: { scrollPaddingInlineStart: pinnedWidth + 4, scrollPaddingInlineEnd: 4, scrollPaddingTop: frame.header + 4, scrollPaddingBottom: 4 },
11998
12074
  children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-full flex-col", style: { width: pinnedWidth + drawn.width }, children: [
11999
12075
  hasHeader && /* @__PURE__ */ jsxs("div", { ref: headerRef, className: cn("sticky top-0 z-[3] flex shrink-0 bg-[var(--ds-bg-surface)] border-b-[length:var(--ds-border-width)]", border), children: [
12000
12076
  /* @__PURE__ */ jsx(
@@ -12031,7 +12107,7 @@ var TimeViewport = forwardRef(function TimeViewport2({
12031
12107
  ] })
12032
12108
  }
12033
12109
  ),
12034
- /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 peer-focus-visible:shadow-[inset_0_0_0_2px_var(--ds-border-focus)] peer-focus-visible:outline-2 peer-focus-visible:-outline-offset-2 peer-focus-visible:outline-transparent" })
12110
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: cn("pointer-events-none absolute inset-0", PEER_FOCUS_RING, "peer-focus-visible:-outline-offset-[var(--ds-focus-inset)]") })
12035
12111
  ] })
12036
12112
  ] }) }) })
12037
12113
  );
@@ -12089,7 +12165,7 @@ function TimeBar({ start, end, minWidth = 0, labels, children, className, style,
12089
12165
  ...props,
12090
12166
  ref: barRef,
12091
12167
  "data-gs-clip": "",
12092
- className: cn("absolute top-ds-2 bottom-ds-2 flex items-center overflow-clip rounded-[var(--ds-radius-sm)] outline-none focus-visible:outline-hidden focus-visible:shadow-[var(--ds-shadow-focus)]", className),
12168
+ className: cn("absolute top-ds-2 bottom-ds-2 flex items-center overflow-clip rounded-[var(--ds-radius-sm)]", FOCUS_RING, className),
12093
12169
  style: { insetInlineStart: x, width: drawnWidth, ...style },
12094
12170
  children: label
12095
12171
  }
@@ -12233,7 +12309,7 @@ var NO_DATES = "no dates";
12233
12309
  var KNOCKOUT = 2;
12234
12310
  var EDGE = "border-[length:1px] border-transparent";
12235
12311
  var EDGE_WIDTH = 1;
12236
- var SELECTED = "outline-solid outline-2 outline-offset-2 outline-[var(--ds-text-primary)]";
12312
+ var SELECTED = "border-[var(--ds-accent-ink)]";
12237
12313
  var UNDATED_INSET = 16;
12238
12314
  var NO_GROUPS = [];
12239
12315
  var NO_STATUSES = {};
@@ -12279,7 +12355,7 @@ function NoDates({ cue, selected, className, ...props }) {
12279
12355
  "data-gantt-shape": "undated",
12280
12356
  className: cn(
12281
12357
  "sticky flex cursor-pointer items-center gap-ds-1 whitespace-nowrap rounded-[var(--ds-radius-sm)] border-[length:1px] border-dashed border-[var(--ds-border-default)] px-ds-1.5 py-ds-0.5 text-xs text-[var(--ds-text-secondary)]",
12282
- "outline-none focus-visible:outline-hidden focus-visible:shadow-[var(--ds-shadow-focus)]",
12358
+ FOCUS_RING,
12283
12359
  selected && SELECTED,
12284
12360
  className
12285
12361
  ),
@@ -12535,8 +12611,8 @@ function Gantt({
12535
12611
  /* @__PURE__ */ jsx("span", { className: "truncate", children: item.title })
12536
12612
  ] });
12537
12613
  const shapeBox = shape.kind === "milestone" ? (
12538
- // the target's own outline is its focus indicator in forced colours, which drop the diamond's shadow ring
12539
- /* @__PURE__ */ jsx("div", { ...treeitem(row), "aria-selected": isSelected, className: "group absolute grid cursor-pointer place-items-center outline-none focus-visible:outline-hidden", style: { insetInlineStart: (shape.x0 + shape.x1) / 2 - TARGET / 2, top: (ROW_HEIGHT2 - TARGET) / 2, width: TARGET, height: TARGET }, children: /* @__PURE__ */ jsx("span", { "data-gantt-shape": "milestone", "data-item": item.id, className: cn("block rotate-45 rounded-[2px] group-focus-visible:shadow-[var(--ds-shadow-focus)]", EDGE, tone.fill, isSelected && SELECTED), style: { width: DIAMOND, height: DIAMOND } }) })
12614
+ // the diamond draws the target's focus: its outline, which forced colours keep, so the target draws none of its own
12615
+ /* @__PURE__ */ jsx("div", { ...treeitem(row), "aria-selected": isSelected, className: "group absolute grid cursor-pointer place-items-center outline-none", style: { insetInlineStart: (shape.x0 + shape.x1) / 2 - TARGET / 2, top: (ROW_HEIGHT2 - TARGET) / 2, width: TARGET, height: TARGET }, children: /* @__PURE__ */ jsx("span", { "data-gantt-shape": "milestone", "data-item": item.id, className: cn("block rotate-45 rounded-[2px]", GROUP_FOCUS_RING, EDGE, tone.fill, isSelected && SELECTED), style: { width: DIAMOND, height: DIAMOND } }) })
12540
12616
  ) : (
12541
12617
  // TimeBar keeps the title in view beside the pinned column while the bar's start is scrolled away
12542
12618
  /* @__PURE__ */ jsx(
@@ -12558,12 +12634,16 @@ function Gantt({
12558
12634
  return /* @__PURE__ */ jsxs(Fragment, { children: [
12559
12635
  shapeBox,
12560
12636
  place !== "inside" && // Beside the bar, past the turn an arrow makes; its own background keeps lines that cross the row off the
12561
- // text. Once it is partly under the pinned column or past the view's end it is hidden, never a fragment.
12637
+ // text. Once it is partly under the pinned column or past the view's end it is hidden, never a fragment. On the
12638
+ // selected row its background takes the row's tint, as the label's does.
12562
12639
  /* @__PURE__ */ jsx(
12563
12640
  "span",
12564
12641
  {
12565
12642
  "aria-hidden": "true",
12566
- className: "pointer-events-none absolute top-1/2 flex -translate-y-1/2 items-center gap-ds-1 whitespace-nowrap rounded-[var(--ds-radius-sm)] bg-[var(--ds-bg-surface)] text-xs font-medium leading-4 text-[var(--ds-text-primary)]",
12643
+ className: cn(
12644
+ "pointer-events-none absolute top-1/2 flex -translate-y-1/2 items-center gap-ds-1 whitespace-nowrap rounded-[var(--ds-radius-sm)] bg-[var(--ds-bg-surface)] text-xs font-medium leading-4 text-[var(--ds-text-primary)]",
12645
+ isSelected && "bg-[image:linear-gradient(var(--ds-accent-subtle),var(--ds-accent-subtle))]"
12646
+ ),
12567
12647
  style: { insetInlineStart: beside, paddingInline: KNOCKOUT, opacity: `clamp(0, calc(min(${beside} - var(--time-scroll, 0), var(--time-view-end, 1e9) - ${beside + textWidth}) * 1000), 1)` },
12568
12648
  children: title
12569
12649
  }
@@ -12633,7 +12713,7 @@ function Gantt({
12633
12713
  children: /* @__PURE__ */ jsxs("div", { ref: treeRef, role: "tree", "aria-label": ariaLabel, className: "relative", onKeyDown, onFocus: () => setFocusWithin(true), onBlur: onTreeBlur, children: [
12634
12714
  /* @__PURE__ */ jsx("span", { ref: probeRef, "aria-hidden": "true", className: "pointer-events-none invisible absolute whitespace-nowrap text-xs font-medium", children: "0" }),
12635
12715
  /* @__PURE__ */ jsx(GanttArrows, { arrows, width: scale.width, height: rows.length * ROW_HEIGHT2 }),
12636
- rows.map((row) => /* @__PURE__ */ jsx(
12716
+ rows.map((row) => /* @__PURE__ */ jsxs(
12637
12717
  TimeRow,
12638
12718
  {
12639
12719
  "data-row-key": row.key,
@@ -12645,7 +12725,10 @@ function Gantt({
12645
12725
  row.item && row.item.id === selected && "bg-[image:linear-gradient(var(--ds-accent-subtle),var(--ds-accent-subtle))] text-[var(--ds-accent-subtle-text)]"
12646
12726
  ),
12647
12727
  onClick: () => onRowClick(row),
12648
- children: row.group ? renderGroup(row, row.group) : renderItem(row, row.item)
12728
+ children: [
12729
+ row.item && row.item.id === selected && /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "absolute inset-0 bg-[var(--ds-accent-subtle)]" }),
12730
+ row.group ? renderGroup(row, row.group) : renderItem(row, row.item)
12731
+ ]
12649
12732
  },
12650
12733
  row.key
12651
12734
  ))
@@ -12700,7 +12783,7 @@ var BUTTON_ELEVATION_SHADOW_ONLY = "shadow-[var(--ds-shadow-button)] active:shad
12700
12783
  var CORNER_SHAPE = cn(BUTTON_ELEVATION, "hover:-translate-y-[var(--ds-hover-lift)]");
12701
12784
  var LAUNCHER_RADIUS = "calc(var(--ds-radius-base) * 4)";
12702
12785
  var ACCENT_GLYPH = cn(
12703
- "text-[var(--ds-accent-primary)]",
12786
+ "text-[var(--ds-accent-ink)]",
12704
12787
  "[&_svg]:[stroke:url(#gs-launcher-gradient)]",
12705
12788
  "[&_svg[fill=currentColor]]:[fill:url(#gs-launcher-gradient-fill)] [&_svg[fill=currentColor]]:[stroke:none]"
12706
12789
  );
@@ -12864,7 +12947,9 @@ function Launcher({
12864
12947
  className: cn(
12865
12948
  container ? "absolute" : "fixed",
12866
12949
  "z-[var(--ds-z-sticky)] inline-flex items-center justify-center cursor-pointer select-none font-medium",
12867
- FOCUS_RING,
12950
+ // a ribbon is flush with the window's edge, where an outline outside it would be cut off: its outline sits inside
12951
+ // (D8), in its own text colour, which reads on its fill as the bold band's does (Sprint 30, P1)
12952
+ isRibbon ? cn(FOCUS_INSET, "[--ds-focus:currentColor]") : FOCUS_RING,
12868
12953
  "transition-[transform,filter,opacity,background-color] duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
12869
12954
  hidden && "invisible pointer-events-none",
12870
12955
  TONE_SURFACE[tone],
@@ -13024,7 +13109,19 @@ function useScrollTrack(activeTab, enabled) {
13024
13109
  ...fadeProps
13025
13110
  };
13026
13111
  }
13027
- var SCROLL_TRACK = "overflow-x-auto overflow-y-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden max-w-full";
13112
+ var SCROLL_TRACK = "overflow-x-auto overflow-y-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden";
13113
+ function TabStrip({ overflow, fullWidth, track, className, children }) {
13114
+ const scrolls = overflow === "scroll";
13115
+ return /* @__PURE__ */ jsx("div", { className: cn("min-w-0 max-w-full", fullWidth && "w-full", className), children: /* @__PURE__ */ jsx(
13116
+ "div",
13117
+ {
13118
+ ...scrolls ? track : {},
13119
+ "data-gs-clip": scrolls ? void 0 : "",
13120
+ className: cn("relative -m-[4px] p-[4px] scroll-px-[4px]", scrolls ? SCROLL_TRACK : "overflow-hidden"),
13121
+ children: /* @__PURE__ */ jsx("div", { role: "tablist", className: cn(trackClasses("horizontal"), scrolls ? "w-max min-w-full" : "w-full"), children })
13122
+ }
13123
+ ) });
13124
+ }
13028
13125
  function getTabsFromDOM(el) {
13029
13126
  const tablist = el.closest('[role="tablist"]');
13030
13127
  if (!tablist) return [];
@@ -13108,11 +13205,11 @@ function TabButton({
13108
13205
  disabled ? "cursor-not-allowed opacity-disabled" : "cursor-pointer",
13109
13206
  "rounded-[var(--ds-nav-v-item-radius)]",
13110
13207
  "transition-colors duration-[var(--ds-transition-speed)]",
13111
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
13208
+ FOCUS_RING,
13112
13209
  // Every item carries the rail width as a transparent placeholder so the
13113
13210
  // label does not shift when the rail appears on the active item.
13114
13211
  "border-l-[length:var(--ds-nav-v-active-rail)]",
13115
- isActive2 ? "bg-[var(--ds-nav-v-active-bg)] text-[var(--ds-nav-v-active-text)] border-[var(--ds-accent-primary)]" : "border-transparent text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-tab-hover-bg)]",
13212
+ isActive2 ? "bg-[var(--ds-nav-v-active-bg)] text-[var(--ds-nav-v-active-text)] border-[var(--ds-accent-ink)]" : "border-transparent text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-tab-hover-bg)]",
13116
13213
  className
13117
13214
  ),
13118
13215
  children: [
@@ -13148,11 +13245,11 @@ function TabButton({
13148
13245
  !fullWidth && !truncate && "shrink-0 whitespace-nowrap",
13149
13246
  disabled ? "cursor-not-allowed opacity-disabled" : "cursor-pointer",
13150
13247
  "transition-colors duration-[var(--ds-transition-speed)]",
13151
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
13248
+ FOCUS_RING,
13152
13249
  fullWidth && "flex-1",
13153
13250
  truncate && "flex-1 min-w-0 justify-start",
13154
13251
  "rounded-[var(--ds-nav-h-item-radius)]",
13155
- isActive2 ? "bg-[var(--ds-nav-h-active-bg)] text-[var(--ds-nav-h-active-text)] border-[var(--ds-accent-primary)]" : "text-[var(--ds-nav-h-inactive-text)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-nav-h-hover-bg)]",
13252
+ isActive2 ? "bg-[var(--ds-nav-h-active-bg)] text-[var(--ds-nav-h-active-text)] border-[var(--ds-accent-ink)]" : "text-[var(--ds-nav-h-inactive-text)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-nav-h-hover-bg)]",
13156
13253
  className
13157
13254
  ),
13158
13255
  style: isActive2 ? { borderBottomWidth: "var(--ds-nav-h-indicator)", marginBottom: "calc(var(--ds-nav-h-track-border) * -1)" } : void 0,
@@ -13184,22 +13281,7 @@ function TabsList({ children, className }) {
13184
13281
  }
13185
13282
  );
13186
13283
  }
13187
- return /* @__PURE__ */ jsx(
13188
- "div",
13189
- {
13190
- role: "tablist",
13191
- ...overflow === "scroll" ? track : {},
13192
- "data-gs-clip": overflow === "scroll" ? void 0 : "",
13193
- className: cn(
13194
- trackClasses("horizontal"),
13195
- "max-w-full",
13196
- overflow === "hidden" ? "overflow-hidden" : overflow === "truncate" ? "overflow-hidden" : SCROLL_TRACK,
13197
- fullWidth && "w-full",
13198
- className
13199
- ),
13200
- children
13201
- }
13202
- );
13284
+ return /* @__PURE__ */ jsx(TabStrip, { overflow, fullWidth, track, className, children });
13203
13285
  }
13204
13286
  function TabsTrigger({ value, children, count, icon, disabled, truncate, className }) {
13205
13287
  const { activeTab, onTabChange, orientation, fullWidth, overflow } = useTabsCtx();
@@ -13231,7 +13313,7 @@ function TabsContent({ value, children, className }) {
13231
13313
  role: "tabpanel",
13232
13314
  "aria-labelledby": `ds-tab-${value}`,
13233
13315
  tabIndex: 0,
13234
- className,
13316
+ className: cn(FOCUS_RING, className),
13235
13317
  children
13236
13318
  }
13237
13319
  );
@@ -13291,22 +13373,7 @@ function TabsRoot(props) {
13291
13373
  }
13292
13374
  );
13293
13375
  }
13294
- const effectiveOverflow = arrayOverflow;
13295
- return /* @__PURE__ */ jsx(
13296
- "div",
13297
- {
13298
- role: "tablist",
13299
- ...effectiveOverflow === "scroll" ? track : {},
13300
- "data-gs-clip": effectiveOverflow === "scroll" ? void 0 : "",
13301
- className: cn(
13302
- trackClasses("horizontal"),
13303
- effectiveOverflow === "scroll" ? SCROLL_TRACK : "overflow-hidden",
13304
- fullWidth && "w-full",
13305
- className
13306
- ),
13307
- children: tabs.map((tab) => /* @__PURE__ */ jsx(TabButton, { hasPanel: false, ...tab, orientation: effectiveOrientation, activeTab, onTabChange, fullWidth, onKeyDown: (e) => handleTabKeyDown(e, tab.id, () => tabs, effectiveOrientation, onTabChange) }, tab.id))
13308
- }
13309
- );
13376
+ return /* @__PURE__ */ jsx(TabStrip, { overflow: arrayOverflow, fullWidth, track, className, children: tabs.map((tab) => /* @__PURE__ */ jsx(TabButton, { hasPanel: false, ...tab, orientation: effectiveOrientation, activeTab, onTabChange, fullWidth, onKeyDown: (e) => handleTabKeyDown(e, tab.id, () => tabs, effectiveOrientation, onTabChange) }, tab.id)) });
13310
13377
  }
13311
13378
  return /* @__PURE__ */ jsx(TabsContext.Provider, { value: { activeTab, onTabChange, orientation: effectiveOrientation, fullWidth, overflow }, children: /* @__PURE__ */ jsx("div", { className, children: props.children }) });
13312
13379
  }
@@ -13454,7 +13521,7 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
13454
13521
  {
13455
13522
  className: cn(
13456
13523
  "text-xs font-medium",
13457
- status === "current" ? "text-[var(--ds-accent-primary)]" : kindOf2(step) === "locked" ? "text-[var(--ds-text-muted)]" : "text-[var(--ds-text-primary)]"
13524
+ status === "current" ? "text-[var(--ds-accent-ink)]" : kindOf2(step) === "locked" ? "text-[var(--ds-text-muted)]" : "text-[var(--ds-text-primary)]"
13458
13525
  ),
13459
13526
  children: step.label
13460
13527
  }
@@ -13487,7 +13554,7 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
13487
13554
  {
13488
13555
  className: cn(
13489
13556
  "text-xs font-medium leading-8",
13490
- status === "current" ? "text-[var(--ds-accent-primary)]" : kindOf2(step) === "locked" ? "text-[var(--ds-text-muted)]" : "text-[var(--ds-text-primary)]"
13557
+ status === "current" ? "text-[var(--ds-accent-ink)]" : kindOf2(step) === "locked" ? "text-[var(--ds-text-muted)]" : "text-[var(--ds-text-primary)]"
13491
13558
  ),
13492
13559
  children: step.label
13493
13560
  }
@@ -13534,7 +13601,7 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
13534
13601
  {
13535
13602
  className: cn(
13536
13603
  "text-xs font-medium",
13537
- status === "current" ? "text-[var(--ds-accent-primary)]" : kindOf2(step) === "locked" ? "text-[var(--ds-text-muted)]" : "text-[var(--ds-text-primary)]"
13604
+ status === "current" ? "text-[var(--ds-accent-ink)]" : kindOf2(step) === "locked" ? "text-[var(--ds-text-muted)]" : "text-[var(--ds-text-primary)]"
13538
13605
  ),
13539
13606
  children: step.label
13540
13607
  }
@@ -13552,9 +13619,10 @@ function StepCircle({ status, index, label, kind, onClick }) {
13552
13619
  const look = cn(
13553
13620
  "flex items-center justify-center shrink-0 w-8 h-8 rounded-[var(--ds-radius-full)] text-xs font-bold transition-colors",
13554
13621
  status === "complete" && "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] text-[var(--ds-text-on-primary)]",
13555
- status === "current" && `border-2 ${ACTIVE_RING} text-[var(--ds-accent-primary)] bg-[var(--ds-bg-surface)]`,
13622
+ // the current step is chosen, not active (D2): the accent border, never the halo
13623
+ status === "current" && "border-2 border-[var(--ds-accent-ink)] text-[var(--ds-accent-ink)] bg-[var(--ds-bg-surface)]",
13556
13624
  status === "error" && "bg-[var(--ds-status-error)] text-[var(--ds-text-on-error)]",
13557
- status === "loading" && `border-2 ${ACTIVE_RING} text-[var(--ds-accent-primary)] bg-[var(--ds-bg-surface)] animate-pulse`,
13625
+ status === "loading" && "border-2 border-[var(--ds-accent-ink)] text-[var(--ds-accent-ink)] bg-[var(--ds-bg-surface)] animate-pulse",
13558
13626
  status === "upcoming" && "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)] text-[var(--ds-text-muted)] bg-[var(--ds-bg-surface)]"
13559
13627
  );
13560
13628
  const glyph = status === "complete" ? /* @__PURE__ */ jsx(Icon, { name: "check", size: 14 }) : status === "error" ? /* @__PURE__ */ jsx(Icon, { name: "warning", size: 14 }) : index + 1;
@@ -13571,10 +13639,10 @@ function StepCircle({ status, index, label, kind, onClick }) {
13571
13639
  "aria-current": status === "current" ? "step" : void 0,
13572
13640
  className: cn(
13573
13641
  look,
13574
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
13642
+ FOCUS_RING,
13575
13643
  kind === "open" ? "cursor-pointer" : "cursor-not-allowed opacity-disabled",
13576
13644
  // a step reached but not done (the user went back) can be gone to: it answers the pointer as the done ones do
13577
- status === "upcoming" && kind === "open" && "hover:border-[var(--ds-accent-primary)] hover:text-[var(--ds-accent-primary)]"
13645
+ status === "upcoming" && kind === "open" && "hover:border-[var(--ds-accent-ink)] hover:text-[var(--ds-accent-ink)]"
13578
13646
  ),
13579
13647
  children: glyph
13580
13648
  }
@@ -13588,6 +13656,12 @@ function ScrollArea({
13588
13656
  maxWidth
13589
13657
  }) {
13590
13658
  const viewportRef = useRef(null);
13659
+ const [viewport, setViewport] = useState(null);
13660
+ useRevealFocus(viewport);
13661
+ const viewportRefs = useCallback((node) => {
13662
+ viewportRef.current = node;
13663
+ setViewport(node);
13664
+ }, []);
13591
13665
  const [thumbY, setThumbY] = useState({ top: 0, height: 0, visible: false });
13592
13666
  const [thumbX, setThumbX] = useState({ left: 0, width: 0, visible: false });
13593
13667
  const [hovering, setHovering] = useState(false);
@@ -13675,10 +13749,10 @@ function ScrollArea({
13675
13749
  /* @__PURE__ */ jsx(
13676
13750
  "div",
13677
13751
  {
13678
- ref: viewportRef,
13752
+ ref: viewportRefs,
13679
13753
  onScroll: updateThumbs,
13680
13754
  className: cn(
13681
- "h-full w-full",
13755
+ "h-full w-full scroll-p-[4px]",
13682
13756
  orientation === "vertical" && "overflow-y-auto overflow-x-hidden",
13683
13757
  orientation === "horizontal" && "overflow-x-auto overflow-y-hidden",
13684
13758
  orientation === "both" && "overflow-auto",
@@ -13802,6 +13876,8 @@ var Sidebar = forwardRef(function Sidebar2({
13802
13876
  "flex flex-col shrink-0 h-full",
13803
13877
  isFloating ? cn("rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-sidebar-border)]", GLASS_SURFACE) : !isMinimal && "border-r-[length:var(--ds-border-width)] border-[var(--ds-sidebar-border)]",
13804
13878
  "text-[var(--ds-sidebar-text)]",
13879
+ // keyboard focus in the sidebar's own focus colour, derived against its background (Sprint 30, P2)
13880
+ "[--ds-focus:var(--ds-sidebar-focus)]",
13805
13881
  isMinimal ? "bg-transparent" : "bg-[var(--ds-sidebar-bg)]",
13806
13882
  !isMinimal && !isFloating && GLASS_BACKDROP,
13807
13883
  !isFloating && "transition-[width] duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
@@ -13840,7 +13916,7 @@ var Sidebar = forwardRef(function Sidebar2({
13840
13916
  isSm ? "py-ds-1.5 text-xs" : "py-ds-2 text-sm",
13841
13917
  "transition-colors cursor-pointer",
13842
13918
  "text-[var(--ds-sidebar-text-muted)] hover:bg-[var(--ds-sidebar-item-hover)] hover:text-[var(--ds-sidebar-text)]",
13843
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
13919
+ FOCUS_RING,
13844
13920
  collapsed ? "justify-center px-0" : "px-ds-3"
13845
13921
  ),
13846
13922
  style: { marginBottom: "var(--ds-space-3)" },
@@ -14004,6 +14080,7 @@ function CompactFlyout({
14004
14080
  },
14005
14081
  className: cn(
14006
14082
  "flex items-center gap-ds-2 px-ds-2 py-ds-1.5 text-xs rounded-[var(--ds-radius-sm)] transition-colors cursor-pointer",
14083
+ FOCUS_RING,
14007
14084
  child.active ? "bg-[var(--ds-sidebar-active-bg)] [background-image:var(--ds-sidebar-active-image)] text-[var(--ds-sidebar-active-accent)] font-medium" : "text-[var(--ds-sidebar-text-secondary)] hover:bg-[var(--ds-sidebar-item-hover)] hover:text-[var(--ds-sidebar-text)]"
14008
14085
  ),
14009
14086
  children: [
@@ -14102,6 +14179,7 @@ function SidebarNav({
14102
14179
  },
14103
14180
  className: cn(
14104
14181
  "flex items-center gap-ds-2 transition-colors cursor-pointer",
14182
+ FOCUS_RING,
14105
14183
  isRoot ? cn(rootPy, rootText, isMinimal ? "rounded-[var(--ds-radius-full)]" : "rounded-[var(--ds-radius-md)]") : cn(childPy, "text-xs rounded-[var(--ds-radius-sm)]"),
14106
14184
  isRoot ? collapsed ? "justify-center px-0" : "px-ds-3" : "px-ds-2",
14107
14185
  // Active style comes from the theme's --ds-sidebar-active-* tokens
@@ -14188,6 +14266,7 @@ function SidebarNav({
14188
14266
  "aria-expanded": isExpanded,
14189
14267
  className: cn(
14190
14268
  "flex items-center gap-ds-2 w-full text-left transition-colors cursor-pointer",
14269
+ FOCUS_RING,
14191
14270
  isRoot ? cn(rootPy, rootText, isMinimal ? "rounded-[var(--ds-radius-full)]" : "rounded-[var(--ds-radius-md)]") : cn(childPy, "text-xs rounded-[var(--ds-radius-sm)]"),
14192
14271
  isRoot ? collapsed ? "justify-center px-0" : "px-ds-3" : "px-ds-2",
14193
14272
  // This row has no fill, so its label, icon and chevron keep the plain
@@ -14201,7 +14280,7 @@ function SidebarNav({
14201
14280
  {
14202
14281
  name: item.icon,
14203
14282
  size: isRoot ? rootIconSize : childIconSize,
14204
- className: cn("shrink-0", (item.active || childActive) && "text-[var(--ds-sidebar-accent)]")
14283
+ className: cn("shrink-0", (item.active || childActive) && "text-[var(--ds-sidebar-accent-ink)]")
14205
14284
  }
14206
14285
  ),
14207
14286
  !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -14214,7 +14293,7 @@ function SidebarNav({
14214
14293
  size: 12,
14215
14294
  className: cn(
14216
14295
  "shrink-0 transition-transform",
14217
- item.active || childActive ? "text-[var(--ds-sidebar-accent)]" : "text-[var(--ds-sidebar-text-muted)]",
14296
+ item.active || childActive ? "text-[var(--ds-sidebar-accent-ink)]" : "text-[var(--ds-sidebar-text-muted)]",
14218
14297
  isExpanded && "rotate-90"
14219
14298
  )
14220
14299
  }
@@ -14307,7 +14386,7 @@ function SidebarNav({
14307
14386
  /* @__PURE__ */ jsx(
14308
14387
  "nav",
14309
14388
  {
14310
- className: "flex-1 flex flex-col",
14389
+ className: "flex-1 flex flex-col [--ds-focus:var(--ds-sidebar-focus)]",
14311
14390
  style: { padding: "var(--ds-space-3) var(--ds-space-2)", gap: "var(--ds-space-3)" },
14312
14391
  children: navContent
14313
14392
  }
@@ -14322,6 +14401,8 @@ function SidebarNav({
14322
14401
  "flex items-center gap-ds-2 mx-ds-2 rounded-[var(--ds-radius-md)] transition-colors cursor-pointer",
14323
14402
  isSm ? "py-ds-1.5 text-xs" : "py-ds-2 text-sm",
14324
14403
  "text-[var(--ds-sidebar-text-muted)] hover:bg-[var(--ds-sidebar-item-hover)] hover:text-[var(--ds-sidebar-text)]",
14404
+ "[--ds-focus:var(--ds-sidebar-focus)]",
14405
+ FOCUS_RING,
14325
14406
  collapsed ? "justify-center px-0" : "px-ds-3"
14326
14407
  ),
14327
14408
  style: { marginBottom: "var(--ds-space-3)" },
@@ -14398,6 +14479,7 @@ function CompactNavItem({
14398
14479
  "aria-controls": !mobile && hasChildren && isOpen ? flyoutId : void 0,
14399
14480
  className: cn(
14400
14481
  "relative flex flex-col items-center justify-center gap-[3px] w-full py-ds-3 px-ds-1 rounded-[var(--ds-radius-md)] transition-colors cursor-pointer",
14482
+ FOCUS_RING,
14401
14483
  // Compact tiles have no edge to stroke: they read the fill and text
14402
14484
  // tokens only, never --ds-sidebar-active-rail.
14403
14485
  item.active || childActive ? "bg-[var(--ds-sidebar-active-bg)] [background-image:var(--ds-sidebar-active-image)] text-[var(--ds-sidebar-active-accent)]" : "text-[var(--ds-sidebar-text-secondary)] hover:bg-[var(--ds-sidebar-item-hover)] hover:text-[var(--ds-sidebar-text)]"
@@ -14449,6 +14531,7 @@ function CompactNavItem({
14449
14531
  onClick: () => onItemClick?.(child, ci),
14450
14532
  className: cn(
14451
14533
  "flex items-center gap-ds-2 px-ds-3 py-ds-1.5 text-[length:var(--ds-font-size-xs)] rounded-[var(--ds-radius-sm)] transition-colors cursor-pointer w-full",
14534
+ FOCUS_RING,
14452
14535
  child.active ? "bg-[var(--ds-sidebar-active-bg)] [background-image:var(--ds-sidebar-active-image)] text-[var(--ds-sidebar-active-accent)] font-medium" : "text-[var(--ds-sidebar-text-muted)] hover:bg-[var(--ds-sidebar-item-hover)] hover:text-[var(--ds-sidebar-text)]"
14453
14536
  ),
14454
14537
  children: [
@@ -14466,6 +14549,12 @@ var variantStyles4 = {
14466
14549
  warning: "border-[var(--ds-status-warning)] bg-[var(--ds-status-warning-bg)]",
14467
14550
  error: "border-[var(--ds-status-error)] bg-[var(--ds-status-error-bg)]"
14468
14551
  };
14552
+ var actionStyles = {
14553
+ default: "text-[var(--ds-accent-ink)]",
14554
+ success: "text-[var(--ds-status-success-text)]",
14555
+ warning: "text-[var(--ds-status-warning-text)]",
14556
+ error: "text-[var(--ds-status-error-text)]"
14557
+ };
14469
14558
  var variantIcons = {
14470
14559
  default: { name: "info", className: "text-[var(--ds-text-muted)]" },
14471
14560
  success: { name: "check", className: "text-[var(--ds-status-success)]" },
@@ -14573,7 +14662,7 @@ function Toast({ variant = "default", message, visible, onDismiss, duration = 5e
14573
14662
  {
14574
14663
  type: "button",
14575
14664
  onClick: pressAction,
14576
- className: "shrink-0 text-xs font-semibold text-[var(--ds-accent-primary)] hover:underline cursor-pointer",
14665
+ className: cn("shrink-0 text-xs font-semibold hover:underline cursor-pointer", actionStyles[variant], FOCUS_RING),
14577
14666
  children: action.label
14578
14667
  }
14579
14668
  ),
@@ -14583,7 +14672,7 @@ function Toast({ variant = "default", message, visible, onDismiss, duration = 5e
14583
14672
  type: "button",
14584
14673
  onClick: onDismiss,
14585
14674
  "aria-label": "Dismiss notification",
14586
- className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer shrink-0",
14675
+ className: cn("text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer shrink-0", FOCUS_RING),
14587
14676
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
14588
14677
  }
14589
14678
  )
@@ -14961,7 +15050,7 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
14961
15050
  waiting,
14962
15051
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: " more waiting" })
14963
15052
  ] }),
14964
- /* @__PURE__ */ jsx("button", { type: "button", onClick: dismissAll, className: "font-medium text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:underline cursor-pointer", children: "Clear all" })
15053
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: dismissAll, className: cn("font-medium text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:underline cursor-pointer", FOCUS_RING), children: "Clear all" })
14965
15054
  ] })
14966
15055
  ]
14967
15056
  }
@@ -15014,7 +15103,9 @@ function BottomNav({ items, onItemClick, maxVisible = 5, className, ref }) {
15014
15103
  className: cn(
15015
15104
  "flex flex-col items-center justify-center gap-ds-0.5 w-full h-full",
15016
15105
  "text-2xs font-medium transition-colors cursor-pointer",
15017
- overflowHasActive || overflowOpen ? "text-[var(--ds-accent-primary)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"
15106
+ // flush with the window's edge, as every bar item is: the outline sits inside (D8)
15107
+ FOCUS_INSET,
15108
+ overflowHasActive || overflowOpen ? "text-[var(--ds-accent-ink)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"
15018
15109
  ),
15019
15110
  children: [
15020
15111
  /* @__PURE__ */ jsx(Icon, { name: "grip-vertical", size: 20 }),
@@ -15043,9 +15134,11 @@ function BottomNav({ items, onItemClick, maxVisible = 5, className, ref }) {
15043
15134
  },
15044
15135
  className: cn(
15045
15136
  "flex items-center gap-ds-2 w-full px-ds-3 py-ds-2 text-sm transition-colors cursor-pointer",
15046
- // The panel clips, so focus is a row tint (never an outline that the corner would trim).
15047
- "focus-visible:outline-none focus-visible:bg-[var(--ds-bg-muted)] focus-visible:text-[var(--ds-text-primary)]",
15048
- item.active ? "text-[var(--ds-accent-primary)] font-medium bg-[var(--ds-bg-muted)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]"
15137
+ // Keyboard focus keeps the row tint and adds the shared outline, inside the row: the menu clips its
15138
+ // full-width rows, which are never filled solid (Sprint 30, P3)
15139
+ "focus-visible:bg-[var(--ds-bg-muted)] focus-visible:text-[var(--ds-text-primary)]",
15140
+ FOCUS_INSET,
15141
+ item.active ? "text-[var(--ds-accent-ink)] font-medium bg-[var(--ds-bg-muted)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]"
15049
15142
  ),
15050
15143
  children: [
15051
15144
  /* @__PURE__ */ jsx(Icon, { name: item.icon, size: 16 }),
@@ -15070,7 +15163,9 @@ function NavButton({ item, onClick }) {
15070
15163
  className: cn(
15071
15164
  "flex flex-col items-center justify-center gap-ds-0.5 flex-1 h-full",
15072
15165
  "text-2xs font-medium transition-colors cursor-pointer",
15073
- item.active ? "text-[var(--ds-accent-primary)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"
15166
+ // flush with the window's edge (the bar's foot and its ends): the outline sits inside, where it shows whole (D8)
15167
+ FOCUS_INSET,
15168
+ item.active ? "text-[var(--ds-accent-ink)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"
15074
15169
  ),
15075
15170
  children: [
15076
15171
  /* @__PURE__ */ jsx(Icon, { name: item.icon, size: 20 }),
@@ -15401,7 +15496,7 @@ function outerHeight(el) {
15401
15496
  function Breadcrumbs2({ items, onLight, onTint, pill, separator = "chevron" }) {
15402
15497
  const follow2 = useCrumbFollow();
15403
15498
  if (!items.length) return null;
15404
- const mutedCls = onLight ? "text-[var(--ds-text-on-primary)]/40" : onTint ? "text-[var(--ds-text-muted-on-tint)]" : "text-[var(--ds-text-secondary)]";
15499
+ const mutedCls = onLight ? "text-[var(--ds-text-on-primary-muted)]" : onTint ? "text-[var(--ds-text-muted-on-tint)]" : "text-[var(--ds-text-secondary)]";
15405
15500
  const sepEl = (key) => {
15406
15501
  const cls = cn("select-none flex items-center", mutedCls);
15407
15502
  if (separator === "slash") return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cls, children: "/" }, `s${key}`);
@@ -15425,13 +15520,15 @@ function Breadcrumbs2({ items, onLight, onTint, pill, separator = "chevron" }) {
15425
15520
  onClick: (e) => follow2(e, item),
15426
15521
  className: cn(
15427
15522
  "transition-colors duration-[var(--ds-transition-speed)]",
15428
- onLight ? "text-[var(--ds-text-on-primary)]/60 hover:text-[var(--ds-text-on-primary)]/90" : cn(mutedCls, "hover:text-[var(--ds-text-primary)]"),
15429
- pillCls
15523
+ // on the band, the band's quieter text, its full text on hover (the quieter text can be 90%)
15524
+ onLight ? "text-[var(--ds-text-on-primary-muted)] hover:text-[var(--ds-text-on-primary)]" : cn(mutedCls, "hover:text-[var(--ds-text-primary)]"),
15525
+ pillCls,
15526
+ FOCUS_RING
15430
15527
  ),
15431
15528
  children: item.label
15432
15529
  }
15433
15530
  ) : /* @__PURE__ */ jsx("span", { "aria-current": isCurrent ? "page" : void 0, className: cn(
15434
- isCurrent ? cn("font-medium", onLight ? "text-[var(--ds-text-on-primary)]/80" : "text-[var(--ds-text-primary)]") : onLight ? "text-[var(--ds-text-on-primary)]/60" : mutedCls,
15531
+ isCurrent ? cn("font-medium", onLight ? "text-[var(--ds-text-on-primary-muted)]" : "text-[var(--ds-text-primary)]") : onLight ? "text-[var(--ds-text-on-primary-muted)]" : mutedCls,
15435
15532
  pillCls
15436
15533
  ), children: item.label })
15437
15534
  ] }, i);
@@ -15462,8 +15559,8 @@ function BackButton({ onLight, onClick }) {
15462
15559
  "flex items-center justify-center shrink-0 cursor-pointer",
15463
15560
  "w-[var(--ds-control-height-sm)] h-[var(--ds-control-height-sm)] rounded-[var(--ds-radius-md)]",
15464
15561
  "transition-colors duration-[var(--ds-transition-speed)]",
15465
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
15466
- onLight ? "bg-[var(--ds-text-on-primary)]/10 text-[var(--ds-text-on-primary)]/80 hover:bg-[var(--ds-text-on-primary)]/20 hover:text-[var(--ds-text-on-primary)]" : [
15562
+ FOCUS_RING,
15563
+ onLight ? "bg-[var(--ds-text-on-primary)]/10 text-[var(--ds-text-on-primary-muted)] hover:bg-[var(--ds-text-on-primary)]/20 hover:text-[var(--ds-text-on-primary)]" : [
15467
15564
  "bg-[var(--ds-bg-surface)] text-[var(--ds-text-secondary)]",
15468
15565
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
15469
15566
  "hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
@@ -15481,8 +15578,9 @@ function BackLink({ onClick, onLight }) {
15481
15578
  onClick,
15482
15579
  className: cn(
15483
15580
  "inline-flex items-center gap-ds-1 mb-ds-3 text-sm underline underline-offset-4 cursor-pointer",
15484
- "rounded-[var(--ds-radius-sm)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
15485
- onLight ? "text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-primary)] hover:text-[var(--ds-accent-primary)]"
15581
+ "rounded-[var(--ds-radius-sm)]",
15582
+ FOCUS_RING,
15583
+ onLight ? "text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-primary)] hover:text-[var(--ds-accent-ink)]"
15486
15584
  ),
15487
15585
  children: [
15488
15586
  /* @__PURE__ */ jsx(Icon, { name: "chevron-left", size: 14 }),
@@ -15494,11 +15592,13 @@ function BackLink({ onClick, onLight }) {
15494
15592
  function Caption({ children, onLight, onTint }) {
15495
15593
  return /* @__PURE__ */ jsx("p", { className: cn(
15496
15594
  "mb-ds-1 text-sm font-medium",
15497
- onLight ? "text-[var(--ds-text-on-primary)]/80" : onTint ? "text-[var(--ds-text-muted-on-tint)]" : "text-[var(--ds-text-secondary)]"
15595
+ onLight ? "text-[var(--ds-text-on-primary-muted)]" : onTint ? "text-[var(--ds-text-muted-on-tint)]" : "text-[var(--ds-text-secondary)]"
15498
15596
  ), children });
15499
15597
  }
15500
15598
  var ON_BAND_STRIP = cn(
15501
15599
  "rounded-[var(--ds-radius-md)] px-ds-3 py-ds-2 border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
15600
+ // on the surface, the page's focus colour again, which the band kept for it (Sprint 30, P2)
15601
+ "[--ds-focus:var(--gs-page-focus)]",
15502
15602
  "bg-[var(--ds-bg-surface)] supports-[color:oklch(from_red_l_c_h)]:bg-[oklch(from_var(--ds-bg-surface)_l_c_h_/_max(alpha,min(1,alpha*1000)*0.6))]",
15503
15603
  GLASS_SURFACE
15504
15604
  );
@@ -15875,7 +15975,7 @@ function ColourfulHeader({
15875
15975
  className: cn(
15876
15976
  "relative overflow-hidden w-full",
15877
15977
  !isFloating && !overlapActive && !noBottomMargin && "mb-ds-6",
15878
- isBold ? "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] [background-origin:border-box]" : "bg-[var(--ds-tint-base)]",
15978
+ isBold ? "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] [background-origin:border-box] [--gs-page-focus:var(--ds-focus)]" : "bg-[var(--ds-tint-base)]",
15879
15979
  isFloating ? cn(
15880
15980
  "rounded-[var(--ds-radius-md)]",
15881
15981
  "border-[length:var(--ds-border-width)]",
@@ -15906,7 +16006,9 @@ function ColourfulHeader({
15906
16006
  className: cn(
15907
16007
  "relative z-10 w-full mx-auto pb-ds-6",
15908
16008
  gutter && "px-ds-6",
15909
- hasBreadcrumbs ? "pt-ds-3" : "pt-ds-6"
16009
+ hasBreadcrumbs ? "pt-ds-3" : "pt-ds-6",
16010
+ // on the band, keyboard focus is the band's text colour, the one colour proven against all it paints (P1, P2)
16011
+ isBold && "[--ds-focus:var(--ds-text-on-primary)]"
15910
16012
  ),
15911
16013
  style: {
15912
16014
  maxWidth: gutter ? contentMaxWidth ?? "var(--ds-bp-xl)" : void 0,
@@ -15936,7 +16038,7 @@ function ColourfulHeader({
15936
16038
  ] }),
15937
16039
  description && /* @__PURE__ */ jsx(ClampedText, { className: cn(
15938
16040
  "mt-ds-1 text-sm",
15939
- isBold ? "text-[var(--ds-text-on-primary)]/70" : "text-[var(--ds-text-secondary)]"
16041
+ isBold ? "text-[var(--ds-text-on-primary-muted)]" : "text-[var(--ds-text-secondary)]"
15940
16042
  ), children: description })
15941
16043
  ] })
15942
16044
  ] }),
@@ -16090,11 +16192,11 @@ function SnappingHeader({
16090
16192
  "relative overflow-hidden",
16091
16193
  "rounded-[var(--ds-radius-md)]",
16092
16194
  "border-[length:var(--ds-border-width)]",
16093
- isBoldColourful ? "border-[color-mix(in_srgb,var(--ds-accent-primary)_70%,black)] bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] [background-origin:border-box]" : cn("border-[var(--ds-border-subtle)] bg-[var(--ds-tint-base)]", GLASS_SURFACE)
16195
+ isBoldColourful ? "border-[color-mix(in_srgb,var(--ds-accent-primary)_70%,black)] bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] [background-origin:border-box] [--gs-page-focus:var(--ds-focus)]" : cn("border-[var(--ds-border-subtle)] bg-[var(--ds-tint-base)]", GLASS_SURFACE)
16094
16196
  ] : [
16095
16197
  "relative overflow-hidden",
16096
16198
  !overlapActive && !wrapperHasCard && "mb-ds-6",
16097
- isBoldColourful ? "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] [background-origin:border-box] border-b-[2px] border-[color-mix(in_srgb,var(--ds-accent-primary)_70%,black)]" : "bg-[var(--ds-tint-base)] border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]"
16199
+ isBoldColourful ? "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] [background-origin:border-box] border-b-[2px] border-[color-mix(in_srgb,var(--ds-accent-primary)_70%,black)] [--gs-page-focus:var(--ds-focus)]" : "bg-[var(--ds-tint-base)] border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]"
16098
16200
  ]),
16099
16201
  !collapsed && !isColourful && isFloating && !wrapperHasCard && FLOATING_HEADER,
16100
16202
  className
@@ -16137,6 +16239,8 @@ function SnappingHeader({
16137
16239
  className: cn(
16138
16240
  "transition-all duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
16139
16241
  isColourful && !collapsed && "relative z-10",
16242
+ // on the band, keyboard focus is the band's text colour, as the colourful header's (P1, P2)
16243
+ onLight && "[--ds-focus:var(--ds-text-on-primary)]",
16140
16244
  collapsed ? cn("flex items-center justify-between gap-ds-4 py-ds-2", !effectiveMaxWidth && "px-ds-4") : [
16141
16245
  (!isColourful || !overlapActive) && "pb-ds-6",
16142
16246
  hasBreadcrumbs && !hideExpandedBreadcrumbs ? "pt-ds-3" : "pt-ds-6"
@@ -16171,7 +16275,7 @@ function SnappingHeader({
16171
16275
  } : {},
16172
16276
  className: cn(
16173
16277
  "text-sm hidden @sm:inline-flex items-center gap-ds-1",
16174
- isClickable ? "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] transition-colors duration-[var(--ds-transition-speed)] cursor-pointer" : "text-[var(--ds-text-secondary)]"
16278
+ isClickable ? cn("text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] transition-colors duration-[var(--ds-transition-speed)] cursor-pointer", FOCUS_RING) : "text-[var(--ds-text-secondary)]"
16175
16279
  ),
16176
16280
  children: [
16177
16281
  lastCrumb?.label,
@@ -16188,7 +16292,7 @@ function SnappingHeader({
16188
16292
  ] }),
16189
16293
  !collapsed && description && /* @__PURE__ */ jsx(ClampedText, { className: cn(
16190
16294
  "mt-ds-1 text-sm",
16191
- onLight ? "text-[var(--ds-text-on-primary)]/70" : "text-[var(--ds-text-secondary)]"
16295
+ onLight ? "text-[var(--ds-text-on-primary-muted)]" : "text-[var(--ds-text-secondary)]"
16192
16296
  ), children: description })
16193
16297
  ] })
16194
16298
  ] }),
@@ -16382,7 +16486,7 @@ function Dock({
16382
16486
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
16383
16487
  "shadow-[var(--ds-shadow-button)]",
16384
16488
  "cursor-pointer origin-bottom",
16385
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
16489
+ FOCUS_RING
16386
16490
  ),
16387
16491
  style: {
16388
16492
  width: baseSize,
@@ -16442,7 +16546,7 @@ var NavItem = forwardRef(function NavItem2({ label, icon, active = false, href,
16442
16546
  "inline-flex items-center gap-ds-2 cursor-pointer select-none whitespace-nowrap",
16443
16547
  // one line: the navbar measures each link (round 12)
16444
16548
  "transition-colors duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
16445
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
16549
+ FOCUS_RING,
16446
16550
  "text-[length:var(--ds-font-size-sm)]"
16447
16551
  );
16448
16552
  const typeClasses = getTypeClasses(type, active);
@@ -16477,12 +16581,12 @@ function getTypeClasses(type, active) {
16477
16581
  case "minimal":
16478
16582
  return cn(
16479
16583
  "px-ds-2 py-ds-1",
16480
- active ? "text-[var(--ds-accent-primary)] font-medium" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-secondary)]"
16584
+ active ? "text-[var(--ds-accent-ink)] font-medium" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-secondary)]"
16481
16585
  );
16482
16586
  case "tint":
16483
16587
  return cn(
16484
16588
  "px-ds-2 py-ds-1 rounded-[var(--ds-radius-sm)]",
16485
- active ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
16589
+ active ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-ink)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
16486
16590
  );
16487
16591
  }
16488
16592
  }
@@ -16562,7 +16666,7 @@ function NavMenu({
16562
16666
  const triggerClasses = cn(
16563
16667
  "inline-flex items-center gap-ds-1 cursor-pointer select-none whitespace-nowrap",
16564
16668
  "transition-colors duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
16565
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
16669
+ FOCUS_RING,
16566
16670
  navItemClasses(type, hasActiveChild),
16567
16671
  className
16568
16672
  );
@@ -16651,9 +16755,10 @@ function NavMenuItemRow({
16651
16755
  },
16652
16756
  className: cn(
16653
16757
  "flex items-center gap-ds-2 mx-ds-1 px-ds-2 py-ds-1.5 rounded-[var(--ds-radius-sm)] transition-colors cursor-pointer",
16758
+ FOCUS_RING,
16654
16759
  // The active row takes the accent tint like an active NavItem; it marks
16655
16760
  // the current page, not a hover.
16656
- item.active ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-primary)] font-medium" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]"
16761
+ item.active ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-ink)] font-medium" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]"
16657
16762
  ),
16658
16763
  children: [
16659
16764
  item.icon && /* @__PURE__ */ jsx(
@@ -16661,7 +16766,7 @@ function NavMenuItemRow({
16661
16766
  {
16662
16767
  name: item.icon,
16663
16768
  size: 16,
16664
- className: cn("shrink-0", item.active && "text-[var(--ds-accent-primary)]")
16769
+ className: cn("shrink-0", item.active && "text-[var(--ds-accent-ink)]")
16665
16770
  }
16666
16771
  ),
16667
16772
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0", children: [
@@ -16710,7 +16815,7 @@ function HamburgerButton({ onClick, open, className: cls }) {
16710
16815
  "w-10 h-10 rounded-[var(--ds-radius-md)]",
16711
16816
  "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)]",
16712
16817
  "hover:bg-[var(--ds-bg-muted)] transition-colors",
16713
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
16818
+ FOCUS_RING,
16714
16819
  "cursor-pointer",
16715
16820
  cls
16716
16821
  ),
@@ -16923,7 +17028,7 @@ var Navbar = forwardRef(function Navbar2({
16923
17028
  showHamburger && /* @__PURE__ */ jsx(HamburgerButton, { onClick: handleHamburger, open: appShell?.sidebarOpen }),
16924
17029
  logo && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0 whitespace-nowrap", children: logo })
16925
17030
  ] }),
16926
- navLinks && !hideNavLinks ? /* @__PURE__ */ jsx(NavbarTypeContext.Provider, { value: type, children: /* @__PURE__ */ jsx("nav", { ref: navRef, className: "flex items-center gap-ds-2 min-w-0 overflow-x-clip justify-center", "data-navbar-type": type, children: linkRow }) }) : /* @__PURE__ */ jsx("div", { "data-navbar-spacer": "" }),
17031
+ navLinks && !hideNavLinks ? /* @__PURE__ */ jsx(NavbarTypeContext.Provider, { value: type, children: /* @__PURE__ */ jsx("nav", { ref: navRef, className: "flex items-center gap-ds-2 min-w-0 overflow-x-clip px-[4px] -mx-[4px] justify-center", "data-navbar-type": type, children: linkRow }) }) : /* @__PURE__ */ jsx("div", { "data-navbar-spacer": "" }),
16927
17032
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-4 justify-end", children: [
16928
17033
  shownSearch && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: shownSearch }),
16929
17034
  actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }),
@@ -16951,7 +17056,7 @@ var Navbar = forwardRef(function Navbar2({
16951
17056
  showHamburger && /* @__PURE__ */ jsx(HamburgerButton, { onClick: handleHamburger, open: appShell?.sidebarOpen }),
16952
17057
  logo && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0 whitespace-nowrap", children: logo }),
16953
17058
  align === "right" && /* @__PURE__ */ jsx("div", { "data-navbar-spacer": "", className: "flex-1" }),
16954
- navLinks && !hideNavLinks && /* @__PURE__ */ jsx(NavbarTypeContext.Provider, { value: type, children: /* @__PURE__ */ jsx("nav", { ref: navRef, className: cn("flex items-center gap-ds-2 min-w-0 overflow-x-clip", align === "left" && "flex-1"), "data-navbar-type": type, children: linkRow }) }),
17059
+ navLinks && !hideNavLinks && /* @__PURE__ */ jsx(NavbarTypeContext.Provider, { value: type, children: /* @__PURE__ */ jsx("nav", { ref: navRef, className: cn("flex items-center gap-ds-2 min-w-0 overflow-x-clip px-[4px] -mx-[4px]", align === "left" && "flex-1"), "data-navbar-type": type, children: linkRow }) }),
16955
17060
  (!navLinks || hideNavLinks) && /* @__PURE__ */ jsx("div", { "data-navbar-spacer": "", className: "flex-1" }),
16956
17061
  shownSearch && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: shownSearch }),
16957
17062
  actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }),
@@ -17009,6 +17114,7 @@ function MobileMenuItem({
17009
17114
  className: cn(
17010
17115
  "flex items-center gap-ds-3 px-ds-3 py-ds-3 text-sm relative cursor-pointer w-full text-left",
17011
17116
  "transition-colors duration-[var(--ds-transition-speed)]",
17117
+ FOCUS_RING,
17012
17118
  "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
17013
17119
  item.active ? "text-[var(--ds-text-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"
17014
17120
  ),
@@ -17034,7 +17140,8 @@ function MobileMenuItem({
17034
17140
  className: cn(
17035
17141
  "flex items-center gap-ds-3 px-ds-3 py-ds-2.5 text-sm cursor-pointer w-full text-left",
17036
17142
  "transition-colors duration-[var(--ds-transition-speed)]",
17037
- item.active ? "text-[var(--ds-accent-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-secondary)]"
17143
+ FOCUS_RING,
17144
+ item.active ? "text-[var(--ds-accent-ink)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-secondary)]"
17038
17145
  ),
17039
17146
  children: [
17040
17147
  item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon, size: 18, className: "shrink-0" }),
@@ -17052,6 +17159,7 @@ function MobileMenuItem({
17052
17159
  className: cn(
17053
17160
  "flex items-center gap-ds-3 px-ds-3 py-ds-2.5 text-sm rounded-[var(--ds-radius-md)] cursor-pointer w-full text-left",
17054
17161
  "transition-colors duration-[var(--ds-transition-speed)]",
17162
+ FOCUS_RING,
17055
17163
  item.active ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
17056
17164
  ),
17057
17165
  children: [
@@ -17069,7 +17177,8 @@ function MobileMenuItem({
17069
17177
  className: cn(
17070
17178
  "flex items-center gap-ds-3 px-ds-3 py-ds-2.5 text-sm rounded-[var(--ds-radius-md)] cursor-pointer w-full text-left",
17071
17179
  "transition-colors duration-[var(--ds-transition-speed)]",
17072
- item.active ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
17180
+ FOCUS_RING,
17181
+ item.active ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-ink)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
17073
17182
  ),
17074
17183
  children: [
17075
17184
  item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon, size: 18, className: "shrink-0" }),
@@ -17197,6 +17306,7 @@ function CommandPalette({ open, onOpenChange, commands, placeholder = "Type a co
17197
17306
  onChange: (e) => setQuery(e.target.value),
17198
17307
  onKeyDown: handleKeyDown,
17199
17308
  placeholder,
17309
+ "data-popup-search": "",
17200
17310
  className: "flex-1 bg-transparent text-sm text-[var(--ds-text-primary)] placeholder:text-[var(--ds-text-muted)] outline-none"
17201
17311
  }
17202
17312
  ),
@@ -17388,6 +17498,7 @@ function ContextMenu({ items, children, className, ref, container }) {
17388
17498
  else if (ref) ref.current = node;
17389
17499
  },
17390
17500
  onContextMenu: handleContextMenu,
17501
+ "data-context-menu": "",
17391
17502
  className,
17392
17503
  children: [
17393
17504
  children,
@@ -17428,9 +17539,11 @@ function ContextMenu({ items, children, className, ref, container }) {
17428
17539
  className: cn(
17429
17540
  "flex items-center w-full text-left text-sm cursor-pointer",
17430
17541
  "px-ds-3 py-ds-1.5 gap-ds-3",
17431
- "transition-colors focus-visible:outline-none focus-visible:bg-[var(--ds-bg-muted)]",
17542
+ "transition-colors focus-visible:bg-[var(--ds-bg-muted)]",
17543
+ // full-width rows in a panel that clips, never filled solid: the outline sits inside (Sprint 30, P3)
17544
+ FOCUS_INSET,
17432
17545
  item.disabled && "opacity-disabled cursor-not-allowed",
17433
- item.destructive ? "text-[var(--ds-status-error)]" : "text-[var(--ds-text-primary)]",
17546
+ item.destructive ? "text-[var(--ds-status-error-text)]" : "text-[var(--ds-text-primary)]",
17434
17547
  isHighlighted && "bg-[var(--ds-bg-muted)]"
17435
17548
  ),
17436
17549
  children: [
@@ -17487,6 +17600,8 @@ function NotificationCenter({
17487
17600
  }, [isControlled, onOpenChangeRef]);
17488
17601
  const triggerRef = useRef(null);
17489
17602
  const panelRef = useRef(null);
17603
+ const [list2, setList] = useState(null);
17604
+ useRevealFocus(list2);
17490
17605
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "end", offset: 8, flip: true });
17491
17606
  const filter = controlledFilter ?? internalFilter;
17492
17607
  const setFilter = onFilterChange ?? setInternalFilter;
@@ -17528,7 +17643,7 @@ function NotificationCenter({
17528
17643
  {
17529
17644
  type: "button",
17530
17645
  onClick: onMarkAllRead,
17531
- className: "text-sm text-[var(--ds-accent-primary)] hover:underline cursor-pointer",
17646
+ className: cn("text-sm text-[var(--ds-accent-ink)] hover:underline cursor-pointer", FOCUS_RING),
17532
17647
  children: "Mark all read"
17533
17648
  }
17534
17649
  ) })
@@ -17541,6 +17656,7 @@ function NotificationCenter({
17541
17656
  onClick: () => setFilter("all"),
17542
17657
  className: cn(
17543
17658
  "px-ds-3 py-ds-1.5 text-sm rounded-[var(--ds-radius-md)] transition-colors cursor-pointer",
17659
+ FOCUS_RING,
17544
17660
  filter === "all" ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-muted)]"
17545
17661
  ),
17546
17662
  children: "All"
@@ -17553,6 +17669,7 @@ function NotificationCenter({
17553
17669
  onClick: () => setFilter("unread"),
17554
17670
  className: cn(
17555
17671
  "px-ds-3 py-ds-1.5 text-sm rounded-[var(--ds-radius-md)] transition-colors cursor-pointer",
17672
+ FOCUS_RING,
17556
17673
  filter === "unread" ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-muted)]"
17557
17674
  ),
17558
17675
  children: [
@@ -17602,7 +17719,7 @@ function NotificationCenter({
17602
17719
  type: "button",
17603
17720
  onClick: () => onMarkRead(n.id),
17604
17721
  "aria-label": "Mark as read",
17605
- className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5",
17722
+ className: cn("text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5", FOCUS_RING),
17606
17723
  children: /* @__PURE__ */ jsx(Icon, { name: "check", size: 14 })
17607
17724
  }
17608
17725
  ),
@@ -17612,7 +17729,7 @@ function NotificationCenter({
17612
17729
  type: "button",
17613
17730
  onClick: () => onDismiss(n.id),
17614
17731
  "aria-label": "Dismiss",
17615
- className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5",
17732
+ className: cn("text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5", FOCUS_RING),
17616
17733
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 14 })
17617
17734
  }
17618
17735
  )
@@ -17639,7 +17756,7 @@ function NotificationCenter({
17639
17756
  "rounded-[var(--ds-radius-md)]",
17640
17757
  "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]",
17641
17758
  "transition-colors cursor-pointer",
17642
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
17759
+ FOCUS_RING
17643
17760
  ),
17644
17761
  children: [
17645
17762
  /* @__PURE__ */ jsx(Icon, { name: "bell", size: 18 }),
@@ -17670,12 +17787,12 @@ function NotificationCenter({
17670
17787
  {
17671
17788
  type: "button",
17672
17789
  onClick: onMarkAllRead,
17673
- className: "text-xs text-[var(--ds-accent-primary)] hover:underline cursor-pointer",
17790
+ className: cn("text-xs text-[var(--ds-accent-ink)] hover:underline cursor-pointer", FOCUS_RING),
17674
17791
  children: "Mark all read"
17675
17792
  }
17676
17793
  )
17677
17794
  ] }),
17678
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
17795
+ /* @__PURE__ */ jsxs("div", { ref: setList, className: "flex-1 overflow-y-auto pb-[4px] scroll-p-[4px]", children: [
17679
17796
  notifications.length === 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-ds-12 text-[var(--ds-text-muted)]", children: [
17680
17797
  /* @__PURE__ */ jsx(Icon, { name: "bell", size: 32, className: "opacity-30 mb-ds-2" }),
17681
17798
  /* @__PURE__ */ jsx("p", { className: "text-sm", children: "No notifications" })
@@ -17716,7 +17833,7 @@ function NotificationCenter({
17716
17833
  type: "button",
17717
17834
  onClick: () => onMarkRead(n.id),
17718
17835
  "aria-label": "Mark as read",
17719
- className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5",
17836
+ className: cn("text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5", FOCUS_RING),
17720
17837
  children: /* @__PURE__ */ jsx(Icon, { name: "check", size: 14 })
17721
17838
  }
17722
17839
  ),
@@ -17726,7 +17843,7 @@ function NotificationCenter({
17726
17843
  type: "button",
17727
17844
  onClick: () => onDismiss(n.id),
17728
17845
  "aria-label": "Dismiss",
17729
- className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5",
17846
+ className: cn("text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5", FOCUS_RING),
17730
17847
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 14 })
17731
17848
  }
17732
17849
  )
@@ -17889,7 +18006,7 @@ function Tour({ steps, open, onClose, onComplete, container }) {
17889
18006
  {
17890
18007
  type: "button",
17891
18008
  onClick: onClose,
17892
- className: "shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer -mt-ds-0.5",
18009
+ className: cn("shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer -mt-ds-0.5", FOCUS_RING),
17893
18010
  "aria-label": "Close tour",
17894
18011
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
17895
18012
  }
@@ -17913,7 +18030,7 @@ function Tour({ steps, open, onClose, onComplete, container }) {
17913
18030
  "px-ds-3 py-ds-1.5 text-xs font-medium rounded-[var(--ds-radius-sm)]",
17914
18031
  "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)]",
17915
18032
  "transition-colors cursor-pointer",
17916
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
18033
+ FOCUS_RING
17917
18034
  ),
17918
18035
  children: "Back"
17919
18036
  }
@@ -17928,7 +18045,7 @@ function Tour({ steps, open, onClose, onComplete, container }) {
17928
18045
  "px-ds-3 py-ds-1.5 text-xs font-medium rounded-[var(--ds-radius-sm)]",
17929
18046
  "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]",
17930
18047
  "transition-colors cursor-pointer",
17931
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
18048
+ FOCUS_RING
17932
18049
  ),
17933
18050
  children: current < steps.length - 1 ? "Next" : "Done"
17934
18051
  }
@@ -18090,6 +18207,7 @@ function focusHeading() {
18090
18207
  const heading = document.querySelector("main h1") ?? document.querySelector("main");
18091
18208
  if (!heading) return;
18092
18209
  if (!heading.hasAttribute("tabindex")) heading.setAttribute("tabindex", "-1");
18210
+ heading.style.outline = "none";
18093
18211
  heading.focus({ preventScroll: false });
18094
18212
  }
18095
18213
  function focusAfterLeaving() {