@photon-ai/pho-ui 2.8.0 → 2.10.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.
@@ -173,6 +173,15 @@ export interface BasicPageTitleProps extends ComponentProps<"h1"> {
173
173
  */
174
174
  declare function Title({ id, morphId, enter, className, children, ...props }: BasicPageTitleProps): import("react").JSX.Element;
175
175
  export interface BasicPageStackProps extends Omit<ComponentProps<"div">, RevealMotionConflicts> {
176
+ /**
177
+ * Play the page entrance on mount (the default). `false` renders the
178
+ * stack at rest, for a loading state that is already on screen — painted
179
+ * as static HTML before the bundle, re-rendered as-is on React's first
180
+ * commit, then shown by the page until its data arrives — so that none of
181
+ * those mounts moves it; the real stack, with its entrance, takes over
182
+ * with the data.
183
+ */
184
+ enter?: boolean;
176
185
  ref?: Ref<HTMLDivElement>;
177
186
  }
178
187
  /**
@@ -192,10 +201,13 @@ export interface BasicPageStackProps extends Omit<ComponentProps<"div">, RevealM
192
201
  * - **1:1 skeleton** — keep ONE stack mounted and branch its children
193
202
  * (`{pending ? bones : content}`): values then fill in place with no
194
203
  * motion.
204
+ * - **Skeleton that is already on screen** (server-painted, then hydrated,
205
+ * then shown by the page) — `enter={false}`: the stack renders at rest and
206
+ * the entrance is the loaded stack's alone.
195
207
  *
196
208
  * Static under reduced motion.
197
209
  */
198
- declare function Stack({ className, children, ref: forwardedRef, ...props }: BasicPageStackProps): import("react").JSX.Element;
210
+ declare function Stack({ enter, className, children, ref: forwardedRef, ...props }: BasicPageStackProps): import("react").JSX.Element;
199
211
  /**
200
212
  * Title + optional description pair. No gap between them — the block's
201
213
  * `gap-3` only applies between this header and the card.
@@ -735,17 +747,18 @@ export interface BasicPageAsideProps {
735
747
  */
736
748
  declare function Aside({ open, variant, modal, placeholder, onClose, children, }: BasicPageAsideProps): import("react").JSX.Element | null;
737
749
  /**
738
- * The table's search, standing at the end of a section header — put it in
739
- * the `Header` beside the title and it adopts the searchable table's box
740
- * (the table then renders none of its own). On a header at least 36rem
741
- * wide the box simply stands open: the header is a container, CSS
742
- * decides, nothing is measured. On a narrower one a round button holds
743
- * the place; clicking it opens the box — the width glides linearly, the
744
- * page's travel — with focus landing inside. Emptied and blurred it
745
- * glides shut and the button returns; Escape clears, blurs, and shuts.
746
- * Renders nothing until a table with `search` is on the page.
747
- */
748
- declare function Search({ variant, className, ...props }: ComponentProps<"div"> & {
750
+ * Controlled, the query and its setter come together: a `value` with no
751
+ * way to change it would be a field that silently ignores typing.
752
+ */
753
+ type BasicPageSearchControl = {
754
+ value: string;
755
+ onValueChange: (value: string) => void;
756
+ } | {
757
+ value?: undefined;
758
+ onValueChange?: undefined;
759
+ };
760
+ export type BasicPageSearchProps = BasicPageSearchOwnProps & BasicPageSearchControl;
761
+ interface BasicPageSearchOwnProps extends ComponentProps<"div"> {
749
762
  /**
750
763
  * The face. `box` (default) is the bordered field beside a section
751
764
  * title — open on a wide header, a round button that glides it open on
@@ -754,7 +767,44 @@ declare function Search({ variant, className, ...props }: ComponentProps<"div">
754
767
  * a boxed control would be noise; it flexes instead of folding.
755
768
  */
756
769
  variant?: "box" | "ghost";
757
- }): import("react").JSX.Element;
770
+ /**
771
+ * When the box folds to its round button. `auto` (default): a header at
772
+ * least 36rem wide shows the box open, a narrower one the button.
773
+ * `always`: the button at every width, the box gliding open from it on
774
+ * click — for a list whose header has the room but wants the lighter
775
+ * face.
776
+ */
777
+ fold?: "auto" | "always";
778
+ /**
779
+ * The field's placeholder in the controlled mode (`value` with
780
+ * `onValueChange`: the page searches its own list rather than a table,
781
+ * the Search shows `placeholder`, reports typing through `onValueChange`,
782
+ * and clears through it on Escape; without `value` it adopts the
783
+ * searchable table on the page). About twenty characters fit the open box.
784
+ */
785
+ placeholder?: string;
786
+ /**
787
+ * The accessible name of the button and the field — the placeholder
788
+ * unless said otherwise, for a placeholder too short to say what the
789
+ * search covers.
790
+ */
791
+ label?: string;
792
+ }
793
+ /**
794
+ * A search standing at the end of a section header. Put it in the
795
+ * `Header` beside the title and it adopts the searchable table's box (the
796
+ * table then renders none of its own); give it `value` and it searches
797
+ * the page's own list of rows, no table needed. On a header at least
798
+ * 36rem wide the box simply stands open: the header is a container, CSS
799
+ * decides, nothing is measured. On a narrower one — or at every width
800
+ * with `fold="always"` — a round button holds the place; clicking it
801
+ * glides the box open on the page spring, the two faces crossfading, with
802
+ * focus landing inside. The box stays while it holds a query; emptied and
803
+ * blurred it glides shut and the button returns, and Escape clears,
804
+ * blurs, and shuts. Without `value`, renders nothing until a table with
805
+ * `search` is on the page.
806
+ */
807
+ declare function Search({ variant, fold, value, onValueChange, placeholder, label, className, ...props }: BasicPageSearchProps): import("react").JSX.Element;
758
808
  export declare const BasicPage: {
759
809
  Root: typeof Root;
760
810
  Aside: typeof Aside;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photon-ai/pho-ui",
3
- "version": "2.8.0",
3
+ "version": "2.10.0",
4
4
  "description": "Pho Design System — Photon's React component library, built on Base UI",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"basic-page-BI8y4hHk.js","names":[],"sources":["../../../../node_modules/.pnpm/@tabler+icons-react@3.46.0_react@19.2.8/node_modules/@tabler/icons-react/dist/esm/icons/IconChevronRight.mjs","../../src/sections/basic-page/basic-page.tsx"],"sourcesContent":["/**\n * @license @tabler/icons-react v3.46.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M9 6l6 6l-6 6\", \"key\": \"svg-0\" }]];\nconst IconChevronRight = createReactComponent(\"outline\", \"chevron-right\", \"ChevronRight\", __iconNode);\n\nexport { __iconNode, IconChevronRight as default };\n//# sourceMappingURL=IconChevronRight.mjs.map\n","import {\n Children,\n createContext,\n Fragment,\n isValidElement,\n useCallback,\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type AnchorHTMLAttributes,\n type ComponentProps,\n type ReactNode,\n type Ref,\n type RefObject,\n} from \"react\";\nimport {\n AnimatePresence,\n motion,\n useIsPresent,\n useReducedMotion,\n} from \"motion/react\";\nimport { createPortal } from \"react-dom\";\nimport { Dialog as BaseDialog } from \"@base-ui/react/dialog\";\nimport {\n IconChevronLeft,\n IconChevronRight,\n IconSearch,\n IconX,\n} from \"@tabler/icons-react\";\nimport {\n Button,\n ButtonLink,\n type ButtonLinkProps,\n type ButtonProps,\n} from \"../../components/button\";\nimport { Error as ErrorLine } from \"../../components/error\";\nimport { Form as FormElement } from \"../../components/form\";\nimport { Menu } from \"../../components/menu\";\nimport {\n createTableSearchStore,\n DataTable,\n GhostSearch,\n TableHostContext,\n TableSearchContext,\n} from \"../../components/data-table\";\nimport { InputGroup } from \"../../components/input-group\";\nimport {\n BAR_RISE_PX,\n FADE_ENTRANCE,\n FADE_SWAP,\n RISE_PX,\n SPRING_ENTRANCE,\n TRAVEL_LINEAR,\n} from \"../../motion\";\nimport { BLUR_MAX_PX, ScrollArea } from \"../../components/scroll-area\";\nimport { Toast } from \"../../components/toast\";\nimport { AnimatedHeight } from \"../../utils/animated-height\";\nimport { cn } from \"../../utils/cn\";\nimport { useLinkComponent } from \"../../utils/link-provider\";\n\n/**\n * BasicPage — Pho's standard content page: a page title, section titles, and\n * bordered cards of label/control rows. Settings, billing, members — any\n * single-column management surface.\n *\n * Spacing: wrap the page in `Root` (a 42rem column with `px-5` gutters,\n * vertical padding). `Stack` separates blocks (`gap-10`). Inside a block,\n * children share `gap-3`. Wrap a section title + description in `Header`\n * (no gap — title and description sit flush) so that pair stays tight —\n * don't put them as loose siblings.\n * Parts already carry `px-5`.\n *\n * Navigation: pages may drill into subpages. `RowLink` is a whole-row link\n * (trailing chevron) and `Back` returns to the parent — both render through\n * the host's `LinkProvider`; the URL itself stays the router's job. `Back`\n * floats in `Root`'s top padding, so `Title` never moves between pages that\n * have a back link and pages that don't.\n */\n\n/* ------------------------------- class map ------------------------------- */\n\n/** Card surface — border, corner, fill; no divider rules. */\nconst BASIC_PAGE_CARD_SURFACE =\n \"rounded-base border border-pho-secondary bg-pho-primary\";\n\n/**\n * Card divider rules — hairlines between direct children. A row followed by\n * an `Alert` drops its divider (the `:has()` rule) so the error line reads\n * as part of the row it belongs to — the alert's own bottom divider takes\n * over toward whatever follows. Kept separate from the surface so\n * `animateHeight` can move the dividers onto the measured content wrapper.\n */\nconst BASIC_PAGE_CARD_DIVIDERS =\n \"divide-y divide-pho-secondary [&>:has(+[data-basic-page-alert])]:border-b-0\";\n\n/** Bordered card that stacks rows with hairline dividers. */\nexport const BASIC_PAGE_CARD = `${BASIC_PAGE_CARD_SURFACE} ${BASIC_PAGE_CARD_DIVIDERS}`;\n\n/**\n * Standalone callout surface — featured block (invite form, notice, etc.).\n * Don't wrap in `Card`; this is the outer chrome.\n */\nexport const BASIC_PAGE_CALLOUT =\n \"flex min-w-0 flex-col gap-3 rounded-base border border-pho-secondary bg-pho-page p-5\";\n\n/**\n * One row — label (+ optional description) left, control right.\n * `min-h-[4.25rem]` (68px) keeps avatar/control rows from collapsing shorter\n * than a comfortable touch target.\n */\nexport const BASIC_PAGE_ROW =\n \"flex min-h-[4.25rem] min-w-0 flex-col gap-3 px-5 py-4 sm:flex-row sm:items-center sm:justify-between sm:gap-6\";\n\n/**\n * One card row that can host subviews. Same padding rhythm as a plain row —\n * an optional trailing `ItemPanel` breaks out of it on its own.\n */\nexport const BASIC_PAGE_ITEM = \"flex flex-col gap-3 px-5 py-4\";\n\n/**\n * Secondary strip closing an `Item` that is last in its card. Negative\n * margins escape the item's `px-5 pb-4` so the gray sheet runs edge-to-edge\n * and owns the row's bottom padding. The body above stays square and the\n * sheet's top is flush against it; only the bottom rounds, nesting into the\n * card's 12px corner (11px inside the 1px border). Content lands back on the\n * item's left gutter.\n */\nexport const BASIC_PAGE_ITEM_PANEL =\n \"-mx-5 -mb-4 flex items-center rounded-b-[calc(0.75rem-1px)] bg-pho-secondary py-1 pr-3 pl-5\";\n\n/**\n * Mid-list variant — the full-bleed sheet only reads well against the card's\n * bottom corner, so when later rows follow, the panel becomes an inset chip\n * instead: fully rounded at the small-control radius, overhanging the text\n * gutter by 8px per side (content stays aligned with the body above), and\n * leaving the row's own bottom padding and the card's hairline divider alone.\n * `-mt-1.5` collapses the item's `gap-3` (12px) to an effective `gap-1.5` (6px).\n */\nexport const BASIC_PAGE_ITEM_PANEL_DIVIDED =\n \"-mx-2 -mt-1.5 flex items-center rounded-sm bg-pho-secondary px-2 py-0.5\";\n\n/* --------------------------------- motion -------------------------------- */\n\n/**\n * One spring for every BasicPage motion — the `Stack` entrance, `Title` /\n * `Header` enter, `Reveal`, the Title ↔ Back morph, and the `Alert` /\n * `Footer` height reveals. Measured off a 60fps iOS push: ~330ms with a\n * long, gentle glide, everything co-landing. Essentially critically damped\n * (an overshooting edge would show clipped content).\n */\nconst ENTRANCE_SPRING = SPRING_ENTRANCE;\n\n/** Fade paired with the spring — spans most of the glide. */\nconst ENTRANCE_FADE = FADE_ENTRANCE;\n\n/**\n * Rise distance for the `Reveal` / `Stack` / `enter` entrances — small\n * travel; the content surfaces, it doesn't slide in.\n */\nconst REVEAL_RISE_PX = RISE_PX;\n\n/* --------------------------------- parts --------------------------------- */\n\n/**\n * Page chrome — centered column at the standard content width. Hosts\n * `Title` + `Stack` (and any route-level siblings). Prefer this over\n * ad-hoc `max-w-*` wrappers in the app shell.\n *\n * `44.5rem` = the 42rem (`2xl`) content column plus the two `px-5` gutters,\n * so wide viewports render exactly as before while narrow ones keep a 20px\n * breathing edge instead of running content into the viewport.\n *\n * `relative` anchors `Back`, which floats in the top padding. The top zone\n * is conditional (`:has()`): pages carrying a `Back` reserve 64px for it;\n * pages without one keep the tighter 48px so the title doesn't sit low.\n *\n * The column is also a `min-h-full` flex stack: when the host gives it a\n * height context (an app shell's scroll pane), a full-page state can claim\n * the leftover viewport with `flex-1` — `ErrorView className=\"flex-1\"`\n * centers itself vertically below the title. Hosts without a height context\n * see a normal column; regular pages are unaffected.\n *\n * Root itself never animates — the page entrance lives on `Stack`, so\n * `Title` and `Back` (the page chrome) stay put while the body below rises\n * in.\n */\nexport interface BasicPageRootProps extends ComponentProps<\"div\"> {\n ref?: Ref<HTMLDivElement>;\n /**\n * Condense on scroll — on by default. Once the title has scrolled out of\n * the pane, a sticky bar pins to the top with the way back and the page\n * name: the large title collapsing into its bar. It assembles itself\n * from `Title` and the trail's tail crumb; nothing to wire. Pass `false`\n * for a page that should scroll away whole.\n */\n condense?: boolean;\n /**\n * The content column's cap. `base` is the 42rem reading column every\n * settings page uses; `wide` opens it to 80rem for a page that is a\n * list first — rows with several columns. Left unsaid, the page sizes\n * itself: `base`, until it carries a `Table` — records in columns run\n * wide on their own. Forms stay on `base`: across a wide row a label\n * and its control drift out of one glance. Either width can host an\n * `Aside` — the column keeps its cap beside the panel, and leftover\n * space is margin, not content width.\n */\n width?: \"base\" | \"wide\";\n}\n\n/** Below this pane width (Tailwind's `md`) an `Aside` is the page, not a panel beside it. */\nconst ASIDE_PANE_MIN_PX = 768;\n\n/**\n * The panel's width — two fifths of a small pane, capped at 28rem. Both\n * sides keep their own measure: the page beside the panel holds its\n * column cap, and whatever is left over is margin, not content width.\n */\nfunction asideWidth(pane: number) {\n return Math.round(Math.min(Math.max(pane * 0.4, 320), 448));\n}\n\ninterface AsideHost {\n /** Where the panel mounts — a cell after the page's column. */\n slot: HTMLElement | null;\n /** Beside the page on a wide pane; as the page on a narrow one. */\n presentation: \"panel\" | \"page\";\n /** Panel width for this pane, px. */\n width: number;\n /** The scroll pane's height, px — null while the document itself scrolls. */\n paneHeight: number | null;\n setOpen: (open: boolean) => void;\n /** An `Aside` announces itself — measuring idles until one is mounted. */\n setPresent: (present: boolean) => void;\n}\n\nconst AsideHostContext = createContext<AsideHost | null>(null);\n\n/** Handed down by the panel to the page inside it. */\ninterface AsidePanelController {\n setBack: (back: CondenseTail | null) => void;\n}\n\nconst AsidePanelContext = createContext<AsidePanelController | null>(null);\n\n/**\n * A page hosts its `Aside`: it measures the pane it lives in — how wide\n * (which decides the presentation and the panel's width), how tall (the\n * panel's height) — and keeps a cell after its column for the panel to\n * mount into. Everything idles until an `Aside` is actually mounted.\n */\nfunction useAsideHost(rootRef: RefObject<HTMLDivElement | null>) {\n const [slot, setSlot] = useState<HTMLElement | null>(null);\n const [present, setPresent] = useState(false);\n const [open, setOpen] = useState(false);\n const [pane, setPane] = useState<{ width: number; height: number | null }>(\n () => ({\n width: typeof window === \"undefined\" ? 0 : window.innerWidth,\n height: null,\n }),\n );\n useLayoutEffect(() => {\n const el = rootRef.current;\n if (!present || el == null) return;\n const scroller = findScrollParent(el);\n const measure = () => {\n const width = el.offsetWidth;\n const height = scroller?.clientHeight ?? null;\n setPane((prev) =>\n prev.width === width && prev.height === height\n ? prev\n : { width, height },\n );\n };\n measure();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measure);\n observer.observe(el);\n if (scroller != null) observer.observe(scroller);\n return () => observer.disconnect();\n }, [rootRef, present]);\n const host = useMemo<AsideHost>(\n () => ({\n slot,\n presentation: pane.width >= ASIDE_PANE_MIN_PX ? \"panel\" : \"page\",\n width: asideWidth(pane.width),\n paneHeight: pane.height,\n setOpen,\n setPresent,\n }),\n [slot, pane],\n );\n return { host, setSlot, open };\n}\n\nfunction scrollTopOf(scroller: Element | null) {\n return scroller == null ? window.scrollY : scroller.scrollTop;\n}\n\nfunction scrollTo(scroller: Element | null, top: number) {\n if (scroller == null) window.scrollTo({ top });\n else scroller.scrollTop = top;\n}\n\nfunction Root({\n condense = true,\n width,\n className,\n children,\n ref,\n ...props\n}: BasicPageRootProps) {\n // While an app shell adopts the bar (CondenseScope), the way back lives\n // there from the start — the page's own corner trail hides and the\n // column keeps its tighter top. Inside an `Aside`'s panel likewise: the\n // close in the panel's corner is the way back.\n const adopted = useContext(CondenseScopeContext)?.adopt ?? false;\n const panel = useContext(AsidePanelContext);\n // A page that carries a table is a wide page — tables announce\n // themselves, so `width` can stay unsaid. Saying it wins either way.\n const [hasTable, setHasTable] = useState(false);\n const tableCount = useRef(0);\n const announceTable = useCallback((present: boolean) => {\n tableCount.current += present ? 1 : -1;\n setHasTable(tableCount.current > 0);\n }, []);\n const wide = width === \"wide\" || (width == null && hasTable);\n // The channel between a searchable table and a `Search` in a header.\n const [tableSearch] = useState(createTableSearchStore);\n const rootRef = useRef<HTMLDivElement | null>(null);\n const [title, setTitle] = useState<{\n label: ReactNode;\n el: HTMLElement | null;\n } | null>(null);\n const [tail, setTail] = useState<CondenseTail | null>(null);\n const registry = useMemo<CondenseRegistry>(() => ({ setTitle, setTail }), []);\n useEffect(() => {\n if (panel == null) return;\n panel.setBack(tail);\n return () => panel.setBack(null);\n }, [panel, tail]);\n const { host, setSlot, open: asideOpen } = useAsideHost(rootRef);\n // On a narrow pane the aside is the page: the pane starts at its top,\n // and when the aside closes, returns to where the list was.\n const asideIsPage = asideOpen && host.presentation === \"page\";\n const wasPage = useRef(false);\n const listScroll = useRef(0);\n useLayoutEffect(() => {\n const el = rootRef.current;\n if (asideIsPage === wasPage.current || el == null) return;\n wasPage.current = asideIsPage;\n const scroller = findScrollParent(el);\n if (asideIsPage) {\n listScroll.current = scrollTopOf(scroller);\n scrollTo(scroller, 0);\n } else {\n scrollTo(scroller, listScroll.current);\n }\n }, [asideIsPage]);\n // The page is a full-width shell that owns the chrome — the trail in its\n // top-left corner, the condensing bar across its top — around a centered\n // content column. The chrome belongs to the pane, not the column: on a\n // wide pane the way back stays at the corner while the column centers.\n const bar = condense ? (\n <CondenseBar\n title={title}\n tail={panel == null ? tail : null}\n silent={asideIsPage || panel != null}\n />\n ) : null;\n const column = (\n <div\n data-basic-page-column=\"\"\n className={cn(\n \"mx-auto flex w-full min-w-0 flex-1 flex-col px-5 pt-12 pb-12 *:min-w-0\",\n // Wide pages breathe wider from `md` up: 32px gutters (narrow\n // viewports keep the 20px edge), cap = 80rem content + the pair.\n wide ? \"max-w-[84rem] md:px-8\" : \"max-w-[44.5rem]\",\n adopted || panel != null\n ? \"[&_[data-basic-page-back]]:hidden\"\n : \"has-[[data-basic-page-back]]:pt-16\",\n )}\n >\n {condense ? (\n <CondenseContext.Provider value={registry}>\n {children}\n </CondenseContext.Provider>\n ) : (\n children\n )}\n </div>\n );\n return (\n <div\n data-basic-page-root=\"\"\n data-basic-page-width={wide ? \"wide\" : undefined}\n className={cn(\n \"relative flex min-h-full w-full min-w-0 flex-row\",\n className,\n )}\n {...props}\n ref={(node) => {\n rootRef.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref != null) ref.current = node;\n }}\n >\n <AsideHostContext.Provider value={host}>\n <TableHostContext.Provider value={announceTable}>\n <TableSearchContext.Provider value={tableSearch}>\n <div\n data-basic-page-main=\"\"\n className={cn(\n \"flex min-w-0 flex-1 flex-col\",\n asideIsPage && \"hidden\",\n )}\n >\n {bar}\n {column}\n </div>\n <div ref={setSlot} className=\"contents\" />\n </TableSearchContext.Provider>\n </TableHostContext.Provider>\n </AsideHostContext.Provider>\n </div>\n );\n}\n\n/**\n * The condensed header — hidden until the whole heading has left the\n * scrollport (observed against the nearest scrollable ancestor), then fades\n * in pinned to the top: the tail crumb as the way back at the left, the\n * page name centered. Unmounted while hidden so it never catches focus or\n * clicks meant for the trail beneath it. It stacks above a scroll area's\n * own top edge (`z-20` over its `z-10`), whose gradient blur would\n * otherwise sit on the title text.\n */\nfunction CondenseBar({\n title,\n tail,\n silent = false,\n}: {\n title: { label: ReactNode; el: HTMLElement | null } | null;\n tail: CondenseTail | null;\n /** A page hidden behind its aside says nothing to the scope. */\n silent?: boolean;\n}) {\n const reduceMotion = useReducedMotion();\n const [condensed, setCondensed] = useState(false);\n const scope = useContext(CondenseScopeContext);\n const owner = useId();\n useEffect(() => {\n if (scope == null) return;\n if (silent) {\n scope.store.clear(owner);\n return;\n }\n scope.store.set(owner, {\n condensed,\n title: title?.label ?? null,\n back: tail,\n });\n }, [scope, owner, condensed, title, tail, silent]);\n useEffect(() => {\n if (scope == null) return;\n return () => scope.store.clear(owner);\n }, [scope, owner]);\n const el = title?.el ?? null;\n useEffect(() => {\n if (el == null || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(\n ([entry]) => setCondensed(entry != null && !entry.isIntersecting),\n { root: findScrollParent(el) },\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [el]);\n return (\n <div\n data-basic-page-condense=\"\"\n className=\"sticky top-0 z-20 h-0 overflow-visible\"\n >\n <AnimatePresence>\n {condensed && !(scope?.adopt ?? false) && (\n <motion.div\n initial={{\n opacity: 0,\n y: reduceMotion ? 0 : CONDENSE_BAR_RISE_PX,\n }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: reduceMotion ? 0 : CONDENSE_BAR_RISE_PX }}\n transition={{ ...CONDENSE_BAR_ENTER, y: CONDENSE_BAR_TRAVEL }}\n // Three columns, the outer two equal, so the page name sits at\n // the bar's center whatever the way back is wide, never under it;\n // a long name truncates in its own cell. A name that is itself a\n // flex row (a word and a badge) centers as a block, which\n // text-align alone would not do.\n className=\"border-pho-secondary bg-pho-page/85 relative grid h-12 grid-cols-[1fr_minmax(0,auto)_1fr] items-center border-b px-3 backdrop-blur\"\n >\n <div className=\"flex min-w-0 items-center justify-start\">\n {tail != null && (\n <ButtonLink\n variant=\"ghost\"\n size=\"sm\"\n href={tail.href}\n onClick={tail.onClick}\n prefix={\n <span className=\"inline-flex [&>svg]:size-[1.125em] [&>svg]:shrink-0\">\n <IconChevronLeft />\n </span>\n }\n className=\"min-w-max\"\n >\n {tail.label}\n </ButtonLink>\n )}\n </div>\n <span className=\"text-pho-primary block max-w-full min-w-0 truncate text-center text-base font-medium\">\n {title?.label}\n </span>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n}\n\n/**\n * Slug for hash targets — \"Danger zone\" → `danger-zone`. Empty / non-string\n * children yield no id; pass an explicit `id` to override or `id=\"\"` to opt out.\n */\nfunction slugifyHeading(value: string): string {\n return value\n .normalize(\"NFKD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nfunction headingIdFromChildren(children: ReactNode): string | undefined {\n if (typeof children !== \"string\" && typeof children !== \"number\") {\n return undefined;\n }\n const slug = slugifyHeading(String(children));\n return slug || undefined;\n}\n\n/** Resolve `id`: explicit wins; `\"\"` opts out; else slug from plain-text children. */\nfunction resolveHeadingId(\n id: string | undefined,\n children: ReactNode,\n): string | undefined {\n if (id === \"\") return undefined;\n if (id != null) return id;\n return headingIdFromChildren(children);\n}\n\n/**\n * Namespaced Motion `layoutId` for the Title ↔ Back shared-element morph —\n * give a parent page's `Title` and its subpage's `Back` the same `morphId`\n * and the word itself travels between the two: the large title shrinks into\n * the back link on drill-in, and grows back on return.\n */\nconst morphLayoutId = (id: string) => `basic-page-morph-${id}`;\n\n/** The morph rides the page-tier entrance spring — one glide, everywhere. */\nconst MORPH_TRANSITION = { layout: ENTRANCE_SPRING } as const;\n\n/**\n * Chevron entrance for a morphing `Back`, measured off a 60fps iOS push\n * (sub-pixel tracking of the recording): the arrow emerges from under the\n * left edge of the arriving word at ~62% of the flight — sliding left by\n * ~2/3 of its own width with a strong ease-out while fading in over ~120ms\n * — and lands together with the word, which is still gliding its last few\n * pixels the whole time. Nothing is sequential.\n */\nconst MORPH_CHEVRON_RISE_X = 10;\nconst MORPH_CHEVRON_ENTER = {\n delay: 0.2,\n duration: 0.12,\n ease: \"easeOut\",\n} as const;\n\n/**\n * Ink crossfade for the morphing word — iOS redraws the word in the\n * destination's ink and fades the origin-ink rendition out over the first\n * half of the flight, so the word takes off in the color it had and lands\n * in the color it gets. `MorphLabel` mounts the ghost only while a layout\n * animation is actually running, so static renders never see it.\n */\nconst MORPH_INK_FADE = { duration: 0.18, ease: \"easeOut\" } as const;\n\n/**\n * Deeper than one level, the back link's slot is already occupied when a\n * navigation happens, and iOS treats the chevron as furniture: it stays put\n * across pushes and pops between subpages, and only the label changes.\n * Measured off the WLAN → Succade push and its pop in the same recording:\n *\n * - **Push** — the outgoing back label leaves as the old title shrinks\n * into its place: iOS slides it the whole way under the chevron in\n * ~130ms; here it fades out with a short drift in that direction — the\n * full slide read as a whoosh next to a word that is still arriving.\n * - **Pop** — the incoming back label slides right out from under the\n * chevron, starting ~40% into the title's return flight, ~150ms.\n * - **Pop to the root** — no back link remains; the chevron fades out in\n * place over the first frames while the word grows into the title.\n */\nconst BACK_LABEL_LEAVE_X = -8;\nconst BACK_LABEL_LEAVE = { duration: 0.16, ease: \"easeOut\" } as const;\nconst BACK_LABEL_ARRIVE = {\n delay: 0.14,\n duration: 0.16,\n ease: \"easeOut\",\n} as const;\nconst BACK_CHEVRON_LEAVE = { duration: 0.12, ease: \"easeOut\" } as const;\n\n/**\n * Trails (breadcrumb navs) currently mounted — one per page. Read at render\n * by an arriving `Breadcrumbs` or morphing `Title`: the outgoing page's\n * trail is still mounted at that moment, so the arriving side knows one is\n * on its way out, and how long it was, and can play the depth transition —\n * hold the chevron, see the old label off, fade a separator, or fade the\n * chevron when no trail returns. `useLayoutEffect` keeps the list exact\n * within a commit: an outgoing trail's cleanup runs before an incoming\n * trail's mount effect.\n */\nconst mountedTrails: { labels: ReactNode[] }[] = [];\n\nfunction useTrailPresence(labels: ReactNode[]) {\n const entry = useRef({ labels });\n entry.current.labels = labels;\n useLayoutEffect(() => {\n const self = entry.current;\n mountedTrails.push(self);\n return () => {\n mountedTrails.splice(mountedTrails.indexOf(self), 1);\n };\n }, []);\n}\n\n/** The trail on its way out at this render, if any — snapshot at mount. */\nfunction useDepartingTrail() {\n const [departing] = useState<{ labels: ReactNode[] } | null>(() => {\n const last = mountedTrails[mountedTrails.length - 1];\n return last ? { labels: [...last.labels] } : null;\n });\n return departing;\n}\n\n/**\n * Condensing header — the page's way back once the title has scrolled away.\n * `Root condense` renders a sticky bar in the reserved top zone; `Title` and\n * `Breadcrumbs` report what belongs in it (the heading, the tail crumb)\n * through this context, so the bar assembles itself and consumers only set\n * the flag. The bar stays out of the way until the whole heading has left\n * the scrollport, then fades in pinned to the top.\n */\ninterface CondenseTail {\n label: ReactNode;\n href: string;\n onClick?: ComponentProps<\"a\">[\"onClick\"];\n}\n\ninterface CondenseRegistry {\n setTitle: (\n title: { label: ReactNode; el: HTMLElement | null } | null,\n ) => void;\n setTail: (tail: CondenseTail | null) => void;\n}\n\nconst CondenseContext = createContext<CondenseRegistry | null>(null);\n\n/**\n * What the condensed bar would show, for an app shell that shows it\n * itself: whether the title has left the pane, the page name, the way\n * back. Idle (`condensed: false`, both null) outside any `CondenseScope`.\n */\nexport interface BasicPageCondense {\n condensed: boolean;\n title: ReactNode | null;\n back: CondenseTail | null;\n}\n\nconst CONDENSE_IDLE: BasicPageCondense = {\n condensed: false,\n title: null,\n back: null,\n};\n\ninterface CondenseStore {\n get: () => BasicPageCondense;\n set: (owner: string, next: BasicPageCondense) => void;\n clear: (owner: string) => void;\n subscribe: (listener: () => void) => () => void;\n}\n\nconst CondenseScopeContext = createContext<{\n store: CondenseStore;\n adopt: boolean;\n} | null>(null);\n\nconst subscribeToNothing = () => () => {};\nconst getIdle = () => CONDENSE_IDLE;\n\n/**\n * Hands the condensed bar to the app shell. On a narrow viewport the shell\n * already has a bar of its own; two stacked bars is one too many. Wrap the\n * shell (its bar and the routed pages) in `CondenseScope` and read\n * `useCondense()` where the bar is: while `condensed`, show `title` in the\n * middle and `back` as a plain chevron beside the menu button. While\n * `adopt` is true the page renders no bar of its own; pass `adopt={false}`\n * on viewports where the shell bar does not exist (desktop) and the page's\n * bar returns. State still tracks either way — `useCondense()` always\n * answers.\n */\nfunction CondenseScope({\n adopt = true,\n children,\n}: {\n adopt?: boolean;\n children?: ReactNode;\n}) {\n const [store] = useState<CondenseStore>(() => {\n let snapshot = CONDENSE_IDLE;\n let owner: string | null = null;\n const listeners = new Set<() => void>();\n const emit = () => {\n for (const listener of listeners) listener();\n };\n return {\n get: () => snapshot,\n set(id, next) {\n owner = id;\n snapshot = next;\n emit();\n },\n // Only the last writer clears — during a page swap the departing\n // page unmounts after the arriving one has already spoken.\n clear(id) {\n if (owner !== id) return;\n owner = null;\n snapshot = CONDENSE_IDLE;\n emit();\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n });\n const value = useMemo(() => ({ store, adopt }), [store, adopt]);\n return (\n <CondenseScopeContext.Provider value={value}>\n {children}\n </CondenseScopeContext.Provider>\n );\n}\n\n/** The nearest scope's condense state — see `CondenseScope`. */\nexport function useCondense(): BasicPageCondense {\n const scope = useContext(CondenseScopeContext);\n return useSyncExternalStore(\n scope?.store.subscribe ?? subscribeToNothing,\n scope?.store.get ?? getIdle,\n scope?.store.get ?? getIdle,\n );\n}\n\n/**\n * The condensed bar's motion, exported for a shell that adopts the bar\n * (`CondenseScope`) so its swap moves the same way: 0.2s ease-out, dropping\n * in from 8px above and leaving the same way.\n */\nexport const CONDENSE_BAR_ENTER = FADE_SWAP;\n/**\n * The displacement channel of the same move: travel is linear, like the\n * library's other linear transitions (the 100ms control inks), while the\n * fade and the soft focus keep the ease-out. Spread it per axis:\n * `transition={{ ...CONDENSE_BAR_ENTER, y: CONDENSE_BAR_TRAVEL }}`.\n */\nexport const CONDENSE_BAR_TRAVEL = TRAVEL_LINEAR;\nexport const CONDENSE_BAR_RISE_PX = BAR_RISE_PX;\n\n/** Nearest scrollable ancestor — the observer root; `null` means viewport. */\nfunction findScrollParent(el: HTMLElement): Element | null {\n if (typeof getComputedStyle !== \"function\") return null;\n for (\n let node = el.parentElement;\n node != null && node !== document.body;\n node = node.parentElement\n ) {\n const { overflowY } = getComputedStyle(node);\n if (overflowY === \"auto\" || overflowY === \"scroll\") return node;\n }\n return null;\n}\n\n/**\n * A shared element can only fly between two points the user has seen. Each\n * morph word tracks whether it is on screen (keyed by `morphId`); when the\n * other end mounts, it flies only if the word it continues was visible.\n * A title scrolled out of view yields a plain fade at the destination\n * instead of a dive in from off-screen.\n */\nconst morphWordSeen = new Map<string, { visible: boolean; el: Element }>();\n\n/**\n * A flight is worth it only over a short distance. The title sits at the\n * column's left edge, the crumb in the page's top-left corner, so the\n * distance between the two ends is the column's inset from the page's\n * edge. Up to a short word's width the morph reads as one word moving\n * up; on a wide pane the column centers far from the corner, the flight\n * becomes a long streak across empty space, and the word fades across\n * instead (the same arrival it plays when the other end was never seen).\n * Measured when the flight is decided, against the page the word is\n * leaving (still mounted at that render), so a window resized since that\n * page loaded is judged as it is now, not as it was.\n */\nconst MORPH_FLIGHT_MAX_INSET = 96;\n\n/** The column's inset from the page's left edge, for a word inside a `Root`. */\nfunction columnInset(el: Element): number {\n const root = el.closest(\"[data-basic-page-root]\");\n const column = root?.querySelector(\"[data-basic-page-column]\");\n if (!root || !column) return 0;\n return (\n column.getBoundingClientRect().left - root.getBoundingClientRect().left\n );\n}\n\n/**\n * Mount-locked flight decision + live visibility tracking for a morph word.\n * `fly` is read from the registry once at mount (was the other end on\n * screen?); the ref then feeds this word's own visibility back for the next\n * navigation. No-ops without `IntersectionObserver` (jsdom, SSR).\n */\nfunction useMorphFlight(morphId: string | undefined) {\n const [fly] = useState(() => {\n if (morphId == null) return false;\n const seen = morphWordSeen.get(morphId);\n return (\n seen != null &&\n seen.visible &&\n seen.el.isConnected &&\n columnInset(seen.el) <= MORPH_FLIGHT_MAX_INSET\n );\n });\n const ref = useRef<HTMLSpanElement | null>(null);\n useEffect(() => {\n if (morphId == null || typeof IntersectionObserver === \"undefined\") return;\n const el = ref.current;\n if (!el) return;\n const observer = new IntersectionObserver(([entry]) => {\n if (entry) {\n morphWordSeen.set(morphId, { visible: entry.isIntersecting, el });\n }\n });\n observer.observe(el);\n return () => {\n observer.disconnect();\n // The other end reads the registry while rendering — before this\n // cleanup runs in the same commit — so clearing here keeps a page-swap\n // flight intact while a later, unrelated mount (the list re-entered\n // from the sidebar) starts fresh instead of trusting stale sight.\n morphWordSeen.delete(morphId);\n };\n }, [morphId]);\n return { fly, ref };\n}\n\n/** How a morph word shows up when it is not flying in. */\ntype MorphArrival = \"fade\" | \"rise\" | \"slide\";\n\nconst MORPH_ARRIVALS = {\n // Back label with nothing to fly from — a plain fade.\n fade: {\n initial: { opacity: 0 },\n animate: { opacity: 1 },\n transition: { opacity: ENTRANCE_FADE },\n },\n // Subpage title behind a departing morph — rises from just below.\n rise: {\n initial: { opacity: 0, y: REVEAL_RISE_PX },\n animate: { opacity: 1, y: 0 },\n transition: { y: ENTRANCE_SPRING, opacity: ENTRANCE_FADE },\n },\n // Back label on a pop between subpages — out from under the chevron.\n slide: {\n initial: { x: \"-100%\" },\n animate: { x: 0 },\n transition: { x: BACK_LABEL_ARRIVE },\n },\n} as const;\n\n/**\n * The shared-element word of a Title ↔ Back morph. While the box flies, an\n * origin-ink copy of the word sits on top and fades out (the iOS snapshot\n * crossfade); the copy only exists during a real layout animation. When the\n * flight is skipped (`fly` false — the other end wasn't on screen, or there\n * is no other end yet), the layout snap is instant and the word appears in\n * place, or plays its `arrival` instead: a fade, a rise on the entrance\n * spring (`Title`'s `enter`), or a slide out from under the back chevron.\n */\nfunction MorphLabel({\n morphId,\n fromClassName,\n fly,\n morphRef,\n arrival,\n className,\n children,\n}: {\n morphId: string;\n /** Ink the word arrives FROM (the other end's text color). */\n fromClassName: string;\n /** Fly the layout box — the other end was on screen at navigation. */\n fly: boolean;\n morphRef: React.Ref<HTMLSpanElement>;\n /** Entrance to play when not flying; omit to appear in place. */\n arrival?: MorphArrival;\n /** Extra classes on the flying span (Title's ellipsis lives here). */\n className?: string;\n children: ReactNode;\n}) {\n const [crossfading, setCrossfading] = useState(false);\n const arriving = !fly && arrival != null ? MORPH_ARRIVALS[arrival] : null;\n return (\n <motion.span\n ref={morphRef}\n layoutId={morphLayoutId(morphId)}\n initial={arriving?.initial}\n animate={arriving?.animate}\n transition={\n fly\n ? MORPH_TRANSITION\n : { layout: { duration: 0 }, ...arriving?.transition }\n }\n onLayoutAnimationStart={() => {\n if (fly) setCrossfading(true);\n }}\n onLayoutAnimationComplete={() => setCrossfading(false)}\n className={cn(\"relative inline-block\", className)}\n >\n {children}\n {crossfading && (\n <motion.span\n aria-hidden\n initial={{ opacity: 1 }}\n animate={{ opacity: 0 }}\n transition={MORPH_INK_FADE}\n // The ghost retires itself: a zero-distance \"flight\" (a crumb that\n // stays put between pages) starts a layout animation that never\n // reports completion.\n onAnimationComplete={() => setCrossfading(false)}\n className={cn(\"absolute inset-0\", fromClassName)}\n >\n {children}\n </motion.span>\n )}\n </motion.span>\n );\n}\n\nexport interface BasicPageTitleProps extends ComponentProps<\"h1\"> {\n /**\n * Shared-element key. Pair with the same `morphId` on the subpage's\n * `Back` and the word morphs between title and back link across the\n * navigation. Skipped under reduced motion.\n */\n morphId?: string;\n /**\n * Rise this title into its line on mount — for a subpage title whose line\n * an inbound Title ↔ Back morph departs from. The moment the word lifts\n * off, this title floats up from just below it (the Stack physics), so\n * the two form one upward stream and never overlap. Combines with\n * `morphId` for a mid-level page (a list that is itself a subpage): the\n * word rises in when the page is entered from its parent and flies in\n * from the back link when returned to from its own subpage. Skipped\n * under reduced motion.\n */\n enter?: boolean;\n ref?: Ref<HTMLHeadingElement>;\n}\n\n/**\n * Ellipsis for a title word that moves. A morphing word flies in from the\n * back link's line above, and an entering word rises from below the\n * baseline: both spend part of the animation outside the heading's box, so\n * the heading itself must not clip — the `overflow: hidden` that\n * `truncate` brings lives on the moving span instead. `align-top` keeps an\n * `inline-block` with hidden overflow from taking its baseline at the\n * bottom edge, which would push the line box (and everything under the\n * title) down by the descender.\n */\nconst TITLE_MOTION_LABEL = \"max-w-full min-w-0 truncate align-top\";\n\n/**\n * Page title — largest heading on the surface. Plain-text children get a slug\n * `id` for hash targets (`#profile`); the heading itself is not a link.\n * Long names ellipsize; keep trailing accessories (`Badge`, status) as\n * `shrink-0` siblings of a `min-w-0 truncate` label inside a `flex min-w-0`\n * row so they stay visible.\n */\nfunction Title({\n id,\n morphId,\n enter,\n className,\n children,\n ...props\n}: BasicPageTitleProps) {\n const headingId = resolveHeadingId(id, children);\n const reduceMotion = useReducedMotion();\n const flight = useMorphFlight(morphId);\n const departingTrail = useDepartingTrail();\n const moving = !reduceMotion && (morphId != null || enter === true);\n // How the word shows up when it is not flying in: `enter` rises it from\n // below; so does a pop (a trail on its way out) whose flight was declined\n // for distance, rather than letting the title simply appear.\n const arrival: MorphArrival | undefined =\n enter || departingTrail != null ? \"rise\" : undefined;\n // Under `Root condense`, hand the bar this heading — its words and its\n // element, so the bar knows what to say and when the heading is gone.\n const condense = useContext(CondenseContext);\n const headingRef = useRef<HTMLHeadingElement | null>(null);\n useLayoutEffect(() => {\n if (condense == null) return;\n condense.setTitle({ label: children, el: headingRef.current });\n return () => condense.setTitle(null);\n }, [condense, children]);\n // A word flying in from a trail while that trail's page leaves: if no\n // trail takes over the slot (a pop to the root), its chevron is left to\n // this title to fade out. Decided after the commit's mount effects, so a\n // page that brings its own trail (a pop between subpages) keeps the\n // chevron still instead of ghosting one under it.\n const [chevronGhost, setChevronGhost] = useState(\n flight.fly && departingTrail != null,\n );\n useLayoutEffect(() => {\n if (mountedTrails.length > 0) setChevronGhost(false);\n }, []);\n return (\n <h1\n id={headingId}\n className={cn(\n \"text-display-base text-pho-primary max-w-full min-w-0 scroll-mt-12 px-5 font-medium\",\n // A static title ellipsizes on the heading; a moving one hands the\n // clip to its span so the flight / rise is never cut off.\n moving ? \"whitespace-nowrap\" : \"truncate\",\n className,\n )}\n {...props}\n ref={(node: HTMLHeadingElement | null) => {\n headingRef.current = node;\n const forwarded = (props as { ref?: Ref<HTMLHeadingElement> }).ref;\n if (typeof forwarded === \"function\") forwarded(node);\n else if (forwarded != null) forwarded.current = node;\n }}\n >\n {morphId != null && !reduceMotion ? (\n <MorphLabel\n morphId={morphId}\n fromClassName=\"text-pho-secondary\"\n fly={flight.fly}\n morphRef={flight.ref}\n arrival={arrival}\n className={TITLE_MOTION_LABEL}\n >\n {children}\n </MorphLabel>\n ) : enter && !reduceMotion ? (\n <motion.span\n initial={{ opacity: 0, y: REVEAL_RISE_PX }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ y: ENTRANCE_SPRING, opacity: ENTRANCE_FADE }}\n className={cn(\"inline-block\", TITLE_MOTION_LABEL)}\n >\n {children}\n </motion.span>\n ) : (\n children\n )}\n {chevronGhost && (\n <motion.span\n aria-hidden\n initial={{ opacity: 1 }}\n animate={{ opacity: 0 }}\n transition={BACK_CHEVRON_LEAVE}\n onAnimationComplete={() => setChevronGhost(false)}\n className={cn(\n \"text-pho-secondary pointer-events-none absolute inline-flex items-center\",\n BACK_CHEVRON_GHOST_POSITION,\n )}\n >\n <IconChevronLeft />\n </motion.span>\n )}\n </h1>\n );\n}\n\n/**\n * Arrival — content that replaces a skeleton reveals on its own. `Stack`\n * watches its `aria-busy`: the render in which it flips from true to false\n * is an arrival, and the rows and items mounting in that render rise in,\n * each on its own, dividers intact. Nothing else counts as arriving: a\n * remount under a new `key`, a row the user toggled on, a page's first\n * render (the stack's entrance covers that) all appear plainly. The\n * declaration is the `aria-busy` a loading page carries anyway.\n */\nconst ArrivalContext = createContext(false);\n\nfunction useArrival() {\n const arriving = useContext(ArrivalContext);\n const reduceMotion = useReducedMotion();\n const [arrives] = useState(() => arriving && !reduceMotion);\n return arrives;\n}\n\n/**\n * Leaving — a row removed from a `Card` collapses and fades out on its own.\n * `Card` keeps departing children around until their exit has played; a\n * row knows it is leaving through presence and clips itself meanwhile.\n * Padding and the divider's border go to zero with the height, so the rows\n * below close up smoothly. Under reduced motion both moments are instant.\n */\nconst ROW_EXIT = {\n height: 0,\n opacity: 0,\n paddingTop: 0,\n paddingBottom: 0,\n borderTopWidth: 0,\n} as const;\nconst ROW_TRANSITION = {\n y: ENTRANCE_SPRING,\n opacity: ENTRANCE_FADE,\n height: ENTRANCE_SPRING,\n paddingTop: ENTRANCE_SPRING,\n paddingBottom: ENTRANCE_SPRING,\n borderTopWidth: ENTRANCE_SPRING,\n} as const;\n\nfunction useRowMotion() {\n const arrives = useArrival();\n const isPresent = useIsPresent();\n const reduceMotion = useReducedMotion();\n return {\n initial: arrives ? { opacity: 0, y: REVEAL_RISE_PX } : false,\n animate: { opacity: 1, y: 0 },\n exit: ROW_EXIT,\n transition: reduceMotion ? { duration: 0 } : ROW_TRANSITION,\n leaving: !isPresent,\n } as const;\n}\n\n/**\n * Development only: the rhythm is the system's, and a wrapper that brings\n * its own gap is the one mistake that keeps recurring. After mount, a\n * container looks at what it got and says so in the console when a block\n * is spaced by some other number. Lint catches the classes; this catches\n * what lint cannot see (a className built at runtime, a host component's\n * own wrapper). Silent where no stylesheet applies (tests, SSR).\n */\n// Guarded: the browser has no `process` global, and this runs at import.\nconst RHYTHM_CHECK =\n typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\";\n\nfunction useRhythmCheck(\n ref: RefObject<HTMLElement | null>,\n part: string,\n own: number,\n child: (el: Element) => number | null,\n) {\n useEffect(() => {\n if (!RHYTHM_CHECK || ref.current == null) return;\n if (typeof getComputedStyle !== \"function\") return;\n const gapOf = (el: Element) => {\n const value = getComputedStyle(el).rowGap;\n return value === \"\" || value === \"normal\" ? null : parseFloat(value);\n };\n const ownGap = gapOf(ref.current);\n if (ownGap != null && ownGap !== own) {\n console.warn(\n `[BasicPage.${part}] spaced at ${ownGap}px instead of ${own}px. Spacing is the system's: Stack gap-10 between blocks, a block gap-3, Fields gap-5. Remove the class and move the wrapper instead.`,\n ref.current,\n );\n }\n for (const el of Array.from(ref.current.children)) {\n const expected = child(el);\n if (expected == null) continue;\n if (getComputedStyle(el).display === \"contents\") continue;\n const gap = gapOf(el);\n if (gap != null && gap !== expected) {\n console.warn(\n `[BasicPage.${part}] a child is spaced at ${gap}px instead of ${expected}px. Spacing is the system's: Stack gap-10 between blocks, a block gap-3, Fields gap-5. Remove the class and move the wrapper instead.`,\n el,\n );\n }\n }\n });\n}\n\nexport interface BasicPageStackProps extends Omit<\n ComponentProps<\"div\">,\n RevealMotionConflicts\n> {\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * Vertical stack under the page title. Blocks (forms / sections) use `gap-10`;\n * each block lays out its children with `gap-3`.\n *\n * Mounting plays the page entrance: the stack rises 12px and fades in on the\n * Card-shell / Command spring while `Title` / `Back` above stay put. What\n * that means per state:\n *\n * - **Entering the page** — whatever the page first shows (skeleton\n * included) rides in with its stack.\n * - **Skeleton in a separate component** — swapping it for the loaded\n * return mounts a fresh stack, so the entrance replays as the\n * data-arrival animation. That's the right feel for shapes the skeleton\n * couldn't predict (lists of unknown length).\n * - **1:1 skeleton** — keep ONE stack mounted and branch its children\n * (`{pending ? bones : content}`): values then fill in place with no\n * motion.\n *\n * Static under reduced motion.\n */\nfunction Stack({\n className,\n children,\n // Pulled out so the spread below can't hand `ref` to motion.div a second\n // time — a later duplicate would replace the composite callback and both\n // the rhythm check and the forwarded ref would silently lose the node.\n ref: forwardedRef,\n ...props\n}: BasicPageStackProps) {\n const reduceMotion = useReducedMotion();\n const stackRef = useRef<HTMLDivElement | null>(null);\n // Blocks run at gap-3; a Fields sheet placed as a block runs at its own.\n useRhythmCheck(stackRef, \"Stack\", 40, (el) =>\n el.hasAttribute(\"data-basic-page-fields\") ? 20 : 12,\n );\n // The render in which `aria-busy` goes from true to false is an arrival:\n // what mounts in it reveals. Compared against the previous render, so a\n // page that never loads never arrives, and a later remount does not.\n const busy = props[\"aria-busy\"];\n const isBusy = busy === true || busy === \"true\";\n const wasBusy = useRef(isBusy);\n const arriving = wasBusy.current && !isBusy;\n useEffect(() => {\n wasBusy.current = isBusy;\n });\n return (\n <motion.div\n ref={(node: HTMLDivElement | null) => {\n stackRef.current = node;\n if (typeof forwardedRef === \"function\") forwardedRef(node);\n else if (forwardedRef != null) forwardedRef.current = node;\n }}\n data-basic-page-stack=\"\"\n initial={reduceMotion ? false : { opacity: 0, y: REVEAL_RISE_PX }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ y: ENTRANCE_SPRING, opacity: ENTRANCE_FADE }}\n className={cn(\n \"mt-7 flex min-w-0 flex-col gap-10\",\n // Under the page title, what the first block leads with decides the\n // seat. Bare surface first (callout, table, card) takes the full\n // block rhythm — title mb-3 + 28 = 40px, the same 40 as `gap-10`\n // between blocks. A first block leading with its own heading\n // (SectionTitle / Header) reads text-under-text and keeps the\n // tighter 36px. A callout stays a surface even when a heading sits\n // directly inside it.\n \"[&:has(>*:first-child:not([data-basic-page-callout])>[data-basic-page-header]:first-child)]:mt-6 [&:has(>*:first-child:not([data-basic-page-callout])>h2:first-child)]:mt-6 [&:has(>[data-basic-page-header]:first-child)]:mt-6 [&:has(>h2:first-child)]:mt-6\",\n // Each block lays out its parts at gap-3; a Fields sheet placed as a\n // block keeps its own field rhythm.\n \"*:flex *:min-w-0 *:flex-col [&>*:not([data-basic-page-fields])]:gap-3\",\n className,\n )}\n {...props}\n >\n <ArrivalContext.Provider value={arriving}>\n {children}\n </ArrivalContext.Provider>\n </motion.div>\n );\n}\n\n/**\n * Title + optional description pair. No gap between them — the block's\n * `gap-3` only applies between this header and the card.\n */\nexport interface BasicPageHeaderProps extends Omit<\n ComponentProps<\"div\">,\n RevealMotionConflicts\n> {\n /**\n * Rise this header into place on mount — the Header flavor of `Title`'s\n * `enter`, for a subpage title that carries a description: the pair\n * floats up from below the departing morph word as one block. Put `enter`\n * here and NOT on the `Title` inside. Skipped under reduced motion.\n */\n enter?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * Title + description as one block, spaced from the card below like a\n * title is. A three-column grid so a `Search` and a `HeaderAction` can\n * stand at the row's end: text stacks in the first column, the search\n * centers against it in the second, the action in the third; unoccupied\n * end columns are empty and nothing changes.\n */\nconst BASIC_PAGE_HEADER =\n \"@container grid min-w-0 grid-cols-[minmax(0,1fr)_auto_auto] mb-3 [&>:not([data-basic-page-search]):not([data-basic-page-header-action])]:col-start-1 [[data-basic-page-callout]_&]:mb-0 [[data-basic-page-stack]>*>&]:mb-0\";\n\nfunction Header({ enter, className, ...props }: BasicPageHeaderProps) {\n const reduceMotion = useReducedMotion();\n if (enter && !reduceMotion) {\n return (\n <motion.div\n initial={{ opacity: 0, y: REVEAL_RISE_PX }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ y: ENTRANCE_SPRING, opacity: ENTRANCE_FADE }}\n data-basic-page-header=\"\"\n className={cn(BASIC_PAGE_HEADER, className)}\n {...props}\n />\n );\n }\n return (\n <div\n data-basic-page-header=\"\"\n className={cn(BASIC_PAGE_HEADER, className)}\n {...props}\n />\n );\n}\n\n/**\n * An action at the header row's end — a small button (or two) seated in\n * the `Header` grid the way `Search` is: top-right of the block, its top\n * edge on the title row, flush with the card edge below. Put it inside\n * `Header` after the text; with a `Search` present it stands to the\n * search's right.\n */\nfunction HeaderAction({\n className,\n ...props\n}: ComponentProps<\"div\"> & { ref?: Ref<HTMLDivElement> }) {\n return (\n <div\n data-basic-page-header-action=\"\"\n className={cn(\n \"flex items-center gap-2\",\n // Its seat in a `Header`'s grid — inert anywhere else. No\n // min-w-0: the title column takes the squeeze and truncates.\n \"[[data-basic-page-header]>&]:col-start-3 [[data-basic-page-header]>&]:row-span-2 [[data-basic-page-header]>&]:row-start-1 [[data-basic-page-header]>&]:ml-4 [[data-basic-page-header]>&]:self-start [[data-basic-page-header]>&]:justify-self-end\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Section heading — same padding as the page title, smaller type. Hashable via\n * slug `id` (not a link).\n */\nfunction SectionTitle({\n id,\n className,\n children,\n ...props\n}: ComponentProps<\"h2\"> & { ref?: Ref<HTMLHeadingElement> }) {\n const headingId = resolveHeadingId(id, children);\n return (\n <h2\n id={headingId}\n className={cn(\n \"text-display-xs text-pho-primary max-w-full min-w-0 scroll-mt-12 truncate px-5 font-medium\",\n // The gap to the card is the title's own, so a wrapper between the\n // Stack and the block (a form, a Reveal) never loses it. As a Stack\n // block's direct child the block's gap-3 already spaces it; inside\n // a Header the pair's own gap does.\n \"mb-3 [[data-basic-page-callout]_&]:mb-0 [[data-basic-page-header]>&]:mb-0 [[data-basic-page-stack]>*>&]:mb-0\",\n className,\n )}\n {...props}\n >\n {children}\n </h2>\n );\n}\n\n/**\n * Supporting copy under a section title. Same display step as the title;\n * color + normal weight keep it secondary. Always wrap with `Header`.\n */\nfunction Description({\n className,\n ...props\n}: ComponentProps<\"p\"> & { ref?: Ref<HTMLParagraphElement> }) {\n return (\n <p\n className={cn(\n \"text-display-xs text-pho-description px-5 font-normal\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * How many rows the searchable table is showing — a `Description` that\n * reads the table's search. Idle: \"12 people.\" A query: \"3 of 12.\"\n * Renders nothing until a table with `search` is on the page. Put it in\n * the `Header` under the section title.\n */\nfunction TableCount({\n one,\n other,\n ...props\n}: ComponentProps<\"p\"> & {\n /** Singular noun — \"1 person.\" */\n one: string;\n /** Plural noun — \"12 people.\" */\n other: string;\n ref?: Ref<HTMLParagraphElement>;\n}) {\n const store = useContext(TableSearchContext);\n const api = useSyncExternalStore(\n store?.subscribe ?? subscribeToNothing,\n () => store?.get().api ?? null,\n () => null,\n );\n if (api == null) return null;\n const text =\n api.query.trim() !== \"\"\n ? `${api.matched} of ${api.total}.`\n : `${api.total} ${api.total === 1 ? one : other}.`;\n return <Description {...props}>{text}</Description>;\n}\n\n/**\n * Optional caption below a section's `Card` (outside the card). One step\n * smaller than `Description` (`text-xs`). Not the card-internal dirty-state\n * `Footer`.\n */\nfunction SectionFooter({\n className,\n ...props\n}: ComponentProps<\"p\"> & { ref?: Ref<HTMLParagraphElement> }) {\n return (\n <p\n className={cn(\n \"text-pho-description px-5 text-xs font-normal\",\n \"mt-3 [[data-basic-page-callout]_&]:mt-0 [[data-basic-page-stack]>*>&]:mt-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface BasicPageCardProps extends ComponentProps<\"div\"> {\n /**\n * Glide the card's height when its content swaps — bones becoming a\n * different number of rows, a row created or revoked, an inline state\n * change. The height follows the content on the page spring instead of\n * jumping. Reserve it for cards whose contents change while mounted.\n */\n animateHeight?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/** Bordered card that stacks rows with hairline dividers. */\nfunction Card({\n animateHeight,\n className,\n children,\n ...props\n}: BasicPageCardProps) {\n // Departing rows stay until their exit has played. `toArray` keys static\n // children by their position, so a conditional row that goes away is\n // seen as itself leaving, not as the last row; list rows bring their own\n // keys (by data id, never by index). Presence never blocks an entrance:\n // rows decide that themselves (only an arrival plays one), so bones and\n // the rows replacing them may even share a position.\n const rows = <AnimatePresence>{Children.toArray(children)}</AnimatePresence>;\n if (animateHeight) {\n return (\n <div\n className={cn(\n BASIC_PAGE_CARD_SURFACE,\n \"min-h-0 overflow-hidden\",\n className,\n )}\n {...props}\n >\n <AnimatedHeight\n transition={ENTRANCE_SPRING}\n className={BASIC_PAGE_CARD_DIVIDERS}\n >\n {rows}\n </AnimatedHeight>\n </div>\n );\n }\n return (\n <div className={cn(BASIC_PAGE_CARD, className)} {...props}>\n {rows}\n </div>\n );\n}\n\n/**\n * One card row that can host subviews. Padding matches a plain row, so use it\n * for any row that may grow an `ItemPanel` — primary content goes straight in\n * as children; the panel, when present, is the last child.\n *\n * ```tsx\n * <BasicPage.Card>\n * <BasicPage.Item>\n * …name, meta, actions…\n * <BasicPage.ItemPanel divided>…secret / expanded content…</BasicPage.ItemPanel>\n * </BasicPage.Item>\n * …later siblings (pass `divided` on the panel above)…\n * </BasicPage.Card>\n * ```\n */\nfunction Item({\n className,\n ...props\n}: Omit<ComponentProps<\"div\">, RevealMotionConflicts> & {\n ref?: Ref<HTMLDivElement>;\n}) {\n const { leaving, ...rowMotion } = useRowMotion();\n return (\n <motion.div\n {...rowMotion}\n className={cn(BASIC_PAGE_ITEM, leaving && \"overflow-hidden\", className)}\n {...props}\n />\n );\n}\n\nexport interface BasicPageItemPanelProps extends ComponentProps<\"div\"> {\n /**\n * Set when later card rows follow this item. The panel then renders as an\n * inset rounded chip (keeping the card's own divider) instead of the\n * full-bleed bottom sheet, which only reads well against the card's corner.\n * @default false\n */\n divided?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * Secondary strip at the end of an `Item`. When the item closes the card,\n * it's a gray sheet flush under the body, full-bleed and owning the row's\n * bottom padding, rounding only into the card's bottom corner. Pass `divided`\n * when later card rows follow — the panel becomes an inset rounded chip and\n * the card's hairline divider stays in charge. Content is already a centered\n * flex row either way.\n */\nfunction ItemPanel({\n divided = false,\n className,\n ...props\n}: BasicPageItemPanelProps) {\n return (\n <div\n className={cn(\n divided ? BASIC_PAGE_ITEM_PANEL_DIVIDED : BASIC_PAGE_ITEM_PANEL,\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Standalone callout. Pair with `CalloutTitle` / `CalloutDescription` (and\n * optional actions) for a featured block — e.g. an invite form. Do not wrap\n * in `Card`; this is already the outer surface.\n *\n * Pass `icon` for a decorative glyph anchored to the header block (first\n * child) — typically a Tabler outline icon. The icon is aria-hidden; keep\n * meaning in the title/description.\n */\nfunction Callout({\n className,\n icon,\n children,\n ...props\n}: ComponentProps<\"div\"> & {\n ref?: Ref<HTMLDivElement>;\n /** Decorative icon, top-right of the header (first child). */\n icon?: ReactNode;\n}) {\n // Treat boolean false (and other empty values) as absent — `icon={cond &&\n // <Icon />}` is a common pattern and must not reserve header padding.\n if (icon == null || icon === false || icon === true) {\n return (\n <div\n data-basic-page-callout=\"\"\n className={cn(BASIC_PAGE_CALLOUT, className)}\n {...props}\n >\n {children}\n </div>\n );\n }\n\n const [header, ...rest] = Children.toArray(children);\n\n return (\n <div\n data-basic-page-callout=\"\"\n className={cn(BASIC_PAGE_CALLOUT, className)}\n {...props}\n >\n <div className=\"relative\">\n <div className=\"min-w-0 pr-16\">{header}</div>\n <div\n aria-hidden\n // Always the top-right corner, its top on the first line's top —\n // not centered, so it holds still as the callout grows.\n className=\"text-pho-primary pointer-events-none absolute top-0 right-0 size-14 opacity-[0.275] [&>svg]:size-full\"\n >\n {icon}\n </div>\n </div>\n {rest}\n </div>\n );\n}\n\n/** Callout heading — same type as `SectionTitle`. Hashable via slug `id`. */\nfunction CalloutTitle({\n id,\n className,\n children,\n ...props\n}: ComponentProps<\"h2\"> & { ref?: Ref<HTMLHeadingElement> }) {\n const headingId = resolveHeadingId(id, children);\n return (\n <h2\n id={headingId}\n className={cn(\n \"text-display-xs text-pho-primary max-w-full min-w-0 scroll-mt-12 truncate font-medium\",\n className,\n )}\n {...props}\n >\n {children}\n </h2>\n );\n}\n\n/** Supporting copy under a callout title. */\nfunction CalloutDescription({\n className,\n ...props\n}: ComponentProps<\"p\"> & { ref?: Ref<HTMLParagraphElement> }) {\n return (\n <p\n className={cn(\n \"text-display-xs text-pho-description font-normal\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface BasicPageRowProps extends Omit<\n ComponentProps<\"div\">,\n \"title\" | RevealMotionConflicts\n> {\n /** Left-side field name — plain text, or compose with a trailing hint. */\n label: ReactNode;\n /** Optional helper under the label. */\n description?: ReactNode;\n /** Associates the label with a control id. When omitted, the label is not a `<label>`. */\n htmlFor?: string;\n /**\n * Stack the label above a full-width control instead of the default\n * label-left / control-right layout at `sm+`. Keeps the compact `gap-3`\n * rhythm — the default row's `justify-between` would otherwise stretch\n * label and control apart under `min-h-[4.25rem]`.\n */\n stacked?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * One row: label (+ optional description) left, control right.\n * Pass `htmlFor` when the row wraps a labelled input; omit it for display/\n * action rows (avatar, danger-zone delete). Pass `stacked` for full-width\n * controls that sit under the label.\n */\nfunction Row({\n label,\n description,\n htmlFor,\n stacked = false,\n className,\n children,\n ...props\n}: BasicPageRowProps) {\n const { leaving, ...rowMotion } = useRowMotion();\n return (\n <motion.div\n {...rowMotion}\n className={cn(\n BASIC_PAGE_ROW,\n stacked && \"sm:flex-col sm:items-stretch sm:justify-start sm:gap-3\",\n leaving && \"overflow-hidden\",\n className,\n )}\n {...props}\n >\n <div className=\"min-w-0\">\n {htmlFor ? (\n <label\n htmlFor={htmlFor}\n className=\"text-pho-primary block text-base font-normal\"\n >\n {label}\n </label>\n ) : (\n // A div, not a p: the label slot takes composed content (an\n // identity with its avatar, a loading bone) as well as a name.\n <div className=\"text-pho-primary text-base font-normal\">{label}</div>\n )}\n {description ? (\n <p className=\"text-pho-description text-xs\">{description}</p>\n ) : null}\n </div>\n <div\n className={cn(\n \"flex w-full min-w-0 items-center gap-2 sm:w-auto\",\n stacked && \"sm:w-full\",\n )}\n >\n {children}\n </div>\n </motion.div>\n );\n}\n\n/** Read-only value on the right side of a row (email, etc.). */\nfunction Value({\n className,\n ...props\n}: ComponentProps<\"p\"> & { ref?: Ref<HTMLParagraphElement> }) {\n return (\n <p\n className={cn(\n \"text-pho-description max-w-full min-w-0 truncate text-base\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface BasicPageAlertProps extends Omit<\n ComponentProps<\"div\">,\n \"children\"\n> {\n /** The caught value — a problem body, an `Error` wrapping one, or unknown. */\n error?: unknown;\n /** Override the derived message. */\n children?: ReactNode;\n /**\n * Explicit presence override — defaults to whether there is anything to\n * show, so the reveal plays as the mutation's error comes and goes.\n */\n show?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * Server error under a row, still inside the card — the partial failure\n * (\"Couldn't update your name\"), rendered by `Error` from the problem body.\n * Keep it mounted and pass the mutation's error: the line reveals and\n * collapses on `Footer`'s height spring + fade. The card drops its divider\n * above the line so it reads as part of the row it belongs to. Matches card\n * horizontal padding — don't re-apply `px-5`.\n *\n * For a page that failed to load outright, swap the stack for an `ErrorView`\n * instead.\n */\nfunction Alert({\n error: errorProp,\n show,\n className,\n children,\n ...props\n}: BasicPageAlertProps) {\n const reduceMotion = useReducedMotion();\n // Inside `Form`, with no `error` of its own, the alert shows the form's.\n const form = useContext(FormContext);\n const error = errorProp ?? form?.error ?? undefined;\n const hasContent = error != null || children != null;\n // Explicit `show` cannot force an empty animated slot — ErrorLine returns\n // null when there is nothing to render.\n const visible = (show ?? true) && hasContent;\n return (\n <AnimatePresence initial={false}>\n {visible ? (\n <motion.div\n data-basic-page-alert=\"\"\n className=\"overflow-hidden\"\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: \"auto\", opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={\n reduceMotion\n ? { duration: 0 }\n : { height: ENTRANCE_SPRING, opacity: ENTRANCE_FADE }\n }\n >\n <ErrorLine\n error={error}\n className={cn(\"px-5 pb-3\", className)}\n {...props}\n >\n {children}\n </ErrorLine>\n </motion.div>\n ) : null}\n </AnimatePresence>\n );\n}\n\n/* ------------------------------- forms ---------------------------------- */\n\ninterface FormContextValue {\n pending: boolean;\n dirty: boolean;\n error: unknown;\n /** Auto-save lifecycle: which field asked, and where the save stands. */\n auto: { field: string | null; phase: \"saving\" | \"saved\" } | null;\n /** Schedules an auto-save (the same typing-pause debounce); null unless `autoSave`. */\n requestSave: ((field?: string) => void) | null;\n}\n\n/**\n * The nearest `Form autoSave`'s trigger, for controls whose changes never\n * bubble as native events (a Base UI checkbox, a select): call it in the\n * control's own onChange and the save runs after the usual pause. Null\n * outside an auto-saving form.\n */\nexport function useAutoSave() {\n return useContext(FormContext)?.requestSave ?? null;\n}\n\n/** Typing pause before an auto-save fires; blur flushes it immediately. */\nexport const AUTO_SAVE_DELAY_MS = 800;\n\n/**\n * The toast lane auto-save failures speak through. Host it once in the app\n * shell — `<Toast.Provider toastManager={formToasts}>` around a viewport —\n * and every `Form autoSave` reaches it without wiring. A failure toast\n * carries Retry and stays until the save lands (the next success closes it).\n */\nexport const formToasts = Toast.createToastManager();\n\nconst FormContext = createContext<FormContextValue | null>(null);\n\n/** Field errors keyed by `name`, the shape Base UI `Form` maps onto fields. */\nexport type BasicPageFormErrors = Record<string, string | string[]>;\n\nexport interface BasicPageFormProps<\n Values extends Record<string, unknown> = Record<string, unknown>,\n> extends Omit<\n ComponentProps<typeof FormElement>,\n // autoSave shadows a legacy DOM string attribute of the same name.\n \"onFormSubmit\" | \"errors\" | \"action\" | \"autoSave\"\n> {\n /**\n * Documents save themselves; transactions submit. With `autoSave`, the\n * form runs `action` on its own — after a typing pause, immediately on\n * blur — once native validation passes, and skips quietly while it does\n * not. Controls stay enabled throughout; put a `Saved` in the field's\n * hint for the inline Saving…/Saved word. A failure keeps the value in\n * place and raises a Retry toast on `formToasts` that stays until a\n * save lands. Keep the explicit `Submit` for creations and anything\n * paid or irreversible.\n */\n autoSave?: boolean;\n /**\n * The save. Receives the field values keyed by `name`, after native\n * validation passed. Resolve to finish; return `{ errors }` to put\n * messages on fields; throw (a `Problem`, an `Error`) for the form's\n * `Alert`. While it runs, every control and the submit are disabled.\n */\n action?: (\n values: Values,\n ) =>\n | void\n | { errors?: BasicPageFormErrors }\n | Promise<void | { errors?: BasicPageFormErrors }>;\n /** Field errors to start with — from a previous round trip. */\n errors?: BasicPageFormErrors;\n}\n\n/**\n * The form. A `<form>` that owns the save lifecycle so the page parts can\n * read it: `Submit` shows pending, `Footer show=\"dirty\"` appears after the\n * first edit and leaves when the action lands, `Alert` shows what the\n * action threw, and field errors the action returns land under their\n * fields. Uncontrolled by design — give fields `defaultValue` and a `name`,\n * and remount with `key` when the record changes; there is no state to\n * seed. Lay the fields out in `Fields` (the form card, its `Footer` inside\n * at the bottom), or keep a `Card` of `Row`s for a settings page that\n * saves, its `Footer` following the card one block gap below.\n *\n * The form adds no spacing. Inside a `Stack`, one form around a card and\n * its footer is one block; a form that spans several blocks goes around\n * the `Stack` instead, and the blocks keep the Stack's rhythm.\n */\nfunction Form<Values extends Record<string, unknown>>({\n action,\n errors: initialErrors,\n autoSave = false,\n className,\n children,\n onChange,\n onBlur,\n ...props\n}: BasicPageFormProps<Values>) {\n const [pending, setPending] = useState(false);\n const [dirty, setDirty] = useState(false);\n const [error, setError] = useState<unknown>(null);\n const [errors, setErrors] = useState<BasicPageFormErrors>(\n initialErrors ?? {},\n );\n const [auto, setAuto] = useState<FormContextValue[\"auto\"]>(null);\n const formRef = useRef<HTMLFormElement | null>(null);\n const autoTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const lastField = useRef<string | null>(null);\n const inFlight = useRef(false);\n const queued = useRef(false);\n const failToast = useRef<string | null>(null);\n const latestRun = useRef<() => Promise<void>>(async () => {});\n useEffect(\n () => () => {\n if (autoTimer.current != null) clearTimeout(autoTimer.current);\n },\n [],\n );\n\n const runAutoSave = async () => {\n const form = formRef.current;\n if (form == null || action == null) return;\n if (inFlight.current) {\n queued.current = true;\n return;\n }\n // Quiet while a field is still invalid — the next pause tries again.\n if (!form.checkValidity()) return;\n const values = Object.fromEntries(new FormData(form).entries());\n const field = lastField.current;\n inFlight.current = true;\n setAuto({ field, phase: \"saving\" });\n setError(null);\n try {\n const result = await action(values as Values);\n const fieldErrors = result?.errors;\n if (fieldErrors != null && Object.keys(fieldErrors).length > 0) {\n setErrors(fieldErrors);\n setAuto(null);\n return;\n }\n setErrors({});\n setDirty(false);\n setAuto({ field, phase: \"saved\" });\n if (failToast.current != null) {\n formToasts.close(failToast.current);\n failToast.current = null;\n }\n } catch (caught) {\n setAuto(null);\n setError(caught);\n // The value stays in the field; the toast stays until a save lands.\n if (failToast.current != null) formToasts.close(failToast.current);\n failToast.current = formToasts.add({\n type: \"error\",\n title: \"Couldn't save\",\n description:\n caught instanceof Error && caught.message !== \"\"\n ? caught.message\n : \"The change is still here — try again.\",\n timeout: 0,\n actionProps: {\n children: \"Retry\",\n onClick: () => {\n void latestRun.current();\n },\n },\n });\n } finally {\n inFlight.current = false;\n if (queued.current) {\n queued.current = false;\n void latestRun.current();\n }\n }\n };\n latestRun.current = runAutoSave;\n\n const scheduleAutoSave = () => {\n if (autoTimer.current != null) clearTimeout(autoTimer.current);\n autoTimer.current = setTimeout(() => {\n autoTimer.current = null;\n void latestRun.current();\n }, AUTO_SAVE_DELAY_MS);\n };\n\n const requestSave = useMemo(\n () =>\n autoSave\n ? (field?: string) => {\n if (field != null) lastField.current = field;\n setDirty(true);\n scheduleAutoSave();\n }\n : null,\n // eslint-disable-next-line react-hooks/exhaustive-deps -- schedule fn is stable per instance\n [autoSave],\n );\n const context = useMemo(\n () => ({ pending, dirty, error, auto, requestSave }),\n [pending, dirty, error, auto, requestSave],\n );\n return (\n <FormContext.Provider value={context}>\n <FormElement\n errors={errors}\n onFormSubmit={async (values) => {\n if (action == null) return;\n setPending(true);\n setError(null);\n try {\n const result = await action(values as Values);\n const fieldErrors = result?.errors;\n if (fieldErrors != null && Object.keys(fieldErrors).length > 0) {\n setErrors(fieldErrors);\n return;\n }\n setErrors({});\n setDirty(false);\n } catch (caught) {\n setError(caught);\n } finally {\n setPending(false);\n }\n }}\n onChange={(event) => {\n setDirty(true);\n setError(null);\n // A field edited after a round trip drops the message it came back\n // with; the next submit validates it fresh.\n const name = (event.target as { name?: string }).name;\n if (name && name in errors) {\n setErrors((current) => {\n const { [name]: _dropped, ...rest } = current;\n return rest;\n });\n }\n if (autoSave) {\n if (name) lastField.current = name;\n scheduleAutoSave();\n }\n onChange?.(event);\n }}\n onBlur={(event) => {\n // Leaving a field flushes the pending pause — the edit lands\n // before attention moves on.\n if (autoSave && dirty && autoTimer.current != null) {\n clearTimeout(autoTimer.current);\n autoTimer.current = null;\n void latestRun.current();\n }\n onBlur?.(event);\n }}\n // No spacing of its own. Transparent to layout, so whatever the form\n // wraps keeps the rhythm of where it sits: several blocks inside a\n // Stack stay Stack blocks, a card and its footer inside a block stay\n // that block's children. As a Stack's direct child it is one block\n // itself (a card and its footer), spaced by the Stack's block rule.\n className={cn(\n \"contents [[data-basic-page-stack]>&]:flex [[data-basic-page-stack]>&]:min-w-0 [[data-basic-page-stack]>&]:flex-col\",\n className,\n )}\n {...props}\n ref={(node: HTMLFormElement | null) => {\n formRef.current = node;\n const forwarded = (props as { ref?: Ref<HTMLFormElement> }).ref;\n if (typeof forwarded === \"function\") forwarded(node);\n else if (forwarded != null) forwarded.current = node;\n }}\n >\n <fieldset\n disabled={pending}\n aria-busy={pending || undefined}\n className=\"contents\"\n >\n {children}\n </fieldset>\n </FormElement>\n </FormContext.Provider>\n );\n}\n\n/**\n * The inline word for an auto-saving field — put it inside the field at\n * its trailing edge (`Input trailing`), where the eye already is while\n * typing. Quiet gray, never colored, so it cannot be read as validation:\n * \"Saving…\" while the save runs, \"Saved\" for a breath after it lands,\n * then nothing at all — idle it renders nothing and reserves nothing.\n * Words fade through the system's 1px soft focus — no travel — and\n * carry their own surface wash, so long\n * input text fades beneath them instead of colliding. With `name` it\n * speaks only for that field; without, for the whole form.\n */\nfunction Saved({\n name,\n className,\n ...props\n}: Omit<ComponentProps<\"output\">, RevealMotionConflicts> & { name?: string }) {\n const form = useContext(FormContext);\n const reduceMotion = useReducedMotion();\n const auto = form?.auto ?? null;\n const mine = auto != null && (name == null || auto.field === name);\n const [linger, setLinger] = useState(false);\n const phase = mine ? auto.phase : null;\n useEffect(() => {\n if (phase !== \"saved\") return;\n setLinger(true);\n const timer = setTimeout(() => setLinger(false), 1800);\n return () => clearTimeout(timer);\n }, [phase, auto]);\n const word =\n phase === \"saving\"\n ? \"Saving…\"\n : phase === \"saved\" && linger\n ? \"Saved\"\n : null;\n const blurAway = reduceMotion ? \"blur(0px)\" : `blur(${BLUR_MAX_PX}px)`;\n return (\n // Both words share one grid cell, right-aligned: the goer fades out\n // exactly where the comer fades in, overlapping by stacking — never\n // by Motion absolutely positioning the goer (which misplaces against\n // a right-anchored container).\n <span className=\"grid justify-items-end\">\n <AnimatePresence initial={false}>\n {word != null && (\n <motion.output\n key={word}\n initial={{ opacity: 0, filter: blurAway }}\n animate={{ opacity: 1, filter: \"blur(0px)\" }}\n exit={{ opacity: 0, filter: blurAway }}\n transition={CONDENSE_BAR_ENTER}\n className={cn(\n \"text-pho-description pl-8 [grid-area:1/1]\",\n className,\n )}\n // The word's own wash: the field surface ramping in from the\n // left — the scroll edge's fade turned sideways.\n style={{\n background:\n \"linear-gradient(to left, var(--background-color-pho-primary) calc(100% - 2rem), transparent)\",\n }}\n {...props}\n >\n {word}\n </motion.output>\n )}\n </AnimatePresence>\n </span>\n );\n}\n\n/** The card's field padding, matched to `Row` (`px-5 py-4`). */\nconst BASIC_PAGE_FIELDS =\n \"flex min-w-0 flex-col gap-5 p-5 [&>[data-basic-page-alert]]:-mx-5 [&>[data-basic-page-alert]]:-mt-2 [&>[data-basic-page-alert]]:-mb-5 [&>[data-basic-page-footer]]:pr-0 [&>[data-basic-page-reveal]]:flex [&>[data-basic-page-reveal]]:flex-col [&>[data-basic-page-reveal]]:gap-5\";\n\n/**\n * The form card: labelled fields stacked, label above control, one rhythm\n * (`gap-5`). Where a `Card` of `Row`s is a table of settings, this is a\n * sheet to fill in — a create page, a long edit. `Input` brings its own\n * label and hint; wrap other controls in `Field`. `Alert` and `Footer`\n * sit inside, at the bottom — unlike a `Card` of rows, a sheet keeps its\n * submit: the button's box lines up with the fields' edge.\n */\nfunction Fields({\n className,\n ...props\n}: ComponentProps<\"div\"> & { ref?: Ref<HTMLDivElement> }) {\n const fieldsRef = useRef<HTMLDivElement | null>(null);\n // Only the sheet's own rhythm: a field's internal gap is the field's.\n useRhythmCheck(fieldsRef, \"Fields\", 20, () => null);\n return (\n <div\n data-basic-page-fields=\"\"\n className={cn(BASIC_PAGE_CARD_SURFACE, BASIC_PAGE_FIELDS, className)}\n {...props}\n ref={(node: HTMLDivElement | null) => {\n fieldsRef.current = node;\n const forwarded = props.ref;\n if (typeof forwarded === \"function\") forwarded(node);\n else if (forwarded != null) forwarded.current = node;\n }}\n />\n );\n}\n\n/** Fields side by side from `sm` (a first and last name), stacked below. */\nfunction Inline({\n className,\n ...props\n}: ComponentProps<\"div\"> & { ref?: Ref<HTMLDivElement> }) {\n return (\n <div\n className={cn(\n \"flex min-w-0 flex-col gap-5 *:min-w-0 sm:flex-row sm:gap-3 sm:*:flex-1\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** The submit. A small effect `Button`, pending while the action runs. */\nfunction Submit({\n children = \"Save changes\",\n ...props\n}: Omit<ButtonProps, \"type\">) {\n const form = useContext(FormContext);\n return (\n <Button\n type=\"submit\"\n variant=\"effect\"\n size=\"sm\"\n loading={form?.pending}\n {...props}\n >\n {children}\n </Button>\n );\n}\n\nexport interface BasicPageFooterProps extends ComponentProps<\"div\"> {\n /**\n * Animated presence for the dirty-state action: the card glides\n * open/closed on a height spring while the button fades. Omit for a\n * static, always-visible footer (no animation). No reveal plays on first\n * mount — only on flips.\n */\n show?: boolean | \"dirty\";\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * Save action for a card. After a `Card` of rows it sits outside, below the\n * surface, its trailing edge on the rows' text edge, so the card\n * holds only rows; inside `Fields` it sits at the bottom of the sheet.\n * Typically a submit button that appears after the form is dirty; pass\n * `show` (instead of conditional rendering) to animate it in and out\n * following the onboarding card's motion language.\n */\nfunction Footer({ show: showProp, className, ...props }: BasicPageFooterProps) {\n // `show=\"dirty\"`: inside `Form`, present once something was edited, gone\n // again once the action lands.\n const form = useContext(FormContext);\n const show = showProp === \"dirty\" ? (form?.dirty ?? false) : showProp;\n const bar = (\n <div\n data-basic-page-footer=\"\"\n // The button's trailing edge lines up with the card's text, not its\n // border: the card's 1px border and `px-5` put the text edge 21px\n // in, and the effect button's 1.5px rim is a box-shadow outside its\n // box — so the box sits 22.5px in and the rim's outer edge meets the\n // text edge.\n className={cn(\n \"flex items-center justify-end gap-3 pr-[calc(1.25rem+2.5px)]\",\n className,\n )}\n {...props}\n />\n );\n if (show === undefined) return bar;\n // A block below the card: the reveal compensates the block gap above it,\n // so the card's bottom edge does not move while the footer is away.\n // Never clipped: the button shows whole from the first frame and fades\n // in as the space below the card opens.\n return (\n <Reveal show={show} gap=\"block\" clip={false}>\n {bar}\n </Reveal>\n );\n}\n\n/**\n * DOM handlers whose React signatures clash with Motion's gesture props of\n * the same name on `motion.*` elements — excluded from `Reveal`'s public API\n * (same policy as Button).\n */\ntype RevealMotionConflicts =\n | \"onAnimationStart\"\n | \"onAnimationEnd\"\n | \"onAnimationIteration\"\n | \"onDrag\"\n | \"onDragStart\"\n | \"onDragEnd\"\n | \"onDragEnter\"\n | \"onDragExit\"\n | \"onDragLeave\"\n | \"onDragOver\"\n | \"onDrop\";\n\nexport interface BasicPageRevealProps extends Omit<\n ComponentProps<\"div\">,\n RevealMotionConflicts\n> {\n /**\n * Animated presence for a `Stack` block that comes and goes: keep the\n * block mounted and drive it with `show` — it glides open and closed\n * (height + fade) on the page spring, with the Stack's `gap-10`\n * compensated so nothing jumps when it fully collapses. Omit `show` for\n * the one-shot data-arrival entrance.\n */\n show?: boolean;\n /**\n * Clip the block while its height animates (the default). Pass `false`\n * for a block whose content must show whole from the first frame — a\n * button with a rim, a shadow — and let it fade in as the space opens.\n */\n clip?: boolean;\n /**\n * Which gap the presence variant compensates while collapsing —\n * `\"stack\"` for a Stack block (`gap-10`), `\"fields\"` for a field in a\n * `Fields` sheet (`gap-5`), `\"block\"` for an element\n * inside a block (`gap-3`). @default \"stack\"\n */\n gap?: \"stack\" | \"block\" | \"fields\";\n /**\n * Set when this wraps multiple `Card` rows — the extra element Reveal\n * needs for `AnimatePresence` sits between them and the card's own\n * `divide-y`, so the hairlines between the wrapped rows disappear unless\n * this restores them. Leave off for content that isn't a run of rows.\n */\n dividers?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * Exactly one surrounding flex gap — the presence variant trades this much\n * margin against its height so neighbours glide to their final positions\n * instead of snapping by a gap when the block unmounts. `dividers` rows sit\n * in a `divide-y` card with no flex `gap` at all, so they get no\n * compensation — `gap` would otherwise overshoot into the next row's padding\n * as the block collapses.\n */\nconst GAP_COMPENSATION = {\n stack: \"-2.5rem\",\n block: \"-0.75rem\",\n fields: \"-1.25rem\",\n none: \"0px\",\n} as const;\n\n/**\n * Data-arrival entrance for regions whose shape the skeleton couldn't\n * predict — a list of unknown length, a region that depends on the data.\n * Wrap that region (keeping `Title` and 1:1 chrome outside): once the shape\n * is known it rises 12px and fades in on the Card-shell / Command spring,\n * the same physics as the `Footer` / `Alert` reveals. When the skeleton\n * mirrors the loaded layout element for element, skip the reveal — values\n * fill into a shape that never moves. The page *entrance* is separate and\n * already built into `Stack`: whatever the page first shows (including the\n * skeleton) rides that. Static under reduced motion.\n *\n * With `show`, Reveal becomes an animated-presence block instead (the\n * `Footer` grammar): a whole section of the Stack that appears or leaves at\n * runtime glides open/closed while its neighbours follow.\n */\nfunction Reveal({\n show,\n clip = true,\n gap = \"stack\",\n dividers,\n className,\n ...props\n}: BasicPageRevealProps) {\n const reduceMotion = useReducedMotion();\n // Card rows carry their own padding and a divide-y border, not a flex\n // gap — dividers always means zero gap to compensate for.\n const gapCompensation = GAP_COMPENSATION[dividers ? \"none\" : gap];\n const nodeRef = useRef<HTMLDivElement | null>(null);\n // The first render with `show` plays no animation (AnimatePresence\n // `initial={false}`); any later mount does.\n const animatesOnMount = useRef(show !== true);\n useEffect(() => {\n animatesOnMount.current = true;\n }, []);\n const clipOn = () => {\n if (clip && nodeRef.current) nodeRef.current.style.overflow = \"hidden\";\n };\n const clipOff = () => {\n if (nodeRef.current) nodeRef.current.style.overflow = \"\";\n };\n\n if (show === undefined) {\n return (\n <motion.div\n initial={reduceMotion ? false : { opacity: 0, y: REVEAL_RISE_PX }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ y: ENTRANCE_SPRING, opacity: ENTRANCE_FADE }}\n className={cn(dividers && BASIC_PAGE_CARD_DIVIDERS, className)}\n {...props}\n />\n );\n }\n\n // Clip only while the height animates. At rest the block is unclipped, so\n // a button's rim, a focus ring, or a shadow at its edge shows whole. The\n // toggle goes through the DOM: on exit AnimatePresence keeps a frozen\n // clone whose props no longer update, but its handlers still reach the\n // node. A block mounting with the animation (a later `show`) starts\n // clipped; the first render with `show` plays no animation and stays open.\n return (\n <AnimatePresence initial={false}>\n {show ? (\n <motion.div\n data-basic-page-reveal=\"\"\n ref={(node: HTMLDivElement | null) => {\n nodeRef.current = node;\n if (clip && node && animatesOnMount.current) {\n node.style.overflow = \"hidden\";\n }\n }}\n onAnimationStart={clipOn}\n onAnimationComplete={clipOff}\n initial={{\n height: 0,\n opacity: 0,\n marginTop: gapCompensation,\n }}\n animate={{ height: \"auto\", opacity: 1, marginTop: 0 }}\n exit={{ height: 0, opacity: 0, marginTop: gapCompensation }}\n transition={\n reduceMotion\n ? { duration: 0 }\n : {\n height: ENTRANCE_SPRING,\n marginTop: ENTRANCE_SPRING,\n opacity: ENTRANCE_FADE,\n }\n }\n className={cn(dividers && BASIC_PAGE_CARD_DIVIDERS, className)}\n {...props}\n />\n ) : null}\n </AnimatePresence>\n );\n}\n\n/* ------------------------------- navigation ------------------------------ */\n\n/**\n * Where the trail's chevron sits, in `Root` coordinates, for the ghost a\n * root `Title` fades out on a pop: the nav's `top-4 left-3` plus the\n * button's 8px icon-side padding, centred in the 32px control; the glyph\n * is 1.125em of the `sm` button's 14px type. Kept next to the trail's own\n * offset.\n */\nconst BACK_CHEVRON_GHOST_POSITION =\n \"top-4 left-5 h-8 text-base [&>svg]:size-[1.125em] [&>svg]:shrink-0\";\n\n/** Tailwind's `sm` — below it the trail collapses to its last crumb. */\nconst TRAIL_WIDE_QUERY = \"(min-width: 40rem)\";\n\nfunction isNarrowViewport() {\n return (\n typeof window !== \"undefined\" &&\n typeof window.matchMedia === \"function\" &&\n !window.matchMedia(TRAIL_WIDE_QUERY).matches\n );\n}\n\ninterface TrailItemContextValue {\n index: number;\n count: number;\n /** The trail on its way out at this render, if any. */\n departing: { labels: ReactNode[] } | null;\n /** Below `sm` — only the last crumb shows, with the chevron; decided at mount. */\n narrow: boolean;\n}\n\nconst TrailItemContext = createContext<TrailItemContextValue | null>(null);\n\nexport interface BasicPageBreadcrumbsProps extends ComponentProps<\"nav\"> {\n /** `Crumb`s, root-most first; the last one is the immediate parent. */\n children: ReactNode;\n}\n\n/**\n * The trail above the title — where this page sits, root-most crumb first,\n * ending with the immediate parent; the current page is the `Title` below.\n * Place inside `Root`, before `Title`. It floats in the page's top-left\n * corner instead of taking layout space, and the column reserves a 64px\n * top zone for it, so `Title` sits at the same position whether or not a\n * page has one.\n *\n * One form at every depth: `‹ parent / parent / parent` — the chevron opens\n * the trail (it is the way up), slashes separate the crumbs (they are the\n * path), one crumb or many. Below `sm` the path collapses to `‹ parent` —\n * the last crumb alone. Crumbs render through the host's `LinkProvider`.\n *\n * Four crumbs or more, the middle folds: `‹ root / … / parent / parent`,\n * with the folded pages behind the ellipsis as a menu. The trail keeps a\n * width ceiling at any depth, and the fold never touches the tail, so the\n * title morph is unaffected.\n *\n * It sits in the page's top-left corner — `top-4 left-3`, the chevron ink\n * 20px from the edge, the column gutter's width — as chrome of the pane,\n * not of the column: on a wide pane it stays at the corner while the\n * column centers. `top-4` centers the 32px buttons in the 64px reserved\n * zone; the ghost hover pills hang in the corner padding.\n *\n * Motion between pages: the tail crumb is the morph partner of the child\n * page's `Title` (pair their `morphId`s); everything before it holds still.\n * On a push the trail grows by one — a separator emerges from under the\n * arriving word; on a pop it loses its tail — the word grows back into the\n * title and the spare separator fades. Collapsed, it behaves as `Back`.\n */\nfunction Breadcrumbs({\n className,\n children,\n ...props\n}: BasicPageBreadcrumbsProps) {\n const items = Children.toArray(children).filter(isValidElement);\n const labels = items.map(\n (item) => (item.props as { children?: ReactNode }).children ?? null,\n );\n const departing = useDepartingTrail();\n useTrailPresence(labels);\n const [narrow] = useState(isNarrowViewport);\n const reduceMotion = useReducedMotion();\n const count = items.length;\n const Link = useLinkComponent();\n // Wide trails fold: first crumb, an ellipsis holding the middle, the last\n // two. The trail keeps a width ceiling at any depth, and the morph always\n // involves the tail, which the fold never touches. Collapsed (below sm)\n // the whole path is already one crumb — nothing to fold.\n const folded = !narrow && count >= TRAIL_FOLD_AT;\n // Under `Root condense`, the bar borrows the tail crumb as its way back.\n const condense = useContext(CondenseContext);\n const tailProps =\n count > 0 ? (items[count - 1]?.props as BasicPageCrumbProps) : null;\n const tailLabel = tailProps?.children ?? \"Back\";\n const tailHref = tailProps?.href;\n const tailClick = tailProps?.onClick;\n useLayoutEffect(() => {\n if (condense == null || tailHref == null) return;\n condense.setTail({ label: tailLabel, href: tailHref, onClick: tailClick });\n return () => condense.setTail(null);\n }, [condense, tailLabel, tailHref, tailClick]);\n // A pop, wide: the trail lost its tail — the separator that stood before\n // it has nothing to precede any more and fades where it was.\n const [spareSeparator, setSpareSeparator] = useState(\n !reduceMotion &&\n !narrow &&\n departing != null &&\n departing.labels.length > count,\n );\n return (\n <nav\n aria-label=\"Breadcrumb\"\n data-basic-page-back=\"\"\n className={cn(\n \"absolute top-4 left-3 flex min-w-max items-center\",\n className,\n )}\n {...props}\n >\n <ol className=\"m-0 flex list-none items-center p-0\">\n {items.map((item, index) => {\n if (folded && index > 0 && index < count - 2) return null;\n return (\n <Fragment key={item.key ?? index}>\n {folded && index === count - 2 && (\n <li className=\"m-0 flex items-center max-sm:hidden\">\n <span aria-hidden className={TRAIL_SEPARATOR}>\n {TRAIL_SEPARATOR_GLYPH}\n </span>\n <Menu.Root>\n <Menu.Trigger\n render={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Hidden pages\"\n className=\"px-2\"\n >\n {TRAIL_FOLD_GLYPH}\n </Button>\n }\n />\n <Menu.Portal>\n <Menu.Positioner align=\"start\" sideOffset={4}>\n <Menu.Popup>\n {items.slice(1, count - 2).map((hidden, i) => {\n const crumb = hidden.props as BasicPageCrumbProps;\n return (\n <Menu.LinkItem\n key={hidden.key ?? i}\n render={\n <Link\n href={crumb.href}\n onClick={crumb.onClick}\n />\n }\n >\n {crumb.children ?? \"Back\"}\n </Menu.LinkItem>\n );\n })}\n </Menu.Popup>\n </Menu.Positioner>\n </Menu.Portal>\n </Menu.Root>\n </li>\n )}\n <li\n className={cn(\n \"m-0 flex items-center\",\n index < count - 1 && \"max-sm:hidden\",\n )}\n >\n <TrailItemContext.Provider\n value={{ index, count, departing, narrow }}\n >\n {item}\n </TrailItemContext.Provider>\n </li>\n </Fragment>\n );\n })}\n {spareSeparator && (\n <li aria-hidden className=\"m-0 flex items-center max-sm:hidden\">\n <motion.span\n initial={{ opacity: 1 }}\n animate={{ opacity: 0 }}\n transition={BACK_CHEVRON_LEAVE}\n onAnimationComplete={() => setSpareSeparator(false)}\n className={TRAIL_SEPARATOR}\n >\n {TRAIL_SEPARATOR_GLYPH}\n </motion.span>\n </li>\n )}\n </ol>\n </nav>\n );\n}\n\n/**\n * `/` between crumbs — description ink, tucked into the buttons' padding.\n * A slash, not a chevron: the trail opens with `‹`, and a `›` after it read\n * as two directions at once. The `‹` is the way up; the slashes are the\n * path.\n */\nconst TRAIL_SEPARATOR =\n \"text-pho-description -mx-1 inline-flex shrink-0 text-base select-none\";\nconst TRAIL_SEPARATOR_GLYPH = \"/\";\n\n/**\n * Wide trails fold at this many crumbs: the first stays (it opens the\n * trail), the middle collapses into an ellipsis menu, the last two stay\n * (where you are, and the step above). Four 160px crumbs and their slashes\n * would pass the 672px content column; the fold caps the trail's width at\n * any depth.\n */\nconst TRAIL_FOLD_AT = 4;\nconst TRAIL_FOLD_GLYPH = \"…\";\n\nexport interface BasicPageCrumbProps extends Omit<\n ButtonLinkProps,\n \"variant\" | \"size\" | \"shape\" | \"prefix\" | \"suffix\" | \"svgOnly\" | \"href\"\n> {\n /** Destination — rendered through the host's `LinkProvider`. */\n href: string;\n /** Label — name the page. @default \"Back\" */\n children?: ReactNode;\n /**\n * Shared-element key. Pair with the same `morphId` on that page's `Title`\n * and the word morphs between title and crumb across the navigation.\n * Skipped under reduced motion.\n */\n morphId?: string;\n}\n\n/**\n * One step of the trail — a small ghost `ButtonLink`. Only meaningful\n * inside `Breadcrumbs`, which decides what shows at which width and which\n * crumb is the tail; `Back` is the one-crumb shorthand.\n */\nfunction Crumb({\n href,\n className,\n morphId,\n children = \"Back\",\n ...props\n}: BasicPageCrumbProps) {\n const trail = useContext(TrailItemContext);\n const reduceMotion = useReducedMotion();\n const morphing = morphId != null && !reduceMotion;\n const flight = useMorphFlight(morphing ? morphId : undefined);\n if (trail == null) {\n throw new Error(\n \"BasicPage.Crumb must be rendered inside BasicPage.Breadcrumbs\",\n );\n }\n const { index, count, departing, narrow } = trail;\n const first = index === 0;\n const last = index === count - 1;\n const grew = departing != null && departing.labels.length < count;\n const shrank = departing != null && departing.labels.length > count;\n // Is this crumb new on screen? Wide: only a tail the trail just grew by\n // (or any tail with no trail before it — a cold mount, the first push).\n // Collapsed: the tail is always the one visible crumb, so a change of\n // trail always changes what shows.\n const newHere = last && (departing == null || grew || narrow);\n // Wide, the chevron opens the trail (first crumb); collapsed, it leads\n // the tail (the only crumb). Both, when they are the same crumb.\n const chevron = first || last;\n const chevronClass =\n first && last ? \"\" : first ? \"max-sm:hidden\" : \"sm:hidden\";\n // Between subpages the chevron is already there — it holds still. Only\n // the first push (no trail on the way out) brings it in with the word.\n const chevronHolds = departing != null;\n // How the label shows up when nothing flies in. Collapsed and a pop: out\n // from under the chevron; a crumb that was already there: no entrance.\n const arrival: MorphArrival | undefined = !last\n ? undefined\n : narrow\n ? shrank\n ? \"slide\"\n : \"fade\"\n : newHere\n ? \"fade\"\n : undefined;\n // Collapsed and a push: the old tail label leaves as the new one arrives\n // in the same slot. Wide, the old tail simply stays as the crumb before.\n const [leavingLabel, setLeavingLabel] = useState<ReactNode>(\n morphing && narrow && flight.fly && departing != null && !shrank\n ? departing.labels[departing.labels.length - 1]\n : null,\n );\n const animateChevron = morphing && chevron && !chevronHolds;\n const animateSeparator =\n morphing && !first && last && (departing == null || grew);\n\n return (\n <>\n {!first && (\n <span aria-hidden className={cn(TRAIL_SEPARATOR, \"max-sm:hidden\")}>\n {animateSeparator ? (\n <motion.span\n initial={\n flight.fly\n ? { opacity: 0, x: MORPH_CHEVRON_RISE_X }\n : { opacity: 0 }\n }\n animate={flight.fly ? { opacity: 1, x: 0 } : { opacity: 1 }}\n transition={flight.fly ? MORPH_CHEVRON_ENTER : ENTRANCE_FADE}\n className=\"inline-flex\"\n >\n {TRAIL_SEPARATOR_GLYPH}\n </motion.span>\n ) : (\n TRAIL_SEPARATOR_GLYPH\n )}\n </span>\n )}\n <ButtonLink\n variant=\"ghost\"\n size=\"sm\"\n href={href}\n prefix={\n chevron ? (\n animateChevron ? (\n // Mirrors the slot's direct-child svg sizing — the fade\n // wrapper sits between the slot and the icon.\n <motion.span\n initial={\n flight.fly\n ? { opacity: 0, x: MORPH_CHEVRON_RISE_X }\n : { opacity: 0 }\n }\n animate={flight.fly ? { opacity: 1, x: 0 } : { opacity: 1 }}\n transition={flight.fly ? MORPH_CHEVRON_ENTER : ENTRANCE_FADE}\n className={cn(\n \"inline-flex [&>svg]:size-[1.125em] [&>svg]:shrink-0\",\n chevronClass,\n )}\n >\n <IconChevronLeft />\n </motion.span>\n ) : (\n <span\n className={cn(\n \"inline-flex [&>svg]:size-[1.125em] [&>svg]:shrink-0\",\n chevronClass,\n )}\n >\n <IconChevronLeft />\n </span>\n )\n ) : (\n // An empty slot keeps every crumb on the icon-side padding, so a\n // crumb measures the same whether or not it is showing a chevron\n // — a label must not shift by 2px when it changes role.\n <span className=\"hidden\" />\n )\n }\n className={cn(\"min-w-max\", className)}\n {...props}\n >\n {morphing && morphId != null ? (\n // The word's box: unclipped while a flight is possible, clipped at\n // its left edge while the label slides out from under the chevron.\n // The departing label sits on top as its own layer so the arriving\n // word is never affected by it. A block, not an inline-block: hidden\n // overflow on an inline-block moves its baseline to the bottom edge\n // and would grow the button by the descender. Crumbs before the tail\n // ellipsize; the tail stays whole so its flight is never clipped.\n <span\n className={cn(\n \"relative block\",\n !flight.fly && arrival === \"slide\" && \"overflow-hidden\",\n !last && \"max-w-40 truncate\",\n )}\n >\n <MorphLabel\n morphId={morphId}\n fromClassName=\"text-pho-primary\"\n fly={flight.fly}\n morphRef={flight.ref}\n arrival={arrival}\n >\n {children}\n </MorphLabel>\n {leavingLabel != null && (\n <motion.span\n aria-hidden\n initial={{ x: 0, opacity: 1 }}\n animate={{ x: BACK_LABEL_LEAVE_X, opacity: 0 }}\n transition={BACK_LABEL_LEAVE}\n onAnimationComplete={() => setLeavingLabel(null)}\n className=\"pointer-events-none absolute inset-y-0 left-0 whitespace-nowrap\"\n >\n {leavingLabel}\n </motion.span>\n )}\n </span>\n ) : (\n <span className={cn(\"block\", !last && \"max-w-40 truncate\")}>\n {children}\n </span>\n )}\n </ButtonLink>\n </>\n );\n}\n\nexport type BasicPageBackProps = BasicPageCrumbProps;\n\n/**\n * Back link for a subpage one level down — the one-crumb `Breadcrumbs`:\n * `‹ parent`, floating in `Root`'s reserved top padding, morphing with the\n * parent's `Title` when they share a `morphId`. Deeper pages list the whole\n * path with `Breadcrumbs` + `Crumb` instead.\n */\nfunction Back({ className, ...props }: BasicPageBackProps) {\n return (\n <Breadcrumbs className={className}>\n <Crumb {...props} />\n </Breadcrumbs>\n );\n}\n\nexport interface BasicPageRowLinkProps extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\" | \"title\" | RevealMotionConflicts\n> {\n /** Left-side field name — plain text, or compose with a trailing badge. */\n label: ReactNode;\n /** Optional helper under the label. */\n description?: ReactNode;\n /** Optional media left of the label (e.g. project avatar). */\n leading?: ReactNode;\n /** Subpage destination — rendered through the host's `LinkProvider`. */\n href: string;\n /** Optional trailing value before the chevron (compose with `Value`). */\n children?: ReactNode;\n ref?: Ref<HTMLAnchorElement>;\n}\n\n/**\n * A row that navigates — the whole row is one link into a subpage, with a\n * trailing chevron cue. Always horizontal: a column stack would park the\n * chevron under the label on narrow viewports. Hover lifts the row, and\n * first/last rows follow the card's corner radius.\n */\nfunction RowLink({\n label,\n description,\n leading,\n href,\n className,\n children,\n ...props\n}: BasicPageRowLinkProps) {\n const Link = useLinkComponent();\n const MotionLink = useMemo(() => motion.create(Link), [Link]);\n const { leaving, ...rowMotion } = useRowMotion();\n return (\n <MotionLink\n href={href}\n {...rowMotion}\n {...props}\n className={cn(\n // Same padding / min-height as `BASIC_PAGE_ROW`, but never `flex-col`\n // — navigation rows keep label left and chevron right at every width.\n \"flex min-h-[4.25rem] min-w-0 flex-row items-center justify-between gap-6 px-5 py-4\",\n \"group outline-pho-brand hover:bg-pho-primary-hover cursor-pointer transition-colors focus-visible:outline-2 focus-visible:-outline-offset-2\",\n // The row whose page is open beside the list keeps the wash.\n \"aria-[current=page]:bg-pho-primary-hover\",\n // Hover wash follows the card's rounding (minus the 1px border).\n \"first:rounded-t-[calc(var(--radius-base)-1px)] last:rounded-b-[calc(var(--radius-base)-1px)]\",\n leaving && \"overflow-hidden\",\n className,\n )}\n >\n <div\n className={cn(\n \"flex min-w-0 flex-1 items-center\",\n leading != null && \"gap-3\",\n )}\n >\n {leading}\n <div className=\"min-w-0\">\n <div className=\"flex min-w-0 items-center gap-1\">\n {typeof label === \"string\" || typeof label === \"number\" ? (\n <p className=\"text-pho-primary truncate text-base font-medium\">\n {label}\n </p>\n ) : (\n label\n )}\n </div>\n {description ? (\n <div className=\"text-pho-description truncate text-xs\">\n {description}\n </div>\n ) : null}\n </div>\n </div>\n <div className=\"flex min-w-0 items-center justify-end gap-2\">\n {children != null ? <div className=\"min-w-0\">{children}</div> : null}\n <IconChevronRight\n aria-hidden\n className=\"text-pho-secondary group-hover:text-pho-secondary-hover size-4 shrink-0 transition-colors\"\n />\n </div>\n </MotionLink>\n );\n}\n\n/* -------------------------------- assembly ------------------------------- */\n\n/**\n * Basic page layout. One stack, one gap rhythm — only title scale differs:\n *\n * ```tsx\n * <BasicPage.Root>\n * <BasicPage.Title>Profile</BasicPage.Title>\n * <BasicPage.Stack>\n * <form>\n * <BasicPage.Card>\n * <BasicPage.Row label=\"Name\" htmlFor=\"first-name\">\n * <Input id=\"first-name\" size=\"sm\" />\n * </BasicPage.Row>\n * </BasicPage.Card>\n * </form>\n * <form>\n * <BasicPage.Header>\n * <BasicPage.SectionTitle>Agent profile</BasicPage.SectionTitle>\n * <BasicPage.Description>…</BasicPage.Description>\n * </BasicPage.Header>\n * <BasicPage.Card>…</BasicPage.Card>\n * </form>\n * <div>\n * <BasicPage.SectionTitle>Danger zone</BasicPage.SectionTitle>\n * <BasicPage.Card>\n * <BasicPage.Row label=\"Delete account\" description=\"…\">\n * <Button color=\"destructive\" size=\"sm\">Delete</Button>\n * </BasicPage.Row>\n * </BasicPage.Card>\n * </div>\n * <BasicPage.Callout icon={<IconMail stroke={1.25} />}>\n * <BasicPage.Header>\n * <BasicPage.CalloutTitle>Invitations</BasicPage.CalloutTitle>\n * <BasicPage.CalloutDescription>…</BasicPage.CalloutDescription>\n * </BasicPage.Header>\n * …invite form…\n * </BasicPage.Callout>\n * </BasicPage.Stack>\n * </BasicPage.Root>\n * ```\n *\n * Subpage — `Back` above the title, `RowLink` rows to drill in:\n *\n * ```tsx\n * <BasicPage.Root>\n * <BasicPage.Back href=\"/settings\">Settings</BasicPage.Back>\n * <BasicPage.Title>Billing</BasicPage.Title>\n * <BasicPage.Stack>\n * <div>\n * <BasicPage.Card>\n * <BasicPage.RowLink label=\"Invoices\" href=\"/settings/invoices\">\n * <BasicPage.Value>12 documents</BasicPage.Value>\n * </BasicPage.RowLink>\n * </BasicPage.Card>\n * </div>\n * </BasicPage.Stack>\n * </BasicPage.Root>\n * ```\n */\nexport interface BasicPageAsideProps {\n /**\n * Whether a page is in it. Defaults to whether there are children — hand\n * it the router's outlet element (`useOutlet()` in React Router), `null`\n * while no subpage route matches, and the aside opens and closes with\n * the URL.\n */\n open?: boolean;\n /**\n * How the panel meets the page on a wide pane. `overlay`, the default,\n * floats over the page's right edge: it takes no ground, surfaces when\n * a subpage opens and leaves with it, and the page beneath stays live.\n * `inline` docks it: the panel's ground is reserved from the first\n * paint, the page beside it never reflows as subpages open and close,\n * and `placeholder` wears the empty panel.\n */\n variant?: \"overlay\" | \"inline\";\n /**\n * For an overlay: dim the page behind and keep interaction in the panel.\n * Focus stays inside, the backdrop is a way out, Escape still closes.\n * Off by default, so the page stays live under the panel and opening\n * another row swaps the panel's page.\n */\n modal?: boolean;\n /**\n * The empty inline panel's face — a quiet line that says what opens\n * here. Optional; without it the reserved panel stands empty. An\n * overlay has no empty face: closed, it is not there.\n */\n placeholder?: ReactNode;\n /**\n * The way out for a page that carries no `Back` — the close in the\n * panel's corner calls it. A page with a `Back` closes through that link.\n */\n onClose?: () => void;\n children?: ReactNode;\n}\n\n/**\n * A subpage beside the page. On a pane wide enough for two columns (48rem,\n * Tailwind's `md`) it is a panel at the right edge. By default the panel\n * floats: it takes no ground, surfaces over the page's edge when a\n * subpage opens and leaves with it, and the page beneath stays live\n * unless `modal` dims it. `variant=\"inline\"` docks it: the panel holds\n * its ground from the first paint, the page beside it never reflows as\n * subpages open and close — only the panel's face changes, `placeholder`\n * while empty, the subpage while one is open. Either way the subpage\n * scrolls on its own, its `Back` is the close in the corner, Escape\n * closes too. On a narrower pane the same subpage is the page — the list\n * steps aside, the subpage takes the pane with its own `Back` — so one\n * subpage component serves both. Works under either `Root` width: the\n * page keeps its own column cap beside the panel, and leftover space is\n * margin.\n */\nfunction Aside({\n open,\n variant = \"overlay\",\n modal = false,\n placeholder,\n onClose,\n children,\n}: BasicPageAsideProps) {\n const host = useContext(AsideHostContext);\n // `Children.toArray` drops `null` / `false` / `true`, so `{ok && <Page/>}`\n // does not count as open the way `children != null` would (`false != null`).\n const isOpen = open ?? Children.toArray(children).length > 0;\n const setOpen = host?.setOpen;\n const setPresent = host?.setPresent;\n useLayoutEffect(() => {\n if (setPresent == null) return;\n setPresent(true);\n return () => setPresent(false);\n }, [setPresent]);\n useLayoutEffect(() => {\n if (setOpen == null) return;\n setOpen(isOpen);\n return () => setOpen(false);\n }, [setOpen, isOpen]);\n useEffect(() => {\n if (\n host == null &&\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\"\n ) {\n console.warn(\"BasicPage.Aside: needs a `BasicPage.Root` around it.\");\n }\n }, [host]);\n if (host == null || host.slot == null) return null;\n if (host.presentation === \"page\") {\n return createPortal(\n isOpen ? (\n <div\n data-basic-page-aside=\"page\"\n className=\"flex min-w-0 flex-1 flex-col\"\n >\n {children}\n </div>\n ) : null,\n host.slot,\n );\n }\n if (variant === \"inline\") {\n return createPortal(\n <AsideInline\n width={host.width}\n paneHeight={host.paneHeight}\n onClose={onClose}\n open={isOpen}\n placeholder={placeholder}\n >\n {children}\n </AsideInline>,\n host.slot,\n );\n }\n return (\n <AsideOverlay\n container={host.slot}\n width={host.width}\n paneHeight={host.paneHeight}\n onClose={onClose}\n open={isOpen}\n modal={modal}\n >\n {children}\n </AsideOverlay>\n );\n}\n\n/**\n * The way out of a panel: the subpage's `Back`, handed up through\n * `AsidePanelContext`, or the host's `onClose`. Whoever closes the panel\n * from outside (Escape, a backdrop) clicks it, so a route-driven aside\n * leaves through the router like any other navigation. It is mounted\n * only while a subpage is open; an empty panel leaves the close request\n * to whoever else wants it.\n */\nfunction useAsideWayOut() {\n const [back, setBack] = useState<CondenseTail | null>(null);\n const panel = useMemo<AsidePanelController>(() => ({ setBack }), []);\n const closeRef = useRef<HTMLSpanElement | null>(null);\n const leave = useCallback(() => {\n const way = closeRef.current?.querySelector<HTMLElement>(\"a, button\");\n if (way == null) return false;\n way.click();\n return true;\n }, []);\n return { back, panel, closeRef, leave };\n}\n\nconst ASIDE_CLOSE = {\n variant: \"ghost\",\n size: \"sm\",\n svgOnly: true,\n \"aria-label\": \"Close\",\n children: <IconX />,\n} as const;\n\n/**\n * A panel's subpage face: the close in the corner (the subpage's `Back`,\n * or `onClose`) over the page in its own scroll area.\n */\nfunction AsideSubpage({\n back,\n onClose,\n closeRef,\n panel,\n children,\n}: {\n back: CondenseTail | null;\n onClose?: () => void;\n closeRef: RefObject<HTMLSpanElement | null>;\n panel: AsidePanelController;\n children?: ReactNode;\n}) {\n return (\n <>\n <span ref={closeRef} className=\"absolute top-2 right-2 z-30\">\n {back != null ? (\n <ButtonLink\n {...ASIDE_CLOSE}\n href={back.href}\n onClick={back.onClick}\n />\n ) : onClose != null ? (\n <Button {...ASIDE_CLOSE} onClick={onClose} />\n ) : null}\n </span>\n <ScrollArea.Root className=\"min-h-0 flex-1\">\n <ScrollArea.Viewport>\n <ScrollArea.Content className=\"flex min-h-full flex-col\">\n <AsidePanelContext.Provider value={panel}>\n {children}\n </AsidePanelContext.Provider>\n </ScrollArea.Content>\n </ScrollArea.Viewport>\n <ScrollArea.Scrollbar>\n <ScrollArea.Thumb />\n </ScrollArea.Scrollbar>\n </ScrollArea.Root>\n </>\n );\n}\n\n/**\n * The docked panel — reserved ground, never a layout event. The cell\n * keeps the panel's width whether or not a subpage is open, so the page\n * beside it never reflows: a form row stretching wide and snapping back\n * on every open is exactly the disturbance this buys out. The sheet is\n * sticky at the pane's top and as tall as the pane; its two faces — the\n * placeholder and the subpage — crossfade in one grid cell (fade and a\n * breath of blur), and the page inside scrolls in its own scroll area.\n */\nfunction AsideInline({\n width,\n paneHeight,\n onClose,\n open,\n placeholder,\n children,\n}: {\n width: number;\n paneHeight: number | null;\n onClose?: () => void;\n open: boolean;\n placeholder?: ReactNode;\n children?: ReactNode;\n}) {\n const reduceMotion = useReducedMotion();\n const { back, panel, closeRef, leave } = useAsideWayOut();\n const blurAway = reduceMotion ? \"blur(0px)\" : `blur(${BLUR_MAX_PX}px)`;\n const height = paneHeight ?? \"100dvh\";\n // Escape is the close even when focus is still on the list (a RowLink\n // with aria-current), not only when it is inside the panel.\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\" || event.defaultPrevented) return;\n if (leave()) event.preventDefault();\n };\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [leave]);\n const face = {\n initial: { opacity: 0, filter: blurAway },\n animate: { opacity: 1, filter: \"blur(0px)\" },\n exit: { opacity: 0, filter: blurAway },\n transition: reduceMotion ? { duration: 0 } : CONDENSE_BAR_ENTER,\n } as const;\n return (\n <aside\n data-basic-page-aside=\"panel\"\n data-basic-page-aside-variant=\"inline\"\n className=\"relative flex shrink-0 flex-col\"\n style={{ width, minHeight: height }}\n >\n {/* Both faces share one grid cell — the swap measures nothing. Part\n of the layout, not a floating layer: a hairline edge, no\n elevation shadow. */}\n <div\n className=\"border-pho-secondary bg-pho-primary sticky top-0 grid border-l *:min-h-0 *:[grid-area:1/1]\"\n style={{ height }}\n >\n <AnimatePresence initial={false}>\n {open ? (\n <motion.div key=\"page\" {...face} className=\"relative flex flex-col\">\n <AsideSubpage\n back={back}\n onClose={onClose}\n closeRef={closeRef}\n panel={panel}\n >\n {children}\n </AsideSubpage>\n </motion.div>\n ) : (\n <motion.div key=\"empty\" {...face} className=\"flex flex-col\">\n {placeholder}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n </aside>\n );\n}\n\n/**\n * The floating panel — a layer over the page's right edge, not a cell in\n * it. It takes no ground: the page keeps its full width, the sheet\n * surfaces from the edge when a subpage opens and leaves with it, and\n * under it the page stays live unless `modal` says otherwise. Built on\n * Base UI's Dialog for what a floating layer owes: Escape from anywhere,\n * and for a modal the focus trap, the return of focus on close, and the\n * page marked hidden behind it. Positioned in the page's own frame and\n * sticky at the pane's top, so it floats inside whatever pane hosts the\n * page, as tall as that pane.\n */\nfunction AsideOverlay({\n container,\n width,\n paneHeight,\n onClose,\n open,\n modal,\n children,\n}: {\n container: HTMLElement;\n width: number;\n paneHeight: number | null;\n onClose?: () => void;\n open: boolean;\n modal: boolean;\n children?: ReactNode;\n}) {\n const { back, panel, closeRef, leave } = useAsideWayOut();\n const height = paneHeight ?? \"100dvh\";\n return (\n <BaseDialog.Root\n open={open}\n modal={modal ? \"trap-focus\" : false}\n // The page beneath stays live: no close on an outside press, none\n // on focus leaving. A modal's backdrop closes on its own click.\n disablePointerDismissal\n onOpenChange={(next, details) => {\n if (next) return;\n // The route owns `open`. A close request means leave through the\n // way out; the panel then follows the URL.\n details.cancel();\n leave();\n }}\n >\n <BaseDialog.Portal container={container} className=\"contents\">\n {modal ? (\n <BaseDialog.Backdrop\n data-basic-page-aside-backdrop=\"\"\n className={cn(\n \"bg-pho-overlay absolute inset-0 z-30 transition-opacity duration-200 ease-out motion-reduce:transition-none\",\n \"data-[ending-style]:opacity-0 data-[starting-style]:opacity-0\",\n )}\n onClick={leave}\n />\n ) : null}\n <BaseDialog.Popup\n data-basic-page-aside=\"panel\"\n data-basic-page-aside-variant=\"overlay\"\n // A live page keeps its focus where it was; a modal takes it.\n initialFocus={modal ? undefined : false}\n finalFocus={modal ? undefined : false}\n className={cn(\n \"absolute inset-y-0 right-0 z-30 flex flex-col outline-none\",\n // Arrives the way content does: a short rise from the edge\n // with the ink, and leaves the same way.\n \"transition-[translate,opacity] duration-250 ease-out motion-reduce:transition-none\",\n \"data-[ending-style]:translate-x-3 data-[ending-style]:opacity-0 data-[ending-style]:duration-200 data-[starting-style]:translate-x-3 data-[starting-style]:opacity-0\",\n )}\n style={{ width }}\n >\n {/* A floating layer: the elevation shadow over the hairline. */}\n <div\n className=\"border-pho-secondary bg-pho-primary shadow-pho-xl sticky top-0 flex flex-col border-l\"\n style={{ height }}\n >\n <AsideSubpage\n back={back}\n onClose={onClose}\n closeRef={closeRef}\n panel={panel}\n >\n {children}\n </AsideSubpage>\n </div>\n </BaseDialog.Popup>\n </BaseDialog.Portal>\n </BaseDialog.Root>\n );\n}\n\n/** The open search box's width. */\nconst SEARCH_OPEN_W = \"14rem\";\n\n/**\n * The table's search, standing at the end of a section header — put it in\n * the `Header` beside the title and it adopts the searchable table's box\n * (the table then renders none of its own). On a header at least 36rem\n * wide the box simply stands open: the header is a container, CSS\n * decides, nothing is measured. On a narrower one a round button holds\n * the place; clicking it opens the box — the width glides linearly, the\n * page's travel — with focus landing inside. Emptied and blurred it\n * glides shut and the button returns; Escape clears, blurs, and shuts.\n * Renders nothing until a table with `search` is on the page.\n */\nfunction Search({\n variant = \"box\",\n className,\n ...props\n}: ComponentProps<\"div\"> & {\n /**\n * The face. `box` (default) is the bordered field beside a section\n * title — open on a wide header, a round button that glides it open on\n * a narrow one. `ghost` is the table-chrome face: a magnifier and a\n * bare field on the text line, no box — for a toolbar band or anywhere\n * a boxed control would be noise; it flexes instead of folding.\n */\n variant?: \"box\" | \"ghost\";\n}) {\n const store = useContext(TableSearchContext);\n const api = useSyncExternalStore(\n store?.subscribe ?? subscribeToNothing,\n () => store?.get().api ?? null,\n () => null,\n );\n useEffect(() => {\n if (store == null) return;\n store.adopt(true);\n return () => store.adopt(false);\n }, [store]);\n // `open` shows the box; `closing` keeps it while the shut glide plays,\n // then the button takes back its place.\n const [phase, setPhase] = useState<\"folded\" | \"open\" | \"closing\">(\"folded\");\n const inputRef = useRef<HTMLInputElement | null>(null);\n const openBox = () => setPhase(\"open\");\n const shutBox = () => {\n setPhase(\"closing\");\n window.setTimeout(\n () => setPhase((now) => (now === \"closing\" ? \"folded\" : now)),\n 250,\n );\n };\n useEffect(() => {\n if (phase === \"open\") inputRef.current?.focus();\n }, [phase]);\n const showBox = phase !== \"folded\";\n if (variant === \"ghost\") {\n return (\n <div\n data-basic-page-search=\"\"\n data-variant=\"ghost\"\n className={cn(\n \"flex min-w-0 flex-1\",\n api == null && \"hidden\",\n className,\n )}\n {...props}\n >\n {api != null && (\n <GhostSearch\n value={api.query}\n onChange={api.setQuery}\n placeholder={api.placeholder}\n aria-label={api.placeholder}\n onKeyDown={(event) => {\n if (event.key !== \"Escape\") return;\n // Ours — not the aside's close.\n event.preventDefault();\n api.setQuery(\"\");\n event.currentTarget.blur();\n }}\n />\n )}\n </div>\n );\n }\n return (\n <div\n data-basic-page-search=\"\"\n data-expanded={phase === \"open\" || undefined}\n className={cn(\n \"grid min-w-0 content-center\",\n // Its seat in a `Header`'s grid — inert anywhere else.\n \"[[data-basic-page-header]>&]:col-start-2 [[data-basic-page-header]>&]:row-span-2 [[data-basic-page-header]>&]:row-start-1 [[data-basic-page-header]>&]:ml-4 [[data-basic-page-header]>&]:justify-items-end [[data-basic-page-header]>&]:self-center\",\n api == null && \"hidden\",\n className,\n )}\n {...props}\n >\n {api != null && (\n <div\n className={cn(\n \"grid w-9 max-w-full justify-items-end transition-[width] duration-200 ease-linear *:[grid-area:1/1] motion-reduce:transition-none\",\n \"@[36rem]:w-56\",\n )}\n style={phase === \"open\" ? { width: SEARCH_OPEN_W } : undefined}\n >\n <Button\n variant=\"outlined\"\n size=\"sm\"\n shape=\"circle\"\n svgOnly\n aria-label={api.placeholder}\n onClick={openBox}\n className={cn(\"@[36rem]:hidden\", showBox && \"hidden\")}\n >\n <IconSearch />\n </Button>\n <div\n className={cn(\n \"w-full @[36rem]:block\",\n showBox ? \"block\" : \"hidden\",\n )}\n >\n <InputGroup.Root size=\"sm\" className=\"w-full\">\n <InputGroup.Addon>\n <IconSearch />\n </InputGroup.Addon>\n <InputGroup.Input\n ref={inputRef}\n type=\"search\"\n value={api.query}\n onChange={(event) => api.setQuery(event.target.value)}\n onBlur={() => {\n if (api.query === \"\") shutBox();\n }}\n onKeyDown={(event) => {\n if (event.key !== \"Escape\") return;\n // Ours — not the aside's close.\n event.preventDefault();\n api.setQuery(\"\");\n shutBox();\n event.currentTarget.blur();\n }}\n placeholder={api.placeholder}\n aria-label={api.placeholder}\n />\n </InputGroup.Root>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const BasicPage = {\n Root,\n Aside,\n Search,\n // The data table under the page's name — records in columns, rows that\n // open in the aside. Same component as `components/data-table`; import\n // from there where a bundle wants the table without the page.\n Table: DataTable,\n CondenseScope,\n Breadcrumbs,\n Crumb,\n Back,\n Title,\n Stack,\n Header,\n HeaderAction,\n SectionTitle,\n Description,\n TableCount,\n SectionFooter,\n Card,\n Item,\n ItemPanel,\n Callout,\n CalloutTitle,\n CalloutDescription,\n Row,\n RowLink,\n Value,\n Alert,\n Footer,\n Form,\n Saved,\n Fields,\n Inline,\n Submit,\n Reveal,\n};\n"],"x_google_ignoreList":[0],"mappings":"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"}