feat: integrar fotos reales en hero, cards, nosotros y process steps

- Hero: Maldonado aerial como background sutil (opacity 0.12)
- DifferenceCards: fotos en cards Soporte (manos) e Infraestructura (monitor)
- nosotros + en/about: bento grid de 5 fotos con captions honestos
- ProcessSteps: background sutil de manos escribiendo (opacity 0.05)
- Imágenes en public/images/photos/ (JPG + WebP + mobile resizes)
This commit is contained in:
Mauricio López Coria
2026-07-02 14:02:50 -03:00
parent 99d9713a40
commit ded2c974b7
19 changed files with 333 additions and 12 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

+26 -12
View File
@@ -12,9 +12,9 @@ interface Props {
const { t } = Astro.props; const { t } = Astro.props;
const items = [ const items = [
{ Icon: MateIcon, titleKey: 'diff.support.title' as TranslationKey, bodyKey: 'diff.support.body' as TranslationKey }, { Icon: MateIcon, titleKey: 'diff.support.title' as TranslationKey, bodyKey: 'diff.support.body' as TranslationKey, photo: '/images/photos/Manos-escribiendo-teclado-café_1200x800' },
{ Icon: SovereigntyIcon, titleKey: 'diff.sovereignty.title' as TranslationKey, bodyKey: 'diff.sovereignty.body' as TranslationKey }, { Icon: SovereigntyIcon, titleKey: 'diff.sovereignty.title' as TranslationKey, bodyKey: 'diff.sovereignty.body' as TranslationKey, photo: null },
{ Icon: GlobeIcon, titleKey: 'diff.infra.title' as TranslationKey, bodyKey: 'diff.infra.body' as TranslationKey }, { Icon: GlobeIcon, titleKey: 'diff.infra.title' as TranslationKey, bodyKey: 'diff.infra.body' as TranslationKey, photo: '/images/photos/Monitor-closeup-grafana-office_1920x1280' },
]; ];
--- ---
<section class="py-24 md:py-32" style="background: var(--hds-bg-soft);" id="por-que-elegirnos"> <section class="py-24 md:py-32" style="background: var(--hds-bg-soft);" id="por-que-elegirnos">
@@ -31,16 +31,30 @@ const items = [
<div class="diff-grid"> <div class="diff-grid">
{items.map((item, i) => ( {items.map((item, i) => (
<article class="glass-card diff-card p-8 flex flex-col" data-diff-index={i}> <article class="glass-card diff-card p-8 flex flex-col relative overflow-hidden" data-diff-index={i}>
<div class="icon-circle mb-6"> {item.photo && (
<item.Icon class="w-7 h-7" /> <div class="absolute inset-0 pointer-events-none" aria-hidden="true" style="opacity: 0.06;">
<img
src={`${item.photo}.webp`}
alt=""
class="w-full h-full object-cover"
loading="lazy"
decoding="async"
onerror="this.style.display='none'"
/>
</div>
)}
<div class="relative z-10 flex flex-col flex-grow">
<div class="icon-circle mb-6">
<item.Icon class="w-7 h-7" />
</div>
<h3 class="text-xl md:text-2xl font-semibold mb-4" style="color: var(--hds-fg);">
{t(item.titleKey)}
</h3>
<p class="text-[15px] leading-relaxed" style="color: var(--hds-fg-soft);">
{t(item.bodyKey)}
</p>
</div> </div>
<h3 class="text-xl md:text-2xl font-semibold mb-4" style="color: var(--hds-fg);">
{t(item.titleKey)}
</h3>
<p class="text-[15px] leading-relaxed" style="color: var(--hds-fg-soft);">
{t(item.bodyKey)}
</p>
</article> </article>
))} ))}
</div> </div>
+19
View File
@@ -20,6 +20,25 @@ const { t, whatsappUrl } = Astro.props;
aria-hidden="true" aria-hidden="true"
></div> ></div>
<!-- Background photo: Maldonado coastline (subtle) -->
<div class="absolute inset-0 pointer-events-none" aria-hidden="true">
<picture>
<source type="image/webp" srcset="/images/photos/Bahía-aérea-atardecer-mar_1920x800.webp 1920w, /images/photos/Bahía-aérea-atardecer-mar_750.webp 750w" sizes="100vw" />
<img
src="/images/photos/Bahía-aérea-atardecer-mar_1920x800.jpg"
alt=""
width="1920" height="800"
class="w-full h-full object-cover"
style="opacity: 0.12;"
loading="eager"
fetchpriority="high"
decoding="async"
/>
</picture>
<!-- Gradient overlay for text readability -->
<div class="absolute inset-0" style="background: linear-gradient(180deg, var(--hds-bg) 0%, transparent 30%, transparent 60%, var(--hds-bg) 100%);"></div>
</div>
<!-- Floating decorative shapes (parallax) --> <!-- Floating decorative shapes (parallax) -->
<div class="absolute inset-0 overflow-hidden pointer-events-none" aria-hidden="true"> <div class="absolute inset-0 overflow-hidden pointer-events-none" aria-hidden="true">
<div id="hero-float-top" class="absolute opacity-30" style="top: 15%; right: 12%;"> <div id="hero-float-top" class="absolute opacity-30" style="top: 15%; right: 12%;">
+12
View File
@@ -18,6 +18,18 @@ const steps = [
<section class="py-24 md:py-32 relative overflow-hidden" style="background: var(--hds-bg-soft);" id="como-trabajamos"> <section class="py-24 md:py-32 relative overflow-hidden" style="background: var(--hds-bg-soft);" id="como-trabajamos">
<div class="absolute inset-0 pointer-events-none" style="background: var(--hds-gradient-section);" aria-hidden="true"></div> <div class="absolute inset-0 pointer-events-none" style="background: var(--hds-gradient-section);" aria-hidden="true"></div>
<!-- Background photo: team working (very subtle, blurred) -->
<div class="absolute inset-0 pointer-events-none" aria-hidden="true" style="opacity: 0.05; backdrop-filter: blur(4px);">
<img
src="/images/photos/Manos-escribiendo-teclado-café_1200x800.webp"
alt=""
class="w-full h-full object-cover"
loading="lazy"
decoding="async"
onerror="this.style.display='none'"
/>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="max-w-3xl mb-16 md:mb-20 reveal"> <div class="max-w-3xl mb-16 md:mb-20 reveal">
<SectionEyebrow text={t('process.eyebrow')} /> <SectionEyebrow text={t('process.eyebrow')} />
+138
View File
@@ -58,6 +58,58 @@ const whatsappUrl = 'https://wa.me/59899812487';
</div> </div>
</section> </section>
<!-- Photo bento grid -->
<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="max-w-3xl mb-10 reveal">
<SectionEyebrow text="Behind the scenes" />
<h2 class="font-display leading-tight mt-5" style="font-size: clamp(1.75rem, 4vw, 2.5rem); color: var(--hds-fg);">
The people and the place
</h2>
</div>
<div class="photo-bento reveal">
<figure class="photo-bento-item photo-bento-lg">
<picture>
<source type="image/webp" srcset="/images/photos/Equipo-en-escritorio-con-mate_1920x1080.webp" />
<img src="/images/photos/Equipo-en-escritorio-con-mate_1920x1080.jpg" alt="Team member working at desk with mate" width="1920" height="1080" class="w-full h-full object-cover" loading="lazy" decoding="async" />
</picture>
<figcaption>Our desk, Maldonado</figcaption>
</figure>
<figure class="photo-bento-item">
<picture>
<source type="image/webp" srcset="/images/photos/Manos-escribiendo-teclado-café_1200x800.webp" />
<img src="/images/photos/Manos-escribiendo-teclado-café_1200x800.jpg" alt="Hands typing on keyboard with coffee" width="1200" height="800" class="w-full h-full object-cover" loading="lazy" decoding="async" />
</picture>
</figure>
<figure class="photo-bento-item">
<picture>
<source type="image/webp" srcset="/images/photos/Monitor-closeup-grafana-office_1920x1280.webp" />
<img src="/images/photos/Monitor-closeup-grafana-office_1920x1280.jpg" alt="Monitoring dashboard closeup" width="1920" height="1280" class="w-full h-full object-cover" loading="lazy" decoding="async" />
</picture>
</figure>
<figure class="photo-bento-item photo-bento-wide">
<picture>
<source type="image/webp" srcset="/images/photos/Bahía-aérea-atardecer-mar_1920x800.webp" />
<img src="/images/photos/Bahía-aérea-atardecer-mar_1920x800.jpg" alt="Maldonado coastline at sunset" width="1920" height="800" class="w-full h-full object-cover" loading="lazy" decoding="async" />
</picture>
<figcaption>Maldonado, Uruguay — our home</figcaption>
</figure>
<figure class="photo-bento-item">
<picture>
<source type="image/webp" srcset="/images/photos/Datacenter-racks-enorme_1920x960.webp" />
<img src="/images/photos/Datacenter-racks-enorme_1920x960.jpg" alt="Datacenter racks (provider infrastructure)" width="1920" height="960" class="w-full h-full object-cover" loading="lazy" decoding="async" />
</picture>
<figcaption>Provider infrastructure (CA, USA, DE)</figcaption>
</figure>
</div>
</div>
</section>
<section class="py-20 md:py-24" style="background: var(--hds-bg-soft);"> <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="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
@@ -95,3 +147,89 @@ const whatsappUrl = 'https://wa.me/59899812487';
<CtaFinal t={t} whatsappUrl={whatsappUrl} /> <CtaFinal t={t} whatsappUrl={whatsappUrl} />
</BaseLayout> </BaseLayout>
<style>
.photo-bento {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@media (min-width: 640px) {
.photo-bento {
grid-template-columns: repeat(2, 1fr);
gap: 1.25rem;
}
}
@media (min-width: 1024px) {
.photo-bento {
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 240px 240px;
gap: 1.5rem;
}
.photo-bento-lg {
grid-column: span 2;
grid-row: span 2;
}
.photo-bento-wide {
grid-column: span 2;
}
}
.photo-bento-item {
position: relative;
overflow: hidden;
border-radius: var(--radius-glass, 24px);
border: 1px solid var(--glass-border);
aspect-ratio: 3 / 2;
}
.photo-bento-lg {
aspect-ratio: 16 / 10;
}
.photo-bento-wide {
aspect-ratio: 24 / 10;
}
@media (min-width: 1024px) {
.photo-bento-item {
aspect-ratio: unset;
}
}
.photo-bento-item img {
transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.photo-bento-item:hover img {
transform: scale(1.05);
}
.photo-bento-item figcaption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 0.75rem 1rem;
font-size: 0.8rem;
color: var(--hds-bg);
background: linear-gradient(transparent, rgba(0,0,0,0.6));
opacity: 0;
transition: opacity 0.3s ease;
}
.photo-bento-item:hover figcaption {
opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
.photo-bento-item img {
transition: none;
}
.photo-bento-item:hover img {
transform: none;
}
}
</style>
+138
View File
@@ -58,6 +58,58 @@ const whatsappUrl = 'https://wa.me/59899812487';
</div> </div>
</section> </section>
<!-- Photo bento grid -->
<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="max-w-3xl mb-10 reveal">
<SectionEyebrow text={lang === 'en' ? 'Behind the scenes' : 'Detrás de pantalla'} />
<h2 class="font-display leading-tight mt-5" style="font-size: clamp(1.75rem, 4vw, 2.5rem); color: var(--hds-fg);">
{lang === 'en' ? 'The people and the place' : 'La gente y el lugar'}
</h2>
</div>
<div class="photo-bento reveal">
<figure class="photo-bento-item photo-bento-lg">
<picture>
<source type="image/webp" srcset="/images/photos/Equipo-en-escritorio-con-mate_1920x1080.webp" />
<img src="/images/photos/Equipo-en-escritorio-con-mate_1920x1080.jpg" alt={lang === 'en' ? 'Team member working at desk with mate' : 'Equipo trabajando en el escritorio con mate'} width="1920" height="1080" class="w-full h-full object-cover" loading="lazy" decoding="async" />
</picture>
<figcaption>{lang === 'en' ? 'Our desk, Maldonado' : 'Nuestro escritorio, Maldonado'}</figcaption>
</figure>
<figure class="photo-bento-item">
<picture>
<source type="image/webp" srcset="/images/photos/Manos-escribiendo-teclado-café_1200x800.webp" />
<img src="/images/photos/Manos-escribiendo-teclado-café_1200x800.jpg" alt={lang === 'en' ? 'Hands typing on keyboard with coffee' : 'Manos escribiendo en teclado con café'} width="1200" height="800" class="w-full h-full object-cover" loading="lazy" decoding="async" />
</picture>
</figure>
<figure class="photo-bento-item">
<picture>
<source type="image/webp" srcset="/images/photos/Monitor-closeup-grafana-office_1920x1280.webp" />
<img src="/images/photos/Monitor-closeup-grafana-office_1920x1280.jpg" alt={lang === 'en' ? 'Monitoring dashboard closeup' : 'Dashboard de monitoreo'} width="1920" height="1280" class="w-full h-full object-cover" loading="lazy" decoding="async" />
</picture>
</figure>
<figure class="photo-bento-item photo-bento-wide">
<picture>
<source type="image/webp" srcset="/images/photos/Bahía-aérea-atardecer-mar_1920x800.webp" />
<img src="/images/photos/Bahía-aérea-atardecer-mar_1920x800.jpg" alt={lang === 'en' ? 'Maldonado coastline at sunset' : 'Bahía de Maldonado al atardecer'} width="1920" height="800" class="w-full h-full object-cover" loading="lazy" decoding="async" />
</picture>
<figcaption>{lang === 'en' ? 'Maldonado, Uruguay — our home' : 'Maldonado, Uruguay — nuestra casa'}</figcaption>
</figure>
<figure class="photo-bento-item">
<picture>
<source type="image/webp" srcset="/images/photos/Datacenter-racks-enorme_1920x960.webp" />
<img src="/images/photos/Datacenter-racks-enorme_1920x960.jpg" alt={lang === 'en' ? 'Datacenter racks (provider infrastructure)' : 'Racks de datacenter (infraestructura de proveedores)'} width="1920" height="960" class="w-full h-full object-cover" loading="lazy" decoding="async" />
</picture>
<figcaption>{lang === 'en' ? 'Provider infrastructure (CA, USA, DE)' : 'Infraestructura de proveedores (CA, USA, DE)'}</figcaption>
</figure>
</div>
</div>
</section>
<section class="py-20 md:py-24" style="background: var(--hds-bg-soft);"> <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="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
@@ -95,3 +147,89 @@ const whatsappUrl = 'https://wa.me/59899812487';
<CtaFinal t={t} whatsappUrl={whatsappUrl} /> <CtaFinal t={t} whatsappUrl={whatsappUrl} />
</BaseLayout> </BaseLayout>
<style>
.photo-bento {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@media (min-width: 640px) {
.photo-bento {
grid-template-columns: repeat(2, 1fr);
gap: 1.25rem;
}
}
@media (min-width: 1024px) {
.photo-bento {
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 240px 240px;
gap: 1.5rem;
}
.photo-bento-lg {
grid-column: span 2;
grid-row: span 2;
}
.photo-bento-wide {
grid-column: span 2;
}
}
.photo-bento-item {
position: relative;
overflow: hidden;
border-radius: var(--radius-glass, 24px);
border: 1px solid var(--glass-border);
aspect-ratio: 3 / 2;
}
.photo-bento-lg {
aspect-ratio: 16 / 10;
}
.photo-bento-wide {
aspect-ratio: 24 / 10;
}
@media (min-width: 1024px) {
.photo-bento-item {
aspect-ratio: unset;
}
}
.photo-bento-item img {
transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.photo-bento-item:hover img {
transform: scale(1.05);
}
.photo-bento-item figcaption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 0.75rem 1rem;
font-size: 0.8rem;
color: var(--hds-bg);
background: linear-gradient(transparent, rgba(0,0,0,0.6));
opacity: 0;
transition: opacity 0.3s ease;
}
.photo-bento-item:hover figcaption {
opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
.photo-bento-item img {
transition: none;
}
.photo-bento-item:hover img {
transform: none;
}
}
</style>