Compare commits

...

13 Commits

Author SHA1 Message Date
Jose Selesan
4b82f6d05b Fix footer 2026-07-13 12:49:21 -03:00
Jose Selesan
c22e3fe918 Fixed form validation 2026-07-13 12:40:37 -03:00
Jose Selesan
401000af97 Fixed Thank you redirect 2026-07-13 12:30:30 -03:00
Jose Selesan
915c8928c8 Added contact form with email sending 2026-07-13 12:27:44 -03:00
Jose Selesan
f923dd2f56 Update logo image to a new 64x64 version 2026-06-09 14:29:13 -03:00
Jose Selesan
890975edf7 More changesd 2026-06-09 14:20:05 -03:00
Jose Selesan
5a754059d8 Fixed OpenRelay script 2026-06-09 14:18:34 -03:00
Jose Selesan
226ac46c2b Add OpenReplay tracking code for user session monitoring 2026-06-09 10:31:27 -03:00
Jose Selesan
f3c2027e44 Add pageview tracking to PostHog analytics integration 2026-06-09 10:12:22 -03:00
Jose Selesan
d604a658f9 Add PostHog analytics integration to track user interactions 2026-06-09 10:03:55 -03:00
Jose Selesan
35b8775531 Update README.md with detailed project description, stack, structure, local development instructions, and deployment guidelines 2026-06-09 09:36:51 -03:00
Jose Selesan
fbf4f31d92 Merge remote initial commit 2026-06-09 09:34:26 -03:00
Jose Selesan
703d17cf2f Initial landing page 2026-06-09 09:32:42 -03:00
29 changed files with 2519 additions and 10 deletions

8
.dockerignore Normal file
View File

@@ -0,0 +1,8 @@
.DS_Store
_site
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.git
.gitignore

10
.eleventy.js Normal file
View File

@@ -0,0 +1,10 @@
module.exports = function(eleventyConfig) {
eleventyConfig.addPassthroughCopy("src/css");
eleventyConfig.addPassthroughCopy("src/images");
return {
dir: {
input: "src",
output: "_site"
}
};
};

16
.gitignore vendored
View File

@@ -8,7 +8,7 @@ yarn-error.log*
lerna-debug.log* lerna-debug.log*
.pnpm-debug.log* .pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html) # Diagnostic reports (https://nodejs.org/api/report)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data # Runtime data
@@ -36,14 +36,14 @@ bower_components
# node-waf configuration # node-waf configuration
.lock-wscript .lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html) # Compiled binary addons
build/Release build/Release
# Dependency directories # Dependency directories
node_modules/ node_modules/
jspm_packages/ jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/) # Snowpack dependency directory
web_modules/ web_modules/
# TypeScript cache # TypeScript cache
@@ -67,7 +67,7 @@ web_modules/
# Optional REPL history # Optional REPL history
.node_repl_history .node_repl_history
# Output of 'npm pack' # Output of npm pack
*.tgz *.tgz
# Yarn Integrity file # Yarn Integrity file
@@ -80,7 +80,7 @@ web_modules/
.env.production.local .env.production.local
.env.local .env.local
# parcel-bundler cache (https://parceljs.org/) # parcel-bundler cache
.cache .cache
.parcel-cache .parcel-cache
@@ -94,9 +94,6 @@ dist
# Gatsby files # Gatsby files
.cache/ .cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output # vuepress build output
.vuepress/dist .vuepress/dist
@@ -136,3 +133,6 @@ dist
.yarn/install-state.gz .yarn/install-state.gz
.pnp.* .pnp.*
# Project
_site/
.DS_Store

17
Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
FROM oven/bun:1 AS build
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun run build
FROM nginx:1.27-alpine AS runtime
COPY --from=build /app/_site /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

173
README.md
View File

@@ -1,3 +1,172 @@
# landing-2pi # 2piDev Landing Page
Repositorio para el código de la landing page de 2piDev Landing institucional de 2piDev construida con Eleventy y Tailwind CSS. El sitio es completamente estático: se genera en `_site/` y se puede servir desde Nginx, Dokploy o cualquier hosting de archivos estáticos.
## Stack
- [Eleventy](https://www.11ty.dev/) para generar HTML estático.
- [Tailwind CSS](https://tailwindcss.com/) para estilos utilitarios y componentes.
- [Bun](https://bun.sh/) como runtime/package manager.
- Nginx en producción vía Docker.
## Estructura
```txt
.
├── src/
│ ├── index.njk # Landing principal
│ ├── css/
│ │ ├── tailwind.css # CSS fuente con directivas Tailwind
│ │ └── styles.css # CSS compilado que Eleventy copia al build
│ └── images/ # Logos, screenshots, retratos y favicon
├── _site/ # Build local generado, ignorado por Git
├── .eleventy.js # Configuración de Eleventy
├── tailwind.config.js # Configuración de Tailwind
├── postcss.config.js # PostCSS + Tailwind + Autoprefixer
├── Dockerfile # Build estático + runtime Nginx
└── package.json # Scripts y dependencias
```
## Desarrollo Local
Instalar dependencias:
```bash
bun install
```
Generar el sitio:
```bash
bun run build
```
Levantar servidor de desarrollo:
```bash
bun run dev
```
Por defecto, Eleventy sirve el sitio en `http://localhost:8080/`.
## Scripts
```json
{
"build:css": "tailwindcss -i ./src/css/tailwind.css -o ./src/css/styles.css --minify",
"build": "bun run build:css && eleventy",
"dev": "bun run build:css && eleventy --serve --watch"
}
```
Notas:
- `src/css/tailwind.css` es el archivo que se edita.
- `src/css/styles.css` es generado por Tailwind y se mantiene versionado para que Eleventy pueda copiarlo como asset estático.
- `_site/` no se versiona.
## Deploy con Dokploy
La forma recomendada para Dokploy es usar el `Dockerfile` incluido. El contenedor:
1. Instala dependencias con Bun.
2. Ejecuta `bun run build`.
3. Copia `_site/` a Nginx.
4. Sirve la landing desde el puerto `80`.
En Dokploy:
- Tipo de deploy: Dockerfile.
- Puerto interno: `80`.
- No requiere variables de entorno.
- No requiere base de datos ni servicios externos.
Build manual de la imagen:
```bash
docker build -t 2pidev-landing-page .
```
Ejecución local con Docker:
```bash
docker run --rm -p 8080:80 2pidev-landing-page
```
Luego abrir `http://localhost:8080/`.
## Assets
Los assets viven en `src/images/` y se copian automáticamente a `_site/images/`.
Incluye:
- Logo principal y favicon.
- Imágenes generadas/originales para hero, resultados y casos de éxito.
- Retratos ficticios generados para testimonios.
- Logos SVG de tecnologías: `.NET`, TypeScript, React, PostgreSQL y AWS.
Para reemplazar una imagen, conservar el mismo nombre de archivo evita cambios en el HTML. Si se cambia el nombre, actualizar la referencia en `src/index.njk`.
## Contenido Principal
Toda la landing está en `src/index.njk`. Las secciones actuales son:
- Header con navegación y CTA.
- Hero principal.
- Problemas frecuentes de empresas en crecimiento.
- Soluciones digitales.
- Resultados y métricas.
- Método de trabajo.
- Stack tecnológico.
- Casos de éxito.
- Testimonios.
- Formulario de contacto.
- Footer.
Algunas listas, como servicios, pasos, tecnologías, casos y testimonios, están definidas como arrays dentro del template para facilitar edición rápida.
## Convenciones
- Mantener textos visibles con tildes y ortografía en español.
- Mantener assets locales siempre que sea posible para evitar dependencias externas.
- Usar SVG para logos e íconos de marca cuando exista una fuente confiable.
- Antes de commitear, correr:
```bash
bun run build
```
## Git
Repositorio remoto:
```bash
git remote -v
```
Debe apuntar a:
```txt
https://git.2pidev.com/jselesan/landing-2pi.git
```
Flujo sugerido:
```bash
git status
bun run build
git add .
git commit -m "Describe el cambio"
git push
```
## Mantenimiento
Si se agregan nuevas tecnologías, sumar el SVG a `src/images/` y actualizar el array `techs` en `src/index.njk`.
Si se agregan nuevas secciones o componentes visuales, preferir mantenerlos en el mismo template mientras la landing siga siendo simple. Crear layouts/includes de Eleventy solo cuando haya repetición real o más páginas.
## Licencias y Uso de Imágenes
Los retratos de testimonios son ficticios y generados para este proyecto. Los logos de tecnologías pertenecen a sus respectivas marcas. Las imágenes de producto/interfaz usadas como visuales de la landing deben mantenerse como assets locales del proyecto para que el deploy sea reproducible.

BIN
bun.lockb Executable file

Binary file not shown.

16
package.json Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "2pidev-landing-page",
"version": "1.0.0",
"private": true,
"scripts": {
"build:css": "tailwindcss -i ./src/css/tailwind.css -o ./src/css/styles.css --minify",
"build": "bun run build:css && eleventy",
"dev": "bun run build:css && eleventy --serve --watch"
},
"devDependencies": {
"@11ty/eleventy": "^3.1.2",
"autoprefixer": "^10.4.21",
"postcss": "^8.5.6",
"tailwindcss": "^3.4.17"
}
}

1550
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};

4
src/_data/site.js Normal file
View File

@@ -0,0 +1,4 @@
module.exports = {
posthogKey: process.env.POSTHOG_API_KEY || null,
posthogHost: process.env.POSTHOG_API_HOST || 'https://us.i.posthog.com',
}

3
src/css/styles.css Normal file

File diff suppressed because one or more lines are too long

84
src/css/tailwind.css Normal file
View File

@@ -0,0 +1,84 @@
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Rajdhani:wght@600;700&display=swap");
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
html {
scroll-behavior: smooth;
}
body {
@apply bg-paper text-ink antialiased;
}
::selection {
@apply bg-flame text-white;
}
}
@layer components {
.section-shell {
@apply mx-auto w-full max-w-6xl px-5 sm:px-6 lg:px-8;
}
.eyebrow {
@apply text-[0.68rem] font-bold uppercase tracking-[0.16em] text-azure;
}
.btn-primary {
@apply inline-flex items-center justify-center rounded-md bg-flame px-5 py-3 text-sm font-bold text-white shadow-lg shadow-orange-500/20 transition hover:bg-ember focus:outline-none focus:ring-2 focus:ring-flame focus:ring-offset-2;
}
.btn-secondary {
@apply inline-flex items-center justify-center rounded-md border border-line bg-white px-5 py-3 text-sm font-bold text-azure transition hover:border-azure hover:bg-blue-50 focus:outline-none focus:ring-2 focus:ring-azure focus:ring-offset-2;
}
.nav-link {
@apply text-sm font-semibold text-slate-700 transition hover:text-azure;
}
.feature-card {
@apply rounded-lg border border-line bg-white p-5 shadow-sm transition hover:-translate-y-1 hover:shadow-soft;
}
.mini-icon {
@apply flex h-9 w-9 items-center justify-center rounded-md text-base font-black text-white;
}
.glass-panel {
background: linear-gradient(145deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.05));
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.28);
backdrop-filter: blur(16px);
}
.dash-screen {
background:
radial-gradient(circle at 20% 18%, rgba(249, 115, 22, 0.18), transparent 30%),
radial-gradient(circle at 80% 18%, rgba(0, 110, 230, 0.22), transparent 32%),
linear-gradient(135deg, #10243f 0%, #071323 52%, #132d4b 100%);
}
.dash-grid {
background-image:
linear-gradient(rgba(255, 255, 255, 0.06) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.06) 1px, transparent 1px);
background-size: 28px 28px;
}
.metric-bar {
@apply h-2 rounded-full bg-slate-700;
}
.metric-fill {
@apply block h-full rounded-full;
}
.case-display {
background:
linear-gradient(180deg, rgba(6, 24, 50, 0.05), rgba(6, 24, 50, 0.16)),
linear-gradient(135deg, #10243f, #071323 48%, #123965);
}
}

30
src/gracias.njk Normal file
View File

@@ -0,0 +1,30 @@
---
layout: false
title: Gracias | 2piDev
---
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title }}</title>
<link rel="icon" href="/images/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="/css/styles.css">
</head>
<body class="min-h-screen bg-gradient-to-br from-orange-50 via-white to-blue-50">
<div class="flex min-h-screen items-center justify-center px-4">
<div class="max-w-md text-center">
<div class="mx-auto mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-green-100">
<svg class="h-10 w-10 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
</svg>
</div>
<h1 class="font-display text-4xl font-bold text-slate-900">¡Gracias!</h1>
<p class="mt-4 text-lg text-slate-600">Hemos recibido tu mensaje y te contactaremos pronto.</p>
<a href="/" class="mt-8 inline-flex items-center gap-2 rounded-lg bg-orange-600 px-6 py-3 text-sm font-bold text-white hover:bg-orange-700 transition-colors">
← Volver al inicio
</a>
</div>
</div>
</body>
</html>

BIN
src/images/case-collage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 KiB

6
src/images/favicon.svg Normal file
View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<path d="M29 35c15-21 49-29 74-9 18 15 23 41 10 62-12 21-36 30-59 24 20 2 39-7 49-24 12-19 7-43-11-56-21-16-50-10-63 3Z" fill="#f97316"/>
<path d="M99 94c-15 21-49 29-74 9C7 88 2 62 15 41c12-21 36-30 59-24-20-2-39 7-49 24-12 19-7 43 11 56 21 16 50 10 63-3Z" fill="#0079ff"/>
<text x="29" y="83" font-family="Arial, sans-serif" font-size="58" font-weight="800" fill="#f97316">2</text>
<path d="M65 84c5-37 8-44 18-44 9 0 13 8 13 20h-7c0-7-2-10-6-10-6 0-8 8-10 34h-8Z" fill="#0079ff"/>
</svg>

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
src/images/logo-64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

13
src/images/logo.svg Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg version="1.0" width="364.77386pt" height="371.71338pt" viewBox="0 0 364.77386 371.71338" preserveAspectRatio="xMidYMid" id="svg5" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(0.1,0,0,-0.1,-101.29014,527.91162)" fill="#ff7f2a" stroke="none">
<path d="m 2640,5268 c -315,-51 -458,-99 -682,-231 -304,-180 -543,-439 -699,-760 -62,-127 -61,-154 1,-34 194,380 603,699 1045,816 42,11 61,14 190,37 124,21 431,13 565,-15 495,-104 901,-396 1150,-826 28,-48 50,-90 50,-93 0,-4 9,-24 20,-44 41,-78 100,-263 125,-398 33,-177 31,-476 -5,-640 -47,-218 -119,-391 -238,-571 -160,-244 -337,-414 -572,-552 -277,-162 -548,-233 -885,-231 -191,1 -123,-15 97,-23 111,-4 194,-2 245,6 43,7 92,15 108,17 122,19 317,77 437,129 375,165 702,475 876,830 62,125 69,144 102,243 144,446 116,917 -81,1332 -72,151 -187,324 -293,439 -163,175 -327,297 -541,401 -264,127 -514,182 -810,179 -82,-1 -175,-6 -205,-11 z" />
</g>
<g transform="matrix(-0.0995434,0.00954526,0.00954526,0.0995434,431.37848,-182.09797)" fill="#0079ff" stroke="none">
<path d="m 2640,5268 c -315,-51 -458,-99 -682,-231 -304,-180 -543,-439 -699,-760 -62,-127 -61,-154 1,-34 194,380 603,699 1045,816 42,11 61,14 190,37 124,21 431,13 565,-15 495,-104 901,-396 1150,-826 28,-48 50,-90 50,-93 0,-4 9,-24 20,-44 41,-78 100,-263 125,-398 33,-177 31,-476 -5,-640 -47,-218 -119,-391 -238,-571 -160,-244 -337,-414 -572,-552 -277,-162 -548,-233 -885,-231 -191,1 -123,-15 97,-23 111,-4 194,-2 245,6 43,7 92,15 108,17 122,19 317,77 437,129 375,165 702,475 876,830 62,125 69,144 102,243 144,446 116,917 -81,1332 -72,151 -187,324 -293,439 -163,175 -327,297 -541,401 -264,127 -514,182 -810,179 -82,-1 -175,-6 -205,-11 z" />
</g>
<text xml:space="preserve" style="font-weight:600;font-size:241.212px;font-family:Rajdhani;fill:#ff7f2a;fill-rule:evenodd;stroke-width:20.1011" x="44.012177" y="278.01367" transform="scale(1.0552555,0.9476378)">2</text>
<g style="fill:#0079ff;fill-rule:evenodd" transform="matrix(0.28537283,0,0,0.29925605,165.05807,115.98893)">
<path fill-rule="nonzero" d="m 0,150.27 18.66,1.39 C 38,129.78 45.6,65.06 159.61,77.72 155.47,355.03 33.86,384.28 41.46,441.17 c 2.76,32.24 28.33,52.05 55.96,53.2 87.3,-2.99 83.38,-120.68 110.56,-417.34 H 320.6 c -5.98,104.57 -22.34,209.13 -24.18,311.63 1.38,68.17 42.84,104.56 97.43,105.02 89.82,3 118.15,-101.8 118.15,-146.48 h -19.35 c -1.84,36.85 -19.57,63.34 -57.35,64.95 C 332.35,413.53 389.01,231.12 389.7,78.41 L 512,79.11 511.31,1.03 C 2.98,-1.44 67.5,-11.4 0,150.27 Z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

7
src/images/tech-aws.svg Normal file
View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" role="img" aria-labelledby="aws-title">
<title id="aws-title">AWS</title>
<rect width="96" height="96" rx="20" fill="#232f3e"/>
<path fill="#fff" d="M25.8 55.5h-7.2l-1.4-5.1H8.7l-1.4 5.1H0L9.1 26h7.7l9 29.5Zm-10-10.8-2.1-7.8c-.2-.6-.4-1.7-.7-3.4h-.1c-.2 1.4-.4 2.6-.7 3.5l-2.1 7.7h5.7Zm42.6 10.8h-7.5L46 36.6c-.2-.8-.4-2.2-.6-4.1h-.1c-.1 1.4-.3 2.8-.7 4.1l-5 18.9h-7.8L24 26h7.4l4.2 19.6c.2 1 .4 2.4.5 4.1h.1c.1-1.3.4-2.7.8-4.2L42.3 26h7.2l4.8 19.8c.2.9.4 2.2.6 3.8h.1c.1-1.2.3-2.5.6-3.9L60 26h6.8l-8.4 29.5Zm9.6-1.1v-7c1.4 1.2 3 2.1 4.8 2.7 1.8.6 3.5.9 5.1.9 1.9 0 3.3-.3 4.2-.9.9-.6 1.4-1.4 1.4-2.4 0-.7-.3-1.3-.8-1.8-.5-.5-1.2-.9-2-1.3-.8-.4-2.1-.8-3.9-1.5-3-1.1-5.2-2.4-6.6-4-1.4-1.6-2.1-3.5-2.1-5.8 0-3 1.2-5.4 3.6-7 2.4-1.7 5.5-2.5 9.3-2.5 3.6 0 6.4.5 8.5 1.6v6.7c-2.3-1.5-5-2.3-8.1-2.3-1.6 0-2.9.3-3.8.8-.9.5-1.4 1.3-1.4 2.3 0 1 .5 1.8 1.5 2.4 1 .6 2.8 1.3 5.2 2.2 3.2 1.1 5.5 2.4 7 4 1.5 1.6 2.2 3.7 2.2 6.1 0 3.2-1.2 5.6-3.6 7.2-2.4 1.6-5.8 2.4-10.1 2.4-4.3 0-7.7-.7-10.4-2.1Z" transform="translate(2 5) scale(.92)"/>
<path fill="#ff9900" d="M25 70c13.5 7.6 32 8.1 46.1 1.2 2.1-1 3.8 1.7 1.9 3.1-13.2 9.8-34.4 10.9-50.2 2.1-2.2-1.2-.1-7.7 2.2-6.4Z"/>
<path fill="#ff9900" d="M69.7 66.6c2.7-.3 8.6-1 9.7 1.2 1.1 2.2-1.2 7.8-2.2 10.3-.3.8-1.4.4-1.3-.3.4-2.5 1.2-6.5.4-7.5-.8-1-4.8-.8-7.3-.7-.8 0-1-.9-.3-1.4.3-.2.6-.4 1-.5Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" fill="none">
<rect width="512" height="512" fill="#512bd4" rx="77" ry="77" style="stroke-width:1.12281"/>
<path fill="#fff" d="M91.255 327.11q-5.43 0-9.232-3.618-3.801-3.712-3.801-8.815 0-5.197 3.801-8.909t9.232-3.712q5.52 0 9.322 3.712 3.892 3.712 3.892 8.909 0 5.103-3.892 8.815-3.801 3.62-9.322 3.619m144.722-2.041h-23.532l-61.996-97.807a44 44 0 0 1-3.891-7.703h-.544q.725 4.27.725 18.28v87.23h-20.817V192h25.07l59.915 95.487q3.8 5.94 4.888 8.166h.361q-.905-5.289-.905-17.91V192h20.726zm101.369 0H264.49V192h69.96v18.745h-48.42v37.675h44.62v18.652h-44.62v39.345h51.316zM440.89 210.745H403.6V325.07h-21.54V210.745h-37.198V192h96.027z" style="stroke-width:1.12281"/>
</svg>

After

Width:  |  Height:  |  Size: 748 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="64" viewBox="0 0 25.6 25.6" width="64"><style><![CDATA[.B{stroke-linecap:round}.C{stroke-linejoin:round}.D{stroke-linejoin:miter}.E{stroke-width:.716}]]></style><g fill="none" stroke="#fff"><path d="M18.983 18.636c.163-1.357.114-1.555 1.124-1.336l.257.023c.777.035 1.793-.125 2.4-.402 1.285-.596 2.047-1.592.78-1.33-2.89.596-3.1-.383-3.1-.383 3.053-4.53 4.33-10.28 3.227-11.687-3.004-3.84-8.205-2.024-8.292-1.976l-.028.005c-.57-.12-1.2-.19-1.93-.2-1.308-.02-2.3.343-3.054.914 0 0-9.277-3.822-8.846 4.807.092 1.836 2.63 13.9 5.66 10.25C8.29 15.987 9.36 14.86 9.36 14.86c.53.353 1.167.533 1.834.468l.052-.044a2.01 2.01 0 0 0 .021.518c-.78.872-.55 1.025-2.11 1.346-1.578.325-.65.904-.046 1.056.734.184 2.432.444 3.58-1.162l-.046.183c.306.245.285 1.76.33 2.842s.116 2.093.337 2.688.48 2.13 2.53 1.7c1.713-.367 3.023-.896 3.143-5.81" fill="#000" stroke="#000" stroke-linecap="butt" stroke-width="2.149" class="D"/><path d="M23.535 15.6c-2.89.596-3.1-.383-3.1-.383 3.053-4.53 4.33-10.28 3.228-11.687-3.004-3.84-8.205-2.023-8.292-1.976l-.028.005a10.31 10.31 0 0 0-1.929-.201c-1.308-.02-2.3.343-3.054.914 0 0-9.278-3.822-8.846 4.807.092 1.836 2.63 13.9 5.66 10.25C8.29 15.987 9.36 14.86 9.36 14.86c.53.353 1.167.533 1.834.468l.052-.044a2.02 2.02 0 0 0 .021.518c-.78.872-.55 1.025-2.11 1.346-1.578.325-.65.904-.046 1.056.734.184 2.432.444 3.58-1.162l-.046.183c.306.245.52 1.593.484 2.815s-.06 2.06.18 2.716.48 2.13 2.53 1.7c1.713-.367 2.6-1.32 2.725-2.906.088-1.128.286-.962.3-1.97l.16-.478c.183-1.53.03-2.023 1.085-1.793l.257.023c.777.035 1.794-.125 2.39-.402 1.285-.596 2.047-1.592.78-1.33z" fill="#336791" stroke="none"/><g class="E"><g class="B"><path d="M12.814 16.467c-.08 2.846.02 5.712.298 6.4s.875 2.05 2.926 1.612c1.713-.367 2.337-1.078 2.607-2.647l.633-5.017M10.356 2.2S1.072-1.596 1.504 7.033c.092 1.836 2.63 13.9 5.66 10.25C8.27 15.95 9.27 14.907 9.27 14.907m6.1-13.4c-.32.1 5.164-2.005 8.282 1.978 1.1 1.407-.175 7.157-3.228 11.687" class="C"/><path d="M20.425 15.17s.2.98 3.1.382c1.267-.262.504.734-.78 1.33-1.054.49-3.418.615-3.457-.06-.1-1.745 1.244-1.215 1.147-1.652-.088-.394-.69-.78-1.086-1.744-.347-.84-4.76-7.29 1.224-6.333.22-.045-1.56-5.7-7.16-5.782S7.99 8.196 7.99 8.196" stroke-linejoin="bevel"/></g><g class="C"><path d="M11.247 15.768c-.78.872-.55 1.025-2.11 1.346-1.578.325-.65.904-.046 1.056.734.184 2.432.444 3.58-1.163.35-.49-.002-1.27-.482-1.468-.232-.096-.542-.216-.94.23z"/><path d="M11.196 15.753c-.08-.513.168-1.122.433-1.836.398-1.07 1.316-2.14.582-5.537-.547-2.53-4.22-.527-4.22-.184s.166 1.74-.06 3.365c-.297 2.122 1.35 3.916 3.246 3.733" class="B"/></g></g><g fill="#fff" class="D"><path d="M10.322 8.145c-.017.117.215.43.516.472s.558-.202.575-.32-.215-.246-.516-.288-.56.02-.575.136z" stroke-width=".239"/><path d="M19.486 7.906c.016.117-.215.43-.516.472s-.56-.202-.575-.32.215-.246.516-.288.56.02.575.136z" stroke-width=".119"/></g><path d="M20.562 7.095c.05.92-.198 1.545-.23 2.524-.046 1.422.678 3.05-.413 4.68" class="B C E"/></g></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

10
src/images/tech-react.svg Normal file
View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" role="img" aria-labelledby="react-title">
<title id="react-title">React</title>
<rect width="96" height="96" rx="20" fill="#0f172a"/>
<g fill="none" stroke="#61dafb" stroke-width="4">
<ellipse cx="48" cy="48" rx="33" ry="12"/>
<ellipse cx="48" cy="48" rx="33" ry="12" transform="rotate(60 48 48)"/>
<ellipse cx="48" cy="48" rx="33" ry="12" transform="rotate(120 48 48)"/>
</g>
<circle cx="48" cy="48" r="6.5" fill="#61dafb"/>
</svg>

After

Width:  |  Height:  |  Size: 515 B

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
fill="none"
height="64"
viewBox="0 0 64 64"
width="64"
version="1.1"
id="svg2"
sodipodi:docname="ts-icon.svg"
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2" />
<sodipodi:namedview
id="namedview2"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.3867188"
inkscape:cx="256"
inkscape:cy="256"
inkscape:window-width="1392"
inkscape:window-height="997"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<rect
fill="#3178c6"
height="64"
rx="6.25"
width="64"
id="rect1"
x="0"
y="0"
style="stroke-width:0.125" />
<rect
fill="#3178c6"
height="64"
rx="6.25"
width="64"
id="rect2"
x="0"
y="0"
style="stroke-width:0.125" />
<path
clip-rule="evenodd"
d="m 39.617375,50.928 v 6.257625 c 1.01725,0.5215 2.220375,0.9125 3.609375,1.17325 1.389,0.26075 2.852875,0.391125 4.391875,0.391125 1.499875,0 2.924625,-0.143375 4.2745,-0.43025 1.349875,-0.28675 2.5335,-0.759375 3.55075,-1.41775 1.01725,-0.65825 1.822625,-1.51875 2.416,-2.58125 C 58.45325,53.25825 58.75,51.944875 58.75,50.3805 58.75,49.24625 58.5805,48.25225 58.241375,47.398375 57.90225,46.5445 57.41325,45.785125 56.774125,45.12025 56.135125,44.455375 55.368875,43.858875 54.4755,43.330875 53.582125,42.802875 52.574625,42.30425 51.453,41.835 50.631375,41.496 49.8945,41.166875 49.242375,40.8475 48.59025,40.528 48.036,40.202125 47.5795,39.86975 c -0.4565,-0.3325 -0.808625,-0.6845 -1.056375,-1.056 -0.24775,-0.371625 -0.37175,-0.792 -0.37175,-1.261375 0,-0.430125 0.110875,-0.818 0.332625,-1.1635 0.22175,-0.3455 0.53475,-0.642 0.939,-0.88975 0.404375,-0.247625 0.899875,-0.44 1.48675,-0.576875 0.587,-0.136875 1.239,-0.20525 1.956375,-0.20525 0.521625,0 1.072625,0.03912 1.653,0.11725 0.580375,0.07825 1.164,0.198875 1.751,0.36175 0.586875,0.163 1.157375,0.368375 1.71175,0.616 0.55425,0.24775 1.066125,0.5345 1.535625,0.8605 v -5.847 C 56.5655,30.4605 55.525375,30.19 54.39725,30.014 53.269125,29.838 51.974625,29.75 50.514,29.75 c -1.486875,0 -2.895375,0.15975 -4.225625,0.479125 -1.33025,0.319375 -2.50075,0.818 -3.511625,1.495875 -1.01075,0.678 -1.8095,1.541625 -2.396375,2.591125 C 39.7935,35.3655 39.5,36.62025 39.5,38.080375 c 0,1.86425 0.538,3.45475 1.614,4.7715 1.075875,1.316625 2.709375,2.43125 4.9005,3.343875 0.86075,0.352 1.662875,0.697375 2.40625,1.036375 0.743375,0.339 1.38575,0.691 1.926875,1.056 0.54125,0.365 0.968375,0.762625 1.2815,1.192875 0.313,0.430125 0.4695,0.919 0.4695,1.466625 0,0.404125 -0.09788,0.778875 -0.2935,1.124375 -0.195625,0.3455 -0.492375,0.64525 -0.890125,0.8995 -0.39775,0.25425 -0.893375,0.453 -1.48675,0.596375 -0.5935,0.1435 -1.287875,0.215125 -2.0835,0.215125 -1.356375,0 -2.699625,-0.237875 -4.03,-0.71375 C 41.9845,52.5935 40.752,51.87975 39.617375,50.928 Z M 29.0975,35.51025 H 37.125 V 30.375 H 14.75 v 5.13525 h 7.98825 V 58.375 h 6.35925 z"
fill="#ffffff"
fill-rule="evenodd"
id="path2"
style="stroke-width:0.125" />
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

484
src/index.njk Normal file
View File

@@ -0,0 +1,484 @@
---
layout: false
title: 2piDev | Software a medida
description: Soluciones digitales de élite para empresas que necesitan operar, crecer y vender con sistemas a medida.
---
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="{{ description }}">
<title>{{ title }}</title>
<link rel="icon" href="/images/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="/css/styles.css">
{% if site.posthogKey %}
<script>
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host+"/static/array.full.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getSurveys getActiveMatchingSurveys captureException".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init('{{ site.posthogKey }}', {
api_host: '{{ site.posthogHost }}',
capture_performance: true,
disable_session_recording: false,
})
posthog.capture('$pageview');
</script>
{% endif %}
<!-- OpenReplay Tracking Code -->
<script>
var initOpts = {
projectKey: "5o5HLGZhn4TlGBbOFkVu",
// 0 - plain, 1 - obscured, 2 - ignored
defaultInputMode: 2,
obscureTextNumbers: false,
obscureTextEmails: true,
};
var startOpts = { userID: "" };
(function(A,s,a,y,e,r){
r=window.OpenReplay=[e,r,y,[s-1, e]];
s=document.createElement('script');s.src=A;s.async=!a;
document.getElementsByTagName('head')[0].appendChild(s);
r.start=function(v){r.push([0])};
r.stop=function(v){r.push([1])};
r.setUserID=function(id){r.push([2,id])};
r.setUserAnonymousID=function(id){r.push([3,id])};
r.setMetadata=function(k,v){r.push([4,k,v])};
r.event=function(k,p,i){r.push([5,k,p,i])};
r.issue=function(k,p){r.push([6,k,p])};
r.isActive=function(){return false};
r.getSessionToken=function(){};
})("//static.openreplay.com/latest/openreplay.js",1,0,initOpts,startOpts);
</script>
</head>
<body>
<header class="sticky top-0 z-50 border-b border-line/70 bg-white/92 backdrop-blur">
<nav class="section-shell flex h-16 items-center justify-between gap-5">
<a href="#inicio" class="flex items-center gap-3" aria-label="2piDev inicio">
<img src="/images/logo-64x64.png" alt="" class="h-10 w-10">
<span class="font-display text-xl font-bold text-ink">2piDev</span>
</a>
<div class="hidden items-center gap-8 md:flex">
<a href="#servicios" class="nav-link">Servicios</a>
<a href="#soluciones" class="nav-link">Soluciones</a>
<a href="#tecnologias" class="nav-link">Tecnología</a>
<a href="#contacto" class="nav-link">Contacto</a>
</div>
<a href="#contacto" class="btn-primary hidden px-4 py-2 text-xs sm:inline-flex">Agendar consulta</a>
</nav>
</header>
<main id="inicio">
<section class="overflow-hidden bg-[linear-gradient(115deg,#fff4ec_0%,#f7f8fc_43%,#eef6ff_100%)] pb-20 pt-14 lg:pb-28 lg:pt-20">
<div class="section-shell grid items-center gap-12 lg:grid-cols-[0.95fr_1.05fr]">
<div>
<p class="mb-5 inline-flex items-center gap-2 rounded-full border border-orange-200 bg-white/70 px-3 py-1.5 text-xs font-bold uppercase tracking-[0.14em] text-ember">
<span class="h-2 w-2 rounded-full bg-flame"></span>
Software de alto rendimiento
</p>
<h1 class="max-w-2xl font-display text-5xl font-bold leading-[0.95] text-ink sm:text-6xl lg:text-7xl">
Software que impulsa el crecimiento de tu empresa
</h1>
<p class="mt-7 max-w-xl text-lg leading-8 text-muted">
Diseñamos y desarrollamos soluciones tecnológicas personalizadas que automatizan procesos y te otorgan una ventaja competitiva real en el mercado actual.
</p>
<div class="mt-8 flex flex-col gap-3 sm:flex-row">
<a href="#contacto" class="btn-primary">Agendar una consulta</a>
<a href="#casos" class="btn-secondary">Ver soluciones</a>
</div>
<div class="mt-9 grid max-w-xl grid-cols-1 gap-3 text-sm font-semibold text-slate-700 sm:grid-cols-2">
<span class="flex items-center gap-2"><span class="h-2.5 w-2.5 rounded-full bg-flame"></span>Proyectos a medida</span>
<span class="flex items-center gap-2"><span class="h-2.5 w-2.5 rounded-full bg-azure"></span>Arquitecturas escalables</span>
<span class="flex items-center gap-2"><span class="h-2.5 w-2.5 rounded-full bg-azure"></span>Tecnologías modernas</span>
<span class="flex items-center gap-2"><span class="h-2.5 w-2.5 rounded-full bg-flame"></span>Enfoque de negocio</span>
</div>
</div>
<div class="relative mx-auto w-full max-w-2xl">
<div class="overflow-hidden rounded-xl border border-white/60 bg-slate-900 shadow-panel">
<img src="/images/hero-dashboard.png" alt="Dashboard empresarial premium en una pantalla de oficina" class="aspect-[16/9] h-full w-full object-cover">
</div>
<div class="absolute -bottom-8 left-6 rounded-lg border border-line bg-white p-5 shadow-soft sm:left-10">
<p class="text-xs font-bold uppercase tracking-wide text-muted">Rendimiento operativo</p>
<p class="mt-1 text-2xl font-black text-azure">+94%</p>
</div>
</div>
</div>
</section>
<section class="py-20">
<div class="section-shell">
<div class="mx-auto max-w-3xl text-center">
<h2 class="font-display text-4xl font-bold leading-tight text-ink sm:text-5xl">Muchas empresas crecen más lento por culpa de sus sistemas</h2>
<p class="mt-4 text-muted">Identificamos cuellos de botella tecnológicos que están frenando tu escalabilidad.</p>
</div>
<div class="mt-12 grid gap-5 md:grid-cols-2 lg:grid-cols-3">
{% set pains = [
["P", "Procesos manuales", "Pérdida de tiempo en tareas repetitivas que podrían estar automatizadas.", "bg-orange-600"],
["I", "Información fragmentada", "Datos dispersos en diferentes hojas de cálculo y herramientas aisladas.", "bg-blue-600"],
["S", "Sistemas legados", "Software obsoleto que no permite integraciones ni nuevas funcionalidades.", "bg-orange-600"],
["A", "Falta de automatización", "Dependencia excesiva de la intervención humana para flujos críticos.", "bg-blue-600"],
["M", "Visibilidad de métricas", "Imposibilidad de tomar decisiones basadas en datos en tiempo real.", "bg-orange-600"],
["E", "Software estancado", "Herramientas que no se adaptan cuando el negocio necesita crecer.", "bg-blue-600"]
] %}
{% for item in pains %}
<article class="feature-card">
<div class="mini-icon {{ item[3] }}">{{ item[0] }}</div>
<h3 class="mt-4 text-lg font-bold text-ink">{{ item[1] }}</h3>
<p class="mt-2 text-sm leading-6 text-muted">{{ item[2] }}</p>
</article>
{% endfor %}
</div>
</div>
</section>
<section id="servicios" class="bg-white py-20">
<div class="section-shell">
<div class="flex flex-col justify-between gap-5 sm:flex-row sm:items-end">
<div>
<p class="eyebrow">Nuestras capacidades</p>
<h2 class="mt-2 font-display text-4xl font-bold text-ink sm:text-5xl">Soluciones Digitales de Élite</h2>
</div>
<a href="#contacto" class="text-sm font-bold text-flame hover:text-ember">Explorar portfolio completo</a>
</div>
<div id="soluciones" class="mt-10 grid gap-5 md:grid-cols-2 lg:grid-cols-3">
{% set services = [
["</>", "Custom Software", "Desarrollo desde cero adaptado 100% a las reglas de tu negocio y necesidades específicas.", "bg-orange-600"],
["W", "Enterprise Web Apps", "Plataformas robustas, seguras y escalables accesibles desde cualquier navegador.", "bg-blue-600"],
["M", "Mobile Apps", "Experiencias nativas e híbridas diseñadas para detallar al usuario final en iOS y Android.", "bg-slate-900"],
["O", "Process Automation", "Eliminamos la fricción operativa mediante flujos de trabajo inteligentes y automatizados.", "bg-orange-600"],
["A", "APIs & Integrations", "Conectamos tus herramientas para que tus datos fluyan sin interrupciones entre sistemas.", "bg-blue-600"],
["C", "IT Consulting", "Acompañamiento estratégico para definir tu roadmap tecnológico y arquitectura.", "bg-slate-900"]
] %}
{% for item in services %}
<article class="feature-card min-h-52">
<div class="mini-icon {{ item[3] }}">{{ item[0] }}</div>
<h3 class="mt-5 text-xl font-bold text-ink">{{ item[1] }}</h3>
<p class="mt-3 text-sm leading-6 text-muted">{{ item[2] }}</p>
</article>
{% endfor %}
</div>
</div>
</section>
<section class="relative overflow-hidden bg-navy py-20 text-white">
<div class="absolute inset-0">
<img src="/images/results-dashboard.png" alt="" class="h-full w-full object-cover opacity-35">
<div class="absolute inset-0 bg-gradient-to-r from-navy via-navy/90 to-navy/55"></div>
</div>
<div class="section-shell relative grid gap-10 lg:grid-cols-[0.9fr_1.1fr] lg:items-center">
<div>
<h2 class="font-display text-4xl font-bold leading-tight sm:text-5xl">Resultados que se traducen en valor real</h2>
<p class="mt-5 max-w-xl text-blue-100">Nuestras implementaciones no solo resuelven procesos técnicos, sino que impactan directamente en el balance final de tu empresa.</p>
<div class="mt-9 grid grid-cols-2 gap-5">
<div class="border-l-2 border-flame pl-4">
<p class="text-4xl font-black text-flame">+40%</p>
<p class="mt-1 text-xs font-bold uppercase tracking-wide text-blue-100">Eficiencia operativa</p>
</div>
<div class="border-l-2 border-azure pl-4">
<p class="text-4xl font-black text-azure">-60%</p>
<p class="mt-1 text-xs font-bold uppercase tracking-wide text-blue-100">Tareas manuales</p>
</div>
<div class="border-l-2 border-flame pl-4">
<p class="text-4xl font-black text-white">24/7</p>
<p class="mt-1 text-xs font-bold uppercase tracking-wide text-blue-100">Disponibilidad</p>
</div>
<div class="border-l-2 border-azure pl-4">
<p class="text-4xl font-black text-white">100%</p>
<p class="mt-1 text-xs font-bold uppercase tracking-wide text-blue-100">Escalabilidad</p>
</div>
</div>
</div>
<div class="glass-panel rounded-lg p-6">
<div class="mb-6 flex items-center justify-between">
<h3 class="text-xl font-bold">Performance global</h3>
<span class="text-flame">+8%</span>
</div>
<div class="space-y-5">
<div>
<div class="mb-2 flex justify-between text-sm font-semibold"><span>Automatización de procesos</span><span>92%</span></div>
<div class="metric-bar"><span class="metric-fill w-[92%] bg-flame"></span></div>
</div>
<div>
<div class="mb-2 flex justify-between text-sm font-semibold"><span>Reducción de latencia</span><span>76%</span></div>
<div class="metric-bar"><span class="metric-fill w-[76%] bg-azure"></span></div>
</div>
<div>
<div class="mb-2 flex justify-between text-sm font-semibold"><span>Integración de datos</span><span>100%</span></div>
<div class="metric-bar"><span class="metric-fill w-full bg-azure"></span></div>
</div>
</div>
</div>
</div>
</section>
<section class="py-20">
<div class="section-shell">
<div class="text-center">
<h2 class="font-display text-4xl font-bold text-ink sm:text-5xl">Nuestro Método de Trabajo</h2>
<p class="mt-3 text-muted">Un proceso riguroso diseñado para el éxito del proyecto.</p>
</div>
<div class="mt-10 grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{% set steps = [
["Diagnóstico", "Relevamos procesos, sistemas actuales y objetivos de negocio para detectar oportunidades concretas de mejora."],
["Estrategia", "Definimos alcance, prioridades técnicas y roadmap para construir una solución viable, escalable y medible."],
["Prototipo UX/UI", "Diseñamos flujos e interfaces iniciales para validar la experiencia antes de avanzar al desarrollo completo."],
["Desarrollo", "Construimos el software con entregas iterativas, código mantenible e integración continua con tus herramientas."],
["Lanzamiento", "Implementamos la solución en producción, acompañando la adopción del equipo y ajustando los últimos detalles."],
["Evolución", "Medimos resultados, incorporamos mejoras y escalamos la plataforma según nuevas necesidades del negocio."]
] %}
{% for step in steps %}
<div class="rounded-lg border border-line bg-white p-6 shadow-sm">
<p class="font-display text-2xl font-bold text-flame/50">0{{ loop.index }}</p>
<h3 class="mt-3 text-lg font-bold text-ink">{{ step[0] }}</h3>
<p class="mt-3 text-sm leading-6 text-muted">{{ step[1] }}</p>
</div>
{% endfor %}
</div>
</div>
</section>
<section id="tecnologias" class="border-y border-line bg-[#eef2fa] py-10">
<div class="section-shell">
<p class="text-center text-xs font-bold uppercase tracking-[0.2em] text-muted">Stack tecnológico premium</p>
<div class="mt-8 grid grid-cols-2 gap-4 text-center sm:grid-cols-3 lg:grid-cols-5">
{% set techs = [
[".NET", "tech-dotnet.svg"],
["TypeScript", "tech-typescript.svg"],
["React", "tech-react.svg"],
["Postgres", "tech-postgres.svg"],
["AWS", "tech-aws.svg"]
] %}
{% for tech in techs %}
<div class="{% if tech[0] == 'AWS' %}hidden sm:flex{% else %}flex{% endif %} min-h-32 flex-col items-center justify-center rounded-lg border border-white/70 bg-white/70 px-4 py-5 shadow-sm">
<img src="/images/{{ tech[1] }}" alt="Logo {{ tech[0] }}" class="h-12 w-12 object-contain">
<p class="mt-3 font-display text-xl font-bold text-slate-600">{{ tech[0] }}</p>
</div>
{% endfor %}
</div>
</div>
</section>
<section id="casos" class="bg-white py-20">
<div class="section-shell">
<div class="text-center">
<h2 class="font-display text-4xl font-bold text-ink sm:text-5xl">Casos de Éxito</h2>
<p class="mt-3 text-muted">Soluciones reales implementadas para empresas líderes.</p>
</div>
<div class="mt-10 grid gap-6 lg:grid-cols-3">
{% set cases = [
["Industria", "ERP Empresarial a Medida", "Centralización de inventario y facturación para una distribuidora nacional.", "object-left"],
["Logística", "Plataforma SaaS de Tracking", "Sistema en tiempo real para el seguimiento de flotas internacionales.", "object-center"],
["Retail", "Automatización Operativa", "Workflow inteligente que redujo los tiempos de entrega en un 35%.", "object-right"]
] %}
{% for item in cases %}
<article class="overflow-hidden rounded-lg border border-line bg-white shadow-sm">
<div class="h-48 overflow-hidden bg-navy">
<img src="/images/case-collage.png" alt="Interfaces digitales modernas para logística, analítica y arquitectura cloud" class="h-full w-full object-cover {{ item[3] }}">
</div>
<div class="p-5">
<p class="text-xs font-bold uppercase tracking-wide text-flame">{{ item[0] }}</p>
<h3 class="mt-2 text-xl font-bold text-ink">{{ item[1] }}</h3>
<p class="mt-3 text-sm leading-6 text-muted">{{ item[2] }}</p>
</div>
</article>
{% endfor %}
</div>
</div>
</section>
<section class="bg-[#f0f4ff] py-14">
<div class="section-shell grid gap-5 md:grid-cols-3">
{% set quotes = [
["Ricardo Méndez", "CEO, Logística Global", "2piDev transformó nuestro sistema de turnos. Pasamos de hojas de cálculo a un software integrado que nos ahorra horas diarias.", "testimonial-alicia.png"],
["Sofía Larrea", "Directora Ops, TextilNova", "La capacidad técnica del equipo es impresionante, pero su entendimiento del negocio fue lo que realmente marcó diferencia.", "testimonial-sofia.png"],
["Alicia Vega", "CTO, Fintech Base", "Escalabilidad y seguridad desde el primer día. La nube tecnológica en el centro nos permitió crecer sin sobresaltos.", "testimonial-ricardo.png"]
] %}
{% for item in quotes %}
<figure class="rounded-lg border border-line bg-white p-6 shadow-sm">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<img src="/images/{{ item[3] }}" alt="Retrato de {{ item[0] }}" class="h-12 w-12 rounded-full object-cover ring-2 ring-blue-100">
<div>
<figcaption class="font-bold text-ink">{{ item[0] }}</figcaption>
<p class="text-xs text-muted">{{ item[1] }}</p>
</div>
</div>
<span class="font-display text-4xl font-bold text-blue-200">99</span>
</div>
<blockquote class="mt-5 text-sm leading-6 text-slate-700">"{{ item[2] }}"</blockquote>
</figure>
{% endfor %}
</div>
</section>
<section id="contacto" class="bg-[linear-gradient(115deg,#9a3300_0%,#f97316_38%,#061832_39%,#062957_100%)] py-20 text-white">
<div class="section-shell grid gap-10 lg:grid-cols-[0.95fr_1.05fr] lg:items-center">
<div>
<h2 class="font-display text-5xl font-bold leading-[0.95] sm:text-6xl">Tu próximo sistema puede convertirse en una ventaja competitiva</h2>
<p class="mt-6 max-w-xl text-orange-50">Hablemos sobre cómo la tecnología puede acelerar tus objetivos de negocio hoy mismo.</p>
<div class="mt-8 space-y-3 text-sm font-bold">
<p class="flex items-center gap-3"><span class="h-2.5 w-2.5 rounded-full bg-white"></span>hola@2pidev.com</p>
<p class="flex items-center gap-3"><span class="h-2.5 w-2.5 rounded-full bg-white"></span>Sede Central, Hub de Innovación Digital</p>
</div>
</div>
<form action="https://formsubmit.co/hola@2pidev.com" method="POST" class="rounded-xl border border-white/20 bg-white p-6 text-ink shadow-panel">
<input type="hidden" name="_next" value="https://www.2pidev.com/gracias">
<input type="hidden" name="_captcha" value="false">
<div class="grid gap-4 sm:grid-cols-2">
<label class="text-sm font-bold">Nombre
<input id="nombre" class="field mt-2 w-full rounded-md border border-line px-4 py-3 font-normal outline-none focus:border-azure focus:ring-2 focus:ring-blue-100" type="text" name="nombre" placeholder="Juan Pérez">
<p class="error-message hidden mt-1 text-xs text-red-500">Por favor completá tu nombre</p>
</label>
<label class="text-sm font-bold">Empresa
<input id="empresa" class="field mt-2 w-full rounded-md border border-line px-4 py-3 font-normal outline-none focus:border-azure focus:ring-2 focus:ring-blue-100" type="text" name="empresa" placeholder="Nombre de tu empresa">
</label>
</div>
<label class="mt-4 block text-sm font-bold">Email corporativo
<input id="email" class="field mt-2 w-full rounded-md border border-line px-4 py-3 font-normal outline-none focus:border-azure focus:ring-2 focus:ring-blue-100" type="text" name="email" placeholder="juan@empresa.com">
<p class="error-message hidden mt-1 text-xs text-red-500">Ingresá un email válido</p>
</label>
<label class="mt-4 block text-sm font-bold">Mensaje
<textarea id="mensaje" class="field mt-2 min-h-32 w-full rounded-md border border-line px-4 py-3 font-normal outline-none focus:border-azure focus:ring-2 focus:ring-blue-100" name="mensaje" placeholder="Cuéntanos un poco sobre tu proyecto..."></textarea>
<p class="error-message hidden mt-1 text-xs text-red-500">Por favor completá tu mensaje</p>
</label>
<button class="btn-primary mt-5 w-full" type="submit">Enviar mensaje</button>
</form>
</div>
</section>
</main>
<footer class="bg-white py-12">
<div class="section-shell grid gap-8 md:grid-cols-[auto_1fr]">
<div>
<div class="flex items-center gap-3">
<img src="/images/logo.svg" alt="" class="h-10 w-10">
<span class="font-display text-xl font-bold">2piDev</span>
</div>
<p class="mt-4 text-sm leading-6 text-muted">Engineering elegance. Software de alto rendimiento diseñado para el futuro corporativo.</p>
</div>
<div class="md:text-right">
<h3 class="text-sm font-black uppercase tracking-wide text-ink">Contacto</h3>
<div class="mt-4 space-y-2 text-sm text-muted">
<p>Hablemos de tu proyecto</p>
<a class="font-bold text-azure" href="mailto:hola@2pidev.com">hola@2pidev.com</a>
</div>
</div>
</div>
<div class="section-shell mt-10 border-t border-line pt-6 text-xs text-muted">
<p>(c) 2026 2piDev. Engineered Elegance.</p>
</div>
</footer>
{% if site.posthogKey %}
<script>
(function() {
document.addEventListener('click', function(e) {
var link = e.target.closest('a');
if (!link) return;
if (link.matches('.btn-primary, .btn-secondary, .nav-link')) {
posthog.capture('cta_click', {
label: link.textContent.trim(),
href: link.getAttribute('href')
});
}
});
var form = document.querySelector('form');
if (form) {
form.addEventListener('submit', function() {
var data = new FormData(form);
posthog.capture('contact_form_submitted', {
nombre: data.get('nombre') || '',
empresa: data.get('empresa') || '',
email: data.get('email') || ''
});
});
}
var sections = ['servicios', 'soluciones', 'tecnologias', 'contacto', 'casos'];
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
posthog.capture('section_view', { section: entry.target.id });
}
});
}, { threshold: 0.3 });
sections.forEach(function(id) {
var el = document.getElementById(id);
if (el) observer.observe(el);
});
})();
</script>
{% endif %}
<script>
(function() {
var form = document.querySelector('form');
if (!form) return;
var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
var fields = {
nombre: { el: document.getElementById('nombre'), msg: 'Por favor completá tu nombre' },
email: { el: document.getElementById('email'), msg: 'Ingresá un email válido' },
mensaje: { el: document.getElementById('mensaje'), msg: 'Por favor completá tu mensaje' }
};
function showError(field, message) {
field.el.classList.add('border-red-500');
field.el.classList.remove('border-line');
var errorEl = field.el.parentElement.querySelector('.error-message');
if (errorEl) {
errorEl.textContent = message || field.msg;
errorEl.classList.remove('hidden');
}
}
function clearError(field) {
field.el.classList.remove('border-red-500');
field.el.classList.add('border-line');
var errorEl = field.el.parentElement.querySelector('.error-message');
if (errorEl) errorEl.classList.add('hidden');
}
function validate() {
var valid = true;
if (!fields.nombre.el.value.trim()) {
showError(fields.nombre);
valid = false;
} else {
clearError(fields.nombre);
}
if (!fields.email.el.value.trim() || !emailRegex.test(fields.email.el.value.trim())) {
showError(fields.email);
valid = false;
} else {
clearError(fields.email);
}
if (!fields.mensaje.el.value.trim()) {
showError(fields.mensaje);
valid = false;
} else {
clearError(fields.mensaje);
}
return valid;
}
form.addEventListener('submit', function(e) {
if (!validate()) {
e.preventDefault();
}
});
Object.keys(fields).forEach(function(key) {
fields[key].el.addEventListener('input', function() {
clearError(fields[key]);
});
});
})();
</script>
</body>
</html>

27
tailwind.config.js Normal file
View File

@@ -0,0 +1,27 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,njk,md,js}"],
theme: {
extend: {
colors: {
ink: "#071323",
muted: "#637083",
paper: "#f7f8fc",
line: "#dde3ed",
flame: "#f97316",
ember: "#b94700",
azure: "#006ee6",
navy: "#061832"
},
fontFamily: {
sans: ["Inter", "ui-sans-serif", "system-ui", "sans-serif"],
display: ["Rajdhani", "Inter", "ui-sans-serif", "system-ui", "sans-serif"]
},
boxShadow: {
soft: "0 18px 50px rgba(15, 35, 63, 0.12)",
panel: "0 22px 70px rgba(6, 24, 50, 0.22)"
}
}
},
plugins: []
};