Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions app/services/[slug]/ExpertisePageClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const ArchitectureSection = dynamic(() => import("@/components/expertise/Archite
const EngagementModels = dynamic(() => import("@/components/expertise/EngagementModels").then(mod => ({ default: mod.EngagementModels })));
const RiskReversal = dynamic(() => import("@/components/expertise/RiskReversal").then(mod => ({ default: mod.RiskReversal })));
const PhilosophySection = dynamic(() => import("@/components/expertise/PhilosophySection").then(mod => ({ default: mod.PhilosophySection })));
const HowItWorks = dynamic(() => import("@/components/expertise/HowItWorks").then(mod => ({ default: mod.HowItWorks })));
const Testimonials = dynamic(() => import("@/components/sections/Testimonials").then(mod => ({ default: mod.Testimonials })));
const Stats = dynamic(() => import("@/components/sections/Stats").then(mod => ({ default: mod.Stats })));
const CalInline = dynamic(() => import("@/components/CalInline").then(mod => ({ default: mod.CalInline })));
Expand Down Expand Up @@ -354,10 +355,11 @@ export default function ExpertisePageClient({

{expertise.slug === "dotnet" && (
<Stats
title=".NET Development Track Record"
title="Proven Outcomes from Enterprise .NET Engineering"
subtitle="Procedure has been building production .NET systems for over a decade, from enterprise platforms to high-throughput APIs serving millions of requests."
stats={[
{ value: "40+", label: "Production .NET Systems" },
{ value: "6\u20138 Weeks", label: "Time to Production Release" },
{ value: "6–8 Weeks", label: "Time to Production Release" },
{ value: "95%+", label: "Client Retention Rate" },
{ value: ".NET 8 + Azure", label: "Primary Stack" },
]}
Expand All @@ -366,8 +368,8 @@ export default function ExpertisePageClient({

<div id={["nodejs", "nextjs", "react", "python", "angular", "flutter", "react-native", "prometheus-monitoring", "istio-consulting", "thanos-long-term-storage"].includes(expertise.slug) ? "services" : undefined}>
<CapabilitiesGrid
title={expertise.slug === "dotnet" ? ".NET Development Services We Offer" : expertise.slug === "nextjs" ? "What We Build With Next.js" : expertise.slug === "nodejs" ? "Node.js Development Services" : expertise.slug === "react" ? "React Development Services" : expertise.slug === "python" ? "Python Development Services" : expertise.slug === "angular" ? "Angular Development Services" : expertise.slug === "flutter" ? "What We Build With Flutter" : expertise.slug === "react-native" ? "What We Build With React Native" : expertise.slug === "prometheus-monitoring" ? "Prometheus Consulting & Implementation Services" : expertise.slug === "istio-consulting" ? "Istio Consulting & Implementation Services" : "Key Capabilities"}
subtitle={expertise.slug === "dotnet" ? "End-to-end .NET services, from greenfield builds to legacy modernization." : expertise.slug === "nextjs" ? "From marketing sites to complex web applications, we deliver production-grade Next.js solutions." : expertise.slug === "nodejs" ? "APIs, microservices, real-time systems, and the backend your product runs on." : expertise.slug === "react" ? "From SPAs to enterprise dashboards, we build React applications that ship fast and stay maintainable." : expertise.slug === "python" ? "Backend systems, AI/ML, and the data infrastructure your product depends on." : expertise.slug === "angular" ? "Full-stack applications, migrations, and performance work." : expertise.slug === "flutter" ? "Cross-platform apps for mobile, web, and desktop from a single Dart codebase." : expertise.slug === "react-native" ? "Cross-platform mobile apps from a single TypeScript codebase." : expertise.slug === "prometheus-monitoring" ? "From initial setup to enterprise production support." : expertise.slug === "istio-consulting" ? "From fresh deployment to ambient mode migration and enterprise support." : "Everything you need to build production-grade solutions"}
title={expertise.slug === "dotnet" ? ".NET Development Capabilities for Production-Grade Systems" : expertise.slug === "nextjs" ? "What We Build With Next.js" : expertise.slug === "nodejs" ? "Node.js Development Services" : expertise.slug === "react" ? "React Development Services" : expertise.slug === "python" ? "Python Development Services" : expertise.slug === "angular" ? "Angular Development Services" : expertise.slug === "flutter" ? "What We Build With Flutter" : expertise.slug === "react-native" ? "What We Build With React Native" : expertise.slug === "prometheus-monitoring" ? "Prometheus Consulting & Implementation Services" : expertise.slug === "istio-consulting" ? "Istio Consulting & Implementation Services" : "Key Capabilities"}
subtitle={expertise.slug === "dotnet" ? "Everything required to design, modernize, and operate production-grade .NET systems at scale." : expertise.slug === "nextjs" ? "From marketing sites to complex web applications, we deliver production-grade Next.js solutions." : expertise.slug === "nodejs" ? "APIs, microservices, real-time systems, and the backend your product runs on." : expertise.slug === "react" ? "From SPAs to enterprise dashboards, we build React applications that ship fast and stay maintainable." : expertise.slug === "python" ? "Backend systems, AI/ML, and the data infrastructure your product depends on." : expertise.slug === "angular" ? "Full-stack applications, migrations, and performance work." : expertise.slug === "flutter" ? "Cross-platform apps for mobile, web, and desktop from a single Dart codebase." : expertise.slug === "react-native" ? "Cross-platform mobile apps from a single TypeScript codebase." : expertise.slug === "prometheus-monitoring" ? "From initial setup to enterprise production support." : expertise.slug === "istio-consulting" ? "From fresh deployment to ambient mode migration and enterprise support." : "Everything you need to build production-grade solutions"}
capabilities={capabilities}
/>
</div>
Expand Down Expand Up @@ -437,12 +439,12 @@ export default function ExpertisePageClient({

{expertise.slug === "dotnet" ? (
<TechStack
title=".NET Technology Stack We Use"
title=".NET Technology Stack (Production-Proven)"
variant="grouped"
groups={[
{ category: "Runtime & Frameworks", items: [".NET 8 / .NET 9", "ASP.NET Core", "Blazor", "Entity Framework Core", "Minimal APIs"] },
{ category: "Cloud Platforms", items: ["Microsoft Azure", "AWS", "GCP"] },
{ category: "Infrastructure & DevOps", items: ["Docker", "Kubernetes", "Terraform", "GitHub Actions", "Azure DevOps"] },
{ category: "Infrastructure & DevOps", items: ["Docker", "[Kubernetes](/services/kubernetes)", "Terraform", "GitHub Actions", "Azure DevOps"] },
{ category: "Data & Caching", items: ["SQL Server", "PostgreSQL", "Redis", "Azure Cosmos DB"] },
{ category: "Observability", items: ["Application Insights", "Serilog", "OpenTelemetry", "Seq"] },
{ category: "Messaging & Patterns", items: ["RabbitMQ", "Azure Service Bus", "MediatR", "MassTransit"] },
Expand Down Expand Up @@ -696,6 +698,15 @@ export default function ExpertisePageClient({
</div>
)}

{pageData.howItWorks && (
<HowItWorks
title={pageData.howItWorks.title}
subtitle={pageData.howItWorks.subtitle}
steps={pageData.howItWorks.steps}
closingNote={pageData.howItWorks.closingNote}
/>
)}

{pageData.riskReversal && (
<RiskReversal
title={pageData.riskReversal.title}
Expand Down
3 changes: 2 additions & 1 deletion components/expertise/CapabilitiesGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { m } from "framer-motion";
import { ReactNode } from "react";
import { cn } from "@/lib/utils";
import { LinkedText } from "@/components/ui/LinkedText";

interface Capability {
icon: ReactNode;
Expand Down Expand Up @@ -92,7 +93,7 @@ export function CapabilitiesGrid({
{capability.title}
</h3>
<p className="text-text-secondary leading-relaxed">
{capability.description}
<LinkedText text={capability.description} />
</p>
</m.div>
))}
Expand Down
134 changes: 134 additions & 0 deletions components/expertise/HowItWorks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
"use client";

import { motion } from "framer-motion";
import { cn } from "@/lib/utils";

interface HowItWorksStep {
title: string;
description: string;
}

interface HowItWorksProps {
title?: string;
subtitle?: string;
steps: HowItWorksStep[];
closingNote?: string;
}

const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.12,
delayChildren: 0.2,
},
},
};

const stepVariants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: [0.16, 1, 0.3, 1] as const },
},
};

export function HowItWorks({
title = "How It Works",
subtitle,
steps,
closingNote,
}: HowItWorksProps) {
return (
<section className="py-16 sm:py-24 bg-base">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Section header */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="text-center mb-12 sm:mb-16"
>
<h2 className="text-3xl sm:text-4xl font-semibold tracking-tight text-text-primary mb-4">
{title}
</h2>
{subtitle && (
<p className="text-lg text-text-secondary max-w-3xl mx-auto">
{subtitle}
</p>
)}
</motion.div>

{/* Vertical timeline */}
<motion.div
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: "-80px" }}
variants={containerVariants}
className="relative"
>
{/* Timeline line */}
<div className="absolute left-[23px] sm:left-[27px] top-0 bottom-0 w-px bg-border" />

<div className="space-y-6">
{steps.map((step, index) => (
<motion.div
key={index}
variants={stepVariants}
className="relative flex gap-5 sm:gap-6"
>
{/* Step number circle */}
<div
className={cn(
"relative z-10 flex-shrink-0",
"w-[48px] h-[48px] sm:w-[56px] sm:h-[56px]",
"rounded-full flex items-center justify-center",
"bg-surface-elevated border-2 border-accent/30",
"group-hover:border-accent/60 transition-colors duration-300"
)}
>
<span className="text-base sm:text-lg font-bold text-accent-light">
{index + 1}
</span>
</div>

{/* Step content card */}
<div
className={cn(
"flex-1 p-5 sm:p-6 rounded-xl",
"bg-surface-elevated/80 backdrop-blur-sm",
"border border-border",
"hover:border-accent/30 transition-colors duration-300"
)}
>
<h3 className="text-lg font-semibold text-text-primary mb-2">
{step.title}
</h3>
<p className="text-sm sm:text-base text-text-secondary leading-relaxed">
{step.description}
</p>
</div>
</motion.div>
))}
</div>
</motion.div>

{/* Closing note */}
{closingNote && (
<motion.p
initial={{ opacity: 0, y: 12 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.3 }}
className="text-center text-base sm:text-lg text-accent-light font-medium mt-10 sm:mt-12"
>
{closingNote}
</motion.p>
)}
</div>
</section>
);
}
3 changes: 2 additions & 1 deletion components/expertise/TechStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { m } from "framer-motion";
import Link from "next/link";
import { TechIcon, hasTechIcon } from "@/lib/tech-icons";
import { LinkedText } from "@/components/ui/LinkedText";

// Technologies that have dedicated pages — map display name to slug
const techPageSlugs: Record<string, string> = {
Expand Down Expand Up @@ -116,7 +117,7 @@ export function TechStack({
key={itemIndex}
className="px-3 py-1.5 text-sm text-text-secondary bg-surface/80 border border-border rounded-lg hover:border-accent/20 hover:text-text-primary transition-colors duration-200"
>
{item}
<LinkedText text={item} />
</span>
))}
</div>
Expand Down
5 changes: 3 additions & 2 deletions components/expertise/WhoWeWorkWith.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { m, AnimatePresence } from "framer-motion";
import { ReactNode, useState } from "react";
import { cn } from "@/lib/utils";
import { LinkedText } from "@/components/ui/LinkedText";

interface TargetAudience {
icon: ReactNode;
Expand Down Expand Up @@ -246,9 +247,9 @@ export function WhoWeWorkWith({
<div>
{isObject ? (
<>
<span className="block text-text-primary font-medium text-sm mb-1">{item.title}</span>
<span className="block text-text-primary font-medium text-sm mb-1"><LinkedText text={item.title} /></span>
{item.description && (
<span className="block text-text-muted text-sm leading-relaxed">{item.description}</span>
<span className="block text-text-muted text-sm leading-relaxed"><LinkedText text={item.description} /></span>
)}
</>
) : (
Expand Down
1 change: 1 addition & 0 deletions components/expertise/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export { EngagementModels } from "./EngagementModels";
export { RiskReversal } from "./RiskReversal";
export { ExpertiseCTAWithTestimonial } from "./ExpertiseCTAWithTestimonial";
export { PhilosophySection } from "./PhilosophySection";
export { HowItWorks } from "./HowItWorks";
7 changes: 7 additions & 0 deletions components/sections/Stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ interface Stat {

interface StatsProps {
title?: string;
subtitle?: string;
stats?: Stat[];
}

export function Stats({
title = "Proven Results from Embedded Engineering",
subtitle,
stats = defaultStats,
}: StatsProps) {
return (
Expand All @@ -45,6 +47,11 @@ export function Stats({
<h2 className="text-2xl sm:text-3xl font-semibold tracking-tight text-text-primary">
{title}
</h2>
{subtitle && (
<p className="text-lg text-text-secondary max-w-3xl mx-auto mt-4">
{subtitle}
</p>
)}
</m.div>

<div
Expand Down
68 changes: 68 additions & 0 deletions components/ui/LinkedText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Link from "next/link";
import { Fragment } from "react";

/**
* Parses markdown-style links [text](url) in a string and renders them
* as Next.js Link components. Non-link text is rendered as plain text.
*
* Usage: <LinkedText text="Build [backend systems](/services/backend-development) at scale" />
*/
export function LinkedText({
text,
className,
}: {
text: string;
className?: string;
}) {
const parts = parseLinkedText(text);

if (parts.length === 1 && parts[0].type === "text") {
return <>{text}</>;
}

return (
<>
{parts.map((part, i) =>
part.type === "link" ? (
<Link
key={i}
href={part.href}
className={
className ??
"text-accent-light hover:text-accent underline underline-offset-2 decoration-accent/30 hover:decoration-accent/60 transition-colors"
}
>
{part.text}
</Link>
) : (
<Fragment key={i}>{part.text}</Fragment>
),
)}
</>
);
}

type TextPart =
| { type: "text"; text: string }
| { type: "link"; text: string; href: string };

function parseLinkedText(input: string): TextPart[] {
const regex = /\[([^\]]+)\]\(([^)]+)\)/g;
const parts: TextPart[] = [];
let lastIndex = 0;
let match: RegExpExecArray | null;

while ((match = regex.exec(input)) !== null) {
if (match.index > lastIndex) {
parts.push({ type: "text", text: input.slice(lastIndex, match.index) });
}
parts.push({ type: "link", text: match[1], href: match[2] });
lastIndex = match.index + match[0].length;
}

if (lastIndex < input.length) {
parts.push({ type: "text", text: input.slice(lastIndex) });
}

return parts;
}
Loading
Loading