feat(404): preparar infraestructura para imagen IA del robot, SVG fallback funcional

- src/pages/404.astro refactorizado: usa <picture> con AVIF/WebP/PNG + onerror que activa SVG inline fallback
- public/images/404/ creado con README.md explicando el workflow de optimizacion
- El SVG procedural del robot (con mate, vapor, ojos medio cerrados, mejillas coral) queda como fallback visible mientras se espera la imagen IA
- Cuando Mauri suba el robot_404.png (o avif/webp), basta con copiar a public/images/404/ y rebuild

Pendiente: que Mauri suba el archivo robot_404.png a public/images/404/ (o confirme la ruta donde lo dejó)
This commit is contained in:
Mauri
2026-06-09 09:54:13 -03:00
parent db6668793e
commit 46af1316de
2 changed files with 167 additions and 104 deletions
+48
View File
@@ -0,0 +1,48 @@
# 404 Robot Image Assets
Cuando se suba la imagen final del robot (PNG/WebP/AVIF), debe ir en esta carpeta:
```
public/images/404/
├── robot-mate.png (fallback, mínimo 480x480)
├── robot-mate.webp (preferido moderno, <50KB)
└── robot-mate.avif (óptimo, <30KB)
```
## Cómo se usa
El `src/pages/404.astro` usa `<picture>` con esta estructura:
```html
<picture>
<source type="image/avif" srcset="/images/404/robot-mate.avif" />
<source type="image/webp" srcset="/images/404/robot-mate.webp" />
<img src="/images/404/robot-mate.png" alt="..." onerror="fallback" />
</picture>
```
Si **ninguna** de las versiones existe, el `onerror` muestra el SVG inline de respaldo (mismo robot dibujado en SVG).
## Generar las versiones optimizadas
Una vez que se tenga la imagen original (PNG o JPG) en `public/images/404/robot-mate-original.png`:
```bash
cd public/images/404
# WebP
convert robot-mate-original.png -quality 82 -resize 600x600 robot-mate.webp
# AVIF
avifenc --min 25 --max 35 --speed 6 -o robot-mate.avif robot-mate-original.png
# PNG fallback
convert robot-mate-original.png -quality 90 -resize 480x480 robot-mate.png
```
## Reemplazo
1. Mauri guarda la imagen IA final (la del robot vago con mate) en `public/images/404/`
2. Correr el script de arriba para generar AVIF/WebP/PNG
3. Rebuild + deploy
4. El sitio muestra la imagen IA automáticamente; si no carga, fallback SVG
+119 -104
View File
@@ -11,124 +11,140 @@ import BaseLayout from '@/layouts/BaseLayout.astro';
<div class="relative max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 w-full"> <div class="relative max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 w-full">
<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">
<!-- Robot SVG -->
<div class="order-2 lg:order-1" aria-hidden="true"> <div class="order-2 lg:order-1" aria-hidden="true">
<svg viewBox="0 0 480 480" xmlns="http://www.w3.org/2000/svg" class="w-full max-w-md mx-auto"> <picture>
<defs> <source type="image/avif" srcset="/images/404/robot-mate.avif" />
<linearGradient id="bg-grad" x1="0" y1="0" x2="0" y2="1"> <source type="image/webp" srcset="/images/404/robot-mate.webp" />
<stop offset="0%" stop-color="#FAF6EE" stop-opacity="0"/> <img
<stop offset="100%" stop-color="#EE7623" stop-opacity="0.08"/> src="/images/404/robot-mate.png"
</linearGradient> alt="Robot tomando mate con cara de vago"
<filter id="soft-shadow" x="-50%" y="-50%" width="200%" height="200%"> width="480"
<feGaussianBlur in="SourceAlpha" stdDeviation="8"/> height="480"
<feOffset dx="0" dy="4" result="offsetblur"/> class="w-full max-w-md mx-auto h-auto"
<feComponentTransfer><feFuncA type="linear" slope="0.15"/></feComponentTransfer> loading="eager"
<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge> decoding="async"
</filter> onerror="this.style.display='none'; document.getElementById('robot-fallback').style.display='block';"
</defs> />
</picture>
<circle cx="240" cy="240" r="220" fill="url(#bg-grad)"/> <div id="robot-fallback" style="display:none;" aria-hidden="true">
<svg viewBox="0 0 480 480" xmlns="http://www.w3.org/2000/svg" class="w-full max-w-md mx-auto">
<defs>
<linearGradient id="bg-grad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#FAF6EE" stop-opacity="0"/>
<stop offset="100%" stop-color="#EE7623" stop-opacity="0.08"/>
</linearGradient>
<filter id="soft-shadow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceAlpha" stdDeviation="8"/>
<feOffset dx="0" dy="4" result="offsetblur"/>
<feComponentTransfer><feFuncA type="linear" slope="0.15"/></feComponentTransfer>
<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<g opacity="0.35"> <circle cx="240" cy="240" r="220" fill="url(#bg-grad)"/>
<rect x="60" y="80" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="68" y="80" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="76" y="80" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="404" y="80" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="412" y="80" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="68" y="88" width="8" height="8" fill="#FFCBA1" rx="1"/>
<rect x="412" y="88" width="8" height="8" fill="#FFCBA1" rx="1"/>
<rect x="60" y="380" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="68" y="380" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="404" y="380" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="412" y="380" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="68" y="388" width="8" height="8" fill="#FFCBA1" rx="1"/>
<rect x="412" y="388" width="8" height="8" fill="#FFCBA1" rx="1"/>
</g>
<ellipse cx="240" cy="420" rx="100" ry="10" fill="#EE7623" opacity="0.12"/> <g opacity="0.35">
<rect x="60" y="80" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="68" y="80" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="76" y="80" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="404" y="80" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="412" y="80" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="68" y="88" width="8" height="8" fill="#FFCBA1" rx="1"/>
<rect x="412" y="88" width="8" height="8" fill="#FFCBA1" rx="1"/>
<rect x="60" y="380" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="68" y="380" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="404" y="380" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="412" y="380" width="8" height="8" fill="#FA9F5C" rx="1"/>
<rect x="68" y="388" width="8" height="8" fill="#FFCBA1" rx="1"/>
<rect x="412" y="388" width="8" height="8" fill="#FFCBA1" rx="1"/>
</g>
<g filter="url(#soft-shadow)"> <ellipse cx="240" cy="420" rx="100" ry="10" fill="#EE7623" opacity="0.12"/>
<rect x="160" y="240" width="160" height="140" rx="20" fill="#E8E2D5" stroke="#EE7623" stroke-width="3"/>
<circle cx="200" cy="280" r="6" fill="#EE7623"> <g filter="url(#soft-shadow)">
<animate attributeName="opacity" values="1;0.3;1" dur="1.8s" repeatCount="indefinite"/> <rect x="160" y="240" width="160" height="140" rx="20" fill="#E8E2D5" stroke="#EE7623" stroke-width="3"/>
<circle cx="200" cy="280" r="6" fill="#EE7623">
<animate attributeName="opacity" values="1;0.3;1" dur="1.8s" repeatCount="indefinite"/>
</circle>
<circle cx="200" cy="280" r="6" fill="#EE7623" opacity="0.3"/>
<rect x="220" y="276" width="80" height="8" rx="2" fill="#1A1612" opacity="0.15"/>
<rect x="220" y="290" width="60" height="6" rx="2" fill="#1A1612" opacity="0.1"/>
<circle cx="280" cy="340" r="14" fill="#FAF6EE" stroke="#EE7623" stroke-width="2"/>
<circle cx="280" cy="340" r="6" fill="#EE7623"/>
</g>
<g filter="url(#soft-shadow)">
<rect x="170" y="100" width="140" height="140" rx="24" fill="#F5EFE3" stroke="#EE7623" stroke-width="3"/>
</g>
<line x1="240" y1="100" x2="240" y2="68" stroke="#EE7623" stroke-width="4" stroke-linecap="round"/>
<circle cx="240" cy="60" r="8" fill="#EE7623">
<animate attributeName="r" values="6;9;6" dur="2s" repeatCount="indefinite"/>
</circle> </circle>
<circle cx="200" cy="280" r="6" fill="#EE7623" opacity="0.3"/>
<rect x="220" y="276" width="80" height="8" rx="2" fill="#1A1612" opacity="0.15"/>
<rect x="220" y="290" width="60" height="6" rx="2" fill="#1A1612" opacity="0.1"/>
<circle cx="280" cy="340" r="14" fill="#FAF6EE" stroke="#EE7623" stroke-width="2"/>
<circle cx="280" cy="340" r="6" fill="#EE7623"/>
</g>
<g filter="url(#soft-shadow)"> <g>
<rect x="170" y="100" width="140" height="140" rx="24" fill="#F5EFE3" stroke="#EE7623" stroke-width="3"/> <path d="M 200 165 Q 210 172 220 165" stroke="#1A1612" stroke-width="4" fill="none" stroke-linecap="round"/>
</g> <line x1="195" y1="158" x2="225" y2="155" stroke="#1A1612" stroke-width="2" stroke-linecap="round" opacity="0.5"/>
<path d="M 195 158 Q 210 168 225 158" stroke="#1A1612" stroke-width="3" fill="none" stroke-linecap="round"/>
<line x1="240" y1="100" x2="240" y2="68" stroke="#EE7623" stroke-width="4" stroke-linecap="round"/> <ellipse cx="260" cy="167" rx="10" ry="12" fill="#FAF6EE" stroke="#1A1612" stroke-width="2"/>
<circle cx="240" cy="60" r="8" fill="#EE7623"> <circle cx="263" cy="170" r="5" fill="#1A1612"/>
<animate attributeName="r" values="6;9;6" dur="2s" repeatCount="indefinite"/> <circle cx="264" cy="167" r="2" fill="#FAF6EE"/>
</circle> </g>
<g> <ellipse cx="190" cy="195" rx="8" ry="4" fill="#FA9F5C" opacity="0.6"/>
<path d="M 200 165 Q 210 172 220 165" stroke="#1A1612" stroke-width="4" fill="none" stroke-linecap="round"/> <ellipse cx="290" cy="195" rx="8" ry="4" fill="#FA9F5C" opacity="0.6"/>
<line x1="195" y1="158" x2="225" y2="155" stroke="#1A1612" stroke-width="2" stroke-linecap="round" opacity="0.5"/>
<path d="M 195 158 Q 210 168 225 158" stroke="#1A1612" stroke-width="3" fill="none" stroke-linecap="round"/>
<ellipse cx="260" cy="167" rx="10" ry="12" fill="#FAF6EE" stroke="#1A1612" stroke-width="2"/> <ellipse cx="240" cy="210" rx="14" ry="10" fill="#1A1612"/>
<circle cx="263" cy="170" r="5" fill="#1A1612"/> <ellipse cx="240" cy="212" rx="10" ry="6" fill="#FAF6EE"/>
<circle cx="264" cy="167" r="2" fill="#FAF6EE"/> <line x1="240" y1="205" x2="240" y2="218" stroke="#1A1612" stroke-width="1.5" opacity="0.3"/>
</g>
<ellipse cx="190" cy="195" rx="8" ry="4" fill="#FA9F5C" opacity="0.6"/> <g filter="url(#soft-shadow)">
<ellipse cx="290" cy="195" rx="8" ry="4" fill="#FA9F5C" opacity="0.6"/> <rect x="120" y="180" width="24" height="80" rx="12" fill="#F5EFE3" stroke="#EE7623" stroke-width="3" transform="rotate(-25 132 220)"/>
<circle cx="110" cy="170" r="18" fill="#F5EFE3" stroke="#EE7623" stroke-width="3"/>
<line x1="92" y1="160" x2="86" y2="152" stroke="#EE7623" stroke-width="3" stroke-linecap="round"/>
<line x1="98" y1="155" x2="94" y2="145" stroke="#EE7623" stroke-width="3" stroke-linecap="round"/>
<line x1="106" y1="153" x2="104" y2="142" stroke="#EE7623" stroke-width="3" stroke-linecap="round"/>
<line x1="114" y1="156" x2="115" y2="146" stroke="#EE7623" stroke-width="3" stroke-linecap="round"/>
<line x1="120" y1="162" x2="124" y2="155" stroke="#EE7623" stroke-width="3" stroke-linecap="round"/>
</g>
<ellipse cx="240" cy="210" rx="14" ry="10" fill="#1A1612"/> <g filter="url(#soft-shadow)">
<ellipse cx="240" cy="212" rx="10" ry="6" fill="#FAF6EE"/> <rect x="336" y="220" width="24" height="80" rx="12" fill="#F5EFE3" stroke="#EE7623" stroke-width="3" transform="rotate(30 348 260)"/>
<line x1="240" y1="205" x2="240" y2="218" stroke="#1A1612" stroke-width="1.5" opacity="0.3"/> <circle cx="358" cy="290" r="16" fill="#F5EFE3" stroke="#EE7623" stroke-width="3"/>
</g>
<g filter="url(#soft-shadow)"> <g transform="translate(330 280)">
<rect x="120" y="180" width="24" height="80" rx="12" fill="#F5EFE3" stroke="#EE7623" stroke-width="3" transform="rotate(-25 132 220)"/> <ellipse cx="22" cy="28" rx="24" ry="20" fill="#1A1612"/>
<circle cx="110" cy="170" r="18" fill="#F5EFE3" stroke="#EE7623" stroke-width="3"/> <ellipse cx="14" cy="22" rx="6" ry="4" fill="#FAF6EE" opacity="0.2"/>
<line x1="92" y1="160" x2="86" y2="152" stroke="#EE7623" stroke-width="3" stroke-linecap="round"/> <ellipse cx="22" cy="14" rx="20" ry="5" fill="#EE7623"/>
<line x1="98" y1="155" x2="94" y2="145" stroke="#EE7623" stroke-width="3" stroke-linecap="round"/> <ellipse cx="22" cy="13" rx="18" ry="3" fill="#FFCBA1"/>
<line x1="106" y1="153" x2="104" y2="142" stroke="#EE7623" stroke-width="3" stroke-linecap="round"/> <rect x="35" y="6" width="8" height="14" rx="2" fill="#8A8580"/>
<line x1="114" y1="156" x2="115" y2="146" stroke="#EE7623" stroke-width="3" stroke-linecap="round"/> <rect x="36" y="2" width="6" height="6" rx="1" fill="#C8C2B8"/>
<line x1="120" y1="162" x2="124" y2="155" stroke="#EE7623" stroke-width="3" stroke-linecap="round"/> <ellipse cx="22" cy="11" rx="14" ry="2" fill="#2E7D5B" opacity="0.8"/>
</g> </g>
<g filter="url(#soft-shadow)"> <g opacity="0.4">
<rect x="336" y="220" width="24" height="80" rx="12" fill="#F5EFE3" stroke="#EE7623" stroke-width="3" transform="rotate(30 348 260)"/> <path d="M 380 270 Q 384 262 380 254" stroke="#8A8580" stroke-width="2" fill="none" stroke-linecap="round">
<circle cx="358" cy="290" r="16" fill="#F5EFE3" stroke="#EE7623" stroke-width="3"/> <animate attributeName="opacity" values="0;0.6;0" dur="2.5s" repeatCount="indefinite"/>
</g> </path>
<path d="M 392 270 Q 396 262 392 254" stroke="#8A8580" stroke-width="2" fill="none" stroke-linecap="round">
<animate attributeName="opacity" values="0;0.6;0" dur="2.5s" begin="0.5s" repeatCount="indefinite"/>
</path>
<path d="M 386 274 Q 390 266 386 258" stroke="#8A8580" stroke-width="2" fill="none" stroke-linecap="round">
<animate attributeName="opacity" values="0;0.6;0" dur="2.5s" begin="1s" repeatCount="indefinite"/>
</path>
</g>
<g transform="translate(330 280)"> <g filter="url(#soft-shadow)">
<ellipse cx="22" cy="28" rx="24" ry="20" fill="#1A1612"/> <rect x="195" y="380" width="30" height="50" rx="10" fill="#F5EFE3" stroke="#EE7623" stroke-width="3"/>
<ellipse cx="14" cy="22" rx="6" ry="4" fill="#FAF6EE" opacity="0.2"/> <rect x="255" y="380" width="30" height="50" rx="10" fill="#F5EFE3" stroke="#EE7623" stroke-width="3"/>
<ellipse cx="22" cy="14" rx="20" ry="5" fill="#EE7623"/> <ellipse cx="210" cy="432" rx="22" ry="8" fill="#EE7623"/>
<ellipse cx="22" cy="13" rx="18" ry="3" fill="#FFCBA1"/> <ellipse cx="270" cy="432" rx="22" ry="8" fill="#EE7623"/>
<rect x="35" y="6" width="8" height="14" rx="2" fill="#8A8580"/> </g>
<rect x="36" y="2" width="6" height="6" rx="1" fill="#C8C2B8"/> </svg>
<ellipse cx="22" cy="11" rx="14" ry="2" fill="#2E7D5B" opacity="0.8"/> </div>
</g>
<g opacity="0.4">
<path d="M 380 270 Q 384 262 380 254" stroke="#8A8580" stroke-width="2" fill="none" stroke-linecap="round">
<animate attributeName="opacity" values="0;0.6;0" dur="2.5s" repeatCount="indefinite"/>
</path>
<path d="M 392 270 Q 396 262 392 254" stroke="#8A8580" stroke-width="2" fill="none" stroke-linecap="round">
<animate attributeName="opacity" values="0;0.6;0" dur="2.5s" begin="0.5s" repeatCount="indefinite"/>
</path>
<path d="M 386 274 Q 390 266 386 258" stroke="#8A8580" stroke-width="2" fill="none" stroke-linecap="round">
<animate attributeName="opacity" values="0;0.6;0" dur="2.5s" begin="1s" repeatCount="indefinite"/>
</path>
</g>
<g filter="url(#soft-shadow)">
<rect x="195" y="380" width="30" height="50" rx="10" fill="#F5EFE3" stroke="#EE7623" stroke-width="3"/>
<rect x="255" y="380" width="30" height="50" rx="10" fill="#F5EFE3" stroke="#EE7623" stroke-width="3"/>
<ellipse cx="210" cy="432" rx="22" ry="8" fill="#EE7623"/>
<ellipse cx="270" cy="432" rx="22" ry="8" fill="#EE7623"/>
</g>
</svg>
</div> </div>
<div class="order-1 lg:order-2 text-center lg:text-left"> <div class="order-1 lg:order-2 text-center lg:text-left">
@@ -158,7 +174,6 @@ import BaseLayout from '@/layouts/BaseLayout.astro';
</a> </a>
</div> </div>
<!-- Server-side include (SSI) — cPanel pone el HTML y Apache lo procesa -->
<!--#if expr="$REMOTE_ADDR" --> <!--#if expr="$REMOTE_ADDR" -->
<div class="mt-10 pt-6 border-t" style="border-color: var(--hds-line);"> <div class="mt-10 pt-6 border-t" style="border-color: var(--hds-line);">
<p class="text-xs font-semibold tracking-[0.2em] uppercase mb-3" style="color: var(--color-hds-naranja);"> <p class="text-xs font-semibold tracking-[0.2em] uppercase mb-3" style="color: var(--color-hds-naranja);">