# Stepper Intent Onboarding

> An intent-driven onboarding block with hidden Stepper state, intent routing, profile validation, interests, async setup, and a signed-in completion state.

A shadcn/ui block from the [@stepper](https://francozeta-stepper.vercel.app) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/stepper/stepper-intent-onboarding).

- Page, with a live preview: https://shadcn.exchange/r/stepper/stepper-intent-onboarding
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/stepper/stepper-intent-onboarding-light-87ccbfb836df380e.png
- Registry item: https://francozeta-stepper.vercel.app/stepper-intent-onboarding.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://francozeta-stepper.vercel.app/stepper-intent-onboarding.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@radix-ui/react-slot`, `lucide-react`, `react-hook-form`, `react-icons`, `zod`
- Registry items: `button`, `checkbox`, `field`, `input`, `separator`

## Files

### `registry/default/ui/stepper.tsx`

```tsx
"use client";

import * as React from "react";
import { Slot } from "@radix-ui/react-slot";

import { cn } from "@/lib/utils";

// ----------------------------------------------------------------------------
// Types
// ----------------------------------------------------------------------------

type StepperOrientation = "horizontal" | "vertical";
type StepperValue = string;
type StepperStepPosition = "previous" | "current" | "next";
type StepperStepState =
  | "inactive"
  | "active"
  | "completed"
  | "disabled"
  | "error";

type StepperStepInput<TValue extends StepperValue = StepperValue> = {
  value: TValue;
  disabled?: boolean;
};

type StepperStep<TValue extends StepperValue = StepperValue> = {
  value: TValue;
  disabled: boolean;
};

type StepperStepsValue<TSteps extends readonly StepperStepInput[]> =
  TSteps[number]["value"];

type RegisteredStep<TValue extends StepperValue = StepperValue> =
  StepperStep<TValue> & {
    id: string;
  };

type StepperNavigationGuard = () => boolean | Promise<boolean>;

type StepperApi<TValue extends StepperValue = StepperValue> = {
  value: TValue | undefined;
  orientation: StepperOrientation;
  steps: StepperStep<TValue>[];
  currentIndex: number;
  totalSteps: number;
  setValue: (value: TValue) => void;
  getStepIndex: (value: TValue) => number;
  canGoPrevious: boolean;
  canGoNext: boolean;
  goPrevious: () => void;
  goNext: () => void;
};

type StepperItemApi<TValue extends StepperValue = StepperValue> = {
  value: TValue;
  index: number;
  disabled: boolean;
  completed: boolean;
  isActive: boolean;
  stepState: StepperStepState;
  stepPosition: StepperStepPosition;
  orientation: StepperOrientation;
  setValue: (value: TValue) => void;
  triggerId: string;
  contentId: string;
};

type StepperContextValue<TValue extends StepperValue = StepperValue> =
  StepperApi<TValue> & {
    isExplicitMode: boolean;
    transitionFromIndex: number;
    transitionToIndex: number;
    registerStep: (step: RegisteredStep) => void;
    unregisterStep: (id: string) => void;
    getTriggerId: (value: TValue) => string;
    getContentId: (value: TValue) => string;
  };

type StepperItemContextValue<TValue extends StepperValue = StepperValue> =
  StepperItemApi<TValue>;

type StepperProps<TValue extends StepperValue = StepperValue> =
  React.ComponentPropsWithoutRef<"div"> & {
    value?: TValue;
    defaultValue?: TValue;
    onValueChange?: (value: TValue) => void;
    orientation?: StepperOrientation;
    steps?: readonly StepperStepInput<TValue>[];
  };

type StepperPropsWithSteps<
  TSteps extends readonly StepperStepInput[],
> = Omit<StepperProps<StepperStepsValue<TSteps>>, "steps"> & {
  steps: TSteps;
};

type StepperPropsWithoutSteps<
  TValue extends StepperValue = StepperValue,
> = Omit<StepperProps<TValue>, "steps"> & {
  steps?: undefined;
};

type StepperListProps = React.ComponentPropsWithoutRef<"ol">;

type StepperItemProps<TValue extends StepperValue = StepperValue> = Omit<
  React.ComponentPropsWithoutRef<"li">,
  "value"
> & {
  value: TValue;
  completed?: boolean;
  defaultTrigger?: boolean;
  disabled?: boolean;
  error?: boolean;
  separator?: boolean;
};

type StepperTriggerProps = React.ComponentPropsWithoutRef<"button"> & {
  asChild?: boolean;
};

type StepperIndicatorProps = React.ComponentPropsWithoutRef<"span">;

type StepperLabelProps = React.ComponentPropsWithoutRef<"span">;

type StepperDescriptionProps = React.ComponentPropsWithoutRef<"span">;

type StepperSeparatorProps = React.ComponentPropsWithoutRef<"span">;

type StepperContentProps<TValue extends StepperValue = StepperValue> =
  React.ComponentPropsWithoutRef<"div"> & {
    value: TValue;
    forceMount?: boolean;
    keepMounted?: boolean;
    asChild?: boolean;
  };

type StepperButtonProps = React.ComponentPropsWithoutRef<"button"> & {
  asChild?: boolean;
};

type StepperPreviousProps = StepperButtonProps & {
  onBeforePrevious?: StepperNavigationGuard;
};

type StepperNextProps = StepperButtonProps & {
  onBeforeNext?: StepperNavigationGuard;
};

// ----------------------------------------------------------------------------
// Helpers
// ----------------------------------------------------------------------------

const isDevelopment = process.env.NODE_ENV !== "production";

function warnDev(message: string) {
  if (isDevelopment) {
    console.warn(message);
  }
}

function getSafeId(value: StepperValue) {
  return value.replace(/[^a-zA-Z0-9_-]/g, "-");
}

function normalizeStep<TValue extends StepperValue>(
  step: StepperStepInput<TValue>
): StepperStep<TValue> {
  return {
    value: step.value,
    disabled: Boolean(step.disabled),
  };
}

function normalizeSteps<TValue extends StepperValue>(
  steps: readonly StepperStepInput<TValue>[] | undefined
) {
  return steps?.map(normalizeStep) ?? [];
}

function getDuplicateStepValues<TValue extends StepperValue>(
  steps: ReadonlyArray<{ value: TValue }>
) {
  const seenValues = new Set<TValue>();
  const duplicateValues = new Set<TValue>();

  steps.forEach((step) => {
    if (seenValues.has(step.value)) {
      duplicateValues.add(step.value);
      return;
    }

    seenValues.add(step.value);
  });

  return Array.from(duplicateValues);
}

function getStepByValue<TValue extends StepperValue>(
  steps: readonly StepperStep<TValue>[],
  value: TValue | undefined
) {
  return steps.find((step) => step.value === value);
}

function getCurrentStepValue<TValue extends StepperValue>(
  steps: readonly StepperStep<TValue>[],
  selectedValue: TValue | undefined
) {
  const selectedStep = getStepByValue(steps, selectedValue);

  if (selectedStep && !selectedStep.disabled) {
    return selectedStep.value;
  }

  return steps.find((step) => !step.disabled)?.value;
}

function getNextEnabledStep<TValue extends StepperValue>(
  steps: readonly StepperStep<TValue>[],
  currentValue: TValue | undefined
) {
  const currentIndex = steps.findIndex((step) => step.value === currentValue);

  if (currentIndex === -1) {
    return undefined;
  }

  return steps.slice(currentIndex + 1).find((step) => !step.disabled);
}

function getPreviousEnabledStep<TValue extends StepperValue>(
  steps: readonly StepperStep<TValue>[],
  currentValue: TValue | undefined
) {
  const currentIndex = steps.findIndex((step) => step.value === currentValue);

  if (currentIndex === -1) {
    return undefined;
  }

  return steps
    .slice(0, currentIndex)
    .reverse()
    .find((step) => !step.disabled);
}

function getKeyboardNavigationStepValue<TValue extends StepperValue>({
  key,
  orientation,
  steps,
  value,
}: {
  key: string;
  orientation: StepperOrientation;
  steps: readonly StepperStep<TValue>[];
  value: TValue;
}) {
  const enabledSteps = steps.filter((step) => !step.disabled);
  const currentIndex = enabledSteps.findIndex((step) => step.value === value);

  if (currentIndex < 0) {
    return undefined;
  }

  if (key === "Home") {
    return enabledSteps.at(0)?.value;
  }

  if (key === "End") {
    return enabledSteps.at(-1)?.value;
  }

  if (
    (orientation === "horizontal" && key === "ArrowRight") ||
    (orientation === "vertical" && key === "ArrowDown")
  ) {
    return enabledSteps[Math.min(currentIndex + 1, enabledSteps.length - 1)]
      ?.value;
  }

  if (
    (orientation === "horizontal" && key === "ArrowLeft") ||
    (orientation === "vertical" && key === "ArrowUp")
  ) {
    return enabledSteps[Math.max(currentIndex - 1, 0)]?.value;
  }

  return undefined;
}

async function resolveNavigationGuard(
  guard: StepperNavigationGuard | undefined
) {
  if (!guard) {
    return true;
  }

  return (await guard()) !== false;
}

function setDisplayName(component: object, displayName: string) {
  (component as { displayName?: string }).displayName = displayName;
}

// ----------------------------------------------------------------------------
// Context
// ----------------------------------------------------------------------------

const StepperContext = React.createContext<StepperContextValue | null>(null);
const StepperItemContext =
  React.createContext<StepperItemContextValue | null>(null);
const StepperListContext = React.createContext(false);

function useStepperContext(component: string) {
  const context = React.useContext(StepperContext);

  if (!context) {
    throw new Error(`${component} must be used within Stepper`);
  }

  return context;
}

function useStepperItemContext(component: string) {
  const context = React.useContext(StepperItemContext);

  if (!context) {
    throw new Error(`${component} must be used within StepperItem`);
  }

  return context;
}

function useStepperListContext() {
  return React.useContext(StepperListContext);
}

// ----------------------------------------------------------------------------
// Internal Hooks
// ----------------------------------------------------------------------------

function useRegisteredSteps() {
  const [registeredSteps, setRegisteredSteps] = React.useState<
    RegisteredStep[]
  >([]);

  const registerStep = React.useCallback((step: RegisteredStep) => {
    setRegisteredSteps((currentSteps) => {
      const existingStepIndex = currentSteps.findIndex(
        (currentStep) => currentStep.id === step.id
      );

      if (existingStepIndex === -1) {
        return [...currentSteps, step];
      }

      const existingStep = currentSteps[existingStepIndex];

      if (
        existingStep.value === step.value &&
        existingStep.disabled === step.disabled
      ) {
        return currentSteps;
      }

      const nextSteps = [...currentSteps];
      nextSteps[existingStepIndex] = step;

      return nextSteps;
    });
  }, []);

  const unregisterStep = React.useCallback((stepId: string) => {
    setRegisteredSteps((currentSteps) =>
      currentSteps.filter((step) => step.id !== stepId)
    );
  }, []);

  return {
    registeredSteps,
    registerStep,
    unregisterStep,
  };
}

function useStepperSteps(
  stepsProp: readonly StepperStepInput[] | undefined,
  registeredSteps: RegisteredStep[]
) {
  const isExplicitMode = stepsProp !== undefined;
  const explicitSteps = React.useMemo(
    () => (isExplicitMode ? normalizeSteps(stepsProp) : undefined),
    [isExplicitMode, stepsProp]
  );
  const steps = React.useMemo(
    () =>
      explicitSteps ??
      registeredSteps.map((step) => ({
        value: step.value,
        disabled: step.disabled,
      })),
    [explicitSteps, registeredSteps]
  );

  return {
    isExplicitMode,
    steps,
  };
}

function useStepperValue({
  value,
  defaultValue,
  onValueChange,
  steps,
}: {
  value: StepperValue | undefined;
  defaultValue: StepperValue | undefined;
  onValueChange: ((value: StepperValue) => void) | undefined;
  steps: StepperStep[];
}) {
  const isControlled = value !== undefined;
  const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);
  const selectedValue = isControlled ? value : uncontrolledValue;
  const currentValue = React.useMemo(
    () => getCurrentStepValue(steps, selectedValue),
    [selectedValue, steps]
  );
  const currentIndex = React.useMemo(
    () =>
      currentValue === undefined
        ? -1
        : steps.findIndex((step) => step.value === currentValue),
    [currentValue, steps]
  );

  const setValue = React.useCallback(
    (nextValue: StepperValue) => {
      const nextStepRecord = getStepByValue(steps, nextValue);

      if (!nextStepRecord || nextStepRecord.disabled) {
        return;
      }

      if (!isControlled) {
        setUncontrolledValue(nextValue);
      }

      onValueChange?.(nextValue);
    },
    [isControlled, onValueChange, steps]
  );

  return {
    currentValue,
    currentIndex,
    setValue,
  };
}

function useStepperNavigation({
  currentValue,
  setValue,
  steps,
}: {
  currentValue: StepperValue | undefined;
  setValue: (value: StepperValue) => void;
  steps: StepperStep[];
}) {
  const previousStep = React.useMemo(
    () => getPreviousEnabledStep(steps, currentValue),
    [currentValue, steps]
  );
  const nextStep = React.useMemo(
    () => getNextEnabledStep(steps, currentValue),
    [currentValue, steps]
  );
  const goPrevious = React.useCallback(() => {
    if (previousStep) {
      setValue(previousStep.value);
    }
  }, [previousStep, setValue]);
  const goNext = React.useCallback(() => {
    if (nextStep) {
      setValue(nextStep.value);
    }
  }, [nextStep, setValue]);

  return {
    canGoPrevious: Boolean(previousStep),
    canGoNext: Boolean(nextStep),
    goPrevious,
    goNext,
  };
}

function useStepperTransition(currentIndex: number) {
  const previousIndexRef = React.useRef(currentIndex);
  const [transition, setTransition] = React.useState(() => ({
    from: currentIndex,
    to: currentIndex,
  }));

  React.useLayoutEffect(() => {
    const previousIndex = previousIndexRef.current;

    if (previousIndex === currentIndex) {
      return;
    }

    setTransition({
      from: previousIndex,
      to: currentIndex,
    });
    previousIndexRef.current = currentIndex;
  }, [currentIndex]);

  return transition;
}

function getStepTransitionDelay({
  from,
  index,
  to,
}: {
  from: number;
  index: number;
  to: number;
}) {
  if (from < 0 || to < 0 || index < 0 || from === to) {
    return 0;
  }

  if (to > from) {
    return index >= from && index < to ? (index - from) * 80 : 0;
  }

  return index >= to && index < from ? (from - index - 1) * 80 : 0;
}

function useDuplicateStepWarning(steps: StepperStep[]) {
  const duplicateStepValues = React.useMemo(
    () => getDuplicateStepValues(steps),
    [steps]
  );
  const duplicateStepValuesKey = duplicateStepValues.join("\0");

  React.useEffect(() => {
    if (!isDevelopment || duplicateStepValues.length === 0) {
      return;
    }

    duplicateStepValues.forEach((stepValue) => {
      warnDev(
        `StepperItem value "${stepValue}" is duplicated. Step values must be unique within a Stepper.`
      );
    });
  }, [duplicateStepValues, duplicateStepValuesKey]);
}

function useNavigationButton({
  canNavigate,
  disabled,
  navigate,
  onBeforeNavigate,
}: {
  canNavigate: boolean;
  disabled: boolean | undefined;
  navigate: () => void;
  onBeforeNavigate: StepperNavigationGuard | undefined;
}) {
  const [isPending, setIsPending] = React.useState(false);
  const pendingRef = React.useRef(false);
  const isDisabled = disabled || !canNavigate || isPending;

  const handleClick = React.useCallback(
    async (
      event: React.MouseEvent<HTMLButtonElement>,
      onClick: React.MouseEventHandler<HTMLButtonElement> | undefined
    ) => {
      onClick?.(event);

      if (isDisabled || pendingRef.current) {
        event.preventDefault();
        return;
      }

      if (event.defaultPrevented) {
        return;
      }

      pendingRef.current = true;
      setIsPending(true);

      try {
        const canCompleteNavigation =
          await resolveNavigationGuard(onBeforeNavigate);

        if (canCompleteNavigation) {
          navigate();
        }
      } finally {
        pendingRef.current = false;
        setIsPending(false);
      }
    },
    [isDisabled, navigate, onBeforeNavigate]
  );

  return {
    isDisabled,
    handleClick,
  };
}

function useStepper<
  TValue extends StepperValue = StepperValue,
>(): StepperApi<TValue> {
  const context = useStepperContext(
    "useStepper"
  ) as unknown as StepperContextValue<TValue>;

  return {
    value: context.value,
    orientation: context.orientation,
    steps: context.steps,
    currentIndex: context.currentIndex,
    totalSteps: context.totalSteps,
    setValue: context.setValue,
    getStepIndex: context.getStepIndex,
    canGoPrevious: context.canGoPrevious,
    canGoNext: context.canGoNext,
    goPrevious: context.goPrevious,
    goNext: context.goNext,
  };
}

function useStepperItem<
  TValue extends StepperValue = StepperValue,
>(): StepperItemApi<TValue> {
  return useStepperItemContext(
    "useStepperItem"
  ) as unknown as StepperItemContextValue<TValue>;
}

// ----------------------------------------------------------------------------
// Root
// ----------------------------------------------------------------------------

function Stepper<const TSteps extends readonly StepperStepInput[]>(
  props: StepperPropsWithSteps<TSteps>
): React.ReactElement;
function Stepper<TValue extends StepperValue = StepperValue>(
  props: StepperPropsWithoutSteps<TValue>
): React.ReactElement;
function Stepper({
  value,
  defaultValue,
  onValueChange,
  orientation = "horizontal",
  steps: stepsProp,
  className,
  children,
  ...props
}: StepperProps) {
  const id = React.useId();
  const { registeredSteps, registerStep, unregisterStep } =
    useRegisteredSteps();
  const { isExplicitMode, steps } = useStepperSteps(
    stepsProp,
    registeredSteps
  );
  const {
    currentValue,
    currentIndex,
    setValue: setStepperValue,
  } = useStepperValue({
    value,
    defaultValue,
    onValueChange,
    steps,
  });
  const { canGoPrevious, canGoNext, goPrevious, goNext } =
    useStepperNavigation({
      currentValue,
      setValue: setStepperValue,
      steps,
    });
  const { from: transitionFromIndex, to: transitionToIndex } =
    useStepperTransition(currentIndex);

  useDuplicateStepWarning(steps);

  const context = React.useMemo<StepperContextValue>(
    () => ({
      value: currentValue,
      orientation,
      steps,
      isExplicitMode,
      currentIndex,
      transitionFromIndex,
      transitionToIndex,
      totalSteps: steps.length,
      registerStep,
      unregisterStep,
      setValue: setStepperValue,
      getStepIndex: (stepValue) =>
        steps.findIndex((step) => step.value === stepValue),
      getTriggerId: (stepValue) => `${id}-trigger-${getSafeId(stepValue)}`,
      getContentId: (stepValue) => `${id}-content-${getSafeId(stepValue)}`,
      canGoPrevious,
      canGoNext,
      goPrevious,
      goNext,
    }),
    [
      canGoNext,
      canGoPrevious,
      currentValue,
      currentIndex,
      transitionFromIndex,
      transitionToIndex,
      goNext,
      goPrevious,
      id,
      isExplicitMode,
      orientation,
      registerStep,
      setStepperValue,
      steps,
      unregisterStep,
    ]
  );

  return (
    <StepperContext.Provider value={context}>
      <div
        data-slot="stepper"
        data-orientation={orientation}
        className={cn("flex flex-col gap-6", className)}
        {...props}
      >
        {children}
      </div>
    </StepperContext.Provider>
  );
}

// ----------------------------------------------------------------------------
// List
// ----------------------------------------------------------------------------

function StepperList({
  className,
  "aria-label": ariaLabel = "Progress steps",
  children,
  ...props
}: StepperListProps) {
  const { orientation } = useStepperContext("StepperList");

  return (
    <StepperListContext.Provider value={true}>
      <ol
        aria-label={props["aria-labelledby"] ? undefined : ariaLabel}
        data-slot="stepper-list"
        data-orientation={orientation}
        className={cn(
          "flex min-w-0",
          "data-[orientation=horizontal]:w-full data-[orientation=horizontal]:gap-0 data-[orientation=horizontal]:overflow-x-auto data-[orientation=horizontal]:pb-2",
          "data-[orientation=vertical]:flex-col data-[orientation=vertical]:gap-4",
          "[&>li:last-child_[data-slot=stepper-separator]]:hidden",
          className
        )}
        {...props}
      >
        {children}
      </ol>
    </StepperListContext.Provider>
  );
}

// ----------------------------------------------------------------------------
// Item
// ----------------------------------------------------------------------------

function StepperItem<TValue extends StepperValue = StepperValue>({
  value,
  completed = false,
  defaultTrigger = true,
  disabled = false,
  error = false,
  separator = true,
  className,
  style,
  children,
  ...props
}: StepperItemProps<TValue>) {
  const {
    value: currentValue,
    orientation,
    steps,
    isExplicitMode,
    transitionFromIndex,
    transitionToIndex,
    registerStep,
    unregisterStep,
    setValue,
    getStepIndex,
    getTriggerId,
    getContentId,
  } = useStepperContext("StepperItem");
  const registrationId = React.useId();
  const isInsideStepperList = useStepperListContext();
  const index = getStepIndex(value);
  const step = getStepByValue(steps, value);
  const isDisabled = step?.disabled ?? disabled;
  const currentIndex =
    currentValue === undefined ? -1 : getStepIndex(currentValue);
  const isActive = currentValue === value;
  const stepPosition: StepperStepPosition =
    currentIndex < 0 || index < 0
      ? "next"
      : index < currentIndex
        ? "previous"
        : index === currentIndex
          ? "current"
          : "next";
  const stepState: StepperStepState = isDisabled
    ? "disabled"
    : error
      ? "error"
      : isActive
        ? "active"
        : completed
          ? "completed"
          : "inactive";
  const renderDefaultTrigger = defaultTrigger;
  const isLastStep = index >= 0 && index === steps.length - 1;
  const shouldRenderSeparator =
    isInsideStepperList && separator && !isLastStep;
  const separatorTransitionDelay = getStepTransitionDelay({
    from: transitionFromIndex,
    index,
    to: transitionToIndex,
  });
  const itemStyle = {
    ...style,
    "--stepper-separator-delay": `${separatorTransitionDelay}ms`,
  } as React.CSSProperties;

  React.useLayoutEffect(() => {
    if (!isInsideStepperList || isExplicitMode) {
      return;
    }

    registerStep({
      id: registrationId,
      value,
      disabled,
    });

    return () => unregisterStep(registrationId);
  }, [
    disabled,
    isExplicitMode,
    isInsideStepperList,
    registerStep,
    registrationId,
    unregisterStep,
    value,
  ]);

  React.useEffect(() => {
    if (!isDevelopment || isInsideStepperList) {
      return;
    }

    const timeout = window.setTimeout(() => {
      warnDev(
        `StepperItem with value "${value}" must be rendered inside StepperList to participate in step order.`
      );
    }, 0);

    return () => window.clearTimeout(timeout);
  }, [isInsideStepperList, value]);

  React.useEffect(() => {
    if (
      !isDevelopment ||
      !isInsideStepperList ||
      index >= 0
    ) {
      return;
    }

    const timeout = window.setTimeout(() => {
      warnDev(
        `StepperItem with value "${value}" could not be found in the Stepper order. Check that it is rendered inside StepperList. For dynamic or conditional step order, pass an explicit steps prop to Stepper.`
      );
    }, 0);

    return () => window.clearTimeout(timeout);
  }, [index, isInsideStepperList, value]);

  const itemContext = React.useMemo<StepperItemContextValue>(
    () => ({
      value,
      index,
      disabled: isDisabled,
      completed,
      isActive,
      stepState,
      stepPosition,
      orientation,
      setValue,
      triggerId: getTriggerId(value),
      contentId: getContentId(value),
    }),
    [
      value,
      index,
      isDisabled,
      completed,
      isActive,
      stepState,
      stepPosition,
      orientation,
      setValue,
      getTriggerId,
      getContentId,
    ]
  );

  return (
    <li
      data-slot="stepper-item"
      data-orientation={orientation}
      data-state={stepState}
      data-position={stepPosition}
      data-disabled={isDisabled ? "" : undefined}
      data-error={error ? "" : undefined}
      data-completed={completed ? "" : undefined}
      className={cn(
        "group/stepper-item relative flex min-w-0",
        "[--stepper-indicator-size:1.75rem] [--stepper-separator-offset:calc(var(--stepper-indicator-size)_/_2)] [--stepper-separator-y:calc(var(--stepper-indicator-size)_/_2)] sm:[--stepper-indicator-size:2.25rem]",
        "data-[orientation=horizontal]:min-w-16 data-[orientation=horizontal]:flex-1 data-[orientation=horizontal]:flex-col data-[orientation=horizontal]:items-center sm:data-[orientation=horizontal]:min-w-28",
        "data-[orientation=vertical]:items-start data-[orientation=vertical]:gap-3",
        className
      )}
      style={itemStyle}
      {...props}
    >
      <StepperItemContext.Provider value={itemContext}>
        {renderDefaultTrigger ? (
          <>
            <StepperTrigger>
              <StepperIndicator />
              <StepperLabel>{children}</StepperLabel>
            </StepperTrigger>
            {shouldRenderSeparator ? <StepperSeparator /> : null}
          </>
        ) : (
          <>
            {children}
            {shouldRenderSeparator ? <StepperSeparator /> : null}
          </>
        )}
      </StepperItemContext.Provider>
    </li>
  );
}

// ----------------------------------------------------------------------------
// Trigger
// ----------------------------------------------------------------------------

function StepperTrigger({
  asChild = false,
  className,
  children,
  disabled,
  onClick,
  onKeyDown,
  tabIndex,
  ...props
}: StepperTriggerProps) {
  const {
    value,
    disabled: itemDisabled,
    isActive,
    stepState,
    orientation,
    setValue,
    triggerId,
    contentId,
  } = useStepperItemContext("StepperTrigger");
  const { steps, getTriggerId } = useStepperContext("StepperTrigger");
  const isDisabled = itemDisabled || disabled;
  const Comp = asChild ? Slot : "button";

  return (
    <Comp
      id={triggerId}
      type={asChild ? undefined : "button"}
      aria-current={isActive ? "step" : undefined}
      aria-controls={isActive ? contentId : undefined}
      aria-disabled={isDisabled ? true : undefined}
      disabled={asChild ? undefined : isDisabled}
      tabIndex={asChild && isDisabled ? -1 : tabIndex}
      data-slot="stepper-trigger"
      data-state={stepState}
      data-disabled={isDisabled ? "" : undefined}
      className={cn(
        "group inline-flex min-h-10 min-w-0 items-center gap-2 rounded-lg text-left text-sm font-medium outline-none",
        "text-muted-foreground transition-[color,background-color,border-color,box-shadow,transform] hover:text-foreground active:scale-[0.96]",
        "focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:outline-none",
        "disabled:pointer-events-none disabled:opacity-50",
        "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
        "data-[state=active]:text-foreground",
        "data-[state=completed]:text-foreground",
        "data-[state=error]:text-destructive",
        orientation === "horizontal" && "w-full flex-col items-center text-center",
        orientation === "vertical" && "justify-start",
        className
      )}
      onClick={(event: React.MouseEvent<HTMLButtonElement>) => {
        onClick?.(event);

        if (isDisabled) {
          event.preventDefault();
          return;
        }

        if (!event.defaultPrevented) {
          setValue(value);
        }
      }}
      onKeyDown={(event: React.KeyboardEvent<HTMLButtonElement>) => {
        onKeyDown?.(event);

        if (event.defaultPrevented || isDisabled) {
          return;
        }

        const targetStepValue = getKeyboardNavigationStepValue({
          key: event.key,
          orientation,
          steps,
          value,
        });

        if (!targetStepValue || targetStepValue === value) {
          return;
        }

        const targetTrigger = document.getElementById(
          getTriggerId(targetStepValue)
        );

        if (targetTrigger instanceof HTMLElement) {
          event.preventDefault();
          targetTrigger.focus();
        }
      }}
      {...props}
    >
      {children}
    </Comp>
  );
}

function StepperIndicator({
  className,
  children,
  ...props
}: StepperIndicatorProps) {
  const { index, stepState } = useStepperItemContext("StepperIndicator");
  const stepNumber = index >= 0 ? index + 1 : undefined;
  const content =
    children !== undefined
      ? children
      : stepState === "error"
        ? "!"
        : stepState === "completed"
          ? "\u2713"
          : stepNumber
            ? stepNumber
            : null;

  return (
    <>
      {stepNumber ? (
        <span className="sr-only">Step {stepNumber}:</span>
      ) : null}
      <span
        aria-hidden="true"
        data-slot="stepper-indicator"
        className={cn(
          "flex size-(--stepper-indicator-size) shrink-0 items-center justify-center rounded-full border border-border bg-background text-xs font-semibold text-muted-foreground shadow-sm",
          "transition-[color,background-color,border-color,box-shadow,transform]",
          "group-hover:border-foreground/30",
          "group-data-[state=active]:border-primary group-data-[state=active]:bg-primary group-data-[state=active]:text-primary-foreground group-data-[state=active]:shadow-md",
          "group-data-[state=completed]:border-primary group-data-[state=completed]:bg-primary group-data-[state=completed]:text-primary-foreground",
          "group-data-[state=error]:border-destructive group-data-[state=error]:bg-destructive group-data-[state=error]:text-destructive-foreground",
          "group-data-[position=previous]:text-foreground",
          "[transition-delay:var(--stepper-separator-delay)] motion-reduce:[transition-delay:0ms]",
          "[&>svg]:size-3.5 [&>svg]:shrink-0 sm:[&>svg]:size-4",
          className
        )}
        {...props}
      >
        {content}
      </span>
    </>
  );
}

function StepperLabel({ className, ...props }: StepperLabelProps) {
  const { orientation } = useStepperItemContext("StepperLabel");

  return (
    <span
      data-slot="stepper-label"
      className={cn(
        "min-w-0 text-xs leading-tight font-medium sm:text-sm",
        orientation === "horizontal" && "max-w-40 text-balance",
        className
      )}
      {...props}
    />
  );
}

function StepperDescription({ className, ...props }: StepperDescriptionProps) {
  const { orientation } = useStepperItemContext("StepperDescription");

  return (
    <span
      data-slot="stepper-description"
      className={cn(
        "text-xs leading-snug font-normal text-muted-foreground",
        orientation === "horizontal" && "max-w-44 text-balance",
        className
      )}
      {...props}
    />
  );
}

function StepperSeparator({ className, ...props }: StepperSeparatorProps) {
  const { orientation } = useStepperItemContext("StepperSeparator");

  return (
    <span
      aria-hidden="true"
      data-slot="stepper-separator"
      className={cn(
        "overflow-hidden bg-muted-foreground/25 after:absolute after:inset-0 after:bg-primary after:content-['']",
        "after:transition-transform after:duration-[220ms] after:ease-out after:[transition-delay:var(--stepper-separator-delay)] motion-reduce:after:transition-none motion-reduce:after:[transition-delay:0ms]",
        orientation === "horizontal" &&
          "absolute left-[calc(50%_+_var(--stepper-separator-offset))] right-[calc(-50%_+_var(--stepper-separator-offset))] top-(--stepper-separator-y) h-px after:origin-left after:scale-x-0 group-data-[completed]/stepper-item:after:scale-x-100 group-data-[position=previous]/stepper-item:after:scale-x-100 group-data-[state=completed]/stepper-item:after:scale-x-100",
        orientation === "vertical" &&
          "absolute left-[calc(var(--stepper-indicator-size)_/_2)] top-[calc(var(--stepper-indicator-size)_+_0.5rem)] h-[calc(100%_-_var(--stepper-indicator-size)_+_0.75rem)] w-px after:origin-top after:scale-y-0 group-data-[completed]/stepper-item:after:scale-y-100 group-data-[position=previous]/stepper-item:after:scale-y-100 group-data-[state=completed]/stepper-item:after:scale-y-100",
        className
      )}
      {...props}
    />
  );
}

// ----------------------------------------------------------------------------
// Content
// ----------------------------------------------------------------------------

function StepperContent<TValue extends StepperValue = StepperValue>({
  value,
  forceMount = false,
  keepMounted = false,
  asChild = false,
  className,
  children,
  ...props
}: StepperContentProps<TValue>) {
  const {
    value: currentValue,
    steps,
    getTriggerId,
    getContentId,
  } = useStepperContext("StepperContent");
  const isActive = currentValue === value;
  const hasMatchingStep = steps.some((step) => step.value === value);
  const shouldKeepMounted = forceMount || keepMounted;

  React.useEffect(() => {
    if (!isDevelopment || hasMatchingStep) {
      return;
    }

    const timeout = window.setTimeout(() => {
      warnDev(
        `StepperContent value "${value}" does not match any StepperItem. Content values should map to a step value.`
      );
    }, 0);

    return () => window.clearTimeout(timeout);
  }, [hasMatchingStep, value]);

  if (!shouldKeepMounted && !isActive) {
    return null;
  }

  const Comp = asChild ? Slot : "div";

  return (
    <Comp
      id={getContentId(value)}
      role="region"
      aria-labelledby={getTriggerId(value)}
      data-slot="stepper-content"
      data-state={isActive ? "active" : "inactive"}
      hidden={shouldKeepMounted ? !isActive : undefined}
      className={cn(
        "rounded-lg border border-border/70 bg-muted/25 p-4 text-sm text-foreground shadow-sm",
        className
      )}
      {...props}
    >
      {children}
    </Comp>
  );
}

// ----------------------------------------------------------------------------
// Navigation
// ----------------------------------------------------------------------------

function StepperPrevious({
  asChild = false,
  className,
  children = "Previous",
  disabled,
  onBeforePrevious,
  onClick,
  tabIndex,
  ...props
}: StepperPreviousProps) {
  const { canGoPrevious, goPrevious } = useStepperContext("StepperPrevious");
  const { handleClick, isDisabled } = useNavigationButton({
    canNavigate: canGoPrevious,
    disabled,
    navigate: goPrevious,
    onBeforeNavigate: onBeforePrevious,
  });
  const Comp = asChild ? Slot : "button";

  return (
    <Comp
      type={asChild ? undefined : "button"}
      disabled={asChild ? undefined : isDisabled}
      aria-disabled={isDisabled ? true : undefined}
      tabIndex={asChild && isDisabled ? -1 : tabIndex}
      data-slot="stepper-previous"
      data-disabled={isDisabled ? "" : undefined}
      className={
        asChild
          ? className
          : cn(
              "inline-flex h-9 min-w-24 items-center justify-center gap-2 rounded-none border border-border bg-background px-3 text-sm font-medium text-foreground",
              "transition-[color,background-color,border-color,box-shadow,transform] hover:border-foreground/20 hover:bg-muted hover:text-foreground active:scale-[0.97]",
              "focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:outline-none",
              "disabled:pointer-events-none disabled:opacity-50",
              "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
              "[&>svg]:size-4 [&>svg]:shrink-0",
              className
            )
      }
      onClick={(event: React.MouseEvent<HTMLButtonElement>) =>
        handleClick(event, onClick)
      }
      {...props}
    >
      {children}
    </Comp>
  );
}

function StepperNext({
  asChild = false,
  className,
  children = "Next",
  disabled,
  onBeforeNext,
  onClick,
  tabIndex,
  ...props
}: StepperNextProps) {
  const { canGoNext, goNext } = useStepperContext("StepperNext");
  const { handleClick, isDisabled } = useNavigationButton({
    canNavigate: canGoNext,
    disabled,
    navigate: goNext,
    onBeforeNavigate: onBeforeNext,
  });
  const Comp = asChild ? Slot : "button";

  return (
    <Comp
      type={asChild ? undefined : "button"}
      disabled={asChild ? undefined : isDisabled}
      aria-disabled={isDisabled ? true : undefined}
      tabIndex={asChild && isDisabled ? -1 : tabIndex}
      data-slot="stepper-next"
      data-disabled={isDisabled ? "" : undefined}
      className={
        asChild
          ? className
          : cn(
              "inline-flex h-9 min-w-24 items-center justify-center gap-2 rounded-none border border-primary bg-primary px-3 text-sm font-medium text-primary-foreground",
              "transition-[background-color,border-color,box-shadow,transform] hover:bg-primary/90 active:scale-[0.97]",
              "focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:outline-none",
              "disabled:pointer-events-none disabled:opacity-50",
              "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
              "[&>svg]:size-4 [&>svg]:shrink-0",
              className
            )
      }
      onClick={(event: React.MouseEvent<HTMLButtonElement>) =>
        handleClick(event, onClick)
      }
      {...props}
    >
      {children}
    </Comp>
  );
}

// ----------------------------------------------------------------------------
// Exports
// ----------------------------------------------------------------------------

setDisplayName(Stepper, "Stepper");
setDisplayName(StepperList, "StepperList");
setDisplayName(StepperItem, "StepperItem");
setDisplayName(StepperTrigger, "StepperTrigger");
setDisplayName(StepperIndicator, "StepperIndicator");
setDisplayName(StepperLabel, "StepperLabel");
setDisplayName(StepperDescription, "StepperDescription");
setDisplayName(StepperSeparator, "StepperSeparator");
setDisplayName(StepperContent, "StepperContent");
setDisplayName(StepperPrevious, "StepperPrevious");
setDisplayName(StepperNext, "StepperNext");

export {
  Stepper,
  StepperContent,
  StepperDescription,
  StepperIndicator,
  StepperItem,
  StepperLabel,
  StepperList,
  StepperNext,
  StepperPrevious,
  StepperSeparator,
  StepperTrigger,
  useStepper,
  useStepperItem,
};

export type {
  StepperApi,
  StepperButtonProps,
  StepperContentProps,
  StepperDescriptionProps,
  StepperIndicatorProps,
  StepperItemApi,
  StepperItemProps,
  StepperLabelProps,
  StepperListProps,
  StepperNavigationGuard,
  StepperNextProps,
  StepperOrientation,
  StepperPreviousProps,
  StepperProps,
  StepperSeparatorProps,
  StepperStep,
  StepperStepInput,
  StepperStepPosition,
  StepperStepState,
  StepperStepsValue,
  StepperTriggerProps,
  StepperValue,
};
```

### `registry/default/examples/stepper-intent-onboarding.tsx`

```tsx
"use client";

import * as React from "react";
import {
  ArrowRight,
  BookOpen,
  BriefcaseBusiness,
  Check,
  Circle,
  CircleHelp,
  Home,
  ImagePlus,
  KeyRound,
  Laptop,
  ListChecks,
  LoaderCircle,
  Mail,
  Monitor,
  Plane,
  School,
  Sparkles,
  SquareCheckBig,
  UsersRound,
  type LucideIcon,
} from "lucide-react";
import {
  useForm,
  useWatch,
  type FieldPath,
  type UseFormRegisterReturn,
} from "react-hook-form";
import { SiGoogle } from "react-icons/si";
import { z } from "zod/v3";

import { StepperLogo } from "@/components/stepper-logo";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Field,
  FieldDescription,
  FieldError,
  FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import {
  Stepper,
  StepperContent,
  StepperItem,
  StepperList,
} from "@/components/ui/stepper";
import { cn } from "@/lib/utils";

const emailStepSchema = z.object({
  email: z.string().trim().email("Enter a valid work email."),
});

const verificationStepSchema = z.object({
  code: z
    .string()
    .trim()
    .regex(/^\d{6}$/, "Enter the 6-digit verification code."),
});

const profileStepSchema = z.object({
  name: z.string().trim().min(2, "Enter your name."),
  password: z.string().min(8, "Use at least 8 characters."),
});

const intentStepSchema = z.object({
  intent: z.enum(["work", "personal", "school"], {
    required_error: "Choose what you are setting up.",
  }),
});

const collaborationStepSchema = z.object({
  collaboration: z.enum(["with-others", "on-my-own"], {
    required_error: "Choose a collaboration mode.",
  }),
});

const onboardingStepValues = [
  "account",
  "verify",
  "profile",
  "intent",
  "collaboration",
  "interests",
  "generating",
  "workspace",
] as const;

type OnboardingStep = (typeof onboardingStepValues)[number];
type IntentValue = z.infer<typeof intentStepSchema>["intent"];
type CollaborationValue = z.infer<
  typeof collaborationStepSchema
>["collaboration"];
type InterestValue =
  | "career"
  | "books"
  | "todo"
  | "site"
  | "travel"
  | "projects"
  | "finance"
  | "food"
  | "hobbies"
  | "habits";

type IntentOnboardingValues = {
  email: string;
  code: string;
  name: string;
  password: string;
  intent?: IntentValue;
  collaboration?: CollaborationValue;
  interests: InterestValue[];
  marketingOptOut: boolean;
};

type StepDefinition = {
  value: OnboardingStep;
  label: string;
};

const onboardingSteps = [
  { value: "account", label: "Create account" },
  { value: "verify", label: "Verify email" },
  { value: "profile", label: "Create profile" },
  { value: "intent", label: "Choose intent" },
  { value: "collaboration", label: "Choose collaboration" },
  { value: "interests", label: "Choose interests" },
  { value: "generating", label: "Prepare setup" },
  { value: "workspace", label: "Signed in" },
] satisfies StepDefinition[];

const intentOptions = [
  {
    value: "work",
    title: "For work",
    description: "Track projects, goals, and meeting notes",
    icon: BriefcaseBusiness,
  },
  {
    value: "personal",
    title: "For personal life",
    description: "Write better, think more clearly, stay organized",
    icon: Home,
  },
  {
    value: "school",
    title: "For school",
    description: "Keep notes, research, and tasks in one place",
    icon: School,
  },
] satisfies Array<{
  value: IntentValue;
  title: string;
  description: string;
  icon: LucideIcon;
}>;

const collaborationOptions = [
  {
    value: "with-others",
    title: "With others",
    description: "Plan travel, household chores, group activities",
    icon: UsersRound,
  },
  {
    value: "on-my-own",
    title: "On my own",
    description: "Manage expenses, track hobbies, stay organized",
    icon: CircleHelp,
  },
] satisfies Array<{
  value: CollaborationValue;
  title: string;
  description: string;
  icon: LucideIcon;
}>;

const interestOptions = [
  { value: "career", label: "Career building", icon: BriefcaseBusiness },
  { value: "books", label: "Books and media", icon: Monitor },
  { value: "todo", label: "To-do list", icon: ListChecks },
  { value: "site", label: "Site or blog", icon: Laptop },
  { value: "travel", label: "Travel", icon: Plane },
  { value: "projects", label: "Project tracking", icon: SquareCheckBig },
  { value: "finance", label: "Personal finance", icon: Circle },
  { value: "food", label: "Food and nutrition", icon: Home },
  { value: "hobbies", label: "Hobbies", icon: BookOpen },
  { value: "habits", label: "Habit tracking", icon: Check },
] satisfies Array<{
  value: InterestValue;
  label: string;
  icon: LucideIcon;
}>;

const stepFields = {
  account: ["email"],
  verify: ["code"],
  profile: ["name", "password"],
  intent: ["intent"],
  collaboration: ["collaboration"],
  interests: [],
  generating: [],
  workspace: [],
} satisfies Record<OnboardingStep, FieldPath<IntentOnboardingValues>[]>;

function StepperIntentOnboardingExample() {
  const [step, setStep] = React.useState<OnboardingStep>("account");
  const [completedSteps, setCompletedSteps] = React.useState<
    Partial<Record<OnboardingStep, boolean>>
  >({});
  const [isGenerating, setIsGenerating] = React.useState(false);
  const generationTimeoutRef = React.useRef<number | null>(null);
  const form = useForm<IntentOnboardingValues>({
    mode: "onChange",
    defaultValues: {
      email: "",
      code: "",
      name: "",
      password: "",
      intent: undefined,
      collaboration: undefined,
      interests: [],
      marketingOptOut: false,
    },
  });
  const watchedValues = useWatch({ control: form.control });
  const values = React.useMemo<IntentOnboardingValues>(
    () => ({
      email: watchedValues.email ?? "",
      code: watchedValues.code ?? "",
      name: watchedValues.name ?? "",
      password: watchedValues.password ?? "",
      intent: watchedValues.intent,
      collaboration: watchedValues.collaboration,
      interests: watchedValues.interests ?? [],
      marketingOptOut: watchedValues.marketingOptOut ?? false,
    }),
    [
      watchedValues.code,
      watchedValues.collaboration,
      watchedValues.email,
      watchedValues.intent,
      watchedValues.interests,
      watchedValues.marketingOptOut,
      watchedValues.name,
      watchedValues.password,
    ]
  );
  const currentIndex = getVisibleStepIndex(step, values);
  const totalVisibleSteps = getVisibleSteps(values).length;
  const progress = Math.round(((currentIndex + 1) / totalVisibleSteps) * 100);

  React.useEffect(() => {
    return () => {
      if (generationTimeoutRef.current) {
        window.clearTimeout(generationTimeoutRef.current);
      }
    };
  }, []);

  async function continueFromStep() {
    const isValid = validateCurrentStep(step, form);

    if (!isValid) {
      return;
    }

    setCompletedSteps((current) => ({ ...current, [step]: true }));

    if (step === "interests") {
      startGeneration();
      return;
    }

    const nextStep = getNextStep(step, values);

    if (nextStep) {
      setStep(nextStep);
    }
  }

  function continueWithProvider(provider: string) {
    const providerEmail =
      provider === "Google" ? "alex.smith@example.com" : "alex@example.com";

    form.clearErrors(["email", "code"]);
    form.setValue("email", providerEmail, {
      shouldDirty: true,
      shouldTouch: true,
      shouldValidate: true,
    });
    setCompletedSteps((current) => ({
      ...current,
      account: true,
      verify: true,
    }));
    setStep("profile");
  }

  function resetFlow() {
    if (generationTimeoutRef.current) {
      window.clearTimeout(generationTimeoutRef.current);
      generationTimeoutRef.current = null;
    }

    setIsGenerating(false);
    setCompletedSteps({});
    form.reset();
    setStep("account");
  }

  function selectIntent(intent: IntentValue) {
    form.clearErrors("intent");
    form.setValue("intent", intent, {
      shouldDirty: true,
      shouldTouch: true,
      shouldValidate: true,
    });
    setCompletedSteps((current) => ({
      ...current,
      intent: false,
      collaboration: false,
    }));
  }

  function selectCollaboration(collaboration: CollaborationValue) {
    form.clearErrors("collaboration");
    form.setValue("collaboration", collaboration, {
      shouldDirty: true,
      shouldTouch: true,
      shouldValidate: true,
    });
  }

  function toggleInterest(interest: InterestValue) {
    const currentInterests = values.interests;
    const nextInterests = currentInterests.includes(interest)
      ? currentInterests.filter((item) => item !== interest)
      : [...currentInterests, interest];

    form.setValue("interests", nextInterests, {
      shouldDirty: true,
      shouldTouch: true,
    });
  }

  function skipInterests() {
    form.setValue("interests", [], {
      shouldDirty: true,
      shouldTouch: true,
    });
    setCompletedSteps((current) => ({ ...current, interests: true }));
    startGeneration();
  }

  function startGeneration() {
    setIsGenerating(true);
    setStep("generating");

    if (generationTimeoutRef.current) {
      window.clearTimeout(generationTimeoutRef.current);
    }

    generationTimeoutRef.current = window.setTimeout(() => {
      setIsGenerating(false);
      setCompletedSteps((current) => ({
        ...current,
        interests: true,
        generating: true,
        workspace: true,
      }));
      setStep("workspace");
    }, 700);
  }

  return (
    <Stepper
      value={step}
      onValueChange={(nextStep) => setStep(nextStep as OnboardingStep)}
      className="min-w-0 gap-0 overflow-hidden rounded-xl border border-border bg-background text-foreground shadow-sm"
    >
      <div className="h-1 bg-muted">
        <div
          className="h-full bg-foreground transition-[width] duration-300 ease-out"
          style={{ width: `${progress}%` }}
        />
      </div>

      <div className="flex min-h-[560px] flex-col">
        <HiddenStepList completedSteps={completedSteps} values={values} />

        <StepperContent value="account" className="border-0 bg-transparent p-0 shadow-none">
          <CenteredStage>
            <AuthPanel
              email={values.email}
              error={form.formState.errors.email?.message}
              registerEmail={form.register("email", {
                onChange: () => form.clearErrors("email"),
              })}
              onContinue={() => void continueFromStep()}
              onGoogle={() => continueWithProvider("Google")}
            />
          </CenteredStage>
        </StepperContent>

        <StepperContent value="verify" className="border-0 bg-transparent p-0 shadow-none">
          <CenteredStage>
            <VerifyPanel
              email={values.email}
              error={form.formState.errors.code?.message}
              registerCode={form.register("code", {
                onChange: () => form.clearErrors("code"),
              })}
              onContinue={() => void continueFromStep()}
            />
          </CenteredStage>
        </StepperContent>

        <StepperContent value="profile" className="border-0 bg-transparent p-0 shadow-none">
          <CenteredStage>
            <ProfilePanel
              values={values}
              errors={{
                name: form.formState.errors.name?.message,
                password: form.formState.errors.password?.message,
              }}
              registerName={form.register("name", {
                onChange: () => form.clearErrors("name"),
              })}
              registerPassword={form.register("password", {
                onChange: () => form.clearErrors("password"),
              })}
              marketingOptOut={values.marketingOptOut}
              onMarketingChange={(checked) =>
                form.setValue("marketingOptOut", checked, {
                  shouldDirty: true,
                  shouldTouch: true,
                })
              }
              onContinue={() => void continueFromStep()}
            />
          </CenteredStage>
        </StepperContent>

        <StepperContent value="intent" className="border-0 bg-transparent p-0 shadow-none">
          <CenteredStage>
            <IntentPanel
              value={values.intent}
              error={form.formState.errors.intent?.message}
              onSelect={selectIntent}
              onContinue={() => void continueFromStep()}
            />
          </CenteredStage>
        </StepperContent>

        <StepperContent
          value="collaboration"
          className="border-0 bg-transparent p-0 shadow-none"
        >
          <CenteredStage>
            <CollaborationPanel
              value={values.collaboration}
              error={form.formState.errors.collaboration?.message}
              onSelect={selectCollaboration}
              onContinue={() => void continueFromStep()}
            />
          </CenteredStage>
        </StepperContent>

        <StepperContent value="interests" className="border-0 bg-transparent p-0 shadow-none">
          <CenteredStage>
            <InterestsPanel
              selectedInterests={values.interests}
              onToggleInterest={toggleInterest}
              onContinue={() => void continueFromStep()}
              onSkip={skipInterests}
            />
          </CenteredStage>
        </StepperContent>

        <StepperContent value="generating" className="border-0 bg-transparent p-0 shadow-none">
          <CenteredStage>
            <GeneratingPanel values={values} isGenerating={isGenerating} />
          </CenteredStage>
        </StepperContent>

        <StepperContent value="workspace" className="border-0 bg-transparent p-0 shadow-none">
          <SignedInPanel values={values} onRestart={resetFlow} />
        </StepperContent>
      </div>
    </Stepper>
  );
}

function HiddenStepList({
  completedSteps,
  values,
}: {
  completedSteps: Partial<Record<OnboardingStep, boolean>>;
  values: IntentOnboardingValues;
}) {
  return (
    <StepperList
      aria-label="Intent onboarding flow steps"
      className="sr-only !w-px !gap-0 !overflow-hidden !pb-0 data-[orientation=horizontal]:!w-px data-[orientation=horizontal]:!gap-0 data-[orientation=horizontal]:!overflow-hidden data-[orientation=horizontal]:!pb-0"
    >
      {onboardingSteps.map((item) => (
        <StepperItem
          key={item.value}
          value={item.value}
          completed={Boolean(completedSteps[item.value])}
          disabled={isStepSkipped(item.value, values)}
        >
          {item.label}
        </StepperItem>
      ))}
    </StepperList>
  );
}

function CenteredStage({ children }: { children: React.ReactNode }) {
  return (
    <div className="flex min-h-[560px] flex-1 items-start justify-center px-5 pb-10 pt-14 sm:px-6 sm:pt-16">
      {children}
    </div>
  );
}

function AuthPanel({
  email,
  error,
  registerEmail,
  onContinue,
  onGoogle,
}: {
  email: string;
  error?: string;
  registerEmail: UseFormRegisterReturn<"email">;
  onContinue: () => void;
  onGoogle: () => void;
}) {
  return (
    <div className="w-full max-w-[380px]">
      <StageHeading
        title="Create your account"
        description="Start with a focused onboarding flow."
        align="center"
        logo
      />

      <ProviderButton onClick={onGoogle} />

      <div className="my-5 flex items-center gap-3 text-xs font-medium text-muted-foreground">
        <Separator className="flex-1 bg-border" />
        or
        <Separator className="flex-1 bg-border" />
      </div>

      <Field data-invalid={Boolean(error)}>
        <FieldLabel htmlFor="intent-email" className="text-muted-foreground">
          Work email
        </FieldLabel>
        <div className="relative">
          <Input
            id="intent-email"
            type="email"
            placeholder="Enter your email address..."
            autoComplete="email"
            aria-invalid={Boolean(error)}
            className="h-10 rounded-md border-border bg-background pr-10 text-sm shadow-none focus-visible:ring-ring"
            {...registerEmail}
          />
          {email ? (
            <span className="absolute top-1/2 right-3 -translate-y-1/2 rounded-full bg-foreground p-0.5 text-background">
              <Check className="size-3" />
            </span>
          ) : (
            <Mail className="absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground" />
          )}
        </div>
        {error ? (
          <FieldError>{error}</FieldError>
        ) : (
          <FieldDescription className="text-muted-foreground">
            Use an organization email to easily collaborate with teammates.
          </FieldDescription>
        )}
      </Field>

      <PrimaryAction className="mt-6" onClick={onContinue}>
        Continue
      </PrimaryAction>

      <p className="mt-5 text-center text-xs leading-5 text-muted-foreground">
        By continuing, you acknowledge that you understand and agree to the{" "}
        <span className="underline decoration-border underline-offset-2">
          Terms & Conditions
        </span>{" "}
        and{" "}
        <span className="underline decoration-border underline-offset-2">
          Privacy Policy
        </span>
        .
      </p>
    </div>
  );
}

function VerifyPanel({
  email,
  error,
  registerCode,
  onContinue,
}: {
  email: string;
  error?: string;
  registerCode: UseFormRegisterReturn<"code">;
  onContinue: () => void;
}) {
  return (
    <div className="w-full max-w-[380px]">
      <StageHeading
        title="Check your inbox"
        description="Use the code sent to your email."
        align="center"
      />

      <div className="mt-7 rounded-lg border border-border bg-muted/40 p-3 text-sm text-muted-foreground">
        We sent a verification code to{" "}
        <span className="font-medium text-foreground">{email}</span>.
      </div>

      <Field data-invalid={Boolean(error)} className="mt-5">
        <FieldLabel htmlFor="intent-code" className="text-muted-foreground">
          Verification code
        </FieldLabel>
        <Input
          id="intent-code"
          inputMode="numeric"
          placeholder="Enter code"
          aria-invalid={Boolean(error)}
          className="h-10 rounded-md border-border bg-background font-mono text-sm shadow-none focus-visible:ring-ring"
          {...registerCode}
        />
        {error ? (
          <FieldError>{error}</FieldError>
        ) : (
          <FieldDescription className="text-muted-foreground">
            Use 123456 for this demo.
          </FieldDescription>
        )}
      </Field>

      <PrimaryAction className="mt-6" onClick={onContinue}>
        Continue
      </PrimaryAction>
      <p className="mt-4 text-center text-sm text-muted-foreground">
        Resend in 25s
      </p>
    </div>
  );
}

function ProfilePanel({
  values,
  errors,
  registerName,
  registerPassword,
  marketingOptOut,
  onMarketingChange,
  onContinue,
}: {
  values: IntentOnboardingValues;
  errors: { name?: string; password?: string };
  registerName: UseFormRegisterReturn<"name">;
  registerPassword: UseFormRegisterReturn<"password">;
  marketingOptOut: boolean;
  onMarketingChange: (checked: boolean) => void;
  onContinue: () => void;
}) {
  return (
    <div className="w-full max-w-[380px]">
      <StageHeading
        title="Create a profile"
        description="This is how you'll appear."
        align="center"
      />

      <div className="mt-9 flex flex-col items-center gap-3 text-sm text-muted-foreground">
        <div className="grid size-14 place-items-center rounded-full border border-border bg-muted/50 text-base font-semibold text-foreground">
          {getInitials(values.name || values.email)}
        </div>
        <button
          type="button"
          className="inline-flex items-center gap-1 hover:text-foreground"
        >
          <ImagePlus className="size-4" />
          Add a photo
        </button>
      </div>

      <div className="mt-9 grid gap-4">
        <Field data-invalid={Boolean(errors.name)}>
          <FieldLabel htmlFor="intent-name" className="text-muted-foreground">
            Enter your name
          </FieldLabel>
          <Input
            id="intent-name"
            placeholder="Name"
            autoComplete="name"
            aria-invalid={Boolean(errors.name)}
            className="h-10 rounded-md border-border bg-background text-sm shadow-none focus-visible:ring-ring"
            {...registerName}
          />
          {errors.name ? <FieldError>{errors.name}</FieldError> : null}
        </Field>

        <Field data-invalid={Boolean(errors.password)}>
          <FieldLabel htmlFor="intent-password" className="text-muted-foreground">
            Set a password
          </FieldLabel>
          <div className="relative">
            <Input
              id="intent-password"
              type="password"
              placeholder="Password"
              autoComplete="new-password"
              aria-invalid={Boolean(errors.password)}
              className="h-10 rounded-md border-border bg-background pr-10 text-sm shadow-none focus-visible:ring-ring"
              {...registerPassword}
            />
            <KeyRound className="absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground" />
          </div>
          {errors.password ? (
            <FieldError>{errors.password}</FieldError>
          ) : null}
        </Field>
      </div>

      <PrimaryAction className="mt-9" onClick={onContinue}>
        Continue
      </PrimaryAction>

      <label className="mt-4 flex items-start gap-2 text-xs leading-5 text-muted-foreground">
        <Checkbox
          checked={marketingOptOut}
          onCheckedChange={(checked) => onMarketingChange(checked === true)}
          className="mt-0.5 border-border"
        />
        No, I do not want to receive marketing communications.
      </label>
    </div>
  );
}

function IntentPanel({
  value,
  error,
  onSelect,
  onContinue,
}: {
  value?: IntentValue;
  error?: string;
  onSelect: (value: IntentValue) => void;
  onContinue: () => void;
}) {
  return (
    <div className="w-full max-w-[520px]">
      <StageHeading
        title="What are you setting up?"
        description="This helps customize your experience"
        align="center"
      />

      <div className="mt-8 grid gap-3">
        {intentOptions.map((option) => (
          <LargeOptionButton
            key={option.value}
            active={value === option.value}
            icon={option.icon}
            title={option.title}
            description={option.description}
            onClick={() => onSelect(option.value)}
          />
        ))}
      </div>

      {error ? (
        <p className="mt-3 text-center text-sm text-destructive">{error}</p>
      ) : null}

      <PrimaryAction className="mt-7" onClick={onContinue}>
        Continue
      </PrimaryAction>
    </div>
  );
}

function CollaborationPanel({
  value,
  error,
  onSelect,
  onContinue,
}: {
  value?: CollaborationValue;
  error?: string;
  onSelect: (value: CollaborationValue) => void;
  onContinue: () => void;
}) {
  return (
    <div className="w-full max-w-[500px]">
      <StageHeading
        title="How will you work?"
        description="Pick the mode that best fits this setup"
        align="center"
      />

      <div className="mt-8 grid gap-3 sm:grid-cols-2">
        {collaborationOptions.map((option) => (
          <TileOptionButton
            key={option.value}
            active={value === option.value}
            icon={option.icon}
            title={option.title}
            description={option.description}
            onClick={() => onSelect(option.value)}
          />
        ))}
      </div>

      {error ? (
        <p className="mt-3 text-center text-sm text-destructive">{error}</p>
      ) : null}

      <PrimaryAction className="mt-7" onClick={onContinue}>
        Continue
      </PrimaryAction>
    </div>
  );
}

function InterestsPanel({
  selectedInterests,
  onToggleInterest,
  onContinue,
  onSkip,
}: {
  selectedInterests: InterestValue[];
  onToggleInterest: (value: InterestValue) => void;
  onContinue: () => void;
  onSkip: () => void;
}) {
  return (
    <div className="w-full max-w-[520px] text-center">
      <StageHeading
        title="Choose starter goals"
        description="Stepper will prepare the optional path after sign-in."
        align="center"
      />

      <p className="mb-3 mt-7 text-xs font-medium text-muted-foreground">
        {selectedInterests.length} selected
      </p>

      <div className="flex flex-wrap justify-center gap-2.5">
        {interestOptions.map((option) => (
          <InterestChip
            key={option.value}
            option={option}
            active={selectedInterests.includes(option.value)}
            onClick={() => onToggleInterest(option.value)}
          />
        ))}
      </div>

      <PrimaryAction className="mx-auto mt-10 max-w-[380px]" onClick={onContinue}>
        Continue
      </PrimaryAction>
      <button
        type="button"
        className="mx-auto mt-4 block w-full max-w-[380px] rounded-md py-1 text-sm font-medium text-muted-foreground hover:text-foreground"
        onClick={onSkip}
      >
        Skip for now
      </button>
    </div>
  );
}

function GeneratingPanel({
  values,
  isGenerating,
}: {
  values: IntentOnboardingValues;
  isGenerating: boolean;
}) {
  const items = [
    "Choosing starter pages",
    "Mapping your interests",
    "Preparing a completion checklist",
  ];

  return (
    <div className="w-full max-w-[460px] text-center">
      <div className="mx-auto grid size-14 place-items-center rounded-xl border border-border bg-muted/40 shadow-sm">
        <Sparkles className="size-6 text-foreground" />
      </div>
      <h2 className="mt-6 text-xl font-semibold tracking-tight text-foreground">
        Preparing your setup
      </h2>
      <p className="mt-3 text-sm font-medium text-muted-foreground">
        {values.intent
          ? `Tuned for ${getIntentLabel(values.intent).toLowerCase()}.`
          : "Tuned for your workflow."}
      </p>

      <div className="mt-8 rounded-xl border border-border bg-muted/30 p-4 text-left">
        {items.map((item, index) => (
          <div key={item} className="flex items-center gap-3 py-2">
            <span className="grid size-7 place-items-center rounded-full bg-background text-muted-foreground">
              {index === items.length - 1 && isGenerating ? (
                <LoaderCircle className="size-4 animate-spin" />
              ) : (
                <Check className="size-4" />
              )}
            </span>
            <span className="text-sm font-medium text-foreground">{item}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function SignedInPanel({
  values,
  onRestart,
}: {
  values: IntentOnboardingValues;
  onRestart: () => void;
}) {
  const selectedLabels = values.interests
    .map(
      (interest) =>
        interestOptions.find((item) => item.value === interest)?.label
    )
    .filter(Boolean)
    .slice(0, 2);

  return (
    <div className="flex min-h-[560px] flex-1 items-center justify-center px-5 py-12 sm:px-6">
      <div className="w-full max-w-[360px] overflow-hidden rounded-xl border border-border bg-card text-left">
        <div className="grid h-28 place-items-center border-b border-border bg-background">
          <OnboardingLogo className="size-12 text-foreground" />
        </div>

        <div className="p-4 sm:p-5">
          <h2 className="text-base font-semibold text-foreground">
            You&apos;re signed in
          </h2>
          <p className="mt-2 text-sm leading-6 text-muted-foreground">
            Stepper completed the account, intent, and optional setup path
            {values.email ? ` for ${values.email}` : ""}.
          </p>

          <div className="mt-5 grid gap-2 text-sm">
            <ChecklistRow checked text="Account verified" />
            <ChecklistRow checked text={getIntentSummary(values)} />
            <ChecklistRow
              checked
              text={
                selectedLabels.length > 0
                  ? `Prepared ${selectedLabels.join(" and ")}`
                  : "Skipped optional goals"
              }
            />
          </div>

          <PrimaryAction className="mt-6" onClick={onRestart}>
            Run again
          </PrimaryAction>
        </div>
      </div>
    </div>
  );
}

function StageHeading({
  title,
  description,
  align = "left",
  logo = false,
}: {
  title: string;
  description: string;
  align?: "left" | "center";
  logo?: boolean;
}) {
  return (
    <div className={cn(align === "center" && "text-center")}>
      {logo ? <OnboardingLogo className="mx-auto mb-5 size-10" /> : null}
      <h2 className="text-xl font-semibold tracking-tight text-foreground sm:text-2xl">
        {title}
      </h2>
      <p className="mt-1.5 text-sm leading-6 text-muted-foreground">
        {description}
      </p>
    </div>
  );
}

function OnboardingLogo({ className }: { className?: string }) {
  return <StepperLogo className={cn("size-10 text-foreground", className)} />;
}

function ProviderButton({ onClick }: { onClick: () => void }) {
  return (
    <Button
      type="button"
      variant="outline"
      className="relative mt-7 h-9 w-full justify-center rounded-md border-border bg-background text-sm font-medium text-foreground shadow-none hover:bg-muted/60"
      onClick={onClick}
    >
      <SiGoogle className="absolute left-4 size-4 text-foreground" aria-hidden="true" />
      Continue with Google
    </Button>
  );
}

function PrimaryAction({
  children,
  className,
  onClick,
}: {
  children: React.ReactNode;
  className?: string;
  onClick: () => void;
}) {
  return (
    <Button
      type="button"
      className={cn(
        "h-9 w-full rounded-md bg-foreground text-sm font-semibold text-background shadow-none hover:bg-foreground/90 focus-visible:ring-ring",
        className
      )}
      onClick={onClick}
    >
      {children}
      <ArrowRight data-icon="inline-end" />
    </Button>
  );
}

function LargeOptionButton({
  active,
  icon: Icon,
  title,
  description,
  onClick,
}: {
  active: boolean;
  icon: LucideIcon;
  title: string;
  description: string;
  onClick: () => void;
}) {
  return (
    <button
      type="button"
      className={cn(
        "flex min-h-20 w-full items-center gap-4 rounded-lg border border-border bg-background px-5 text-left transition-colors hover:bg-muted/60",
        active && "border-foreground bg-muted/30 ring-2 ring-foreground/10"
      )}
      onClick={onClick}
    >
      <Icon className="size-7 shrink-0 text-foreground" />
      <span className="min-w-0">
        <span className="block text-sm font-semibold text-foreground">
          {title}
        </span>
        <span className="mt-1 block text-sm leading-5 text-muted-foreground">
          {description}
        </span>
      </span>
    </button>
  );
}

function TileOptionButton({
  active,
  icon: Icon,
  title,
  description,
  onClick,
}: {
  active: boolean;
  icon: LucideIcon;
  title: string;
  description: string;
  onClick: () => void;
}) {
  return (
    <button
      type="button"
      className={cn(
        "flex min-h-36 flex-col items-center justify-center rounded-lg border border-border bg-background p-5 text-center transition-colors hover:bg-muted/60",
        active && "border-foreground bg-muted/30 ring-2 ring-foreground/10"
      )}
      onClick={onClick}
    >
      <Icon className="mb-4 size-8 text-foreground" />
      <span className="text-sm font-semibold text-foreground">{title}</span>
      <span className="mt-2 max-w-40 text-xs leading-5 text-muted-foreground">
        {description}
      </span>
    </button>
  );
}

function InterestChip({
  option,
  active,
  onClick,
}: {
  option: (typeof interestOptions)[number];
  active: boolean;
  onClick: () => void;
}) {
  const Icon = option.icon;

  return (
    <button
      type="button"
      className={cn(
        "inline-flex h-9 items-center gap-2 rounded-lg border border-border bg-background px-3.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted/60",
        active && "border-foreground text-foreground ring-2 ring-foreground/10"
      )}
      onClick={onClick}
    >
      {active ? <Check className="size-4" /> : <Icon className="size-4" />}
      {option.label}
    </button>
  );
}

function ChecklistRow({ checked, text }: { checked?: boolean; text: string }) {
  return (
    <div className="flex items-center gap-3">
      <span
        className={cn(
          "grid size-5 shrink-0 place-items-center border border-border",
          checked && "border-foreground bg-foreground text-background"
        )}
      >
        {checked ? <Check className="size-4" /> : null}
      </span>
      <span className={cn(checked && "text-muted-foreground")}>
        {text}
      </span>
    </div>
  );
}

function validateCurrentStep(
  step: OnboardingStep,
  form: ReturnType<typeof useForm<IntentOnboardingValues>>
) {
  const values = form.getValues();
  const schema = getStepSchema(step);

  form.clearErrors(stepFields[step]);

  if (!schema) {
    return true;
  }

  const result = schema.safeParse(values);

  if (result.success) {
    return true;
  }

  result.error.issues.forEach((issue) => {
    const field = issue.path[0] as FieldPath<IntentOnboardingValues>;

    form.setError(field, { message: issue.message, type: "manual" });
  });

  const firstIssue = result.error.issues[0];

  if (firstIssue) {
    const firstField = firstIssue.path[0] as FieldPath<IntentOnboardingValues>;

    form.setFocus(firstField);
  }

  return false;
}

function getStepSchema(step: OnboardingStep) {
  if (step === "account") return emailStepSchema;
  if (step === "verify") return verificationStepSchema;
  if (step === "profile") return profileStepSchema;
  if (step === "intent") return intentStepSchema;
  if (step === "collaboration") return collaborationStepSchema;

  return undefined;
}

function getVisibleSteps(values: IntentOnboardingValues) {
  return onboardingStepValues.filter((step) => !isStepSkipped(step, values));
}

function getVisibleStepIndex(step: OnboardingStep, values: IntentOnboardingValues) {
  const visibleSteps = getVisibleSteps(values);
  const index = visibleSteps.indexOf(step);

  return index === -1 ? 0 : index;
}

function getNextStep(step: OnboardingStep, values: IntentOnboardingValues) {
  const visibleSteps = getVisibleSteps(values);
  const currentIndex = visibleSteps.indexOf(step);

  return visibleSteps[currentIndex + 1];
}

function isStepSkipped(step: OnboardingStep, values: IntentOnboardingValues) {
  return step === "collaboration" && values.intent !== "personal";
}

function getInitials(value: string) {
  const normalized = value.trim();

  if (!normalized) {
    return "A";
  }

  return normalized
    .split(/[\s@.]+/)
    .filter(Boolean)
    .slice(0, 2)
    .map((part) => part[0]?.toUpperCase())
    .join("");
}

function getIntentLabel(intent: IntentValue) {
  return intentOptions.find((option) => option.value === intent)?.title ?? intent;
}

function getIntentSummary(values: IntentOnboardingValues) {
  if (!values.intent) {
    return "Intent captured";
  }

  const label = getIntentLabel(values.intent).replace(/^For /, "");

  return `Intent set for ${label.toLowerCase()}`;
}

export { StepperIntentOnboardingExample };
```

### `registry/default/components/stepper-logo.tsx`

```tsx
import type { SVGProps } from "react";

function StepperLogo(props: SVGProps<SVGSVGElement>) {
  return (
    <svg
      viewBox="0 0 789.79 1058.83"
      fill="currentColor"
      aria-hidden="true"
      focusable="false"
      {...props}
    >
      <path d="M361.13,1058.28c28.61-24.08,55.58-46.86,82.62-69.55,41.82-35.09,83.8-69.99,125.49-105.23,39.2-33.13,57.7-75.81,57.69-126.9-.05-177.51-.02-355.01-.02-532.52v-9.26c10,3.28,18.99,5.96,27.69,9.36,1.36.53,1.99,4.36,2.02,6.68.17,14.17.25,28.35-.05,42.51-.11,5.26,1.68,7.95,6.82,9.47,15.55,4.58,30.97,9.59,47.4,14.73.21-2.91.48-5.06.49-7.22.1-14.17.02-28.35.23-42.52.15-10.07,3.69-12.79,13.08-9.76,17.45,5.63,34.83,11.45,52.15,17.48,9.32,3.24,12.66,8.43,12.67,18.46.03,35.78-.07,71.56.05,107.34.03,7.56-2.6,13.42-8.55,18.1-9.31,7.32-18.32,15.04-27.71,22.25-3.47,2.67-4.92,5.42-4.91,9.88.11,138.01.03,276.02.1,414.03.01,32.23-2.08,64.19-14.18,94.47-24.52,61.33-67.45,101.93-133.21,115.85-9.24,1.96-18.91,2.61-28.38,2.64-67.61.24-135.22.17-202.83.18-2.22,0-4.44-.24-8.67-.49ZM707.67,578.13c0-28.19.75-55.54-.53-82.79-.32-6.88-5.92-14.7-11.24-19.89-10.81-10.58-24.41-6.66-29.84,7.52-1.06,2.78-1.94,5.84-1.96,8.77-.15,24.56-.09,49.13-.09,74.2,14.53,4.06,28.94,8.09,43.64,12.2Z" />
      <path d="M550.29,611.15h-140.74c-.15-3.18-.37-5.67-.37-8.15-.02-58.79.02-117.58-.07-176.37,0-4.36.43-7.09,5.61-8.45,58.86-15.42,117.64-31.12,176.45-46.74.42-.11.9-.01,2.63-.01,0,2.81,0,5.68,0,8.54-.03,125.94.18,251.88-.23,377.83-.13,40.79-16.04,74.87-47.7,101.19-78.26,65.05-156.45,130.16-234.85,195.04-3.21,2.66-8.17,4.4-12.36,4.49-21.32.45-42.66.21-66.02.21,3.09-2.75,4.81-4.38,6.64-5.88,71.94-58.84,143.89-117.65,215.82-176.5,19.42-15.89,38.64-32.02,58.12-47.83,25.37-20.58,37.33-47.15,37.14-79.7-.26-43.22-.07-86.44-.08-129.66,0-2.29,0-4.57,0-8Z" />
      <path d="M560.11,210.07c0-7.96,0-15.13,0-22.31.01-55.3.02-110.61.03-165.91,0-13.02,7.86-22.02,19.1-21.85,11.18.17,18.62,8.67,18.62,21.58-.02,58.79,3.25,117.83-1.19,176.29-3.69,48.59-.74,96.93-2.82,145.33-.24,5.6-3.85,5.64-7.21,6.51-38.68,10.01-77.36,19.98-116.08,29.85-26.11,6.66-52.25,13.21-78.42,19.65-13.71,3.38-24.86-5.34-24.87-19.28-.03-34.86.01-69.71.07-104.57.02-12.2,5.31-18.61,17.17-21.91,16.55-4.6,33.02-9.5,49.53-14.27,12.34-3.56,18.24.81,18.31,13.64.07,11.85-.05,23.7.09,35.55.09,7.1,1.61,8.22,8.64,6.39,14.15-3.7,28.21-7.77,42.4-11.3,5.37-1.34,6.93-4.22,6.84-9.43-.26-14.4.02-28.81-.19-43.22-.1-6.94,2.65-10.63,9.69-11.98,13.22-2.53,26.31-5.68,40.3-8.76Z" />
      <path d="M361.32,924.39c-47.66,5.68-95.31,11.36-143.75,17.13-.12,2.35-.34,4.79-.35,7.24-.04,23.47.13,46.94-.25,70.41-.05,3.11-2.01,7.08-4.39,9.12-10.77,9.2-21.85,18.06-33.17,26.57-2.88,2.16-7.06,3.65-10.65,3.67-49.49.29-98.99.3-148.49.31-13.39,0-20.24-6.84-20.25-20.15-.04-31.6-.03-63.21.03-94.81.03-13.68,7.96-21.79,21.61-21.78,95.97.05,191.94.2,287.91.31,17.15.02,34.3,0,51.45,0,.1.66.2,1.33.3,1.99Z" />
      <path d="M179.64,881.76c0-27.43-.27-54.36.13-81.28.16-11.16,9.64-18.62,21.94-18.62,52.72.02,105.44.02,158.16.07,44.36.04,88.72.15,133.07.23,3.93,0,7.87,0,11.94,1.58-41.18,4.92-82.37,9.83-124.17,14.82v83.19h-201.07Z" />
      <path d="M541.46,651.31v11.92h-63.14v83.22h-180.91c-.16-2.11-.45-4.12-.45-6.13-.03-21.82-.06-43.65.02-65.47.05-15.26,8.51-23.64,23.77-23.63,70.81.02,141.62.06,212.43.1,2.51,0,5.02,0,8.29,0Z" />
      <path d="M789.79,68.43c-21.66,23.89-42.73,46.06-74.45,52.96-7.27,1.58-15.63,2.2-22.63.21-25.49-7.24-50.59-9.24-76.16,1.67-.36-2.34-.77-3.82-.79-5.3-.28-22.74-.36-45.48-.88-68.22-.11-4.86,1.6-6.76,6.03-8.28,34.87-11.94,67.86-10,98.71,11.86,19.17,13.58,40.53,19.02,63.96,15.43,1.5-.23,3.04-.17,6.2-.33Z" />
      <path d="M230.89,504.03c27.49.25,49.21,22.49,48.96,50.14-.27,28.73-22.72,51.45-50.58,51.18-27.41-.27-49.86-23.56-49.59-51.46.26-27.63,23.35-50.1,51.22-49.85Z" />
    </svg>
  );
}

export { StepperLogo };
```
