Back to buttons
"use client";

import * as React from "react";
import { motion, type HTMLMotionProps, AnimatePresence } from "motion/react";
import { Lock, Unlock, Terminal } from "lucide-react";
import { cn } from "@/lib/utils";

const DEFAULT_TEXT = "Encrypt Data";
const CYCLES_PER_CHAR = 4;
const SHUFFLE_SPEED = 30;
const SCRAMBLE_CHARS = "010101_!@#$%^&*()<>{}[]░▒▓█";

interface EncryptButtonProps
  extends Omit<HTMLMotionProps<"button">, "children"> {
  text?: string;
  children?: React.ReactNode;
  icon?: React.ReactNode;
  showIcon?: boolean;
}

const EncryptButton = React.forwardRef<HTMLButtonElement, EncryptButtonProps>(
  (
    {
      text,
      className,
      children,
      icon,
      showIcon = true,
      onMouseEnter,
      onMouseLeave,
      ...props
    },
    ref,
  ) => {
    const labelText =
      (typeof children === "string" ? children : text) || DEFAULT_TEXT;

    const [displayText, setDisplayText] = React.useState(labelText);
    const [isHovered, setIsHovered] = React.useState(false);
    const [isDecrypted, setIsDecrypted] = React.useState(false);
    const intervalRef = React.useRef<NodeJS.Timeout | null>(null);

    const stopScramble = React.useCallback(() => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
        intervalRef.current = null;
      }
      setDisplayText(labelText);
      setIsDecrypted(false);
    }, [labelText]);

    const startScramble = React.useCallback(() => {
      stopScramble();
      let step = 0;
      const totalSteps = labelText.length * CYCLES_PER_CHAR;

      intervalRef.current = setInterval(() => {
        setDisplayText(() => {
          const scrambled = labelText
            .split("")
            .map((char, index) => {
              if (step / CYCLES_PER_CHAR > index) return char;
              if (char === " ") return " ";
              return SCRAMBLE_CHARS[
                Math.floor(Math.random() * SCRAMBLE_CHARS.length)
              ];
            })
            .join("");
          return scrambled;
        });

        step++;
        if (step > totalSteps) {
          if (intervalRef.current) {
            clearInterval(intervalRef.current);
            intervalRef.current = null;
          }
          setDisplayText(labelText);
          setIsDecrypted(true);
        }
      }, SHUFFLE_SPEED);
    }, [labelText, stopScramble]);

    React.useEffect(() => {
      setDisplayText(labelText);
    }, [labelText]);

    React.useEffect(() => {
      return () => {
        if (intervalRef.current) clearInterval(intervalRef.current);
      };
    }, []);

    return (
      <motion.button
        ref={ref}
        onMouseEnter={(e) => {
          setIsHovered(true);
          startScramble();
          if (onMouseEnter) onMouseEnter(e);
        }}
        onMouseLeave={(e) => {
          setIsHovered(false);
          stopScramble();
          if (onMouseLeave) onMouseLeave(e);
        }}
        whileHover={{ scale: 1.02 }}
        whileTap={{ scale: 0.98 }}
        className={cn(
          "group relative inline-flex items-center justify-center gap-2.5 overflow-hidden rounded-lg px-6 py-3 font-mono text-xs font-semibold uppercase tracking-widest transition-all duration-300 select-none cursor-pointer outline-none",
          "bg-background text-foreground border border-border",
          "hover:border-primary/60 hover:text-primary hover:shadow-sm",
          "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
          className,
        )}
        {...props}
      >
        {/* HUD Tech Corner Brackets */}
        <span className="absolute top-1 left-1 size-1.5 border-t border-l border-border transition-colors duration-300 group-hover:border-primary group-hover:size-2" />
        <span className="absolute top-1 right-1 size-1.5 border-t border-r border-border transition-colors duration-300 group-hover:border-primary group-hover:size-2" />
        <span className="absolute bottom-1 left-1 size-1.5 border-b border-l border-border transition-colors duration-300 group-hover:border-primary group-hover:size-2" />
        <span className="absolute bottom-1 right-1 size-1.5 border-b border-r border-border transition-colors duration-300 group-hover:border-primary group-hover:size-2" />

        {/* Ambient Backlight Glow */}
        <div className="absolute inset-0 bg-linear-to-r from-primary/0 via-primary/10 to-primary/0 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />

        {/* Laser Sweep Scan Beam */}
        <AnimatePresence>
          {isHovered && (
            <motion.span
              initial={{ x: "-100%" }}
              animate={{ x: "200%" }}
              exit={{ opacity: 0 }}
              transition={{
                repeat: Infinity,
                duration: 1.4,
                ease: "easeInOut",
              }}
              className="absolute inset-y-0 w-1/3 bg-linear-to-r from-transparent via-primary/20 to-transparent -skew-x-12 pointer-events-none"
            />
          )}
        </AnimatePresence>

        {/* High-Tech Icon Indicator */}
        {showIcon && (
          <span className="relative z-10 flex items-center justify-center">
            {icon ? (
              icon
            ) : (
              <AnimatePresence mode="wait" initial={false}>
                {isDecrypted ? (
                  <motion.span
                    key="unlocked"
                    initial={{ scale: 0.5, opacity: 0 }}
                    animate={{ scale: 1, opacity: 1 }}
                    exit={{ scale: 0.5, opacity: 0 }}
                    transition={{ duration: 0.15 }}
                  >
                    <Unlock className="size-4 text-primary" />
                  </motion.span>
                ) : isHovered ? (
                  <motion.span
                    key="terminal"
                    initial={{ scale: 0.8, opacity: 0 }}
                    animate={{ scale: 1, opacity: 1 }}
                    exit={{ scale: 0.8, opacity: 0 }}
                    transition={{ duration: 0.15 }}
                  >
                    <Terminal className="size-4 text-primary animate-pulse" />
                  </motion.span>
                ) : (
                  <motion.span
                    key="locked"
                    initial={{ scale: 0.8, opacity: 0 }}
                    animate={{ scale: 1, opacity: 1 }}
                    exit={{ scale: 0.8, opacity: 0 }}
                    transition={{ duration: 0.15 }}
                  >
                    <Lock className="size-4 text-muted-foreground transition-colors group-hover:text-primary" />
                  </motion.span>
                )}
              </AnimatePresence>
            )}
          </span>
        )}

        {/* Text Container with invisible reference element for width stability */}
        <span className="relative z-10 inline-flex items-center justify-center">
          <span className="opacity-0" aria-hidden={true}>
            {children || labelText}
          </span>
          <span className="absolute inset-0 flex items-center justify-center font-mono">
            {displayText}
          </span>
        </span>
      </motion.button>
    );
  },
);

EncryptButton.displayName = "EncryptButton";

export { EncryptButton, type EncryptButtonProps };

const EncryptButtonDemo = () => {
  return <EncryptButton text="Encrypt Data" />;
};

export default EncryptButtonDemo;


Button 26 - Encrypt

By ShadcnSpace

Try the interaction, copy the React source files, or download the complete example.

About Button 26 - Encrypt

Button 26 - Encrypt is an animated React and Tailwind CSS button by ShadcnSpace. Try the interaction, copy the React source, or download the example with creator credit.

Effects and categories
Hover effects, Animated
Using this example
Copy a TSX file or download the original source and standalone demo. Test keyboard focus, motion and contrast in your own project.

Usage, Tailwind and licensing questions

Read the source files

components/shadcn-space/button/button-26.tsx

"use client";

import * as React from "react";
import { motion, type HTMLMotionProps, AnimatePresence } from "motion/react";
import { Lock, Unlock, Terminal } from "lucide-react";
import { cn } from "@/lib/utils";

const DEFAULT_TEXT = "Encrypt Data";
const CYCLES_PER_CHAR = 4;
const SHUFFLE_SPEED = 30;
const SCRAMBLE_CHARS = "010101_!@#$%^&*()<>{}[]░▒▓█";

interface EncryptButtonProps
  extends Omit<HTMLMotionProps<"button">, "children"> {
  text?: string;
  children?: React.ReactNode;
  icon?: React.ReactNode;
  showIcon?: boolean;
}

const EncryptButton = React.forwardRef<HTMLButtonElement, EncryptButtonProps>(
  (
    {
      text,
      className,
      children,
      icon,
      showIcon = true,
      onMouseEnter,
      onMouseLeave,
      ...props
    },
    ref,
  ) => {
    const labelText =
      (typeof children === "string" ? children : text) || DEFAULT_TEXT;

    const [displayText, setDisplayText] = React.useState(labelText);
    const [isHovered, setIsHovered] = React.useState(false);
    const [isDecrypted, setIsDecrypted] = React.useState(false);
    const intervalRef = React.useRef<NodeJS.Timeout | null>(null);

    const stopScramble = React.useCallback(() => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
        intervalRef.current = null;
      }
      setDisplayText(labelText);
      setIsDecrypted(false);
    }, [labelText]);

    const startScramble = React.useCallback(() => {
      stopScramble();
      let step = 0;
      const totalSteps = labelText.length * CYCLES_PER_CHAR;

      intervalRef.current = setInterval(() => {
        setDisplayText(() => {
          const scrambled = labelText
            .split("")
            .map((char, index) => {
              if (step / CYCLES_PER_CHAR > index) return char;
              if (char === " ") return " ";
              return SCRAMBLE_CHARS[
                Math.floor(Math.random() * SCRAMBLE_CHARS.length)
              ];
            })
            .join("");
          return scrambled;
        });

        step++;
        if (step > totalSteps) {
          if (intervalRef.current) {
            clearInterval(intervalRef.current);
            intervalRef.current = null;
          }
          setDisplayText(labelText);
          setIsDecrypted(true);
        }
      }, SHUFFLE_SPEED);
    }, [labelText, stopScramble]);

    React.useEffect(() => {
      setDisplayText(labelText);
    }, [labelText]);

    React.useEffect(() => {
      return () => {
        if (intervalRef.current) clearInterval(intervalRef.current);
      };
    }, []);

    return (
      <motion.button
        ref={ref}
        onMouseEnter={(e) => {
          setIsHovered(true);
          startScramble();
          if (onMouseEnter) onMouseEnter(e);
        }}
        onMouseLeave={(e) => {
          setIsHovered(false);
          stopScramble();
          if (onMouseLeave) onMouseLeave(e);
        }}
        whileHover={{ scale: 1.02 }}
        whileTap={{ scale: 0.98 }}
        className={cn(
          "group relative inline-flex items-center justify-center gap-2.5 overflow-hidden rounded-lg px-6 py-3 font-mono text-xs font-semibold uppercase tracking-widest transition-all duration-300 select-none cursor-pointer outline-none",
          "bg-background text-foreground border border-border",
          "hover:border-primary/60 hover:text-primary hover:shadow-sm",
          "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
          className,
        )}
        {...props}
      >
        {/* HUD Tech Corner Brackets */}
        <span className="absolute top-1 left-1 size-1.5 border-t border-l border-border transition-colors duration-300 group-hover:border-primary group-hover:size-2" />
        <span className="absolute top-1 right-1 size-1.5 border-t border-r border-border transition-colors duration-300 group-hover:border-primary group-hover:size-2" />
        <span className="absolute bottom-1 left-1 size-1.5 border-b border-l border-border transition-colors duration-300 group-hover:border-primary group-hover:size-2" />
        <span className="absolute bottom-1 right-1 size-1.5 border-b border-r border-border transition-colors duration-300 group-hover:border-primary group-hover:size-2" />

        {/* Ambient Backlight Glow */}
        <div className="absolute inset-0 bg-linear-to-r from-primary/0 via-primary/10 to-primary/0 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />

        {/* Laser Sweep Scan Beam */}
        <AnimatePresence>
          {isHovered && (
            <motion.span
              initial={{ x: "-100%" }}
              animate={{ x: "200%" }}
              exit={{ opacity: 0 }}
              transition={{
                repeat: Infinity,
                duration: 1.4,
                ease: "easeInOut",
              }}
              className="absolute inset-y-0 w-1/3 bg-linear-to-r from-transparent via-primary/20 to-transparent -skew-x-12 pointer-events-none"
            />
          )}
        </AnimatePresence>

        {/* High-Tech Icon Indicator */}
        {showIcon && (
          <span className="relative z-10 flex items-center justify-center">
            {icon ? (
              icon
            ) : (
              <AnimatePresence mode="wait" initial={false}>
                {isDecrypted ? (
                  <motion.span
                    key="unlocked"
                    initial={{ scale: 0.5, opacity: 0 }}
                    animate={{ scale: 1, opacity: 1 }}
                    exit={{ scale: 0.5, opacity: 0 }}
                    transition={{ duration: 0.15 }}
                  >
                    <Unlock className="size-4 text-primary" />
                  </motion.span>
                ) : isHovered ? (
                  <motion.span
                    key="terminal"
                    initial={{ scale: 0.8, opacity: 0 }}
                    animate={{ scale: 1, opacity: 1 }}
                    exit={{ scale: 0.8, opacity: 0 }}
                    transition={{ duration: 0.15 }}
                  >
                    <Terminal className="size-4 text-primary animate-pulse" />
                  </motion.span>
                ) : (
                  <motion.span
                    key="locked"
                    initial={{ scale: 0.8, opacity: 0 }}
                    animate={{ scale: 1, opacity: 1 }}
                    exit={{ scale: 0.8, opacity: 0 }}
                    transition={{ duration: 0.15 }}
                  >
                    <Lock className="size-4 text-muted-foreground transition-colors group-hover:text-primary" />
                  </motion.span>
                )}
              </AnimatePresence>
            )}
          </span>
        )}

        {/* Text Container with invisible reference element for width stability */}
        <span className="relative z-10 inline-flex items-center justify-center">
          <span className="opacity-0" aria-hidden={true}>
            {children || labelText}
          </span>
          <span className="absolute inset-0 flex items-center justify-center font-mono">
            {displayText}
          </span>
        </span>
      </motion.button>
    );
  },
);

EncryptButton.displayName = "EncryptButton";

export { EncryptButton, type EncryptButtonProps };

const EncryptButtonDemo = () => {
  return <EncryptButton text="Encrypt Data" />;
};

export default EncryptButtonDemo;


lib/utils.ts

import { config } from "@/config";
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

export function capitalize(str: string) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
type GroupBy<T, K extends keyof T> = Record<string, T[]>;

export function groupBy<T, K extends keyof T>(
  array: T[],
  key: K
): GroupBy<T, K> {
  return array.reduce((acc, item) => {
    const keyValue = String(item[key]);
    if (!acc[keyValue]) {
      acc[keyValue] = [];
    }
    acc[keyValue].push(item);
    return acc;
  }, {} as GroupBy<T, K>);
}

export function absoluteUrl(path: string) {
  return process.env.NODE_ENV === "development"
    ? `http://localhost:3000/${path}`
    : `${config.appUrl}/${path}`;
}

config.ts

export const config = {
  appUrl:
    process.env.NODE_ENV === "production"
      ? process.env.VERCEL_PROJECT_PRODUCTION_URL ??
      process.env.NEXT_PUBLIC_APP_URL!
      : "http://localhost:3000",
  social: {
    github: "https://github.com/shadcnspace/shadcnspace",
    twitter: "https://twitter.com/",
  },
};

demo.tsx

export { default as default } from "@/components/shadcn-space/button/button-26";

CSS

/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties;
@layer theme, base, components, utilities;
@layer theme {
  :root, :host {
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
      "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
      "Segoe UI Symbol", "Noto Color Emoji";
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
      "Courier New", monospace;
    --spacing: 0.25rem;
    --text-xs: 0.75rem;
    --text-xs--line-height: calc(1 / 0.75);
    --font-weight-semibold: 600;
    --tracking-widest: 0.1em;
    --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
    --default-transition-duration: 150ms;
    --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    --default-font-family: var(--font-sans);
    --default-font-feature-settings: "cv02", "cv03", "cv04", "cv11";
    --default-mono-font-family: var(--font-mono);
  }
}
@layer base {
  *, ::after, ::before, ::backdrop, ::file-selector-button {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    border: 0 solid;
  }
  html, :host {
    line-height: 1.5;
    -webkit-text-size-adjust: 100%;
    tab-size: 4;
    font-family: var(--default-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
    font-feature-settings: var(--default-font-feature-settings, normal);
    font-variation-settings: var(--default-font-variation-settings, normal);
    -webkit-tap-highlight-color: transparent;
  }
  hr {
    height: 0;
    color: inherit;
    border-top-width: 1px;
  }
  abbr:where([title]) {
    -webkit-text-decoration: underline dotted;
    text-decoration: underline dotted;
  }
  h1, h2, h3, h4, h5, h6 {
    font-size: inherit;
    font-weight: inherit;
  }
  a {
    color: inherit;
    -webkit-text-decoration: inherit;
    text-decoration: inherit;
  }
  b, strong {
    font-weight: bolder;
  }
  code, kbd, samp, pre {
    font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
    font-feature-settings: var(--default-mono-font-feature-settings, normal);
    font-variation-settings: var(--default-mono-font-variation-settings, normal);
    font-size: 1em;
  }
  small {
    font-size: 80%;
  }
  sub, sup {
    font-size: 75%;
    line-height: 0;
    position: relative;
    vertical-align: baseline;
  }
  sub {
    bottom: -0.25em;
  }
  sup {
    top: -0.5em;
  }
  table {
    text-indent: 0;
    border-color: inherit;
    border-collapse: collapse;
  }
  :-moz-focusring:where(:not(iframe)) {
    outline: auto;
  }
  progress {
    vertical-align: baseline;
  }
  summary {
    display: list-item;
  }
  ol, ul, menu {
    list-style: none;
  }
  img, svg, video, canvas, audio, iframe, embed, object {
    display: block;
    vertical-align: middle;
  }
  img, video {
    max-width: 100%;
    height: auto;
  }
  button, input, select, optgroup, textarea, ::file-selector-button {
    font: inherit;
    font-feature-settings: inherit;
    font-variation-settings: inherit;
    letter-spacing: inherit;
    color: inherit;
    border-radius: 0;
    background-color: transparent;
    opacity: 1;
  }
  :where(select:is([multiple], [size])) optgroup {
    font-weight: bolder;
  }
  :where(select:is([multiple], [size])) optgroup option {
    padding-inline-start: 20px;
  }
  ::file-selector-button {
    margin-inline-end: 4px;
  }
  ::placeholder {
    opacity: 1;
  }
  @supports (not (-webkit-appearance: -apple-pay-button))  or (contain-intrinsic-size: 1px) {
    ::placeholder {
      color: currentcolor;
      @supports (color: color-mix(in lab, red, red)) {
        color: color-mix(in oklab, currentcolor 50%, transparent);
      }
    }
  }
  textarea {
    resize: vertical;
  }
  ::-webkit-search-decoration {
    -webkit-appearance: none;
  }
  ::-webkit-date-and-time-value {
    min-height: 1lh;
    text-align: inherit;
  }
  ::-webkit-datetime-edit {
    display: inline-flex;
  }
  ::-webkit-datetime-edit-fields-wrapper {
    padding: 0;
  }
  ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
    padding-block: 0;
  }
  ::-webkit-calendar-picker-indicator {
    line-height: 1;
  }
  :-moz-ui-invalid {
    box-shadow: none;
  }
  button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {
    appearance: button;
  }
  ::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
    height: auto;
  }
  [hidden]:where(:not([hidden="until-found"])) {
    display: none !important;
  }
}
@layer utilities {
  .pointer-events-none {
    pointer-events: none;
  }
  .invisible {
    visibility: hidden;
  }
  .absolute {
    position: absolute;
  }
  .relative {
    position: relative;
  }
  .inset-0 {
    inset: 0px;
  }
  .inset-y-0 {
    inset-block: 0px;
  }
  .top-1 {
    top: var(--spacing);
  }
  .right-1 {
    right: var(--spacing);
  }
  .bottom-1 {
    bottom: var(--spacing);
  }
  .left-1 {
    left: var(--spacing);
  }
  .z-10 {
    z-index: 10;
  }
  .flex {
    display: flex;
  }
  .inline-flex {
    display: inline-flex;
  }
  .size-1\.5 {
    width: calc(var(--spacing) * 1.5);
    height: calc(var(--spacing) * 1.5);
  }
  .size-4 {
    width: calc(var(--spacing) * 4);
    height: calc(var(--spacing) * 4);
  }
  .w-1\/3 {
    width: calc(1 / 3 * 100%);
  }
  .-skew-x-12 {
    --tw-skew-x: skewX(calc(12deg * -1));
    transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
  }
  .animate-pulse {
    animation: var(--animate-pulse);
  }
  .cursor-pointer {
    cursor: pointer;
  }
  .items-center {
    align-items: center;
  }
  .justify-center {
    justify-content: center;
  }
  .gap-2\.5 {
    gap: calc(var(--spacing) * 2.5);
  }
  .overflow-hidden {
    overflow: hidden;
  }
  .rounded-lg {
    border-radius: var(--radius);
  }
  .border {
    border-style: var(--tw-border-style);
    border-width: 1px;
  }
  .border-t {
    border-top-style: var(--tw-border-style);
    border-top-width: 1px;
  }
  .border-r {
    border-right-style: var(--tw-border-style);
    border-right-width: 1px;
  }
  .border-b {
    border-bottom-style: var(--tw-border-style);
    border-bottom-width: 1px;
  }
  .border-l {
    border-left-style: var(--tw-border-style);
    border-left-width: 1px;
  }
  .border-border {
    border-color: var(--border);
  }
  .bg-background {
    background-color: var(--background);
  }
  .bg-linear-to-r {
    --tw-gradient-position: to right;
    @supports (background-image: linear-gradient(in lab, red, red)) {
      --tw-gradient-position: to right in oklab;
    }
    background-image: linear-gradient(var(--tw-gradient-stops));
  }
  .from-primary\/0 {
    --tw-gradient-from: var(--primary);
    @supports (color: color-mix(in lab, red, red)) {
      --tw-gradient-from: color-mix(in oklab, var(--primary) 0%, transparent);
    }
    --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
  }
  .from-transparent {
    --tw-gradient-from: transparent;
    --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
  }
  .via-primary\/10 {
    --tw-gradient-via: var(--primary);
    @supports (color: color-mix(in lab, red, red)) {
      --tw-gradient-via: color-mix(in oklab, var(--primary) 10%, transparent);
    }
    --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
    --tw-gradient-stops: var(--tw-gradient-via-stops);
  }
  .via-primary\/20 {
    --tw-gradient-via: var(--primary);
    @supports (color: color-mix(in lab, red, red)) {
      --tw-gradient-via: color-mix(in oklab, var(--primary) 20%, transparent);
    }
    --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
    --tw-gradient-stops: var(--tw-gradient-via-stops);
  }
  .to-primary\/0 {
    --tw-gradient-to: var(--primary);
    @supports (color: color-mix(in lab, red, red)) {
      --tw-gradient-to: color-mix(in oklab, var(--primary) 0%, transparent);
    }
    --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
  }
  .to-transparent {
    --tw-gradient-to: transparent;
    --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
  }
  .px-6 {
    padding-inline: calc(var(--spacing) * 6);
  }
  .py-3 {
    padding-block: calc(var(--spacing) * 3);
  }
  .font-mono {
    font-family: var(--font-mono);
  }
  .text-xs {
    font-size: var(--text-xs);
    line-height: var(--tw-leading, var(--text-xs--line-height));
  }
  .font-semibold {
    --tw-font-weight: var(--font-weight-semibold);
    font-weight: var(--font-weight-semibold);
  }
  .tracking-widest {
    --tw-tracking: var(--tracking-widest);
    letter-spacing: var(--tracking-widest);
  }
  .text-foreground {
    color: var(--foreground);
  }
  .text-muted-foreground {
    color: var(--muted-foreground);
  }
  .text-primary {
    color: var(--primary);
  }
  .uppercase {
    text-transform: uppercase;
  }
  .opacity-0 {
    opacity: 0%;
  }
  .transition {
    transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }
  .transition-all {
    transition-property: all;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }
  .transition-colors {
    transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }
  .transition-opacity {
    transition-property: opacity;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }
  .duration-300 {
    --tw-duration: 300ms;
    transition-duration: 300ms;
  }
  .outline-none {
    --tw-outline-style: none;
    outline-style: none;
  }
  .select-none {
    -webkit-user-select: none;
    user-select: none;
  }
  @media (hover: hover) {
    .group-hover\:size-2:is(:where(.group):hover *) {
      width: calc(var(--spacing) * 2);
      height: calc(var(--spacing) * 2);
    }
    .group-hover\:border-primary:is(:where(.group):hover *) {
      border-color: var(--primary);
    }
    .group-hover\:text-primary:is(:where(.group):hover *) {
      color: var(--primary);
    }
    .group-hover\:opacity-100:is(:where(.group):hover *) {
      opacity: 100%;
    }
    .hover\:border-primary\/60:hover {
      border-color: var(--primary);
    }
    @supports (color: color-mix(in lab, red, red)) {
      .hover\:border-primary\/60:hover {
        border-color: color-mix(in oklab, var(--primary) 60%, transparent);
      }
    }
    .hover\:text-primary:hover {
      color: var(--primary);
    }
    .hover\:shadow-sm:hover {
      --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
      box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
    }
  }
  .focus-visible\:ring-2:focus-visible {
    --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }
  .focus-visible\:ring-ring:focus-visible {
    --tw-ring-color: var(--ring);
  }
  .focus-visible\:ring-offset-2:focus-visible {
    --tw-ring-offset-width: 2px;
    --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
  }
  .focus-visible\:ring-offset-background:focus-visible {
    --tw-ring-offset-color: var(--background);
  }
}
:root {
  --background: #fff;
  --foreground: #111;
  --primary: #171717;
  --primary-foreground: #fff;
  --secondary: #eee;
  --secondary-foreground: #171717;
  --muted: #eee;
  --muted-foreground: #666;
  --accent: #eee;
  --accent-foreground: #171717;
  --border: #ddd;
  --input: #ddd;
  --ring: #777;
  --destructive: #dc2626;
  --destructive-foreground: #fff;
  --popover: #fff;
  --popover-foreground: #111;
  --success: #15803d;
  --success-foreground: #fff;
  --radius: .625rem;
  --hairline: 1px;
}
.dark {
  --background: #090909;
  --foreground: #fafafa;
  --primary: #fafafa;
  --primary-foreground: #171717;
  --secondary: #262626;
  --secondary-foreground: #fafafa;
  --muted: #262626;
  --muted-foreground: #a3a3a3;
  --accent: #262626;
  --accent-foreground: #fafafa;
  --border: #333;
  --input: #333;
  --popover: #171717;
  --popover-foreground: #fafafa;
}
:root {
  --radius: 0.5rem;
  --background: oklch(1 0 0);
  --foreground: oklch(0.1296 0.0274 261.69);
  --card: oklch(1 0 0);
  --card-foreground: oklch(0.1296 0.0274 261.69);
  --popover: oklch(1 0 0);
  --popover-foreground: oklch(0.1296 0.0274 261.69);
  --primary: oklch(0.1296 0.0274 261.69);
  --primary-foreground: oklch(0.9276 0.0058 264.53);
  --secondary: oklch(0.967 0.0029 264.54);
  --secondary-foreground: oklch(0.1296 0.0274 261.69);
  --muted: oklch(0.967 0.0029 264.54);
  --muted-foreground: oklch(0.4461 0.0263 256.8);
  --accent: oklch(0.967 0.0029 264.54);
  --accent-foreground: oklch(0.1296 0.0274 261.69);
  --destructive: oklch(0.6368 0.2078 25.33);
  --border: oklch(0.9276 0.0058 264.53);
  --input: oklch(0.922 0 0);
  --ring: oklch(0.7137 0.0192 261.32);
  --chart-1: oklch(63.681% 0.20784 25.315);
  --chart-2: oklch(78.453% 0.13254 181.95);
  --chart-3: oklch(62.31% 0.1881 259.83);
  --chart-4: oklch(87.896% 0.15335 91.611);
  --chart-5: oklch(75.763% 0.15899 55.922);
  --sidebar: oklch(0.985 0 0);
  --sidebar-foreground: oklch(0.1296 0.0274 261.69);
  --sidebar-primary: oklch(0.1296 0.0274 261.69);
  --sidebar-primary-foreground: oklch(0.9276 0.0058 264.53);
  --sidebar-accent: oklch(0.967 0.0029 264.54);
  --sidebar-accent-foreground: oklch(0.1296 0.0274 261.69);
  --sidebar-border: oklch(0.9276 0.0058 264.53);
  --sidebar-ring: oklch(0.7137 0.0192 261.32);
}
.dark {
  --background: oklch(0.1296 0.0274 261.69);
  --foreground: oklch(0.9276 0.0058 264.53);
  --card: oklch(0.1296 0.0274 261.69);
  --card-foreground: oklch(0.9276 0.0058 264.53);
  --popover: oklch(0.1296 0.0274 261.69);
  --popover-foreground: oklch(0.9276 0.0058 264.53);
  --primary: oklch(0.9276 0.0058 264.53);
  --primary-foreground: oklch(0.1296 0.0274 261.69);
  --secondary: oklch(0.3729 0.0306 259.73);
  --secondary-foreground: oklch(0.9276 0.0058 264.53);
  --muted: oklch(0.2781 0.0296 256.85);
  --muted-foreground: oklch(0.8717 0.0093 258.34);
  --accent: oklch(0.2781 0.0296 256.85);
  --accent-foreground: oklch(0.9276 0.0058 264.53);
  --destructive: oklch(0.6368 0.2078 25.33);
  --border: oklch(27.808% 0.02963 256.871 / 70%);
  --input: oklch(1 0 0 / 15%);
  --ring: oklch(0.4461 0.0263 256.8);
  --chart-1: oklch(63.681% 0.20784 25.315);
  --chart-2: oklch(78.453% 0.13254 181.95);
  --chart-3: oklch(62.31% 0.1881 259.83);
  --chart-4: oklch(87.896% 0.15335 91.611);
  --chart-5: oklch(75.763% 0.15899 55.922);
  --sidebar: oklch(0.205 0 0);
  --sidebar-foreground: oklch(0.9276 0.0058 264.53);
  --sidebar-primary: oklch(0.9276 0.0058 264.53);
  --sidebar-primary-foreground: oklch(0.1296 0.0274 261.69);
  --sidebar-accent: oklch(0.2781 0.0296 256.85);
  --sidebar-accent-foreground: oklch(0.9276 0.0058 264.53);
  --sidebar-border: var(--border);
  --sidebar-ring: oklch(0.4461 0.0263 256.8);
}
@property --tw-rotate-x {
  syntax: "*";
  inherits: false;
}
@property --tw-rotate-y {
  syntax: "*";
  inherits: false;
}
@property --tw-rotate-z {
  syntax: "*";
  inherits: false;
}
@property --tw-skew-x {
  syntax: "*";
  inherits: false;
}
@property --tw-skew-y {
  syntax: "*";
  inherits: false;
}
@property --tw-border-style {
  syntax: "*";
  inherits: false;
  initial-value: solid;
}
@property --tw-gradient-position {
  syntax: "*";
  inherits: false;
}
@property --tw-gradient-from {
  syntax: "<color>";
  inherits: false;
  initial-value: #0000;
}
@property --tw-gradient-via {
  syntax: "<color>";
  inherits: false;
  initial-value: #0000;
}
@property --tw-gradient-to {
  syntax: "<color>";
  inherits: false;
  initial-value: #0000;
}
@property --tw-gradient-stops {
  syntax: "*";
  inherits: false;
}
@property --tw-gradient-via-stops {
  syntax: "*";
  inherits: false;
}
@property --tw-gradient-from-position {
  syntax: "<length-percentage>";
  inherits: false;
  initial-value: 0%;
}
@property --tw-gradient-via-position {
  syntax: "<length-percentage>";
  inherits: false;
  initial-value: 50%;
}
@property --tw-gradient-to-position {
  syntax: "<length-percentage>";
  inherits: false;
  initial-value: 100%;
}
@property --tw-font-weight {
  syntax: "*";
  inherits: false;
}
@property --tw-tracking {
  syntax: "*";
  inherits: false;
}
@property --tw-duration {
  syntax: "*";
  inherits: false;
}
@property --tw-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-shadow-color {
  syntax: "*";
  inherits: false;
}
@property --tw-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}
@property --tw-inset-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-inset-shadow-color {
  syntax: "*";
  inherits: false;
}
@property --tw-inset-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}
@property --tw-ring-color {
  syntax: "*";
  inherits: false;
}
@property --tw-ring-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-inset-ring-color {
  syntax: "*";
  inherits: false;
}
@property --tw-inset-ring-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-ring-inset {
  syntax: "*";
  inherits: false;
}
@property --tw-ring-offset-width {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
@property --tw-ring-offset-color {
  syntax: "*";
  inherits: false;
  initial-value: #fff;
}
@property --tw-ring-offset-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@keyframes pulse {
  50% {
    opacity: 0.5;
  }
}
@layer properties {
  @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
    *, ::before, ::after, ::backdrop {
      --tw-rotate-x: initial;
      --tw-rotate-y: initial;
      --tw-rotate-z: initial;
      --tw-skew-x: initial;
      --tw-skew-y: initial;
      --tw-border-style: solid;
      --tw-gradient-position: initial;
      --tw-gradient-from: #0000;
      --tw-gradient-via: #0000;
      --tw-gradient-to: #0000;
      --tw-gradient-stops: initial;
      --tw-gradient-via-stops: initial;
      --tw-gradient-from-position: 0%;
      --tw-gradient-via-position: 50%;
      --tw-gradient-to-position: 100%;
      --tw-font-weight: initial;
      --tw-tracking: initial;
      --tw-duration: initial;
      --tw-shadow: 0 0 #0000;
      --tw-shadow-color: initial;
      --tw-shadow-alpha: 100%;
      --tw-inset-shadow: 0 0 #0000;
      --tw-inset-shadow-color: initial;
      --tw-inset-shadow-alpha: 100%;
      --tw-ring-color: initial;
      --tw-ring-shadow: 0 0 #0000;
      --tw-inset-ring-color: initial;
      --tw-inset-ring-shadow: 0 0 #0000;
      --tw-ring-inset: initial;
      --tw-ring-offset-width: 0px;
      --tw-ring-offset-color: #fff;
      --tw-ring-offset-shadow: 0 0 #0000;
    }
  }
}