feat(v3): rediseño con cloud dividers, animaciones scroll reveal, galería stock, página /servicios con 9 planes, 404 gracioso con robot, planos dark mode con todos los planes
- 9 planes de hosting con tabla completa de Mauri (USD/año) - /servicios muestra los 3 servicios a medida + 9 planes categorizados - /planes: 3 destacados + grilla con los 6 restantes - 404 con robot SVG animado tomando mate (pronet.uy + peugeot style) - Cloud dividers SVG entre secciones de la home - Scroll reveal animations (.reveal + .stagger con IntersectionObserver) - Galería de imágenes stock (datacenters, cables, código) en AVIF + WebP + JPG - Sección de testimonios con placeholder honesto (Working on it) - Back-to-top arreglado: ahora usa color naranja + icono blanco, visible en ambos temas - WorldMap con animación SVG de 4 países - Toggle light/dark funcional con anti-flash script - Footer siempre dark con Maldonado, Uruguay - i18n es/en con toggle y contenido bilingüe Tech: Astro 5 + Tailwind v4 + Keystatic + Gitea + lftp
This commit is contained in:
@@ -4,8 +4,8 @@ const label = 'Volver arriba';
|
||||
<button
|
||||
type="button"
|
||||
id="back-to-top"
|
||||
class="fixed bottom-24 right-6 z-30 inline-flex items-center justify-center w-11 h-11 rounded-full text-white shadow-md opacity-0 invisible transition-all"
|
||||
style="background: var(--hds-fg);"
|
||||
class="fixed bottom-24 right-6 z-30 inline-flex items-center justify-center w-11 h-11 rounded-full shadow-md opacity-0 invisible transition-all"
|
||||
style="background: var(--color-hds-naranja); color: #FAF6EE;"
|
||||
aria-label={label}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
|
||||
@@ -18,7 +18,7 @@ const items = [
|
||||
---
|
||||
<section class="py-20 md:py-28" style="background: var(--hds-bg-soft);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-3xl mb-14 md:mb-20">
|
||||
<div class="max-w-3xl mb-14 md:mb-20 reveal">
|
||||
<SectionEyebrow text={t('diff.eyebrow')} />
|
||||
<h2 class="font-display leading-tight mt-5" style="font-size: clamp(2rem, 5vw, 3.5rem); color: var(--hds-fg);">
|
||||
{t('diff.title')}
|
||||
@@ -28,7 +28,7 @@ const items = [
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 stagger">
|
||||
{items.map((item) => (
|
||||
<article class="service-card">
|
||||
<div class="icon-circle mb-6">
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
import SectionEyebrow from './SectionEyebrow.astro';
|
||||
import FAQAccordion from './FAQAccordion.astro';
|
||||
|
||||
interface Props {
|
||||
lang: 'es' | 'en';
|
||||
}
|
||||
const { lang } = Astro.props;
|
||||
|
||||
const faqs = lang === 'en' ? [
|
||||
{ q: 'What makes you different from a regular hosting provider?', a: 'A real person answers your call. We are a small team of senior SysAdmins/SREs based in Maldonado, Uruguay. No tickets, no chatbots, no offshore call centers.' },
|
||||
{ q: 'Where is my data physically located?', a: 'We operate infrastructure across four countries: Canada, the United States, Germany and Uruguay. We can work with you to choose the region that best fits your compliance and latency needs.' },
|
||||
{ q: 'Do you offer 24/7 support?', a: 'No. We are available during business hours (Monday to Friday, 9 to 18 UYT). Outside those hours we monitor critical alerts, but human support is provided during business hours.' },
|
||||
{ q: 'Do you manage WordPress sites?', a: 'Yes. We are part of the official WordPress ecosystem. We offer hosting, maintenance, security patches, performance tuning and full development.' },
|
||||
{ q: 'Can you build my company website from scratch?', a: 'Yes. We design and develop institutional sites and e-commerce stores (WooCommerce) with focus on performance, accessibility (WCAG AA) and SEO.' },
|
||||
{ q: 'What if I need a plan that is not listed?', a: 'We have 9 plans total (Básico, WordPress, Institucional, Empresarial, E-commerce, Avanzado, Avanzado HD, Corporativo, Corporativo HD). Chat with us on WhatsApp and we will tailor a plan for you.' },
|
||||
{ q: 'Do you provide cloud servers and virtualization?', a: 'Yes. We install and administer Linux servers, KVM virtualization, private clouds (Nextcloud-style), high availability and 24/7 monitoring with Prometheus/Grafana.' },
|
||||
] : [
|
||||
{ q: '¿Qué los diferencia de un proveedor de hosting tradicional?', a: 'Una persona real te atiende. Somos un equipo chico de SysAdmins/SREs senior radicados en Maldonado, Uruguay. Sin tickets, sin chatbots, sin call centers offshore.' },
|
||||
{ q: '¿Dónde están físicamente mis datos?', a: 'Operamos infraestructura en cuatro países: Canadá, Estados Unidos, Alemania y Uruguay. Podemos trabajar con vos para elegir la región que mejor se ajuste a tus necesidades de compliance y latencia.' },
|
||||
{ q: '¿Tienen soporte 24/7?', a: 'No. Estamos disponibles en horario comercial (lunes a viernes, 9 a 18 hs UYT). Fuera de ese horario monitoreamos alertas críticas, pero el soporte humano se brinda en horario comercial.' },
|
||||
{ q: '¿Manejan sitios WordPress?', a: 'Sí. Somos parte del ecosistema oficial de WordPress. Ofrecemos hosting, mantenimiento, parches de seguridad, optimización de performance y desarrollo completo.' },
|
||||
{ q: '¿Pueden armar el sitio web de mi empresa desde cero?', a: 'Sí. Diseñamos y desarrollamos sitios institucionales y e-commerce (WooCommerce) con foco en performance, accesibilidad (WCAG AA) y SEO.' },
|
||||
{ q: '¿Qué pasa si necesito un plan que no está en la lista?', a: 'Tenemos 9 planes (Básico, WordPress, Institucional, Empresarial, E-commerce, Avanzado, Avanzado HD, Corporativo, Corporativo HD). Charlá con nosotros por WhatsApp y te armamos un plan a medida.' },
|
||||
{ q: '¿Brindan servidores cloud y virtualización?', a: 'Sí. Instalamos y administramos servidores Linux, virtualización KVM, nubes privadas (estilo Nextcloud), alta disponibilidad y monitoreo con Prometheus/Grafana.' },
|
||||
];
|
||||
---
|
||||
<section class="py-20 md:py-28" style="background: var(--hds-bg);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12">
|
||||
<div class="lg:col-span-4 reveal">
|
||||
<SectionEyebrow text={lang === 'en' ? 'Common questions' : 'Preguntas comunes'} />
|
||||
<h2 class="font-display mt-5" style="font-size: clamp(2rem, 4.5vw, 3rem); color: var(--hds-fg);">
|
||||
{lang === 'en'
|
||||
? <>Everything you wanted to <span style="color: var(--color-hds-naranja); font-style: italic;">ask</span>.</>
|
||||
: <>Todo lo que querías <span style="color: var(--color-hds-naranja); font-style: italic;">preguntar</span>.</>}
|
||||
</h2>
|
||||
<p class="text-base mt-5" style="color: var(--hds-fg-soft);">
|
||||
{lang === 'en'
|
||||
? "Did not find your answer? Write to us and we will help."
|
||||
: '¿No encontraste tu respuesta? Escribinos y te ayudamos.'}
|
||||
</p>
|
||||
<a
|
||||
href="https://wa.me/59899812487"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn-primary text-sm mt-6"
|
||||
>
|
||||
{lang === 'en' ? 'Chat on WhatsApp' : 'Hablar por WhatsApp'}
|
||||
</a>
|
||||
</div>
|
||||
<div class="lg:col-span-8 reveal">
|
||||
<div class="rounded-2xl p-6 md:p-8" style="background: var(--hds-card); border: 1px solid var(--hds-line);">
|
||||
<FAQAccordion items={faqs} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
interface Props {
|
||||
lang: 'es' | 'en';
|
||||
}
|
||||
const { lang } = Astro.props;
|
||||
|
||||
const items = [
|
||||
{ src: 'server-room', alt: lang === 'en' ? 'Server room with rack infrastructure' : 'Sala de servidores con racks de infraestructura', title: lang === 'en' ? 'Infrastructure' : 'Infraestructura', col: 'md:col-span-2', row: 'md:row-span-2' },
|
||||
{ src: 'cables', alt: lang === 'en' ? 'Network cables' : 'Cables de red', title: lang === 'en' ? 'Network' : 'Red', col: '', row: '' },
|
||||
{ src: 'code', alt: lang === 'en' ? 'Code on screen' : 'Código en pantalla', title: 'Dev', col: '', row: '' },
|
||||
{ src: 'team', alt: lang === 'en' ? 'Team collaboration' : 'Colaboración del equipo', title: lang === 'en' ? 'Team' : 'Equipo', col: 'md:col-span-2', row: '' },
|
||||
{ src: 'workspace', alt: lang === 'en' ? 'Workspace' : 'Espacio de trabajo', title: lang === 'en' ? 'Workspace' : 'Workspace', col: '', row: '' },
|
||||
{ src: 'racks', alt: lang === 'en' ? 'Server racks' : 'Racks de servidores', title: lang === 'en' ? 'Datacenter' : 'Datacenter', col: '', row: '' },
|
||||
];
|
||||
---
|
||||
<section class="py-20 md:py-28" style="background: var(--hds-bg);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="text-center mb-12 reveal">
|
||||
<span class="inline-block px-3 py-1 rounded-full text-xs font-semibold tracking-[0.2em] uppercase" style="background: var(--hds-bg-soft); color: var(--color-hds-naranja);">
|
||||
{lang === 'en' ? 'Our work' : 'Nuestro trabajo'}
|
||||
</span>
|
||||
<h2 class="font-display mt-4" style="font-size: clamp(2rem, 5vw, 3.5rem); color: var(--hds-fg);">
|
||||
{lang === 'en' ? 'Behind the scenes' : 'Detrás de escena'}.
|
||||
</h2>
|
||||
<p class="mt-4 max-w-2xl mx-auto" style="color: var(--hds-fg-soft);">
|
||||
{lang === 'en'
|
||||
? 'Datacenters, infrastructure, team and the work that powers your projects.'
|
||||
: 'Datacenters, infraestructura, equipo y el trabajo que sostiene tus proyectos.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 auto-rows-[180px] gap-3 md:gap-4 stagger">
|
||||
{items.map((item, idx) => (
|
||||
<figure class={`relative overflow-hidden rounded-xl group ${item.col} ${item.row}`}>
|
||||
<picture>
|
||||
<source type="image/avif" srcset={`/images/gallery/${item.src}.avif`} />
|
||||
<source type="image/webp" srcset={`/images/gallery/${item.src}.webp`} />
|
||||
<img
|
||||
src={`/images/gallery/${item.src}.jpg`}
|
||||
alt={item.alt}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
|
||||
style={item.row ? '' : 'aspect-ratio: 4/3;'}
|
||||
/>
|
||||
</picture>
|
||||
<figcaption class="absolute inset-x-0 bottom-0 p-4 bg-gradient-to-t from-black/80 to-transparent text-white opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<span class="text-xs font-semibold tracking-[0.2em] uppercase" style="color: var(--color-hds-naranja);">
|
||||
{item.title}
|
||||
</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -8,10 +8,36 @@ interface Props {
|
||||
|
||||
const { t, whatsappUrl } = Astro.props;
|
||||
---
|
||||
<section class="relative overflow-hidden pt-24 pb-32 md:pt-32 md:pb-40">
|
||||
<section class="relative overflow-hidden pt-20 pb-28 md:pt-32 md:pb-40">
|
||||
<div class="absolute inset-0 particle-grid" aria-hidden="true"></div>
|
||||
<div class="absolute inset-0" style="background: linear-gradient(180deg, transparent 0%, var(--hds-bg) 100%);" aria-hidden="true"></div>
|
||||
|
||||
<!-- Cuadraditos decorativos (estilo grilla del logo) -->
|
||||
<div class="absolute top-32 right-8 md:right-16 opacity-50" aria-hidden="true">
|
||||
<svg width="120" height="120" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="12" height="12" fill="#EE7623" rx="1"/>
|
||||
<rect x="14" y="0" width="12" height="12" fill="#FA9F5C" rx="1"/>
|
||||
<rect x="28" y="0" width="12" height="12" fill="#FFCBA1" rx="1"/>
|
||||
<rect x="0" y="14" width="12" height="12" fill="#FA9F5C" rx="1"/>
|
||||
<rect x="14" y="14" width="12" height="12" fill="#EE7623" rx="1"/>
|
||||
<rect x="28" y="14" width="12" height="12" fill="#FA9F5C" rx="1"/>
|
||||
<rect x="0" y="28" width="12" height="12" fill="#FFCBA1" rx="1"/>
|
||||
<rect x="14" y="28" width="12" height="12" fill="#FA9F5C" rx="1"/>
|
||||
<rect x="28" y="28" width="12" height="12" fill="#EE7623" rx="1"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-12 left-8 md:left-16 opacity-40" aria-hidden="true">
|
||||
<svg width="80" height="80" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="10" height="10" fill="#FA9F5C" rx="1"/>
|
||||
<rect x="12" y="0" width="10" height="10" fill="#EE7623" rx="1"/>
|
||||
<rect x="0" y="12" width="10" height="10" fill="#FFCBA1" rx="1"/>
|
||||
<rect x="12" y="12" width="10" height="10" fill="#FA9F5C" rx="1"/>
|
||||
<rect x="0" y="24" width="10" height="10" fill="#EE7623" rx="1"/>
|
||||
<rect x="12" y="24" width="10" height="10" fill="#FFCBA1" rx="1"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-4xl">
|
||||
<div class="eyebrow mb-7">
|
||||
@@ -24,15 +50,15 @@ const { t, whatsappUrl } = Astro.props;
|
||||
</h1>
|
||||
|
||||
<p class="text-lg md:text-xl leading-relaxed mb-10 max-w-2xl" style="color: var(--hds-fg-soft);">
|
||||
{t('hero.body')}
|
||||
Soporte humano senior en Maldonado, Uruguay. Del otro lado hay una persona que conoce tu proyecto, no un ticket automático. La misma tecnología que los gigantes, con la cercanía que nos diferencia.
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-4 mb-14">
|
||||
<a href="#planes" class="btn-primary">
|
||||
{t('hero.ctaPrimary')}
|
||||
<a href="/planes/" class="btn-primary">
|
||||
Ver planes
|
||||
</a>
|
||||
<a href={whatsappUrl} target="_blank" rel="noopener noreferrer" class="btn-secondary">
|
||||
{t('hero.ctaSecondary')}
|
||||
Hablar por WhatsApp
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -47,7 +73,7 @@ const { t, whatsappUrl } = Astro.props;
|
||||
</span>
|
||||
<span class="flex items-center gap-2">
|
||||
<span style="color: var(--color-hds-naranja);">●</span>
|
||||
{t('diff.support.title')}
|
||||
Soporte humano
|
||||
</span>
|
||||
<span class="flex items-center gap-2">
|
||||
<span style="color: var(--color-hds-naranja);">●</span>
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
import { monthlyEquivalent, type Plan } from '@/lib/pricing';
|
||||
|
||||
interface Props {
|
||||
plans: Plan[];
|
||||
whatsappUrl: string;
|
||||
}
|
||||
|
||||
const { plans, whatsappUrl } = Astro.props;
|
||||
|
||||
const categoryLabel: Record<Plan['category'], string> = {
|
||||
entry: 'Entrada',
|
||||
mid: 'Profesional',
|
||||
business: 'Business',
|
||||
premium: 'Premium',
|
||||
};
|
||||
|
||||
const categoryColor: Record<Plan['category'], string> = {
|
||||
entry: 'var(--color-hds-durazno)',
|
||||
mid: 'var(--color-hds-naranja)',
|
||||
business: 'var(--color-hds-coral)',
|
||||
premium: 'var(--hds-tinta)',
|
||||
};
|
||||
---
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{plans.map((plan) => (
|
||||
<article class="group relative rounded-xl p-6 transition-all hover:-translate-y-0.5 hover:shadow-md" style="background: var(--hds-card); border: 1px solid var(--hds-line);">
|
||||
<div class="absolute top-0 left-0 right-0 h-1 rounded-t-xl" style={`background: ${categoryColor[plan.category]};`}></div>
|
||||
|
||||
<div class="flex items-start justify-between gap-3 mb-4">
|
||||
<div>
|
||||
<div class="text-[10px] font-bold tracking-[0.2em] uppercase mb-1" style="color: var(--hds-fg-muted);">
|
||||
{categoryLabel[plan.category]}
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold" style="color: var(--hds-fg);">{plan.name}</h3>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="font-display leading-none" style="font-size: 2.5rem; color: var(--color-hds-naranja); font-weight: 500; letter-spacing: -0.02em;">${plan.priceUsdYear}</div>
|
||||
<div class="text-[10px] font-semibold tracking-wider uppercase" style="color: var(--hds-fg-muted);">USD/año</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-xs mb-4" style="color: var(--hds-fg-muted);">
|
||||
~USD {monthlyEquivalent(plan.priceUsdYear)}/mes · {plan.technical} USD técnico · {plan.pricePerMb} USD/MB
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2 text-xs mb-4 pb-4 border-b" style="border-color: var(--hds-line);">
|
||||
<div>
|
||||
<div class="font-semibold" style="color: var(--hds-fg);">{plan.features[0]?.match(/\d+/)?.[0] || '—'} GB</div>
|
||||
<div style="color: var(--hds-fg-muted);">disco</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-semibold" style="color: var(--hds-fg);">{plan.features[2]?.includes('∞') ? '∞' : (plan.features[2]?.match(/\d+/)?.[0] || '—')}</div>
|
||||
<div style="color: var(--hds-fg-muted);">cuentas FTP</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-semibold" style="color: var(--hds-fg);">{plan.features[3]?.match(/\d+/)?.[0] || '—'}</div>
|
||||
<div style="color: var(--hds-fg-muted);">correos</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-semibold" style="color: var(--hds-fg);">{plan.features[5]?.match(/\d+/)?.[0] || '0'}</div>
|
||||
<div style="color: var(--hds-fg-muted);">bases de datos</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href={`https://wa.me/59899812487?text=${encodeURIComponent(`Hola, me interesa el plan ${plan.name} (USD ${plan.priceUsdYear}/año)`)}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn-ghost text-sm"
|
||||
>
|
||||
Ver detalles
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<line x1="5" y1="12" x2="19" y2="12"/>
|
||||
<polyline points="12 5 19 12 12 19"/>
|
||||
</svg>
|
||||
</a>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
import { monthlyEquivalent, type Plan } from '@/lib/pricing';
|
||||
|
||||
interface Props {
|
||||
plans: Plan[];
|
||||
whatsappUrl: string;
|
||||
}
|
||||
|
||||
const { plans, whatsappUrl } = Astro.props;
|
||||
---
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8">
|
||||
{plans.map((plan) => (
|
||||
<div
|
||||
class="relative rounded-2xl p-8 transition-all flex flex-col h-full group hover:-translate-y-1"
|
||||
style={plan.recommended
|
||||
? `background: var(--hds-card); border: 2px solid var(--color-hds-naranja); box-shadow: 0 16px 48px rgba(238,118,35,0.18);`
|
||||
: `background: var(--hds-card); border: 1px solid var(--hds-line);`}
|
||||
>
|
||||
{plan.recommended && (
|
||||
<div class="absolute -top-3.5 left-1/2 -translate-x-1/2 text-[11px] font-bold tracking-[0.2em] uppercase px-4 py-1.5 rounded-full whitespace-nowrap" style="background: var(--color-hds-naranja); color: white; box-shadow: 0 4px 16px rgba(238,118,35,0.4);">
|
||||
⭐ Recomendado
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="mb-6">
|
||||
<div class="text-xs font-semibold tracking-[0.25em] uppercase mb-3" style={`color: ${plan.recommended ? 'var(--color-hds-naranja)' : 'var(--hds-fg-muted)'};`}>
|
||||
{plan.name}
|
||||
</div>
|
||||
<div class="flex items-baseline gap-1.5">
|
||||
<span class="text-2xl font-semibold" style="color: var(--hds-fg);">USD</span>
|
||||
<span class="font-display leading-none" style="font-size: 4.5rem; color: var(--hds-fg); font-weight: 500; letter-spacing: -0.03em;">{plan.priceUsdYear}</span>
|
||||
</div>
|
||||
<div class="text-sm mt-2" style="color: var(--hds-fg-muted);">
|
||||
por año · ~USD {monthlyEquivalent(plan.priceUsdYear)}/mes
|
||||
</div>
|
||||
<div class="text-xs mt-1.5 inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full" style="background: var(--hds-bg-soft); color: var(--hds-fg-muted);">
|
||||
<span>Técnico: USD {plan.technical}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t pt-6 mb-6 flex-grow" style="border-color: var(--hds-line);">
|
||||
<ul class="space-y-2.5 text-sm" style="color: var(--hds-fg-soft);">
|
||||
{plan.features.slice(0, 6).map((feature) => (
|
||||
<li class="flex items-start gap-2.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="flex-shrink-0 mt-0.5" style="color: var(--color-hds-naranja);" aria-hidden="true">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>
|
||||
<span>{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
{plan.features.length > 6 && (
|
||||
<li class="text-xs italic" style="color: var(--hds-fg-muted);">
|
||||
+ {plan.features.length - 6} características más
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href={`https://wa.me/59899812487?text=${encodeURIComponent(`Hola, me interesa el plan ${plan.name} (USD ${plan.priceUsdYear}/año)`)}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class={plan.recommended ? 'btn-primary justify-center' : 'btn-secondary justify-center'}
|
||||
>
|
||||
Elegir {plan.name}
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -16,14 +16,14 @@ const steps = [
|
||||
---
|
||||
<section class="py-20 md:py-28" style="background: var(--hds-bg-soft);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-3xl mb-14 md:mb-20">
|
||||
<div class="max-w-3xl mb-14 md:mb-20 reveal">
|
||||
<SectionEyebrow text={t('process.eyebrow')} />
|
||||
<h2 class="font-display leading-tight mt-5" style="font-size: clamp(2rem, 5vw, 3.5rem); color: var(--hds-fg);">
|
||||
{t('process.title')}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<ol class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-10 md:gap-8 relative">
|
||||
<ol class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-10 md:gap-8 relative stagger">
|
||||
<div class="hidden lg:block absolute top-12 left-[12.5%] right-[12.5%] h-px" style="background: linear-gradient(to right, transparent, var(--color-hds-naranja) 20%, var(--color-hds-naranja) 80%, transparent);" aria-hidden="true"></div>
|
||||
|
||||
{steps.map((step) => (
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
const script = `
|
||||
(function() {
|
||||
if (!('IntersectionObserver' in window)) {
|
||||
document.querySelectorAll('.reveal, .stagger').forEach(el => el.classList.add('is-visible'));
|
||||
return;
|
||||
}
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-visible');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.12, rootMargin: '0px 0px -50px 0px' });
|
||||
document.querySelectorAll('.reveal, .stagger').forEach(el => observer.observe(el));
|
||||
})();
|
||||
`;
|
||||
---
|
||||
<script is:inline set:html={script}></script>
|
||||
@@ -91,7 +91,7 @@ const groups = [
|
||||
---
|
||||
<section id="servicios" class="py-20 md:py-28" style="background: var(--hds-bg);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-3xl mb-14 md:mb-20">
|
||||
<div class="max-w-3xl mb-14 md:mb-20 reveal">
|
||||
<SectionEyebrow text={t('services.eyebrow')} />
|
||||
<h2 class="font-display leading-tight mt-5" style="font-size: clamp(2rem, 5vw, 3.5rem); color: var(--hds-fg);">
|
||||
{t('services.title')}
|
||||
|
||||
@@ -16,7 +16,7 @@ const stats = [
|
||||
---
|
||||
<section class="py-20 md:py-24" style="background: var(--hds-bg); border-top: 1px solid var(--hds-line); border-bottom: 1px solid var(--hds-line);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-10 md:gap-8">
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-10 md:gap-8 stagger">
|
||||
{stats.map((stat) => (
|
||||
<div class="text-center">
|
||||
<div class="font-display leading-none mb-3" style="font-size: clamp(3rem, 6vw, 4.5rem); color: var(--color-hds-naranja); font-weight: 500;">
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
interface Props {
|
||||
lang: 'es' | 'en';
|
||||
}
|
||||
const { lang } = Astro.props;
|
||||
|
||||
const items = [
|
||||
{ initials: 'M', name: 'Mauricio', role: lang === 'en' ? 'Founder & SysAdmin' : 'Fundador y SysAdmin', quote: lang === 'en' ? 'We answer the phone. That is the whole point.' : 'Atendemos el teléfono. Ese es todo el punto.' },
|
||||
{ initials: '?', name: lang === 'en' ? 'Your client here' : 'Tu cliente acá', role: lang === 'en' ? 'Reserved spot' : 'Espacio reservado', quote: lang === 'en' ? 'This space is for your first testimonial.' : 'Este espacio es para tu primer testimonio.' },
|
||||
{ initials: '?', name: lang === 'en' ? 'Coming soon' : 'Próximamente', role: lang === 'en' ? 'Reserved spot' : 'Espacio reservado', quote: lang === 'en' ? 'Tell us when a client is happy and we add it here.' : 'Contanos cuando un cliente esté conforme y lo sumamos acá.' },
|
||||
];
|
||||
---
|
||||
<section class="py-20 md:py-28" style="background: var(--hds-bg-soft);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="text-center mb-12 reveal">
|
||||
<span class="inline-block px-3 py-1 rounded-full text-xs font-semibold tracking-[0.2em] uppercase" style="background: var(--hds-bg); color: var(--color-hds-naranja);">
|
||||
{lang === 'en' ? 'Testimonials' : 'Testimonios'}
|
||||
</span>
|
||||
<h2 class="font-display mt-4" style="font-size: clamp(2rem, 5vw, 3.5rem); color: var(--hds-fg);">
|
||||
{lang === 'en' ? 'What clients say' : 'Lo que dicen los clientes'}.
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 stagger">
|
||||
{items.map((item, idx) => (
|
||||
<article class="rounded-2xl p-8 relative" style={`background: var(--hds-card); border: 1px solid var(--hds-line); ${idx === 0 ? 'border-color: var(--color-hds-naranja); border-width: 1.5px;' : ''}`}>
|
||||
<svg class="absolute top-6 right-6 opacity-20" width="40" height="40" viewBox="0 0 24 24" fill="var(--color-hds-naranja)" aria-hidden="true">
|
||||
<path d="M3 21c3 0 7-1 7-8V5H3v6h3c0 4-1 5-3 5v5zm10 0c3 0 7-1 7-8V5h-7v6h3c0 4-1 5-3 5v5z"/>
|
||||
</svg>
|
||||
|
||||
<div class="flex items-center gap-3 mb-5">
|
||||
<div class="w-12 h-12 rounded-full flex items-center justify-center font-display text-xl" style={`background: ${idx === 0 ? 'var(--color-hds-naranja)' : 'var(--hds-bg-soft)'}; color: ${idx === 0 ? 'white' : 'var(--color-hds-naranja)'};`}>
|
||||
{item.initials}
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-semibold text-sm" style="color: var(--hds-fg);">{item.name}</div>
|
||||
<div class="text-xs" style="color: var(--hds-fg-muted);">{item.role}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<blockquote class="text-[15px] leading-relaxed" style="color: var(--hds-fg-soft);">
|
||||
"{item.quote}"
|
||||
</blockquote>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div class="mt-10 text-center">
|
||||
<p class="text-sm italic" style="color: var(--hds-fg-muted);">
|
||||
{lang === 'en' ? 'Customer testimonials are published with explicit consent. Working on it.' : 'Los testimonios se publican con consentimiento explícito del cliente. Working on it.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
Reference in New Issue
Block a user