feat: Awwwards transformation Phases 1-6

Phase 1 — Design System:
- Glassmorphism tokens (--glass-bg, --glass-border, --blur-glass)
- Gradient tokens (--hds-gradient-hero, --hds-gradient-section)
- Motion tokens (--ease-spring, --ease-out-expo)
- .glass-card component class with hover uplift + mouse-tracking glow
- .noise-overlay: SVG feTurbulence texture at 0.025 opacity
- Buttons with spring easing, prefers-reduced-motion support

Phase 2 — Preloader + Hero:
- Preloader.astro: vanilla JS logo fade-in + loading bar + slide-up
- Uses sessionStorage to skip on repeat visits
- Hero.astro: full-bleed 100dvh, GSAP timeline, glass ring decoration
- Parallax floating SVG shapes (sine yoyo animation)
- i18n: hero.scrollDown key

Phase 3 — 'Por qué elegirnos' Bento Grid:
- DifferenceCards.astro: asymmetric 3-column bento grid
- Glass cards with mouse-tracking glow (--mouse-x/--mouse-y)
- GSAP ScrollTrigger stagger reveal (opacity 0→1, y: 40→0)

Phase 4 — Pricing + Process:
- planes.astro: 5 visible + 4 in details accordion
- Empresarial as hero card, price annual only (no toggle)
- ProcessSteps.astro: timeline + ScrollTrigger progress bar

Phase 5 — Global Polish:
- LenisScroll.astro: smooth scroll (duration 1.2, cubic easing)
- GSAP ticker sync, noise overlay in BaseLayout

Phase 6 — Deploy:
- deploy.sh: auto-detects repo path, auto-builds if stale
- Relative paths instead of hardcoded /root/opencode/development
This commit is contained in:
Mauricio López Coria
2026-07-01 23:16:36 -03:00
parent f3d7f3d64f
commit 9aeaeafa3f
14 changed files with 896 additions and 123 deletions
+163 -43
View File
@@ -2,10 +2,9 @@
import BaseLayout from '@/layouts/BaseLayout.astro';
import SectionEyebrow from '@/components/SectionEyebrow.astro';
import PricingTiersFeatured from '@/components/PricingTiersFeatured.astro';
import PricingGrid from '@/components/PricingGrid.astro';
import CloudDivider from '@/components/CloudDivider.astro';
import CtaFinal from '@/components/CtaFinal.astro';
import { PLANS, getFeaturedPlans, getOtherPlans, ADDITIONAL_PLANS } from '@/lib/pricing';
import { PLANS } from '@/lib/pricing';
import { getLangFromUrl, useTranslations } from '@/i18n/utils';
const lang = getLangFromUrl(Astro.url);
@@ -13,8 +12,16 @@ const t = useTranslations(lang);
const whatsappUrl = 'https://wa.me/59899812487';
const base = lang === 'en' ? '/en' : '';
const featured = getFeaturedPlans();
const other = getOtherPlans();
// 5 visible plans on home — Empresarial as hero
const VISIBLE_SLUGS = ['basico', 'institucional', 'empresarial', 'e-commerce', 'avanzado'];
const visible = PLANS.filter(p => VISIBLE_SLUGS.includes(p.slug));
const empresarial = PLANS.find(p => p.slug === 'empresarial');
const visibleWithoutHero = visible.filter(p => p.slug !== 'empresarial');
const advanced = PLANS.filter(p => !VISIBLE_SLUGS.includes(p.slug));
function monthly(annual: number): string {
return (annual / 12).toFixed(2);
}
---
<BaseLayout
title={lang === 'en' ? 'Hosting plans' : 'Planes de hosting'}
@@ -23,66 +30,179 @@ const other = getOtherPlans();
: 'Planes de hosting en USD, con cobro anual. Desde básico hasta corporativo HD, elegí el plan que se adapta a tu proyecto.'}
lang={lang}
>
<section class="pt-20 pb-12 md:pt-28 md:pb-16 relative" style="background: var(--hds-bg-soft);">
<div class="absolute inset-0 particle-grid opacity-60" aria-hidden="true"></div>
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<section class="pt-24 pb-16 md:pt-32 md:pb-20 relative overflow-hidden" style="background: var(--hds-bg-soft);">
<div class="absolute inset-0" style="background: var(--hds-gradient-hero);" aria-hidden="true"></div>
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<SectionEyebrow text={lang === 'en' ? 'Hosting plans' : 'Planes de hosting'} />
<h1 class="font-display leading-tight mt-5" style="font-size: clamp(2.5rem, 6vw, 4.5rem); color: var(--hds-fg);">
Elegí el plan que se<br/>adapta a tu <span style="color: var(--color-hds-naranja); font-style: italic;">proyecto</span>.
{lang === 'en' ? 'Plans designed around your' : 'Planes pensados alrededor de tu'} <span style="color: var(--color-hds-naranja);">{lang === 'en' ? 'project' : 'proyecto'}</span>.
</h1>
<p class="text-lg mt-5 max-w-2xl" style="color: var(--hds-fg-soft);">
<p class="text-lg mt-5 max-w-2xl mx-auto" style="color: var(--hds-fg-soft);">
{lang === 'en'
? 'Prices in USD, billed annually. Features may vary depending on the project and server configuration.'
: 'Precios en USD, con cobro anual. Las características pueden variar según el proyecto y la configuración del servidor.'}
? 'Prices in USD, billed annually. No surprises, no hidden costs.'
: 'Precios en USD, facturación anual. Sin sorpresas, sin costos ocultos.'}
</p>
</div>
</section>
<CloudDivider fillClass="fill-hds-bg" />
<section class="py-16 md:py-20" 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">
<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' ? 'Most popular' : 'Más elegidos'}
</span>
<h2 class="font-display mt-4" style="font-size: clamp(1.75rem, 4vw, 2.5rem); color: var(--hds-fg);">
{lang === 'en' ? 'Featured plans' : 'Planes destacados'}
<!-- Hero plan: Empresarial (glass-card-hero) -->
{empresarial && (
<section class="py-16 md:py-24" style="background: var(--hds-bg);">
<div class="max-w-xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-8">
<span class="inline-block px-3 py-1 rounded-full text-xs font-semibold tracking-[0.2em] uppercase mb-4" style="background: var(--hds-bg-soft); color: var(--color-hds-naranja);">
{lang === 'en' ? 'Most popular' : 'El más elegido'}
</span>
</div>
<article class="glass-card glass-card-hero p-8 md:p-12 text-center">
<div class="text-xs font-semibold tracking-[0.25em] uppercase mb-3" style="color: var(--color-hds-naranja);">
{empresarial.name}
</div>
<div class="flex items-baseline justify-center gap-1.5 mb-6">
<span class="text-lg font-semibold" style="color: var(--hds-fg-soft);">US$</span>
<span class="font-display leading-none" style="font-size: 5rem; color: var(--hds-fg); font-weight: 500; letter-spacing: -0.03em;">{empresarial.priceUsdYear}</span>
<span class="text-lg" style="color: var(--hds-fg-soft);">/año</span>
</div>
<div class="text-sm mb-8" style="color: var(--hds-fg-muted);">
(US${monthly(empresarial.priceUsdYear)}/mes)
</div>
<div class="border-t pt-6 mb-8" style="border-color: var(--glass-border);">
<ul class="grid grid-cols-2 gap-2.5 text-sm text-left max-w-md mx-auto">
{empresarial.features.map(f => (
<li class="flex items-start gap-2.5">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" 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 style="color: var(--hds-fg-soft);">{f}</span>
</li>
))}
</ul>
</div>
<a href={whatsappUrl} target="_blank" rel="noopener noreferrer" class="btn-primary text-base px-10 inline-flex">
{lang === 'en' ? 'Choose' : 'Elegir'} {empresarial.name}
</a>
</article>
</div>
</section>
)}
<!-- 4 secondary plans (glass cards in 2-col grid) -->
<section class="py-12 md:py-16" style="background: var(--hds-bg);">
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-10">
<h2 class="font-display" style="font-size: clamp(1.5rem, 3.5vw, 2.25rem); color: var(--hds-fg);">
{lang === 'en' ? 'More options for every need' : 'Más opciones para cada necesidad'}
</h2>
</div>
<PricingTiersFeatured plans={featured} whatsappUrl={whatsappUrl} />
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 stagger">
{visibleWithoutHero.map(plan => (
<article class="glass-card p-8 flex flex-col h-full">
<div class="text-xs font-semibold tracking-[0.25em] uppercase mb-3" style={`color: ${plan.slug === 'institucional' ? 'var(--color-hds-naranja)' : 'var(--hds-fg-muted)'};`}>
{plan.slug === 'institucional' ? `⭐ ${plan.name} (recomendado)` : plan.name}
</div>
<div class="flex items-baseline gap-1 mb-3">
<span class="text-lg font-semibold" style="color: var(--hds-fg-soft);">US$</span>
<span class="font-display leading-none" style="font-size: 3.5rem; color: var(--hds-fg); font-weight: 500; letter-spacing: -0.03em;">{plan.priceUsdYear}</span>
<span class="text-sm" style="color: var(--hds-fg-soft);">/año</span>
</div>
<div class="text-xs mb-6" style="color: var(--hds-fg-muted);">
(US${monthly(plan.priceUsdYear)}/mes)
</div>
<div class="mt-10 text-center text-sm space-y-1" style="color: var(--hds-fg-muted);">
<p>⚠️ {lang === 'en' ? 'Prices are in US Dollars (USD). Billed annually.' : 'Los precios están expresados en dólares estadounidenses (USD). El cobro es anual.'}</p>
<p>{lang === 'en' ? 'Features may vary based on project and server configuration.' : 'Las características pueden variar según el proyecto y la configuración del servidor.'}</p>
<div class="border-t pt-5 mb-6 flex-grow" style="border-color: var(--glass-border);">
<ul class="space-y-2 text-sm">
{plan.features.slice(0, 5).map(f => (
<li class="flex items-start gap-2.5">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" 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 style="color: var(--hds-fg-soft);">{f}</span>
</li>
))}
</ul>
{plan.features.length > 5 && (
<p class="text-xs mt-3 italic" style="color: var(--hds-fg-muted);">
+{plan.features.length - 5} características más
</p>
)}
</div>
<a href={whatsappUrl} target="_blank" rel="noopener noreferrer" class="btn-secondary justify-center">
{lang === 'en' ? 'Choose' : 'Elegir'} {plan.name}
</a>
</article>
))}
</div>
</div>
</section>
<CloudDivider fillClass="fill-hds-bg-soft" flip={true} />
<!-- Advanced plans accordion -->
<section class="py-12 md:py-20" style="background: var(--hds-bg);">
<div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
<details class="group">
<summary class="flex items-center justify-between cursor-pointer py-4 px-6 rounded-2xl glass-card">
<div>
<span class="font-semibold" style="color: var(--hds-fg);">
{lang === 'en' ? 'Advanced plans' : 'Planes avanzados'}
</span>
<span class="text-sm ml-3" style="color: var(--hds-fg-muted);">
{lang === 'en' ? `${advanced.length} plans for high-demand projects` : `${advanced.length} planes para proyectos de alta demanda`}
</span>
</div>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="transition-transform group-open:rotate-180" style="color: var(--color-hds-naranja);" aria-hidden="true">
<polyline points="6 9 12 15 18 9"/>
</svg>
</summary>
<section class="py-20 md:py-24" 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">
<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(--hds-fg-soft);">
{lang === 'en' ? 'Complete catalog' : 'Catálogo completo'}
</span>
<h2 class="font-display mt-4" style="font-size: clamp(1.75rem, 4vw, 2.5rem); color: var(--hds-fg);">
{lang === 'en' ? 'All 9 plans' : 'Los 9 planes'}
</h2>
<p class="mt-3 max-w-2xl mx-auto" style="color: var(--hds-fg-soft);">
{lang === 'en' ? 'From basic personal hosting to corporate HD with everything. Find the one that fits.' : 'Desde el hosting personal básico hasta corporativo HD con todo incluido. Encontrá el que mejor se ajusta.'}
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-5 mt-6">
{advanced.map(plan => (
<article class="glass-card p-6 flex flex-col h-full">
<div class="text-xs font-semibold tracking-[0.2em] uppercase mb-3" style="color: var(--hds-fg-muted);">
{plan.name}
</div>
<div class="flex items-baseline gap-1 mb-2">
<span class="text-sm font-semibold" style="color: var(--hds-fg-soft);">US$</span>
<span class="font-display leading-none" style="font-size: 2.75rem; color: var(--hds-fg); font-weight: 500; letter-spacing: -0.03em;">{plan.priceUsdYear}</span>
<span class="text-xs" style="color: var(--hds-fg-soft);">/año</span>
</div>
<div class="text-xs mb-4" style="color: var(--hds-fg-muted);">
(US${monthly(plan.priceUsdYear)}/mes)
</div>
<PricingGrid plans={other} whatsappUrl={whatsappUrl} />
<div class="border-t pt-4 mb-5 flex-grow" style="border-color: var(--glass-border);">
<ul class="space-y-1.5 text-sm">
{plan.features.slice(0, 5).map(f => (
<li class="flex items-start gap-2.5">
<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" 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 style="color: var(--hds-fg-soft);">{f}</span>
</li>
))}
</ul>
</div>
<div class="mt-16 text-center">
<p class="text-sm" style="color: var(--hds-fg-muted);">
{lang === 'en' ? 'Need a custom plan?' : '¿Necesitás un plan a medida?'} <a href={whatsappUrl} target="_blank" rel="noopener noreferrer" class="font-semibold" style="color: var(--color-hds-naranja);">{lang === 'en' ? 'Chat with us' : 'Charla con nosotros'}</a>
</p>
</div>
<a href={whatsappUrl} target="_blank" rel="noopener noreferrer" class="btn-ghost justify-center text-sm">
{lang === 'en' ? 'Choose' : 'Elegir'} {plan.name}
</a>
</article>
))}
</div>
</details>
</div>
</section>
<CloudDivider fillClass="fill-hds-bg-soft" />
<!-- CTA -->
<section class="py-16 md:py-24" style="background: var(--hds-bg-soft);">
<div class="max-w-2xl mx-auto px-4 text-center">
<p class="text-sm mb-4" style="color: var(--hds-fg-muted);">
{lang === 'en' ? 'Need a custom plan?' : '¿Necesitás un plan a medida?'}
</p>
<a href={whatsappUrl} target="_blank" rel="noopener noreferrer" class="btn-primary text-base px-10 inline-flex">
{lang === 'en' ? 'Let us talk' : 'Charlemos'}
</a>
</div>
</section>
</BaseLayout>