Compare commits

...
4 Commits
Author SHA1 Message Date
platform-mcpandClaude Opus 4.8 4929a1c906 feat(services): apply CRO improvements (audit points 7–11)
E2E Test Suite / Smoke Tests (P0) (push) Failing after 9m26s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 10m9s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 11m1s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 11m53s
E2E Test Suite / Form Interaction Tests (push) Failing after 12m45s
Deploy to Production / Build & Verify (push) Has been cancelled
Deploy to Production / Pre-Deploy Tests (push) Has been cancelled
Deploy to Production / Deploy to Railway (push) Has been cancelled
Deploy to Production / Deploy to Render (push) Has been cancelled
Deploy to Production / Deploy to VPS (PM2) (push) Has been cancelled
Deploy to Production / Deploy to Fly.io (push) Has been cancelled
Deploy to Production / Post-Deploy Verification (push) Has been cancelled
Deploy to Production / Notify on Failure (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (webkit) (push) Has been cancelled
E2E Test Suite / Mobile Device Tests (push) Has been cancelled
E2E Test Suite / Security Header Tests (push) Has been cancelled
E2E Test Suite / Test Report Summary (push) Has been cancelled
Ping Search Engines / Notify Search Engines (push) Failing after 11m38s
- Replace generic H1 with outcome-led headline ("Custom software for
  enterprises and governments — shipped in weeks, not quarters.")
- Rewrite each service description to lead with the outcome and end
  with the tech stack
- Reorder service cards so Government IT and ERP/Enterprise lead,
  before Web/Mobile
- Add "What's included" label above each pricing tier's bullet list
  (Most Popular badge on Professional retained)
- Unify CTAs to one primary ("Get my free proposal") + one secondary
  ("See our work" → /portfolio)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 14:10:50 +05:30
platform-mcp 8192ed189a pre-agent: Apply services page CRO improvements 2026-06-17 14:06:10 +05:30
platform-mcp 5e81c4bab5 agent: Apply homepage CRO improvements 2026-06-17 14:05:47 +05:30
platform-mcp de0426cad9 pre-agent: Apply homepage CRO improvements 2026-06-17 13:58:15 +05:30
7 changed files with 289 additions and 235 deletions
+5 -5
View File
@@ -1,11 +1,11 @@
# Graph Report - 2e709500-79ab-403f-9ca0-3f027ac9f36e (2026-06-17)
## Corpus Check
- 57 files · ~321,797 words
- 57 files · ~323,450 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 520 nodes · 563 edges · 93 communities (86 shown, 7 thin omitted)
- 522 nodes · 567 edges · 93 communities (86 shown, 7 thin omitted)
- Extraction: 99% EXTRACTED · 1% INFERRED · 0% AMBIGUOUS · INFERRED: 4 edges (avg confidence: 0.8)
- Token cost: 0 input · 0 output
@@ -84,8 +84,8 @@ Cohesion: 0.05
Nodes (37): 1. `performance_test.py`, 2. `quick-perf-check.sh`, 3. `lighthouse_audit.py`, Available Scripts, Basic Response Time, Best Practices, CI/CD Integration, code:bash (# Quick check (recommended)) (+29 more)
### Community 2 - "Community 2"
Cohesion: 0.14
Nodes (27): checkRateLimit(), corsHeaders(), escapeHtml(), OPTIONS(), POST(), sendContactEmail(), validateContactForm(), checkRateLimit() (+19 more)
Cohesion: 0.13
Nodes (29): checkRateLimit(), corsHeaders(), escapeHtml(), getTransporter(), OPTIONS(), POST(), readSmtpConfig(), sendContactEmail() (+21 more)
### Community 3 - "Community 3"
Cohesion: 0.16
@@ -204,7 +204,7 @@ _Questions this graph is uniquely positioned to answer:_
- **Should `Community 1` be split into smaller, more focused modules?**
_Cohesion score 0.05 - nodes in this community are weakly interconnected._
- **Should `Community 2` be split into smaller, more focused modules?**
_Cohesion score 0.14 - nodes in this community are weakly interconnected._
_Cohesion score 0.13 - nodes in this community are weakly interconnected._
- **Should `Community 4` be split into smaller, more focused modules?**
_Cohesion score 0.09 - nodes in this community are weakly interconnected._
- **Should `Community 5` be split into smaller, more focused modules?**
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+65 -5
View File
@@ -138,11 +138,29 @@
"community": 2,
"norm_label": "validatecontactform()"
},
{
"label": "readSmtpConfig()",
"file_type": "code",
"source_file": "src/pages/api/contact.ts",
"source_location": "L146",
"id": "api_contact_readsmtpconfig",
"community": 2,
"norm_label": "readsmtpconfig()"
},
{
"label": "getTransporter()",
"file_type": "code",
"source_file": "src/pages/api/contact.ts",
"source_location": "L187",
"id": "api_contact_gettransporter",
"community": 2,
"norm_label": "gettransporter()"
},
{
"label": "sendContactEmail()",
"file_type": "code",
"source_file": "src/pages/api/contact.ts",
"source_location": "L168",
"source_location": "L294",
"id": "api_contact_sendcontactemail",
"community": 2,
"norm_label": "sendcontactemail()"
@@ -151,7 +169,7 @@
"label": "escapeHtml()",
"file_type": "code",
"source_file": "src/pages/api/contact.ts",
"source_location": "L316",
"source_location": "L466",
"id": "api_contact_escapehtml",
"community": 2,
"norm_label": "escapehtml()"
@@ -160,7 +178,7 @@
"label": "corsHeaders()",
"file_type": "code",
"source_file": "src/pages/api/contact.ts",
"source_location": "L330",
"source_location": "L480",
"id": "api_contact_corsheaders",
"community": 2,
"norm_label": "corsheaders()"
@@ -169,7 +187,7 @@
"label": "OPTIONS()",
"file_type": "code",
"source_file": "src/pages/api/contact.ts",
"source_location": "L355",
"source_location": "L505",
"id": "api_contact_options",
"community": 2,
"norm_label": "options()"
@@ -178,7 +196,7 @@
"label": "POST()",
"file_type": "code",
"source_file": "src/pages/api/contact.ts",
"source_location": "L362",
"source_location": "L512",
"id": "api_contact_post",
"community": 2,
"norm_label": "post()"
@@ -4823,6 +4841,26 @@
"source": "src_pages_api_contact_ts",
"target": "api_contact_validatecontactform"
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "src/pages/api/contact.ts",
"source_location": "L146",
"weight": 1.0,
"source": "src_pages_api_contact_ts",
"target": "api_contact_readsmtpconfig",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "src/pages/api/contact.ts",
"source_location": "L187",
"weight": 1.0,
"source": "src_pages_api_contact_ts",
"target": "api_contact_gettransporter",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
@@ -4895,6 +4933,28 @@
"source": "api_contact_post",
"target": "api_contact_validatecontactform"
},
{
"relation": "calls",
"context": "call",
"confidence": "EXTRACTED",
"source_file": "src/pages/api/contact.ts",
"source_location": "L295",
"weight": 1.0,
"source": "api_contact_sendcontactemail",
"target": "api_contact_readsmtpconfig",
"confidence_score": 1.0
},
{
"relation": "calls",
"context": "call",
"confidence": "EXTRACTED",
"source_file": "src/pages/api/contact.ts",
"source_location": "L310",
"weight": 1.0,
"source": "api_contact_sendcontactemail",
"target": "api_contact_gettransporter",
"confidence_score": 1.0
},
{
"relation": "calls",
"context": "call",
+113
View File
@@ -0,0 +1,113 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 720" width="1200" height="720" role="img" aria-label="WorkRoot project dashboard preview">
<defs>
<linearGradient id="wr-accent" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#22d3ee"/>
<stop offset="1" stop-color="#0891b2"/>
</linearGradient>
<linearGradient id="wr-bar" x1="0" y1="1" x2="0" y2="0">
<stop offset="0" stop-color="#0891b2"/>
<stop offset="1" stop-color="#22d3ee"/>
</linearGradient>
<linearGradient id="wr-area" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#0891b2" stop-opacity="0.35"/>
<stop offset="1" stop-color="#0891b2" stop-opacity="0"/>
</linearGradient>
</defs>
<!-- App canvas -->
<rect width="1200" height="720" fill="#f1f5f9"/>
<!-- Sidebar -->
<rect width="240" height="720" fill="#0f172a"/>
<circle cx="44" cy="52" r="14" fill="url(#wr-accent)"/>
<rect x="68" y="44" width="104" height="16" rx="6" fill="#e2e8f0"/>
<!-- Nav items -->
<rect x="20" y="108" width="200" height="40" rx="10" fill="#0e7490"/>
<rect x="40" y="120" width="18" height="16" rx="4" fill="#67e8f9"/>
<rect x="70" y="121" width="120" height="14" rx="5" fill="#e2e8f0"/>
<g fill="#334155">
<rect x="40" y="172" width="18" height="16" rx="4"/>
<rect x="40" y="224" width="18" height="16" rx="4"/>
<rect x="40" y="276" width="18" height="16" rx="4"/>
<rect x="40" y="328" width="18" height="16" rx="4"/>
</g>
<g fill="#475569">
<rect x="70" y="173" width="110" height="14" rx="5"/>
<rect x="70" y="225" width="96" height="14" rx="5"/>
<rect x="70" y="277" width="124" height="14" rx="5"/>
<rect x="70" y="329" width="84" height="14" rx="5"/>
</g>
<!-- Top header -->
<rect x="240" width="960" height="80" fill="#ffffff"/>
<line x1="240" y1="80" x2="1200" y2="80" stroke="#e2e8f0" stroke-width="2"/>
<rect x="276" y="30" width="220" height="20" rx="7" fill="#0f172a"/>
<rect x="980" y="26" width="150" height="28" rx="14" fill="#f1f5f9"/>
<circle cx="1150" cy="40" r="18" fill="url(#wr-accent)"/>
<!-- Stat cards -->
<g>
<rect x="276" y="116" width="280" height="120" rx="16" fill="#ffffff" stroke="#e2e8f0" stroke-width="2"/>
<rect x="300" y="140" width="100" height="12" rx="5" fill="#94a3b8"/>
<text x="300" y="196" font-family="Plus Jakarta Sans, Segoe UI, sans-serif" font-size="44" font-weight="700" fill="#0f172a">20+</text>
<rect x="476" y="160" width="56" height="56" rx="14" fill="#ecfeff"/>
<rect x="492" y="180" width="24" height="16" rx="4" fill="#0891b2"/>
</g>
<g>
<rect x="588" y="116" width="280" height="120" rx="16" fill="#ffffff" stroke="#e2e8f0" stroke-width="2"/>
<rect x="612" y="140" width="120" height="12" rx="5" fill="#94a3b8"/>
<text x="612" y="196" font-family="Plus Jakarta Sans, Segoe UI, sans-serif" font-size="44" font-weight="700" fill="#0f172a">11</text>
<rect x="788" y="160" width="56" height="56" rx="14" fill="#fffbeb"/>
<rect x="804" y="180" width="24" height="16" rx="4" fill="#f59e0b"/>
</g>
<g>
<rect x="900" y="116" width="280" height="120" rx="16" fill="#ffffff" stroke="#e2e8f0" stroke-width="2"/>
<rect x="924" y="140" width="140" height="12" rx="5" fill="#94a3b8"/>
<text x="924" y="196" font-family="Plus Jakarta Sans, Segoe UI, sans-serif" font-size="44" font-weight="700" fill="#0f172a">50,000+</text>
<rect x="1100" y="160" width="56" height="56" rx="14" fill="#ecfeff"/>
<rect x="1116" y="180" width="24" height="16" rx="4" fill="#0891b2"/>
</g>
<!-- Chart card -->
<rect x="276" y="268" width="556" height="416" rx="16" fill="#ffffff" stroke="#e2e8f0" stroke-width="2"/>
<rect x="300" y="296" width="160" height="16" rx="6" fill="#0f172a"/>
<rect x="300" y="324" width="100" height="12" rx="5" fill="#94a3b8"/>
<!-- Area line -->
<path d="M300 560 L370 520 L440 540 L510 470 L580 500 L650 430 L720 450 L808 380"
fill="none" stroke="url(#wr-bar)" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M300 560 L370 520 L440 540 L510 470 L580 500 L650 430 L720 450 L808 380 L808 650 L300 650 Z"
fill="url(#wr-area)"/>
<g fill="#0891b2">
<circle cx="370" cy="520" r="6"/>
<circle cx="510" cy="470" r="6"/>
<circle cx="650" cy="430" r="6"/>
<circle cx="808" cy="380" r="6"/>
</g>
<line x1="300" y1="650" x2="808" y2="650" stroke="#e2e8f0" stroke-width="2"/>
<!-- Activity / table card -->
<rect x="860" y="268" width="320" height="416" rx="16" fill="#ffffff" stroke="#e2e8f0" stroke-width="2"/>
<rect x="884" y="296" width="150" height="16" rx="6" fill="#0f172a"/>
<g>
<circle cx="900" cy="356" r="12" fill="#ecfeff"/>
<rect x="924" y="344" width="180" height="12" rx="5" fill="#334155"/>
<rect x="924" y="364" width="110" height="10" rx="5" fill="#94a3b8"/>
</g>
<g>
<circle cx="900" cy="412" r="12" fill="#fffbeb"/>
<rect x="924" y="400" width="160" height="12" rx="5" fill="#334155"/>
<rect x="924" y="420" width="130" height="10" rx="5" fill="#94a3b8"/>
</g>
<g>
<circle cx="900" cy="468" r="12" fill="#ecfeff"/>
<rect x="924" y="456" width="190" height="12" rx="5" fill="#334155"/>
<rect x="924" y="476" width="96" height="10" rx="5" fill="#94a3b8"/>
</g>
<g>
<circle cx="900" cy="524" r="12" fill="#fffbeb"/>
<rect x="924" y="512" width="150" height="12" rx="5" fill="#334155"/>
<rect x="924" y="532" width="120" height="10" rx="5" fill="#94a3b8"/>
</g>
<rect x="884" y="600" width="272" height="48" rx="12" fill="url(#wr-accent)"/>
<rect x="980" y="617" width="80" height="14" rx="6" fill="#ffffff"/>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

+38 -163
View File
@@ -119,15 +119,11 @@ const testimonials = [
},
];
// Stats data — canonical site-wide proof set. Keep this identical to the
// ProofNumbers band, the /portfolio stats bar, and the /about stats grid so
// a visitor browsing multiple pages never sees a conflicting number.
const stats = [
{ value: 20, suffix: '+', label: 'Projects', icon: 'briefcase' },
{ value: 11, suffix: '', label: 'Districts Deployed', icon: 'building' },
{ value: 50000, suffix: '+', label: 'Records Managed', icon: 'database' },
{ value: 8, suffix: '+', label: 'Years Avg Experience', icon: 'clock' },
];
// NOTE: The homepage stat band is rendered once by <ProofNumbers />, which
// holds the canonical site-wide proof set (20+ Projects · 11 Districts ·
// 50,000+ Records · 8+ Years). The previously-duplicated "Numbers That
// Matter" stats section (and its local `stats` array + count-up script) was
// removed so a visitor never sees the same four numbers twice on one page.
// Technology stack data — relocated below the fold so social proof leads the
// hero. Each entry pairs the framework name with a short context line and a
@@ -212,7 +208,7 @@ const techStack = [
</h1>
<p class="mt-8 text-xl sm:text-2xl text-secondary-200 max-w-3xl mx-auto opacity-0 animate-slide-up leading-relaxed" style="animation-delay: 0.6s;">
WorkRoot designs, builds, and runs production-grade web, mobile, and ERP systems for startups, enterprises, and government bodies.
We design, build, and run production-grade web, mobile, and ERP systems for startups, enterprises, and government bodies.
</p>
<!-- Dual CTA Buttons — action verb + clear secondary -->
@@ -236,7 +232,7 @@ const techStack = [
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
</svg>
See our work
See 20+ case studies
</a>
</div>
@@ -254,6 +250,35 @@ const techStack = [
<span>No commitment</span>
</p>
<!-- Product preview — framed dashboard mock so the hero leads with a
concrete product visual, not text alone. The image is a polished
placeholder UI (public/images/product-dashboard.svg); swap in a
real product screenshot or screen-recording poster frame
(≥1200×720, .webp/.png) at the same path — no template change
needed. Explicit width/height keep CLS at zero while it loads. -->
<figure class="mt-16 sm:mt-20 max-w-4xl mx-auto opacity-0 animate-slide-up" style="animation-delay: 1s;">
<div class="relative rounded-2xl border border-white/15 bg-white/5 backdrop-blur-sm shadow-2xl shadow-primary/20 overflow-hidden">
<!-- Glow behind the frame -->
<div class="absolute -inset-px rounded-2xl bg-gradient-to-tr from-primary-500/10 via-transparent to-accent-400/10 pointer-events-none" aria-hidden="true"></div>
<!-- Browser chrome -->
<div class="flex items-center gap-2 px-4 py-3 border-b border-white/10 bg-white/5" aria-hidden="true">
<span class="w-3 h-3 rounded-full bg-red-400/80"></span>
<span class="w-3 h-3 rounded-full bg-accent-400/80"></span>
<span class="w-3 h-3 rounded-full bg-green-400/80"></span>
<span class="ml-4 inline-flex items-center h-6 px-3 rounded-md bg-white/10 text-xs text-secondary-300 font-medium">app.workroot.in/dashboard</span>
</div>
<img
src="/images/product-dashboard.svg"
alt="WorkRoot project dashboard preview — delivery metrics, activity timeline, and deployment overview"
width="1200"
height="720"
loading="eager"
decoding="async"
class="block w-full h-auto"
/>
</div>
</figure>
</div>
</div>
@@ -426,54 +451,6 @@ const techStack = [
</div>
</section>
<!-- Stats/Metrics Section - Enhanced -->
<section class="py-24 bg-white dark:bg-secondary-900">
<div class="container-wrapper">
<div class="text-center max-w-3xl mx-auto mb-16" data-animate="fade-up">
<span class="inline-block px-4 py-2 rounded-full bg-accent-50 dark:bg-accent-900/20 text-accent-700 dark:text-accent-300 text-sm font-bold uppercase tracking-wider mb-4">Track Record</span>
<h2 class="text-4xl sm:text-5xl font-bold text-secondary-900 dark:text-secondary-100 mb-6">Numbers That Matter</h2>
<p class="text-xl text-secondary-600 dark:text-secondary-400">
Our success is measured by the impact we create for our clients.
</p>
</div>
<div class="grid grid-cols-2 lg:grid-cols-4 gap-8">
{stats.map((stat, index) => (
<div class="group text-center bg-gradient-to-br from-secondary-50 to-white dark:from-secondary-800 dark:to-secondary-800/80 rounded-3xl p-10 border-2 border-secondary-100 dark:border-secondary-700 hover:border-primary-400 hover:shadow-2xl transition-all duration-500 hover:-translate-y-2" data-animate="scale-up" data-delay={String(index * 100)}>
{/* Icon */}
<div class="w-16 h-16 rounded-2xl bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center mx-auto mb-6 group-hover:scale-110 group-hover:rotate-12 transition-all duration-500 shadow-lg">
{stat.icon === 'briefcase' && (
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
</svg>
)}
{stat.icon === 'database' && (
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"></path>
</svg>
)}
{stat.icon === 'building' && (
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"></path>
</svg>
)}
{stat.icon === 'clock' && (
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
)}
</div>
<div class="stat-counter text-5xl font-bold text-secondary-900 dark:text-secondary-100 mb-2" data-target={stat.value} data-suffix={stat.suffix}>
{stat.value.toLocaleString('en-IN')}{stat.suffix}
</div>
<div class="text-secondary-600 dark:text-secondary-400 font-semibold">{stat.label}</div>
</div>
))}
</div>
</div>
</section>
<!-- Testimonials Section - Enhanced -->
<section class="py-24 bg-gradient-to-b from-secondary-50 to-white dark:from-secondary-900 dark:to-secondary-900">
<div class="container-wrapper">
@@ -681,7 +658,7 @@ const techStack = [
<div class="container-wrapper text-center relative z-10">
<div class="max-w-4xl mx-auto" data-animate="fade-up">
<h2 class="text-4xl sm:text-5xl lg:text-6xl font-bold text-white mb-8 leading-tight">
Ready to ship your next <span class="text-transparent bg-clip-text bg-gradient-to-r from-primary-300 to-accent-400">production system?</span>
Ready to ship in <span class="text-transparent bg-clip-text bg-gradient-to-r from-primary-300 to-accent-400">8 weeks</span> instead of 8 months?
</h2>
<p class="text-xl sm:text-2xl text-secondary-200 mb-10 max-w-2xl mx-auto leading-relaxed">
Tell us about your project and we'll send back a free, no-obligation scope and timeline within one business day.
@@ -722,7 +699,7 @@ const techStack = [
<svg class="mr-2 w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path>
</svg>
See our work
See 20+ case studies
</a>
</div>
@@ -805,67 +782,11 @@ const techStack = [
animation: pulse-slower 6s ease-in-out infinite;
}
/* Stat counter animation */
.stat-counter {
font-variant-numeric: tabular-nums;
}
/* (FAQ accordion styles moved into <HomepageFaq />, which now renders the
only homepage FAQ section.) */
</style>
<script>
// Stats counter animation
// The initial textContent already renders the final value (server-side),
// so users with no JS / failed hydration / reduced-motion still see the
// correct number. The animation here briefly resets to 0 and counts up.
function animateCounters() {
const counters = document.querySelectorAll<HTMLElement>('.stat-counter');
const duration = 2000;
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
counters.forEach((counter) => {
const target = parseInt(counter.getAttribute('data-target') || '0', 10);
const suffix = counter.getAttribute('data-suffix') || '';
// Guaranteed final state — set the resolved value up front so a
// mid-animation failure can never leave the user staring at "0".
// Use the Indian locale grouping so large counters (e.g. 50,000+)
// render with the same comma format as the server-rendered markup.
const finalText = target.toLocaleString('en-IN') + suffix;
if (reduceMotion || !Number.isFinite(target) || target <= 0) {
counter.textContent = finalText;
return;
}
// Avoid running the animation more than once per counter.
if (counter.dataset.counted === 'true') return;
counter.dataset.counted = 'true';
const startTime = performance.now();
counter.textContent = (0).toLocaleString('en-IN') + suffix;
function updateCounter(currentTime: number) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const easeOutQuart = 1 - Math.pow(1 - progress, 4);
const current = Math.floor(easeOutQuart * target);
counter.textContent = current.toLocaleString('en-IN') + suffix;
if (progress < 1) {
requestAnimationFrame(updateCounter);
} else {
// Snap to exact final value to avoid floor-rounding drift.
counter.textContent = finalText;
}
}
requestAnimationFrame(updateCounter);
});
}
// Testimonials carousel
function initCarousel() {
const track = document.querySelector('.testimonial-track') as HTMLElement;
@@ -946,53 +867,7 @@ const techStack = [
// (FAQ accordion script moved into <HomepageFaq />, which now renders the
// only homepage FAQ section and ships its own inline init.)
// Intersection Observer for counter animation
function initStatsCounter() {
const firstCounter = document.querySelector('.stat-counter');
const statsSection = firstCounter?.closest('section');
// If the markup isn't there for any reason, bail silently — the
// server-rendered numbers are already correct.
if (!statsSection) return;
// Older browsers / failed feature detection: just render the final
// values immediately. They're already in the DOM, but this also
// protects against the path where data-target was patched at runtime.
if (typeof IntersectionObserver === 'undefined') {
animateCounters();
return;
}
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
animateCounters();
observer.disconnect();
}
});
},
// Lower threshold so the animation fires reliably on tall mobile
// viewports; rootMargin lets it kick in just before the section
// scrolls into view for a smoother first impression.
{ threshold: 0.1, rootMargin: '0px 0px -10% 0px' }
);
observer.observe(statsSection);
// If the section is already in the viewport at load time the
// observer fires on its next tick, but we also guard with a fallback
// so anyone who lands mid-scroll never sees the static "0" path.
const rect = statsSection.getBoundingClientRect();
const inView = rect.top < window.innerHeight && rect.bottom > 0;
if (inView) {
animateCounters();
observer.disconnect();
}
}
function bootHomepageScripts() {
initStatsCounter();
initCarousel();
}
+63 -58
View File
@@ -6,12 +6,58 @@ import BaseLayout from '../layouts/BaseLayout.astro';
import SEO from '../components/SEO.astro';
// Service data with extended details
// Order is intentional: Government IT and ERP/Enterprise lead (highest-value
// outcomes), followed by Web, Mobile, Design, and Consulting.
const services = [
{
id: 'government-systems',
title: 'Government IT Solutions',
shortDescription: 'Digitize public services across districts with secure, audit-ready systems citizens actually trust. Built on Spring Boot + Astro + PostgreSQL.',
fullDescription: 'Serve more citizens with fewer queues. We build secure, compliant systems that are already live across 11+ districts handling critical public services — digital records, real-time reporting, and 99.9% uptime under strict security standards. Built on Spring Boot, Astro, Golang, PostgreSQL, and MySQL.',
icon: 'government',
features: [
'Smart city solutions (SCMS)',
'Multi-district deployment',
'Secure & compliant architecture',
'Digital record keeping',
'Real-time reporting dashboards',
'High availability infrastructure',
],
techStack: ['Spring Boot', 'Astro', 'Golang', 'PostgreSQL', 'MySQL'],
stats: { projects: '3+', districts: '11+', uptime: '99.9%' },
gradient: 'from-red-500 to-orange-700',
bgLight: 'bg-red-50',
iconColor: 'text-red-500',
accentBorder: 'border-red-500',
tagBg: 'bg-red-100 text-red-700',
},
{
id: 'erp-solutions',
title: 'ERP & Enterprise Systems',
shortDescription: 'Replace 5 spreadsheets with one system your team actually uses. Built on Spring Boot + Angular.',
fullDescription: 'Replace scattered spreadsheets and manual handoffs with one system your team actually uses. From university management to personnel administration, we ship ERP platforms that handle complex workflows, role-based access, and 20,000+ users without breaking a sweat. Built on Spring Boot, Angular, Next.js, MySQL, and PostgreSQL.',
icon: 'enterprise',
features: [
'Custom ERP development',
'University management systems',
'Personnel management platforms',
'Role-based access control',
'Dashboard & reporting',
'Multi-tenant architecture',
],
techStack: ['Spring Boot', 'Angular', 'Next.js', 'MySQL', 'PostgreSQL'],
stats: { deployments: '5+', users: '20,000+', districts: '11' },
gradient: 'from-purple-500 to-indigo-700',
bgLight: 'bg-purple-50',
iconColor: 'text-purple-500',
accentBorder: 'border-purple-500',
tagBg: 'bg-purple-100 text-purple-700',
},
{
id: 'web-development',
title: 'Web Development',
shortDescription: 'Custom web applications built with Angular, React, Next.js, Astro, and Spring Boot.',
fullDescription: 'From responsive websites to complex web applications, we deliver high-performance solutions using Angular, React, Next.js, and Astro on the frontend with Spring Boot and Golang backends. Our web solutions are optimized for speed, security, and SEO.',
shortDescription: 'Turn visitors into customers with fast, SEO-ready web apps that load in under 2 seconds. Built on Angular, React, Next.js + Spring Boot.',
fullDescription: 'Turn visitors into customers with web apps that load in under 2 seconds and rank on day one. From responsive sites to complex SPAs, we deliver high-performance, secure, SEO-ready solutions tuned for conversion. Built on Angular, React, Next.js, and Astro with Spring Boot and Golang backends.',
icon: 'web',
features: [
'Custom web application development',
@@ -32,8 +78,8 @@ const services = [
{
id: 'mobile-development',
title: 'Mobile App Development',
shortDescription: 'Cross-platform mobile apps with Expo (React Native) for iOS and Android.',
fullDescription: 'We build cross-platform mobile applications using Expo and React Native, delivering native-like performance on both iOS and Android. Our mobile apps feature offline-first architecture, push notifications, and seamless backend integration.',
shortDescription: 'Put your business in every customer\'s pocket with one app for iOS and Android. Built on Expo + React Native.',
fullDescription: 'Put your business in every customer\'s pocket with one codebase for iOS and Android. We ship cross-platform apps with native-like performance, offline-first architecture, push notifications, and seamless backend integration — published to both stores. Built on Expo, React Native, Spring Boot, and PostgreSQL.',
icon: 'mobile',
features: [
'Expo / React Native development',
@@ -51,55 +97,11 @@ const services = [
accentBorder: 'border-green-500',
tagBg: 'bg-green-100 text-green-700',
},
{
id: 'erp-solutions',
title: 'ERP & Enterprise Systems',
shortDescription: 'Custom ERP, CRM, and management systems for enterprises and institutions.',
fullDescription: 'We design and build custom ERP solutions tailored to your organization. From university management systems to police administration platforms, our enterprise solutions handle complex workflows, role-based access, and large-scale data management.',
icon: 'enterprise',
features: [
'Custom ERP development',
'University management systems',
'Personnel management platforms',
'Role-based access control',
'Dashboard & reporting',
'Multi-tenant architecture',
],
techStack: ['Spring Boot', 'Angular', 'Next.js', 'MySQL', 'PostgreSQL'],
stats: { deployments: '5+', users: '20,000+', districts: '11' },
gradient: 'from-purple-500 to-indigo-700',
bgLight: 'bg-purple-50',
iconColor: 'text-purple-500',
accentBorder: 'border-purple-500',
tagBg: 'bg-purple-100 text-purple-700',
},
{
id: 'government-systems',
title: 'Government IT Solutions',
shortDescription: 'Scalable technology solutions for government departments and smart city initiatives.',
fullDescription: 'We partner with government bodies to deliver secure, scalable, and compliant technology solutions. Our systems are deployed across multiple districts and handle critical public services with high availability and strict security standards.',
icon: 'government',
features: [
'Smart city solutions (SCMS)',
'Multi-district deployment',
'Secure & compliant architecture',
'Digital record keeping',
'Real-time reporting dashboards',
'High availability infrastructure',
],
techStack: ['Astro', 'Golang', 'Spring Boot', 'PostgreSQL', 'MySQL'],
stats: { projects: '3+', districts: '11+', uptime: '99.9%' },
gradient: 'from-red-500 to-orange-700',
bgLight: 'bg-red-50',
iconColor: 'text-red-500',
accentBorder: 'border-red-500',
tagBg: 'bg-red-100 text-red-700',
},
{
id: 'ui-ux-design',
title: 'UI/UX Design',
shortDescription: 'Modern, responsive UI/UX design with TailwindCSS and design systems.',
fullDescription: 'We create intuitive, accessible, and visually appealing user interfaces. Our design process includes wireframing, prototyping, usability testing, and implementation with TailwindCSS and modern component libraries.',
shortDescription: 'Win trust in the first 5 seconds with interfaces that convert and meet WCAG. Built on TailwindCSS + Figma.',
fullDescription: 'Win trust in the first 5 seconds with interfaces that are intuitive, accessible, and on-brand. Our process — wireframing, prototyping, and usability testing — turns into pixel-perfect, WCAG-compliant builds with dark mode out of the box. Built on TailwindCSS, Figma, CSS3, and HTML5.',
icon: 'design',
features: [
'Responsive web design',
@@ -120,8 +122,8 @@ const services = [
{
id: 'it-consulting',
title: 'IT Consulting & DevOps',
shortDescription: 'Technical consulting, cloud deployment, CI/CD pipelines, and DevOps services.',
fullDescription: 'We help organizations optimize their technology infrastructure with expert consulting, cloud migration, CI/CD pipeline setup, and DevOps best practices. From architecture review to production deployment, we ensure your systems run efficiently.',
shortDescription: 'Ship faster and sleep better with automated deploys and 99.9% uptime. Built on Docker, Nginx + GitHub Actions.',
fullDescription: 'Ship faster and sleep better. We optimize your infrastructure with expert consulting, cloud migration, CI/CD automation, and DevOps best practices — taking you from architecture review to a production deployment that just runs. Built on Docker, Nginx, Linux, Git, and GitHub Actions.',
icon: 'consulting',
features: [
'Architecture review & consulting',
@@ -329,9 +331,9 @@ const processSteps = [
Our Services
</span>
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold text-white mb-6 leading-tight">
Solutions That
Custom software for enterprises and governments —
<span class="text-transparent bg-clip-text bg-gradient-to-r from-primary-400 via-primary-300 to-accent-400">
Power Growth
shipped in weeks, not quarters.
</span>
</h1>
<p class="text-lg sm:text-xl text-secondary-300 max-w-2xl mx-auto mb-10 leading-relaxed">
@@ -723,10 +725,10 @@ const processSteps = [
<div class="mt-10 text-center">
<a
href="/contact"
href="/portfolio"
class="inline-flex items-center gap-2 text-primary-600 hover:text-primary-700 font-semibold text-sm transition-colors duration-200 group"
>
Start your success story
See our work
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 group-hover:translate-x-1 transition-transform duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
@@ -780,6 +782,9 @@ const processSteps = [
</div>
</div>
<p class={`text-xs font-semibold uppercase tracking-wider mb-3 ${tier.highlighted ? 'text-secondary-400' : 'text-secondary-400 dark:text-secondary-500'}`}>
What's included
</p>
<ul class="space-y-3 mb-8 flex-1">
{tier.features.map((feature) => (
<li class="flex items-start gap-3">
@@ -892,10 +897,10 @@ const processSteps = [
</svg>
</a>
<a
href="/about"
href="/portfolio"
class="inline-flex items-center justify-center px-8 py-4 border-2 border-white/40 text-white font-semibold rounded-xl hover:bg-white/10 hover:border-white/60 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-primary-600"
>
Learn About Us
See our work
</a>
</div>