{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stepper",
  "title": "Stepper",
  "description": "Step indicator for a multi-step flow, with markers, connectors, segments and a counter.",
  "dependencies": [
    "class-variance-authority@^0.7.1",
    "lucide-react@^1.27.0"
  ],
  "registryDependencies": [
    "@pestoui/style",
    "@pestoui/utils"
  ],
  "files": [
    {
      "path": "pestoui/ui/stepper.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva } from \"class-variance-authority\"\nimport { CircleAlertIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype StepperVariant = \"horizontal\" | \"vertical\" | \"dots\" | \"bar\"\ntype StepperState = \"complete\" | \"current\" | \"upcoming\"\n/* A decoration on the derived state, never a replacement for it. There is\n   deliberately no per-item `state` prop: the whole value of this component is\n   that four steps stay consistent because one number drives them. */\ntype StepperStatus = \"error\"\ntype StepperSize = \"sm\" | \"default\" | \"lg\"\ntype StepperConnectorStyle = \"line\" | \"dashed\" | \"progress\" | \"none\"\ntype StepperLabels = \"below\" | \"end\" | \"none\"\n\nconst stepperVariants = cva(\"group/stepper flex list-none\", {\n  variants: {\n    variant: {\n      horizontal: \"w-full items-start\",\n      vertical: \"w-full flex-col\",\n      /* gap-3 is load-bearing: an interactive dot expands its hit area by 6px\n         per side, so a smaller gap would make neighbouring targets overlap. */\n      dots: \"w-fit items-center gap-3\",\n      /* The rails are the steps here, so they need a visible seam between them\n         — contiguous segments would read as one bar with stripes painted on. */\n      bar: \"w-full items-start gap-2\",\n    },\n  },\n  defaultVariants: {\n    variant: \"horizontal\",\n  },\n})\n\n/* Step markers are the one place a full round is correct — these are dots, not\n   icon tiles, so rounded-full never collapses a squircle here. */\nconst stepperMarkerVariants = cva(\n  \"relative flex shrink-0 items-center justify-center rounded-full transition-all duration-base ease-out-expo outline-none [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n  {\n    variants: {\n      state: {\n        complete: \"bg-selected text-selected-foreground shadow-soft\",\n        /* Nothing here is filled: a card-coloured disc outlined by a hairline,\n           with the step number sitting on it. Border, numeral and the halo\n           that hugs them all take the accent together, the numeral in its\n           text tone, --selected-ink. */\n        current:\n          \"border-2 border-selected bg-card text-selected-ink ring-4 ring-selected/15\",\n        upcoming:\n          \"border border-border bg-card text-muted-foreground dark:border-input dark:bg-input/30\",\n        /* Same ringed treatment as current, retoned — a failed step is still\n           the step you are on, so it must not lose its prominence. */\n        error:\n          \"border-2 border-destructive bg-card text-destructive ring-4 ring-destructive/15\",\n      },\n      size: {\n        sm: \"size-6 text-xs font-semibold tabular-nums [&_svg]:size-3.5\",\n        default: \"size-8 text-caption font-semibold tabular-nums [&_svg]:size-4\",\n        lg: \"size-10 text-sm font-semibold tabular-nums [&_svg]:size-5\",\n        dot: \"size-2.5\",\n      },\n    },\n    /* A pip is two and a half units across with no border, no glyph and no\nA pip is two and a half units across with no border, no glyph and no\n       label on screen — the fill IS the whole indicator. */\n    compoundVariants: [\n      { size: \"dot\", state: \"complete\", class: \"bg-selected\" },\n      {\n        size: \"dot\",\n        state: \"current\",\n        class: \"border-0 bg-selected ring-selected/20\",\n      },\n      { size: \"dot\", state: \"upcoming\", class: \"bg-secondary\" },\n      {\n        size: \"dot\",\n        state: \"error\",\n        class: \"border-0 bg-destructive ring-destructive/20\",\n      },\n    ],\n    defaultVariants: {\n      state: \"upcoming\",\n      size: \"default\",\n    },\n  }\n)\n\n/* `dashed` has to be a border rather than a background: a dashed gradient would\n   need an arbitrary value and would stop honouring --border entirely. That is\n   also why the fill axis paints border-color for dashed and background-color\n   for the rest, instead of one shared colour slot. */\nconst stepperConnectorVariants = cva(\n  \"flex-1 transition-colors duration-spring-glide ease-spring-glide\",\n  {\n    variants: {\n      orientation: {\n        horizontal: \"mx-2\",\n        vertical: \"my-1.5\",\n      },\n      connector: {\n        line: \"rounded-full\",\n        dashed: \"border-dashed\",\n        /* The track for a fill that grows; the growing part is a child. */\n        progress: \"relative overflow-hidden rounded-full bg-border\",\n        /* Still occupies its slot so step widths stay equal — this is the same\n           mechanism the first and last half-connectors have always used. */\n        none: \"invisible rounded-full\",\n      },\n      filled: {\n        true: \"\",\n        false: \"\",\n      },\n    },\n    compoundVariants: [\n      {\n        orientation: \"horizontal\",\n        connector: \"dashed\",\n        class: \"h-0 border-t-2\",\n      },\n      {\n        orientation: \"vertical\",\n        connector: \"dashed\",\n        class: \"min-h-5 w-0 border-l-2\",\n      },\n      {\n        orientation: \"horizontal\",\n        connector: [\"line\", \"progress\", \"none\"],\n        class: \"h-0.5\",\n      },\n      {\n        orientation: \"vertical\",\n        connector: [\"line\", \"progress\", \"none\"],\n        class: \"min-h-5 w-0.5\",\n      },\n      /* Half a unit thick either way, so the filled rail takes the accent. */\n      { connector: \"line\", filled: true, class: \"bg-selected\" },\n      { connector: \"line\", filled: false, class: \"bg-border\" },\n      { connector: \"dashed\", filled: true, class: \"border-selected\" },\n      { connector: \"dashed\", filled: false, class: \"border-border\" },\n    ],\n    defaultVariants: {\n      orientation: \"horizontal\",\n      connector: \"line\",\n      filled: false,\n    },\n  }\n)\n\n/* variant=\"bar\" only. The rail carries the fill, so the state axis is purely\n   colour — how far it travels is an inline scaleX, because a step can report a\n   partial value and a utility cannot express a continuous number. */\nconst stepperSegmentVariants = cva(\n  \"block size-full origin-left rounded-full transition-transform duration-spring-glide ease-spring-glide\",\n  {\n    variants: {\n      state: {\n        complete: \"bg-selected\",\n        current: \"bg-selected\",\n        upcoming: \"bg-selected\",\n        error: \"bg-destructive\",\n      },\n    },\n    defaultVariants: {\n      state: \"upcoming\",\n    },\n  }\n)\n\ntype StepperContextValue = {\n  variant: StepperVariant\n  size: StepperSize\n  connector: StepperConnectorStyle\n  labels: StepperLabels\n  activeStep: number\n  count: number\n}\n\nconst StepperContext = React.createContext<StepperContextValue | null>(null)\nconst StepperIndexContext = React.createContext(0)\n\n/* State is derived, never passed per item: everything before activeStep is\n   complete, activeStep itself is current, the rest are upcoming. */\nfunction useStepperItem() {\n  const stepper = React.useContext(StepperContext)\n  const index = React.useContext(StepperIndexContext)\n\n  if (!stepper) {\n    throw new Error(\"StepperItem must be used within <Stepper>\")\n  }\n\n  const state: StepperState =\n    index < stepper.activeStep\n      ? \"complete\"\n      : index === stepper.activeStep\n        ? \"current\"\n        : \"upcoming\"\n\n  return {\n    variant: stepper.variant,\n    size: stepper.size,\n    connector: stepper.connector,\n    labels: stepper.labels,\n    index,\n    state,\n    isFirst: index === 0,\n    isLast: index === stepper.count - 1,\n    /* The connector before a step reads as filled only when that step is complete. */\n    lineBefore: index < stepper.activeStep,\n    lineAfter: index + 1 < stepper.activeStep,\n  }\n}\n\n/* A vertical marker column has to hold its label optically level with the\n   marker's centre, and that offset moves with the marker's diameter. */\nconst stepperVerticalLabelOffset = {\n  sm: \"pt-0.5\",\n  default: \"pt-1.5\",\n  lg: \"pt-2.5\",\n} as const\n\nfunction resolveLabels(\n  variant: StepperVariant,\n  labels: StepperLabels | undefined\n): StepperLabels {\n  if (variant === \"dots\") return \"none\"\n  if (labels === undefined) return variant === \"vertical\" ? \"end\" : \"below\"\n  /* A vertical stack has no room below a marker, and a bar's text always sits\n     under its rail — clamp the nonsensical pairings instead of rendering them. */\n  if (variant === \"vertical\") return labels === \"below\" ? \"end\" : labels\n  if (variant === \"bar\") return labels === \"end\" ? \"below\" : labels\n  return labels\n}\n\nfunction StepperConnector({\n  orientation,\n  connector,\n  filled,\n  hidden = false,\n}: {\n  orientation: \"horizontal\" | \"vertical\"\n  connector: StepperConnectorStyle\n  filled: boolean\n  hidden?: boolean\n}) {\n  const resolved = hidden ? \"none\" : connector\n\n  return (\n    <span\n      data-slot=\"stepper-connector\"\n      aria-hidden=\"true\"\n      className={cn(\n        stepperConnectorVariants({ orientation, connector: resolved, filled })\n      )}\n    >\n      {resolved === \"progress\" ? (\n        /* Growing the fill rather than swapping a colour reads as travel, and\n           it borrows the same spring the progress bar uses so the two\n           components move in the same language. */\n        <span\n          data-slot=\"stepper-connector-fill\"\n          className={cn(\n            \"absolute inset-0 rounded-full bg-selected transition-transform duration-spring-glide ease-spring-glide\",\n            orientation === \"horizontal\" ? \"origin-left\" : \"origin-top\"\n          )}\n          style={{\n            transform:\n              orientation === \"horizontal\"\n                ? `scaleX(${filled ? 1 : 0})`\n                : `scaleY(${filled ? 1 : 0})`,\n          }}\n        />\n      ) : null}\n    </span>\n  )\n}\n\n/* One definition for the title/description pair so the three type strings it\n   carries exist in exactly one place — when the type scale lands they become a\n   one-line change instead of a six-site hunt. */\nfunction StepperLabel({\n  titleId,\n  descriptionId,\n  title,\n  description,\n  state,\n  status,\n}: {\n  titleId: string\n  descriptionId: string\n  title: React.ReactNode\n  description?: React.ReactNode\n  state: StepperState\n  status?: StepperStatus\n}) {\n  return (\n    <>\n      <span\n        id={titleId}\n        data-slot=\"stepper-title\"\n        className={cn(\n          \"text-body font-semibold tracking-title transition-colors duration-base ease-out-expo\",\n          status === \"error\"\n            ? \"text-destructive\"\n            : state === \"upcoming\"\n              ? \"text-muted-foreground\"\n              : \"text-foreground\"\n        )}\n      >\n        {/* Colour alone cannot say \"this step failed\". The prefix rides the\n            accessible name, so the failure survives greyscale and is spoken. */}\n        {status === \"error\" ? <span className=\"sr-only\">Error: </span> : null}\n        {title}\n      </span>\n      {description ? (\n        <span\n          id={descriptionId}\n          data-slot=\"stepper-description\"\n          className=\"text-caption text-muted-foreground\"\n        >\n          {description}\n        </span>\n      ) : null}\n    </>\n  )\n}\n\ninterface StepperProps extends React.ComponentProps<\"ol\"> {\n  variant?: StepperVariant\n  activeStep?: number\n  /** Marker diameter. Ignored by `dots` (fixed pip) and `bar` (no marker). */\n  size?: StepperSize\n  /** Ignored by `dots`, which is unconnected by design. */\n  connector?: StepperConnectorStyle\n  /** Defaults to `below` for horizontal/bar, `end` for vertical, `none` for dots. */\n  labels?: StepperLabels\n}\n\nfunction Stepper({\n  className,\n  variant = \"horizontal\",\n  activeStep = 0,\n  size = \"default\",\n  connector,\n  labels,\n  children,\n  ...props\n}: StepperProps) {\n  const childList = React.Children.toArray(children).filter(React.isValidElement)\n  /* The counter reports on the list, so it must not be counted as part of it —\n     and it cannot live inside the <ol> either, where a <p> would be invalid. */\n  const items = childList.filter((child) => child.type !== StepperCounter)\n  const counters = childList.filter((child) => child.type === StepperCounter)\n\n  const resolvedLabels = resolveLabels(variant, labels)\n  /* Dots have never drawn connectors and adding them by default would move\n     every existing dot stepper, so their default is \"none\", not \"line\". */\n  const resolvedConnector = connector ?? (variant === \"dots\" ? \"none\" : \"line\")\n\n  return (\n    <StepperContext.Provider\n      value={{\n        variant,\n        size,\n        connector: resolvedConnector,\n        labels: resolvedLabels,\n        activeStep,\n        count: items.length,\n      }}\n    >\n      <ol\n        data-slot=\"stepper\"\n        data-variant={variant}\n        data-size={size}\n        data-connector={resolvedConnector}\n        data-labels={resolvedLabels}\n        role=\"list\"\n        className={cn(stepperVariants({ variant }), className)}\n        {...props}\n      >\n        {items.map((child, index) => (\n          <StepperIndexContext.Provider key={child.key ?? index} value={index}>\n            {child}\n          </StepperIndexContext.Provider>\n        ))}\n      </ol>\n      {counters}\n    </StepperContext.Provider>\n  )\n}\n\ntype StepperCounterProps = React.ComponentProps<\"p\">\n\n/* Reads the count off the same context the list does, so \"Step 2 of 4\" cannot\n   drift from the number of children. Place it as a child of <Stepper>; it is\n   lifted out of the <ol> and rendered after it. */\nfunction StepperCounter({ className, children, ...props }: StepperCounterProps) {\n  const stepper = React.useContext(StepperContext)\n\n  if (!stepper) {\n    throw new Error(\"StepperCounter must be used within <Stepper>\")\n  }\n\n  const { activeStep, count, labels } = stepper\n  /* aria-current already announces which step is current. When labels are on\n     screen the counter is a visual restatement of that, so let it stay silent\n     and only speak when it is the sole indication of where you are. */\n  const redundant = labels !== \"none\"\n\n  return (\n    <p\n      data-slot=\"stepper-counter\"\n      aria-hidden={redundant || undefined}\n      aria-live={redundant ? undefined : \"polite\"}\n      className={cn(\n        \"text-sm font-medium tabular-nums text-muted-foreground\",\n        className\n      )}\n      {...props}\n    >\n      {children ?? `Step ${Math.min(activeStep + 1, count)} of ${count}`}\n    </p>\n  )\n}\n\n/* `onSelect` is omitted alongside `title` because both names already exist on\n   the intrinsic li — the DOM handler takes an event, ours takes the step index,\n   and an interface cannot narrow an inherited member. It is destructured out\n   below, so nothing of the intrinsic shape reaches the element. */\ninterface StepperItemProps\n  extends Omit<React.ComponentProps<\"li\">, \"title\" | \"onSelect\"> {\n  title: React.ReactNode\n  description?: React.ReactNode\n  /** Replaces the step number inside the marker. Hidden from AT — `title` names the step. */\n  icon?: React.ReactNode\n  /** Decorates the derived state; it never replaces it. */\n  status?: StepperStatus\n  /** 0–100. `variant=\"bar\"` on the current step only — partial fill of its rail. */\n  progress?: number\n  onSelect?: (index: number) => void\n}\n\nfunction StepperItem({\n  className,\n  title,\n  description,\n  icon,\n  status,\n  progress,\n  onSelect,\n  ...props\n}: StepperItemProps) {\n  const {\n    variant,\n    size,\n    connector,\n    labels,\n    index,\n    state,\n    isFirst,\n    isLast,\n    lineBefore,\n    lineAfter,\n  } = useStepperItem()\n  const uid = React.useId()\n  const titleId = `${uid}-title`\n  const descriptionId = `${uid}-description`\n  const interactive = typeof onSelect === \"function\"\n  const isDot = variant === \"dots\"\n  const isBar = variant === \"bar\"\n  const errored = status === \"error\"\n  const paintedState = errored ? \"error\" : state\n\n  const label = (\n    <StepperLabel\n      titleId={titleId}\n      descriptionId={descriptionId}\n      title={title}\n      description={description}\n      state={state}\n      status={status}\n    />\n  )\n\n  /* When the marker is a real button it owns aria-current, so the li must not\n     repeat it — otherwise the step is announced as current twice. */\n  const current = state === \"current\" && !interactive ? \"step\" : undefined\n\n  if (isBar) {\n    /* Colour comes from the status, distance from the derived state: an errored\n       step still shows how far it got, it just shows it in destructive. */\n    const fill =\n      state === \"complete\"\n        ? 1\n        : state === \"current\"\n          ? typeof progress === \"number\"\n            ? Math.min(1, Math.max(0, progress / 100))\n            : 1\n          : 0\n\n    const rail = (\n      <span\n        data-slot=\"stepper-rail\"\n        data-state={state}\n        aria-hidden=\"true\"\n        className={cn(\n          \"relative block h-1 w-full overflow-hidden rounded-full bg-secondary\",\n          interactive &&\n            state === \"upcoming\" &&\n            \"transition-colors duration-instant ease-out-expo group-hover/stepper-bar:bg-selected/25\"\n        )}\n      >\n        <span\n          data-slot=\"stepper-segment\"\n          data-state={paintedState}\n          className={cn(stepperSegmentVariants({ state: paintedState }))}\n          style={{ transform: `scaleX(${fill})` }}\n        />\n      </span>\n    )\n\n    const body = (\n      <>\n        {rail}\n        {labels === \"none\" ? (\n          <span className=\"sr-only\">{label}</span>\n        ) : (\n          <div className=\"mt-2 flex min-w-0 flex-col gap-0.5\">{label}</div>\n        )}\n      </>\n    )\n\n    return (\n      <li\n        data-slot=\"stepper-item\"\n        data-variant={variant}\n        data-state={state}\n        data-status={status}\n        aria-current={current}\n        className={cn(\"flex min-w-0 flex-1 basis-0 flex-col\", className)}\n        {...props}\n      >\n        {interactive ? (\n          /* A bar has no marker to hang a round ring on, so the whole\n             rail-plus-label column becomes the target and takes the list-row\n             radius — a rounded-full ring around a 4px rail is unreadable. */\n          <button\n            type=\"button\"\n            data-slot=\"stepper-segment-button\"\n            data-state={state}\n            aria-labelledby={titleId}\n            aria-describedby={description ? descriptionId : undefined}\n            aria-current={state === \"current\" ? \"step\" : undefined}\n            onClick={() => onSelect?.(index)}\n            className=\"group/stepper-bar flex w-full min-w-0 cursor-pointer flex-col rounded-md text-left outline-none focus-visible:ring-3 focus-visible:ring-ring/50\"\n          >\n            {body}\n          </button>\n        ) : (\n          body\n        )}\n      </li>\n    )\n  }\n\n  const markerClassName = cn(\n    stepperMarkerVariants({ state: paintedState, size: isDot ? \"dot\" : size }),\n    interactive &&\n      \"cursor-pointer after:absolute focus-visible:ring-3 focus-visible:ring-ring/50 active:duration-spring-press active:ease-spring-press\",\n    /* Enlarged hit area, sized so adjacent targets never overlap. */\n    interactive && (isDot ? \"after:-inset-x-1.5 after:-inset-y-2\" : \"after:-inset-2\"),\n    /* A dot is a third the size of a numbered marker, so it needs a deeper\n       press to register the same amount of movement on screen. */\n    interactive && (isDot ? \"active:scale-90\" : \"active:scale-95\"),\n    /* A dot has no border or glyph to tint, so it hovers on its fill instead.\n       Both previews are held at the accent the state they lead to now paints\n         in, which is also what keeps them legible once alpha is taken off. */\n    interactive &&\n      state === \"upcoming\" &&\n      !errored &&\n      (isDot\n        ? \"hover:bg-selected/40\"\n        : \"hover:border-selected/50 hover:text-foreground\"),\n    errored && \"animate-shake\"\n  )\n\n  /* The number stays in flow and the tick is centred absolutely over it, so the\n     swap costs no layout and the resting box is unchanged. Completing a step is\n     the one moment in this component worth watching: the number drops out on a\n     short tween while the tick draws itself on. */\n  const markerContent = isDot ? null : errored ? (\n    <CircleAlertIcon aria-hidden=\"true\" />\n  ) : (\n    <>\n      <span\n        aria-hidden=\"true\"\n        className={cn(\n          \"transition-[opacity,scale]\",\n          /* A number sits on the text baseline happily; an icon does not, so it\n             only gets a box when there is one to place. */\n          icon ? \"flex items-center justify-center\" : undefined,\n          state === \"complete\"\n            ? \"scale-90 opacity-0 duration-exit ease-out-expo\"\n            : \"scale-100 opacity-100 duration-spring-swap ease-spring-swap\"\n        )}\n      >\n        {icon ?? index + 1}\n      </span>\n      {/* pathLength normalises the stroke to 100 units so draw-path's dash\n          offset resolves over the whole tick regardless of its real length.\n\n          strokeWidth 3, not lucide's stock 2, and it is the SAME 3 the checkbox\n          tick carries — one weight for one glyph, stated in viewBox units so it\n          scales with the marker instead of pinning an absolute px. The three\n          marker sizes render this 24-unit box at 14 / 16 / 20px, so the tick\n          inks 1.75 / 2.00 / 2.50px and stays a constant 12.5% of its own glyph\n          box at every one of them. At 2 the sm marker inked 1.1667px — the\n          exact weight the checkbox measured as too light beside its own 2px\n          indeterminate bar — and the sm marker draws the same 14px box the\n          checkbox does, so at 3 the two are now identical to the digit: 1.75px\n          each. Measured across the 46 tick glyphs the docs page renders: 3 at\n          1.1667, 40 at 1.3333, 3 at 1.6667 before; 1.75 / 2.00 / 2.50 after.\n          The geometry takes it — the short leg is √50 ≈ 7.07 units, still 2.4\n          stroke-widths long, and the round caps paint 19 × 14 units inside the\n          24 the svg clips at. */}\n      <svg\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeWidth=\"3\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        aria-hidden=\"true\"\n        className=\"absolute inset-0 m-auto\"\n      >\n        <path\n          d=\"M20 6 9 17l-5-5\"\n          pathLength=\"100\"\n          className={cn(\"draw-path\", state === \"complete\" && \"draw-path-done\")}\n        />\n      </svg>\n    </>\n  )\n\n  /* Keying on status remounts the marker when a step fails, which is what makes\n     the shake fire once on arrival rather than never at all. */\n  const markerKey = errored ? \"error\" : \"ok\"\n\n  const marker = interactive ? (\n    <button\n      key={markerKey}\n      type=\"button\"\n      data-slot=\"stepper-marker\"\n      data-state={state}\n      data-status={status}\n      aria-labelledby={titleId}\n      aria-describedby={description ? descriptionId : undefined}\n      aria-current={state === \"current\" ? \"step\" : undefined}\n      onClick={() => onSelect?.(index)}\n      className={markerClassName}\n    >\n      {markerContent}\n    </button>\n  ) : (\n    <span\n      key={markerKey}\n      data-slot=\"stepper-marker\"\n      data-state={state}\n      data-status={status}\n      className={markerClassName}\n    >\n      {markerContent}\n    </span>\n  )\n\n  if (isDot) {\n    return (\n      <li\n        data-slot=\"stepper-item\"\n        data-variant={variant}\n        data-state={state}\n        data-status={status}\n        aria-current={current}\n        className={cn(\"flex items-center\", className)}\n        {...props}\n      >\n        {marker}\n        <span className=\"sr-only\">{label}</span>\n      </li>\n    )\n  }\n\n  if (variant === \"vertical\") {\n    return (\n      <li\n        data-slot=\"stepper-item\"\n        data-variant={variant}\n        data-state={state}\n        data-status={status}\n        aria-current={current}\n        className={cn(\"flex gap-3.5\", className)}\n        {...props}\n      >\n        <div className=\"flex flex-col items-center self-stretch\">\n          {marker}\n          {isLast ? null : (\n            /* The connector is the progression made visible, so it travels on\n               the layout spring rather than snapping the moment the step behind\n               it completes. */\n            <StepperConnector\n              orientation=\"vertical\"\n              connector={connector}\n              filled={lineAfter}\n            />\n          )}\n        </div>\n        {labels === \"none\" ? (\n          <span className=\"sr-only\">{label}</span>\n        ) : (\n          <div\n            className={cn(\n              \"flex min-w-0 flex-col gap-0.5\",\n              stepperVerticalLabelOffset[size],\n              isLast ? \"pb-0\" : \"pb-7\"\n            )}\n          >\n            {label}\n          </div>\n        )}\n      </li>\n    )\n  }\n\n  if (labels === \"end\") {\n    /* Step widths stop being uniform the moment the text sits inline, so every\n       item is forced onto the same basis and titles wrap instead of shoving\n       their neighbours down the row. */\n    return (\n      <li\n        data-slot=\"stepper-item\"\n        data-variant={variant}\n        data-state={state}\n        data-status={status}\n        aria-current={current}\n        className={cn(\"flex min-w-0 flex-1 basis-0 items-center gap-3\", className)}\n        {...props}\n      >\n        {marker}\n        <div className=\"flex min-w-0 flex-col gap-0.5\">{label}</div>\n        <StepperConnector\n          orientation=\"horizontal\"\n          connector={connector}\n          filled={lineAfter}\n          hidden={isLast}\n        />\n      </li>\n    )\n  }\n\n  return (\n    <li\n      data-slot=\"stepper-item\"\n      data-variant={variant}\n      data-state={state}\n      data-status={status}\n      aria-current={current}\n      className={cn(\n        \"relative flex min-w-0 flex-1 flex-col items-center text-center\",\n        className\n      )}\n      {...props}\n    >\n      <div className=\"flex w-full items-center\">\n        <StepperConnector\n          orientation=\"horizontal\"\n          connector={connector}\n          filled={lineBefore}\n          hidden={isFirst}\n        />\n        {marker}\n        <StepperConnector\n          orientation=\"horizontal\"\n          connector={connector}\n          filled={lineAfter}\n          hidden={isLast}\n        />\n      </div>\n      {labels === \"none\" ? (\n        <span className=\"sr-only\">{label}</span>\n      ) : (\n        <div\n          className={cn(\n            \"flex min-w-0 flex-col gap-0.5 px-1\",\n            size === \"sm\" ? \"mt-2\" : \"mt-3\"\n          )}\n        >\n          {label}\n        </div>\n      )}\n    </li>\n  )\n}\n\nexport {\n  Stepper,\n  StepperItem,\n  StepperCounter,\n  stepperVariants,\n  stepperMarkerVariants,\n  stepperConnectorVariants,\n  stepperSegmentVariants,\n}\nexport type {\n  StepperProps,\n  StepperItemProps,\n  StepperCounterProps,\n  StepperVariant,\n  StepperState,\n  StepperStatus,\n  StepperSize,\n  StepperConnectorStyle,\n  StepperLabels,\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}