Compare commits

...
6 Commits
Author SHA1 Message Date
fanfuso 28cce9a68c mejoras en el blog 2026-09-22 21:21:19 -03:00
fanfuso 0a88f4df20 v1 lista 2026-09-21 17:00:09 -03:00
fanfuso 3b38b9b760 cambios postproduccion 2026-09-21 11:08:17 -03:00
fanfuso e36ec1e8ca cambios php 2026-09-21 02:57:06 -03:00
fanfuso 82816cb680 efecto fluid 2026-09-20 16:01:31 -03:00
fanfuso cfb97b486e blog+contacto php listo 2026-09-20 13:28:57 -03:00
45 changed files with 10963 additions and 1197 deletions
+132
View File
@@ -0,0 +1,132 @@
<!DOCTYPE html>
<html lang="es" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Responder el mismo mensaje 50 veces al día no es escalable. Las automatizaciones bien hechas te permiten captar prospectos calificados mientras dormís.">
<meta name="keywords" content="automatizaciones, Instagram, chatbots, leads, ManyChat, Amor y Roma">
<meta property="og:title" content="Automatizaciones en Instagram: de 0 a 100 leads/mes">
<meta property="og:description" content="Responder el mismo mensaje 50 veces al día no es escalable. Las automatizaciones bien hechas te permiten captar prospectos.">
<meta property="og:image" content="../assets/blog/automatizacion.jpg">
<title>Automatizaciones en Instagram | Blog Amor y Roma</title>
<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="../style/main.css">
<link rel="stylesheet" href="../style/blog.css">
</head>
<body>
<div id="menu-container"></div>
<main>
<article class="articulo-completo">
<div class="articulo-header">
<a href="../blog.html" class="volver-link">← Volver al blog</a>
<span class="badge-filosofia">Automatizaciones</span>
<h1>Automatizaciones en Instagram: de 0 a 100 leads/mes sin volverte loco</h1>
<div class="articulo-meta">
<span>📅 12 de Agosto de 2026</span>
<span>⏱ 9 min de lectura</span>
<span>✍️ Amor y Roma</span>
</div>
<img src="../assets/blog/automatizacion.jpg" alt="Automatizaciones en Instagram" class="articulo-imagen">
</div>
<div class="articulo-contenido">
<p>Si tenés un negocio y usás Instagram para vender, probablemente vivís esto:</p>
<ul>
<li>Te escriben por DM preguntando lo mismo una y otra vez</li>
<li>Se te escapan mensajes entre medio de otras tareas</li>
<li>Perdé ventas por no responder a tiempo</li>
<li>Pasás horas al día pegado al celular</li>
</ul>
<p><strong>Hay una forma mejor.</strong></p>
<h2>¿Qué son las automatizaciones (y qué no son)?</h2>
<p>No son robots fríos que responden "Hola, en breve te atendemos". Eso es lo que la gente ODIA.</p>
<p>Las automatizaciones bien hechas son flujos conversacionales inteligentes que:</p>
<ul>
<li>Responden al instante, 24/7</li>
<li>Hacen preguntas para entender qué necesita la persona</li>
<li>Filtran y cualifican leads automáticamente</li>
<li>Derivan a WhatsApp o agendan llamadas solo cuando corresponde</li>
<li>Recopilan datos (nombre, email, teléfono) sin que vos pidas nada</li>
</ul>
<h2>El sistema de 0 a 100 leads/mes</h2>
<h3>Paso 1: Identificá las preguntas repetitivas</h3>
<p>¿Qué te preguntan siempre?</p>
<ul>
<li>"¿Cuál es el precio?"</li>
<li>"¿Hacés envíos a...?"</li>
<li>"¿Tienen disponibilidad para...?"</li>
</ul>
<p>Esas son tus primeras automatizaciones.</p>
<h3>Paso 2: Creá flujos por palabras clave</h3>
<p>En Instagram podés configurar que cuando alguien comente con cierta palabra en una publicación o historia, se dispare un mensaje automático por DM.</p>
<p><strong>Ejemplo:</strong></p>
<ul>
<li>Publicás: "¿Querés saber más sobre nuestro servicio? Comentá INFO"</li>
<li>La persona comenta "INFO"</li>
<li>Automáticamente recibe un DM con información y preguntas de cualificación</li>
</ul>
<h3>Paso 3: Cualificá antes de derivar</h3>
<p>No todos los que escriben son clientes ideales. Tu flujo debería preguntar:</p>
<ul>
<li>¿Qué necesitás específicamente?</li>
<li>¿Cuándo lo necesitás?</li>
<li>¿Tenés presupuesto definido?</li>
</ul>
<p>Así solo recibís notificaciones de leads calificados.</p>
<h3>Paso 4: Integrá con tu CRM o base de datos</h3>
<p>Los datos que recopilás deberían ir a algún lado útil:</p>
<ul>
<li>Google Sheets</li>
<li>Tu CRM</li>
<li>Tu plataforma de email marketing</li>
</ul>
<p>Así no perdés ningún prospecto y podés hacer seguimiento.</p>
<h2>Errores que matan tus automatizaciones</h2>
<ul>
<li><strong>Ser demasiado robótico:</strong> Usá un tono conversacional, como si hablaras vos.</li>
<li><strong>Pedir demasiada información de una:</strong> Pedí solo lo esencial al principio.</li>
<li><strong>No dar opción a hablar con humano:</strong> Siempre dejá una salida: "Si preferís hablar con alguien, escribinos 'AGENTE'".</li>
<li><strong>Configurar y olvidar:</strong> Revisá cada tanto si los flujos funcionan y ajustá según feedback.</li>
</ul>
<h2>Herramientas que usamos en Amor y Roma</h2>
<p>Trabajamos principalmente con:</p>
<ul>
<li><strong>ManyChat:</strong> El más completo para Instagram y Facebook</li>
<li><strong>Zapier:</strong> Para conectar todo entre sí</li>
<li><strong>Google Sheets/Airtable:</strong> Para almacenar leads</li>
</ul>
<h2>El resultado: tiempo + conversión</h2>
<p>Cuando implementamos automatizaciones para un cliente, normalmente vemos:</p>
<ul>
<li>⏰ <strong>8-12 horas/semana</strong> ahorradas en respuestas manuales</li>
<li>📈 <strong>40-60% más de leads</strong> cualificados (porque no se escapa nadie)</li>
<li> <strong>Cero estrés</strong> por "no respondí a tiempo"</li>
</ul>
<blockquote>Las automatizaciones bien hechas no reemplazan la atención humana: la potencian, liberándote de lo repetitivo para que te enfoques en lo que realmente importa.</blockquote>
<h2>¿Listo para dejar de vivir pegado al celular?</h2>
<p>Si sentís que Instagram te consume más tiempo del que debería, o sabés que se te escapan oportunidades por no responder rápido, hablemos. En Amor y Roma diseñamos flujos de automatización que suenan humanos, cualifican solos y te avisan solo cuando hay un lead de verdad.</p>
<div style="text-align: center; margin-top: 3rem;">
<a href="../contacto.html" class="btn btn-primary">Automatizá tu Instagram</a>
</div>
</div>
</article>
</main>
<div id="footer-container"></div>
<script src="../scripts/main.js"></script>
</body>
</html>
+110
View File
@@ -0,0 +1,110 @@
<!DOCTYPE html>
<html lang="es" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="No es magia, es método. Una landing page efectiva no depende de la suerte. Acá te contamos los 7 principios que aplicamos en cada proyecto.">
<meta name="keywords" content="landing page, conversión, desarrollo web, diseño web, Amor y Roma">
<meta property="og:title" content="Landing pages que convierten: 7 principios probados">
<meta property="og:description" content="No es magia, es método. Una landing page efectiva no depende de la suerte ni de tendencias pasajeras.">
<meta property="og:image" content="../assets/blog/landing-page.jpg">
<title>Landing pages que convierten | Blog Amor y Roma</title>
<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="../style/main.css">
<link rel="stylesheet" href="../style/blog.css">
</head>
<body>
<div id="menu-container"></div>
<main>
<article class="articulo-completo">
<div class="articulo-header">
<a href="../blog.html" class="volver-link">← Volver al blog</a>
<span class="badge-filosofia">Web</span>
<h1>Landing pages que convierten: 7 principios probados</h1>
<div class="articulo-meta">
<span> 15 de Julio de 2026</span>
<span>⏱ 8 min de lectura</span>
<span>✍️ Amor y Roma</span>
</div>
<img src="../assets/blog/landing-page.jpg" alt="Landing page que convierte" class="articulo-imagen">
</div>
<div class="articulo-contenido">
<p>Si estás invirtiendo en publicidad o generando contenido en redes, pero tu sitio web no convierte, estás tirando plata. Literal.</p>
<p>Una landing page (página de aterrizaje) no es un lugar bonito para mostrar información. Es una herramienta estratégica diseñada con un único objetivo: conseguir que el visitante tome una acción específica.</p>
<h2>Los 7 principios que usamos en Amor y Roma</h2>
<h3>1. Claridad antes que creatividad</h3>
<p>Tu visitante tiene 3 segundos para entender:</p>
<ul>
<li>Qué ofrecés</li>
<li>Para quién es</li>
<li>Qué tiene que hacer</li>
</ul>
<p>Si necesita más tiempo, perdiste. Por eso, evitamos lenguaje rebuscado y vamos directo al grano.</p>
<h3>2. Un solo objetivo, un solo llamado a la acción</h3>
<p>¿Querés que agenden una llamada? ¿Que descarguen algo? ¿Que compren? Elegí UNA cosa y eliminá toda distracción. <strong>Menos es más.</strong></p>
<h3>3. Beneficios, no características</h3>
<p>A nadie le importa que tu sitio tenga "diseño responsive" o "SEO optimizado". Lo que importa es:</p>
<ul>
<li>"Podés contactarnos desde el celular en cualquier momento"</li>
<li>"Te van a encontrar cuando te busquen en Google"</li>
</ul>
<p>Traducí cada característica en un beneficio tangible.</p>
<h3>4. Prueba social real</h3>
<p>Testimonios genéricos ("Excelente servicio, muy recomendado") no sirven. Necesitás:</p>
<ul>
<li>Nombres reales (o al menos iniciales y cargo)</li>
<li>Resultados específicos ("Aumentamos un 40% las consultas en 2 meses")</li>
<li>Fotos o logos que den credibilidad</li>
</ul>
<h3>5. Diseño que guía la mirada</h3>
<p>Usamos patrones visuales probados:</p>
<ul>
<li><strong>F-Pattern:</strong> La gente escanea en forma de F</li>
<li><strong>Z-Pattern:</strong> Para páginas con menos contenido</li>
</ul>
<p>Colocamos la información clave en esos puntos calientes.</p>
<h3>6. Velocidad = Conversiones</h3>
<p>Cada segundo de carga reduce tus conversiones un 7%. Por eso:</p>
<ul>
<li>Optimizamos cada imagen</li>
<li>Minimizamos código</li>
<li>Usamos hosting de calidad</li>
<li>Evitamos plugins innecesarios</li>
</ul>
<h3>7. Mobile-first, no mobile-also</h3>
<p>El 70%+ de tu tráfico viene del celular. Si tu sitio se ve "más o menos" en móvil, no alcanza. Tiene que ser impecable.</p>
<h2>El método Amor y Roma: Estrategia + Ejecución</h2>
<p>No usamos plantillas de WordPress pesadas ni constructores visuales que ralentizan todo. Desarrollamos cada landing desde cero, pensando en:</p>
<ul>
<li>Tu audiencia específica</li>
<li>Tu propuesta de valor única</li>
<li>Tus objetivos comerciales reales</li>
</ul>
<blockquote>Una landing page efectiva no es un lugar bonito. Es una herramienta estratégica diseñada con un único objetivo: conseguir que el visitante tome una acción específica.</blockquote>
<h2>¿Tu web convierte o solo existe?</h2>
<p>Si sentís que tu sitio actual no está generando los resultados que esperás, o estás por lanzar algo nuevo y querés hacerlo bien, conversemos. Desarrollamos landing pages y sitios web a medida que no solo se ven bien, sino que trabajan para vos 24/7.</p>
<div style="text-align: center; margin-top: 3rem;">
<a href="../contacto.html" class="btn btn-primary">Desarrollá tu landing page</a>
</div>
</div>
</article>
</main>
<div id="footer-container"></div>
<script src="../scripts/main.js"></script>
</body>
</html>
+88
View File
@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="es" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Muchos emprendedores confunden el símbolo con la sustancia. Tener un logo bonito no es tener una marca. Acá te explicamos la diferencia.">
<meta name="keywords" content="branding, logo, identidad de marca, diseño estratégico, Amor y Roma">
<meta property="og:title" content="Tu logo no es tu marca (y por qué importa)">
<meta property="og:description" content="Muchos emprendedores confunden el símbolo con la sustancia. Tener un logo bonito no es tener una marca.">
<meta property="og:image" content="../assets/blog/logo-marca.jpg">
<title>Tu logo no es tu marca | Blog Amor y Roma</title>
<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="../style/main.css">
<link rel="stylesheet" href="../style/blog.css">
</head>
<body>
<div id="menu-container"></div>
<main>
<article class="articulo-completo">
<div class="articulo-header">
<a href="../blog.html" class="volver-link">← Volver al blog</a>
<span class="badge-filosofia">Branding</span>
<h1>Tu logo no es tu marca (y por qué importa)</h1>
<div class="articulo-meta">
<span>📅 10 de Junio de 2026</span>
<span>⏱ 6 min de lectura</span>
<span>✍️ Amor y Roma</span>
</div>
<img src="../assets/blog/logo-marca.jpg" alt="Logo vs Marca" class="articulo-imagen">
</div>
<div class="articulo-contenido">
<p>Cuando nos dicen "necesito un logo", en realidad escuchamos "necesito que mi marca exista". Y ahí está el primer error conceptual que puede costarte caro.</p>
<h2>Logo vs. Marca: la diferencia fundamental</h2>
<p>El logo es solo la punta del iceberg. Es el símbolo visual que representa algo mucho más grande: tu promesa de valor, tu personalidad, tu forma de operar en el mundo. La marca, en cambio, es la experiencia completa que las personas tienen con tu negocio.</p>
<p><strong>Pensalo así: el logo es tu firma. La marca es tu reputación.</strong></p>
<h2>¿Por qué las plantillas de branding no funcionan?</h2>
<p>En un mundo saturado de Canva y logos pre-fabricados, la tentación de "resolver rápido" es enorme. Pero cuando usás una plantilla:</p>
<ul>
<li><strong>Perdés diferenciación:</strong> Si tu logo se parece al de otros 50 negocios, ¿por qué te elegirían a vos?</li>
<li><strong>No comunicás tu esencia:</strong> Un símbolo genérico no transmite tu historia, tus valores ni tu forma única de operar.</li>
<li><strong>Limitás tu crecimiento:</strong> Lo que funciona hoy para un emprendimiento incipiente, mañana puede quedar chico para una empresa consolidada.</li>
</ul>
<h2>El branding estratégico: Amor + Roma en acción</h2>
<p>En Amor y Roma, no empezamos dibujando. Empezamos preguntando:</p>
<ul>
<li>¿Qué problema resolvés?</li>
<li>¿A quién le hablás?</li>
<li>¿Qué te hace diferente?</li>
<li>¿Cómo querés que se sienta la gente al interactuar con vos?</li>
</ul>
<p>Recién después de entender eso, construimos:</p>
<ul>
<li><strong>Arquetipo de marca:</strong> ¿Sos el héroe, el sabio, el creador? Esto define tu personalidad.</li>
<li><strong>Sistema visual completo:</strong> No solo un logo, sino colores, tipografías, estilos de imagen que funcionen juntos.</li>
<li><strong>Manual de aplicación:</strong> Para que tu marca se vea coherente en cada punto de contacto.</li>
<li><strong>Personajes o elementos distintivos:</strong> Si aplica, creamos activos que humanicen tu comunicación.</li>
</ul>
<h2>El resultado: una marca que resuena</h2>
<p>Cuando invertís en branding estratégico, no estás comprando "un dibujo lindo". Estás construyendo:</p>
<ul>
<li><strong>Reconocimiento:</strong> La gente te identifica al instante.</li>
<li><strong>Confianza:</strong> Transmitís profesionalismo y solidez.</li>
<li><strong>Conexión emocional:</strong> Tu audiencia se siente representada por vos.</li>
<li><strong>Base para crecer:</strong> Tenés los cimientos para escalar sin perder identidad.</li>
</ul>
<blockquote>Una marca bien construida no solo se ve profesional: genera confianza, atrae clientes ideales y permite cobrar lo que realmente valés.</blockquote>
<h2>¿Estás listo para construir una marca, no solo un logo?</h2>
<p>Si sentís que tu identidad actual no te representa, o estás empezando y querés hacerlo bien desde el inicio, hablemos. En Amor y Roma diseñamos identidades con esencia y estrategia, sin plantillas ni fórmulas repetidas.</p>
<div style="text-align: center; margin-top: 3rem;">
<a href="../contacto.html" class="btn btn-primary">Hablemos sobre tu marca</a>
</div>
</div>
</article>
</main>
<div id="footer-container"></div>
<script src="../scripts/main.js"></script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64px" height="64px" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"
viewBox="0 0 5.34 5.31"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<g id="Capa_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<g id="_2347189464160">
<path fill="#221728" fill-opacity="0.250980" d="M2.67 0c1.47,0 2.67,1.19 2.67,2.65 0,1.47 -1.2,2.66 -2.67,2.66 -1.47,0 -2.67,-1.19 -2.67,-2.66 0,-1.46 1.2,-2.65 2.67,-2.65z"/>
<path fill="#221728" d="M0.24 3.79c0.29,0.65 0.98,1.28 1.78,1.46l0 -1.77 -0.55 0 -0.01 -0.83 0.55 0.01c0.06,-0.62 -0.02,-1.13 0.4,-1.44 0.39,-0.27 0.98,-0.27 1.5,-0.18l-0.01 0.75c-0.78,-0.05 -0.96,0.06 -0.9,0.87l0.86 -0.01 -0.16 0.83 -0.71 0.01 0 1.82c0.92,-0.08 1.72,-0.74 2.08,-1.47 0.29,-0.61 0.35,-1.32 0.15,-1.96 -0.14,-0.43 -0.36,-0.82 -0.66,-1.11 -1.06,-1.03 -2.83,-1.04 -3.86,0.05 -0.73,0.78 -0.9,2 -0.46,2.97z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64px" height="64px" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"
viewBox="0 0 7.24 7.24"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<g id="Capa_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<g id="_2346936265280">
<path fill="#221728" d="M2.1 0.58c0.23,-0.02 0.51,0 0.75,0 0.51,0 1.01,0 1.52,0 0.25,0 0.52,-0.01 0.76,0 0.83,0.04 1.48,0.69 1.53,1.51 0.01,0.24 0,0.52 0,0.76l0 1.52c0,0.25 0.01,0.52 0,0.76 -0.04,0.83 -0.69,1.48 -1.52,1.53 -0.23,0.01 -0.51,0 -0.75,0l-1.52 0c-0.25,0 -0.52,0.01 -0.76,0 -0.83,-0.04 -1.48,-0.69 -1.53,-1.52 -0.02,-0.23 0,-0.51 0,-0.75l0 -1.52c0,-0.53 -0.06,-0.91 0.15,-1.37 0.23,-0.51 0.75,-0.88 1.37,-0.92zm-0.02 -0.57c-1.02,0.06 -1.77,0.77 -2,1.62 -0.08,0.28 -0.08,0.51 -0.08,0.82l0 1.81c0,0.29 -0.01,0.61 0.01,0.9 0.05,1.01 0.78,1.78 1.61,2 0.29,0.07 0.52,0.07 0.83,0.07 0.6,0 1.2,0 1.81,0 0.29,0 0.61,0.02 0.9,0 1.01,-0.05 1.77,-0.77 2,-1.62 0.07,-0.28 0.07,-0.51 0.07,-0.82l0 -1.81c0,-0.29 0.01,-0.61 0,-0.9 -0.06,-1.01 -0.78,-1.78 -1.62,-2 -0.28,-0.08 -0.51,-0.07 -0.82,-0.07l-1.81 0c-0.29,0 -0.62,-0.02 -0.9,0z"/>
<path fill="#221728" d="M3.53 2.29c0.27,-0.02 0.58,0.06 0.8,0.2 0.1,0.07 0.1,0.07 0.18,0.14 0.11,0.09 0.22,0.23 0.29,0.38 0.21,0.41 0.2,0.93 -0.05,1.32 -0.07,0.11 -0.07,0.09 -0.14,0.18 -0.1,0.11 -0.25,0.23 -0.38,0.3 -0.16,0.07 -0.32,0.12 -0.52,0.14 -0.77,0.05 -1.37,-0.54 -1.42,-1.24 -0.01,-0.21 0.02,-0.41 0.09,-0.58 0.07,-0.18 0.14,-0.27 0.25,-0.41 0.21,-0.25 0.56,-0.4 0.9,-0.43zm-0.01 -0.57c-1.02,0.06 -1.86,0.92 -1.8,2 0.05,1.01 0.92,1.86 2,1.8 1.01,-0.06 1.86,-0.92 1.8,-2.01 -0.06,-1 -0.92,-1.85 -2,-1.79z"/>
<path fill="#221728" fill-opacity="0.250980" d="M3.53 2.29c-0.34,0.03 -0.69,0.18 -0.9,0.43 -0.11,0.14 -0.18,0.23 -0.25,0.41 -0.07,0.17 -0.1,0.37 -0.09,0.58 0.05,0.7 0.65,1.29 1.42,1.24 0.2,-0.02 0.36,-0.07 0.52,-0.14 0.13,-0.07 0.28,-0.19 0.38,-0.3 0.07,-0.09 0.07,-0.07 0.14,-0.18 0.25,-0.39 0.26,-0.91 0.05,-1.32 -0.07,-0.15 -0.18,-0.29 -0.29,-0.38 -0.08,-0.07 -0.08,-0.07 -0.18,-0.14 -0.22,-0.14 -0.53,-0.22 -0.8,-0.2zm-0.01 -0.57c1.08,-0.06 1.94,0.79 2,1.79 0.06,1.09 -0.79,1.95 -1.8,2.01 -1.08,0.06 -1.95,-0.79 -2,-1.8 -0.06,-1.08 0.78,-1.94 1.8,-2zm1.95 -0.38c0.22,-0.03 0.4,0.13 0.43,0.32 0.03,0.22 -0.13,0.41 -0.32,0.43 -0.23,0.04 -0.41,-0.13 -0.44,-0.32 -0.03,-0.23 0.14,-0.4 0.33,-0.43zm-3.37 -0.76c-0.62,0.04 -1.14,0.41 -1.37,0.92 -0.21,0.46 -0.15,0.84 -0.15,1.37l0 1.52c0,0.24 -0.02,0.52 0,0.75 0.05,0.83 0.7,1.48 1.53,1.52 0.24,0.01 0.51,0 0.76,0l1.52 0c0.24,0 0.52,0.01 0.75,0 0.83,-0.05 1.48,-0.7 1.52,-1.53 0.01,-0.24 0,-0.51 0,-0.76l0 -1.52c0,-0.24 0.01,-0.52 0,-0.76 -0.05,-0.82 -0.7,-1.47 -1.53,-1.51 -0.24,-0.01 -0.51,0 -0.76,0 -0.51,0 -1.01,0 -1.52,0 -0.24,0 -0.52,-0.02 -0.75,0z"/>
<path fill="#221728" d="M5.47 1.34c-0.19,0.03 -0.36,0.2 -0.33,0.43 0.03,0.19 0.21,0.36 0.44,0.32 0.19,-0.02 0.35,-0.21 0.32,-0.43 -0.03,-0.19 -0.21,-0.35 -0.43,-0.32z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="64px" height="64px" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"
viewBox="0 0 4.52 4.52"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<g id="Capa_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<g id="_2346933204656">
<polygon fill="#221728" fill-opacity="0.250980" stroke="#373435" stroke-width="0.02" stroke-miterlimit="22.9256" stroke-opacity="0.250980" points="0.28,0.35 4.19,0.35 4.19,4.2 0.28,4.2 "/>
<path fill="#221728" d="M0.7 1.72l0.67 0c0.01,0.16 0,0.37 0,0.54l0 1.49c0,0.02 0,0.05 0,0.07 0,0.03 0.01,0.03 -0.01,0.05l-0.66 0 0 -2.15zm1.73 0.29c0.05,-0.03 0.06,-0.14 0.26,-0.25 0.31,-0.18 0.87,-0.13 1.06,0.24 0.13,0.26 0.12,0.55 0.12,0.87 0,0.33 0,0.67 0,1l-0.67 0c0,-0.33 0,-0.67 0,-1 0,-0.32 0,-0.63 -0.37,-0.62 -0.35,0.02 -0.38,0.31 -0.38,0.63 0,0.33 0,0.66 0,0.99l-0.66 0 0 -2.15 0.64 0 0 0.29zm-1.45 -1.36c0.22,-0.03 0.41,0.13 0.44,0.33 0.07,0.51 -0.69,0.61 -0.77,0.12 -0.04,-0.24 0.13,-0.42 0.33,-0.45zm-0.4 -0.64c-0.3,0.04 -0.54,0.26 -0.57,0.56 -0.02,0.15 -0.01,2.5 -0.01,2.82 0,0.16 -0.01,0.4 0.01,0.55 0.03,0.3 0.27,0.53 0.56,0.57 0.15,0.01 2.49,0 2.82,0 0.16,0 0.4,0.02 0.55,0 0.3,-0.04 0.53,-0.27 0.57,-0.57 0.01,-0.14 0,-2.49 0,-2.81 0,-0.16 0.02,-0.4 0,-0.56 -0.04,-0.29 -0.27,-0.53 -0.57,-0.56 -0.14,-0.02 -2.49,-0.01 -2.81,-0.01 -0.16,0 -0.4,-0.01 -0.55,0.01z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="40px" height="40px" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"
viewBox="0 0 10.64 10.63"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<g id="Capa_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<g id="_2348118537696">
<path fill="#221728" d="M3.3 2.99c-0.45,0.32 -0.61,0.87 -0.5,1.4 0.19,1 1.46,2.29 2.19,2.7 0.09,0.05 0.24,0.11 0.32,0.17l0.63 0.27c0.2,0 0.52,0.22 0.99,0.11 0.21,-0.04 0.44,-0.14 0.6,-0.27 0.17,-0.16 0.34,-0.37 0.37,-0.61 0.02,-0.14 0,-0.23 -0.04,-0.29 -0.1,-0.18 -0.78,-0.61 -1.09,-0.63 -0.2,-0.01 -0.25,-0.01 -0.36,0.12 -0.02,0.05 -0.14,0.2 -0.21,0.31 -0.06,0.1 -0.12,0.24 -0.28,0.23 -0.6,-0.11 -1.43,-0.9 -1.76,-1.42 -0.09,-0.15 -0.1,-0.24 0,-0.37 0.11,-0.15 0.25,-0.26 0.31,-0.5 0.02,-0.3 -0.22,-0.88 -0.41,-1.12 -0.06,-0.07 -0.11,-0.12 -0.21,-0.16 -0.04,-0.02 -0.08,-0.03 -0.12,-0.03 -0.14,-0.02 -0.16,-0.03 -0.27,0.02l-0.16 0.07z"/>
<path fill="#221728" fill-opacity="0.250980" stroke="#373435" stroke-width="0.03" stroke-miterlimit="22.9256" stroke-opacity="0.250980" d="M1.2 4.14c0.04,-0.15 0.15,-0.53 0.2,-0.58 0.11,-0.44 0.86,-1.29 1.14,-1.53 0.48,-0.39 1.09,-0.71 1.75,-0.89 1.46,-0.42 3.02,0.07 4.09,1.13 0.17,0.17 0.33,0.36 0.48,0.58 0.25,0.35 0.54,0.9 0.62,1.33 0.1,0.22 0.15,0.85 0.15,1.14 0,0.17 -0.02,0.44 -0.07,0.73 -0.09,0.61 -0.25,0.99 -0.56,1.51 -0.05,0.09 -0.16,0.25 -0.23,0.34 -1.27,1.7 -3.59,2.24 -5.52,1.17 -0.06,-0.03 -0.19,-0.09 -0.22,-0.14 -0.18,-0.06 -0.53,0.1 -0.73,0.14 -0.06,0.03 -0.45,0.13 -0.51,0.12 -0.06,0.06 -0.38,0.12 -0.45,0.13 0.02,-0.14 0.1,-0.37 0.11,-0.4l0.02 -0.03c-0.01,-0.08 0.24,-1 0.28,-1.1 0.01,-0.1 -0.01,-0.09 -0.04,-0.17 -0.05,-0.04 -0.24,-0.4 -0.28,-0.5 -0.41,-0.58 -0.46,-1.92 -0.33,-2.61 0.01,-0.07 0.07,-0.28 0.1,-0.37zm2.1 -1.15c-0.45,0.32 -0.61,0.87 -0.5,1.4 0.19,1 1.46,2.29 2.19,2.7 0.09,0.05 0.24,0.11 0.32,0.17l0.63 0.27c0.2,0 0.52,0.22 0.99,0.11 0.21,-0.04 0.44,-0.14 0.6,-0.27 0.17,-0.16 0.34,-0.37 0.37,-0.61 0.02,-0.14 0,-0.23 -0.04,-0.29 -0.1,-0.18 -0.78,-0.61 -1.09,-0.63 -0.2,-0.01 -0.25,-0.01 -0.36,0.12 -0.02,0.05 -0.14,0.2 -0.21,0.31 -0.06,0.1 -0.12,0.24 -0.28,0.23 -0.6,-0.11 -1.43,-0.9 -1.76,-1.42 -0.09,-0.15 -0.1,-0.24 0,-0.37 0.11,-0.15 0.25,-0.26 0.31,-0.5 0.02,-0.3 -0.22,-0.88 -0.41,-1.12 -0.06,-0.07 -0.11,-0.12 -0.21,-0.16 -0.04,-0.02 -0.08,-0.03 -0.12,-0.03 -0.14,-0.02 -0.16,-0.03 -0.27,0.02l-0.16 0.07z"/>
<path fill="#221728" d="M1.2 4.14c0.04,-0.15 0.15,-0.53 0.2,-0.58 0.11,-0.44 0.86,-1.29 1.14,-1.53 0.48,-0.39 1.09,-0.71 1.75,-0.89 1.46,-0.42 3.02,0.07 4.09,1.13 0.17,0.17 0.33,0.36 0.48,0.58 0.25,0.35 0.54,0.9 0.62,1.33 0.1,0.22 0.15,0.85 0.15,1.14 0,0.17 -0.02,0.44 -0.07,0.73 -0.09,0.61 -0.25,0.99 -0.56,1.51 -0.05,0.09 -0.16,0.25 -0.23,0.34 -1.27,1.7 -3.59,2.24 -5.52,1.17 -0.06,-0.03 -0.19,-0.09 -0.22,-0.14 -0.18,-0.06 -0.53,0.1 -0.73,0.14 -0.06,0.03 -0.45,0.13 -0.51,0.12 -0.06,0.06 -0.38,0.12 -0.45,0.13 0.02,-0.14 0.1,-0.37 0.11,-0.4l0.02 -0.03c-0.01,-0.08 0.24,-1 0.28,-1.1 0.01,-0.1 -0.01,-0.09 -0.04,-0.17 -0.05,-0.04 -0.24,-0.4 -0.28,-0.5 -0.41,-0.58 -0.46,-1.92 -0.33,-2.61 0.01,-0.07 0.07,-0.28 0.1,-0.37zm0.59 -2.77c-0.42,0.4 -0.74,0.73 -1.05,1.28 -0.06,0.1 -0.11,0.2 -0.16,0.3 -0.06,0.11 -0.1,0.2 -0.15,0.32l-0.09 0.25c-0.03,0.07 -0.01,0.04 -0.05,0.08 0,0.09 -0.13,0.49 -0.16,0.64 -0.05,0.23 -0.08,0.47 -0.09,0.71 -0.06,0.81 0.1,1.3 0.1,1.5 0.04,0.04 0.08,0.26 0.09,0.33 0.04,0.13 0.08,0.23 0.11,0.35 0.04,0.05 0.14,0.33 0.18,0.41 0.05,0.11 0.18,0.28 0.15,0.41 -0.02,0.09 -0.23,0.9 -0.25,0.92 -0.03,0.13 -0.07,0.25 -0.1,0.37 -0.01,0.07 -0.06,0.29 -0.1,0.34 -0.32,1.01 -0.35,1.14 0.35,0.99 0.03,-0.02 0.48,-0.13 0.54,-0.13l0.64 -0.18c0.04,-0.03 0.47,-0.14 0.53,-0.14 0.01,0 0.01,-0.01 0.04,-0.02 0.03,-0.01 0.04,-0.01 0.08,-0.02 0.08,-0.03 0.21,-0.05 0.31,-0.08 0.12,-0.04 0.17,0.01 0.28,0.07 0.14,0.07 0.48,0.19 0.56,0.24 0.11,0.02 0.21,0.07 0.32,0.1 0.25,0.06 0.45,0.12 0.72,0.15 0.14,0.02 0.27,0.04 0.41,0.05 0.28,0.01 0.63,0.01 0.9,-0.03 0.95,-0.15 2.17,-0.61 2.88,-1.24 0.06,-0.06 0.02,-0.03 0.09,-0.06 0.02,-0.04 0.04,-0.05 0.08,-0.09 0.04,-0.03 0.06,-0.05 0.1,-0.09 0.55,-0.56 1.28,-1.87 1.46,-2.62 0.27,-1.19 0.02,-1.89 0.04,-2.19 -0.04,-0.05 -0.03,-0.09 -0.05,-0.18 -0.05,-0.22 -0.12,-0.38 -0.16,-0.56 -0.02,-0.03 -0.21,-0.5 -0.21,-0.54 -0.05,-0.03 -0.06,-0.08 -0.08,-0.13 -0.06,-0.08 -0.09,-0.17 -0.14,-0.25 -0.05,-0.07 -0.11,-0.16 -0.14,-0.24 -0.1,-0.11 -0.19,-0.28 -0.33,-0.43 -0.07,-0.07 -0.12,-0.14 -0.18,-0.21 -0.12,-0.15 -0.45,-0.45 -0.57,-0.55 -0.54,-0.46 -1.49,-0.87 -2.19,-1.07 -0.53,-0.09 -0.99,-0.17 -1.55,-0.11 -0.27,0.02 -0.48,0.04 -0.73,0.1 -0.51,0.11 -0.91,0.26 -1.32,0.48 -0.41,0.21 -0.78,0.45 -1.11,0.77z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Enlaces oficiales de Estudio Creativo. Transformando ideas en experiencias visuales extraordinarias."> <meta name="description" content="Enlaces oficiales de Estudio Creativo. Transformando ideas en experiencias visuales extraordinarias.">
<title>Estudio Creativo - Enlaces</title> <title>Estudio Creativo - Enlaces</title>
<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
<link rel="icon" href="images/icons/favicon.png" type="image/x-icon"> <link rel="icon" href="images/icons/favicon.png" type="image/x-icon">
<!-- Fuentes sincronizadas con main.css --> <!-- Fuentes sincronizadas con main.css -->
+80 -301
View File
@@ -4,305 +4,102 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Blog de Amor y Roma. Estrategia, branding, desarrollo web y marketing digital con esencia y solidez."> <meta name="description" content="Blog de Amor y Roma. Estrategia, branding, desarrollo web y marketing digital con esencia y solidez.">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css">
<title>Blog | Amor y Roma — Recursos y Estrategias</title> <title>Blog | Amor y Roma — Recursos y Estrategias</title>
<link rel="stylesheet" href="style/main.css"> <link rel="stylesheet" href="style/main.css">
<link rel="stylesheet" href="style/blog.css">
</head> </head>
<body> <body>
<div id="fluid-bg" aria-hidden="true">
<canvas id="fluidCanvas"></canvas>
</div>
<div id="menu-container"></div> <div id="menu-container"></div>
<main> <main>
<!-- SECCIÓN 1: HERO COMPACTO --> <!-- HERO -->
<section class="blog-hero-compact"> <section class="blog-hero-compact">
<div class="hero-compact-content"> <div class="hero-compact-content">
<span class="badge-filosofia"></span> <span class="badge-filosofia">Ideas que construyen marcas</span>
<h1 class="hero-compact-title">Ideas que construyen marcas</h1> <h1 class="hero-compact-title">Estrategia, diseño y tecnología explicados sin tecnicismos</h1>
<p class="hero-compact-subtitle"> <p class="hero-compact-subtitle">
Estrategia, diseño y tecnología explicados sin tecnicismos innecesarios. Consejos prácticos de branding, web y marketing digital para hacer crecer tu negocio con esencia y estructura.
</p>
</div>
</section>
<!-- SECCIÓN 2: HEADER DE FILTROS (STICKY CON BUSCADOR) -->
<section class="blog-filters-header" id="blog-filters-header">
<div class="filters-header-container">
<!-- Fila superior: Buscador + Filtros principales -->
<div class="filters-top-row">
<div class="search-wrapper-compact">
<span class="search-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/>
<path d="m21 21-4.35-4.35"/>
</svg>
</span>
<input
type="text"
id="blog-search"
placeholder="Buscá por tema: branding, web, redes..."
autocomplete="off"
aria-label="Buscar artículos"
>
<button class="search-clear" id="search-clear" type="button" aria-label="Limpiar búsqueda">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 6 6 18M6 6l12 12"/>
</svg>
</button>
</div>
<div class="filter-group-compact">
<button class="filter-btn active" data-filter="all">Todos</button>
<button class="filter-btn" data-filter="destacados">Destacados</button>
</div>
</div>
<!-- Fila inferior: Categorías + Ordenamiento -->
<div class="filters-bottom-row">
<div class="filter-categories-compact">
<button class="category-btn active" data-category="all">Todas</button>
<button class="category-btn" data-category="branding">Branding</button>
<button class="category-btn" data-category="web">Web</button>
<button class="category-btn" data-category="redes">Redes</button>
<button class="category-btn" data-category="automatizaciones">Automatizaciones</button>
<button class="category-btn" data-category="paid">Paid Media</button>
<button class="category-btn" data-category="estrategia">Estrategia</button>
<button class="category-btn" data-category="casos">Casos</button>
</div>
<div class="filter-secondary-compact">
<select id="sort-select" class="sort-select-compact">
<option value="recent">Más recientes</option>
<option value="popular">Más populares</option>
<option value="alphabetical">A-Z</option>
</select>
</div>
</div>
<!-- Info de resultados -->
<div class="results-info-compact">
<span id="results-count">Mostrando <strong>todos</strong> los artículos</span>
<button class="reset-filters-compact" id="reset-filters">Limpiar filtros</button>
</div>
</div>
</section>
<!-- SECCIÓN 3: ARTÍCULOS DESTACADOS -->
<section class="featured-posts" id="featured-posts">
<div class="section-header">
<h2 class="section-title">Más leídos</h2>
<p class="section-subtitle">Los artículos que más están ayudando a marcas como la tuya.</p>
</div>
<div class="featured-grid">
<!-- Artículo destacado 1 -->
<article class="featured-card" data-categories="branding estrategia" data-tags="identidad, marca, posicionamiento" data-featured="true">
<div class="featured-image">
<img src="../assets/blog/branding-completo.jpg" alt="Guía completa de branding">
<span class="featured-badge">Más leído</span>
</div>
<div class="featured-content">
<div class="featured-meta">
<span class="category-tag branding">Branding</span>
<time datetime="2024-01-15">15 Ene 2024</time>
</div>
<h3 class="featured-title">Guía completa: Cómo construir una identidad de marca que perdure</h3>
<p class="featured-excerpt">
Desde el diagnóstico inicial hasta el manual de identidad. Todo lo que necesitás saber para crear una marca con esencia y solidez estratégica.
</p> </p>
<div class="featured-footer">
<span class="reading-time">⏱ 12 min de lectura</span>
<a href="#" class="btn-read">Leer artículo →</a>
</div>
</div> </div>
</article> </section>
<!-- Artículo destacado 2 --> <!-- FILTROS -->
<article class="featured-card" data-categories="web" data-tags="landing page, conversión, diseño" data-featured="true"> <section class="blog-filters-section" id="blog-filters-header">
<div class="featured-image"> <div class="filters-container">
<img src="../assets/blog/landing-page.jpg" alt="Landing pages"> <div class="filters-top-row">
</div> <div class="search-wrapper-compact">
<div class="featured-content"> <span class="search-icon">
<div class="featured-meta"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<span class="category-tag web">Web</span> <circle cx="11" cy="11" r="8"/>
<time datetime="2024-01-10">10 Ene 2024</time> <path d="m21 21-4.35-4.35"/>
</div> </svg>
<h3 class="featured-title">Landing pages que convierten: 7 principios probados</h3> </span>
<p class="featured-excerpt"> <input type="text" id="blog-search" placeholder="Buscá por tema: branding, web, redes..." autocomplete="off" aria-label="Buscar artículos">
Estructura, copy y diseño working together para transformar visitantes en clientes reales. <button class="search-clear" id="search-clear" type="button" aria-label="Limpiar búsqueda">
</p> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<div class="featured-footer"> <path d="M18 6 6 18M6 6l12 12"/>
<span class="reading-time">⏱ 8 min de lectura</span> </svg>
<a href="#" class="btn-read">Leer artículo →</a> </button>
</div> </div>
</div>
</article>
<!-- Artículo destacado 3 --> <div class="filter-group-compact">
<article class="featured-card" data-categories="automatizaciones" data-tags="instagram, chatbots, leads" data-featured="true"> <button class="filter-btn active" data-filter="all">Todos</button>
<div class="featured-image"> <button class="filter-btn" data-filter="destacados">Destacados</button>
<img src="../assets/blog/automatizacion.jpg" alt="Automatizaciones"> </div>
</div>
<div class="featured-content">
<div class="featured-meta">
<span class="category-tag automatizaciones">Automatizaciones</span>
<time datetime="2024-01-05">5 Ene 2024</time>
</div> </div>
<h3 class="featured-title">Automatizaciones en Instagram: de 0 a 100 leads/mes</h3>
<p class="featured-excerpt">
Flujos inteligentes que trabajan 24/7 mientras vos te enfocás en lo que mejor sabés hacer.
</p>
<div class="featured-footer">
<span class="reading-time"> 9 min de lectura</span>
<a href="#" class="btn-read">Leer artículo →</a>
</div>
</div>
</article>
</div>
</section>
<!-- SECCIÓN 4: GRID DE ARTÍCULOS --> <div class="filters-bottom-row">
<div class="filter-categories-compact">
<button class="category-btn active" data-category="all">Todas</button>
<button class="category-btn" data-category="branding">Branding</button>
<button class="category-btn" data-category="web">Web</button>
<button class="category-btn" data-category="redes">Redes</button>
<button class="category-btn" data-category="automatizaciones">Automatizaciones</button>
<button class="category-btn" data-category="paid">Paid Media</button>
<button class="category-btn" data-category="estrategia">Estrategia</button>
<button class="category-btn" data-category="casos">Casos</button>
</div>
<div class="filter-secondary-compact">
<select id="sort-select" class="sort-select-compact">
<option value="recent">Más recientes</option>
<option value="popular">Más populares</option>
</select>
</div>
</div>
<div class="results-info-compact">
<span id="results-count">Mostrando <strong>todos</strong> los artículos</span>
<button class="reset-filters-compact" id="reset-filters">Limpiar filtros</button>
</div>
</div>
</section>
<!-- DESTACADOS (Se llena dinámicamente con JS) -->
<section class="featured-posts" id="featured-posts" style="display: none;">
<div class="section-header">
<h2 class="section-title">Más leídos</h2>
<p class="section-subtitle">Los artículos que más están ayudando a marcas como la tuya.</p>
</div>
<div class="featured-grid" id="featured-grid">
<!-- JS inyecta las tarjetas aquí -->
</div>
</section>
<!-- GRID DE ARTÍCULOS (Se llena dinámicamente con JS) -->
<section class="blog-grid-section"> <section class="blog-grid-section">
<div class="section-header">
<h2 class="section-title" id="grid-section-title">Ideas frescas</h2>
<p class="section-subtitle">Encontrá nuestros últimos articulos de vanguardia.</p>
</div>
<div class="blog-grid" id="blog-grid"> <div class="blog-grid" id="blog-grid">
<!-- JS inyecta las tarjetas aquí -->
<!-- ARTÍCULO 1 -->
<article class="blog-card" data-categories="redes estrategia" data-tags="contenido, planificación, calendario" data-featured="false">
<div class="card-image">
<img src="../assets/blog/contenido-redes.jpg" alt="Contenido para redes">
<span class="card-category redes">Redes</span>
</div>
<div class="card-content">
<div class="card-meta">
<time datetime="2024-01-20">20 Ene 2024</time>
<span class="reading-time-mini">8 min</span>
</div>
<h3 class="card-title">Planificación de contenido: el método de los 3 pilares</h3>
<p class="card-excerpt">
Cómo estructurar tu calendario editorial para mantener coherencia sin volverte loco.
</p>
<div class="card-tags">
<span class="tag">Contenido</span>
<span class="tag">Planificación</span>
</div>
<a href="#" class="card-link">Leer artículo</a>
</div>
</article>
<!-- ARTÍCULO 2 -->
<article class="blog-card" data-categories="branding" data-tags="logo, identidad, diseño" data-featured="false">
<div class="card-image">
<img src="../assets/blog/logo-design.jpg" alt="Diseño de logo">
<span class="card-category branding">Branding</span>
</div>
<div class="card-content">
<div class="card-meta">
<time datetime="2024-01-18">18 Ene 2024</time>
<span class="reading-time-mini">6 min</span>
</div>
<h3 class="card-title">Tu logo no es tu marca (y por qué importa)</h3>
<p class="card-excerpt">
La diferencia entre un símbolo bonito y una identidad estratégica que realmente funciona.
</p>
<div class="card-tags">
<span class="tag">Logo</span>
<span class="tag">Identidad</span>
</div>
<a href="#" class="card-link">Leer artículo</a>
</div>
</article>
<!-- ARTÍCULO 3 -->
<article class="blog-card" data-categories="web paid" data-tags="seo, google ads, tráfico" data-featured="false">
<div class="card-image">
<img src="../assets/blog/seo-web.jpg" alt="SEO">
<span class="card-category web">Web</span>
</div>
<div class="card-content">
<div class="card-meta">
<time datetime="2024-01-12">12 Ene 2024</time>
<span class="reading-time-mini">10 min</span>
</div>
<h3 class="card-title">SEO vs Paid Media: cuándo usar cada uno</h3>
<p class="card-excerpt">
No son enemigos, son complementarios. Te explicamos cómo combinarlos estratégicamente.
</p>
<div class="card-tags">
<span class="tag">SEO</span>
<span class="tag">Google Ads</span>
</div>
<a href="#" class="card-link">Leer artículo</a>
</div>
</article>
<!-- ARTÍCULO 4 -->
<article class="blog-card" data-categories="casos estrategia" data-tags="caso de éxito, tecnología, ecosistema" data-featured="false">
<div class="card-image">
<img src="../assets/blog/caso-tech.jpg" alt="Caso de éxito">
<span class="card-category casos">Casos</span>
</div>
<div class="card-content">
<div class="card-meta">
<time datetime="2024-01-08">8 Ene 2024</time>
<span class="reading-time-mini">7 min</span>
</div>
<h3 class="card-title">Caso de éxito: Ecosistema digital para startup tech</h3>
<p class="card-excerpt">
Cómo construimos una presencia integral desde cero: branding, web y redes en 90 días.
</p>
<div class="card-tags">
<span class="tag">Caso de Éxito</span>
<span class="tag">Tecnología</span>
</div>
<a href="#" class="card-link">Leer artículo</a>
</div>
</article>
<!-- ARTÍCULO 5 -->
<article class="blog-card" data-categories="automatizaciones redes" data-tags="manychat, instagram, ventas" data-featured="false">
<div class="card-image">
<img src="../assets/blog/chatbot.jpg" alt="Chatbot">
<span class="card-category automatizaciones">Automatizaciones</span>
</div>
<div class="card-content">
<div class="card-meta">
<time datetime="2024-01-03">3 Ene 2024</time>
<span class="reading-time-mini">9 min</span>
</div>
<h3 class="card-title">Chatbots que no parecen robots: la clave está en el copy</h3>
<p class="card-excerpt">
Cómo escribir flujos de conversación que suenen humanos y realmente vendan.
</p>
<div class="card-tags">
<span class="tag">ManyChat</span>
<span class="tag">Ventas</span>
</div>
<a href="#" class="card-link">Leer artículo</a>
</div>
</article>
<!-- ARTÍCULO 6 -->
<article class="blog-card" data-categories="estrategia branding" data-tags="posicionamiento, competencia, diferenciación" data-featured="false">
<div class="card-image">
<img src="../assets/blog/posicionamiento.jpg" alt="Posicionamiento">
<span class="card-category estrategia">Estrategia</span>
</div>
<div class="card-content">
<div class="card-meta">
<time datetime="2023-12-28">28 Dic 2023</time>
<span class="reading-time-mini">11 min</span>
</div>
<h3 class="card-title">Posicionamiento: cómo destacar en un mercado saturado</h3>
<p class="card-excerpt">
Estrategias prácticas para encontrar tu espacio único y volverte inolvidable.
</p>
<div class="card-tags">
<span class="tag">Posicionamiento</span>
<span class="tag">Diferenciación</span>
</div>
<a href="#" class="card-link">Leer artículo</a>
</div>
</article>
</div> </div>
<!-- ESTADO VACÍO (cuando no hay resultados) -->
<div class="empty-state" id="empty-state" style="display: none;"> <div class="empty-state" id="empty-state" style="display: none;">
<div class="empty-icon">🔍</div> <div class="empty-icon">🔍</div>
<h3>No encontramos artículos con esos filtros</h3> <h3>No encontramos artículos con esos filtros</h3>
@@ -310,34 +107,16 @@
<button class="btn btn-primary" id="empty-reset">Ver todos los artículos</button> <button class="btn btn-primary" id="empty-reset">Ver todos los artículos</button>
</div> </div>
<!-- PAGINACIÓN / CARGAR MÁS -->
<div class="load-more-container"> <div class="load-more-container">
<button class="btn btn-secondary" id="load-more">Cargar más artículos</button> <button class="btn btn-secondary" id="load-more">Cargar más artículos</button>
</div> </div>
</section> </section>
<!-- SECCIÓN 5: NEWSLETTER -->
<section class="newsletter-section">
<div class="newsletter-card">
<div class="newsletter-content">
<h2 class="newsletter-title">Ideas frescas en tu inbox</h2>
<p class="newsletter-subtitle">
Suscribite y recibí nuestras mejores estrategias de branding, web y marketing digital.
Sin spam, solo contenido de valor.
</p>
</div>
<form class="newsletter-form" id="newsletter-form">
<button type="submit" class="btn btn-primary">Tu email profesional</button>
<button type="submit" class="btn btn-primary">Suscribirme</button>
</form>
</div>
</section>
</main> </main>
<div id="footer-container"></div> <div id="footer-container"></div>
<script src="scripts/main.js"></script> <script src="scripts/main.js"></script>
<script src="scripts/blog.js"></script> <script src="scripts/blog.js"></script>
<link rel="stylesheet" href="style/blog.css"> <script src="scripts/fluid.js"></script>
</body> </body>
</html> </html>
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* Configuración general del formulario de contacto.
* NO subir este archivo a repos públicos.
*/
// ─── SMTP (DonWeb / Ferozo) ────────────────────────────────────────────
// Reemplazá los valores por los reales de tu casilla contacto@amoryroma.com.ar
define('SMTP_HOST', 'dtc036.ferozo.com'); // o el que te dio DonWeb
define('SMTP_PORT', 465); // 587 (TLS) o 465 (SSL)
define('SMTP_SECURE', 'ssl'); // 'tls' o 'ssl'
define('SMTP_USER', 'contacto@amoryroma.com.ar');
define('SMTP_PASS', '1J@oUz7f');
define('SMTP_FROM', 'contacto@amoryroma.com.ar');
define('SMTP_FROM_NAME', 'Amor & Roma');
// ─── Destinatarios ─────────────────────────────────────────────────────
define('MAIL_TO', 'contacto@amoryroma.com.ar');
define('MAIL_TO_NAME', 'Amor & Roma');
// ─── Redirección post-éxito ────────────────────────────────────────────
define('REDIRECT_URL', 'index.html');
define('REDIRECT_MS', 4000); // milisegundos antes de redirigir
// ─── Modo debug ────────────────────────────────────────────────────────
// true → muestra errores SMTP en la respuesta JSON (solo para probar)
// false → producción (recomendado)
define('DEBUG_MODE', false);
+32 -6
View File
@@ -4,10 +4,17 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Contactá a Amor y Roma. Contanos sobre tu proyecto y coordinemos un diagnóstico estratégico sin compromiso."> <meta name="description" content="Contactá a Amor y Roma. Contanos sobre tu proyecto y coordinemos un diagnóstico estratégico sin compromiso.">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css">
<title>Contacto | Amor y Roma — Agencia Boutique</title> <title>Contacto | Amor y Roma — Agencia Boutique</title>
<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="style/main.css"> <link rel="stylesheet" href="style/main.css">
<link rel="stylesheet" href="style/contacto.css">
<link rel="stylesheet" href="style/modal.css">
</head> </head>
<body> <body>
<div id="fluid-bg" aria-hidden="true">
<canvas id="fluidCanvas"></canvas>
</div>
<div id="menu-container"></div> <div id="menu-container"></div>
<main> <main>
@@ -45,14 +52,14 @@
<span class="canal-icono canal-email"></span> <span class="canal-icono canal-email"></span>
<div class="canal-contenido"> <div class="canal-contenido">
<strong>Email</strong> <strong>Email</strong>
<a href="mailto:contacto@amoryroma.com">hola@amoryroma.com</a> <a href="mailto:contacto@amoryroma.com">contacto@amoryroma.com</a>
</div> </div>
</li> </li>
<li class="canal-item"> <li class="canal-item">
<span class="canal-icono canal-whatsapp"></span> <span class="canal-icono canal-whatsapp"></span>
<div class="canal-contenido"> <div class="canal-contenido">
<strong>WhatsApp</strong> <strong>WhatsApp</strong>
<a href="https://wa.me/543886118304" target="_blank" rel="noopener">+54 9 11 0000-0000</a> <a href="https://wa.me/543886118304" target="_blank" rel="noopener">+54 9 388 6118304</a>
</div> </div>
</li> </li>
<li class="canal-item"> <li class="canal-item">
@@ -69,6 +76,13 @@
<a href="https://www.linkedin.com/company/amoryroma-estudiocreativo/" target="_blank" rel="noopener">Amor & Roma - Estudio creativo</a> <a href="https://www.linkedin.com/company/amoryroma-estudiocreativo/" target="_blank" rel="noopener">Amor & Roma - Estudio creativo</a>
</div> </div>
</li> </li>
<li class="canal-item">
<span class="canal-icono canal-facebook"></span>
<div class="canal-contenido">
<strong>Facebook</strong>
<a href="https://web.facebook.com/amoryroma.creativo/" target="_blank" rel="noopener">Amor & Roma - Estudio creativo</a>
</div>
</li>
</ul> </ul>
<div class="contacto-horario"> <div class="contacto-horario">
@@ -174,6 +188,11 @@
Acepto que mis datos sean utilizados para contactarme respecto a mi consulta. <span class="form-required">*</span> Acepto que mis datos sean utilizados para contactarme respecto a mi consulta. <span class="form-required">*</span>
</label> </label>
</div> </div>
<!-- Honeypot anti-spam: invisible para humanos -->
<div class="form-honeypot" aria-hidden="true">
<label for="website">No completar este campo</label>
<input type="text" id="website" name="website" tabindex="-1" autocomplete="off">
</div>
<button type="submit" class="btn btn-primary btn-form-enviar"> <button type="submit" class="btn btn-primary btn-form-enviar">
Enviar consulta Enviar consulta
@@ -238,19 +257,26 @@
<h2 class="cta-title">¿Preferís una conversación directa?</h2> <h2 class="cta-title">¿Preferís una conversación directa?</h2>
<p class="cta-subtitle">Agendá una llamada de 15 minutos sin compromiso. Ideal si tenés dudas o querés validar si somos el equipo adecuado para tu proyecto.</p> <p class="cta-subtitle">Agendá una llamada de 15 minutos sin compromiso. Ideal si tenés dudas o querés validar si somos el equipo adecuado para tu proyecto.</p>
<div class="cta-buttons"> <div class="cta-buttons">
<a href="https://calendly.com/amoryroma" target="_blank" rel="noopener" class="btn btn-cta-primary">Agendar llamada</a> <a href="https://wa.me/543886118304" target="_blank" rel="noopener" class="btn btn-cta-primary">Agendar llamada</a>
<a href="https://wa.me/543886118304" target="_blank" rel="noopener" class="btn btn-cta-secondary">Escribir por WhatsApp</a> <a href="https://wa.me/543886118304" target="_blank" rel="noopener" class="btn btn-cta-secondary">Escribir por WhatsApp</a>
</div> </div>
</div> </div>
</section> </section>
<!-- MODAL DE ÉXITO -->
<div id="modal-contacto" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="modal-titulo">
<div class="modal-card">
<div class="modal-icono" aria-hidden="true">✓</div>
<h2 id="modal-titulo" class="modal-titulo">¡Mensaje enviado!</h2>
<p class="modal-mensaje">Gracias por escribirnos. Te contactaremos en menos de 48 horas hábiles.</p>
<button type="button" class="modal-cerrar">Volver al inicio</button>
</div>
</div>
</main> </main>
<div id="footer-container"></div> <div id="footer-container"></div>
<script src="scripts/main.js"></script> <script src="scripts/main.js"></script>
<script src="scripts/efecto.js"></script>
<script src="scripts/contacto.js"></script> <script src="scripts/contacto.js"></script>
<script src="scripts/fluid.js"></script>
<link rel="stylesheet" href="style/contacto.css">
</body> </body>
</html> </html>
+171
View File
@@ -0,0 +1,171 @@
<?php
/**
* Endpoint AJAX del formulario de contacto.
* Recibe POST y responde JSON.
*/
// ── Silenciar salida directa, solo JSON ────────────────────────────────
if (ob_get_length() > 0) { ob_clean(); }
header('Content-Type: application/json; charset=UTF-8');
// ── Errores a log, no a pantalla ───────────────────────────────────────
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
ini_set('log_errors', 1);
error_reporting(E_ALL);
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/validar.php';
// ── Respuesta helper ───────────────────────────────────────────────────
function responder($status, $message, $code = 200, $extra = []) {
http_response_code($code);
echo json_encode(array_merge([
'status' => $status,
'message' => $message,
], $extra));
exit;
}
// ── Método ─────────────────────────────────────────────────────────────
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
responder('error', 'Método no permitido.', 405);
}
// ── Honeypot ───────────────────────────────────────────────────────────
if (!empty($_POST['website'])) {
// Fingimos éxito para no darle pistas al bot
responder('success', '¡Gracias! Te contactaremos a la brevedad.');
}
// ── Validación ─────────────────────────────────────────────────────────
$resultado = validar_contacto($_POST);
if (!$resultado['ok']) {
responder('error', implode(' ', $resultado['errores']), 400);
}
$d = $resultado['datos'];
// ── PHPMailer ──────────────────────────────────────────────────────────
require_once __DIR__ . '/lib/PHPMailer.php';
require_once __DIR__ . '/lib/SMTP.php';
require_once __DIR__ . '/lib/Exception.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\SMTP;
// ── Mapas para mostrar etiquetas legibles ─────────────────────────────
$etiquetas = [
'servicio' => [
'branding' => 'Branding & Identidad',
'web' => 'Desarrollo Web a Medida',
'rrss' => 'Gestión de Redes Sociales',
'automatizaciones'=> 'Automatizaciones & Flujos',
'paid' => 'Paid Media & Campañas',
'ecosistema' => 'Ecosistema Digital Completo',
'otro' => 'Otro / Aún no definido',
],
'etapa' => [
'idea' => 'Solo tengo la idea',
'inicio' => 'Estoy empezando',
'marcha' => 'En marcha, necesito orden',
'redisenio' => 'Rediseñar / reposicionar',
'escalar' => 'Escalar lo existente',
],
'presupuesto' => [
'esencial' => 'Hasta $250.000 (Esencial)',
'impulso' => '$250.000 – $500.000 (Impulso)',
'premium' => '$500.000 – $800.000 (Premium)',
'elite' => 'Más de $800.000 (Élite)',
'proyecto' => 'Proyecto cerrado (one-off)',
'no-se' => 'A evaluar por el equipo',
],
'origen' => [
'instagram' => 'Instagram',
'linkedin' => 'LinkedIn',
'referido' => 'Referido',
'google' => 'Google',
'otro' => 'Otro',
],
];
$mail = new PHPMailer(true);
try {
// SMTP
$mail->isSMTP();
$mail->Host = SMTP_HOST;
$mail->SMTPAuth = true;
$mail->Username = SMTP_USER;
$mail->Password = SMTP_PASS;
$mail->SMTPSecure = SMTP_SECURE;
$mail->Port = SMTP_PORT;
$mail->CharSet = 'UTF-8';
$mail->SMTPDebug = 0;
// De / Para
$mail->setFrom(SMTP_FROM, SMTP_FROM_NAME);
$mail->addAddress(MAIL_TO, MAIL_TO_NAME);
$mail->addReplyTo($d['email'], $d['nombre']);
// Contenido
$mail->Subject = 'Nuevo contacto: ' . $d['nombre'] . ($d['empresa'] ? ' (' . $d['empresa'] . ')' : '');
$mail->isHTML(true);
$servicio_txt = $etiquetas['servicio'][$d['servicio']] ?? '—';
$etapa_txt = $etiquetas['etapa'][$d['etapa']] ?? '—';
$presupuesto_txt = $etiquetas['presupuesto'][$d['presupuesto']] ?? '—';
$origen_txt = $etiquetas['origen'][$d['origen']] ?? '—';
$rows = [
'Nombre' => $d['nombre'],
'Empresa / Marca' => $d['empresa'] ?: '—',
'Email' => $d['email'],
'Teléfono' => $d['telefono'] ?: '—',
'Servicio' => $servicio_txt,
'Etapa' => $etapa_txt,
'Presupuesto' => $presupuesto_txt,
'Cómo nos conoció'=> $origen_txt,
];
$html = '<div style="font-family:Arial,sans-serif;font-size:14px;color:#222;max-width:640px">';
$html .= '<h2 style="margin:0 0 16px;color:#111">Nueva consulta desde el sitio</h2>';
$html .= '<table cellpadding="8" cellspacing="0" style="border-collapse:collapse;width:100%">';
foreach ($rows as $label => $valor) {
$html .= '<tr>';
$html .= '<td style="background:#f5f2ec;border:1px solid #e5e0d5;font-weight:bold;width:180px">' . htmlspecialchars($label) . '</td>';
$html .= '<td style="border:1px solid #e5e0d5">' . htmlspecialchars($valor) . '</td>';
$html .= '</tr>';
}
$html .= '</table>';
$html .= '<h3 style="margin:24px 0 8px;color:#111">Mensaje</h3>';
$html .= '<div style="white-space:pre-wrap;background:#fafafa;border:1px solid #eee;padding:12px;border-radius:6px">'
. htmlspecialchars($d['mensaje']) . '</div>';
$html .= '</div>';
$mail->Body = $html;
$mail->AltBody = "Nueva consulta\n\n"
. "Nombre: {$d['nombre']}\n"
. "Empresa: {$d['empresa']}\n"
. "Email: {$d['email']}\n"
. "Teléfono: {$d['telefono']}\n"
. "Servicio: $servicio_txt\n"
. "Etapa: $etapa_txt\n"
. "Presupuesto: $presupuesto_txt\n"
. "Origen: $origen_txt\n\n"
. "Mensaje:\n{$d['mensaje']}\n";
$mail->send();
responder('success', '¡Gracias! Te contactaremos en menos de 48 horas hábiles.', 200, [
'redirect' => REDIRECT_URL,
'delay' => REDIRECT_MS,
]);
} catch (Exception $e) {
error_log('[contacto] Falló el envío: ' . $mail->ErrorInfo);
$msg = DEBUG_MODE
? 'Error SMTP: ' . $mail->ErrorInfo
: 'No pudimos enviar tu mensaje. Probá por WhatsApp o intentá más tarde.';
responder('error', $msg, 500);
}
+4 -4
View File
@@ -3,7 +3,7 @@
<!-- SECCIÓN 1: Contenido Principal (Columnas, enlaces y CTAs) --> <!-- SECCIÓN 1: Contenido Principal (Columnas, enlaces y CTAs) -->
<div class="footer-content"> <div class="footer-content">
<div class="footer-col" id="footer-brand"> <div class="footer-col" id="footer-brand">
<a href="inicio.html" class="logo"> <a href="index.html" class="logo">
<img src="assets/logos/logo_ciruela.png" alt="Logo" class="logo-light"> <img src="assets/logos/logo_ciruela.png" alt="Logo" class="logo-light">
<img src="assets/logos/logo_frio.png" alt="Logo" class="logo-dark"> <img src="assets/logos/logo_frio.png" alt="Logo" class="logo-dark">
</a> </a>
@@ -13,10 +13,10 @@
<div class="footer-col" id="footer-links"> <div class="footer-col" id="footer-links">
<h4>Explorar</h4> <h4>Explorar</h4>
<ul> <ul>
<li><a href="inicio.html">Inicio</a></li> <li><a href="index.html">Inicio</a></li>
<li><a href="servicios.html">Servicios</a></li> <li><a href="servicios.html">Servicios</a></li>
<li><a href="planes.html">Planes</a></li> <li><a href="planes.html">Planes</a></li>
<li><a href="inicio.html#portfolio">Portfolio</a></li> <li><a href="index.html#portfolio">Portfolio</a></li>
<li><a href="blog.html">Blog</a></li> <li><a href="blog.html">Blog</a></li>
</ul> </ul>
</div> </div>
@@ -36,7 +36,7 @@
<h4></h4> <h4></h4>
<div class="menu-buttons"> <div class="menu-buttons">
<a href="contacto.html" class="btn-menu btn-menu-primary">¿Tenés un proyecto en mente?</a> <a href="contacto.html" class="btn-menu btn-menu-primary">¿Tenés un proyecto en mente?</a>
<a href="inicio.html#diagnostico" class="btn-menu btn-menu-secondary">Empezar diagnóstico</a> <a href="index.html#diagnostico" class="btn-menu btn-menu-secondary">Empezar diagnóstico</a>
<div class="menu-social"> <div class="menu-social">
<a href="https://www.instagram.com/amoryroma.creativo" target="_blank" rel="noopener"><i class="fa-brands fa-instagram"></i> Instagram</a> <a href="https://www.instagram.com/amoryroma.creativo" target="_blank" rel="noopener"><i class="fa-brands fa-instagram"></i> Instagram</a>
<a href="https://web.facebook.com/amoryroma.creativo/" target="_blank" rel="noopener"><i class="fa-brands fa-facebook"></i> Facebook</a> <a href="https://web.facebook.com/amoryroma.creativo/" target="_blank" rel="noopener"><i class="fa-brands fa-facebook"></i> Facebook</a>
+17 -11
View File
@@ -1,6 +1,6 @@
<header class="site-header"> <header class="site-header">
<nav class="main-nav"> <nav class="main-nav">
<a href="inicio.html" class="logo"> <a href="index.html" class="logo">
<img src="assets/logos/logo_ciruela.png" alt="Logo" class="logo-light"> <img src="assets/logos/logo_ciruela.png" alt="Logo" class="logo-light">
<img src="assets/logos/logo_frio.png" alt="Logo" class="logo-dark"> <img src="assets/logos/logo_frio.png" alt="Logo" class="logo-dark">
</a> </a>
@@ -35,12 +35,12 @@
<div class="menu-column"> <div class="menu-column">
<h3>Amor&Roma</h3> <h3>Amor&Roma</h3>
<ul> <ul>
<li><a href="inicio.html">Comencemos</a></li> <li><a href="index.html">Comencemos</a></li>
<li><a href="inicio.html#servicios">Qué hacemos?</a></li> <li><a href="index.html#servicios">Qué hacemos?</a></li>
<li><a href="inicio.html#diagnostico">Diagnóstico</a></li> <li><a href="index.html#diagnostico">Diagnóstico</a></li>
<li><a href="inicio.html#metodologia">Método A&R</a></li> <li><a href="index.html#metodologia">Método A&R</a></li>
<li><a href="inicio.html#planes-destacados">Destacados</a></li> <li><a href="index.html#planes-destacados">Destacados</a></li>
<li><a href="inicio.html#portfolio">Desafios</a></li> <li><a href="index.html#portfolio">Desafios</a></li>
</ul> </ul>
</div> </div>
@@ -48,7 +48,7 @@
<div class="menu-column"> <div class="menu-column">
<h3>Navegación</h3> <h3>Navegación</h3>
<ul> <ul>
<li><a href="inicio.html">Inicio</a></li> <li><a href="index.html">Inicio</a></li>
<li><a href="servicios.html">Servicios</a></li> <li><a href="servicios.html">Servicios</a></li>
<li><a href="planes.html">Planes</a></li> <li><a href="planes.html">Planes</a></li>
<!--<li><a href="portfolio.html">Portfolio</a></li>--> <!--<li><a href="portfolio.html">Portfolio</a></li>-->
@@ -69,7 +69,13 @@
<p class="contact-item"> <p class="contact-item">
<span class="contact-icon icon-phone" aria-hidden="true"></span> <span class="contact-icon icon-phone" aria-hidden="true"></span>
<span><a href="https://wa.me/543886118304">Whatsapp</a></span> <span>
<a href="https://wa.me/543886118304?text=Hola!%20Quiero%20m%C3%A1s%20informaci%C3%B3n."
target="_blank"
rel="noopener noreferrer">
Whatsapp
</a>
</span>
</p> </p>
<div class="menu-social"> <div class="menu-social">
@@ -81,12 +87,12 @@
<div class="menu-buttons"> <div class="menu-buttons">
<a href="contacto.html" class="btn-menu btn-menu-primary">Hablemos</a> <a href="contacto.html" class="btn-menu btn-menu-primary">Hablemos</a>
<a href="trabaja.html" class="btn-menu btn-menu-secondary">Trabaja con nosotros</a> <!--<a href="trabaja.html" class="btn-menu btn-menu-secondary">Trabaja con nosotros</a>-->
</div> </div>
<!-- Logo de marca integrado en el menú --> <!-- Logo de marca integrado en el menú -->
<div class="menu-brand"> <div class="menu-brand">
<a href="inicio.html" class="logo"> <a href="index.html" class="logo">
<img src="assets/logos/logo_ciruela.png" alt="Logo" class="logo-light"> <img src="assets/logos/logo_ciruela.png" alt="Logo" class="logo-light">
<img src="assets/logos/logo_frio.png" alt="Logo" class="logo-dark"> <img src="assets/logos/logo_frio.png" alt="Logo" class="logo-dark">
</a> </a>
+10 -4
View File
@@ -4,22 +4,27 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content=""> <meta name="description" content="">
<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
<title>Inicio - A&R</title> <title>Inicio - A&R</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css">
<link rel="stylesheet" href="style/main.css"> <link rel="stylesheet" href="style/main.css">
</head> </head>
<body> <body>
<div id="fluid-bg" aria-hidden="true">
<canvas id="fluidCanvas"></canvas>
</div>
<div id="menu-container"></div> <div id="menu-container"></div>
<main> <main>
<!-- Sección Hero --> <!-- Sección Hero -->
<section id="hero" class="hero-section"> <section id="hero" class="hero-section">
<div class="hero-content"> <div class="hero-content">
<h1 class="hero-title">Marcas con esencia, construidas con estrategia</h1> <span class="badge-filosofia">Agencia Boutique de Marketing Digital</span>
<p class="hero-subtitle">Diseñamos servicios que se adaptan a tu realidad, para ayudarte a crecer sin perder lo que te hace único</p> <h1 class="hero-title">En un mundo de ruido, solo lo auténtico resuena</h1>
<p class="hero-subtitle">Combinamos el instinto creativo con la ingeniería de procesos para desarrollar tu identidad, sitio web y automatizaciones. <br>Porque lo auténtico, cuando está bien construido, siempre deja eco.</p>
<div class="hero-cta-group"> <div class="hero-cta-group">
<a href="#" class="btn btn-primary hero-cta-primary">Hablemos</a> <a href="contacto.html" class="btn btn-primary hero-cta-primary">Hablemos</a>
<a href="inicio.html#servicios" class="btn btn-secondary hero-cta-secondary">Conocé más</a> <a href="index.html#servicios" class="btn btn-secondary hero-cta-secondary">Conocé más</a>
</div> </div>
</div> </div>
</section> </section>
@@ -531,6 +536,7 @@
<div id="footer-container"></div> <div id="footer-container"></div>
<script src="scripts/main.js"></script> <script src="scripts/main.js"></script>
<script src="scripts/fluid.js"></script>
</body> </body>
</html> </html>
+40
View File
@@ -0,0 +1,40 @@
<?php
/**
* PHPMailer Exception class.
* PHP Version 5.5.
*
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder)
* @copyright 2012 - 2020 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
* @note This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
namespace PHPMailer\PHPMailer;
/**
* PHPMailer exception handler.
*
* @author Marcus Bointon <phpmailer@synchromedia.co.uk>
*/
class Exception extends \Exception
{
/**
* Prettify error message output.
*
* @return string
*/
public function errorMessage()
{
return '<strong>' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "</strong><br />\n";
}
}
+5586
View File
File diff suppressed because it is too large Load Diff
+1617
View File
File diff suppressed because it is too large Load Diff
+15 -11
View File
@@ -5,18 +5,22 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Conoce la filosofía, metodología y principios de Amor y Roma, agencia boutique de marketing digital. Marcas con esencia, construidas con estrategia."> <meta name="description" content="Conoce la filosofía, metodología y principios de Amor y Roma, agencia boutique de marketing digital. Marcas con esencia, construidas con estrategia.">
<title>Nosotros | Amor y Roma — Agencia Boutique</title> <title>Nosotros | Amor y Roma — Agencia Boutique</title>
<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css">
<link rel="stylesheet" href="style/main.css"> <link rel="stylesheet" href="style/main.css">
<link rel="stylesheet" href="style/nosotros.css"> <link rel="stylesheet" href="style/nosotros.css">
</head> </head>
<body> <body>
<div id="fluid-bg" aria-hidden="true">
<canvas id="fluidCanvas"></canvas>
</div>
<div id="menu-container"></div> <div id="menu-container"></div>
<main> <main>
<!-- SECCIÓN 1: HERO / FILOSOFÍA --> <!-- SECCIÓN 1: HERO / FILOSOFÍA -->
<section class="hero-section nosotros-hero"> <section class="hero-section nosotros-hero">
<div class="hero-content"> <div class="hero-content">
<h1 class="hero-title">Amor & Roma</h1> <h1 class="hero-title">Amor&Roma</h1>
<p class="hero-subtitle"> <p class="hero-subtitle">
No somos una agencia tradicional. <br>Combinamos el arte del marketing digital con la ciencia de los datos. No somos una agencia tradicional. <br>Combinamos el arte del marketing digital con la ciencia de los datos.
</p> </p>
@@ -38,13 +42,13 @@
<div class="card-icon icon-amor"></div> <div class="card-icon icon-amor"></div>
<h3>Amor</h3> <h3>Amor</h3>
<span class="card-tag">La Pasión y el Arte</span> <span class="card-tag">La Pasión y el Arte</span>
<p>Representa la chispa creativa, la intuición visual, la dirección de arte meticulosa, la empatía con la visión del cliente y la estética no negociable. Es el alma y la identidad de cada marca.</p> <p>Representa el instinto creativo, la intuición visual, la dirección de arte meticulosa, la empatía con la visión del cliente y la estética no negociable. <br>Es el alma y la identidad de cada marca.</p>
</article> </article>
<article class="dualidad-card card-roma"> <article class="dualidad-card card-roma">
<div class="card-icon icon-roma"></div> <div class="card-icon icon-roma"></div>
<h3>Roma</h3> <h3>Roma</h3>
<span class="card-tag">La Construcción y la Estructura</span> <span class="card-tag">La Construcción y la Estructura</span>
<p>Representa la arquitectura, la solidez, la ingeniería de procesos, la estrategia a largo plazo y la infraestructura digital orientada a resultados. Es el imperio que sostiene esa visión.</p> <p>Representa la arquitectura, el método, la ingeniería de procesos, la estrategia a largo plazo y la infraestructura digital orientada a resultados. <br>Es el imperio que sostiene esa visión.</p>
</article> </article>
</div> </div>
</section> </section>
@@ -77,8 +81,7 @@
</div> </div>
<p class="equipo-colaboradores"> <p class="equipo-colaboradores">
Como todo equipo en crecimiento, sumamos colaboradores según el proyecto — diseñadores, redactores y especialistas que se suman cuando hace falta. Trabajamos en equipo ayudando a las PyMEs a competir y crecer. </p>
</p>
</section> </section>
<!-- SECCIÓN 3: PRINCIPIOS BOUTIQUE --> <!-- SECCIÓN 3: PRINCIPIOS BOUTIQUE -->
@@ -125,7 +128,7 @@
<p class="transparencia-titulo">INVERSIÓN</p> <p class="transparencia-titulo">INVERSIÓN</p>
<p class="transparencia-dato">Desde $250.000/mes</p> <p class="transparencia-dato">Desde $250.000/mes</p>
<p class="transparencia-texto"> <p class="transparencia-texto">
Planes desde <strong>$250.000</strong> (Esencial) hasta <strong>$1.000.000</strong> (Élite). El armado inicial —branding, sitio web— se presupuesta por proyecto. Planes desde <strong>$250.000</strong> (Esencia) hasta <strong>$1.000.000</strong> (Élite). El armado inicial —branding, sitio web— se presupuesta por proyecto.
</p> </p>
</article> </article>
@@ -172,7 +175,7 @@
<!-- ARQUETIPO 1 --> <!-- ARQUETIPO 1 -->
<article class="caso-card"> <article class="caso-card">
<div class="caso-header"> <div class="caso-header">
<span class="caso-badge">Arquetipo 1</span> <span class="caso-badge">Mordi - El mundo en un bocado</span>
<h3 class="caso-titulo">Identidad Integral</h3> <h3 class="caso-titulo">Identidad Integral</h3>
</div> </div>
<p class="caso-descripcion">Desarrollo de marca completo, mascota propia y administración continua de redes.</p> <p class="caso-descripcion">Desarrollo de marca completo, mascota propia y administración continua de redes.</p>
@@ -184,7 +187,7 @@
<!-- ARQUETIPO 2 --> <!-- ARQUETIPO 2 -->
<article class="caso-card"> <article class="caso-card">
<div class="caso-header"> <div class="caso-header">
<span class="caso-badge">Arquetipo 2</span> <span class="caso-badge">ArWorks - higiene y seguridad</span>
<h3 class="caso-titulo">Infraestructura B2B</h3> <h3 class="caso-titulo">Infraestructura B2B</h3>
</div> </div>
<p class="caso-descripcion">Consultora de Seguridad: Sitio web corporativo a medida y arquitectura sobria.</p> <p class="caso-descripcion">Consultora de Seguridad: Sitio web corporativo a medida y arquitectura sobria.</p>
@@ -196,7 +199,7 @@
<!-- ARQUETIPO 3 (INSÍGNIA) --> <!-- ARQUETIPO 3 (INSÍGNIA) -->
<article class="caso-card caso-insignia"> <article class="caso-card caso-insignia">
<div class="caso-header"> <div class="caso-header">
<span class="caso-badge caso-badge-insignia">Arquetipo 3 (Insígnia)</span> <span class="caso-badge caso-badge-insignia">vetta io</span>
<h3 class="caso-titulo">Ecosistema Digital Completo</h3> <h3 class="caso-titulo">Ecosistema Digital Completo</h3>
</div> </div>
<p class="caso-descripcion">Empresa de Tecnología: Branding + Web a medida + RRSS activas.</p> <p class="caso-descripcion">Empresa de Tecnología: Branding + Web a medida + RRSS activas.</p>
@@ -208,7 +211,7 @@
<!-- ARQUETIPO 4 --> <!-- ARQUETIPO 4 -->
<article class="caso-card"> <article class="caso-card">
<div class="caso-header"> <div class="caso-header">
<span class="caso-badge">Arquetipo 4</span> <span class="caso-badge">ide - consultores</span>
<h3 class="caso-titulo">Profesionalización de Servicios</h3> <h3 class="caso-titulo">Profesionalización de Servicios</h3>
</div> </div>
<p class="caso-descripcion">Consultores Independientes: Web a medida + Gestión estratégica de RRSS.</p> <p class="caso-descripcion">Consultores Independientes: Web a medida + Gestión estratégica de RRSS.</p>
@@ -226,7 +229,7 @@
<p class="cta-subtitle">Agendemos una reunión. Sin plantillas, sin compromisos, solo estrategia a medida.</p> <p class="cta-subtitle">Agendemos una reunión. Sin plantillas, sin compromisos, solo estrategia a medida.</p>
<div class="cta-buttons"> <div class="cta-buttons">
<a href="contacto.html" class="btn btn-cta-primary">Hablemos de tu proyecto</a> <a href="contacto.html" class="btn btn-cta-primary">Hablemos de tu proyecto</a>
<a href="inicio.html#portfolio" class="btn btn-cta-secondary">Ver Casos de Éxito</a> <a href="index.html#portfolio" class="btn btn-cta-secondary">Ver Casos de Éxito</a>
</div> </div>
</div> </div>
</section> </section>
@@ -235,5 +238,6 @@
<div id="footer-container"></div> <div id="footer-container"></div>
<script src="scripts/main.js"></script> <script src="scripts/main.js"></script>
<script src="scripts/fluid.js"></script>
</body> </body>
</html> </html>
+7 -2
View File
@@ -6,12 +6,16 @@
<meta name="description" content="Planes de gestión de redes sociales de Amor y Roma. Soluciones escalables desde $250.000/mes."> <meta name="description" content="Planes de gestión de redes sociales de Amor y Roma. Soluciones escalables desde $250.000/mes.">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css">
<title>Planes - Amor y Roma</title> <title>Planes - Amor y Roma</title>
<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="style/main.css"> <link rel="stylesheet" href="style/main.css">
<link rel="stylesheet" href="style/planes.css"> <link rel="stylesheet" href="style/planes.css">
<!-- Font Awesome --> <!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head> </head>
<body> <body>
<div id="fluid-bg" aria-hidden="true">
<canvas id="fluidCanvas"></canvas>
</div>
<div id="menu-container"></div> <div id="menu-container"></div>
<main> <main>
@@ -489,10 +493,10 @@
<section class="planes-cta"> <section class="planes-cta">
<div class="planes-cta-card"> <div class="planes-cta-card">
<h2 class="planes-cta-titulo">¿No sabés cuál plan elegir?</h2> <h2 class="planes-cta-titulo">¿No sabés cuál plan elegir?</h2>
<p class="planes-cta-subtitle">Hablamos contigo y te recomendamos el plan ideal según tus objetivos y presupuesto.</p> <p class="planes-cta-subtitle">Contanos sobre tu negocio y te recomendamos el plan ideal según tus objetivos y presupuesto.</p>
<div class="planes-cta-buttons"> <div class="planes-cta-buttons">
<a href="contacto.html" class="btn btn-cta-primary">Asesoramiento gratuito</a> <a href="contacto.html" class="btn btn-cta-primary">Asesoramiento gratuito</a>
<a href="inicio.html#diagnostico" class="btn btn-cta-secondary">Hacé el diagnóstico</a> <a href="index.html#diagnostico" class="btn btn-cta-secondary">Hacé el diagnóstico</a>
</div> </div>
</div> </div>
</section> </section>
@@ -505,5 +509,6 @@
<script src="scripts/main.js"></script> <script src="scripts/main.js"></script>
<script src="scripts/planes.js"></script> <script src="scripts/planes.js"></script>
<script src="scripts/fluid.js"></script>
</body> </body>
</html> </html>
+230 -163
View File
@@ -1,185 +1,252 @@
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', async () => {
console.log('Blog JS cargado'); const GHOST_API_URL = 'https://blog.amoryroma.com.ar';
const GHOST_API_KEY = '82450ba91ca6b5b9628006eec4';
const searchInput = document.getElementById('blog-search'); // Elementos del DOM
const searchClear = document.getElementById('search-clear'); const featuredSection = document.getElementById('featured-posts');
const featuredGrid = document.getElementById('featured-grid');
const blogGrid = document.getElementById('blog-grid'); const blogGrid = document.getElementById('blog-grid');
const emptyState = document.getElementById('empty-state'); const loadMoreBtn = document.getElementById('load-more');
const resultsCount = document.getElementById('results-count'); const resultsCount = document.getElementById('results-count');
const filtersHeader = document.getElementById('blog-filters-header'); const emptyState = document.getElementById('empty-state');
const gridSectionTitle = document.getElementById('grid-section-title');
if (!searchInput || !blogGrid) { const gridSectionSubtitle = document.getElementById('grid-section-subtitle');
console.error('Faltan elementos del DOM');
return;
}
let currentFilters = { // Estado de los filtros
search: '', let posts = [];
let state = {
category: 'all', category: 'all',
featured: 'all', filter: 'all', // 'all' o 'destacados'
search: '',
sort: 'recent' sort: 'recent'
}; };
// ===== EFECTO SCROLL EN FILTROS ===== // Cargar artículos desde Ghost API
window.addEventListener('scroll', () => { async function loadPosts() {
if (window.scrollY > 100) { try {
filtersHeader.classList.add('is-scrolled'); const response = await fetch(`${GHOST_API_URL}/ghost/api/content/posts/?key=${GHOST_API_KEY}&limit=all&include=tags,authors`);
} else { const data = await response.json();
filtersHeader.classList.remove('is-scrolled');
} posts = data.posts.map(post => ({
}); id: post.id,
slug: post.slug,
titulo: post.title,
extracto: post.excerpt,
categoria: post.primary_tag ? post.primary_tag.name.toLowerCase() : 'general',
tags: post.tags ? post.tags.map(t => t.name.toLowerCase()) : [],
imagen: post.feature_image || 'assets/blog/default.jpg',
fecha: post.published_at,
tiempo_lectura: Math.ceil(post.reading_time) || 5,
destacado: post.featured
}));
// ===== BUSCADOR ===== renderPosts();
searchInput.addEventListener('input', (e) => { setupEventListeners();
const value = e.target.value.trim(); } catch (error) {
currentFilters.search = value; console.error('Error cargando posts de Ghost:', error);
if(emptyState) {
emptyState.style.display = 'block';
emptyState.querySelector('h3').textContent = 'Error al cargar el blog';
emptyState.querySelector('p').textContent = 'Por favor, recarga la página.';
}
}
}
function formatDate(dateString) {
const options = { day: '2-digit', month: 'short', year: 'numeric' };
return new Date(dateString).toLocaleDateString('es-AR', options);
}
function createCard(post, isFeatured = false) {
const catFormatted = post.categoria.charAt(0).toUpperCase() + post.categoria.slice(1);
const cardClass = isFeatured ? 'featured-card' : 'blog-card';
const badge = isFeatured && post.destacado ? '<span class="featured-badge">Destacado</span>' : '';
const metaClass = isFeatured ? 'featured-meta' : 'card-meta';
const titleClass = isFeatured ? 'featured-title' : 'card-title';
const excerptClass = isFeatured ? 'featured-excerpt' : 'card-excerpt';
const linkClass = isFeatured ? 'btn-read' : 'card-link';
const linkText = isFeatured ? 'Leer artículo →' : 'Leer artículo';
return `
<article class="${cardClass}" data-categories="${post.categoria}" data-featured="${post.destacado}">
<div class="${isFeatured ? 'featured-image' : 'card-image'}">
<img src="${post.imagen}" alt="${post.titulo}" loading="lazy">
${!isFeatured ? `<span class="card-category ${post.categoria}">${catFormatted}</span>` : ''}
${badge}
</div>
<div class="${isFeatured ? 'featured-content' : 'card-content'}">
<div class="${metaClass}">
<span class="category-tag ${post.categoria}">${catFormatted}</span>
<time datetime="${post.fecha}">${formatDate(post.fecha)}</time>
</div>
<h3 class="${titleClass}">${post.titulo}</h3>
<p class="${excerptClass}">${post.extracto}</p>
<div class="${isFeatured ? 'featured-footer' : ''}">
${isFeatured ? `<span class="reading-time">⏱ ${post.tiempo_lectura} min</span>` : `<span class="reading-time-mini">${post.tiempo_lectura} min</span>`}
<a href="${GHOST_API_URL}/${post.slug}/" class="${linkClass}" target="_blank">${linkText}</a>
</div>
</div>
</article>
`;
}
function renderPosts() {
// 1. FILTRAR
let filtered = posts.filter(post => {
const matchCategory = state.category === 'all' || post.categoria === state.category;
const matchFilter = state.filter === 'all' || (state.filter === 'destacados' && post.destacado);
const q = state.search.toLowerCase();
const matchSearch = !q ||
post.titulo.toLowerCase().includes(q) ||
post.extracto.toLowerCase().includes(q) ||
post.tags.some(tag => tag.includes(q));
return matchCategory && matchFilter && matchSearch;
});
// 2. ORDENAR
if (state.sort === 'recent') {
filtered.sort((a, b) => new Date(b.fecha) - new Date(a.fecha));
} else if (state.sort === 'popular') {
// Como la API pública de Ghost no expone "vistas", simulamos popularidad poniendo los destacados primero
filtered.sort((a, b) => (b.destacado === a.destacado) ? 0 : b.destacado ? 1 : -1);
}
// 3. DECIDIR VISTA: ¿Es la vista por defecto (sin filtros)?
const isDefaultView = state.category === 'all' && state.filter === 'all' && state.search === '';
if (isDefaultView) {
// VISTA POR DEFECTO: Mostrar Destacados + Últimos 3
if (featuredSection) featuredSection.style.display = 'block';
if (gridSectionTitle) gridSectionTitle.textContent = 'Últimos artículos';
if (gridSectionSubtitle) gridSectionSubtitle.textContent = 'Lo más nuevo y a la vanguardia.';
if (loadMoreBtn) {
loadMoreBtn.style.display = 'inline-block';
loadMoreBtn.textContent = 'Ver todos los artículos en el Blog';
loadMoreBtn.onclick = () => window.location.href = `${GHOST_API_URL}/`;
}
const featuredPosts = filtered.filter(p => p.destacado).slice(0, 3);
const latestPosts = filtered.slice(0, 3);
if (featuredGrid) featuredGrid.innerHTML = featuredPosts.map(p => createCard(p, true)).join('');
if (blogGrid) blogGrid.innerHTML = latestPosts.map(p => createCard(p, false)).join('');
if (resultsCount) resultsCount.innerHTML = `Mostrando los <strong>últimos 3</strong> artículos`;
if (emptyState) emptyState.style.display = 'none';
} else {
// VISTA FILTRADA: Ocultar Destacados, mostrar TODOS los resultados en la grilla principal
if (featuredSection) featuredSection.style.display = 'none';
if (loadMoreBtn) loadMoreBtn.style.display = 'none'; // Ocultamos "cargar más" porque ya mostramos todos los filtrados
// Actualizar títulos según el filtro
if (gridSectionTitle) {
if (state.search) gridSectionTitle.textContent = `Resultados de búsqueda`;
else if (state.category !== 'all') gridSectionTitle.textContent = `Artículos de ${state.category.charAt(0).toUpperCase() + state.category.slice(1)}`;
else if (state.filter === 'destacados') gridSectionTitle.textContent = `Artículos Destacados`;
}
if (gridSectionSubtitle) gridSectionSubtitle.textContent = `Se encontraron ${filtered.length} artículo${filtered.length !== 1 ? 's' : ''}`;
if (blogGrid) {
if (filtered.length === 0) {
blogGrid.innerHTML = '';
if (emptyState) {
emptyState.style.display = 'block';
emptyState.querySelector('h3').textContent = 'No encontramos artículos con esos filtros';
}
} else {
if (emptyState) emptyState.style.display = 'none';
// Mostramos TODOS los artículos que coinciden con el filtro, no solo 3
blogGrid.innerHTML = filtered.map(p => createCard(p, false)).join('');
}
}
if (resultsCount) {
resultsCount.innerHTML = `Mostrando <strong>${filtered.length}</strong> artículo${filtered.length !== 1 ? 's' : ''}`;
}
}
}
function setupEventListeners() {
// 1. Botones de Categoría
document.querySelectorAll('.category-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('.category-btn').forEach(b => b.classList.remove('active'));
e.target.classList.add('active');
state.category = e.target.dataset.category;
renderPosts();
});
});
// 2. Botones de Filtro (Todos / Destacados)
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
e.target.classList.add('active');
state.filter = e.target.dataset.filter;
renderPosts();
});
});
// 3. Buscador
const searchInput = document.getElementById('blog-search');
const searchClear = document.getElementById('search-clear');
if (searchInput) {
searchInput.addEventListener('input', (e) => {
state.search = e.target.value;
if (searchClear) searchClear.classList.toggle('visible', state.search.length > 0);
renderPosts();
});
}
if (searchClear) { if (searchClear) {
searchClear.classList.toggle('visible', value.length > 0); searchClear.addEventListener('click', () => {
if (searchInput) {
searchInput.value = '';
state.search = '';
searchClear.classList.remove('visible');
searchInput.focus();
renderPosts();
}
});
} }
// 4. Ordenamiento
const sortSelect = document.getElementById('sort-select');
if (sortSelect) {
sortSelect.addEventListener('change', (e) => {
state.sort = e.target.value;
renderPosts();
});
}
// 5. Botón Limpiar Filtros
const resetBtn = document.getElementById('reset-filters');
const emptyReset = document.getElementById('empty-reset');
filterArticles(); function resetAll() {
}); state = { category: 'all', filter: 'all', search: '', sort: 'recent' };
if (searchClear) { // Resetear UI
searchClear.addEventListener('click', () => {
searchInput.value = '';
searchClear.classList.remove('visible');
currentFilters.search = '';
filterArticles();
searchInput.focus();
});
}
// ===== FILTROS PRINCIPALES =====
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentFilters.featured = btn.dataset.filter;
filterArticles();
});
});
// ===== CATEGORÍAS =====
document.querySelectorAll('.category-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.category-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.category-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active'); document.querySelector('.category-btn[data-category="all"]').classList.add('active');
currentFilters.category = btn.dataset.category;
filterArticles();
});
});
// ===== ORDENAMIENTO =====
const sortSelect = document.getElementById('sort-select');
if (sortSelect) {
sortSelect.addEventListener('change', (e) => {
currentFilters.sort = e.target.value;
sortArticles();
});
}
// ===== RESET =====
const resetFilters = document.getElementById('reset-filters');
if (resetFilters) {
resetFilters.addEventListener('click', () => {
currentFilters = { search: '', category: 'all', featured: 'all', sort: 'recent' };
searchInput.value = '';
searchClear?.classList.remove('visible');
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
document.querySelector('.filter-btn[data-filter="all"]')?.classList.add('active'); document.querySelector('.filter-btn[data-filter="all"]').classList.add('active');
document.querySelectorAll('.category-btn').forEach(b => b.classList.remove('active'));
document.querySelector('.category-btn[data-category="all"]')?.classList.add('active');
if (searchInput) searchInput.value = '';
if (searchClear) searchClear.classList.remove('visible');
if (sortSelect) sortSelect.value = 'recent'; if (sortSelect) sortSelect.value = 'recent';
filterArticles(); renderPosts();
}); }
if (resetBtn) resetBtn.addEventListener('click', resetAll);
if (emptyReset) emptyReset.addEventListener('click', resetAll);
} }
// ===== FUNCIÓN DE FILTRADO ===== // Iniciar
const filterArticles = () => { await loadPosts();
const articles = document.querySelectorAll('.blog-card, .featured-card');
let visibleCount = 0;
articles.forEach(article => {
const categories = article.dataset.categories || '';
const tags = article.dataset.tags || '';
const isFeatured = article.dataset.featured === 'true';
const title = article.querySelector('.card-title, .featured-title')?.textContent.toLowerCase() || '';
const excerpt = article.querySelector('.card-excerpt, .featured-excerpt')?.textContent.toLowerCase() || '';
const searchTerm = currentFilters.search.toLowerCase();
const matchSearch = searchTerm === '' ||
title.includes(searchTerm) ||
excerpt.includes(searchTerm) ||
categories.toLowerCase().includes(searchTerm) ||
tags.toLowerCase().includes(searchTerm);
const matchCategory = currentFilters.category === 'all' ||
categories.toLowerCase().includes(currentFilters.category.toLowerCase());
let matchFeatured = true;
if (currentFilters.featured === 'destacados') {
matchFeatured = isFeatured;
}
if (matchSearch && matchCategory && matchFeatured) {
article.style.display = '';
visibleCount++;
} else {
article.style.display = 'none';
}
});
sortArticles();
updateResultsCount(visibleCount);
if (emptyState) {
emptyState.style.display = visibleCount === 0 ? 'block' : 'none';
}
};
const sortArticles = () => {
const articles = Array.from(document.querySelectorAll('.blog-card'));
const container = blogGrid;
switch(currentFilters.sort) {
case 'recent':
articles.sort((a, b) => {
const dateA = new Date(a.querySelector('time')?.datetime || 0);
const dateB = new Date(b.querySelector('time')?.datetime || 0);
return dateB - dateA;
});
break;
case 'alphabetical':
articles.sort((a, b) => {
const titleA = a.querySelector('.card-title')?.textContent || '';
const titleB = b.querySelector('.card-title')?.textContent || '';
return titleA.localeCompare(titleB);
});
break;
}
articles.forEach(article => container.appendChild(article));
};
const updateResultsCount = (count) => {
if (!resultsCount) return;
if (count === 0) {
resultsCount.innerHTML = 'No se encontraron artículos';
} else if (currentFilters.category === 'all' && currentFilters.search === '') {
resultsCount.innerHTML = `Mostrando <strong>${count}</strong> artículos`;
} else {
resultsCount.innerHTML = `Mostrando <strong>${count}</strong> artículo${count !== 1 ? 's' : ''}`;
}
};
// Inicializar
filterArticles();
}); });
+105
View File
@@ -0,0 +1,105 @@
/**
* Envío de formulario de contacto con Formspree + modal de éxito.
*/
(function () {
'use strict';
const form = document.getElementById('form-contacto');
const modal = document.getElementById('modal-contacto');
const btnCerrar = modal ? modal.querySelector('.modal-cerrar') : null;
const btnEnviar = form ? form.querySelector('button[type="submit"]') : null;
if (!form || !modal) return;
const FORMSPREE_URL = 'https://formspree.io/f/xkjgjzpn';
let redirectTimer = null;
// ── Helpers ───────────────────────────────────────────────────────
function abrirModal(mensaje) {
const texto = modal.querySelector('.modal-mensaje');
if (texto && mensaje) texto.textContent = mensaje;
modal.classList.add('is-visible');
modal.setAttribute('aria-hidden', 'false');
document.body.style.overflow = 'hidden';
}
function cerrarModal(redirigir = false) {
modal.classList.remove('is-visible');
modal.setAttribute('aria-hidden', 'true');
document.body.style.overflow = '';
if (redirectTimer) clearTimeout(redirectTimer);
if (redirigir) window.location.href = 'index.html';
}
function mostrarError(mensaje) {
modal.classList.add('is-error');
abrirModal(mensaje);
}
// ── Cerrar modal ───────────────────────────────────────────────────
if (btnCerrar) {
btnCerrar.addEventListener('click', () => cerrarModal(true));
}
modal.addEventListener('click', (e) => {
if (e.target === modal) cerrarModal(true);
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.classList.contains('is-visible')) {
cerrarModal(true);
}
});
// ── Submit ─────────────────────────────────────────────────────────
form.addEventListener('submit', async (e) => {
e.preventDefault();
// Validación nativa HTML5
if (!form.checkValidity()) {
form.reportValidity();
return;
}
// Estado de carga
const textoOriginal = btnEnviar ? btnEnviar.innerHTML : '';
if (btnEnviar) {
btnEnviar.disabled = true;
btnEnviar.innerHTML = 'Enviando…';
}
try {
const data = new FormData(form);
const res = await fetch(FORMSPREE_URL, {
method: 'POST',
body: data,
headers: {
'Accept': 'application/json'
}
});
if (res.ok) {
modal.classList.remove('is-error');
abrirModal('¡Mensaje enviado! Te contactaremos en menos de 48 horas hábiles.');
form.reset();
// Redirección automática después de 4 segundos
redirectTimer = setTimeout(() => {
window.location.href = 'index.html';
}, 4000);
} else {
const json = await res.json().catch(() => ({}));
mostrarError(json.error || 'No pudimos enviar tu mensaje. Intentá nuevamente.');
}
} catch (err) {
mostrarError('Hubo un problema de conexión. Probá nuevamente.');
} finally {
if (btnEnviar) {
btnEnviar.disabled = false;
btnEnviar.innerHTML = textoOriginal;
}
}
});
})();
+199 -247
View File
@@ -1,6 +1,6 @@
"use strict"; "use strict";
// ===== CONFIGURACIÓN DEL EFECTO ===== // ===== CONFIGURACIÓN DEL EFECTO (Parámetros calibrados de fluid - copia.js) =====
const config = { const config = {
TEXTURE_DOWNSAMPLE: 1, TEXTURE_DOWNSAMPLE: 1,
DENSITY_DISSIPATION: 0.975, DENSITY_DISSIPATION: 0.975,
@@ -11,22 +11,62 @@ const config = {
SPLAT_RADIUS: 0.003 SPLAT_RADIUS: 0.003
}; };
// ===== COLORES DE MARCA SEGÚN EL TEMA =====
function getBrandColors() {
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
if (isDark) {
return [
[0.850, 0.080, 0.380], // Carmesí / Fucsia vivo de marca
[0.050, 0.450, 0.550], // Teal profundo
[0.650, 0.200, 0.450] // Plum medio
];
} else {
return [
[0.729, 0.071, 0.302], // Bordo de marca (#BA124D)
[0.000, 0.247, 0.322], // Teal de marca (#003F52)
[0.550, 0.150, 0.280] // Plum suave
];
}
}
let brandColors = getBrandColors();
let currentColor = brandColors[0];
let targetColor = brandColors[1];
let transitionProgress = 0;
const transitionSpeed = 0.008;
// Observador para detectar cambios en data-theme
const themeObserver = new MutationObserver(() => {
brandColors = getBrandColors();
currentColor = brandColors[0];
targetColor = brandColors[1];
transitionProgress = 0;
});
themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
function lerpColor(colorA, colorB, t) {
return [
colorA[0] + (colorB[0] - colorA[0]) * t,
colorA[1] + (colorB[1] - colorA[1]) * t,
colorA[2] + (colorB[2] - colorA[2]) * t
];
}
// ===== INICIALIZAR CANVAS ===== // ===== INICIALIZAR CANVAS =====
let canvas = document.getElementById("fluidCanvas"); let canvas = document.getElementById("fluidCanvas");
console.log("Canvas encontrado:", canvas);
if (!canvas) { if (!canvas) {
console.error("ERROR: No se encontró el canvas con id 'fluidCanvas'"); console.error("ERROR: No se encontró el canvas con id 'fluidCanvas'");
} }
canvas.width = window.innerWidth; canvas.width = window.innerWidth;
canvas.height = window.innerHeight; canvas.height = window.innerHeight;
// ===== CONTEXTO WEBGL ===== // ===== CONTEXTO WEBGL TRANSPARENTE =====
let pointers = []; let pointers = [];
let splatStack = [];
function getWebGLContext(canvas) { function getWebGLContext(canvas) {
let params = { let params = {
alpha: false, alpha: true, // Transparente para respetar el fondo CSS
premultipliedAlpha: false, // Clave para evitar bordes oscuros o quemados
depth: false, depth: false,
stencil: false, stencil: false,
antialias: false antialias: false
@@ -34,20 +74,22 @@ function getWebGLContext(canvas) {
let gl = canvas.getContext("webgl2", params); let gl = canvas.getContext("webgl2", params);
let isWebGL2 = !!gl; let isWebGL2 = !!gl;
if (!isWebGL2) if (!isWebGL2)
gl = gl = canvas.getContext("webgl", params) || canvas.getContext("experimental-webgl", params);
canvas.getContext("webgl", params) ||
canvas.getContext("experimental-webgl", params);
let halfFloat = gl.getExtension("OES_texture_half_float"); let halfFloat = gl.getExtension("OES_texture_half_float");
let support_linear_float = gl.getExtension("OES_texture_half_float_linear"); let support_linear_float = gl.getExtension("OES_texture_half_float_linear");
if (isWebGL2) { if (isWebGL2) {
gl.getExtension("EXT_color_buffer_float"); gl.getExtension("EXT_color_buffer_float");
support_linear_float = gl.getExtension("OES_texture_float_linear"); support_linear_float = gl.getExtension("OES_texture_float_linear");
} }
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearColor(0.0, 0.0, 0.0, 0.0);
let internalFormat = isWebGL2 ? gl.RGBA16F : gl.RGBA; let internalFormat = isWebGL2 ? gl.RGBA16F : gl.RGBA;
let internalFormatRG = isWebGL2 ? gl.RG16F : gl.RGBA; let internalFormatRG = isWebGL2 ? gl.RG16F : gl.RGBA;
let formatRG = isWebGL2 ? gl.RG : gl.RGBA; let formatRG = isWebGL2 ? gl.RG : gl.RGBA;
let texType = isWebGL2 ? gl.HALF_FLOAT : halfFloat.HALF_FLOAT_OES; let texType = isWebGL2 ? gl.HALF_FLOAT : halfFloat.HALF_FLOAT_OES;
return { return {
gl: gl, gl: gl,
ext: { ext: {
@@ -74,29 +116,24 @@ function pointerPrototype() {
this.dy = 0; this.dy = 0;
this.down = false; this.down = false;
this.moved = false; this.moved = false;
this.color = [30, 0, 300]; this.color = [0.729, 0.071, 0.302];
} }
pointers.push(new pointerPrototype()); pointers.push(new pointerPrototype());
// ===== SHADERS ===== // ===== SHADERS =====
let GLProgram = (function () { let GLProgram = (function () {
function GLProgram(vertexShader, fragmentShader) { function GLProgram(vertexShader, fragmentShader) {
if (!(this instanceof GLProgram)) if (!(this instanceof GLProgram)) throw new TypeError("Cannot call a class as a function");
throw new TypeError("Cannot call a class as a function");
this.uniforms = {}; this.uniforms = {};
this.program = gl.createProgram(); this.program = gl.createProgram();
gl.attachShader(this.program, vertexShader); gl.attachShader(this.program, vertexShader);
gl.attachShader(this.program, fragmentShader); gl.attachShader(this.program, fragmentShader);
gl.linkProgram(this.program); gl.linkProgram(this.program);
if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) throw gl.getProgramInfoLog(this.program);
throw gl.getProgramInfoLog(this.program);
let uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS); let uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS);
for (let i = 0; i < uniformCount; i++) { for (let i = 0; i < uniformCount; i++) {
let uniformName = gl.getActiveUniform(this.program, i).name; let uniformName = gl.getActiveUniform(this.program, i).name;
this.uniforms[uniformName] = gl.getUniformLocation( this.uniforms[uniformName] = gl.getUniformLocation(this.program, uniformName);
this.program,
uniformName
);
} }
} }
GLProgram.prototype.bind = function bind() { GLProgram.prototype.bind = function bind() {
@@ -109,66 +146,92 @@ function compileShader(type, source) {
let shader = gl.createShader(type); let shader = gl.createShader(type);
gl.shaderSource(shader, source); gl.shaderSource(shader, source);
gl.compileShader(shader); gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) throw gl.getShaderInfoLog(shader);
throw gl.getShaderInfoLog(shader);
return shader; return shader;
} }
// ===== VERTEX SHADER =====
let baseVertexShader = compileShader( let baseVertexShader = compileShader(
gl.VERTEX_SHADER, gl.VERTEX_SHADER,
"precision highp float; precision mediump sampler2D; attribute vec2 aPosition; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform vec2 texelSize; void main () { vUv = aPosition * 0.5 + 0.5; vL = vUv - vec2(texelSize.x, 0.0); vR = vUv + vec2(texelSize.x, 0.0); vT = vUv + vec2(0.0, texelSize.y); vB = vUv - vec2(0.0, texelSize.y); gl_Position = vec4(aPosition, 0.0, 1.0); }" "precision highp float; precision mediump sampler2D; attribute vec2 aPosition; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform vec2 texelSize; void main () { vUv = aPosition * 0.5 + 0.5; vL = vUv - vec2(texelSize.x, 0.0); vR = vUv + vec2(texelSize.x, 0.0); vT = vUv + vec2(0.0, texelSize.y); vB = vUv - vec2(0.0, texelSize.y); gl_Position = vec4(aPosition, 0.0, 1.0); }"
); );
// ===== FRAGMENT SHADERS =====
let clearShader = compileShader( let clearShader = compileShader(
gl.FRAGMENT_SHADER, gl.FRAGMENT_SHADER,
"precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTexture; uniform float value; void main () { gl_FragColor = value * texture2D(uTexture, vUv); }" "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTexture; uniform float value; void main () { gl_FragColor = value * texture2D(uTexture, vUv); }"
); );
// SHADER DE VISUALIZACIÓN: compatible con GLSL ES 1.0 y libre de blanqueo
let displayShader = compileShader( let displayShader = compileShader(
gl.FRAGMENT_SHADER, gl.FRAGMENT_SHADER,
"precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTexture; void main () { gl_FragColor = texture2D(uTexture, vUv); }" `precision highp float;
precision mediump sampler2D;
varying vec2 vUv;
uniform sampler2D uTexture;
uniform float uIsDark;
void main () {
vec4 c = texture2D(uTexture, vUv);
float len = length(c.rgb);
// Sintaxis 100% compatible con WebGL 1 y 2 (4 componentes)
if (len < 0.001) {
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
return;
}
// Normalización cromática: conserva el matiz puro de la marca y previene el blanco
vec3 pureColor = c.rgb / len;
if (uIsDark > 0.5) {
// MODO OSCURO: color rico con presencia viva sin deslumbrar
float alpha = clamp(len * 1.8, 0.0, 0.80);
gl_FragColor = vec4(pureColor, alpha);
} else {
// MODO CLARO: tinte traslúcido tipo acuarela
float alpha = clamp(len * 1.2, 0.0, 0.38);
gl_FragColor = vec4(pureColor, alpha);
}
}`
); );
let splatShader = compileShader( let splatShader = compileShader(
gl.FRAGMENT_SHADER, gl.FRAGMENT_SHADER,
"precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTarget; uniform float aspectRatio; uniform vec3 color; uniform vec2 point; uniform float radius; void main () { vec2 p = vUv - point.xy; p.x *= aspectRatio; vec3 splat = exp(-dot(p, p) / radius) * color; vec3 base = texture2D(uTarget, vUv).xyz; gl_FragColor = vec4(base + splat, 1.0); }" "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTarget; uniform float aspectRatio; uniform vec3 color; uniform vec2 point; uniform float radius; void main () { vec2 p = vUv - point.xy; p.x *= aspectRatio; vec3 splat = exp(-dot(p, p) / radius) * color; vec3 base = texture2D(uTarget, vUv).xyz; gl_FragColor = vec4(base + splat, 1.0); }"
); );
let advectionManualFilteringShader = compileShader( let advectionManualFilteringShader = compileShader(
gl.FRAGMENT_SHADER, gl.FRAGMENT_SHADER,
"precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uVelocity; uniform sampler2D uSource; uniform vec2 texelSize; uniform float dt; uniform float dissipation; vec4 bilerp (in sampler2D sam, in vec2 p) { vec4 st; st.xy = floor(p - 0.5) + 0.5; st.zw = st.xy + 1.0; vec4 uv = st * texelSize.xyxy; vec4 a = texture2D(sam, uv.xy); vec4 b = texture2D(sam, uv.zy); vec4 c = texture2D(sam, uv.xw); vec4 d = texture2D(sam, uv.zw); vec2 f = p - st.xy; return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); } void main () { vec2 coord = gl_FragCoord.xy - dt * texture2D(uVelocity, vUv).xy; gl_FragColor = dissipation * bilerp(uSource, coord); gl_FragColor.a = 1.0; }" "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uVelocity; uniform sampler2D uSource; uniform vec2 texelSize; uniform float dt; uniform float dissipation; vec4 bilerp (in sampler2D sam, in vec2 p) { vec4 st; st.xy = floor(p - 0.5) + 0.5; st.zw = st.xy + 1.0; vec4 uv = st * texelSize.xyxy; vec4 a = texture2D(sam, uv.xy); vec4 b = texture2D(sam, uv.zy); vec4 c = texture2D(sam, uv.xw); vec4 d = texture2D(sam, uv.zw); vec2 f = p - st.xy; return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); } void main () { vec2 coord = gl_FragCoord.xy - dt * texture2D(uVelocity, vUv).xy; gl_FragColor = dissipation * bilerp(uSource, coord); gl_FragColor.a = 1.0; }"
); );
let advectionShader = compileShader( let advectionShader = compileShader(
gl.FRAGMENT_SHADER, gl.FRAGMENT_SHADER,
"precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uVelocity; uniform sampler2D uSource; uniform vec2 texelSize; uniform float dt; uniform float dissipation; void main () { vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize; gl_FragColor = dissipation * texture2D(uSource, coord); }" "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uVelocity; uniform sampler2D uSource; uniform vec2 texelSize; uniform float dt; uniform float dissipation; void main () { vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize; gl_FragColor = dissipation * texture2D(uSource, coord); }"
); );
let divergenceShader = compileShader( let divergenceShader = compileShader(
gl.FRAGMENT_SHADER, gl.FRAGMENT_SHADER,
"precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; vec2 sampleVelocity (in vec2 uv) { vec2 multiplier = vec2(1.0, 1.0); if (uv.x < 0.0) { uv.x = 0.0; multiplier.x = -1.0; } if (uv.x > 1.0) { uv.x = 1.0; multiplier.x = -1.0; } if (uv.y < 0.0) { uv.y = 0.0; multiplier.y = -1.0; } if (uv.y > 1.0) { uv.y = 1.0; multiplier.y = -1.0; } return multiplier * texture2D(uVelocity, uv).xy; } void main () { float L = sampleVelocity(vL).x; float R = sampleVelocity(vR).x; float T = sampleVelocity(vT).y; float B = sampleVelocity(vB).y; float div = 0.5 * (R - L + T - B); gl_FragColor = vec4(div, 0.0, 0.0, 1.0); }" "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; vec2 sampleVelocity (in vec2 uv) { vec2 multiplier = vec2(1.0, 1.0); if (uv.x < 0.0) { uv.x = 0.0; multiplier.x = -1.0; } if (uv.x > 1.0) { uv.x = 1.0; multiplier.x = -1.0; } if (uv.y < 0.0) { uv.y = 0.0; multiplier.y = -1.0; } if (uv.y > 1.0) { uv.y = 1.0; multiplier.y = -1.0; } return multiplier * texture2D(uVelocity, uv).xy; } void main () { float L = sampleVelocity(vL).x; float R = sampleVelocity(vR).x; float T = sampleVelocity(vT).y; float B = sampleVelocity(vB).y; float div = 0.5 * (R - L + T - B); gl_FragColor = vec4(div, 0.0, 0.0, 1.0); }"
); );
let curlShader = compileShader( let curlShader = compileShader(
gl.FRAGMENT_SHADER, gl.FRAGMENT_SHADER,
"precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; void main () { float L = texture2D(uVelocity, vL).y; float R = texture2D(uVelocity, vR).y; float T = texture2D(uVelocity, vT).x; float B = texture2D(uVelocity, vB).x; float vorticity = R - L - T + B; gl_FragColor = vec4(vorticity, 0.0, 0.0, 1.0); }" "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; void main () { float L = texture2D(uVelocity, vL).y; float R = texture2D(uVelocity, vR).y; float T = texture2D(uVelocity, vT).x; float B = texture2D(uVelocity, vB).x; float vorticity = R - L - T + B; gl_FragColor = vec4(vorticity, 0.0, 0.0, 1.0); }"
); );
let vorticityShader = compileShader( let vorticityShader = compileShader(
gl.FRAGMENT_SHADER, gl.FRAGMENT_SHADER,
"precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; uniform sampler2D uCurl; uniform float curl; uniform float dt; void main () { float L = texture2D(uCurl, vL).y; float R = texture2D(uCurl, vR).y; float T = texture2D(uCurl, vT).x; float B = texture2D(uCurl, vB).x; float C = texture2D(uCurl, vUv).x; vec2 force = vec2(abs(T) - abs(B), abs(R) - abs(L)); force *= 1.0 / length(force + 0.00001) * curl * C; vec2 vel = texture2D(uVelocity, vUv).xy; gl_FragColor = vec4(vel + force * dt, 0.0, 1.0); }" "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; uniform sampler2D uCurl; uniform float curl; uniform float dt; void main () { float L = texture2D(uCurl, vL).y; float R = texture2D(uCurl, vR).y; float T = texture2D(uCurl, vT).x; float B = texture2D(uCurl, vB).x; float C = texture2D(uCurl, vUv).x; vec2 force = vec2(abs(T) - abs(B), abs(R) - abs(L)); force *= 1.0 / length(force + 0.00001) * curl * C; vec2 vel = texture2D(uVelocity, vUv).xy; gl_FragColor = vec4(vel + force * dt, 0.0, 1.0); }"
); );
let pressureShader = compileShader( let pressureShader = compileShader(
gl.FRAGMENT_SHADER, gl.FRAGMENT_SHADER,
"precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uPressure; uniform sampler2D uDivergence; vec2 boundary (in vec2 uv) { uv = min(max(uv, 0.0), 1.0); return uv; } void main () { float L = texture2D(uPressure, boundary(vL)).x; float R = texture2D(uPressure, boundary(vR)).x; float T = texture2D(uPressure, boundary(vT)).x; float B = texture2D(uPressure, boundary(vB)).x; float C = texture2D(uPressure, vUv).x; float divergence = texture2D(uDivergence, vUv).x; float pressure = (L + R + B + T - divergence) * 0.25; gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0); }" "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uPressure; uniform sampler2D uDivergence; vec2 boundary (in vec2 uv) { uv = min(max(uv, 0.0), 1.0); return uv; } void main () { float L = texture2D(uPressure, boundary(vL)).x; float R = texture2D(uPressure, boundary(vR)).x; float T = texture2D(uPressure, boundary(vT)).x; float B = texture2D(uPressure, boundary(vB)).x; float C = texture2D(uPressure, vUv).x; float divergence = texture2D(uDivergence, vUv).x; float pressure = (L + R + B + T - divergence) * 0.25; gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0); }"
); );
let gradientSubtractShader = compileShader( let gradientSubtractShader = compileShader(
gl.FRAGMENT_SHADER, gl.FRAGMENT_SHADER,
"precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uPressure; uniform sampler2D uVelocity; vec2 boundary (in vec2 uv) { uv = min(max(uv, 0.0), 1.0); return uv; } void main () { float L = texture2D(uPressure, boundary(vL)).x; float R = texture2D(uPressure, boundary(vR)).x; float T = texture2D(uPressure, boundary(vT)).x; float B = texture2D(uPressure, boundary(vB)).x; vec2 velocity = texture2D(uVelocity, vUv).xy; velocity.xy -= vec2(R - L, T - B); gl_FragColor = vec4(velocity, 0.0, 1.0); }" "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uPressure; uniform sampler2D uVelocity; vec2 boundary (in vec2 uv) { uv = min(max(uv, 0.0), 1.0); return uv; } void main () { float L = texture2D(uPressure, boundary(vL)).x; float R = texture2D(uPressure, boundary(vR)).x; float T = texture2D(uPressure, boundary(vT)).x; float B = texture2D(uPressure, boundary(vB)).x; vec2 velocity = texture2D(uVelocity, vUv).xy; velocity.xy -= vec2(R - L, T - B); gl_FragColor = vec4(velocity, 0.0, 1.0); }"
); );
// ===== FRAMEBUFFERS ===== // ===== FRAMEBUFFERS =====
@@ -182,51 +245,11 @@ function initFramebuffers() {
let iFormatRG = ext.internalFormatRG; let iFormatRG = ext.internalFormatRG;
let formatRG = ext.formatRG; let formatRG = ext.formatRG;
let texType = ext.texType; let texType = ext.texType;
density = createDoubleFBO( density = createDoubleFBO(0, textureWidth, textureHeight, iFormat, gl.RGBA, texType, support_linear_float ? gl.LINEAR : gl.NEAREST);
0, velocity = createDoubleFBO(2, textureWidth, textureHeight, iFormatRG, formatRG, texType, support_linear_float ? gl.LINEAR : gl.NEAREST);
textureWidth, divergence = createFBO(4, textureWidth, textureHeight, iFormatRG, formatRG, texType, gl.NEAREST);
textureHeight, curl = createFBO(5, textureWidth, textureHeight, iFormatRG, formatRG, texType, gl.NEAREST);
iFormat, pressure = createDoubleFBO(6, textureWidth, textureHeight, iFormatRG, formatRG, texType, gl.NEAREST);
gl.RGBA,
texType,
support_linear_float ? gl.LINEAR : gl.NEAREST
);
velocity = createDoubleFBO(
2,
textureWidth,
textureHeight,
iFormatRG,
formatRG,
texType,
support_linear_float ? gl.LINEAR : gl.NEAREST
);
divergence = createFBO(
4,
textureWidth,
textureHeight,
iFormatRG,
formatRG,
texType,
gl.NEAREST
);
curl = createFBO(
5,
textureWidth,
textureHeight,
iFormatRG,
formatRG,
texType,
gl.NEAREST
);
pressure = createDoubleFBO(
6,
textureWidth,
textureHeight,
iFormatRG,
formatRG,
texType,
gl.NEAREST
);
} }
function createFBO(texId, w, h, internalFormat, format, type, param) { function createFBO(texId, w, h, internalFormat, format, type, param) {
@@ -240,13 +263,7 @@ function createFBO(texId, w, h, internalFormat, format, type, param) {
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, w, h, 0, format, type, null); gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, w, h, 0, format, type, null);
let fbo = gl.createFramebuffer(); let fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D( gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
texture,
0
);
gl.viewport(0, 0, w, h); gl.viewport(0, 0, w, h);
gl.clear(gl.COLOR_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT);
return [texture, fbo, texId]; return [texture, fbo, texId];
@@ -256,17 +273,9 @@ function createDoubleFBO(texId, w, h, internalFormat, format, type, param) {
let fbo1 = createFBO(texId, w, h, internalFormat, format, type, param); let fbo1 = createFBO(texId, w, h, internalFormat, format, type, param);
let fbo2 = createFBO(texId + 1, w, h, internalFormat, format, type, param); let fbo2 = createFBO(texId + 1, w, h, internalFormat, format, type, param);
return { return {
get first() { get first() { return fbo1; },
return fbo1; get second() { return fbo2; },
}, swap: function swap() { let temp = fbo1; fbo1 = fbo2; fbo2 = temp; }
get second() {
return fbo2;
},
swap: function swap() {
let temp = fbo1;
fbo1 = fbo2;
fbo2 = temp;
}
}; };
} }
@@ -276,33 +285,19 @@ initFramebuffers();
let clearProgram = new GLProgram(baseVertexShader, clearShader); let clearProgram = new GLProgram(baseVertexShader, clearShader);
let displayProgram = new GLProgram(baseVertexShader, displayShader); let displayProgram = new GLProgram(baseVertexShader, displayShader);
let splatProgram = new GLProgram(baseVertexShader, splatShader); let splatProgram = new GLProgram(baseVertexShader, splatShader);
let advectionProgram = new GLProgram( let advectionProgram = new GLProgram(baseVertexShader, support_linear_float ? advectionShader : advectionManualFilteringShader);
baseVertexShader,
support_linear_float ? advectionShader : advectionManualFilteringShader
);
let divergenceProgram = new GLProgram(baseVertexShader, divergenceShader); let divergenceProgram = new GLProgram(baseVertexShader, divergenceShader);
let curlProgram = new GLProgram(baseVertexShader, curlShader); let curlProgram = new GLProgram(baseVertexShader, curlShader);
let vorticityProgram = new GLProgram(baseVertexShader, vorticityShader); let vorticityProgram = new GLProgram(baseVertexShader, vorticityShader);
let pressureProgram = new GLProgram(baseVertexShader, pressureShader); let pressureProgram = new GLProgram(baseVertexShader, pressureShader);
let gradienSubtractProgram = new GLProgram( let gradienSubtractProgram = new GLProgram(baseVertexShader, gradientSubtractShader);
baseVertexShader,
gradientSubtractShader
);
// ===== BLIT ===== // ===== BLIT =====
let blit = (function () { let blit = (function () {
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData( gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]), gl.STATIC_DRAW);
gl.ARRAY_BUFFER,
new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]),
gl.STATIC_DRAW
);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer()); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
gl.bufferData( gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 0, 2, 3]), gl.STATIC_DRAW);
gl.ELEMENT_ARRAY_BUFFER,
new Uint16Array([0, 1, 2, 0, 2, 3]),
gl.STATIC_DRAW
);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0); gl.enableVertexAttribArray(0);
return function (destination) { return function (destination) {
@@ -316,53 +311,26 @@ function splat(x, y, dx, dy, color) {
splatProgram.bind(); splatProgram.bind();
gl.uniform1i(splatProgram.uniforms.uTarget, velocity.first[2]); gl.uniform1i(splatProgram.uniforms.uTarget, velocity.first[2]);
gl.uniform1f(splatProgram.uniforms.aspectRatio, canvas.width / canvas.height); gl.uniform1f(splatProgram.uniforms.aspectRatio, canvas.width / canvas.height);
gl.uniform2f( gl.uniform2f(splatProgram.uniforms.point, x / canvas.width, 1.0 - y / canvas.height);
splatProgram.uniforms.point,
x / canvas.width,
1.0 - y / canvas.height
);
gl.uniform3f(splatProgram.uniforms.color, dx, -dy, 1.0); gl.uniform3f(splatProgram.uniforms.color, dx, -dy, 1.0);
gl.uniform1f(splatProgram.uniforms.radius, config.SPLAT_RADIUS); gl.uniform1f(splatProgram.uniforms.radius, config.SPLAT_RADIUS);
blit(velocity.second[1]); blit(velocity.second[1]);
velocity.swap(); velocity.swap();
gl.uniform1i(splatProgram.uniforms.uTarget, density.first[2]); gl.uniform1i(splatProgram.uniforms.uTarget, density.first[2]);
gl.uniform3f( // Inyección equilibrada
splatProgram.uniforms.color, gl.uniform3f(splatProgram.uniforms.color, color[0] * 0.28, color[1] * 0.28, color[2] * 0.28);
color[0] * 0.3,
color[1] * 0.3,
color[2] * 0.3
);
blit(density.second[1]); blit(density.second[1]);
density.swap(); density.swap();
} }
// ===== RESIZE ===== // ===== RESIZE =====
function resizeCanvas() { function resizeCanvas() {
(canvas.width !== canvas.clientWidth || if (canvas.width !== canvas.clientWidth || canvas.height !== canvas.clientHeight) {
canvas.height !== canvas.clientHeight) && canvas.width = canvas.clientWidth;
((canvas.width = canvas.clientWidth), canvas.height = canvas.clientHeight;
(canvas.height = canvas.clientHeight), initFramebuffers();
initFramebuffers()); }
}
// ===== COLORES DE LA MARCA CON TRANSICIÓN =====
const brandColors = [
//[0.874, 0.741, 0.725], // Rosa (#DFBDB9)
[0.729, 0.071, 0.302], // Bordo (#BA124D)
[0.000, 0.247, 0.322] // Teal (#003F52)
];
let currentColor = brandColors[0];
let targetColor = brandColors[1];
let transitionProgress = 0;
const transitionSpeed = 0.008;
function lerpColor(colorA, colorB, t) {
return [
colorA[0] + (colorB[0] - colorA[0]) * t,
colorA[1] + (colorB[1] - colorA[1]) * t,
colorA[2] + (colorB[2] - colorA[2]) * t
];
} }
let colorArr = currentColor; let colorArr = currentColor;
@@ -370,27 +338,33 @@ let count = 0;
// ===== DETECTAR ELEMENTOS DE TEXTO ===== // ===== DETECTAR ELEMENTOS DE TEXTO =====
function isMouseOverTextElement(target) { function isMouseOverTextElement(target) {
// Listado de selectores que necesitan "modo apaciguado"
const textSelectors = [ const textSelectors = [
'p', '.hero-content p','h1', 'h2', 'h3', 'h4', 'h5', '.hero-content h1', 'p', '.hero-content p', 'h1', 'h2', 'h3', 'h4', 'h5', '.hero-content h1',
'.hero-content h2', '.eyebrow', 'a.sheet-link', '.sheet-link span', '.hero-content h2', '.eyebrow', 'a', 'button', '.btn', '.servicio-item', '.plan-card'
'.menu-btn', '.sheet-link-desc', '.sheet-link-main'
]; ];
// Verificar si el target o sus ancestros coinciden con algún selector
for (const selector of textSelectors) { for (const selector of textSelectors) {
if (target.closest(selector)) { if (target.closest(selector)) return true;
return true;
}
} }
return false; return false;
} }
// ===== EVENTOS CON COLORES DE MARCA ===== // ===== CAPTURA DE MOUSE SIMÉTRICA Y CENTRADA =====
let isFirstMove = true;
document.addEventListener("mousemove", function (e) { document.addEventListener("mousemove", function (e) {
// Detectar si estamos sobre un elemento de texto const rect = canvas.getBoundingClientRect();
const currentX = e.clientX - rect.left;
const currentY = e.clientY - rect.top;
if (isFirstMove) {
pointers[0].x = currentX;
pointers[0].y = currentY;
isFirstMove = false;
return;
}
const overText = isMouseOverTextElement(e.target); const overText = isMouseOverTextElement(e.target);
count++; count++;
if (count > 1) { if (count > 1) {
transitionProgress += transitionSpeed; transitionProgress += transitionSpeed;
@@ -407,53 +381,55 @@ document.addEventListener("mousemove", function (e) {
pointers[0].down = true; pointers[0].down = true;
pointers[0].color = colorArr; pointers[0].color = colorArr;
pointers[0].moved = pointers[0].down; pointers[0].moved = true;
// Aplicar multiplicador diferente según si estamos sobre texto o no const multiplier = overText ? 8.0 : 10.0;
const multiplier = overText ? 8 : 10.0; // Reducido a 4x sobre texto (vs 10x)
pointers[0].dx = (currentX - pointers[0].x) * multiplier;
pointers[0].dx = (e.clientX - canvas.offsetLeft - pointers[0].x) * multiplier; pointers[0].dy = (currentY - pointers[0].y) * multiplier;
pointers[0].dy = (e.clientY - canvas.offsetTop - pointers[0].y) * multiplier; pointers[0].x = currentX;
pointers[0].x = e.clientX - canvas.offsetLeft; pointers[0].y = currentY;
pointers[0].y = e.clientY - canvas.offsetTop;
}); });
canvas.addEventListener( window.addEventListener("mouseleave", function () {
"touchmove", isFirstMove = true;
function (e) { pointers[0].moved = false;
e.preventDefault(); });
let touches = e.targetTouches;
count++; canvas.addEventListener("touchmove", function (e) {
if (count > 20) { e.preventDefault();
transitionProgress += transitionSpeed; let touches = e.targetTouches;
if (transitionProgress >= 1) { count++;
transitionProgress = 0; if (count > 20) {
let currentIndex = brandColors.indexOf(currentColor); transitionProgress += transitionSpeed;
let nextIndex = (currentIndex + 1) % brandColors.length; if (transitionProgress >= 1) {
currentColor = targetColor; transitionProgress = 0;
targetColor = brandColors[nextIndex]; let currentIndex = brandColors.indexOf(currentColor);
} let nextIndex = (currentIndex + 1) % brandColors.length;
colorArr = lerpColor(currentColor, targetColor, transitionProgress); currentColor = targetColor;
count = 0; targetColor = brandColors[nextIndex];
} }
colorArr = lerpColor(currentColor, targetColor, transitionProgress);
for (let i = 0, len = touches.length; i < len; i++) { count = 0;
if (i >= pointers.length) pointers.push(new pointerPrototype()); }
pointers[i].id = touches[i].identifier;
pointers[i].down = true; const rect = canvas.getBoundingClientRect();
pointers[i].x = touches[i].pageX; for (let i = 0, len = touches.length; i < len; i++) {
pointers[i].y = touches[i].pageY; if (i >= pointers.length) pointers.push(new pointerPrototype());
pointers[i].color = colorArr; let pointer = pointers[i];
let pointer = pointers[i]; let touchX = touches[i].clientX - rect.left;
pointer.moved = pointer.down; let touchY = touches[i].clientY - rect.top;
pointer.dx = (touches[i].pageX - pointer.x) * 10.0;
pointer.dy = (touches[i].pageY - pointer.y) * 10.0; pointer.id = touches[i].identifier;
pointer.x = touches[i].pageX; pointer.down = true;
pointer.y = touches[i].pageY; pointer.color = colorArr;
} pointer.moved = true;
}, pointer.dx = (touchX - pointer.x) * 10.0;
false pointer.dy = (touchY - pointer.y) * 10.0;
); pointer.x = touchX;
pointer.y = touchY;
}
}, false);
// ===== LOOP PRINCIPAL ===== // ===== LOOP PRINCIPAL =====
let lastTime = Date.now(); let lastTime = Date.now();
@@ -465,27 +441,17 @@ function update() {
gl.viewport(0, 0, textureWidth, textureHeight); gl.viewport(0, 0, textureWidth, textureHeight);
advectionProgram.bind(); advectionProgram.bind();
gl.uniform2f( gl.uniform2f(advectionProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);
advectionProgram.uniforms.texelSize,
1.0 / textureWidth,
1.0 / textureHeight
);
gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.first[2]); gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.first[2]);
gl.uniform1i(advectionProgram.uniforms.uSource, velocity.first[2]); gl.uniform1i(advectionProgram.uniforms.uSource, velocity.first[2]);
gl.uniform1f(advectionProgram.uniforms.dt, dt); gl.uniform1f(advectionProgram.uniforms.dt, dt);
gl.uniform1f( gl.uniform1f(advectionProgram.uniforms.dissipation, config.VELOCITY_DISSIPATION);
advectionProgram.uniforms.dissipation,
config.VELOCITY_DISSIPATION
);
blit(velocity.second[1]); blit(velocity.second[1]);
velocity.swap(); velocity.swap();
gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.first[2]); gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.first[2]);
gl.uniform1i(advectionProgram.uniforms.uSource, density.first[2]); gl.uniform1i(advectionProgram.uniforms.uSource, density.first[2]);
gl.uniform1f( gl.uniform1f(advectionProgram.uniforms.dissipation, config.DENSITY_DISSIPATION);
advectionProgram.uniforms.dissipation,
config.DENSITY_DISSIPATION
);
blit(density.second[1]); blit(density.second[1]);
density.swap(); density.swap();
@@ -498,20 +464,12 @@ function update() {
} }
curlProgram.bind(); curlProgram.bind();
gl.uniform2f( gl.uniform2f(curlProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);
curlProgram.uniforms.texelSize,
1.0 / textureWidth,
1.0 / textureHeight
);
gl.uniform1i(curlProgram.uniforms.uVelocity, velocity.first[2]); gl.uniform1i(curlProgram.uniforms.uVelocity, velocity.first[2]);
blit(curl[1]); blit(curl[1]);
vorticityProgram.bind(); vorticityProgram.bind();
gl.uniform2f( gl.uniform2f(vorticityProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);
vorticityProgram.uniforms.texelSize,
1.0 / textureWidth,
1.0 / textureHeight
);
gl.uniform1i(vorticityProgram.uniforms.uVelocity, velocity.first[2]); gl.uniform1i(vorticityProgram.uniforms.uVelocity, velocity.first[2]);
gl.uniform1i(vorticityProgram.uniforms.uCurl, curl[2]); gl.uniform1i(vorticityProgram.uniforms.uCurl, curl[2]);
gl.uniform1f(vorticityProgram.uniforms.curl, config.CURL); gl.uniform1f(vorticityProgram.uniforms.curl, config.CURL);
@@ -520,11 +478,7 @@ function update() {
velocity.swap(); velocity.swap();
divergenceProgram.bind(); divergenceProgram.bind();
gl.uniform2f( gl.uniform2f(divergenceProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);
divergenceProgram.uniforms.texelSize,
1.0 / textureWidth,
1.0 / textureHeight
);
gl.uniform1i(divergenceProgram.uniforms.uVelocity, velocity.first[2]); gl.uniform1i(divergenceProgram.uniforms.uVelocity, velocity.first[2]);
blit(divergence[1]); blit(divergence[1]);
@@ -538,11 +492,7 @@ function update() {
pressure.swap(); pressure.swap();
pressureProgram.bind(); pressureProgram.bind();
gl.uniform2f( gl.uniform2f(pressureProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);
pressureProgram.uniforms.texelSize,
1.0 / textureWidth,
1.0 / textureHeight
);
gl.uniform1i(pressureProgram.uniforms.uDivergence, divergence[2]); gl.uniform1i(pressureProgram.uniforms.uDivergence, divergence[2]);
pressureTexId = pressure.first[2]; pressureTexId = pressure.first[2];
gl.activeTexture(gl.TEXTURE0 + pressureTexId); gl.activeTexture(gl.TEXTURE0 + pressureTexId);
@@ -554,11 +504,7 @@ function update() {
} }
gradienSubtractProgram.bind(); gradienSubtractProgram.bind();
gl.uniform2f( gl.uniform2f(gradienSubtractProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);
gradienSubtractProgram.uniforms.texelSize,
1.0 / textureWidth,
1.0 / textureHeight
);
gl.uniform1i(gradienSubtractProgram.uniforms.uPressure, pressure.first[2]); gl.uniform1i(gradienSubtractProgram.uniforms.uPressure, pressure.first[2]);
gl.uniform1i(gradienSubtractProgram.uniforms.uVelocity, velocity.first[2]); gl.uniform1i(gradienSubtractProgram.uniforms.uVelocity, velocity.first[2]);
blit(velocity.second[1]); blit(velocity.second[1]);
@@ -567,6 +513,12 @@ function update() {
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
displayProgram.bind(); displayProgram.bind();
gl.uniform1i(displayProgram.uniforms.uTexture, density.first[2]); gl.uniform1i(displayProgram.uniforms.uTexture, density.first[2]);
const isDarkTheme = document.documentElement.getAttribute('data-theme') === 'dark';
if (displayProgram.uniforms.uIsDark !== undefined) {
gl.uniform1f(displayProgram.uniforms.uIsDark, isDarkTheme ? 1.0 : 0.0);
}
blit(null); blit(null);
requestAnimationFrame(update); requestAnimationFrame(update);
@@ -574,9 +526,9 @@ function update() {
update(); update();
// ===== REDIMENSIONAR AL CAMBIAR TAMAÑO DE VENTANA ===== // ===== REDIMENSIONAR =====
window.addEventListener("resize", function() { window.addEventListener("resize", function() {
canvas.width = window.innerWidth; canvas.width = canvas.clientWidth;
canvas.height = window.innerHeight; canvas.height = canvas.clientHeight;
initFramebuffers(); initFramebuffers();
}); });
+1 -1
View File
@@ -734,4 +734,4 @@ document.addEventListener('DOMContentLoaded', () => {
initPlanesFocusEffect(); initPlanesFocusEffect();
initPortfolioCarousel(); initPortfolioCarousel();
initMarqueeHover(); // ← además, esta NUNCA se está llamando initMarqueeHover(); // ← además, esta NUNCA se está llamando
}); });
+10 -5
View File
@@ -6,10 +6,14 @@
<meta name="description" content="Servicios de Amor y Roma: gestión de redes, branding, desarrollo web, automatizaciones y paid media."> <meta name="description" content="Servicios de Amor y Roma: gestión de redes, branding, desarrollo web, automatizaciones y paid media.">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css">
<title>Servicios - Amor y Roma</title> <title>Servicios - Amor y Roma</title>
<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="style/main.css"> <link rel="stylesheet" href="style/main.css">
<link rel="stylesheet" href="style/servicios.css"> <link rel="stylesheet" href="style/servicios.css">
</head> </head>
<body> <body>
<div id="fluid-bg" aria-hidden="true">
<canvas id="fluidCanvas"></canvas>
</div>
<div id="menu-container"></div> <div id="menu-container"></div>
<main> <main>
@@ -18,7 +22,7 @@
<div class="servicios-hero-content"> <div class="servicios-hero-content">
<span class="servicios-hero-tag">Nuestros servicios</span> <span class="servicios-hero-tag">Nuestros servicios</span>
<h1 class="servicios-hero-title">Soluciones a medida para cada etapa de tu marca</h1> <h1 class="servicios-hero-title">Soluciones a medida para cada etapa de tu marca</h1>
<p class="servicios-hero-subtitle">Combinamos estrategia, diseño y tecnología para construir marcas con esencia y estructura.</p> <p class="servicios-hero-subtitle">Diseñamos servicios que se adaptan a tu realidad, para ayudarte a crecer sin perder lo que te hace único.</p>
<a href="#servicios-selector" class="btn btn-primary servicios-hero-cta">Explorar servicios</a> <a href="#servicios-selector" class="btn btn-primary servicios-hero-cta">Explorar servicios</a>
</div> </div>
</section> </section>
@@ -254,7 +258,7 @@
<div class="panel-footer"> <div class="panel-footer">
<div class="panel-precio"> <div class="panel-precio">
<span class="precio-label">Desde</span> <span class="precio-label">Desde</span>
<span class="precio-valor">$220.000<span class="precio-periodo">/mes</span></span> <span class="precio-valor">$220.000<span class="precio-periodo">/campaña</span></span>
</div> </div>
<a href="contacto.html?servicio=paid" class="btn btn-secondary panel-cta">Cotizar este servicio</a> <a href="contacto.html?servicio=paid" class="btn btn-secondary panel-cta">Cotizar este servicio</a>
</div> </div>
@@ -263,8 +267,8 @@
</div> </div>
</section> </section>
<!-- Sección 4: Separador visual --> <!-- Sección 4: Separador visual
<section class="servicios-separador"></section> <section class="servicios-separador"></section>-->
<!-- Sección 5: Casos de éxito --> <!-- Sección 5: Casos de éxito -->
<section class="servicios-casos"> <section class="servicios-casos">
@@ -304,7 +308,7 @@
<h2 class="servicios-cta-titulo">¿No sabés cuál servicio necesitás?</h2> <h2 class="servicios-cta-titulo">¿No sabés cuál servicio necesitás?</h2>
<p class="servicios-cta-subtitle">Hacé nuestro diagnóstico gratuito y te recomendamos la solución ideal para tu momento.</p> <p class="servicios-cta-subtitle">Hacé nuestro diagnóstico gratuito y te recomendamos la solución ideal para tu momento.</p>
<div class="servicios-cta-buttons"> <div class="servicios-cta-buttons">
<a href="inicio.html#diagnostico" class="btn btn-cta-primary">Hacer el diagnóstico</a> <a href="index.html#diagnostico" class="btn btn-cta-primary">Hacer el diagnóstico</a>
<a href="contacto.html" class="btn btn-cta-secondary">Hablemos directamente</a> <a href="contacto.html" class="btn btn-cta-secondary">Hablemos directamente</a>
</div> </div>
</div> </div>
@@ -318,5 +322,6 @@
<script src="scripts/main.js"></script> <script src="scripts/main.js"></script>
<script src="scripts/servicios.js"></script> <script src="scripts/servicios.js"></script>
<script src="scripts/fluid.js"></script>
</body> </body>
</html> </html>
+103 -346
View File
@@ -3,7 +3,7 @@
============================================ */ ============================================ */
.blog-hero-compact { .blog-hero-compact {
padding: calc(var(--spacing-lg) + 2rem) var(--spacing-md) var(--spacing-lg); padding: calc(var(--spacing-lg) + 2rem) var(--spacing-md) var(--spacing-lg);
background-color: var(--bg-base); background-color: transparent;
text-align: center; text-align: center;
position: relative; position: relative;
} }
@@ -54,13 +54,12 @@
} }
/* ============================================ /* ============================================
SECCIÓN FILTROS Y BUSCADOR (INTEGRADOS, NO STICKY) SECCIÓN FILTROS Y BUSCADOR
============================================ */ ============================================ */
.blog-filters-section { .blog-filters-section {
padding: var(--spacing-md) var(--spacing-md); padding: var(--spacing-md) var(--spacing-md);
background-color: var(--bg-surface); background-color: var(--bg-surface-t0);
border-bottom: 1px solid var(--border); position: relative;
position: relative; /* Sin sticky, fluye normal */
z-index: 1; z-index: 1;
} }
@@ -69,18 +68,18 @@
margin: 0 auto; margin: 0 auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center;
gap: var(--spacing-sm); gap: var(--spacing-sm);
} }
/* Fila superior: Buscador + Filtros principales */
.filters-top-row { .filters-top-row {
display: flex; display: flex;
gap: var(--spacing-sm); gap: var(--spacing-sm);
align-items: center; align-items: center;
justify-content: center;
flex-wrap: wrap; flex-wrap: wrap;
} }
/* Buscador con forma redondeada (pill) */
.search-wrapper-compact { .search-wrapper-compact {
flex: 1; flex: 1;
min-width: 280px; min-width: 280px;
@@ -90,7 +89,7 @@
align-items: center; align-items: center;
background-color: var(--bg-base); background-color: var(--bg-base);
border: 1.5px solid var(--border); border: 1.5px solid var(--border);
border-radius: 999px; /* Forma redondeada como los botones */ border-radius: 999px;
padding: 0.5rem 0.5rem 0.5rem 1.1rem; padding: 0.5rem 0.5rem 0.5rem 1.1rem;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
@@ -148,7 +147,6 @@
justify-content: center; justify-content: center;
} }
/* Filtros principales (Todos / Destacados) */
.filter-group-compact { .filter-group-compact {
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
@@ -180,10 +178,9 @@
color: var(--text-on-accent); color: var(--text-on-accent);
} }
/* Fila inferior: Categorías + Ordenamiento */
.filters-bottom-row { .filters-bottom-row {
display: flex; display: flex;
justify-content: space-between; justify-content: center;
align-items: center; align-items: center;
gap: var(--spacing-sm); gap: var(--spacing-sm);
flex-wrap: wrap; flex-wrap: wrap;
@@ -195,7 +192,7 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.4rem; gap: 0.4rem;
flex: 1; justify-content: center;
} }
.category-btn { .category-btn {
@@ -247,11 +244,11 @@
border-color: var(--brand-teal); border-color: var(--brand-teal);
} }
/* Info de resultados */
.results-info-compact { .results-info-compact {
display: flex; display: flex;
justify-content: space-between; justify-content: center;
align-items: center; align-items: center;
gap: var(--spacing-sm);
font-family: var(--font-small); font-family: var(--font-small);
font-size: 0.85rem; font-size: 0.85rem;
color: var(--text-muted); color: var(--text-muted);
@@ -276,7 +273,7 @@
} }
/* ============================================ /* ============================================
TAGS CON MEJOR CONTRASTE (UNIFICADO) TAGS DE CATEGORÍAS
============================================ */ ============================================ */
.category-tag { .category-tag {
font-family: var(--font-small); font-family: var(--font-small);
@@ -290,89 +287,16 @@
transition: all 0.2s ease; transition: all 0.2s ease;
} }
/* Branding */ .category-tag.branding { background-color: var(--brand-crimson); color: #ffffff; border-color: var(--brand-crimson); }
.category-tag.branding { .category-tag.web { background-color: var(--brand-teal); color: #ffffff; border-color: var(--brand-teal); }
background-color: var(--brand-crimson); .category-tag.automatizaciones { background-color: var(--brand-plum); color: #ffffff; border-color: var(--brand-plum); }
color: #ffffff; .category-tag.redes { background-color: #00a88c; color: #ffffff; border-color: #00a88c; }
border-color: var(--brand-crimson); .category-tag.paid { background-color: #6b4c9a; color: #ffffff; border-color: #6b4c9a; }
} .category-tag.estrategia { background-color: #d97706; color: #ffffff; border-color: #d97706; }
[data-theme="dark"] .category-tag.branding { .category-tag.casos { background-color: #475569; color: #ffffff; border-color: #475569; }
background-color: rgba(186, 18, 77, 0.95);
color: #ffffff;
border-color: rgba(186, 18, 77, 1);
}
/* Web */ [data-theme="dark"] .category-tag.automatizaciones { background-color: rgba(23, 12, 29, 0.95); color: var(--brand-sage-light); border-color: rgba(23, 12, 29, 1); }
.category-tag.web { [data-theme="dark"] .category-tag.redes { background-color: rgba(5, 104, 87, 0.95); color: #ffffff; border-color: rgba(0, 168, 140, 1); }
background-color: var(--brand-teal);
color: #ffffff;
border-color: var(--brand-teal);
}
[data-theme="dark"] .category-tag.web {
background-color: rgba(0, 63, 82, 0.95);
color: #ffffff;
border-color: rgba(0, 63, 82, 1);
}
/* Automatizaciones */
.category-tag.automatizaciones {
background-color: var(--brand-plum);
color: #ffffff;
border-color: var(--brand-plum);
}
[data-theme="dark"] .category-tag.automatizaciones {
background-color: rgba(23, 12, 29, 0.95);
color: var(--brand-sage-light);
border-color: rgba(23, 12, 29, 1);
}
/* Redes */
.category-tag.redes {
background-color: #00a88c;
color: #ffffff;
border-color: #00a88c;
}
[data-theme="dark"] .category-tag.redes {
background-color: rgba(5, 104, 87, 0.95);
color: #ffffff;
border-color: rgba(0, 168, 140, 1);
}
/* Paid Media */
.category-tag.paid {
background-color: #6b4c9a;
color: #ffffff;
border-color: #6b4c9a;
}
[data-theme="dark"] .category-tag.paid {
background-color: rgba(107, 76, 154, 0.95);
color: #ffffff;
border-color: rgba(107, 76, 154, 1);
}
/* Estrategia */
.category-tag.estrategia {
background-color: #d97706;
color: #ffffff;
border-color: #d97706;
}
[data-theme="dark"] .category-tag.estrategia {
background-color: rgba(217, 119, 6, 0.95);
color: #ffffff;
border-color: rgba(217, 119, 6, 1);
}
/* Casos */
.category-tag.casos {
background-color: #475569;
color: #ffffff;
border-color: #475569;
}
[data-theme="dark"] .category-tag.casos {
background-color: rgba(71, 85, 105, 0.95);
color: #ffffff;
border-color: rgba(71, 85, 105, 1);
}
.category-tag:hover { .category-tag:hover {
transform: translateY(-1px); transform: translateY(-1px);
@@ -380,11 +304,11 @@
} }
/* ============================================ /* ============================================
ARTÍCULOS DESTACADOS (VERSIÓN SIMÉTRICA) ARTÍCULOS DESTACADOS
============================================ */ ============================================ */
.featured-posts { .featured-posts {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: var(--bg-base-t3);
position: relative; position: relative;
} }
@@ -411,7 +335,7 @@
.featured-posts .section-title { .featured-posts .section-title {
font-family: var(--font-title); font-family: var(--font-title);
font-style: italic; font-style: italic;
font-size: clamp(2rem, 4vw, 2.8rem); font-size: clamp(1.2rem, 2vw, 1.8rem);
color: var(--text-primary); color: var(--text-primary);
margin-bottom: var(--spacing-xs); margin-bottom: var(--spacing-xs);
line-height: 1.2; line-height: 1.2;
@@ -444,24 +368,16 @@
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 480px; min-height: 250px !important;
} }
/* Glow sutil para destacar en hover */
.featured-card::before { .featured-card::before {
content: ""; content: "";
position: absolute; position: absolute;
top: 0; top: 0; left: 0; right: 0; bottom: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 20px; border-radius: 20px;
padding: 2px; padding: 2px;
background: linear-gradient(135deg, background: linear-gradient(135deg, rgba(186, 18, 77, 0.3) 0%, rgba(0, 63, 82, 0.3) 50%, rgba(186, 18, 77, 0.3) 100%);
rgba(186, 18, 77, 0.3) 0%,
rgba(0, 63, 82, 0.3) 50%,
rgba(186, 18, 77, 0.3) 100%
);
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor; -webkit-mask-composite: xor;
mask-composite: exclude; mask-composite: exclude;
@@ -475,9 +391,7 @@
box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.15); box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.15);
} }
.featured-card:hover::before { .featured-card:hover::before { opacity: 1; }
opacity: 1;
}
.featured-badge { .featured-badge {
position: absolute; position: absolute;
@@ -499,7 +413,7 @@
.featured-image { .featured-image {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
aspect-ratio: 16/10; aspect-ratio: 16/8;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -510,9 +424,7 @@
transition: transform 0.5s ease; transition: transform 0.5s ease;
} }
.featured-card:hover .featured-image img { .featured-card:hover .featured-image img { transform: scale(1.05); }
transform: scale(1.05);
}
.featured-content { .featured-content {
padding: var(--spacing-md); padding: var(--spacing-md);
@@ -532,18 +444,27 @@
.featured-title { .featured-title {
font-family: var(--font-title); font-family: var(--font-title);
font-style: italic; font-style: italic;
font-size: 1.3rem; font-size: 1.1rem;
color: var(--text-primary); color: var(--text-primary);
line-height: 1.3; line-height: 1.2;
margin-bottom: var(--spacing-xs); margin-bottom: var(--spacing-xs);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
} }
.featured-excerpt { .featured-excerpt {
font-family: var(--font-body); font-family: var(--font-body);
font-size: 0.95rem; font-size: 0.8rem;
color: var(--text-muted); color: var(--text-muted);
line-height: 1.6; line-height: 1.5;
flex: 1; flex: 1;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
margin-bottom: 0.3rem !important;
} }
.featured-footer { .featured-footer {
@@ -583,9 +504,45 @@
============================================ */ ============================================ */
.blog-grid-section { .blog-grid-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-surface); background-color: var(--bg-surface-t3);
}
.blog-grid-section .section-header {
max-width: 1400px;
margin: 0 auto var(--spacing-lg);
text-align: left;
padding-left: var(--spacing-sm);
position: relative;
} }
.blog-grid-section .section-header::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 60px;
background: linear-gradient(180deg, var(--brand-teal) 0%, var(--brand-crimson) 100%);
border-radius: 2px;
}
.blog-grid-section .section-title {
font-family: var(--font-title);
font-style: italic;
font-size: clamp(1.2rem, 2vw, 1.8rem);
color: var(--text-primary);
margin-bottom: var(--spacing-xs);
line-height: 1.2;
padding-left: var(--spacing-md);
}
.blog-grid-section .section-subtitle {
font-family: var(--font-body);
font-size: 1.1rem;
color: var(--text-muted);
line-height: 1.6;
padding-left: var(--spacing-md);
max-width: 600px;
}
.blog-grid { .blog-grid {
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
@@ -623,9 +580,7 @@
transition: transform 0.5s ease; transition: transform 0.5s ease;
} }
.blog-card:hover .card-image img { .blog-card:hover .card-image img { transform: scale(1.08); }
transform: scale(1.08);
}
.card-category { .card-category {
position: absolute; position: absolute;
@@ -659,9 +614,7 @@
color: var(--text-muted); color: var(--text-muted);
} }
.reading-time-mini { .reading-time-mini { font-weight: 600; }
font-weight: 600;
}
.card-title { .card-title {
font-family: var(--font-title); font-family: var(--font-title);
@@ -680,23 +633,6 @@
flex: 1; flex: 1;
} }
.card-tags {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin-top: var(--spacing-xs);
}
.tag {
font-family: var(--font-small);
font-size: 0.75rem;
padding: 0.25rem 0.7rem;
background-color: var(--bg-surface);
border: 1px solid var(--border);
border-radius: 999px;
color: var(--text-secondary);
}
.card-link { .card-link {
font-family: var(--font-small); font-family: var(--font-small);
font-size: 0.9rem; font-size: 0.9rem;
@@ -723,12 +659,6 @@
display: none; display: none;
} }
.empty-icon {
font-size: 4rem;
margin-bottom: var(--spacing-sm);
opacity: 0.5;
}
.empty-state h3 { .empty-state h3 {
font-family: var(--font-title); font-family: var(--font-title);
font-style: italic; font-style: italic;
@@ -750,213 +680,40 @@
margin-top: var(--spacing-lg); margin-top: var(--spacing-lg);
} }
/* ============================================
NEWSLETTER
============================================ */
.newsletter-section {
padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base);
}
.newsletter-card {
max-width: 900px;
margin: 0 auto;
background-color: var(--brand-teal);
border-radius: 24px;
padding: var(--spacing-lg) var(--spacing-md);
display: grid;
grid-template-columns: 1.5fr 1fr;
gap: var(--spacing-lg);
align-items: center;
position: relative;
overflow: hidden;
}
.newsletter-card::before {
content: "";
position: absolute;
top: -100px;
right: -100px;
width: 300px;
height: 300px;
border-radius: 50%;
background: radial-gradient(circle, var(--brand-crimson) 0%, transparent 70%);
opacity: 0.3;
filter: blur(60px);
}
.newsletter-content {
position: relative;
z-index: 1;
}
.newsletter-title {
font-family: var(--font-title);
font-style: italic;
font-size: clamp(1.8rem, 3vw, 2.5rem);
color: #ffffff;
margin-bottom: var(--spacing-xs);
}
.newsletter-subtitle {
font-family: var(--font-body);
font-size: 1rem;
color: rgba(255, 255, 255, 0.85);
line-height: 1.6;
}
.newsletter-form {
display: flex;
gap: var(--spacing-sm);
position: relative;
z-index: 1;
}
.newsletter-form input {
flex: 1;
padding: 0.85rem 1.2rem;
border: none;
border-radius: 999px;
font-family: var(--font-body);
font-size: 0.95rem;
background-color: rgba(255, 255, 255, 0.95);
color: var(--text-primary);
}
.newsletter-form input::placeholder {
color: var(--text-muted);
}
.newsletter-form .btn {
white-space: nowrap;
background-color: var(--brand-crimson);
color: var(--text-on-accent);
border: 1.5px solid var(--brand-crimson);
}
.newsletter-form .btn:hover {
background-color: #ffffff;
color: var(--brand-teal);
border-color: #ffffff;
}
/* ============================================ /* ============================================
RESPONSIVE BLOG RESPONSIVE BLOG
============================================ */ ============================================ */
@media (max-width: 1024px) { @media (max-width: 1024px) {
.blog-grid, .blog-grid, .featured-grid { grid-template-columns: repeat(2, 1fr); }
.featured-grid { .featured-card { min-height: 420px; }
grid-template-columns: repeat(2, 1fr);
}
.featured-card {
min-height: 420px;
}
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.blog-hero-compact { .blog-hero-compact { padding: var(--spacing-lg) var(--spacing-sm) var(--spacing-md); }
padding: var(--spacing-lg) var(--spacing-sm) var(--spacing-md); .blog-filters-section { padding: var(--spacing-sm); }
} .filters-top-row { flex-direction: column; align-items: stretch; }
.search-wrapper-compact { max-width: 100%; min-width: 100%; }
.blog-filters-section { .filter-group-compact { justify-content: center; }
padding: var(--spacing-sm); .filters-bottom-row { flex-direction: column; align-items: stretch; }
}
.filters-top-row {
flex-direction: column;
align-items: stretch;
}
.search-wrapper-compact {
max-width: 100%;
min-width: 100%;
}
.filter-group-compact {
justify-content: center;
}
.filters-bottom-row {
flex-direction: column;
align-items: stretch;
}
.filter-categories-compact { .filter-categories-compact {
overflow-x: auto; overflow-x: auto; flex-wrap: nowrap; -webkit-overflow-scrolling: touch;
flex-wrap: nowrap; scrollbar-width: none; padding-bottom: 4px;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
padding-bottom: 4px;
}
.filter-categories-compact::-webkit-scrollbar {
display: none;
}
.category-btn {
flex-shrink: 0;
}
.filter-secondary-compact {
align-self: flex-end;
}
.blog-grid,
.featured-grid {
grid-template-columns: 1fr;
max-width: 500px;
}
.featured-card {
min-height: auto;
}
.newsletter-card {
grid-template-columns: 1fr;
text-align: center;
}
.newsletter-form {
flex-direction: column;
}
.newsletter-form .btn {
width: 100%;
} }
.filter-categories-compact::-webkit-scrollbar { display: none; }
.category-btn { flex-shrink: 0; }
.filter-secondary-compact { align-self: flex-end; }
.blog-grid, .featured-grid { grid-template-columns: 1fr; max-width: 500px; }
.featured-card { min-height: auto; }
} }
@media (max-width: 480px) { @media (max-width: 480px) {
.blog-hero-compact .hero-compact-title { .blog-hero-compact .hero-compact-title { font-size: 2rem; }
font-size: 2rem; .search-wrapper-compact { padding: 0.4rem 0.4rem 0.4rem 1rem; }
} .search-wrapper-compact input { font-size: 0.9rem; }
.filter-btn { font-size: 0.8rem; padding: 0.4rem 0.8rem; }
.search-wrapper-compact { .featured-title, .card-title { font-size: 1.2rem; }
padding: 0.4rem 0.4rem 0.4rem 1rem;
}
.search-wrapper-compact input {
font-size: 0.9rem;
}
.filter-btn {
font-size: 0.8rem;
padding: 0.4rem 0.8rem;
}
.featured-title,
.card-title {
font-size: 1.2rem;
}
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.blog-card, .blog-card, .featured-card, .filter-btn, .category-btn, .search-wrapper-compact, .category-tag { transition: none; }
.featured-card,
.filter-btn,
.category-btn,
.search-wrapper-compact,
.category-tag {
transition: none;
}
} }
+26 -15
View File
@@ -33,7 +33,7 @@
============================================ */ ============================================ */
.contacto-grid-section { .contacto-grid-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-surface); background-color: var(--bg-surface-t2);
} }
.contacto-grid { .contacto-grid {
@@ -95,8 +95,7 @@
width: 40px; width: 40px;
height: 40px; height: 40px;
flex-shrink: 0; flex-shrink: 0;
background-color: var(--brand-crimson); border-radius: 1px;
border-radius: 10px;
-webkit-mask-size: contain; -webkit-mask-size: contain;
mask-size: contain; mask-size: contain;
-webkit-mask-repeat: no-repeat; -webkit-mask-repeat: no-repeat;
@@ -108,22 +107,27 @@
.canal-email { .canal-email {
-webkit-mask-image: url("../assets/iconos/buzon.svg"); -webkit-mask-image: url("../assets/iconos/buzon.svg");
mask-image: url("../assets/iconos/buzon.svg"); mask-image: url("../assets/iconos/buzon.svg");
background-color: var(--brand-teal); background-color: var(--brand-teal-contraste);
} }
.canal-whatsapp { .canal-whatsapp {
-webkit-mask-image: url("../assets/vectores/whatsapp.svg"); -webkit-mask-image: url("../assets/logos/whatsapp.svg");
mask-image: url("../assets/vectores/whatsapp.svg"); mask-image: url("../assets/logos/whatsapp.svg");
background-color: #25D366; background-color: var(--brand-teal-contraste);
} }
.canal-instagram { .canal-instagram {
-webkit-mask-image: url("../assets/vectores/insta.svg"); -webkit-mask-image: url("../assets/logos/instagram.svg");
mask-image: url("../assets/vectores/insta.svg"); mask-image: url("../assets/logos/instagram.svg");
background: linear-gradient(45deg, #f09433, #e6683c, #dc2743, #cc2366, #bc1888); background-color: var(--brand-teal-contraste);
} }
.canal-linkedin { .canal-linkedin {
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.063 2.063 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z'/%3E%3C/svg%3E"); -webkit-mask-image: url("../assets/logos/linkedin.svg");
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.063 2.063 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z'/%3E%3C/svg%3E"); mask-image: url("../assets/logos/linkedin.svg");
background-color: #0A66C2; background-color: var(--brand-teal-contraste);
}
.canal-facebook {
-webkit-mask-image: url("../assets/logos/facebook.svg");
mask-image: url("../assets/logos/facebook.svg");
background-color: var(--brand-teal-contraste);
} }
.canal-contenido { .canal-contenido {
@@ -311,7 +315,7 @@
============================================ */ ============================================ */
.proceso-section { .proceso-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: var(--bg-base-t3);
} }
.proceso-grid { .proceso-grid {
@@ -388,7 +392,7 @@
============================================ */ ============================================ */
.notas-section { .notas-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-surface); background-color: var(--bg-surface-t1);
} }
.notas-container { .notas-container {
@@ -420,6 +424,13 @@
color: var(--text-muted); color: var(--text-muted);
line-height: 1.6; line-height: 1.6;
} }
.form-honeypot {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
}
/* ============================================ /* ============================================
RESPONSIVE CONTACTO RESPONSIVE CONTACTO
File diff suppressed because it is too large Load Diff
+259 -40
View File
@@ -31,6 +31,14 @@
--bg-surface-elevated: #f5f9f8; --bg-surface-elevated: #f5f9f8;
--bg-icon-contrast: var(--brand-teal); --bg-icon-contrast: var(--brand-teal);
--bg-base-t0:rgba(234,240,238, 0.98);
--bg-base-t1:rgba(234,240,238, 0.95);
--bg-base-t2:rgba(234,240,238, 0.7);
--bg-base-t3:rgba(234,240,238, 0.5);
--bg-surface-t0:rgb(255, 255, 255, 0.98);
--bg-surface-t1:rgb(255, 255, 255, 0.95);
--bg-surface-t2: rgb(255, 255, 255, 0.7);
--text-primary: var(--brand-plum); --text-primary: var(--brand-plum);
--text-secondary: var(--brand-teal); --text-secondary: var(--brand-teal);
--text-muted: rgba(34, 23, 40, 0.65); --text-muted: rgba(34, 23, 40, 0.65);
@@ -77,6 +85,14 @@
--brand-teal-contraste: #116c88; --brand-teal-contraste: #116c88;
--brand-crimson-contraste: #d81a5c; --brand-crimson-contraste: #d81a5c;
--bg-base-t0:rgba(17,9,22, 0.98);
--bg-base-t1:rgba(17,9,22, 0.95);
--bg-base-t2:rgba(17,9,22, 0.7);
--bg-base-t3:rgba(17,9,22, 0.5);
--bg-surface-t0:rgb(23,12,29, 0.98);
--bg-surface-t1:rgb(23,12,29, 0.95);
--bg-surface-t2: rgb(23,12,29, 0.7);
--text-primary: var(--brand-sage-light); --text-primary: var(--brand-sage-light);
--text-secondary: var(--brand-rose-muted); --text-secondary: var(--brand-rose-muted);
--text-muted: rgba(234, 240, 238, 0.65); --text-muted: rgba(234, 240, 238, 0.65);
@@ -108,11 +124,13 @@
html { html {
font-size: var(--font-size-base); font-size: var(--font-size-base);
scroll-behavior: smooth; scroll-behavior: smooth;
background-color: var(--bg-base);
} }
body { body {
font-family: var(--font-family-base); font-family: var(--font-family-base);
background-color: var(--bg-base); /*background-color: var(--bg-base);*/
background-color: transparent;
color: var(--text-primary); color: var(--text-primary);
line-height: 1.5; line-height: 1.5;
min-height: 100vh; min-height: 100vh;
@@ -156,6 +174,20 @@ ul { list-style: none; }
z-index: 100; z-index: 100;
} }
.badge-filosofia {
display: inline-block;
padding: 0.4rem 1rem;
border-radius: 999px;
color: var(--badge-text);
font-family: var(--font-small);
font-weight: 400;
font-size: 1rem;
letter-spacing: 0.5px;
text-transform: uppercase;
opacity: 0;
animation: fadeInUp 0.9s ease-out 0s forwards;
}
.site-header { .site-header {
background-color: var(--bg-surface); background-color: var(--bg-surface);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
@@ -500,7 +532,8 @@ body.menu-open .site-header {
============================================ */ ============================================ */
main section { main section {
padding: var(--spacing-md) var(--spacing-sm); padding: var(--spacing-md) var(--spacing-sm);
border-bottom: 1px dashed var(--border); background-color: transparent;
/*border-bottom: 1px dashed var(--border);*/
} }
main section h2 { margin-bottom: var(--spacing-sm); font-size: 1.5rem; } main section h2 { margin-bottom: var(--spacing-sm); font-size: 1.5rem; }
@@ -508,7 +541,7 @@ main section h2 { margin-bottom: var(--spacing-sm); font-size: 1.5rem; }
border-top: 1px solid var(--border); border-top: 1px solid var(--border);
padding-top: var(--spacing-lg); padding-top: var(--spacing-lg);
margin-top: auto; margin-top: auto;
background-color: var(--bg-surface); background-color: var(--bg-surface-t0);
position: relative; position: relative;
} }
@@ -565,7 +598,7 @@ main section h2 { margin-bottom: var(--spacing-sm); font-size: 1.5rem; }
============================================ */ ============================================ */
.hero-section { .hero-section {
min-height: calc(100vh - var(--header-height)); min-height: calc(100vh - var(--header-height));
background-color: var(--bg-base); background-color: var(--bg-base-t3);
display: flex; align-items: center; justify-content: center; display: flex; align-items: center; justify-content: center;
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
text-align: center; text-align: center;
@@ -665,7 +698,7 @@ main section h2 { margin-bottom: var(--spacing-sm); font-size: 1.5rem; }
============================================ */ ============================================ */
.servicios-section { .servicios-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: var(--bg-base-t3);
} }
.servicios-container { .servicios-container {
@@ -789,7 +822,7 @@ SECCIÓN DIAGNÓSTICO
============================================ */ ============================================ */
.diagnostico-section { .diagnostico-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: transparent;
} }
.diagnostico-header { .diagnostico-header {
@@ -1364,7 +1397,7 @@ RESPONSIVE DIAGNÓSTICO
SECCIÓN METODOLOGÍA SECCIÓN METODOLOGÍA
============================================ */ ============================================ */
.metodologia-section { .metodologia-section {
padding: var(--spacing-lg) var(--spacing-md); background-color: var(--bg-base); padding: var(--spacing-lg) var(--spacing-md); background-color: var(--bg-base-t2);
} }
.metodologia-header { .metodologia-header {
@@ -1474,7 +1507,7 @@ RESPONSIVE DIAGNÓSTICO
SECCIÓN PLANES DESTACADOS SECCIÓN PLANES DESTACADOS
============================================ */ ============================================ */
.planes-section { .planes-section {
padding: var(--spacing-lg) var(--spacing-md); background-color: var(--bg-base); padding: var(--spacing-lg) var(--spacing-md); background-color: transparent;
} }
.planes-header { text-align: center; max-width: 700px; margin: 0 auto var(--spacing-lg); } .planes-header { text-align: center; max-width: 700px; margin: 0 auto var(--spacing-lg); }
@@ -1550,7 +1583,7 @@ RESPONSIVE DIAGNÓSTICO
/* ============================================ /* ============================================
SECCIÓN PORTFOLIO SECCIÓN PORTFOLIO
============================================ */ ============================================ */
.portfolio-section { padding: var(--spacing-lg) var(--spacing-md); background-color: var(--bg-base); } .portfolio-section { padding: var(--spacing-lg) var(--spacing-md); background-color: var(--bg-base-t2); }
.portfolio-container { .portfolio-container {
display: grid; grid-template-columns: 1fr 1.2fr; gap: var(--spacing-lg); display: grid; grid-template-columns: 1fr 1.2fr; gap: var(--spacing-lg);
max-width: 1400px; margin: 0 auto; align-items: center; max-width: 1400px; margin: 0 auto; align-items: center;
@@ -1667,56 +1700,242 @@ RESPONSIVE DIAGNÓSTICO
/* ============================================ /* ============================================
SECCIÓN CTA FINAL SECCIÓN CTA FINAL
============================================ */ ============================================ */
.cta-section { padding: var(--spacing-lg) var(--spacing-md); background-color: var(--bg-base); } .cta-section {
padding: var(--spacing-lg) var(--spacing-md);
background-color: transparent;
}
.cta-card { .cta-card {
max-width: 900px; margin: 0 auto; background-color: var(--brand-teal); max-width: 900px;
border-radius: 24px; padding: var(--spacing-lg) var(--spacing-md); margin: 0 auto;
text-align: center; position: relative; overflow: hidden; /* Fondo teal sólido y oscuro para máximo contraste */
box-shadow: 0 20px 60px -10px rgba(0, 63, 82, 0.4); background-color: var(--brand-teal);
border-radius: 24px;
padding: var(--spacing-lg) var(--spacing-md);
text-align: center;
position: relative;
overflow: hidden;
/* Sombra más pronunciada */
box-shadow: 0 20px 60px -10px rgba(0, 63, 82, 0.5);
/* Borde sutil para definir mejor */
border: 1px solid rgba(255, 255, 255, 0.1);
} }
.cta-card::before, .cta-card::after { /* Glows decorativos con degradé crimson-teal */
content: ""; position: absolute; width: 300px; height: 300px; .cta-card::before,
border-radius: 50%; filter: blur(80px); opacity: 0.35; pointer-events: none; .cta-card::after {
content: "";
position: absolute;
width: 400px;
height: 400px;
border-radius: 50%;
filter: blur(100px);
opacity: 0.4;
pointer-events: none;
z-index: 0;
}
/* Glow superior izquierdo - Crimson/Rosa */
.cta-card::before {
top: -150px;
left: -150px;
background: radial-gradient(
circle,
rgba(186, 18, 77, 0.6) 0%,
rgba(186, 18, 77, 0.3) 40%,
transparent 70%
);
animation: glowPulse 8s ease-in-out infinite;
}
/* Glow inferior derecho - Teal/Rose */
.cta-card::after {
bottom: -150px;
right: -150px;
background: radial-gradient(
circle,
rgba(0, 168, 140, 0.5) 0%,
rgba(223, 189, 185, 0.3) 40%,
transparent 70%
);
animation: glowPulse 8s ease-in-out infinite reverse;
}
/* Animación sutil de los glows */
@keyframes glowPulse {
0%, 100% {
transform: scale(1) translate(0, 0);
opacity: 0.4;
}
50% {
transform: scale(1.1) translate(20px, -20px);
opacity: 0.5;
}
} }
.cta-card::before { top: -100px; left: -100px; background: radial-gradient(circle, var(--brand-crimson) 0%, transparent 70%); }
.cta-card::after { bottom: -100px; right: -100px; background: radial-gradient(circle, var(--brand-rose-muted) 0%, transparent 70%); }
.cta-title { .cta-title {
font-family: var(--font-title); font-style: italic; font-family: var(--font-title);
font-size: clamp(2rem, 4vw, 3rem); color: #ffffff; font-style: italic;
margin-bottom: var(--spacing-sm); line-height: 1.15; position: relative; z-index: 1; font-size: clamp(2rem, 4vw, 3rem);
color: #ffffff;
margin-bottom: var(--spacing-sm);
line-height: 1.15;
position: relative;
z-index: 1;
/* Sombra de texto para mejorar legibilidad */
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
} }
.cta-subtitle { .cta-subtitle {
font-family: var(--font-body); font-size: clamp(1rem, 2vw, 1.15rem); font-family: var(--font-body);
color: rgba(255, 255, 255, 0.85); max-width: 600px; margin: 0 auto var(--spacing-md); font-size: clamp(1rem, 2vw, 1.15rem);
line-height: 1.6; position: relative; z-index: 1; color: rgba(255, 255, 255, 0.9);
max-width: 600px;
margin: 0 auto var(--spacing-md);
line-height: 1.6;
position: relative;
z-index: 1;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
} }
.cta-buttons { .cta-buttons {
display: flex; justify-content: center; gap: var(--spacing-sm); display: flex;
flex-wrap: wrap; position: relative; z-index: 1; justify-content: center;
gap: var(--spacing-sm);
flex-wrap: wrap;
position: relative;
z-index: 1;
} }
/* Botón primario - Blanco con hover crimson */
.btn-cta-primary { .btn-cta-primary {
background-color: #ffffff; color: var(--brand-teal); border: 1.5px solid #ffffff; background-color: #ffffff;
padding: 0.85rem 2rem; border-radius: 999px; font-family: var(--font-small); color: var(--brand-teal);
font-size: 0.95rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; border: 2px solid #ffffff;
box-shadow: 0 4px 14px -3px rgba(0, 0, 0, 0.2); padding: 0.85rem 2.2rem;
} border-radius: 999px;
.btn-cta-primary:hover { font-family: var(--font-small);
background-color: var(--brand-crimson); color: #ffffff; border-color: var(--brand-crimson); font-size: 0.95rem;
transform: translateY(-2px); box-shadow: 0 6px 20px -3px rgba(186, 18, 77, 0.45); font-weight: 700;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
box-shadow: 0 4px 14px -3px rgba(0, 0, 0, 0.25);
text-decoration: none;
display: inline-block;
} }
.btn-cta-secondary { .btn-cta-primary:hover {
background-color: transparent; color: #ffffff; border: 1.5px solid rgba(255, 255, 255, 0.6); background-color: var(--brand-crimson);
padding: 0.85rem 2rem; border-radius: 999px; font-family: var(--font-small); color: #ffffff;
font-size: 0.95rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; border-color: var(--brand-crimson);
transform: translateY(-3px);
box-shadow: 0 8px 24px -4px rgba(186, 18, 77, 0.5);
} }
/* Botón secundario - Transparente con borde blanco */
.btn-cta-secondary {
background-color: transparent;
color: #ffffff;
border: 2px solid rgba(255, 255, 255, 0.8);
padding: 0.85rem 2.2rem;
border-radius: 999px;
font-family: var(--font-small);
font-size: 0.95rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
text-decoration: none;
display: inline-block;
}
.btn-cta-secondary:hover { .btn-cta-secondary:hover {
background-color: rgba(255, 255, 255, 0.1); border-color: #ffffff; transform: translateY(-2px); background-color: rgba(255, 255, 255, 0.15);
border-color: #ffffff;
transform: translateY(-3px);
box-shadow: 0 8px 24px -4px rgba(255, 255, 255, 0.2);
}
/* ===== CANVAS FLUIDO Y APILAMIENTO ===== */
main {
position: relative;
z-index: 1;
}
#footer-container {
position: relative;
z-index: 2;
}
#fluid-bg {
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none;
}
#fluidCanvas {
display: block;
width: 100vw;
height: 100vh;
background: transparent;
}
/* Responsive */
@media (max-width: 768px) {
.cta-section {
padding: var(--spacing-md) var(--spacing-sm);
}
.cta-card {
padding: var(--spacing-lg) var(--spacing-md);
border-radius: 20px;
}
.cta-card::before,
.cta-card::after {
width: 250px;
height: 250px;
filter: blur(60px);
}
.cta-buttons {
flex-direction: column;
align-items: center;
}
.btn-cta-primary,
.btn-cta-secondary {
width: 100%;
max-width: 320px;
}
}
@media (max-width: 480px) {
.cta-card {
padding: var(--spacing-md) var(--spacing-sm);
}
.cta-title {
font-size: 1.8rem;
}
.cta-subtitle {
font-size: 1rem;
}
}
@media (prefers-reduced-motion: reduce) {
.cta-card::before,
.cta-card::after {
animation: none;
}
.btn-cta-primary,
.btn-cta-secondary {
transition: none;
}
} }
@media (max-width: 768px) { @media (max-width: 768px) {
+94
View File
@@ -0,0 +1,94 @@
/* ── Modal de contacto ──────────────────────────────────────────────── */
#modal-contacto {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(20, 18, 15, 0.55);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
opacity: 0;
visibility: hidden;
transition: opacity .35s ease, visibility .35s ease;
z-index: 9999;
padding: 1.5rem;
}
#modal-contacto.is-visible {
opacity: 1;
visibility: visible;
}
#modal-contacto .modal-card {
background: #fff;
color: #1c1a17;
border-radius: 14px;
padding: 2.5rem 2rem;
max-width: 460px;
width: 100%;
text-align: center;
box-shadow: 0 24px 60px rgba(0, 0, 0, .25);
transform: translateY(20px) scale(.97);
transition: transform .35s cubic-bezier(.2, .8, .2, 1);
position: relative;
}
#modal-contacto.is-visible .modal-card {
transform: translateY(0) scale(1);
}
#modal-contacto .modal-icono {
width: 64px;
height: 64px;
border-radius: 50%;
background: #f1ece1;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 1.25rem;
font-size: 1.75rem;
color: #8b7355;
}
#modal-contacto .modal-titulo {
font-size: 1.4rem;
margin: 0 0 .75rem;
letter-spacing: -0.01em;
}
#modal-contacto .modal-mensaje {
font-size: .98rem;
line-height: 1.55;
color: #4a453d;
margin: 0 0 1.75rem;
}
#modal-contacto .modal-cerrar {
display: inline-block;
background: #1c1a17;
color: #fff;
border: none;
padding: .85rem 1.75rem;
border-radius: 999px;
font-size: .95rem;
cursor: pointer;
transition: background .25s ease, transform .25s ease;
font-family: inherit;
}
#modal-contacto .modal-cerrar:hover {
background: #8b7355;
transform: translateY(-1px);
}
/* Variante error */
#modal-contacto.is-error .modal-icono {
background: #fdecec;
color: #c0392b;
}
@media (max-width: 480px) {
#modal-contacto .modal-card { padding: 2rem 1.25rem; }
#modal-contacto .modal-titulo { font-size: 1.2rem; }
}
+7 -24
View File
@@ -18,24 +18,7 @@
[data-theme="dark"] .logo-light { display: none; } [data-theme="dark"] .logo-light { display: none; }
[data-theme="dark"] .logo-dark { display: block; } [data-theme="dark"] .logo-dark { display: block; }
/* (Se omiten estilos intermedios del menú por brevedad, asumo que ya los tienes en tu archivo original.
Si necesitas el bloque completo del menú, avísame, pero se mantiene intacto tu CSS original aquí). */
.site-footer {
border-top: 1px solid var(--border);
padding-top: var(--spacing-lg);
margin-top: auto;
background-color: var(--bg-surface);
}
.footer-bottom {
border-top: 1px solid var(--border);
padding: var(--spacing-sm);
text-align: center;
font-size: 0.875rem;
color: var(--text-muted);
background-color: var(--bg-surface);
font-family: 'Jost', sans-serif;
}
/* ============================================ /* ============================================
COMPONENTES REUTILIZABLES COMPONENTES REUTILIZABLES
@@ -69,7 +52,7 @@
============================================ */ ============================================ */
.hero-section { .hero-section {
min-height: calc(100vh - var(--header-height)); min-height: calc(100vh - var(--header-height));
background-color: var(--bg-base); background-color: transparent;
display: flex; align-items: center; justify-content: center; display: flex; align-items: center; justify-content: center;
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
text-align: center; text-align: center;
@@ -133,7 +116,7 @@
/* --- DUALIDAD --- */ /* --- DUALIDAD --- */
.dualidad-section { .dualidad-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-surface); background-color: var(--bg-surface-t2);
} }
.dualidad-grid { .dualidad-grid {
display: grid; grid-template-columns: 1fr 1fr; gap: var(--spacing-md); display: grid; grid-template-columns: 1fr 1fr; gap: var(--spacing-md);
@@ -191,7 +174,7 @@
/* --- PRINCIPIOS --- */ /* --- PRINCIPIOS --- */
.principios-section { .principios-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: var(--bg-base-t2);
} }
.principios-grid { .principios-grid {
display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--spacing-md); display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--spacing-md);
@@ -228,7 +211,7 @@
============================================ */ ============================================ */
.casos-section { .casos-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: var(--bg-base-t3);
} }
.casos-grid-simetrico { .casos-grid-simetrico {
@@ -341,7 +324,7 @@
.caso-descripcion { .caso-descripcion {
font-family: var(--font-body); font-family: var(--font-body);
font-size: 0.95rem; font-size: 0.95rem;
color: var(--brand-sage-light); color: var(--text-secondary);
line-height: 1.6; line-height: 1.6;
flex: 1; flex: 1;
} }
@@ -452,7 +435,7 @@
============================================ */ ============================================ */
.transparencia-section { .transparencia-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: var(--bg-base-t2);
} }
.transparencia-grid { .transparencia-grid {
@@ -580,7 +563,7 @@
============================================ */ ============================================ */
.equipo-section { .equipo-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: transparent;
} }
/* Título con gradiente en la palabra destacada */ /* Título con gradiente en la palabra destacada */
+5 -5
View File
@@ -3,7 +3,7 @@
============================================ */ ============================================ */
.planes-hero { .planes-hero {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: var(--bg-base-t2);
min-height: 70vh; min-height: 70vh;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -71,7 +71,7 @@
============================================ */ ============================================ */
.planes-selector-section { .planes-selector-section {
padding: var(--spacing-sm) var(--spacing-xs); padding: var(--spacing-sm) var(--spacing-xs);
background-color: var(--bg-surface); background-color: var(--bg-surface-t1);
border-top: 1px solid var(--border); border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
@@ -174,7 +174,7 @@
============================================ */ ============================================ */
.planes-panel-section { .planes-panel-section {
padding: var(--spacing-md) var(--spacing-md); padding: var(--spacing-md) var(--spacing-md);
background-color: var(--bg-base); background-color: transparent;
min-height: 600px; min-height: 600px;
} }
@@ -419,7 +419,7 @@
============================================ */ ============================================ */
.planes-comparativa-section { .planes-comparativa-section {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: transparent;
} }
.comparativa-container { .comparativa-container {
@@ -517,7 +517,7 @@
============================================ */ ============================================ */
.planes-cta { .planes-cta {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: transparent;
} }
.planes-cta-card { .planes-cta-card {
+9 -11
View File
@@ -3,7 +3,7 @@
============================================ */ ============================================ */
.servicios-hero { .servicios-hero {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: transparent;
min-height: 70vh; min-height: 70vh;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -71,14 +71,12 @@
============================================ */ ============================================ */
.servicios-selector-section { .servicios-selector-section {
padding: var(--spacing-md) var(--spacing-sm); padding: var(--spacing-md) var(--spacing-sm);
background-color: var(--bg-surface); background-color: var(--bg-base-t1);
border-top: 1px solid var(--border); border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
[data-theme="dark"] .servicios-selector-section {
background-color: rgba(45, 32, 52, 0.95);
}
.servicios-selector-container { .servicios-selector-container {
max-width: 1200px; max-width: 1200px;
@@ -195,7 +193,7 @@
============================================ */ ============================================ */
.servicios-panel-section { .servicios-panel-section {
padding: var(--spacing-md) var(--spacing-xs); padding: var(--spacing-md) var(--spacing-xs);
background-color: var(--bg-base); background-color: transparent;
min-height: 400px; min-height: 400px;
} }
@@ -462,7 +460,7 @@
============================================ */ ============================================ */
.servicios-casos { .servicios-casos {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: var(--bg-base-t2);
} }
.servicios-casos-container { .servicios-casos-container {
@@ -510,11 +508,11 @@
.caso-tag { .caso-tag {
font-family: var(--font-small); font-family: var(--font-small);
font-size: 0.7rem; font-size: 0.85rem;
font-weight: 600; font-weight: 500;
letter-spacing: 0.5px; letter-spacing: 0.5px;
text-transform: uppercase; text-transform: uppercase;
color: var(--brand-teal); color: #006d8f;
display: inline-block; display: inline-block;
margin-bottom: var(--spacing-xs); margin-bottom: var(--spacing-xs);
} }
@@ -537,7 +535,7 @@
============================================ */ ============================================ */
.servicios-cta { .servicios-cta {
padding: var(--spacing-lg) var(--spacing-md); padding: var(--spacing-lg) var(--spacing-md);
background-color: var(--bg-base); background-color: transparent;
} }
.servicios-cta-card { .servicios-cta-card {
+3
View File
@@ -0,0 +1,3 @@
<?php
echo "¡PHP funciona!";
?>
+76
View File
@@ -0,0 +1,76 @@
<?php
/**
* Funciones de sanitización y validación reutilizables.
*/
function limpiar_texto($valor, $max = 500) {
$valor = trim((string)$valor);
$valor = strip_tags($valor);
$valor = preg_replace('/[\r\n\t]+/', ' ', $valor);
if (mb_strlen($valor) > $max) {
$valor = mb_substr($valor, 0, $max);
}
return $valor;
}
function limpiar_mensaje($valor, $max = 5000) {
$valor = trim((string)$valor);
$valor = strip_tags($valor);
if (mb_strlen($valor) > $max) {
$valor = mb_substr($valor, 0, $max);
}
return $valor;
}
function limpiar_email($valor) {
return filter_var(trim((string)$valor), FILTER_SANITIZE_EMAIL);
}
function email_valido($valor) {
return (bool) filter_var($valor, FILTER_VALIDATE_EMAIL);
}
function en_lista($valor, array $permitidos) {
return in_array($valor, $permitidos, true) ? $valor : '';
}
/**
* Valida los datos del formulario y devuelve
* ['ok' => bool, 'errores' => [], 'datos' => []]
*/
function validar_contacto(array $post) {
$errores = [];
$datos = [
'nombre' => limpiar_texto($post['nombre'] ?? '', 120),
'empresa' => limpiar_texto($post['empresa'] ?? '', 120),
'email' => limpiar_email($post['email'] ?? ''),
'telefono' => limpiar_texto($post['telefono'] ?? '', 40),
'servicio' => en_lista($post['servicio'] ?? '', [
'branding','web','rrss','automatizaciones','paid','ecosistema','otro'
]),
'etapa' => en_lista($post['etapa'] ?? '', [
'idea','inicio','marcha','redisenio','escalar'
]),
'presupuesto' => en_lista($post['presupuesto'] ?? '', [
'esencial','impulso','premium','elite','proyecto','no-se'
]),
'mensaje' => limpiar_mensaje($post['mensaje'] ?? '', 5000),
'origen' => en_lista($post['origen'] ?? '', [
'instagram','linkedin','referido','google','otro'
]),
'consentimiento'=> !empty($post['consentimiento']),
];
if ($datos['nombre'] === '') $errores[] = 'El nombre es obligatorio.';
if (!email_valido($datos['email'])) $errores[] = 'El email no es válido.';
if ($datos['servicio'] === '') $errores[] = 'Seleccioná un servicio de interés.';
if ($datos['mensaje'] === '') $errores[] = 'El mensaje es obligatorio.';
if (!$datos['consentimiento']) $errores[] = 'Debés aceptar el uso de tus datos.';
return [
'ok' => empty($errores),
'errores' => $errores,
'datos' => $datos,
];
}
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<!-- Eliminamos el bloque <handlers> manual para que no choque con DonWeb -->
<!-- 1. DOCUMENTO PREDETERMINADO -->
<defaultDocument>
<files>
<clear />
<add value="index.php" />
<add value="index.html" />
</files>
</defaultDocument>
<!-- 2. MODO DETALLADO PARA DIAGNÓSTICO -->
<httpErrors errorMode="Detailed" />
</system.webServer>
</configuration>