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-circleWhere 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.