# Date Picker (Notchset): prompt.md (v1.0.0)

- id: `date-picker` · version 1.0.0 · component · free
- category: Inputs
- build: Base UI (this item also ships a Radix build)
- install (this build): `npx shadcn@latest add https://beautiful-ui.dev/r/date-picker.json`
- npm dependencies: @base-ui/react@^1
- registry dependencies: utils, https://beautiful-ui.dev/r/notchset-foundation.json
- docs: https://beautiful-ui.dev/components/date-picker
- The install command carries everything this item needs (files, CSS, tokens, npm and registry dependencies). Prefer it to copying source by hand.

shadcn's date picker in Notchset: a 40px trigger whose date rolls digit by digit, and a popup under a leader with preset chips, a 40px-day grid and its keys; it closes just after you pick.

## Build it
- Stack: React 19 (`ref` is a plain prop), TypeScript, Tailwind CSS v4 utilities, a shadcn-initialised project with the `@/*` alias.
- Packages: `@base-ui/react@^1` (Base UI build); `radix-ui@^1` (Radix build).
- Files: `components/ui/notchset/date-picker.tsx`; shared code: `lib/beautiful-ui/notchset/calendar.tsx`, `lib/beautiful-ui/notchset/instrument.tsx`, `lib/beautiful-ui/notchset/root.ts`.
- Registry dependencies, installed with it automatically: shadcn `utils` (cn), `notchset-foundation`.
- Builds: separate Base UI and Radix files. Install the one that matches the project (see Install): a free item's bare URL installs the Base UI build of it and its dependencies.
- Exports to keep: `DatePicker`, and every exported type.
- CSS: the install adds the notchset foundation (tokens, keyframes, motion levels) to your global stylesheet once. Nothing to import by hand.
- Re-running `add` (or `--overwrite`) re-applies those rules: put overrides in your own CSS, never in the installed rules.
- Tokens: retheme with the `--notchset-*` custom properties (`--notchset-check`, `--notchset-control-edge`, `--notchset-draw-from`, `--notchset-ease-bloom`, `--notchset-ease-travel`, `--notchset-fade`, `--notchset-focus-color`, `--notchset-focus-inset`, `--notchset-life-from`, `--notchset-life-ms`, `--notchset-node-blink`, `--notchset-node-bloom`, `--notchset-node-delay`, `--notchset-plate-color`, `--notchset-rise-from`, `--notchset-rule`, `--notchset-scroll`, `--notchset-signal`, `--notchset-sweep-to`). Never add Tailwind colour classes inside the component.

```tsx
import { DatePicker } from "@/components/ui/notchset/date-picker";
```

## Parts

| Part | data-slot | What it is for |
|---|---|---|
| `DatePicker` | `date-picker-trigger` | The trigger. |
| `—` | `date-picker-content` | The popup. |

Style a part with `[data-slot="<slot>"]` selectors or its `className`; keep the attributes when editing.

## Sound
- Keep every `data-slot` and `data-sound` attribute: the sound layer reads them.
- Installing this item adds no audio. Nothing plays until the app mounts `SoundProvider` once (install: `npx shadcn@latest add https://beautiful-ui.dev/r/notchset-sound.json`, import from `@/components/ui/notchset/sound-provider`); `useSound()` gives `muted` and `setMuted` for a mute control. Without a provider the audio engine never loads.

## Match the original
- Read `components/ui/notchset/date-picker.tsx` as the reference implementation before changing or recreating anything, and match it: sizes, colours per theme, motion timings, copy and behaviour.
- If you deviate (a prop you can't honour, a style you changed, a dependency you swapped), say so in your reply, part by part.
- Keep the accessibility contract, the keyboard map and the motion levels listed below.

## Use it when
- date picker, date input, due date, schedule date, popover calendar, shadcn date picker, Notchset
- One date in a form or a toolbar

### Not when
- A range: use Date Range Picker
- A visible month: use Calendar

## Mistakes
- Give it an aria-label or a visible label with aria-labelledby

## Usage

```tsx
"use client";

import * as React from "react";
import { DatePicker } from "@/components/ui/notchset/date-picker";

export function ReportDate() {
  const [date, setDate] = React.useState<Date | undefined>(new Date());
  return <DatePicker value={date} onValueChange={setDate} presets={[{ label: "TODAY", date: new Date() }]} />;
}
```

## Props

| Prop | Type | Default | What it does |
|---|---|---|---|
| `value / onValueChange` | `Date` |  | Controlled; or defaultValue. |
| `presets` | `{ label, date }[]` |  | Chips above the grid. |
| `disabled` | `(date) => boolean` |  | Days that can't be picked. |
| `startMonth / endMonth` | `Date` |  | How far the arrows go. |
| `events` | `Record<YYYY-MM-DD, string[]>` |  | Dots under days. |
| `placeholder` | `ReactNode` |  | Shown with no date. |

Full docs: https://beautiful-ui.dev/components/date-picker

## Customising
- Colours: the component reads your shadcn tokens (`--background`, `--foreground`, `--border` …), refined by the `--notchset-*` tokens. The signal colour is `--notchset-signal` (it falls back to `--destructive`). Set tokens on `:root`, or on any container to retheme one area.
- Dark mode follows the `.dark` class on an ancestor (the shadcn and next-themes convention).
- Update later by re-running the install with `--overwrite` (review the diff if you edited it). Changelog: https://beautiful-ui.dev/r/changelog.json

## Keyboard

| Keys | Action |
|---|---|
| Enter / Space | Open |
| ← → ↑ ↓ | Day / week |
| PgUp / PgDn | Month |
| Enter | Pick |
| Esc | Close |

## Performance

- The popup mounts only while open.

## Responsive

- The trigger fills its container; the popup is 306px wide.

## Motion inventory

| Interaction | What moves |
|---|---|
| Open | The leader drops (100ms), the popup unrolls (380ms after 80ms) |
| Pick | The block moves, then the popup rolls up 420ms later; the trigger's digits roll, ones first |

## Accessibility contract (preserve when editing)
- The Base UI or Radix popover: Esc closes and focus returns to the trigger
- Opening focuses the picked day; the grid keys are listed in the popup

## Install

Base UI project (a base-* style in components.json):

```bash
npx shadcn@latest add https://beautiful-ui.dev/r/date-picker.json
```

Radix project (a radix-*, new-york or default style):

```bash
npx shadcn@latest add https://beautiful-ui.dev/r/radix-nova/date-picker.json
```

Or add the `@beautiful-ui` registry to components.json and run `npx shadcn@latest add @beautiful-ui/date-picker`: the CLI picks the build from your style.

## Credits
- Built on shadcn/ui (https://ui.shadcn.com)

## Source (Base UI build)

### components/ui/notchset/date-picker.tsx

```tsx
"use client";

/**
 * Date Picker (Notchset) v1.0.0 · Beautiful UI, by Chimpy (https://x.com/stochastichimp)
 * Docs: https://beautiful-ui.dev/components/date-picker · Agent prompt: https://beautiful-ui.dev/md/date-picker.md
 * MIT licensed: free to use, change and share.
 */

import * as React from "react";
import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
import { cn } from "@/lib/utils";
import { NOTCHSET_ROOT, OWN_SOUND, motionOff, playCue, useNotchsetPortal } from "@/lib/beautiful-ui/notchset/instrument";
import { type CalendarEvents, type DatePreset, DatePickerFace, DatePickerPanel, PickerLeader, pickerContentClass, pickerTriggerClass } from "@/lib/beautiful-ui/notchset/calendar";

/*
 * Notchset Date Picker (Base UI build). shadcn's date picker (a popover holding a calendar) in
 * Notchset: a 40px trigger whose date rolls digit by digit, a popup that unrolls under an 8px leader
 * with preset chips, a 40px-day grid and its key legend. Picking moves the ink block, then the popup
 * closes 420ms later (at once under reduced motion) and focus returns to the trigger.
 */

function DatePicker({
  value: valueProp,
  defaultValue,
  onValueChange,
  presets,
  placeholder,
  disabled,
  startMonth,
  endMonth,
  events,
  eventLabel,
  locale,
  id,
  className,
  "aria-label": ariaLabel,
  "aria-describedby": describedBy,
}: {
  value?: Date;
  defaultValue?: Date;
  onValueChange?: (date: Date) => void;
  presets?: readonly DatePreset[];
  placeholder?: React.ReactNode;
  disabled?: (date: Date) => boolean;
  startMonth?: Date;
  endMonth?: Date;
  events?: CalendarEvents;
  eventLabel?: (count: number) => string;
  locale?: string;
  id?: string;
  className?: string;
  "aria-label"?: string;
  "aria-describedby"?: string;
}) {
  const [own, setOwn] = React.useState(defaultValue);
  const value = valueProp ?? own;
  const [open, setOpen] = React.useState(false);
  const close = React.useRef<ReturnType<typeof setTimeout>>(undefined);
  React.useEffect(() => () => clearTimeout(close.current), []);
  const scoped = useNotchsetPortal();
  return (
    <PopoverPrimitive.Root
      open={open}
      onOpenChange={(next) => {
        clearTimeout(close.current);
        if (next) playCue(document.activeElement, "tick");
        setOpen(next);
      }}
    >
      <PopoverPrimitive.Trigger
        {...OWN_SOUND}
        id={id}
        aria-label={ariaLabel}
        aria-describedby={describedBy}
        data-slot="date-picker-trigger"
        className={cn(`notchset-focus`, pickerTriggerClass, className)}
      >
        <DatePickerFace value={value} placeholder={placeholder} locale={locale} />
      </PopoverPrimitive.Trigger>
      <PopoverPrimitive.Portal container={scoped}>
        <PopoverPrimitive.Positioner className="isolate z-50 outline-none" side="bottom" align="start" sideOffset={8} collisionPadding={8}>
          <PopoverPrimitive.Popup {...NOTCHSET_ROOT} aria-label="Choose a date" data-slot="date-picker-content" initialFocus={false} className={pickerContentClass}>
            <PickerLeader />
            <DatePickerPanel
              value={value}
              presets={presets}
              disabled={disabled}
              startMonth={startMonth}
              endMonth={endMonth}
              events={events}
              eventLabel={eventLabel}
              locale={locale}
              onPick={(d) => {
                setOwn(d);
                onValueChange?.(d);
                clearTimeout(close.current);
                const still = motionOff(document.activeElement);
                close.current = setTimeout(() => setOpen(false), still ? 0 : 420);
              }}
            />
          </PopoverPrimitive.Popup>
        </PopoverPrimitive.Positioner>
      </PopoverPrimitive.Portal>
    </PopoverPrimitive.Root>
  );
}

export { DatePicker };
export type { CalendarEvents, DatePreset } from "@/lib/beautiful-ui/notchset/calendar";
```

### lib/beautiful-ui/notchset/calendar.tsx

```tsx
"use client";

import * as React from "react";
import { cn } from "@/lib/utils";
import { OWN_SOUND, motionOff, playCue } from "@/lib/beautiful-ui/notchset/instrument";

/*
 * Notchset calendar core, shared by Calendar, Date Picker and Date Range Picker (handoff 1.12). Days
 * are whole numbers (days since 1970-01-01 of the local date), so moving and comparing is arithmetic.
 * A 6-week grid of square cells (44px or 40px) starting on Monday, with an optional ISO week column.
 * Single mode has one ink block that stretches to the picked day: the leading edge moves first and
 * the trailing edge 85ms later (300ms). Range mode paints its two ends ink and the days between,
 * rippling out from the start. A month change slides the grid 14px away and fades (110ms), then
 * settles it in from the other side (380ms) while the title rolls 8px.
 */

const GLIDE = "cubic-bezier(.65,0,.35,1)";
const TRAVEL = "cubic-bezier(.7,0,.25,1)";
const SETTLE = "cubic-bezier(.34,1.3,.5,1)";

export type Day = number;
export const toDay = (d: Date): Day => Math.round(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()) / 864e5);
export const dayOf = (y: number, m: number, d: number): Day => Math.round(Date.UTC(y, m, d) / 864e5);
export const fromDay = (n: Day) => {
  const u = new Date(n * 864e5);
  return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate());
};
/** [year, month (0–11), date, weekday (Monday 0 … Sunday 6)]. */
export const fields = (n: Day): [number, number, number, number] => {
  const u = new Date(n * 864e5);
  return [u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), (u.getUTCDay() + 6) % 7];
};
export const isoDay = (n: Day) => {
  const [y, m, d] = fields(n);
  return `${y}-${String(m + 1).padStart(2, "0")}-${String(d).padStart(2, "0")}`;
};
/** Parses YYYY-MM-DD (month and day may be one digit); null if it is not a real date. */
export const parseIsoDay = (t: string): Day | null => {
  const m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(t.trim());
  if (!m) return null;
  const y = +m[1]!, mo = +m[2]! - 1, d = +m[3]!;
  if (mo < 0 || mo > 11 || d < 1 || d > 31) return null;
  const n = dayOf(y, mo, d);
  return fields(n)[2] === d ? n : null;
};
/** ISO 8601 week number. */
export const isoWeek = (n: Day) => {
  const wd = fields(n)[3];
  const th = n - wd + 3;
  const j4 = dayOf(fields(th)[0], 0, 4);
  return Math.floor((n - wd - (j4 - fields(j4)[3])) / 7) + 1;
};
/** A month as one number (year × 12 + month), for comparing and stepping. */
export const monthIndex = (n: Day) => {
  const [y, m] = fields(n);
  return y * 12 + m;
};
const monthStart = (mi: number) => dayOf(Math.floor(mi / 12), mi % 12, 1);

/** Today, as a day; null while rendering on the server, so the first client render matches it. */
const noop = () => () => {};
export function useToday(): Day | null {
  return React.useSyncExternalStore(
    noop,
    () => toDay(new Date()),
    () => null,
  );
}

const fmt = (locale: string, o: Intl.DateTimeFormatOptions) => new Intl.DateTimeFormat(locale, { timeZone: "UTC", ...o });
export function monthTitle(mi: number, locale = "en-US") {
  return `${fmt(locale, { month: "long" }).format(new Date(monthStart(mi) * 864e5)).toUpperCase()} ${Math.floor(mi / 12)}`;
}
/** THU · OCT 08 · 2026 */
export function longLabel(n: Day, locale = "en-US") {
  const d = new Date(n * 864e5);
  const wd = fmt(locale, { weekday: "short" }).format(d).toUpperCase().replace(".", "");
  const mo = fmt(locale, { month: "short" }).format(d).toUpperCase().replace(".", "");
  const [y, , dd] = fields(n);
  return `${wd} · ${mo} ${String(dd).padStart(2, "0")} · ${y}`;
}
/** OCT 08 */
export function shortLabel(n: Day, locale = "en-US") {
  const mo = fmt(locale, { month: "short" }).format(new Date(n * 864e5)).toUpperCase().replace(".", "");
  return `${mo} ${String(fields(n)[2]).padStart(2, "0")}`;
}
/** "Today", "In 6 days", "3 days ago": the distance from today, in caps. */
export function relLabel(n: Day, today: Day | null) {
  if (today == null) return "";
  const d = n - today;
  if (!d) return "TODAY";
  const k = Math.abs(d);
  return d > 0 ? `IN ${k} DAY${k > 1 ? "S" : ""}` : `${k} DAY${k > 1 ? "S" : ""} AGO`;
}

export type CalendarEvents = Readonly<Record<string, readonly string[]>>;

export type CalendarGridProps = {
  size?: 44 | 40;
  locale?: string;
  /** Single: the picked day. */
  selected?: Day | null;
  /** Range: the two ends, in pick order (b is null while the second end is being chosen). */
  range?: { a: Day | null; b: Day | null } | null;
  onPick: (n: Day, el: HTMLElement | null) => void;
  /** The month shown (year × 12 + month) and its change. */
  month: number;
  onMonthChange: (mi: number) => void;
  /** First and last months that can be shown. */
  startMonth?: number;
  endMonth?: number;
  isDisabled?: (n: Day) => boolean;
  events?: CalendarEvents;
  eventLabel?: (count: number) => string;
  showWeekNumber?: boolean;
  showToday?: boolean;
  /** Focus the tabbable day when mounted (the pickers' popups). */
  autoFocus?: boolean;
  className?: string;
};

type Phase = "idle" | "out" | "pre" | "in";

export function CalendarGrid({
  size = 44,
  locale = "en-US",
  selected = null,
  range = null,
  onPick,
  month,
  onMonthChange,
  startMonth,
  endMonth,
  isDisabled,
  events,
  eventLabel = (n) => `${n} event${n > 1 ? "s" : ""}`,
  showWeekNumber = false,
  showToday = false,
  autoFocus = false,
  className,
}: CalendarGridProps) {
  const root = React.useRef<HTMLDivElement>(null);
  const today = useToday();
  // The month on screen lags the asked-for month by the slide-out (110ms).
  const [view, setView] = React.useState<{ shown: number; ph: Phase; nd: 1 | -1; asked: number }>({ shown: month, ph: "idle", nd: 1, asked: month });
  const timers = React.useRef<ReturnType<typeof setTimeout>[]>([]);
  React.useEffect(() => () => timers.current.forEach(clearTimeout), []);
  if (view.asked !== month) {
    // A month set by the owner (not by this grid's own controls) is shown at once.
    setView((v) => ({ ...v, asked: month, shown: month, ph: "idle" }));
  }
  React.useEffect(() => {
    const target = view.asked;
    timers.current.forEach(clearTimeout);
    const at = (ms: number, f: (v: typeof view) => typeof view) => setTimeout(() => setView((v) => (v.asked === target ? f(v) : v)), ms);
    timers.current = [
      at(110, (v) => (v.shown === target ? v : { ...v, shown: target, ph: "pre" })),
      at(140, (v) => (v.ph === "pre" ? { ...v, ph: "in" } : v)),
      at(560, (v) => (v.ph === "idle" ? v : { ...v, ph: "idle" })),
    ];
  }, [view.asked]);
  /** This grid's own month changes (arrows, keys, picking a day in the next month) slide. */
  const goTo = (mi: number) => {
    const still = motionOff(root.current);
    setView((v) =>
      still
        ? { ...v, asked: mi, shown: mi, ph: "idle" }
        : { ...v, asked: mi, nd: mi > v.asked ? 1 : -1, ph: v.shown === mi ? (v.ph === "idle" ? "idle" : "in") : "out" },
    );
    onMonthChange(mi);
  };

  const shown = view.shown;
  const start = monthStart(shown) - fields(monthStart(shown))[3];
  const inShown = (n: Day) => monthIndex(n) === shown;
  const dis = (n: Day) => Boolean(isDisabled?.(n));

  // The focus day: roving tabindex across the grid. Kept when it is in the shown month.
  const fallback = selected ?? range?.b ?? range?.a ?? (today != null && monthIndex(today) === shown ? today : monthStart(shown));
  const [fcState, setFc] = React.useState<Day | null>(null);
  // A focus day in the month being moved to is kept: its cell takes focus once the grid shows it.
  const fc = fcState != null && monthIndex(fcState) === view.asked ? fcState : inShown(fallback) ? fallback : monthStart(shown);
  const wantFocus = React.useRef(autoFocus);
  React.useLayoutEffect(() => {
    if (!wantFocus.current) return;
    const cell = root.current?.querySelector<HTMLElement>(`[role=gridcell][data-day="${fc}"]`);
    if (cell) {
      cell.focus({ preventScroll: true });
      wantFocus.current = false;
    }
  });

  // Selection block (single mode): edges in grid steps; which edges lead comes from the move.
  const [move, setMove] = React.useState({ dx: 0, dy: 0 });
  const S = size;
  const si = selected != null ? selected - start : -1;
  const blockOn = range == null && si >= 0 && si < 42;
  const jump = view.ph !== "idle";
  const edge = (lead: boolean) => `300ms ${GLIDE} ${lead ? 0 : 85}ms`;
  const blockStyle: React.CSSProperties = blockOn
    ? {
        left: (si % 7) * S,
        top: Math.floor(si / 7) * S,
        right: (6 - (si % 7)) * S,
        bottom: (5 - Math.floor(si / 7)) * S,
        opacity: 1,
        transition: jump ? "opacity 120ms linear" : `left ${edge(move.dx <= 0)}, right ${edge(move.dx >= 0)}, top ${edge(move.dy <= 0)}, bottom ${edge(move.dy >= 0)}, opacity 120ms linear`,
      }
    : { inset: 0, opacity: 0, transition: "none" };

  const pick = (n: Day, el: HTMLElement | null) => {
    if (dis(n)) return;
    playCue(el, "tick");
    setFc(n);
    if (range == null && selected != null && inShown(n)) {
      const o = selected - start, k = n - start;
      setMove({ dx: Math.sign((k % 7) - (((o % 7) + 7) % 7)), dy: Math.sign(Math.floor(k / 7) - Math.floor(o / 7)) });
    } else setMove({ dx: 0, dy: 0 });
    if (!inShown(n)) goTo(monthIndex(n));
    onPick(n, el);
  };

  const prevOff = startMonth != null && shown <= startMonth;
  const nextOff = endMonth != null && shown >= endMonth;
  const nav = (d: 1 | -1, el: HTMLElement) => {
    if (d < 0 ? prevOff : nextOff) return;
    playCue(el, "tick");
    goTo(view.asked + d);
  };

  const clampMonth = (n: Day) => {
    if (startMonth != null && monthIndex(n) < startMonth) return monthStart(startMonth);
    if (endMonth != null && monthIndex(n) > endMonth) return monthStart(endMonth + 1) - 1;
    return n;
  };
  const onKeyDown = (e: React.KeyboardEvent) => {
    const steps: Record<string, number> = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 };
    let n = fc;
    if (steps[e.key] != null) n += (e.key === "ArrowLeft" || e.key === "ArrowRight") && getComputedStyle(e.currentTarget).direction === "rtl" ? -steps[e.key]! : steps[e.key]!;
    else if (e.key === "Home") n -= fields(n)[3];
    else if (e.key === "End") n += 6 - fields(n)[3];
    else if (e.key === "PageUp" || e.key === "PageDown") {
      const [y, m, d] = fields(n);
      const mi = y * 12 + m + (e.key === "PageUp" ? -1 : 1);
      const last = fields(monthStart(mi + 1) - 1)[2];
      n = dayOf(Math.floor(mi / 12), mi % 12, Math.min(d, last));
    } else if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      pick(fc, e.target as HTMLElement);
      return;
    } else return;
    e.preventDefault();
    n = clampMonth(n);
    setFc(n);
    wantFocus.current = true;
    if (!inShown(n)) goTo(monthIndex(n));
  };

  // Range painting: ends ink; between them a hover tint while choosing, a fill once chosen.
  const [hover, setHover] = React.useState<Day | null>(null);
  const A = range?.a ?? null;
  const B = range?.b ?? (A != null ? hover : null);
  const lo = A != null && B != null ? Math.min(A, B) : A;
  const hi = A != null && B != null ? Math.max(A, B) : A;
  const complete = range?.b != null;

  const set = view.ph === "pre";
  const gridMotion: React.CSSProperties = {
    opacity: view.ph === "out" || view.ph === "pre" ? 0 : 1,
    transform: `translateX(${view.ph === "out" ? -view.nd * 14 : view.ph === "pre" ? view.nd * 14 : 0}px)`,
    transition: set ? "none" : view.ph === "out" ? `opacity 110ms linear, transform 110ms ${TRAVEL}` : `opacity 220ms linear, transform 380ms ${SETTLE}`,
  };
  const titleMotion: React.CSSProperties = {
    opacity: gridMotion.opacity,
    transform: `translateY(${view.ph === "out" ? -view.nd * 8 : view.ph === "pre" ? view.nd * 8 : 0}px)`,
    transition: gridMotion.transition,
  };
  const title = monthTitle(shown, locale);
  const wdFmt = fmt(locale, { weekday: "short" });
  const wdLong = fmt(locale, { weekday: "long", month: "long", day: "numeric", year: "numeric" });

  const cells = Array.from({ length: 42 }, (_, i) => start + i);
  const navClass =
    cn(`notchset-focus`, "[--notchset-focus-inset:-3px] flex size-7 flex-none cursor-pointer items-center justify-center border border-solid border-[var(--notchset-rule,var(--border))] bg-transparent p-0 text-foreground aria-disabled:cursor-not-allowed aria-disabled:text-[var(--notchset-rule,var(--border))]");

  return (
    <div ref={root} data-slot="calendar-grid" className={cn("flex flex-col gap-2", className)}>
      <div className="flex h-7 items-center gap-1.5">
        <span className="relative h-5 min-w-0 flex-1 overflow-hidden">
          <span aria-live="polite" className="absolute start-0 top-0 flex h-5 items-center font-mono text-[10.5px] font-medium tracking-[0.12em] whitespace-nowrap" style={titleMotion}>
            {title}
          </span>
        </span>
        {showToday && (
          <button
            {...OWN_SOUND}
            type="button"
            disabled={today == null}
            onClick={(e) => today != null && pick(today, e.currentTarget)}
            className={cn(`notchset-focus`, "[--notchset-focus-inset:-3px] h-7 cursor-pointer border-0 bg-transparent px-2 font-mono text-[9px] font-medium tracking-[0.1em] text-foreground underline underline-offset-[3px]")}
          >
            TODAY
          </button>
        )}
        <button {...OWN_SOUND} type="button" aria-label="Previous month" aria-disabled={prevOff || undefined} onClick={(e) => nav(-1, e.currentTarget)} className={navClass}>
          <svg aria-hidden width="10" height="10" viewBox="0 0 12 12" className="rtl:-scale-x-100">
            <path d="M7.5 2.5 L4 6 L7.5 9.5" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="square" />
          </svg>
        </button>
        <button {...OWN_SOUND} type="button" aria-label="Next month" aria-disabled={nextOff || undefined} onClick={(e) => nav(1, e.currentTarget)} className={navClass}>
          <svg aria-hidden width="10" height="10" viewBox="0 0 12 12" className="rtl:-scale-x-100">
            <path d="M4.5 2.5 L8 6 L4.5 9.5" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="square" />
          </svg>
        </button>
      </div>
      <div aria-hidden className="flex h-4 items-center border-0 border-b border-solid border-[var(--notchset-rule,var(--border))] pb-1.5">
        {showWeekNumber && <span className="w-[34px] flex-none" />}
        <div className="grid" style={{ gridTemplateColumns: `repeat(7, ${S}px)` }}>
          {cells.slice(0, 7).map((n, i) => (
            <span key={i} className="text-center font-mono text-[8px] tracking-[0.1em] text-muted-foreground">
              {wdFmt.format(new Date(n * 864e5)).slice(0, 2).toUpperCase()}
            </span>
          ))}
        </div>
      </div>
      <div className="flex">
        {showWeekNumber && (
          <div aria-hidden className="grid w-[34px] flex-none" style={{ gridAutoRows: S, opacity: gridMotion.opacity, transition: gridMotion.transition }}>
            {Array.from({ length: 6 }, (_, r) => (
              <span key={r} className="flex items-center font-mono text-[8px] tracking-[0.06em] text-muted-foreground">
                W{String(isoWeek(start + r * 7)).padStart(2, "0")}
              </span>
            ))}
          </div>
        )}
        <div
          role="grid"
          aria-label={title}
          aria-multiselectable={range != null || undefined}
          onKeyDown={onKeyDown}
          onMouseLeave={() => setHover(null)}
          className="relative grid"
          style={{ gridTemplateColumns: `repeat(7, ${S}px)`, gridAutoRows: S, ...gridMotion }}
        >
          <span aria-hidden data-slot="calendar-block" className="pointer-events-none absolute bg-foreground" style={blockStyle} />
          {Array.from({ length: 6 }, (_, r) => (
            <div key={r} role="row" className="contents">
              {cells.slice(r * 7, r * 7 + 7).map((n) => {
                const [, , d] = fields(n);
                const inM = inShown(n);
                const off = dis(n);
                const ev = events?.[isoDay(n)] ?? [];
                const isToday = n === today;
                const end = range != null && A != null && (n === A || n === range.b);
                const mid = range != null && lo != null && hi != null && n > lo && n < hi;
                const on = range != null ? end : n === selected;
                const dl = complete && lo != null ? Math.min(320, Math.abs(n - lo) * 10) : 0;
                return (
                  <button
                    {...OWN_SOUND}
                    key={n}
                    type="button"
                    role="gridcell"
                    data-day={n}
                    data-selected={on || undefined}
                    data-range-middle={mid || undefined}
                    tabIndex={n === fc ? 0 : -1}
                    aria-selected={on}
                    aria-disabled={off || undefined}
                    aria-current={isToday ? "date" : undefined}
                    aria-label={`${wdLong.format(new Date(n * 864e5))}${isToday ? ", today" : ""}${ev.length ? `, ${eventLabel(ev.length)}` : ""}`}
                    onClick={(e) => pick(n, e.currentTarget)}
                    onMouseEnter={() => !off && setHover(n)}
                    className={cn(
                      `notchset-focus`,
                      "relative flex cursor-pointer flex-col items-center justify-center gap-[3px] border-0 p-0 [--notchset-focus-inset:-2px]",
                      off ? "cursor-not-allowed text-[var(--notchset-rule,var(--border))]" : inM ? "text-foreground" : "text-muted-foreground",
                      on && "text-background",
                      range != null && end ? "bg-foreground" : mid ? (complete ? "bg-[var(--notchset-received-hover,var(--accent))]" : "bg-[var(--notchset-faceplate,var(--muted))]") : "bg-transparent",
                      !on && !mid && !off && "hover:bg-[var(--notchset-faceplate,var(--muted))]",
                    )}
                    style={{ height: S, transition: range != null ? `background-color 160ms linear ${dl}ms, color 160ms linear ${dl}ms` : `background-color 120ms linear, color 140ms linear ${on ? 140 : 0}ms` }}
                  >
                    <span
                      className={cn(
                        "flex h-[18px] w-[26px] items-center justify-center border border-solid font-mono text-[11px] tabular-nums transition-[border-color] duration-[140ms] ease-linear",
                        inM ? "font-medium" : "font-normal",
                        isToday ? (on ? "border-background" : "border-foreground") : "border-transparent",
                      )}
                    >
                      {d}
                    </span>
                    <svg aria-hidden width="14" height="3" viewBox="0 0 14 3" className="overflow-visible">
                      {ev.slice(0, 3).map((_, j, arr) => (
                        <circle key={j} cx={(arr.length === 1 ? [7] : arr.length === 2 ? [4.5, 9.5] : [2, 7, 12])[j]} cy="1.5" r="1.2" fill="currentColor" />
                      ))}
                    </svg>
                  </button>
                );
              })}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/** The pickers' 40px trigger icon. */
export function CalendarIcon() {
  return (
    <svg aria-hidden width="13" height="13" viewBox="0 0 13 13" className="flex-none">
      <rect x="1.5" y="2.5" width="10" height="9" fill="none" stroke="currentColor" strokeWidth="1.1" />
      <path d="M1.5 5.5 H11.5 M4.5 1 V3.5 M8.5 1 V3.5" stroke="currentColor" strokeWidth="1.1" />
    </svg>
  );
}

export function PickerChevron() {
  return (
    <svg
      aria-hidden
      width="9"
      height="9"
      viewBox="0 0 12 12"
      className="flex-none transition-transform duration-300 ease-[cubic-bezier(.34,1.5,.5,1)] group-data-[popup-open]/picker:rotate-180 group-data-[state=open]/picker:rotate-180"
    >
      <path d="M2.5 4.5 L6 8 L9.5 4.5" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="square" />
    </svg>
  );
}

export const pickerTriggerClass = cn(
  "group/picker relative flex h-10 w-full min-w-0 cursor-pointer items-center gap-2.5 border border-solid border-[var(--notchset-control-edge,var(--input))] bg-background px-3 text-start text-foreground outline-none transition-[border-color] duration-[120ms] ease-linear [--notchset-focus-inset:-4px] pointer-coarse:h-11",
  "data-[popup-open]:border-foreground data-[state=open]:border-foreground data-[disabled]:cursor-not-allowed data-[disabled]:opacity-40",
);

/** The 8px leader from the trigger down to the popup's edge, 16px in. */
export function PickerLeader() {
  return <span aria-hidden className="pointer-events-none absolute start-4 -top-[9px] h-2 w-px origin-top bg-foreground motion-safe:animate-[notchset-leader-y_100ms_var(--notchset-ease-travel)_both]" />;
}

/** The popup: an ink frame that unrolls (grid rows, 380ms after 80ms) and rolls up (180ms). */
export const pickerContentClass = cn(
  "relative z-50 grid grid-rows-[1fr] border border-solid border-foreground bg-background text-foreground outline-none",
  "transition-[grid-template-rows] duration-[380ms] ease-[var(--notchset-ease-unfold)] delay-[80ms]",
  "data-[starting-style]:grid-rows-[0fr] data-[ending-style]:grid-rows-[0fr] data-[ending-style]:delay-0 data-[ending-style]:duration-[180ms] data-[ending-style]:ease-[var(--notchset-ease-travel)]",
  "motion-safe:data-[state=open]:animate-[notchset-unfold_380ms_var(--notchset-ease-unfold)_80ms_both] motion-safe:data-[state=closed]:animate-[notchset-fold_180ms_var(--notchset-ease-travel)_both]",
);

/** A 24px preset chip; ink when it is the current value. */
export function PresetChip({ on, className, ...props }: React.ComponentProps<"button"> & { on?: boolean }) {
  return (
    <button
      {...OWN_SOUND}
      type="button"
      aria-pressed={on}
      className={cn(`notchset-focus`, "[--notchset-focus-inset:-3px] h-6 cursor-pointer border border-solid px-[7px] font-mono text-[8.5px] font-medium tracking-[0.06em] whitespace-nowrap transition-[background-color,color] duration-[120ms] ease-linear",
        on ? "border-foreground bg-foreground text-background" : "border-[var(--notchset-rule,var(--border))] bg-transparent text-foreground hover:bg-[var(--notchset-faceplate,var(--muted))]",
        className,
      )}
      {...props}
    />
  );
}

/** The 28px key legend under a picker's grid. */
export function PickerKeys({ keys }: { keys: readonly string[] }) {
  return (
    <div aria-hidden className="flex h-7 items-center gap-3.5 border-0 border-t border-solid border-[var(--notchset-rule,var(--border))] px-3 font-mono text-[8px] tracking-[0.1em] whitespace-nowrap text-muted-foreground">
      {keys.map((k) => (
        <span key={k}>{k}</span>
      ))}
    </div>
  );
}

export type DatePreset = { label: string; date: Date };

/** The date picker trigger's inside: icon, the date rolling digit by digit (ones first), chevron. */
export function DatePickerFace({ value, placeholder = "PICK A DATE", locale }: { value: Date | undefined; placeholder?: React.ReactNode; locale?: string }) {
  return (
    <>
      <CalendarIcon />
      <span className="flex min-w-0 flex-1">
        {value ? (
          <RollingLabel text={longLabel(toDay(value), locale)} />
        ) : (
          <span className="truncate font-mono text-[11.5px] leading-[18px] font-medium text-muted-foreground">{placeholder}</span>
        )}
      </span>
      <PickerChevron />
    </>
  );
}

/** Text whose digits roll (620ms, the ones digit first, each digit to its left 40ms later, after `delay`). */
export function RollingLabel({ text, height = 18, delay = 0, className }: { text: string; height?: number; delay?: number; className?: string }) {
  const chars = [...text];
  let k = 0;
  const delays = chars.map((_, i) => (/\d/.test(chars[chars.length - 1 - i]!) ? k++ : 0)).reverse();
  return (
    <span className={cn("relative inline-flex overflow-hidden font-mono text-[11.5px] font-medium whitespace-pre tabular-nums", className)} style={{ height, lineHeight: `${height}px` }}>
      <span className="sr-only">{text}</span>
      <span aria-hidden className="flex">
        {chars.map((d, i) =>
          /\d/.test(d) ? (
            <span
              key={chars.length - i}
              className="flex flex-col transition-transform duration-[620ms] ease-[cubic-bezier(.34,1.15,.5,1)] motion-reduce:transition-none"
              style={{ transform: `translateY(${-Number(d) * height}px)`, transitionDelay: `${delay + delays[i]! * 40}ms` }}
            >
              {Array.from({ length: 10 }, (_, n) => (
                <span key={n} style={{ height }}>
                  {n}
                </span>
              ))}
            </span>
          ) : (
            <span key={`s${chars.length - i}`}>{d}</span>
          ),
        )}
      </span>
    </span>
  );
}

/** The date picker popup's inside: presets, the 40px grid, the key legend. */
export function DatePickerPanel({
  value,
  onPick,
  presets,
  disabled,
  startMonth,
  endMonth,
  events,
  eventLabel,
  locale,
}: {
  value: Date | undefined;
  onPick: (date: Date) => void;
  presets?: readonly DatePreset[];
  disabled?: (date: Date) => boolean;
  startMonth?: Date;
  endMonth?: Date;
  events?: CalendarEvents;
  eventLabel?: (count: number) => string;
  locale?: string;
}) {
  const sel = value ? toDay(value) : null;
  const [month, setMonth] = React.useState(() => monthIndex(sel ?? toDay(new Date())));
  const isDisabled = disabled ? (n: Day) => disabled(fromDay(n)) : undefined;
  return (
    <div className="min-h-0 overflow-hidden">
      <div className="flex flex-col gap-2.5 px-3 pt-3 pb-1.5">
        {presets && presets.length > 0 && (
          <div className="flex gap-1">
            {presets.map((p) => {
              const n = toDay(p.date);
              return (
                <PresetChip
                  key={p.label}
                  on={n === sel}
                  disabled={isDisabled?.(n)}
                  onClick={(e) => {
                    playCue(e.currentTarget, "tick");
                    setMonth(monthIndex(n));
                    onPick(fromDay(n));
                  }}
                >
                  {p.label}
                </PresetChip>
              );
            })}
          </div>
        )}
        <CalendarGrid
          size={40}
          locale={locale}
          selected={sel}
          month={month}
          onMonthChange={setMonth}
          startMonth={startMonth ? monthIndex(toDay(startMonth)) : undefined}
          endMonth={endMonth ? monthIndex(toDay(endMonth)) : undefined}
          isDisabled={isDisabled}
          events={events}
          eventLabel={eventLabel}
          autoFocus
          onPick={(n) => onPick(fromDay(n))}
        />
      </div>
      <PickerKeys keys={["ARROWS MOVE", "PGUP / PGDN MONTH", "↵ PICK"]} />
    </div>
  );
}
```

### lib/beautiful-ui/notchset/instrument.tsx

```tsx
"use client";

import * as React from "react";
import { cn } from "@/lib/utils";

/*
 * Notchset instrument parts, shared by every member of a pack: the node (a round light that reports
 * state), the legend printed above a control, the plate a key presses onto, the traced check, the
 * five-tick scanner, and the glyph flip that lands a changed label left to right. Nodes and checks
 * are inline SVG (round at every DPR); the CSS lives in the Notchset foundation. Also: the one motion
 * policy (reduced motion or a data-motion="off" ancestor), ref composition, the portal container
 * and the sound attributes every Notchset root carries.
 */

// ---------------------------------------------------------------------------------------------
// Motion, refs, portals, sound
// ---------------------------------------------------------------------------------------------

/** True under prefers-reduced-motion or inside a data-motion="off" scope. Checked when an effect runs. */
/**
 * Whether motion is off for an element, kept live: it follows the OS reduced-motion setting and any
 * data-motion attribute change above it, so a preference switched mid-playback stops decoration at once.
 */
export function useMotionOff(ref: React.RefObject<Element | null>) {
  const [off, setOff] = React.useState(false);
  React.useEffect(() => {
    const read = () => setOff(motionOff(ref.current));
    const r = requestAnimationFrame(read);
    const mq = window.matchMedia?.("(prefers-reduced-motion: reduce)");
    mq?.addEventListener?.("change", read);
    const mo = new MutationObserver(read);
    mo.observe(document.documentElement, { attributes: true, attributeFilter: ["data-motion"], subtree: true });
    return () => {
      cancelAnimationFrame(r);
      mq?.removeEventListener?.("change", read);
      mo.disconnect();
    };
  }, [ref]);
  return off;
}

/**
 * The microphone, or a rejection after `timeoutMs` (a prompt left open, a denied permission). A stream
 * that arrives after the timeout is stopped at once, so a late "Allow" never leaves the mic on.
 */
export function requestMic(timeoutMs = 5000): Promise<MediaStream> {
  if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia) return Promise.reject(new Error("no mic"));
  const ask = navigator.mediaDevices.getUserMedia({ audio: true });
  return new Promise((resolve, reject) => {
    let late = false;
    const t = setTimeout(() => {
      late = true;
      reject(new Error("timeout"));
    }, timeoutMs);
    ask.then(
      (stream) => {
        clearTimeout(t);
        if (late) stream.getTracks().forEach((track) => track.stop());
        else resolve(stream);
      },
      (err) => {
        clearTimeout(t);
        reject(err);
      },
    );
  });
}

export function motionOff(el: Element | null | undefined) {
  if (typeof window === "undefined") return true;
  if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return true;
  return Boolean(el?.closest('[data-motion="off"]'));
}

function assignRef<T>(ref: React.Ref<T> | undefined, node: T | null): () => void {
  if (typeof ref === "function") {
    const cleanup = ref(node);
    return typeof cleanup === "function" ? cleanup : () => ref(null);
  }
  if (ref) (ref as React.RefObject<T | null>).current = node;
  return () => {
    if (ref) (ref as React.RefObject<T | null>).current = null;
  };
}

/** Composes two refs, keeping React 19 ref cleanups. */
export function useComposedRefs<T>(a: React.Ref<T> | undefined, b: React.Ref<T> | undefined): React.RefCallback<T> {
  return React.useCallback(
    (node: T | null) => {
      const release = [assignRef(a, node), assignRef(b, node)];
      return () => release.forEach((f) => f());
    },
    [a, b],
  );
}

/** Runs ours after theirs, unless theirs called preventDefault. */
export function composeHandler<E extends { defaultPrevented: boolean }>(theirs: ((e: E) => void) | undefined, ours: (e: E) => void) {
  return (e: E) => {
    theirs?.(e);
    if (!e.defaultPrevented) ours(e);
  };
}

/** Keeps the latest value in a ref, for callbacks read inside timers and animation frames. */
export function useLatest<T>(value: T) {
  const ref = React.useRef(value);
  React.useLayoutEffect(() => {
    ref.current = value;
  });
  return ref;
}

/*
 * Where Notchset overlays (menus, popovers, tooltips) portal to. Unset, document.body, as shadcn's
 * do. Provide an element to keep them inside a scoped theme: a preview, a widget, a shadow root.
 */
export const NotchsetPortalContext = React.createContext<HTMLElement | null>(null);
export function useNotchsetPortal(): HTMLElement | undefined {
  return React.useContext(NotchsetPortalContext) ?? undefined;
}

export { NOTCHSET_ROOT, OWN_SOUND } from "@/lib/beautiful-ui/notchset/root";

// ---------------------------------------------------------------------------------------------
// Parts
// ---------------------------------------------------------------------------------------------

export type NodeState = "off" | "on" | "live" | "signal";

/** The node: a 9×9 ring whose dot blooms on (`on`), blinks (`live`) or turns signal. */
function Node({ state = "off", delay, className, style, ...props }: Omit<React.ComponentProps<"svg">, "children"> & { state?: NodeState; delay?: number }) {
  return (
    <svg
      aria-hidden
      data-slot="indicator"
      data-state={state}
      width="9"
      height="9"
      viewBox="0 0 9 9"
      className={cn(`notchset-node`, className)}
      style={delay ? ({ "--notchset-node-delay": `${delay}ms`, ...style } as React.CSSProperties) : style}
      {...props}
    >
      <circle data-slot="indicator-ring" cx="4.5" cy="4.5" r="3.25" fill="none" strokeWidth="1" />
      <circle data-slot="indicator-dot" cx="4.5" cy="4.5" />
    </svg>
  );
}

/** A small solid round mark (the node where a leader meets a popup, a pointer): SVG, round at every DPR. */
function Dot({ size = 7, className, style }: { size?: number; className?: string; style?: React.CSSProperties }) {
  return (
    <svg aria-hidden width={size} height={size} viewBox={`0 0 ${size} ${size}`} className={cn("pointer-events-none absolute overflow-visible", className)} style={style}>
      <circle cx={size / 2} cy={size / 2} r={size / 2} fill="currentColor" />
    </svg>
  );
}

/** The legend: a mono label above a control, with an optional node (or any trailing reading). */
function Legend({ label, node, children, className, ...props }: React.ComponentProps<"div"> & { label?: React.ReactNode; node?: NodeState }) {
  return (
    <div data-slot="legend" className={cn(`notchset-legend`, className)} {...props}>
      <span data-slot="legend-label">{label}</span>
      {(children != null || node) && (
        <span data-slot="legend-reading" className="flex items-center gap-2">
          {children}
          {node && <Node state={node} />}
        </span>
      )}
    </div>
  );
}

/** A key on its plate: the plate is an offset frame behind the child; `--notchset-plate-color` tints it. */
function Plated({ className, children, ...props }: React.ComponentProps<"span">) {
  return (
    <span data-slot="plated" className={cn("relative inline-flex", className)} {...props}>
      <span aria-hidden data-slot="plate" className={`notchset-plate`} />
      {children}
    </span>
  );
}

/**
 * The success check. Mounted, it traces itself in over --notchset-check (instant under reduced
 * motion); pass `drawn` to drive the trace yourself.
 */
function Check({ drawn, size = 16 }: { drawn?: boolean; size?: number }) {
  return (
    <svg aria-hidden data-slot="check" width={size} height={size} viewBox="0 0 24 24">
      <path
        d="M4 12.5 L9.5 18 L20 6"
        fill="none"
        stroke="currentColor"
        strokeWidth={1.75}
        strokeLinecap="square"
        pathLength={1}
        strokeDasharray={1}
        className={drawn === undefined ? `notchset-check-draw` : undefined}
        style={drawn === undefined ? undefined : { strokeDashoffset: drawn ? 0 : 1, transition: "stroke-dashoffset var(--notchset-check) var(--notchset-ease-travel)" }}
      />
    </svg>
  );
}

/** The scanner: five 1px ticks fading in turn while something works (static, centre lit, when motion is off). */
function Scanner({ height = 12 }: { height?: number }) {
  return (
    <span aria-hidden data-slot="scanner" className={cn(`notchset-scanner`, "inline-flex items-end gap-[2px]")} style={{ height }}>
      {[0.82, 0.66, 1, 0.66, 0.82].map((h, i) => (
        <span key={i} className="w-px bg-current" style={{ height: height * h, animationDelay: `${i * 140 - 700}ms` }} />
      ))}
    </span>
  );
}

// ---------------------------------------------------------------------------------------------
// Glyph flip: each character cycles through glyphs and lands at 110 + i·14ms, left to right
// ---------------------------------------------------------------------------------------------

const GLYPHS = "ABCDEFGHJKLMNPRSTUVWXYZ0123456789#/+=";
const STILL = new Set([" ", "·", "/", ".", ":"]);

export function scramble(text: string, elapsed: number) {
  let out = "";
  for (let i = 0; i < text.length; i++) {
    const ch = text[i]!;
    out += STILL.has(ch) || elapsed >= 110 + i * 14 ? ch : GLYPHS[(Math.floor(elapsed / 36) * 5 + i * 11) % GLYPHS.length];
  }
  return out;
}

/**
 * The text to show for `text`: when it changes, a few frames of glyphs that land left to right.
 * Runs on requestAnimationFrame only while a flip is active; the settled text is never state. Off
 * under reduced motion, inside data-motion="off" (pass the element) or with `off`.
 */
export function useGlyphFlip(text: string, { off = false, el }: { off?: boolean; el?: React.RefObject<Element | null> } = {}) {
  const [frame, setFrame] = React.useState<{ for: string; shown: string } | null>(null);
  const first = React.useRef(true);
  React.useEffect(() => {
    if (first.current) {
      first.current = false;
      return;
    }
    if (off || motionOff(el?.current)) return;
    let raf = 0;
    const t0 = performance.now();
    const end = 110 + text.length * 14 + 40;
    const step = (now: number) => {
      const elapsed = now - t0;
      if (elapsed >= end) return setFrame(null);
      setFrame({ for: text, shown: scramble(text, elapsed) });
      raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [text, off, el]);
  return frame && frame.for === text ? frame.shown : text;
}

/** A label that flips when its text changes; screen readers read the final text only. */
function FlipText({ children, off, className, ...props }: Omit<React.ComponentProps<"span">, "children"> & { children: string; off?: boolean }) {
  const ref = React.useRef<HTMLSpanElement>(null);
  const shown = useGlyphFlip(children, { off, el: ref });
  return (
    <span ref={ref} data-slot="flip-text" className={cn("relative whitespace-nowrap", className)} {...props}>
      <span aria-hidden>{shown}</span>
      <span className="sr-only">{children}</span>
    </span>
  );
}

// ---------------------------------------------------------------------------------------------
// Sound: Notchset components announce cues; the page's beautiful-ui-sound layer (opt-in) plays them.
// ---------------------------------------------------------------------------------------------

/** The Notchset cues, in the shared sound layer's names (played in the mechanical voice). */
export const NOTCHSET_CUES = { tap: "tap", latch: "lock", signal: "destructive", confirm: "halt", done: "success", tick: "tick" } as const;
export type NotchsetCue = keyof typeof NOTCHSET_CUES;

export function playCue(el: Element | null, cue: NotchsetCue) {
  if (!el || typeof CustomEvent === "undefined" || el.closest('[data-sound="off"]')) return;
  el.dispatchEvent(new CustomEvent("beautiful-ui:sound", { bubbles: true, detail: { cue: NOTCHSET_CUES[cue] } }));
}

// ---------------------------------------------------------------------------------------------
// Shortcuts: "⌘↵" → "Meta+Enter Control+Enter" (aria-keyshortcuts)
// ---------------------------------------------------------------------------------------------

const KEY_NAMES: Record<string, string> = { "⌘": "Meta", "⌃": "Control", "⌥": "Alt", "⇧": "Shift", "↵": "Enter", "⏎": "Enter", "⎋": "Escape", "⌫": "Backspace", "⇥": "Tab", "␣": "Space" };

export function kbdToShortcut(kbd: string | undefined): string | undefined {
  if (!kbd) return undefined;
  const parts = [...kbd.replace(/\s+/g, "")].map((c) => KEY_NAMES[c] ?? c.toUpperCase());
  if (!parts.length) return undefined;
  const combo = parts.join("+");
  // ⌘ on a Mac is Ctrl elsewhere: announce both.
  return combo.includes("Meta") ? `${combo} ${combo.replace("Meta", "Control")}` : combo;
}

/**
 * A number that rolls: each digit is a 0–9 strip; the ones digit moves first and each digit to its left
 * 40ms later (620ms, a small overshoot). `delay` offsets the whole number (rows 60ms apart). Non-digits
 * (",", "$", "%") sit still. Screen readers get the plain value.
 */
function RollingNumber({ value, height = 32, delay = 0, className }: { value: string | number; height?: number; delay?: number; className?: string }) {
  const text = String(value);
  const digits = text.split("");
  const lastDigit = digits.reduce((n, d, i) => (/\d/.test(d) ? i : n), -1);
  return (
    <span className={cn("relative inline-flex overflow-hidden tabular-nums", className)} style={{ height, lineHeight: `${height}px` }}>
      <span className="sr-only">{text}</span>
      <span aria-hidden className="flex">
        {digits.map((d, i) =>
          /\d/.test(d) ? (
            <span
              key={digits.length - i}
              className="flex flex-col transition-transform duration-[620ms] ease-[cubic-bezier(.34,1.15,.5,1)]"
              style={{ transform: `translateY(${-Number(d) * height}px)`, transitionDelay: `${delay + (lastDigit - i) * 40}ms` }}
            >
              {Array.from({ length: 10 }, (_, n) => (
                <span key={n} style={{ height }}>
                  {n}
                </span>
              ))}
            </span>
          ) : (
            <span key={`s${digits.length - i}`}>{d}</span>
          ),
        )}
      </span>
    </span>
  );
}

export { Check, Dot, FlipText, Legend, Node, Plated, RollingNumber, Scanner };
```

### lib/beautiful-ui/notchset/root.ts

```tsx
/*
 * Notchset's root attributes, server-safe (no client directive), so server components can spread
 * them too. Every Notchset root (and portalled popup) carries data-notchset (the motion policy's
 * scope), plays its own cues in the mechanical voice, and keeps the page's click layer out.
 */
export const NOTCHSET_ROOT = { "data-notchset": "", "data-sound": "none", "data-sound-voice": "analogue" } as const;

/** On every interactive element: it plays its own cues, so the page's click layer adds no tap. */
export const OWN_SOUND = { "data-sound": "none" } as const;
```

The notchset foundation (the tokens listed above, keyframes and motion levels) installs once with the first component; its CSS is public at https://beautiful-ui.dev/r/notchset-foundation.json.
