@@ -40,12 +40,12 @@ import LogoPattern from "../assets/logo-pattern.png";
4040 <div class =" mx-auto max-w-7xl" >
4141 <div class =" flex flex-col items-center justify-between lg:flex-row" >
4242 <div class =" mb-8 text-center lg:mb-0 lg:w-1/2 lg:text-left" >
43- <h1 class =" mb-6 text-4xl font-bold text-white md: text-6xl " >
44- Web and Blockchain< br /> Development Agency
43+ <h1 class =" text-4xl md: text-6xl font-bold mb-6 text-white " >
44+ Software Development Partner
4545 </h1 >
4646 <p class =" mb-8 text-xl leading-relaxed text-gray-300 md:text-2xl" >
47- Our fully remote developer team can help <br />
48- you spot problems early and ship sooner
47+ Plan, build, and scale your software with a senior partner. <br />
48+ From discovery to delivery, we integrate with your team and ship reliably.
4949 </p >
5050 <div class =" flex flex-wrap justify-center gap-4 lg:justify-start" >
5151 <a
@@ -66,6 +66,123 @@ import LogoPattern from "../assets/logo-pattern.png";
6666 </div >
6767 </section >
6868
69+ <!-- Services Section (selector + detail) -->
70+ <section id =" services" class =" bg-gray-900 py-16" >
71+ <div class =" mx-auto max-w-7xl px-4 sm:px-6 lg:px-8" >
72+ <div class =" mb-12 text-center" >
73+ <h2 class =" mb-4 text-3xl font-bold md:text-4xl" >What We Do</h2 >
74+ <p class =" text-lg text-gray-300" >Pragmatic engineering to ship, scale, and sustain products</p >
75+ </div >
76+
77+ <div class =" grid gap-8 md:grid-cols-4" >
78+ <!-- Selector -->
79+ <div class =" md:col-span-1" >
80+ <div role =" tablist" aria-label =" Services" class =" flex flex-col gap-2" >
81+ <button data-service-button =" web" role =" tab" aria-selected =" true" class =" cursor-pointer rounded-lg border border-gray-700 px-4 py-3 text-left text-gray-200 hover:border-blue-500 hover:text-white focus:outline-none focus:ring-2 focus:ring-blue-600" >
82+ Web Applications
83+ </button >
84+ <button data-service-button =" extensions" role =" tab" aria-selected =" false" class =" cursor-pointer rounded-lg border border-gray-700 px-4 py-3 text-left text-gray-200 hover:border-blue-500 hover:text-white focus:outline-none focus:ring-2 focus:ring-blue-600" >
85+ Niche Extensions
86+ </button >
87+ <button data-service-button =" ai" role =" tab" aria-selected =" false" class =" cursor-pointer rounded-lg border border-gray-700 px-4 py-3 text-left text-gray-200 hover:border-blue-500 hover:text-white focus:outline-none focus:ring-2 focus:ring-blue-600" >
88+ AI & LLM Apps
89+ </button >
90+ <button data-service-button =" blockchain" role =" tab" aria-selected =" false" class =" cursor-pointer rounded-lg border border-gray-700 px-4 py-3 text-left text-gray-200 hover:border-blue-500 hover:text-white focus:outline-none focus:ring-2 focus:ring-blue-600" >
91+ Blockchain & Smart Contracts
92+ </button >
93+ <button data-service-button =" legacy" role =" tab" aria-selected =" false" class =" cursor-pointer rounded-lg border border-gray-700 px-4 py-3 text-left text-gray-200 hover:border-blue-500 hover:text-white focus:outline-none focus:ring-2 focus:ring-blue-600" >
94+ Legacy System Recovery
95+ </button >
96+ <button data-service-button =" cost" role =" tab" aria-selected =" false" class =" cursor-pointer rounded-lg border border-gray-700 px-4 py-3 text-left text-gray-200 hover:border-blue-500 hover:text-white focus:outline-none focus:ring-2 focus:ring-blue-600" >
97+ Cost Optimization
98+ </button >
99+ </div >
100+ </div >
101+
102+ <!-- Detail -->
103+ <div class =" md:col-span-3" >
104+ <div class =" rounded-lg bg-gray-800 p-8 shadow-lg" >
105+ <div data-service-panel =" web" >
106+ <h3 class =" mb-3 text-2xl font-bold text-white" >Web Applications</h3 >
107+ <p class =" text-gray-300" >
108+ Product discovery, architecture, and implementation of robust web apps using modern frameworks.
109+ We focus on clear domain models, great UX, and maintainable code.
110+ </p >
111+ <ul class =" mt-6 grid list-disc gap-2 pl-5 text-gray-300" >
112+ <li >End-to-end feature delivery</li >
113+ <li >APIs and integrations</li >
114+ <li >Performance and accessibility</li >
115+ </ul >
116+ </div >
117+
118+ <div data-service-panel =" extensions" class =" hidden" >
119+ <h3 class =" mb-3 text-2xl font-bold text-white" >Niche Extensions</h3 >
120+ <p class =" text-gray-300" >
121+ We have a deep experience developing niche extensions, including Adobe Illustrator and Chrome extensions, we are keen to expand our portfolio to other niche extensions.
122+ </p >
123+ <ul class =" mt-6 grid list-disc gap-2 pl-5 text-gray-300" >
124+ <li >Manifest V3 ready</li >
125+ <li >Content scripts and messaging</li >
126+ <li >Publishing and updates</li >
127+ </ul >
128+ </div >
129+
130+ <div data-service-panel =" ai" class =" hidden" >
131+ <h3 class =" mb-3 text-2xl font-bold text-white" >AI & LLM Apps</h3 >
132+ <p class =" text-gray-300" >
133+ Production-ready AI capabilities: retrieval, agents, evals, and observability integrated into your product.
134+ </p >
135+ <ul class =" mt-6 grid list-disc gap-2 pl-5 text-gray-300" >
136+ <li >RAG pipelines and embeddings</li >
137+ <li >Tool-using agents and workflows</li >
138+ <li >Voice-powered assistants</li >
139+ </ul >
140+ </div >
141+
142+ <div data-service-panel =" blockchain" class =" hidden" >
143+ <h3 class =" mb-3 text-2xl font-bold text-white" >Blockchain & Smart Contracts</h3 >
144+ <p class =" text-gray-300" >
145+ Practical on-chain solutions with off-chain services when needed. Security-minded reviews and
146+ testable contracts with clean deployment workflows.
147+ </p >
148+ <ul class =" mt-6 grid list-disc gap-2 pl-5 text-gray-300" >
149+ <li >Contract design and audits</li >
150+ <li >Indexing and data pipelines</li >
151+ <li >Wallet and dApp integration</li >
152+ </ul >
153+ </div >
154+
155+ <div data-service-panel =" legacy" class =" hidden" >
156+ <h3 class =" mb-3 text-2xl font-bold text-white" >Legacy System Recovery</h3 >
157+ <p class =" text-gray-300" >
158+ Stabilize, document, and incrementally modernize critical systems without halting business. We
159+ reduce risk with small, measurable steps.
160+ </p >
161+ <ul class =" mt-6 grid list-disc gap-2 pl-5 text-gray-300" >
162+ <li >Readability and tests first</li >
163+ <li >Strangler-fig migrations</li >
164+ <li >Observability and error budgets</li >
165+ </ul >
166+ </div >
167+
168+ <div data-service-panel =" cost" class =" hidden" >
169+ <h3 class =" mb-3 text-2xl font-bold text-white" >Cost Optimization</h3 >
170+ <p class =" text-gray-300" >
171+ Measure first, then optimize. We tune infrastructure and code paths to cut spend while keeping
172+ reliability and user experience intact.
173+ </p >
174+ <ul class =" mt-6 grid list-disc gap-2 pl-5 text-gray-300" >
175+ <li >Profiling and benchmarks</li >
176+ <li >Cloud architecture reviews</li >
177+ <li >Caching and query tuning</li >
178+ </ul >
179+ </div >
180+ </div >
181+ </div >
182+ </div >
183+ </div >
184+ </section >
185+
69186 <!-- Featured Blog Posts -->
70187 <section id =" blog" class =" bg-gray-800 py-16" >
71188 <div class =" mx-auto max-w-7xl px-4 sm:px-6 lg:px-8" >
@@ -179,7 +296,7 @@ import LogoPattern from "../assets/logo-pattern.png";
179296 <div class =" swiper-wrapper" >
180297 {
181298 testimonials .map ((testimonial , index ) => {
182- const fullText = testimonial .body ;
299+ const fullText = testimonial .body || " " ;
183300 const isLong = fullText .length > 200 ;
184301 const truncatedText = isLong
185302 ? fullText .substring (0 , 200 ).trim ()
@@ -284,12 +401,39 @@ import LogoPattern from "../assets/logo-pattern.png";
284401 });
285402 });
286403
287- // Load Swiper JS and initialize
404+ // Tabs for services (no external deps)
405+ const serviceButtons = document.querySelectorAll<HTMLButtonElement>('[data-service-button]');
406+ const servicePanels = document.querySelectorAll<HTMLElement>('[data-service-panel]');
407+ const activateService = (id: string) => {
408+ serviceButtons.forEach((btn) => {
409+ const active = btn.getAttribute('data-service-button') === id;
410+ btn.setAttribute('aria-selected', active ? 'true' : 'false');
411+ btn.classList.toggle('border-blue-500', active);
412+ btn.classList.toggle('text-white', active);
413+ });
414+ servicePanels.forEach((panel) => {
415+ const show = panel.getAttribute('data-service-panel') === id;
416+ panel.classList.toggle('hidden', !show);
417+ });
418+ };
419+ // Default selection
420+ const firstBtn = document.querySelector<HTMLButtonElement>('[data-service-button]');
421+ if (firstBtn) {
422+ const id = firstBtn.getAttribute('data-service-button');
423+ if (id) activateService(id);
424+ }
425+ serviceButtons.forEach((btn) =>
426+ btn.addEventListener('click', () => {
427+ const id = btn.getAttribute('data-service-button');
428+ if (id) activateService(id);
429+ })
430+ );
431+
432+ // Load Swiper JS and initialize (for testimonials/blog only)
288433 const swiperScript = document.createElement("script");
289434 swiperScript.src =
290435 "https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js";
291436 swiperScript.onload = () => {
292- // Initialize Swipers after script is loaded
293437 // Testimonials Swiper
294438 // @ts-ignore - Swiper provided by CDN
295439 const testimonialsSwiper = new Swiper(".testimonials-swiper", {
@@ -309,13 +453,10 @@ import LogoPattern from "../assets/logo-pattern.png";
309453 prevEl: ".testimonials-swiper .swiper-button-prev",
310454 },
311455 breakpoints: {
312- 768: {
313- slidesPerView: 1,
314- },
456+ 768: { slidesPerView: 1 },
315457 },
316458 });
317459
318- // Blog posts Swiper
319460 // Blog posts Swiper
320461 // @ts-ignore - Swiper provided by CDN
321462 const blogSwiper = new Swiper(".blogposts-swiper", {
@@ -346,12 +487,11 @@ import LogoPattern from "../assets/logo-pattern.png";
346487
347488 toggles.forEach((toggle) => {
348489 toggle.addEventListener("click", () => {
349- const textElement = toggle.previousElementSibling;
490+ const textElement = toggle.previousElementSibling as HTMLElement | null;
491+ if (!textElement) return;
350492 const isExpanded = toggle.getAttribute("data-expanded") === "true";
351- const fullText = textElement.getAttribute("data-full-text");
352- const truncatedText = textElement.getAttribute(
353- "data-truncated-text",
354- );
493+ const fullText = textElement.getAttribute("data-full-text") || "";
494+ const truncatedText = textElement.getAttribute("data-truncated-text") || "";
355495
356496 if (isExpanded) {
357497 textElement.textContent = truncatedText;
0 commit comments