{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "topbar",
  "title": "Topbar",
  "description": "Topbar from the Get IT Done design system.",
  "dependencies": [
    "class-variance-authority@^0.7.0",
    "lucide-react@^0.379.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/accordion.json",
    "https://zero-design.itdone.de/r/breadcrumb.json",
    "https://zero-design.itdone.de/r/button.json",
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/popover.json",
    "https://zero-design.itdone.de/r/recipes.json",
    "https://zero-design.itdone.de/r/sheet.json",
    "https://zero-design.itdone.de/r/sidebar.json",
    "https://zero-design.itdone.de/r/status-dot.json",
    "https://zero-design.itdone.de/r/use-escape-key.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/topbar.tsx",
      "content": "'use client';\n\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { ArrowLeft, Globe, HelpCircle, Menu, Moon, Sun } from 'lucide-react';\nimport * as React from 'react';\n\nimport { focusRing, focusRingInverse, transitionBase } from '@/lib/recipes';\nimport { useEscapeKey } from '@/lib/use-escape-key';\nimport { cn } from '@/lib/utils';\nimport { BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, Breadcrumbs } from '@/components/ui/breadcrumb';\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';\nimport { Button } from '@/components/ui/button';\nimport { useLabels, usePreset } from '@/components/ui/labels-context';\nimport { SidebarSwatch } from '@/components/ui/sidebar';\nimport { StatusDot, type StatusDotTone } from '@/components/ui/status-dot';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\nimport { Sheet, SheetContent, SheetDescription, SheetNavLink, type SheetNavLinkProps, SheetTitle } from '@/components/ui/sheet';\n\n// App top bar family (Figma \"Header navigation\": Inset App / Settings / Guest, Simple, Dual-tier, Mobile).\n// Composable: TopBar is the landmark, the parts below fill its start and end slots.\n\nconst topBarVariants = cva(\n  // Theme expression (kette-tokens 5.26.0): top bar image and bevel, accent stripe as the bottom border image; none by default\n  '@container/topbar flex w-full shrink-0 items-center gap-2 border-0 border-b border-solid border-hairline bg-background-default text-text-default bg-[image:var(--image-top-bar)] shadow-[shadow:var(--bevel-top-bar)] [border-image-source:var(--image-accent-stripe)] [border-image-slice:1]',\n  {\n    variants: {\n      variant: {\n        inset: 'h-topbar px-4',\n        guest: 'h-14 px-4',\n        simple: 'h-topbar-lg px-8',\n        mobile: 'h-16 px-4',\n      },\n    },\n    defaultVariants: { variant: 'inset' },\n  },\n);\n\nexport interface TopBarProps extends React.HTMLAttributes<HTMLElement>, VariantProps<typeof topBarVariants> {}\n\nconst TopBar = React.forwardRef<HTMLElement, TopBarProps>(({ className, variant, ...props }, ref) => {\n  const zero = usePreset() === 'zero';\n  return (\n    <header\n      data-slot=\"top-bar\"\n      ref={ref}\n      data-variant={variant ?? 'inset'}\n      className={cn(topBarVariants({ variant }), zero && 'border-border-default', className)}\n      {...props}\n    />\n  );\n});\nTopBar.displayName = 'TopBar';\n\nconst TopBarStart = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => <div ref={ref} className={cn('flex min-w-0 items-center gap-2', className)} {...props} />,\n);\nTopBarStart.displayName = 'TopBarStart';\n\nconst TopBarEnd = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, children, ...props }, ref) => (\n    <div\n      ref={ref}\n      // Children marked `data-topbar-secondary` drop out below the sm breakpoint (display none, so they leave the tab order too), so a long action row does not push the bar past a phone width. An element marked `data-topbar-action` (a Button with an icon, or a NavigationMenuTrigger with an `icon`, at any depth) keeps only its icon below sm: the label stays its accessible name but is no longer drawn, and its text is the tooltip, so the primary action fits 320px next to the breadcrumb. The chevron of a trigger does not count as the icon. A status chip (`role=status`, such as `AutosaveStatus`) has a width ceiling and truncates its text with an ellipsis (full text as tooltip), so it cannot push the title. Under 448px of top bar the ceiling only fits the icon, so the label of an `AutosaveStatus` (`data-status-label`) is hidden visually instead of cut to a fragment such as \"U...\": it stays in the live region as the accessible name and the chip is its icon, a button that opens the status text. This follows the width of the top bar (a size container, under 448px), not the viewport.\n      className={cn(\n        'ml-auto flex shrink-0 items-center gap-2 [&>[role=status]]:max-w-xs [@container_topbar_(max-width:447px)]:[&>[role=status]]:max-w-[var(--size-control-lg)] max-sm:[&>[data-topbar-secondary]]:hidden',\n        'max-sm:[&_[data-topbar-action]:has(svg:not([data-chevron]))]:w-control-md max-sm:[&_[data-topbar-action]:has(svg:not([data-chevron]))]:gap-0 max-sm:[&_[data-topbar-action]:has(svg:not([data-chevron]))]:px-0 max-sm:[&_[data-topbar-action]:has(svg:not([data-chevron]))]:text-[0px]',\n        className,\n      )}\n      {...props}\n    >\n      {/* An action marked `data-topbar-action` loses its label below sm: its text becomes the tooltip, so the icon is still named on hover. */}\n      {React.Children.map(children, (child) =>\n        React.isValidElement<{ 'data-topbar-action'?: unknown; title?: string; children?: React.ReactNode }>(child) &&\n        child.props['data-topbar-action'] !== undefined &&\n        child.props.title === undefined &&\n        typeof child.props.children === 'string'\n          ? React.cloneElement(child, { title: child.props.children })\n          : child,\n      )}\n    </div>\n  ),\n);\nTopBarEnd.displayName = 'TopBarEnd';\n\n// Second row of the Dual-tier header (sub navigation, search).\nconst TopBarSubRow = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => (\n    <div\n      ref={ref}\n      className={cn(\n        'flex h-16 w-full items-center gap-2 border-0 border-b border-solid border-hairline bg-background-default px-8',\n        className,\n      )}\n      {...props}\n    />\n  ),\n);\nTopBarSubRow.displayName = 'TopBarSubRow';\n\n/* -------------------------------------------------------------------------------------------------\n * Breadcrumb (up to three levels)\n * -----------------------------------------------------------------------------------------------*/\n\nexport interface TopBarBreadcrumbItem {\n  label: React.ReactNode;\n  href?: string;\n  onClick?: React.MouseEventHandler<HTMLAnchorElement>;\n}\n\nexport interface TopBarBreadcrumbProps extends Omit<React.HTMLAttributes<HTMLElement>, 'children'> {\n  items: TopBarBreadcrumbItem[];\n  /** Leading object icon tile, e.g. `<ObjectIcon app=\"vacancy\" />`. */\n  icon?: React.ReactNode;\n  /** Muted trailing context, e.g. the selected projects. */\n  context?: React.ReactNode;\n  /** Project colours shown as small squares before the context text (user data, passed as CSS colours). */\n  contextSwatches?: string[];\n  /** Levels shown at most. Longer trails keep the first and the last items and collapse the middle. Default 3. */\n  maxLevels?: number;\n}\n\nconst ELLIPSIS = Symbol('ellipsis');\n\nconst backArrow = <ArrowLeft aria-hidden=\"true\" className=\"mr-1 inline-block h-icon-md w-icon-md align-middle sm:hidden\" />;\nconst backLinkClass = 'block min-w-0 max-w-full truncate leading-[var(--size-control-sm)]';\n\n// Thin header wrapper over Breadcrumbs (type path): adds the icon tile slot, the context slot and the level cap.\nconst TopBarBreadcrumb = React.forwardRef<HTMLElement, TopBarBreadcrumbProps>(\n  ({ className, items, icon, context, contextSwatches, maxLevels = 3, ...props }, ref) => {\n    const limit = Math.max(2, maxLevels);\n    // The ellipsis counts as a level. With only two levels there is no room for it, so the trail becomes first and last.\n    const collapsed: Array<TopBarBreadcrumbItem | typeof ELLIPSIS> =\n      limit === 2 ? [items[0], items[items.length - 1]] : [items[0], ELLIPSIS, ...items.slice(items.length - (limit - 2))];\n    const visible: Array<TopBarBreadcrumbItem | typeof ELLIPSIS> = items.length > limit ? collapsed : items;\n    // The level above the current page, for the back link below sm. A level without a target is not offered.\n    const parent = items.length > 1 && (items[items.length - 2].href || items[items.length - 2].onClick) ? items[items.length - 2] : null;\n    // The trail stays on one line. Every level truncates with an ellipsis (the full text is the tooltip), the parents give way first and the current page keeps its room. Below sm only the back link (the level above) and the current page are drawn: the other parents are display none, so they are neither visible nor focusable. The context text drops out below sm.\n    return (\n      <div className={cn('flex min-w-0 items-center gap-2', className)}>\n        {icon}\n        <Breadcrumbs ref={ref} type=\"path\" className=\"-m-1 min-w-0 overflow-hidden p-1 [&>ol]:flex-nowrap [&_li]:whitespace-nowrap\" {...props}>\n          {parent && !visible.includes(parent) && (\n            // The level above is not drawn on its own (a collapsed trail): below sm it is still offered as the way back.\n            <li data-back-link=\"\" className=\"group/crumb inline-flex min-w-[calc(var(--size-control-lg)*2)] shrink-[100000] items-center sm:hidden\">\n              <BreadcrumbLink href={parent.href} onClick={parent.onClick} title={typeof parent.label === 'string' ? parent.label : undefined} className={backLinkClass}>\n                {backArrow}\n                {parent.label}\n              </BreadcrumbLink>\n            </li>\n          )}\n          {visible.map((item, index) => {\n            const last = index === visible.length - 1;\n            // Below sm the level above the current page stays as the back link (arrow plus label); the other parents are hidden.\n            const back = item === parent;\n            return (\n              <BreadcrumbItem\n                key={item === ELLIPSIS ? 'ellipsis' : index}\n                // The middle levels give way first (weight 100000), then the root (10), then the current page (1): a weight below 1 would absorb almost nothing, so the title could never end in an ellipsis. Every level keeps a floor of a few characters.\n                className={cn('min-w-0', last ? 'shrink min-w-[var(--size-control-lg)]' : index === 0 ? 'shrink-[10]' : 'shrink-[100000]', !last && !back && 'max-sm:hidden', !last && item !== ELLIPSIS && 'min-w-[calc(var(--size-control-lg)*2)]', back && 'max-sm:[&>span:last-child]:hidden')}\n              >\n                {item === ELLIPSIS ? (\n                  <BreadcrumbEllipsis label=\"Hidden levels\" />\n                ) : (\n                  <BreadcrumbLink\n                    href={last ? undefined : item.href}\n                    onClick={item.onClick}\n                    current={last}\n                    title={typeof item.label === 'string' ? item.label : undefined}\n                    className={cn('block min-w-0 max-w-full truncate leading-[var(--size-control-sm)]', last && 'typography-ui-medium text-text-default')}\n                  >\n                    {back && backArrow}\n                    {item.label}\n                  </BreadcrumbLink>\n                )}\n              </BreadcrumbItem>\n            );\n          })}\n        </Breadcrumbs>\n        {context && (\n          <span className=\"typography-ui-sm flex min-w-0 shrink-[3] items-center gap-1 text-text-subtle max-md:hidden\">\n            {contextSwatches?.map((color) => <SidebarSwatch key={color} color={color} size=\"sm\" className=\"mx-0 shrink-0\" />)}\n            <span className=\"min-w-0 truncate\">{context}</span>\n          </span>\n        )}\n      </div>\n    );\n  },\n);\nTopBarBreadcrumb.displayName = 'TopBarBreadcrumb';\n\n/* -------------------------------------------------------------------------------------------------\n * Timer pill\n * -----------------------------------------------------------------------------------------------*/\n\nexport interface TimerPillProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {\n  /** Elapsed time, already formatted (e.g. `01:24:37`). */\n  time: string;\n  /** Shows the pulsing recording dot. Respects reduced motion (the dot stays static). Default true. */\n  running?: boolean;\n  /** Accessible name prefix. Default `Timer`. */\n  label?: string;\n  /** Optional details panel (e.g. project, note, stop button). When set, the pill opens it as a popover. */\n  details?: React.ReactNode;\n  /** Accessible name of the details panel. Defaults to the pill name. */\n  detailsLabel?: string;\n}\n\nconst TimerPill = React.forwardRef<HTMLButtonElement, TimerPillProps>(\n  ({ className, time, running = true, label: labelProp, details, detailsLabel, ...props }, ref) => {\n    const l = useLabels();\n    const label = labelProp ?? l.timer;\n    const name = l.timerState(label, running, time);\n    const zero = usePreset() === 'zero';\n    const pill = (\n    <button\n      ref={ref}\n      type=\"button\"\n      aria-label={name}\n      data-running={running || undefined}\n      className={cn(\n        'typography-ui-sm-semibold inline-flex h-control-md items-center gap-2 rounded-control border border-solid border-border-brand-subtle bg-background-brand-subtle tabular-nums text-text-default',\n        zero ? 'px-[var(--size-control-padding-x-sm)]' : 'px-2',\n        transitionBase,\n        'hover:bg-background-brand-subtle-hover',\n        focusRing,\n        className,\n      )}\n      {...props}\n    >\n      <span\n        aria-hidden\n        className={cn(\n          'h-2 w-2 shrink-0 rounded-full bg-background-destructive-bold-default',\n          running && 'motion-safe:animate-pulse',\n          !running && 'bg-icon-disabled',\n        )}\n      />\n      <span aria-hidden>{time}</span>\n    </button>\n    );\n    if (details == null) return pill;\n    return (\n      <Popover>\n        <PopoverTrigger asChild>{pill}</PopoverTrigger>\n        <PopoverContent align=\"end\" aria-label={detailsLabel ?? name}>\n          {details}\n        </PopoverContent>\n      </Popover>\n    );\n  },\n);\nTimerPill.displayName = 'TimerPill';\n\n/* -------------------------------------------------------------------------------------------------\n * Icon button + escape hint + menu button\n * -----------------------------------------------------------------------------------------------*/\n\nexport interface TopBarIconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  /** Required accessible name. */\n  label: string;\n  /** Unread or pending count shown as a small badge. */\n  count?: number;\n  // Toggle state, for example an open notification panel. Sets aria-pressed and the pressed surface.\n  pressed?: boolean;\n  // Open state of a popover or menu trigger. Sets aria-expanded and the open surface. Do not combine with `pressed`: expanded wins and no aria-pressed is set.\n  expanded?: boolean;\n  // Counts above this show as `<max>+`. Default 99.\n  maxCount?: number;\n  // Small status dot in the bottom corner (for example connection or sync state). Pair it with `statusLabel`, colour alone is not enough.\n  status?: StatusDotTone;\n  // Text for assistive tech, appended to the accessible name, e.g. `Offline`.\n  statusLabel?: string;\n}\n\nconst TopBarIconButton = React.forwardRef<HTMLButtonElement, TopBarIconButtonProps>(\n  ({ className, label, count, pressed: pressedProp, expanded, maxCount = 99, status, statusLabel, children, ...props }, ref) => {\n    // A disclosure trigger exposes aria-expanded only, never aria-pressed.\n    const pressed = expanded === undefined ? pressedProp : undefined;\n    const shown = count && count > maxCount ? `${maxCount}+` : count;\n    const zero = usePreset() === 'zero';\n    return (\n    <button\n      ref={ref}\n      type=\"button\"\n      aria-label={[label, count ? shown : undefined, status ? statusLabel : undefined].filter(Boolean).join(', ')}\n      data-status={status}\n      aria-pressed={pressed}\n      aria-expanded={expanded}\n      data-pressed={pressed || undefined}\n      data-expanded={expanded || undefined}\n      className={cn(\n        'relative inline-flex h-control-md w-control-md shrink-0 items-center justify-center rounded-control [&>svg]:h-icon-sm [&>svg]:w-icon-sm',\n        zero ? 'text-icon-default' : 'text-icon-subtle',\n        transitionBase,\n        'hover:bg-muted hover:text-icon-default active:bg-muted-strong',\n        'data-[pressed]:bg-muted data-[pressed]:text-icon-default',\n        'data-[expanded]:bg-muted data-[expanded]:text-icon-default',\n        'disabled:pointer-events-none disabled:opacity-disabled',\n        focusRing,\n        className,\n      )}\n      {...props}\n    >\n      {children}\n      {count ? (\n        <span\n          aria-hidden\n          className={cn(\n            'absolute right-0 top-0 grid h-4 min-w-icon-sm place-items-center rounded-full bg-background-brand-bold-default text-text-on-brand',\n            zero ? 'typography-avatar-xs px-micro' : 'typography-text-extra-small px-1',\n          )}\n        >\n          {shown}\n        </span>\n      ) : null}\n      {status ? <StatusDot tone={status} size=\"md\" className=\"absolute bottom-0 right-0 ring-2 ring-background\" /> : null}\n    </button>\n    );\n  },\n);\nTopBarIconButton.displayName = 'TopBarIconButton';\n\nexport interface TopBarHelpButtonProps extends Omit<TopBarIconButtonProps, 'label' | 'children'> {\n  // Accessible name. Defaults to the provider text, then English `Help`.\n  label?: string;\n}\n\n// Help icon button (Figma Header navigation Mode=Settings, Help button). Alternative to the close button in settings mode.\nconst TopBarHelpButton = React.forwardRef<HTMLButtonElement, TopBarHelpButtonProps>(({ label: labelProp, ...props }, ref) => {\n  const l = useLabels();\n  return (\n    <TopBarIconButton ref={ref} label={labelProp ?? l.help} {...props}>\n      <HelpCircle aria-hidden />\n    </TopBarIconButton>\n  );\n});\nTopBarHelpButton.displayName = 'TopBarHelpButton';\n\nexport interface TopBarEscapeHintProps extends React.HTMLAttributes<HTMLSpanElement> {\n  /** Called when Escape is pressed. Omit to render the hint text only. */\n  onEscape?: () => void;\n}\n\nconst TopBarEscapeHint = React.forwardRef<HTMLSpanElement, TopBarEscapeHintProps>(\n  ({ className, onEscape, children: childrenProp, ...props }, ref) => {\n    const l = useLabels();\n    const children = childrenProp ?? l.escClosesSettings;\n    useEscapeKey(onEscape);\n    return (\n      // A keyboard hint is no use on a phone: it also took the room of the page title below sm.\n      <span ref={ref} className={cn('typography-ui-sm text-text-subtle max-sm:hidden', className)} {...props}>\n        {children}\n      </span>\n    );\n  },\n);\nTopBarEscapeHint.displayName = 'TopBarEscapeHint';\n\n// Mobile menu trigger: pair with a Sheet or any disclosure; exposes the disclosure state.\nexport interface TopBarMenuButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  open?: boolean;\n  // `inverse` is for a bar on an inverse surface (dark band in light mode, light band in dark mode): icon, hover and focus ring follow the band. `inverse-fixed` needs no change, its dark scope already resolves the default roles.\n  tone?: 'default' | 'inverse' | 'inverse-fixed';\n}\n\nconst TopBarMenuButton = React.forwardRef<HTMLButtonElement, TopBarMenuButtonProps>(\n  ({ className, open = false, tone = 'default', 'aria-label': ariaLabelProp, children, ...props }, ref) => {\n    const l = useLabels();\n    const ariaLabel = ariaLabelProp ?? l.menu;\n    return (\n    <button\n      ref={ref}\n      type=\"button\"\n      aria-label={ariaLabel}\n      aria-expanded={open}\n      className={cn(\n        'inline-flex shrink-0 items-center justify-center rounded-control',\n        // Exactly 40x40 in every theme and preset: a fixed scale size, not a control token (control-md is 32px without preset zero).\n        'h-10 w-10',\n        transitionBase,\n        tone === 'inverse'\n          ? ['text-text-inverse hover:bg-text-inverse/10 active:bg-text-inverse/20', focusRingInverse]\n          : ['text-icon-default hover:bg-muted active:bg-muted-strong', focusRing],\n        className,\n      )}\n      {...props}\n    >\n      {children ?? <Menu className=\"h-icon-lg w-icon-lg\" aria-hidden />}\n    </button>\n  );\n  },\n);\nTopBarMenuButton.displayName = 'TopBarMenuButton';\n\n/* -------------------------------------------------------------------------------------------------\n * Primary navigation (Guest, Simple, Dual-tier)\n * -----------------------------------------------------------------------------------------------*/\n\nexport interface TopBarNavItem extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children' | 'href'> {\n  label: React.ReactNode;\n  href: string;\n  // `true` and `page` mark the current page. `location` marks the current in-page anchor section.\n  current?: boolean | 'page' | 'location';\n}\n\nexport interface TopBarNavProps extends Omit<React.HTMLAttributes<HTMLElement>, 'children'> {\n  items: TopBarNavItem[];\n  /** `muted` renders inactive items in the subtle text color (Guest header). Default `default`. */\n  tone?: 'default' | 'muted';\n}\n\nconst TopBarNav = React.forwardRef<HTMLElement, TopBarNavProps>(\n  ({ className, items, tone = 'default', 'aria-label': ariaLabelProp, ...props }, ref) => {\n    const l = useLabels();\n    const ariaLabel = ariaLabelProp ?? l.primary;\n    return (\n    <nav ref={ref} aria-label={ariaLabel} className={cn('-m-1 min-w-0 overflow-x-auto p-1', className)} {...props}>\n      <ul className=\"m-0 flex w-max list-none items-center gap-1 p-0\">\n        {items.map(({ label, href, current, ...rest }, index) => (\n          <li key={index}>\n            <a\n              {...rest}\n              href={href}\n              aria-current={current ? (current === 'location' ? 'location' : 'page') : undefined}\n              className={cn(\n                'typography-ui-medium inline-flex h-control-md items-center rounded-control px-2 no-underline',\n                transitionBase,\n                'hover:bg-muted',\n                tone === 'muted' ? 'text-text-subtle' : 'text-text-default',\n                'aria-[current=page]:bg-muted aria-[current=page]:text-text-default',\n                'aria-[current=location]:bg-muted aria-[current=location]:text-text-default',\n                focusRing,\n              )}\n            >\n              {label}\n            </a>\n          </li>\n        ))}\n      </ul>\n    </nav>\n  );\n  },\n);\nTopBarNav.displayName = 'TopBarNav';\n\n/* -------------------------------------------------------------------------------------------------\n * Guest header parts: brand with suite byline, language and theme\n * -----------------------------------------------------------------------------------------------*/\n\nexport interface TopBarBrandProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n  // Leading logo or object icon.\n  mark?: React.ReactNode;\n  // Wordmark text.\n  name: React.ReactNode;\n  // Optional suite byline next to the wordmark, for example \"by Get IT Done\" (Figma Header navigation Guest, Suite byline).\n  byline?: React.ReactNode;\n  // Wordmark type role: `sm` is the default UI weight, `md` the small title, `lg` the small display style.\n  // `18`, `20` and `24` set the wordmark at 18px (large role), 20px (title-sm role, same as `md`) and 24px (page title role) for site headers.\n  size?: 'sm' | 'md' | 'lg' | '18' | '20' | '24';\n  // `inverse` sets the wordmark and byline for an inverse surface (dark band in light mode, light band in dark mode).\n  // `inverse-fixed` stays light on dark in both modes, for bands that are dark in light and dark mode.\n  tone?: 'default' | 'inverse' | 'inverse-fixed';\n  // Sets the wordmark in capitals.\n  uppercase?: boolean;\n  // Letter spacing of the wordmark. `default` keeps the type role. `tight`, `wide` and `wider` use the Tailwind tracking steps, usually with `uppercase`.\n  tracking?: 'default' | 'tight' | 'wide' | 'wider';\n}\n\nconst brandSize = {\n  sm: 'typography-ui-semibold',\n  md: 'typography-title-sm',\n  lg: 'typography-display-sm',\n  '18': 'typography-large',\n  '20': 'typography-title-sm',\n  '24': 'typography-page-title',\n} as const;\n\nconst brandTracking = { default: undefined, tight: 'tracking-tight', wide: 'tracking-wide', wider: 'tracking-wider' } as const;\n\n// The title-sm and display-sm utilities omit the letter-spacing of their type role, so it is read from the role token here. typography-ui-semibold already carries it.\nconst brandLetterSpacing = {\n  sm: undefined,\n  md: 'var(--typo-title-sm-ls)',\n  lg: 'var(--typo-display-sm-ls)',\n  '18': undefined,\n  '20': 'var(--typo-title-sm-ls)',\n  '24': undefined,\n} as const;\n\nconst brandTone = {\n  default: { name: 'text-text-default', byline: 'text-text-subtle' },\n  inverse: { name: 'text-text-inverse', byline: 'text-text-inverse-subtle' },\n  // The dark theme is scoped to the brand (class dark), so default text roles resolve light in both modes.\n  'inverse-fixed': { name: 'text-text-default', byline: 'text-text-subtle' },\n} as const;\n\nconst TopBarBrand = React.forwardRef<HTMLDivElement, TopBarBrandProps>(\n  ({ className, mark, name, byline, size = 'sm', tone = 'default', uppercase = false, tracking = 'default', ...props }, ref) => (\n    <div ref={ref} className={cn('flex min-w-0 items-center gap-2', tone === 'inverse-fixed' && 'dark', className)} {...props}>\n      {mark}\n      <span\n        className={cn(brandSize[size], 'truncate', brandTone[tone].name, uppercase && 'uppercase', brandTracking[tracking])}\n        style={{ letterSpacing: tracking === 'default' ? brandLetterSpacing[size] : undefined }}\n      >\n        {name}\n      </span>\n      {byline ? (\n        <span data-byline=\"\" className={cn('typography-ui-sm truncate', brandTone[tone].byline)}>\n          {byline}\n        </span>\n      ) : null}\n    </div>\n  ),\n);\nTopBarBrand.displayName = 'TopBarBrand';\n\nexport interface TopBarPreferencesProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n  // Visible name of the active language, for example \"English\". The language button renders only when set.\n  language?: React.ReactNode;\n  onLanguageClick?: React.MouseEventHandler<HTMLButtonElement>;\n  // Renders the language switch as a link (crawlable on SEO pages). `onLanguageClick` then fires on the anchor.\n  languageHref?: string;\n  // Language of the target page, set on the link as hreflang.\n  languageHrefLang?: string;\n  // Accessible name of the language button. Defaults to the provider text.\n  languageLabel?: string;\n  // Current theme. Drives the icon (moon in light, sun in dark). The theme button renders only with `onThemeToggle`.\n  theme?: 'light' | 'dark';\n  onThemeToggle?: () => void;\n  // Accessible name of the theme button. Defaults to the provider text.\n  themeLabel?: string;\n}\n\n// Language switcher and theme toggle slot of the Guest header (Figma Header navigation Guest, Language and theme).\nconst TopBarPreferences = React.forwardRef<HTMLDivElement, TopBarPreferencesProps>(\n  (\n    {\n      className,\n      language,\n      onLanguageClick,\n      languageHref,\n      languageHrefLang,\n      languageLabel,\n      theme = 'light',\n      onThemeToggle,\n      themeLabel,\n      ...props\n    },\n    ref,\n  ) => {\n    const l = useLabels();\n    return (\n      <div ref={ref} className={cn('flex items-center gap-1', className)} {...props}>\n        {language && languageHref ? (\n          <Button asChild variant=\"ghost\" size=\"control-md\">\n            <a\n              href={languageHref}\n              hrefLang={languageHrefLang}\n              lang={languageHrefLang}\n              aria-label={typeof language === 'string' ? `${languageLabel ?? l.language}: ${language}` : (languageLabel ?? l.language)}\n              onClick={onLanguageClick as unknown as React.MouseEventHandler<HTMLAnchorElement>}\n            >\n              <Globe aria-hidden className=\"h-icon-sm w-icon-sm\" />\n              {language}\n            </a>\n          </Button>\n        ) : language ? (\n          <Button\n            type=\"button\"\n            variant=\"ghost\"\n            size=\"control-md\"\n            aria-label={typeof language === 'string' ? `${languageLabel ?? l.language}: ${language}` : (languageLabel ?? l.language)}\n            onClick={onLanguageClick}\n          >\n            <Globe aria-hidden className=\"h-icon-sm w-icon-sm\" />\n            {language}\n          </Button>\n        ) : null}\n        {onThemeToggle ? (\n          <TopBarIconButton label={themeLabel ?? l.switchTheme} onClick={onThemeToggle}>\n            {theme === 'dark' ? <Sun aria-hidden /> : <Moon aria-hidden />}\n          </TopBarIconButton>\n        ) : null}\n      </div>\n    );\n  },\n);\nTopBarPreferences.displayName = 'TopBarPreferences';\n\n/* -------------------------------------------------------------------------------------------------\n * Guest mobile menu: menu button plus a drawer with the primary links\n * -----------------------------------------------------------------------------------------------*/\n\n// A link of the mobile menu: a TopBarNav link plus its own section-jump handler.\nexport interface TopBarMobileMenuLink extends TopBarNavItem {\n  // Replaces the default jump of this link, see `onNavigate` of the menu.\n  onNavigate?: SheetNavLinkProps['onNavigate'];\n}\n\nexport interface TopBarMobileMenuGroup {\n  // Label of the group. The group collapses and expands with the library Accordion.\n  group: React.ReactNode;\n  // Links inside the group.\n  items: TopBarMobileMenuLink[];\n  // Starts expanded. Default: expanded when one of the links is the current page or section. Ignored when `collapsible` is false.\n  defaultOpen?: boolean;\n  // `false` shows the group label as a visible, always expanded section heading instead of an accordion trigger. Default `true`.\n  collapsible?: boolean;\n}\n\nexport type TopBarMobileMenuItem = TopBarMobileMenuLink | TopBarMobileMenuGroup;\n\nconst isMenuGroup = (item: TopBarMobileMenuItem): item is TopBarMobileMenuGroup => 'items' in item;\n\nexport interface TopBarMobileMenuProps {\n  // Flat links like TopBarNav, optionally mixed with collapsible groups (`{ group, items }`). A `#section` link closes the drawer, waits until the scroll lock is gone, scrolls to the section and focuses it (give the sections `scroll-mt-*` below a fixed header). Any other plain click closes the drawer as well. A modified click or a link with a `target` keeps it open.\n  items: TopBarMobileMenuItem[];\n  // Replaces the default jump of every link, for example to scroll with a router. Runs once the drawer is completely gone. A link can set its own `onNavigate`.\n  onNavigate?: SheetNavLinkProps['onNavigate'];\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  // Deprecated fallback for `triggerLabel`, `title` and `navLabel` (removed after one minor). Use the three specific names.\n  label?: string;\n  // Accessible name of the menu button. Defaults to `label`, then the provider text \"Menu\" / \"Menü\".\n  triggerLabel?: string;\n  // Name of the drawer dialog. Defaults to `label`, then the provider text \"Menu\" / \"Menü\".\n  title?: string;\n  // Name of the navigation landmark. Defaults to `label`, then the provider text \"Navigation\".\n  navLabel?: string;\n  // Drawer width from the sheet size tokens. Default `sm` (440px, never wider than the viewport minus a scrim edge), so the drawer stays narrow at tablet width.\n  size?: React.ComponentProps<typeof SheetContent>['size'];\n  // Accessible name of the close button of the drawer. Defaults to the provider text \"Close menu\" / \"Menü schließen\".\n  closeLabel?: string;\n  // Extra content below the links, for example TopBarPreferences or a call to action. Scrolls with the links. A `SheetNavLink` here jumps to a section like the links above.\n  children?: React.ReactNode;\n  // Sticky footer of the drawer, outside the scrolling list, for actions such as \"Sign in\" that must stay visible while the links scroll.\n  footer?: React.ReactNode;\n  // Classes of the menu button. Default hides it from the md breakpoint up (pair TopBarNav with `hidden md:block`).\n  triggerClassName?: string;\n  // Keeps the menu button at every width, so the drawer replaces a desktop dropdown menu. Standard for landing pages, where the burger always opens this drawer.\n  alwaysVisible?: boolean;\n  // Surface tone of the bar that holds the menu button. The drawer itself always uses the default surface.\n  tone?: TopBarMenuButtonProps['tone'];\n  side?: 'left' | 'right';\n}\n\nconst TopBarMobileMenu = ({\n  items,\n  onNavigate: onNavigateAll,\n  open: openProp,\n  defaultOpen = false,\n  onOpenChange,\n  label: labelProp,\n  triggerLabel,\n  title,\n  navLabel,\n  size = 'sm',\n  closeLabel,\n  children,\n  footer,\n  triggerClassName,\n  alwaysVisible = false,\n  tone,\n  side = 'right',\n}: TopBarMobileMenuProps) => {\n  const l = useLabels({ closeMenu: closeLabel });\n  const menuId = React.useId();\n  const trigger = triggerLabel ?? labelProp ?? l.menu;\n  const dialogTitle = title ?? labelProp ?? l.menu;\n  const navName = navLabel ?? labelProp ?? l.navigation;\n  const [uncontrolled, setUncontrolled] = React.useState(defaultOpen);\n  const open = openProp ?? uncontrolled;\n  const setOpen = (next: boolean) => {\n    if (openProp === undefined) setUncontrolled(next);\n    onOpenChange?.(next);\n  };\n  const renderLink = ({ label: itemLabel, href, current, onNavigate, ...rest }: TopBarMobileMenuLink, index: number) => (\n    <SheetNavLink\n      key={index}\n      {...(rest as Omit<typeof rest, 'color'>)}\n      href={href}\n      variant=\"nav\"\n      closeOnNavigate\n      onNavigate={onNavigate ?? onNavigateAll}\n      aria-current={current ? (current === 'location' ? 'location' : 'page') : undefined}\n      className={cn(\n        'typography-ui-medium inline-flex h-control-lg items-center rounded-control px-3 no-underline',\n        transitionBase,\n        'hover:bg-muted aria-[current=page]:bg-muted aria-[current=location]:bg-muted',\n      )}\n    >\n      {itemLabel}\n    </SheetNavLink>\n  );\n  return (\n    <>\n      <TopBarMenuButton open={open} tone={tone} aria-label={trigger} className={cn(!alwaysVisible && 'md:hidden', triggerClassName)} onClick={() => setOpen(!open)} />\n      <Sheet open={open} onOpenChange={setOpen}>\n        <SheetContent side={side} size={size} padded={false} closeLabel={l.closeMenu} className={cn('bg-background-default text-text-default', size !== 'full' && 'max-w-[calc(100%-3rem)]')}>\n          <SheetTitle className=\"sr-only\">{dialogTitle}</SheetTitle>\n          <SheetDescription className=\"sr-only\">{l.navigation}</SheetDescription>\n          <nav\n            aria-label={navName}\n            className={cn(\n              'flex min-h-0 flex-1 flex-col gap-1 overflow-y-auto p-4 pt-12',\n              // Any other link in the drawer, such as a logo link in the children, gets the shared focus ring too.\n              '[&_a:focus-visible]:outline-none [&_a:focus-visible]:ring-focus [&_a:focus-visible]:ring-offset-focus [&_a:focus-visible]:ring-offset-background [&_a:focus-visible]:ring-ring-default',\n            )}\n          >\n            {items.map((item, index) => {\n              if (isMenuGroup(item) && item.collapsible === false) {\n                const headingId = `${menuId}-group-${index}`;\n                return (\n                  <div key={index} role=\"group\" aria-labelledby={headingId} className=\"flex flex-col gap-1\">\n                    <p id={headingId} className=\"typography-ui-medium px-3 pb-1 pt-3 text-text-subtle\">{item.group}</p>\n                    {/* Same 12px indent as the items of an expanded accordion group. */}\n                    <div className=\"flex flex-col gap-1 pl-3\">{item.items.map((child, childIndex) => renderLink(child, childIndex))}</div>\n                  </div>\n                );\n              }\n              if (isMenuGroup(item)) {\n                const openByDefault = item.defaultOpen ?? item.items.some((child) => child.current);\n                return (\n                  <Accordion key={index} type=\"multiple\" defaultValue={openByDefault ? ['group'] : []}>\n                    <AccordionItem value=\"group\" className=\"border-0\">\n                      <AccordionTrigger className=\"typography-ui-medium h-control-lg px-3 py-0 text-text-default no-underline hover:bg-muted hover:no-underline active:bg-muted-strong\">{item.group}</AccordionTrigger>\n                      <AccordionContent className=\"flex flex-col gap-1 pb-0 pl-3\">{item.items.map((child, childIndex) => renderLink(child, childIndex))}</AccordionContent>\n                    </AccordionItem>\n                  </Accordion>\n                );\n              }\n              return renderLink(item, index);\n            })}\n            {children ? <div className=\"mt-4 flex flex-col gap-2 border-0 border-t border-solid border-hairline pt-4\">{children}</div> : null}\n          </nav>\n          {footer ? <div data-slot=\"topbar-mobile-menu-footer\" className=\"flex shrink-0 flex-col gap-2 border-0 border-t border-solid border-hairline bg-background-default p-4\">{footer}</div> : null}\n        </SheetContent>\n      </Sheet>\n    </>\n  );\n};\nTopBarMobileMenu.displayName = 'TopBarMobileMenu';\n\nexport {\n  TopBarBrand,\n  TopBarMobileMenu,\n  TopBarPreferences,\n  TimerPill,\n  TopBar,\n  TopBarBreadcrumb,\n  TopBarEnd,\n  TopBarEscapeHint,\n  TopBarHelpButton,\n  TopBarIconButton,\n  TopBarMenuButton,\n  TopBarNav,\n  TopBarStart,\n  TopBarSubRow,\n  topBarVariants,\n};\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}