Innovations
heroes /

Consultant Circle Hero

Service-business hero with portrait inside a circle backdrop, floating stat cards, and a 4-card service strip below. Mobile-first, no shadcn, ships zero JS unless you opt in.

Preview

Source

tsx
import {
  Search,
  Menu,
  Trophy,
  TrendingUp,
  Monitor,
  Users,
  Wallet,
  FileText,
} from "lucide-react";
import type { ReactNode } from "react";

export interface ConsultantCircleService {
  icon: ReactNode;
  title: string;
}

export interface ConsultantCircleHeroProps {
  brand?: string;
  navItems?: { label: string; href: string }[];
  headline?: string;
  subhead?: string;
  primaryCta?: { label: string; href: string };
  secondaryCta?: { label: string; href: string };
  imageSrc?: string;
  imageAlt?: string;
  statValue?: string;
  statLabel?: string;
  expertsLabel?: string;
  expertsCount?: string;
  services?: ConsultantCircleService[];
  serviceLabel?: string;
}

const defaultServices: ConsultantCircleService[] = [
  { icon: <Monitor className="w-5 h-5" />, title: "Tech Development" },
  { icon: <Users className="w-5 h-5" />, title: "Customer Insight" },
  { icon: <Wallet className="w-5 h-5" />, title: "Financial Planning" },
  { icon: <FileText className="w-5 h-5" />, title: "Legal Documentation" },
];

export default function ConsultantCircleHero({
  brand = "ProVice",
  navItems = [
    { label: "Home", href: "#" },
    { label: "About", href: "#about" },
    { label: "Services", href: "#services" },
    { label: "Contact", href: "#contact" },
  ],
  headline = "Find the best solution together.",
  subhead = "We not only provide consultation but also consider your thoughts to find the best solution for each of your business problems.",
  primaryCta = { label: "Get Started", href: "#contact" },
  secondaryCta = { label: "Learn More", href: "#services" },
  imageSrc = "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&w=900&q=80",
  imageAlt = "Smiling consultant",
  statValue = "35K+",
  statLabel = "Cases Solved with Satisfaction",
  expertsLabel = "Our Experts",
  expertsCount = "4+",
  services = defaultServices,
  serviceLabel = "Our Service:",
}: ConsultantCircleHeroProps) {
  return (
    <section className="relative overflow-hidden bg-white">
      {/* Decorative background dots */}
      <div aria-hidden className="pointer-events-none absolute inset-0">
        <span className="absolute left-[10%] top-[18%] h-1.5 w-1.5 rounded-full bg-[#ff7a5c]/40" />
        <span className="absolute right-[8%] top-[14%] h-2 w-2 rounded-full bg-[#ff7a5c]/40" />
        <span className="absolute right-[40%] top-[8%] h-1.5 w-1.5 rounded-full bg-[#ff7a5c]/30" />
        <span className="absolute right-[6%] top-[55%] h-2.5 w-2.5 rounded-full bg-[#ff7a5c]/35" />
        <span className="absolute right-[18%] bottom-[18%] h-1.5 w-1.5 rounded-full bg-[#ff7a5c]/30" />
        <span className="absolute left-[45%] bottom-[10%] h-1.5 w-1.5 rounded-full bg-[#ff7a5c]/30" />
        {/* Squiggle top right */}
        <svg
          className="absolute right-[14%] top-[10%] h-6 w-12 text-[#7cc6d8]/50"
          viewBox="0 0 60 30"
          fill="none"
        >
          <path
            d="M2 18 Q 12 4 22 18 T 42 18 T 58 18"
            stroke="currentColor"
            strokeWidth="2"
            strokeLinecap="round"
          />
        </svg>
      </div>

      <div className="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
        {/* Nav */}
        <nav className="flex items-center justify-between py-6">
          <a
            href="/"
            className="text-2xl font-extrabold tracking-tight text-[#ff6b4a]"
          >
            {brand}
          </a>
          <div className="hidden items-center gap-8 md:flex">
            {navItems.map((item, i) => (
              <a
                key={item.label}
                href={item.href}
                className={`text-sm font-medium transition-colors ${
                  i === 0
                    ? "text-slate-900"
                    : "text-slate-500 hover:text-slate-900"
                }`}
              >
                {item.label}
              </a>
            ))}
          </div>
          <div className="flex items-center gap-3">
            <button
              type="button"
              aria-label="Open menu"
              className="rounded-md p-2 text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-900 md:hidden"
            >
              <Menu className="h-5 w-5" />
            </button>
            <button
              type="button"
              aria-label="Search"
              className="rounded-md p-2 text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-900"
            >
              <Search className="h-5 w-5" />
            </button>
          </div>
        </nav>

        {/* Hero grid */}
        <div className="grid items-center gap-10 py-10 lg:grid-cols-2 lg:gap-12 lg:py-16">
          {/* Left column: copy + CTAs */}
          <div className="max-w-xl">
            <h1 className="text-4xl font-extrabold leading-[1.1] tracking-tight text-slate-900 sm:text-5xl lg:text-[3.5rem]">
              {headline}
            </h1>
            <p className="mt-5 max-w-md text-base leading-relaxed text-slate-500 sm:text-lg">
              {subhead}
            </p>
            <div className="mt-7 flex flex-wrap items-center gap-5">
              <a
                href={primaryCta.href}
                className="inline-flex items-center justify-center rounded-md bg-[#ff6b4a] px-7 py-3.5 text-sm font-semibold text-white shadow-lg shadow-[#ff6b4a]/30 transition-all hover:-translate-y-0.5 hover:bg-[#ff5836] hover:shadow-xl hover:shadow-[#ff6b4a]/40"
              >
                {primaryCta.label}
              </a>
              <a
                href={secondaryCta.href}
                className="text-sm font-semibold text-[#ff6b4a] transition-colors hover:text-[#ff5836] hover:underline"
              >
                {secondaryCta.label}
              </a>
            </div>
          </div>

          {/* Right column: portrait composition */}
          <div className="relative mx-auto aspect-square w-full max-w-[480px]">
            {/* Outer dotted ring */}
            <svg
              aria-hidden
              className="absolute inset-0 h-full w-full"
              viewBox="0 0 100 100"
            >
              <circle
                cx="50"
                cy="50"
                r="49"
                fill="none"
                stroke="#ff6b4a"
                strokeWidth="0.25"
                strokeDasharray="0.6 1.4"
                opacity="0.5"
              />
              <circle
                cx="50"
                cy="50"
                r="44"
                fill="none"
                stroke="#7cc6d8"
                strokeWidth="0.2"
                strokeDasharray="0.8 1.2"
                opacity="0.4"
              />
            </svg>

            {/* Soft gradient fill */}
            <div
              className="absolute inset-[8%] rounded-full"
              style={{
                background:
                  "radial-gradient(circle at 30% 75%, #ffd9c8 0%, #ffe6d9 30%, #d8eef3 75%, #b9e1eb 100%)",
              }}
            />

            {/* Portrait — replace imageSrc prop with your own portrait */}
            <div className="absolute inset-[10%] overflow-hidden rounded-full">
              <img
                src={imageSrc}
                alt={imageAlt}
                width={500}
                height={500}
                loading="eager"
                fetchPriority="high"
                className="h-full w-full object-cover"
              />
            </div>

            {/* Floating: stat card */}
            <div className="absolute left-[-4%] top-[28%] flex w-[180px] items-center gap-3 rounded-2xl bg-white p-3 shadow-xl shadow-slate-200/60 sm:left-[-6%] sm:w-[200px]">
              <div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-[#e6f4f7] text-[#5db4c6]">
                <Trophy className="h-5 w-5" />
              </div>
              <div className="leading-tight">
                <div className="text-base font-bold text-slate-900">
                  {statValue}
                </div>
                <div className="text-[11px] text-slate-500">{statLabel}</div>
              </div>
            </div>

            {/* Floating: chart icon top-right */}
            <div className="absolute right-[2%] top-[8%] flex h-12 w-12 items-center justify-center rounded-full bg-white text-[#5db4c6] shadow-lg shadow-slate-200/60">
              <TrendingUp className="h-6 w-6" />
            </div>

            {/* Floating: experts card bottom-right */}
            <div className="absolute bottom-[10%] right-[-4%] rounded-2xl bg-white p-3 shadow-xl shadow-slate-200/60 sm:right-[-6%]">
              <div className="mb-1.5 text-[11px] text-slate-500">
                {expertsLabel}
              </div>
              <div className="flex items-center">
                <span className="-mr-2 inline-block h-7 w-7 rounded-full bg-gradient-to-br from-amber-200 to-amber-400 ring-2 ring-white" />
                <span className="-mr-2 inline-block h-7 w-7 rounded-full bg-gradient-to-br from-rose-200 to-rose-400 ring-2 ring-white" />
                <span className="inline-block h-7 w-7 rounded-full bg-gradient-to-br from-sky-200 to-sky-400 ring-2 ring-white" />
                <span className="ml-3 text-xs font-bold text-slate-700">
                  {expertsCount}
                </span>
              </div>
            </div>
          </div>
        </div>

        {/* Service strip */}
        <div className="pb-12 lg:pb-16">
          <p className="mb-4 text-sm font-semibold text-slate-700">
            {serviceLabel}
          </p>
          <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4">
            {services.map((service) => (
              <div
                key={service.title}
                className="flex items-center gap-3 rounded-2xl bg-[#e8f4f8] p-4 transition-shadow hover:shadow-md"
              >
                <div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-white text-[#5db4c6] shadow-sm">
                  {service.icon}
                </div>
                <span className="text-sm font-semibold leading-tight text-slate-900">
                  {service.title}
                </span>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
}
Claude Code Instructions

CLI Install

npx innovations add consultant-circle

Where to use it

Place this at the top of a consulting / service-business landing page, as the first section (it includes its own brand wordmark + nav strip). In Astro (src/pages/index.astro): --- import ConsultantCircleHero from '../components/innovations/heroes/consultant-circle'; --- <ConsultantCircleHero /> No client:* directive needed — this component is a Server Component / static render and ships zero JavaScript by default. In Next.js (app/page.tsx): import ConsultantCircleHero from '@/components/innovations/heroes/consultant-circle'; // Drop at the top of the page return. CUSTOMIZATION: All copy + image are exposed as props with sensible defaults: <ConsultantCircleHero brand="YourBrand" headline="Your headline here." subhead="Your subhead here." imageSrc="/your-portrait.png" statValue="12K+" statLabel="Clients served" services={[ { icon: <Monitor className="w-5 h-5" />, title: "Service One" }, // ... ]} /> ACCENT COLORS (orange #ff6b4a + teal #5db4c6) are hard-coded for visual fidelity. To rebrand: search/replace the 4 hex values across the file: #ff6b4a / #ff5836 → primary accent (CTA + brand) #5db4c6 / #7cc6d8 → secondary accent (cards + stroke) #e6f4f7 / #e8f4f8 → soft tint for stat-icon and service-card backgrounds NAV STRIP: the component includes its own header. If you already have a global navbar, delete the <nav> block (lines under the "Nav" comment) — the hero will collapse cleanly. PORTRAIT: replace imageSrc with a transparent-background portrait for the cleanest look against the gradient circle. Default Unsplash URL is for development only.