commit 5042721e02e24a0f8805ef4f52d9de08acf8adb6 Author: Patricia Pugliese Date: Thu Aug 20 22:45:27 2026 -0300 Update_02 diff --git a/fluid.js b/fluid.js new file mode 100644 index 0000000..4d1e2dd --- /dev/null +++ b/fluid.js @@ -0,0 +1,558 @@ +"use strict"; + +// ===== CONFIGURACIÓN DEL EFECTO ===== +const config = { + TEXTURE_DOWNSAMPLE: 1, + DENSITY_DISSIPATION: 0.975, + VELOCITY_DISSIPATION: 0.98, + PRESSURE_DISSIPATION: 0.8, + PRESSURE_ITERATIONS: 20, + CURL: 30, + SPLAT_RADIUS: 0.003 +}; + +// ===== INICIALIZAR CANVAS ===== +let canvas = document.getElementById("fluidCanvas"); +console.log("Canvas encontrado:", canvas); +if (!canvas) { + console.error("ERROR: No se encontró el canvas con id 'fluidCanvas'"); +} +canvas.width = window.innerWidth; +canvas.height = window.innerHeight; + +// ===== CONTEXTO WEBGL ===== +let pointers = []; +let splatStack = []; + +function getWebGLContext(canvas) { + let params = { + alpha: false, + depth: false, + stencil: false, + antialias: false + }; + let gl = canvas.getContext("webgl2", params); + let isWebGL2 = !!gl; + if (!isWebGL2) + gl = + canvas.getContext("webgl", params) || + canvas.getContext("experimental-webgl", params); + let halfFloat = gl.getExtension("OES_texture_half_float"); + let support_linear_float = gl.getExtension("OES_texture_half_float_linear"); + if (isWebGL2) { + gl.getExtension("EXT_color_buffer_float"); + support_linear_float = gl.getExtension("OES_texture_float_linear"); + } + gl.clearColor(0.0, 0.0, 0.0, 1.0); + let internalFormat = isWebGL2 ? gl.RGBA16F : gl.RGBA; + let internalFormatRG = isWebGL2 ? gl.RG16F : gl.RGBA; + let formatRG = isWebGL2 ? gl.RG : gl.RGBA; + let texType = isWebGL2 ? gl.HALF_FLOAT : halfFloat.HALF_FLOAT_OES; + return { + gl: gl, + ext: { + internalFormat: internalFormat, + internalFormatRG: internalFormatRG, + formatRG: formatRG, + texType: texType + }, + support_linear_float: support_linear_float + }; +} + +let _getWebGLContext = getWebGLContext(canvas); +let gl = _getWebGLContext.gl; +let ext = _getWebGLContext.ext; +let support_linear_float = _getWebGLContext.support_linear_float; + +// ===== POINTER ===== +function pointerPrototype() { + this.id = -1; + this.x = 0; + this.y = 0; + this.dx = 0; + this.dy = 0; + this.down = false; + this.moved = false; + this.color = [30, 0, 300]; +} +pointers.push(new pointerPrototype()); + +// ===== SHADERS ===== +let GLProgram = (function () { + function GLProgram(vertexShader, fragmentShader) { + if (!(this instanceof GLProgram)) + throw new TypeError("Cannot call a class as a function"); + this.uniforms = {}; + this.program = gl.createProgram(); + gl.attachShader(this.program, vertexShader); + gl.attachShader(this.program, fragmentShader); + gl.linkProgram(this.program); + if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) + throw gl.getProgramInfoLog(this.program); + let uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS); + for (let i = 0; i < uniformCount; i++) { + let uniformName = gl.getActiveUniform(this.program, i).name; + this.uniforms[uniformName] = gl.getUniformLocation( + this.program, + uniformName + ); + } + } + GLProgram.prototype.bind = function bind() { + gl.useProgram(this.program); + }; + return GLProgram; +})(); + +function compileShader(type, source) { + let shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) + throw gl.getShaderInfoLog(shader); + return shader; +} + +// ===== VERTEX SHADER ===== +let baseVertexShader = compileShader( + 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); }" +); + +// ===== FRAGMENT SHADERS ===== +let clearShader = compileShader( + 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); }" +); + +let displayShader = compileShader( + gl.FRAGMENT_SHADER, + "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTexture; void main () { gl_FragColor = texture2D(uTexture, vUv); }" +); + +let splatShader = compileShader( + 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); }" +); + +let advectionManualFilteringShader = compileShader( + 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; }" +); + +let advectionShader = compileShader( + 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); }" +); + +let divergenceShader = compileShader( + 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); }" +); + +let curlShader = compileShader( + 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); }" +); + +let vorticityShader = compileShader( + 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); }" +); + +let pressureShader = compileShader( + 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); }" +); + +let gradientSubtractShader = compileShader( + 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); }" +); + +// ===== FRAMEBUFFERS ===== +let textureWidth, textureHeight; +let density, velocity, divergence, curl, pressure; + +function initFramebuffers() { + textureWidth = gl.drawingBufferWidth >> config.TEXTURE_DOWNSAMPLE; + textureHeight = gl.drawingBufferHeight >> config.TEXTURE_DOWNSAMPLE; + let iFormat = ext.internalFormat; + let iFormatRG = ext.internalFormatRG; + let formatRG = ext.formatRG; + let texType = ext.texType; + density = createDoubleFBO( + 0, + textureWidth, + textureHeight, + iFormat, + 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) { + gl.activeTexture(gl.TEXTURE0 + texId); + let texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, param); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, param); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, w, h, 0, format, type, null); + let fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, + texture, + 0 + ); + gl.viewport(0, 0, w, h); + gl.clear(gl.COLOR_BUFFER_BIT); + return [texture, fbo, texId]; +} + +function createDoubleFBO(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); + return { + get first() { + return fbo1; + }, + get second() { + return fbo2; + }, + swap: function swap() { + let temp = fbo1; + fbo1 = fbo2; + fbo2 = temp; + } + }; +} + +initFramebuffers(); + +// ===== PROGRAMS ===== +let clearProgram = new GLProgram(baseVertexShader, clearShader); +let displayProgram = new GLProgram(baseVertexShader, displayShader); +let splatProgram = new GLProgram(baseVertexShader, splatShader); +let advectionProgram = new GLProgram( + baseVertexShader, + support_linear_float ? advectionShader : advectionManualFilteringShader +); +let divergenceProgram = new GLProgram(baseVertexShader, divergenceShader); +let curlProgram = new GLProgram(baseVertexShader, curlShader); +let vorticityProgram = new GLProgram(baseVertexShader, vorticityShader); +let pressureProgram = new GLProgram(baseVertexShader, pressureShader); +let gradienSubtractProgram = new GLProgram( + baseVertexShader, + gradientSubtractShader +); + +// ===== BLIT ===== +let blit = (function () { + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); + gl.bufferData( + 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.bufferData( + 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.enableVertexAttribArray(0); + return function (destination) { + gl.bindFramebuffer(gl.FRAMEBUFFER, destination); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + }; +})(); + +// ===== SPLAT ===== +function splat(x, y, dx, dy, color) { + splatProgram.bind(); + gl.uniform1i(splatProgram.uniforms.uTarget, velocity.first[2]); + gl.uniform1f(splatProgram.uniforms.aspectRatio, canvas.width / canvas.height); + gl.uniform2f( + splatProgram.uniforms.point, + x / canvas.width, + 1.0 - y / canvas.height + ); + gl.uniform3f(splatProgram.uniforms.color, dx, -dy, 1.0); + gl.uniform1f(splatProgram.uniforms.radius, config.SPLAT_RADIUS); + blit(velocity.second[1]); + velocity.swap(); + gl.uniform1i(splatProgram.uniforms.uTarget, density.first[2]); + gl.uniform3f( + splatProgram.uniforms.color, + color[0] * 0.3, + color[1] * 0.3, + color[2] * 0.3 + ); + blit(density.second[1]); + density.swap(); +} + +// ===== RESIZE ===== +function resizeCanvas() { + (canvas.width !== canvas.clientWidth || + canvas.height !== canvas.clientHeight) && + ((canvas.width = canvas.clientWidth), + (canvas.height = canvas.clientHeight), + 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 count = 0; + +// ===== EVENTOS CON COLORES DE MARCA ===== +canvas.addEventListener("mousemove", function (e) { + console.log("Mouse moviéndose:", e.clientX, e.clientY); + count++; + if (count > 1) { + transitionProgress += transitionSpeed; + if (transitionProgress >= 1) { + transitionProgress = 0; + let currentIndex = brandColors.indexOf(currentColor); + let nextIndex = (currentIndex + 1) % brandColors.length; + currentColor = targetColor; + targetColor = brandColors[nextIndex]; + } + colorArr = lerpColor(currentColor, targetColor, transitionProgress); + count = 0; + } + + pointers[0].down = true; + pointers[0].color = colorArr; + pointers[0].moved = pointers[0].down; + pointers[0].dx = (e.offsetX - pointers[0].x) * 10.0; + pointers[0].dy = (e.offsetY - pointers[0].y) * 10.0; + pointers[0].x = e.offsetX; + pointers[0].y = e.offsetY; +}); + +canvas.addEventListener( + "touchmove", + function (e) { + e.preventDefault(); + let touches = e.targetTouches; + count++; + if (count > 20) { + transitionProgress += transitionSpeed; + if (transitionProgress >= 1) { + transitionProgress = 0; + let currentIndex = brandColors.indexOf(currentColor); + let nextIndex = (currentIndex + 1) % brandColors.length; + currentColor = targetColor; + targetColor = brandColors[nextIndex]; + } + colorArr = lerpColor(currentColor, targetColor, transitionProgress); + count = 0; + } + + for (let i = 0, len = touches.length; i < len; i++) { + if (i >= pointers.length) pointers.push(new pointerPrototype()); + pointers[i].id = touches[i].identifier; + pointers[i].down = true; + pointers[i].x = touches[i].pageX; + pointers[i].y = touches[i].pageY; + pointers[i].color = colorArr; + let pointer = pointers[i]; + pointer.moved = pointer.down; + pointer.dx = (touches[i].pageX - pointer.x) * 10.0; + pointer.dy = (touches[i].pageY - pointer.y) * 10.0; + pointer.x = touches[i].pageX; + pointer.y = touches[i].pageY; + } + }, + false +); + +// ===== LOOP PRINCIPAL ===== +let lastTime = Date.now(); + +function update() { + resizeCanvas(); + let dt = Math.min((Date.now() - lastTime) / 1000, 0.016); + lastTime = Date.now(); + gl.viewport(0, 0, textureWidth, textureHeight); + + advectionProgram.bind(); + gl.uniform2f( + advectionProgram.uniforms.texelSize, + 1.0 / textureWidth, + 1.0 / textureHeight + ); + gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.first[2]); + gl.uniform1i(advectionProgram.uniforms.uSource, velocity.first[2]); + gl.uniform1f(advectionProgram.uniforms.dt, dt); + gl.uniform1f( + advectionProgram.uniforms.dissipation, + config.VELOCITY_DISSIPATION + ); + blit(velocity.second[1]); + velocity.swap(); + + gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.first[2]); + gl.uniform1i(advectionProgram.uniforms.uSource, density.first[2]); + gl.uniform1f( + advectionProgram.uniforms.dissipation, + config.DENSITY_DISSIPATION + ); + blit(density.second[1]); + density.swap(); + + for (let i = 0, len = pointers.length; i < len; i++) { + let pointer = pointers[i]; + if (pointer.moved) { + splat(pointer.x, pointer.y, pointer.dx, pointer.dy, pointer.color); + pointer.moved = false; + } + } + + curlProgram.bind(); + gl.uniform2f( + curlProgram.uniforms.texelSize, + 1.0 / textureWidth, + 1.0 / textureHeight + ); + gl.uniform1i(curlProgram.uniforms.uVelocity, velocity.first[2]); + blit(curl[1]); + + vorticityProgram.bind(); + gl.uniform2f( + vorticityProgram.uniforms.texelSize, + 1.0 / textureWidth, + 1.0 / textureHeight + ); + gl.uniform1i(vorticityProgram.uniforms.uVelocity, velocity.first[2]); + gl.uniform1i(vorticityProgram.uniforms.uCurl, curl[2]); + gl.uniform1f(vorticityProgram.uniforms.curl, config.CURL); + gl.uniform1f(vorticityProgram.uniforms.dt, dt); + blit(velocity.second[1]); + velocity.swap(); + + divergenceProgram.bind(); + gl.uniform2f( + divergenceProgram.uniforms.texelSize, + 1.0 / textureWidth, + 1.0 / textureHeight + ); + gl.uniform1i(divergenceProgram.uniforms.uVelocity, velocity.first[2]); + blit(divergence[1]); + + clearProgram.bind(); + let pressureTexId = pressure.first[2]; + gl.activeTexture(gl.TEXTURE0 + pressureTexId); + gl.bindTexture(gl.TEXTURE_2D, pressure.first[0]); + gl.uniform1i(clearProgram.uniforms.uTexture, pressureTexId); + gl.uniform1f(clearProgram.uniforms.value, config.PRESSURE_DISSIPATION); + blit(pressure.second[1]); + pressure.swap(); + + pressureProgram.bind(); + gl.uniform2f( + pressureProgram.uniforms.texelSize, + 1.0 / textureWidth, + 1.0 / textureHeight + ); + gl.uniform1i(pressureProgram.uniforms.uDivergence, divergence[2]); + pressureTexId = pressure.first[2]; + gl.activeTexture(gl.TEXTURE0 + pressureTexId); + for (let _i = 0; _i < config.PRESSURE_ITERATIONS; _i++) { + gl.bindTexture(gl.TEXTURE_2D, pressure.first[0]); + gl.uniform1i(pressureProgram.uniforms.uPressure, pressureTexId); + blit(pressure.second[1]); + pressure.swap(); + } + + gradienSubtractProgram.bind(); + gl.uniform2f( + gradienSubtractProgram.uniforms.texelSize, + 1.0 / textureWidth, + 1.0 / textureHeight + ); + gl.uniform1i(gradienSubtractProgram.uniforms.uPressure, pressure.first[2]); + gl.uniform1i(gradienSubtractProgram.uniforms.uVelocity, velocity.first[2]); + blit(velocity.second[1]); + velocity.swap(); + + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + displayProgram.bind(); + gl.uniform1i(displayProgram.uniforms.uTexture, density.first[2]); + blit(null); + + requestAnimationFrame(update); +} + +update(); + +// ===== REDIMENSIONAR AL CAMBIAR TAMAÑO DE VENTANA ===== +window.addEventListener("resize", function() { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + initFramebuffers(); +}); \ No newline at end of file diff --git a/fonts/Jost.woff2/jost-v20-latin.zip b/fonts/Jost.woff2/jost-v20-latin.zip new file mode 100644 index 0000000..b7f250b Binary files /dev/null and b/fonts/Jost.woff2/jost-v20-latin.zip differ diff --git a/fonts/Kaleagnetta.ttf b/fonts/Kaleagnetta.ttf new file mode 100644 index 0000000..c47c98e Binary files /dev/null and b/fonts/Kaleagnetta.ttf differ diff --git a/fonts/Kaleagnetta.woff2 b/fonts/Kaleagnetta.woff2 new file mode 100644 index 0000000..8b002b3 Binary files /dev/null and b/fonts/Kaleagnetta.woff2 differ diff --git a/fonts/Raleway.woff2/raleway-v37-latin.zip b/fonts/Raleway.woff2/raleway-v37-latin.zip new file mode 100644 index 0000000..5eebde2 Binary files /dev/null and b/fonts/Raleway.woff2/raleway-v37-latin.zip differ diff --git a/fonts/jost-v20-latin-100.woff2 b/fonts/jost-v20-latin-100.woff2 new file mode 100644 index 0000000..509b5d0 Binary files /dev/null and b/fonts/jost-v20-latin-100.woff2 differ diff --git a/fonts/jost-v20-latin-100italic.woff2 b/fonts/jost-v20-latin-100italic.woff2 new file mode 100644 index 0000000..43db446 Binary files /dev/null and b/fonts/jost-v20-latin-100italic.woff2 differ diff --git a/fonts/jost-v20-latin-200.woff2 b/fonts/jost-v20-latin-200.woff2 new file mode 100644 index 0000000..677f5f1 Binary files /dev/null and b/fonts/jost-v20-latin-200.woff2 differ diff --git a/fonts/jost-v20-latin-200italic.woff2 b/fonts/jost-v20-latin-200italic.woff2 new file mode 100644 index 0000000..5b8d0bb Binary files /dev/null and b/fonts/jost-v20-latin-200italic.woff2 differ diff --git a/fonts/jost-v20-latin-300.woff2 b/fonts/jost-v20-latin-300.woff2 new file mode 100644 index 0000000..5298f65 Binary files /dev/null and b/fonts/jost-v20-latin-300.woff2 differ diff --git a/fonts/jost-v20-latin-300italic.woff2 b/fonts/jost-v20-latin-300italic.woff2 new file mode 100644 index 0000000..ebed537 Binary files /dev/null and b/fonts/jost-v20-latin-300italic.woff2 differ diff --git a/fonts/jost-v20-latin-500.woff2 b/fonts/jost-v20-latin-500.woff2 new file mode 100644 index 0000000..80e9c5b Binary files /dev/null and b/fonts/jost-v20-latin-500.woff2 differ diff --git a/fonts/jost-v20-latin-500italic.woff2 b/fonts/jost-v20-latin-500italic.woff2 new file mode 100644 index 0000000..d5ebb2e Binary files /dev/null and b/fonts/jost-v20-latin-500italic.woff2 differ diff --git a/fonts/jost-v20-latin-600.woff2 b/fonts/jost-v20-latin-600.woff2 new file mode 100644 index 0000000..d083772 Binary files /dev/null and b/fonts/jost-v20-latin-600.woff2 differ diff --git a/fonts/jost-v20-latin-600italic.woff2 b/fonts/jost-v20-latin-600italic.woff2 new file mode 100644 index 0000000..7b46092 Binary files /dev/null and b/fonts/jost-v20-latin-600italic.woff2 differ diff --git a/fonts/jost-v20-latin-700.woff2 b/fonts/jost-v20-latin-700.woff2 new file mode 100644 index 0000000..3d3e683 Binary files /dev/null and b/fonts/jost-v20-latin-700.woff2 differ diff --git a/fonts/jost-v20-latin-700italic.woff2 b/fonts/jost-v20-latin-700italic.woff2 new file mode 100644 index 0000000..eb3d8ed Binary files /dev/null and b/fonts/jost-v20-latin-700italic.woff2 differ diff --git a/fonts/jost-v20-latin-800.woff2 b/fonts/jost-v20-latin-800.woff2 new file mode 100644 index 0000000..9283e11 Binary files /dev/null and b/fonts/jost-v20-latin-800.woff2 differ diff --git a/fonts/jost-v20-latin-800italic.woff2 b/fonts/jost-v20-latin-800italic.woff2 new file mode 100644 index 0000000..996e37c Binary files /dev/null and b/fonts/jost-v20-latin-800italic.woff2 differ diff --git a/fonts/jost-v20-latin-900.woff2 b/fonts/jost-v20-latin-900.woff2 new file mode 100644 index 0000000..6a87996 Binary files /dev/null and b/fonts/jost-v20-latin-900.woff2 differ diff --git a/fonts/jost-v20-latin-900italic.woff2 b/fonts/jost-v20-latin-900italic.woff2 new file mode 100644 index 0000000..2859bfd Binary files /dev/null and b/fonts/jost-v20-latin-900italic.woff2 differ diff --git a/fonts/jost-v20-latin-italic.woff2 b/fonts/jost-v20-latin-italic.woff2 new file mode 100644 index 0000000..1539de1 Binary files /dev/null and b/fonts/jost-v20-latin-italic.woff2 differ diff --git a/fonts/jost-v20-latin-regular.woff2 b/fonts/jost-v20-latin-regular.woff2 new file mode 100644 index 0000000..a93b79d Binary files /dev/null and b/fonts/jost-v20-latin-regular.woff2 differ diff --git a/fonts/literata-v40-latin-200.woff2 b/fonts/literata-v40-latin-200.woff2 new file mode 100644 index 0000000..3445595 Binary files /dev/null and b/fonts/literata-v40-latin-200.woff2 differ diff --git a/fonts/literata-v40-latin-200italic.woff2 b/fonts/literata-v40-latin-200italic.woff2 new file mode 100644 index 0000000..76feefd Binary files /dev/null and b/fonts/literata-v40-latin-200italic.woff2 differ diff --git a/fonts/literata-v40-latin-300.woff2 b/fonts/literata-v40-latin-300.woff2 new file mode 100644 index 0000000..93a1b22 Binary files /dev/null and b/fonts/literata-v40-latin-300.woff2 differ diff --git a/fonts/literata-v40-latin-300italic.woff2 b/fonts/literata-v40-latin-300italic.woff2 new file mode 100644 index 0000000..909f5e2 Binary files /dev/null and b/fonts/literata-v40-latin-300italic.woff2 differ diff --git a/fonts/literata-v40-latin-500.woff2 b/fonts/literata-v40-latin-500.woff2 new file mode 100644 index 0000000..2bc9f86 Binary files /dev/null and b/fonts/literata-v40-latin-500.woff2 differ diff --git a/fonts/literata-v40-latin-500italic.woff2 b/fonts/literata-v40-latin-500italic.woff2 new file mode 100644 index 0000000..48fa315 Binary files /dev/null and b/fonts/literata-v40-latin-500italic.woff2 differ diff --git a/fonts/literata-v40-latin-600.woff2 b/fonts/literata-v40-latin-600.woff2 new file mode 100644 index 0000000..b9ee39e Binary files /dev/null and b/fonts/literata-v40-latin-600.woff2 differ diff --git a/fonts/literata-v40-latin-600italic.woff2 b/fonts/literata-v40-latin-600italic.woff2 new file mode 100644 index 0000000..c9f78bc Binary files /dev/null and b/fonts/literata-v40-latin-600italic.woff2 differ diff --git a/fonts/literata-v40-latin-700.woff2 b/fonts/literata-v40-latin-700.woff2 new file mode 100644 index 0000000..fa3ab3f Binary files /dev/null and b/fonts/literata-v40-latin-700.woff2 differ diff --git a/fonts/literata-v40-latin-700italic.woff2 b/fonts/literata-v40-latin-700italic.woff2 new file mode 100644 index 0000000..75b8ea6 Binary files /dev/null and b/fonts/literata-v40-latin-700italic.woff2 differ diff --git a/fonts/literata-v40-latin-800.woff2 b/fonts/literata-v40-latin-800.woff2 new file mode 100644 index 0000000..293f6ae Binary files /dev/null and b/fonts/literata-v40-latin-800.woff2 differ diff --git a/fonts/literata-v40-latin-800italic.woff2 b/fonts/literata-v40-latin-800italic.woff2 new file mode 100644 index 0000000..f38c480 Binary files /dev/null and b/fonts/literata-v40-latin-800italic.woff2 differ diff --git a/fonts/literata-v40-latin-900.woff2 b/fonts/literata-v40-latin-900.woff2 new file mode 100644 index 0000000..81d4236 Binary files /dev/null and b/fonts/literata-v40-latin-900.woff2 differ diff --git a/fonts/literata-v40-latin-900italic.woff2 b/fonts/literata-v40-latin-900italic.woff2 new file mode 100644 index 0000000..5a1ac78 Binary files /dev/null and b/fonts/literata-v40-latin-900italic.woff2 differ diff --git a/fonts/literata-v40-latin-italic.woff2 b/fonts/literata-v40-latin-italic.woff2 new file mode 100644 index 0000000..bf18a84 Binary files /dev/null and b/fonts/literata-v40-latin-italic.woff2 differ diff --git a/fonts/literata-v40-latin-regular.woff2 b/fonts/literata-v40-latin-regular.woff2 new file mode 100644 index 0000000..15615be Binary files /dev/null and b/fonts/literata-v40-latin-regular.woff2 differ diff --git a/fonts/raleway-v37-latin-100.woff2 b/fonts/raleway-v37-latin-100.woff2 new file mode 100644 index 0000000..59e1c86 Binary files /dev/null and b/fonts/raleway-v37-latin-100.woff2 differ diff --git a/fonts/raleway-v37-latin-100italic.woff2 b/fonts/raleway-v37-latin-100italic.woff2 new file mode 100644 index 0000000..779a639 Binary files /dev/null and b/fonts/raleway-v37-latin-100italic.woff2 differ diff --git a/fonts/raleway-v37-latin-200.woff2 b/fonts/raleway-v37-latin-200.woff2 new file mode 100644 index 0000000..5f473bd Binary files /dev/null and b/fonts/raleway-v37-latin-200.woff2 differ diff --git a/fonts/raleway-v37-latin-200italic.woff2 b/fonts/raleway-v37-latin-200italic.woff2 new file mode 100644 index 0000000..c57473b Binary files /dev/null and b/fonts/raleway-v37-latin-200italic.woff2 differ diff --git a/fonts/raleway-v37-latin-300.woff2 b/fonts/raleway-v37-latin-300.woff2 new file mode 100644 index 0000000..8498009 Binary files /dev/null and b/fonts/raleway-v37-latin-300.woff2 differ diff --git a/fonts/raleway-v37-latin-300italic.woff2 b/fonts/raleway-v37-latin-300italic.woff2 new file mode 100644 index 0000000..4ad216d Binary files /dev/null and b/fonts/raleway-v37-latin-300italic.woff2 differ diff --git a/fonts/raleway-v37-latin-500.woff2 b/fonts/raleway-v37-latin-500.woff2 new file mode 100644 index 0000000..ceda164 Binary files /dev/null and b/fonts/raleway-v37-latin-500.woff2 differ diff --git a/fonts/raleway-v37-latin-500italic.woff2 b/fonts/raleway-v37-latin-500italic.woff2 new file mode 100644 index 0000000..cae089a Binary files /dev/null and b/fonts/raleway-v37-latin-500italic.woff2 differ diff --git a/fonts/raleway-v37-latin-600.woff2 b/fonts/raleway-v37-latin-600.woff2 new file mode 100644 index 0000000..fd77668 Binary files /dev/null and b/fonts/raleway-v37-latin-600.woff2 differ diff --git a/fonts/raleway-v37-latin-600italic.woff2 b/fonts/raleway-v37-latin-600italic.woff2 new file mode 100644 index 0000000..36311a0 Binary files /dev/null and b/fonts/raleway-v37-latin-600italic.woff2 differ diff --git a/fonts/raleway-v37-latin-700.woff2 b/fonts/raleway-v37-latin-700.woff2 new file mode 100644 index 0000000..90960e6 Binary files /dev/null and b/fonts/raleway-v37-latin-700.woff2 differ diff --git a/fonts/raleway-v37-latin-700italic.woff2 b/fonts/raleway-v37-latin-700italic.woff2 new file mode 100644 index 0000000..1631551 Binary files /dev/null and b/fonts/raleway-v37-latin-700italic.woff2 differ diff --git a/fonts/raleway-v37-latin-800.woff2 b/fonts/raleway-v37-latin-800.woff2 new file mode 100644 index 0000000..b6b2e88 Binary files /dev/null and b/fonts/raleway-v37-latin-800.woff2 differ diff --git a/fonts/raleway-v37-latin-800italic.woff2 b/fonts/raleway-v37-latin-800italic.woff2 new file mode 100644 index 0000000..2468448 Binary files /dev/null and b/fonts/raleway-v37-latin-800italic.woff2 differ diff --git a/fonts/raleway-v37-latin-900.woff2 b/fonts/raleway-v37-latin-900.woff2 new file mode 100644 index 0000000..7b9fd79 Binary files /dev/null and b/fonts/raleway-v37-latin-900.woff2 differ diff --git a/fonts/raleway-v37-latin-900italic.woff2 b/fonts/raleway-v37-latin-900italic.woff2 new file mode 100644 index 0000000..8a352d5 Binary files /dev/null and b/fonts/raleway-v37-latin-900italic.woff2 differ diff --git a/fonts/raleway-v37-latin-italic.woff2 b/fonts/raleway-v37-latin-italic.woff2 new file mode 100644 index 0000000..4d7a49e Binary files /dev/null and b/fonts/raleway-v37-latin-italic.woff2 differ diff --git a/fonts/raleway-v37-latin-regular.woff2 b/fonts/raleway-v37-latin-regular.woff2 new file mode 100644 index 0000000..b8dd014 Binary files /dev/null and b/fonts/raleway-v37-latin-regular.woff2 differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..9d01c9b --- /dev/null +++ b/index.html @@ -0,0 +1,674 @@ + + + + + + Amor & Roma — Estudio Creativo + + + + + + + + +
+ + + + + +
+ + +
+ + +
+
+
Estudio Creativo · Agencia Boutique
+

Marcas con esencia,
construidas con estrategia.

+

Branding, diseño web y marketing digital para negocios que quieren crecer sin perder lo que los hace únicos.

+ +
+ ✦ +
Scroll
+
+ + +
+
Lo que hacemos
+

Servicios pensados para marcas con esencia.

+

Cuatro disciplinas que trabajan juntas, no por separado — cada proyecto se arma con la combinación justa para tu marca.

+
+ + + + +
+
+
+

Identidad con alma,
no solo con estilo.

+

Construimos el sistema visual y verbal que sostiene tu marca: desde el naming y el logo hasta el tono de voz con el que hablás en cada canal.

+ Quiero mi identidad de marca → +
+
+
✦ Naming y concepto de marca
+
✦ Diseño de logo e isotipo
+
✦ Paleta, tipografía y manual de marca
+
✦ Tono de voz y guía de comunicación
+
+
+
+
+

Sitios que convierten,
no solo que se ven bien.

+

Diseño y desarrollo de sitios web a medida — rápidos, responsivos y pensados para que quien llega, actúe.

+ Quiero mi sitio web → +
+
+
✦ Landing pages y sitios institucionales
+
✦ UX/UI a medida
+
✦ Optimización SEO técnica
+
✦ E-commerce y tiendas online
+
+
+
+
+

Presencia constante,
estrategia real.

+

Gestión de redes, contenido y pauta — con reportes claros para que sepas siempre qué está funcionando y por qué.

+ Quiero mi estrategia digital → +
+
+
✦ Community management
+
✦ Publicidad en Meta e Instagram Ads
+
✦ Estrategia de contenido y calendario
+
✦ Reportes mensuales de performance
+
+
+
+
+

Tecnología al servicio
de tu tiempo.

+

Automatizamos lo repetitivo para que tu equipo se enfoque en lo estratégico — sin perder el trato cercano con tus clientes.

+ Quiero automatizar procesos → +
+
+
✦ Chatbots y respuestas automáticas
+
✦ Automatización de leads y seguimiento
+
✦ Integraciones con CRM y WhatsApp
+
✦ Consultoría en IA aplicada a tu negocio
+
+
+ ✦ +
+ + +
+
Inversión
+

Un plan para cada etapa de tu marca.

+

Cuatro niveles pensados para acompañar tu crecimiento — desde presencia digital consistente hasta gestión integral de marca.

+
+
+
Esencial
+
★
+
Establecer una presencia digital profesional y consistente.
+
    +
  • 2 redes sociales
  • +
  • 8 publicaciones + 8 historias / mes
  • +
  • 1 diseño de marca (logo)
  • +
  • Reunión mensual de estrategia
  • +
  • Reporte mensual de métricas
  • +
+ +Info +
+
+
Impulso
+
★★
+
Aumentar visibilidad, interacción y generación de leads calificados.
+
    +
  • 2 redes · 12 publicaciones + 4 reels
  • +
  • Logo + paleta de colores
  • +
  • 1 campaña Meta Ads + retargeting
  • +
  • Automatización de respuestas frecuentes
  • +
  • Gestión de Meta Business Suite
  • +
+ +Info +
+
+
Más Popular
+
Premium
+
★★★
+
Consolidar y expandir la empresa comercialmente.
+
    +
  • 3 redes · 16 publicaciones + 8 reels
  • +
  • Sesión de fotos/video quincenal
  • +
  • 2 campañas Meta Ads + Google Ads
  • +
  • Respuestas con IA 24/7
  • +
  • Reporte semanal + reunión mensual
  • +
  • Soporte prioritario
  • +
+ +Info +
+
+
Elite
+
★★★★
+
Máximo posicionamiento y gestión integral de marca.
+
    +
  • 4 redes · 20 publicaciones + 16 reels
  • +
  • Dirección creativa integral de marca
  • +
  • 3 campañas + TikTok Ads + Google Ads
  • +
  • Estrategia 360° con planificación mensual
  • +
  • Gestión de reputación online
  • +
+ +Info +
+
+ Consulta tu plan ideal + ✦ +
+ + +
+
Servicios a la carta
+

Para complementar cualquier plan,
o contratar de forma independiente.

+

Elegí los servicios que necesitás, sin compromiso mensual. Todo lo que tu marca necesita, a tu ritmo.

+
+
+
+
01Diseño Web & Desarrollo
+
+
+
+
+
+
Landing Page ProfesionalDiseño de una sección, UI/UX + SEO básico Desde $350.000
+
Sitio Institucional (hasta 7 secciones)Inicio, Servicios, Nosotros, Blog, Contacto Desde $600.000
+
E-commerceTienda online autoadministrable con pasarela de pago Desde $900.000
+
Mantenimiento WebBackups, seguridad y soporte técnico Desde $50.000/mes
+
+
+
+
+
+
02Branding e Identidad Visual
+
+
+
+
+
+
Diseño de Logotipo3 propuestas + revisiones Desde $180.000
+
Manual de Marca CompletoLogo, paleta, tipografías, tono de voz Desde $300.000
+
Rediseño de Identidad VisualActualización de marca existente A cotizar
+
+
+
+
+
+
03Publicidad Digital Avanzada
+
+
+
+
+
+
Campaña Google AdsBúsqueda y display Desde $150.000/mes
+
Campaña TikTok AdsAlcance a audiencias jóvenes Desde $120.000/mes
+
Auditoría de CampañasAnálisis y oportunidades de mejora Desde $90.000
+
+
+
+
+
+
04Diseño Digital & Gráfico
+
+
+
+
+
+
InfografíasComunicación visual de datos y procesos Desde $80.000/u
+
Plantillas para RedesSet editable de 5 piezas Desde $100.000
+
Material ImpresoFlyers, folletos, tarjetas A cotizar
+
+
+
+
+
+
05Producción Audiovisual
+
+
+
+
+
+
Sesión FotográficaProducto, instalaciones o equipo A cotizar
+
Reel / Video CortoHasta 30 segundos, con edición Desde $120.000
+
Pack de 3 ReelsContenido consistente de alto impacto Desde $300.000
+
+
+
+
+ ✦ +
+ + +
+
Cómo trabajamos
+

Tu proyecto, paso a paso, y sin sorpresas.

+

Un proceso claro y colaborativo — sabés exactamente qué pasa en cada etapa, sin plantillas genéricas.

+
+
+
01
+
Contacto
+
Reunión de brief
+
Nos contás tu marca y objetivos.
+
+
+
02
+
Propuesta
+
Plan de trabajo
+
Entregables, inversión y tiempos.
+
+
+
03
+
Diseño
+
Dirección de arte
+
Concepto visual a tu aprobación.
+
+
+
04
+
Producción
+
Desarrollo
+
Construimos con seguimiento.
+
+
+
05
+
Revisión
+
Ajustes finales
+
Tu feedback moldea el resultado.
+
+
+
+
Lanzamiento
+
Entrega
+
Accesos y capacitación completa.
+
+
+ ✦ +
+ + +
+
Inversión & tiempos
+

Transparencia antes de la primera reunión.

+

Los números, dichos de entrada — no como parte de una negociación, sino como punto de partida.

+
+
+
+
Inversión
+
Desde $250.000/mes
+
Planes desde $250.000 (Esencial) hasta $1.000.000 (Elite). El armado inicial —branding, sitio web— se presupuesta por proyecto.
+
+
+
+
Tiempos
+
15–30 días
+
Presencia digital lista en 15-30 días. Resultados de posicionamiento sostenido en 3-6 meses. Sin promesas de magia inmediata.
+
+
+
+
Contrato
+
3 meses · luego mensual
+
Compromiso mínimo de 3 meses para que la estrategia tome forma, luego renovación mensual. Cancelación con 30 días de aviso.
+
+
+ ✦ +
+ + +
+
Cotizador
+

¿Cuál es tu plan ideal?

+

3 preguntas rápidas y te decimos qué plan se ajusta mejor a tu marca.

+
+
+
+
+
+
+
+
Pregunta 01
+
¿Cómo describirías el momento de tu negocio?
+
+
Recién empezando
Necesito presencia digital desde cero
+
En crecimiento
Ya tengo redes, quiero más alcance
+
Establecido
Busco consolidar y vender más
+
Consolidado
Quiero gestión integral de marca
+
+ +
+
+
Pregunta 02
+
¿Qué es lo que más necesitás hoy?
+
+
Presencia en redes
Contenido consistente y profesional
+
Más leads y ventas
Publicidad digital que convierta
+
Producción de contenido
Fotos, video y piezas de calidad
+
Todo integrado
Estrategia 360° gestionada por otros
+
+ +
+
+
Pregunta 03
+
¿Cuál es tu presupuesto mensual aproximado?
+
+
Hasta $300.000
ARS / mes
+
$300.000 – $450.000
ARS / mes
+
$450.000 – $700.000
ARS / mes
+
Más de $700.000
ARS / mes
+
+ +
+
+
Tu plan recomendado
+
—
+
—
+

—

+
+ Quiero este plan + +
+
+
+ ✦ +
+ + +
+
Nosotros
+

Un equipo chico, con mucha energía.

+

Creemos que las marcas se construyen con escucha, estrategia y mucho amor por lo que hacemos.

+
+
+
Patricia Pugliese
+
Estrategia & Creatividad
+
Investigación, branding, identidad visual y todo lo que da forma a una marca con personalidad.
+
+
+
Francisco Anfuso
+
Desarrollo & Tecnología
+
Diseño web, desarrollo, automatizaciones y soluciones técnicas que funcionan sin complicaciones.
+
+
+ + ✦ +
+ + +
+
Preguntas Frecuentes
+

Todo lo que necesitás saber antes de empezar.

+

Respondemos las dudas más comunes para que llegues con confianza al primer encuentro.

+
+
+
+ ¿Cómo funciona el proceso de trabajo? + − +
+

Empezamos con una llamada de descubrimiento para conocer tu negocio, objetivos y desafíos. Luego armamos una propuesta a medida, la ejecutamos con entregas parciales y ajustamos hasta que estés 100% conforme. Todo con comunicación clara y tiempos acordados.

+
+
+
+ ¿Qué incluye exactamente cada plan? + + +
+

Cada plan tiene un desglose detallado en la sección de Planes. Pero en resumen: incluyen gestión de redes, diseño de marca, publicidad digital, automatizaciones y reportes. Si necesitás algo específico que no esté listado, lo sumamos a medida.

+
+
+
+ ¿Puedo contratar servicios sueltos sin un plan mensual? + + +
+

¡Sí! En la sección "Servicios a la carta" tenés disponibles branding, diseño web, campañas puntuales y producción de contenido. Podés contratarlos de forma independiente, sin compromiso mensual.

+
+
+
+ ¿Qué resultados puedo esperar? + + +
+

Depende del punto de partida, pero nuestros clientes suelen ver aumento en alcance, engagement y generación de leads en los primeros 3 meses. Compartimos reportes mensuales para que siempre sepas cómo evoluciona tu marca.

+
+
+
+ ¿Trabajan con negocios de cualquier rubro? + + +
+

Trabajamos con marcas de moda, gastronomía, tecnología, salud, educación y más. Lo importante es que tengas claros tus valores y quieras construir una marca con identidad propia — el rubro es secundario.

+
+
+
+ ¿Cómo facturan? ¿Hay algún costo de inicio? + + +
+

Facturamos con factura electrónica tipo C o E. No hay costo de inicio: los planes se abonan mes a mes, con un 50% de anticipo en proyectos puntuales (como diseño web o branding completo).

+
+
+
+ ¿Puedo cambiar de plan o cancelar cuando quiera? + + +
+

Por supuesto. Podés subir o bajar de plan con 15 días de aviso. Si querés cancelar, también con 15 días de anticipación. Sin letra chica ni permanencias forzadas.

+
+
+ ✦ +
+ + +
+
Contacto
+

¿Empezamos a construir tu marca?

+

Completá el formulario o escribinos directo — te respondemos en menos de 24 horas.

+
+
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ +
+ + + + + + +
+ + +
+
+ + +
+
+
+ +
+ +
+
+
+
+
+
📧
+ +
+
+
💬
+
+
WhatsApp
+ +54 9 388 611-8304 +
+
+
+
📱
+
+
Redes
+ +
+
+
+
📍
+
+
Argentina
+ Trabajamos remoto worldwide +
+
+
+
+ ✦ +
+ + + + + + + + + + \ No newline at end of file diff --git a/logo_ciruela.png b/logo_ciruela.png new file mode 100644 index 0000000..7abd215 Binary files /dev/null and b/logo_ciruela.png differ diff --git a/logo_frio.png b/logo_frio.png new file mode 100644 index 0000000..d7dce29 Binary files /dev/null and b/logo_frio.png differ diff --git a/logos/logo_bordo.png b/logos/logo_bordo.png new file mode 100644 index 0000000..751feb9 Binary files /dev/null and b/logos/logo_bordo.png differ diff --git a/logos/logo_calido.png b/logos/logo_calido.png new file mode 100644 index 0000000..87a2897 Binary files /dev/null and b/logos/logo_calido.png differ diff --git a/logos/logo_simple_bordo.png b/logos/logo_simple_bordo.png new file mode 100644 index 0000000..7b7274b Binary files /dev/null and b/logos/logo_simple_bordo.png differ diff --git a/logos/logo_simple_calido.png b/logos/logo_simple_calido.png new file mode 100644 index 0000000..08a2695 Binary files /dev/null and b/logos/logo_simple_calido.png differ diff --git a/logos/logo_simple_ciruela.png b/logos/logo_simple_ciruela.png new file mode 100644 index 0000000..950e4e4 Binary files /dev/null and b/logos/logo_simple_ciruela.png differ diff --git a/logos/logo_simple_frio.png b/logos/logo_simple_frio.png new file mode 100644 index 0000000..2e0ea0b Binary files /dev/null and b/logos/logo_simple_frio.png differ diff --git a/logos/logo_simple_teal.png b/logos/logo_simple_teal.png new file mode 100644 index 0000000..b9cd833 Binary files /dev/null and b/logos/logo_simple_teal.png differ diff --git a/logos/logo_teal.png b/logos/logo_teal.png new file mode 100644 index 0000000..35a15fc Binary files /dev/null and b/logos/logo_teal.png differ diff --git a/scripts.js b/scripts.js new file mode 100644 index 0000000..14f16d4 --- /dev/null +++ b/scripts.js @@ -0,0 +1,150 @@ +// ===== MENU ===== +function toggleMenu() { + document.getElementById('sheet').classList.toggle('open'); + document.getElementById('overlay').classList.toggle('open'); +} + +// ===== TABS ===== +function showTab(i, el) { + document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); + document.querySelectorAll('.panel').forEach(p => p.classList.remove('active')); + el.classList.add('active'); + document.querySelector('.panel[data-panel="' + i + '"]').classList.add('active'); +} + +// ===== ACCORDION ===== +function toggleAcc(head) { + const item = head.parentElement; + document.querySelectorAll('.acc-item').forEach(i => { if (i !== item) i.classList.remove('open'); }); + item.classList.toggle('open'); +} + +// ===== FAQ ===== +function toggleFaq(head) { + const item = head.parentElement; + const isOpen = item.classList.contains('open'); + document.querySelectorAll('.faq-item').forEach(i => { + i.classList.remove('open'); + const t = i.querySelector('.faq-toggle'); + if (t) t.textContent = '+'; + }); + if (!isOpen) { + item.classList.add('open'); + const toggle = item.querySelector('.faq-toggle'); + if (toggle) toggle.textContent = '−'; + } +} + +// ===== COTIZADOR ===== +const answers = [null, null, null]; +let current = 0; + +document.querySelectorAll('.opt').forEach(opt => { + opt.addEventListener('click', function() { + const group = this.parentElement; + const q = parseInt(group.dataset.q); + group.querySelectorAll('.opt').forEach(o => o.classList.remove('selected')); + this.classList.add('selected'); + answers[q] = parseInt(this.dataset.val); + document.getElementById('next' + q).disabled = false; + const step = this.closest('.step'); + const qNum = step.querySelector('.q-num'); + if (qNum) qNum.classList.add('active'); + }); +}); + +function updateProgress() { + for (let i = 0; i < 3; i++) { + document.getElementById('p' + (i + 1)).style.width = i <= current ? '100%' : '0%'; + } +} + +function next() { + document.querySelector('.step[data-step="' + current + '"]').classList.remove('active'); + current++; + document.querySelector('.step[data-step="' + current + '"]').classList.add('active'); + updateProgress(); +} + +function prev() { + document.querySelector('.step[data-step="' + current + '"]').classList.remove('active'); + current--; + document.querySelector('.step[data-step="' + current + '"]').classList.add('active'); + updateProgress(); +} + +const plans = [ + { name: 'Esencial', price: '$250.000 ARS', desc: 'Ideal para arrancar con presencia digital profesional y consistente, sin sobre-invertir todavía.' }, + { name: 'Impulso', price: '$420.000 ARS', desc: 'Pensado para marcas en crecimiento que necesitan más alcance, leads y una primera campaña de publicidad.' }, + { name: 'Premium', price: '$650.000 ARS + pauta', desc: 'Nuestro plan más recomendado: gestión integral de contenido, publicidad y estrategia para consolidar y expandir.' }, + { name: 'Elite', price: '$1.000.000 ARS', desc: 'Máximo posicionamiento y gestión de marca completa, para negocios listos para escalar en serio.' } +]; + +function showResult() { + document.querySelector('.step[data-step="2"]').classList.remove('active'); + const avg = Math.round((answers[0] + answers[1] + answers[2]) / 3); + const idx = Math.min(Math.max(avg - 1, 0), 3); + document.getElementById('rp-name').textContent = plans[idx].name; + document.getElementById('rp-price').innerHTML = plans[idx].price + ' / mes'; + document.getElementById('rp-desc').textContent = plans[idx].desc; + document.getElementById('result').classList.add('active'); +} + +function restart() { + answers[0] = answers[1] = answers[2] = null; + current = 0; + document.querySelectorAll('.opt').forEach(o => o.classList.remove('selected')); + document.querySelectorAll('[id^="next"]').forEach(b => b.disabled = true); + document.querySelectorAll('.q-num').forEach(q => q.classList.remove('active')); + document.getElementById('result').classList.remove('active'); + document.querySelectorAll('.step').forEach(s => s.classList.remove('active')); + document.querySelector('.step[data-step="0"]').classList.add('active'); + updateProgress(); +} + +// ===== FILTRO DE SERVICIOS EN CONTACTO ===== +function selectFilter(event) { + const el = event.currentTarget; + const container = el.parentElement; + container.querySelectorAll('.filter-tag').forEach(t => t.classList.remove('active')); + el.classList.add('active'); + const value = el.dataset.value; + document.getElementById('servicioSeleccionado').value = value; + const otroInput = document.getElementById('servicioOtro'); + if (value === 'otro') { + otroInput.style.display = 'block'; + otroInput.required = true; + } else { + otroInput.style.display = 'none'; + otroInput.required = false; + otroInput.value = ''; + } +} + +// ===== FORMULARIO ===== +function handleSubmit(e) { + e.preventDefault(); + const form = document.getElementById('contactForm'); + const data = new FormData(form); + const nombre = data.get('nombre'); + const email = data.get('email'); + const mensaje = data.get('mensaje'); + if (!nombre || !email || !mensaje) { + alert('Por favor, completá todos los campos obligatorios.'); + return false; + } + const btn = form.querySelector('button[type="submit"]'); + const originalText = btn.textContent; + btn.textContent = 'Enviando...'; + btn.disabled = true; + setTimeout(() => { + alert('¡Mensaje enviado con éxito! Te responderemos a la brevedad.'); + form.reset(); + btn.textContent = originalText; + btn.disabled = false; + document.querySelectorAll('.filter-tag').forEach(t => t.classList.remove('active')); + document.querySelector('.filter-tag[data-value="todos"]').classList.add('active'); + document.getElementById('servicioOtro').style.display = 'none'; + }, 1500); + return false; +} \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..b8bfe2b --- /dev/null +++ b/styles.css @@ -0,0 +1,650 @@ +/* ===== FUENTES ===== */ +@import url('https://fonts.googleapis.com/css2?family=Literata:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400;1,500;1,600;1,700;1,800&family=Raleway:wght@300;400;500;600;700&family=Jost:wght@300;400;500;600;700&display=swap'); + +/* ===== VARIABLES ===== */ +:root { + --teal: #003F52; + --ligth-teal: #006d8f; + --bordo: #BA124D; + --oscuro: #110916; + --dusty: #a79bab; + --claro: #EAF0EE; + --rosa: #DFBDB9; + --rose-gold-1: #DFBDB9; + --rose-gold-2: #BA8B7A; + --rose-gold-3: #C89A82; + --font-display: 'Literata', serif; + --font-ui: 'Raleway', sans-serif; + --font-body: 'Jost', sans-serif; + --font-name: 'Kaleagnetta', handwriting; +} + +* { box-sizing: border-box; margin: 0; padding: 0; } + +body { + font-family: var(--font-body); + background: var(--oscuro); + color: var(--claro); + overflow-x: hidden; + font-weight: 300; +} + +/* ===== LOGO ===== */ +.logo-img { height: 32px; width: auto; display: block; } + +/* ===== CANVAS FLUIDO ===== */ +#fluidCanvas { + display: block; + width: 100%; + height: 100vh; + position: fixed; + top: 0; + left: 0; + z-index: 0; + pointer-events: auto; + background: var(--oscuro); +} + +/* Asegurar que el contenido esté por encima */ +.hero, section { + position: relative; + z-index: 1; +} + +/* ===== HEADER ===== */ +header { + position: fixed; top: 0; left: 0; right: 0; z-index: 50; + display: flex; align-items: center; justify-content: space-between; + padding: 26px 48px; + background: linear-gradient(180deg, rgba(17,9,22,.9) 0%, rgba(17,9,22,0) 100%); +} +.menu-btn { + display: flex; align-items: center; gap: 10px; + background: transparent; border: 1px solid rgba(234,240,238,.2); + border-radius: 100px; padding: 10px 20px; + color: var(--claro); font-family: var(--font-ui); font-size: 12px; + letter-spacing: .14em; text-transform: uppercase; font-weight: 500; + cursor: pointer; transition: .25s; +} +.menu-btn:hover { border-color: var(--rosa); color: var(--rosa); } +.menu-btn .bars { display: flex; flex-direction: column; gap: 3px; } +.menu-btn .bars span { width: 14px; height: 1.4px; background: currentColor; display: block; } + +/* ===== OVERLAY & SHEET ===== */ +.overlay { + position: fixed; inset: 0; background: rgba(0,0,0,.6); + opacity: 0; pointer-events: none; transition: .35s; z-index: 90; +} +.overlay.open { opacity: 1; pointer-events: auto; } + +.sheet { + position: fixed; left: 0; right: 0; bottom: 0; z-index: 100; + background: var(--claro); border-radius: 22px 22px 0 0; + padding: 10px 36px 30px; + transform: translateY(102%); + transition: transform .4s cubic-bezier(.2,.8,.2,1); + max-height: 55vh; overflow-y: auto; + color: var(--oscuro); box-shadow: 0 -8px 40px rgba(0,0,0,.3); +} +.sheet.open { transform: translateY(0); } +.sheet-handle { width: 44px; height: 4px; background: rgba(34,23,40,.2); border-radius: 100px; margin: 0 auto 16px; } +.sheet-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } +.sheet-top .logo { font-size: 0; } +.sheet-top .logo img { height: 28px; width: auto; } +.close-btn { + width: 32px; height: 32px; border-radius: 100px; + border: 1px solid rgba(34,23,40,.2); + background: transparent; color: var(--oscuro); font-size: 14px; + cursor: pointer; display: flex; align-items: center; justify-content: center; + transition: .2s; +} +.close-btn:hover { border-color: var(--bordo); color: var(--bordo); } + +.sheet-body { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 30px; align-items: start; } +.sheet-col { display: flex; flex-direction: column; gap: 2px; } +.sheet-col-title { + font-family: var(--font-ui); font-size: 16px; font-weight: 500; + letter-spacing: .12em; text-transform: uppercase; + color: var(--bordo); margin-bottom: 8px; padding-right: 12px; text-align: right; +} +.sheet-link { + display: grid; grid-template-columns: 1fr 20px; align-items: center; + gap: 8px; padding: 6px 12px; border-radius: 6px; + text-decoration: none; color: var(--oscuro); transition: .2s; + background: transparent; text-align: right; +} +.sheet-link:hover { background: rgba(186,18,77,.06); } +.sheet-link-icon { font-size: 12px; color: var(--bordo); opacity: .4; text-align: center; grid-row: 1/3; grid-column: 2; } +.sheet-link-main { + font-family: var(--font-display); font-style: italic; font-weight: 600; + font-size: 16px; color: var(--oscuro); line-height: 1.2; grid-column: 1; +} +.sheet-link-desc { + font-family: var(--font-ui); font-size: 13px; color: var(--dusty); + font-weight: 500; opacity: .8; grid-column: 1; margin-top: -2px; +} +.sheet-link.highlight { + background: var(--bordo); border-radius: 6px; padding: 10px 14px; margin-top: 6px; +} +.sheet-link.highlight .sheet-link-icon { color: #fff; opacity: 1; } +.sheet-link.highlight .sheet-link-main { color: #fff; } +.sheet-link.highlight .sheet-link-desc { color: rgba(255,255,255,.7); } +.sheet-link.highlight:hover { background: #9c0f40; } + +/* ===== HERO ===== */ +.hero { + min-height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + position: relative; + padding: 0 80px; + max-width: 1200px; + margin: 0 auto; + text-align: center; + pointer-events: none; +} +.hero-content { + position: relative; + z-index: 2; + max-width: 900px; + margin: 0 auto; + pointer-events: auto; +} + +/* ===== TIPOGRAFÍA ===== */ +.eyebrow { + font-family: var(--font-ui); font-size: 12px; letter-spacing: .28em; + text-transform: uppercase; color: var(--claro); margin-bottom: 22px; + display: flex; align-items: center; gap: 12px; opacity: 0.6; +} +.eyebrow::before { + content: ''; width: 30px; height: 1px; + background: linear-gradient(90deg, var(--rose-gold-3), var(--rose-gold-1)); +} +h1, h2 { + font-family: var(--font-display); font-weight: 300; font-style: italic; + font-size: clamp(40px, 6vw, 72px); line-height: 1.05; + margin-bottom: 16px; +} +.grad-word { + background: linear-gradient(100deg, var(--rose-gold-1) 0%, var(--bordo) 70%, var(--ligth-teal) 100%); + -webkit-background-clip: text; background-clip: text; color: transparent; +} +p, .lede { + font-family: var(--font-ui); font-size: 18px; line-height: 1.7; + color: var(--claro); max-width: 560px; margin: 0 auto 26px; opacity: 0.8; + font-weight: 300; +} +.lede { font-size: 18px; opacity: 0.7; max-width: 640px; } + +/* ===== BOTONES ===== */ +.btn { + font-family: var(--font-ui); font-size: 13px; font-weight: 500; + letter-spacing: .05em; padding: 16px 30px; border-radius: 2px; + cursor: pointer; transition: .25s; text-decoration: none; + display: inline-flex; align-items: center; gap: 8px; +} + +.btn-solid { + background: var(--bordo); color: #fff; border: 1px solid var(--bordo); +} +.btn-solid:hover { background: #9c0f40; } +.btn-outline { + background: transparent; color: var(--claro); + border: 1px solid rgba(234,240,238,.2); +} +.btn-outline:hover { border-color: var(--rosa); color: var(--rosa); } +.hero-actions { display: flex; gap: 16px; justify-content: center; margin-top: 40px; } +.hero-actions .btn { + pointer-events: auto; +} + +/* ===== HOME BUTTON ===== */ +.home-btn { + position: absolute; right: 20px; bottom: 20px; + font-size: 20px; color: var(--rose-gold-1); + text-decoration: none; transition: .3s; z-index: 10; + pointer-events: auto; +} +.home-btn:hover { color: rgba(234,240,238,.4); } + +/* ===== SCROLL HINT ===== */ +.scroll-hint { + position: absolute; bottom: 36px; left: 50%; transform: translateX(-50%); + font-family: var(--font-ui); font-size: 10.5px; letter-spacing: .2em; + text-transform: uppercase; color: rgba(234,240,238,.3); + display: flex; flex-direction: column; align-items: center; gap: 8px; + pointer-events: auto; +} +.scroll-hint .line { width: 1px; height: 36px; background: rgba(234,240,238,.2); } + +/* ===== SECCIONES GENERALES ===== */ +section { padding: 80px 48px; max-width: 1200px; margin: 0 auto; position: relative; } +section .eyebrow { justify-content: center; } +section h2 { text-align: center; max-width: 800px; margin: 0 auto 12px; } +section .lede { text-align: center; max-width: 900px; margin-bottom: 38px; } + +/* ===== SERVICIOS ===== */ +.tabs { + display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; + margin-bottom: 44px; border-bottom: 1px solid rgba(234,240,238,.06); +} +.tab { + font-family: var(--font-ui); font-size: 20px; font-weight: 400; + letter-spacing: .03em; color: rgba(234,240,238,.4); + background: none; border: none; cursor: pointer; + padding: 14px 22px; position: relative; transition: .2s; +} +.tab .num { + font-family: var(--font-display); font-style: italic; font-size: 13px; + color: var(--rosa); margin-right: 8px; +} +.tab.active { color: var(--claro); } +.tab.active::after { + content: ''; position: absolute; left: 0; right: 0; bottom: -1px; + height: 2px; background: linear-gradient(90deg, var(--rose-gold-3), var(--bordo)); +} +.tab:hover { color: var(--claro); } +.panel { display: none; animation: fade .4s ease; } +.panel.active { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: start; } +@keyframes fade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } +.panel-copy h3 { + font-family: var(--font-display); font-style: italic; font-weight: 500; + font-size: 25px; margin-bottom: 16px; +} +.panel-copy h3 em { color: var(--bordo); font-style: italic; } +.panel-copy p { font-size: 15px; max-width: 600px; margin-bottom: 26px; } + +.panel-list { display: flex; flex-direction: column; border-top: 1px solid rgba(234,240,238,.06); } +.panel-list .item { + display: flex; align-items: center; gap: 14px; + padding: 14px 0; border-bottom: 1px solid rgba(234,240,238,.06); + font-family: var(--font-ui); font-size: 15px; color: var(--claro); opacity: 0.8; +} +.icon-min { + font-size: 14px; color: var(--rose-gold-1); opacity: 0.5; + flex-shrink: 0; width: 24px; text-align: center; +} + +/* ===== PLANES ===== */ +.plans-grid { + display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; margin-bottom: 40px; +} +.plan-card { + background: rgba(234,240,238,.03); border: 1px solid rgba(234,240,238,.06); + border-radius: 10px; padding: 30px 24px; display: flex; flex-direction: column; + text-align: center; +} +.plan-card.reco { + border: 1px solid rgba(223,189,185,.2); + background: rgba(234,240,238,.04); + box-shadow: 0 0 30px rgba(186,18,77,.05); +} +.badge { + align-self: center; font-family: var(--font-ui); font-size: 9px; font-weight: 700; + letter-spacing: .1em; text-transform: uppercase; + color: var(--bordo); border: 1px solid var(--bordo); + padding: 4px 14px; border-radius: 100px; margin-bottom: 14px; +} +.plan-name { + font-family: var(--font-display); font-style: italic; font-weight: 500; + font-size: 24px; margin-bottom: 4px; +} +.plan-stars { color: var(--rosa); font-size: 11px; letter-spacing: 2px; margin-bottom: 14px; } +.plan-obj { + font-family: var(--font-ui); font-size: 12px; line-height: 1.5; + color: rgba(234,240,238,.5); margin-bottom: 22px; min-height: 52px; +} +.plan-feats { list-style: none; flex: 1; margin-bottom: 24px; text-align: left; } +.plan-feats li { + font-family: var(--font-ui); font-size: 12px; line-height: 1.5; + padding: 8px 0; border-top: 1px solid rgba(234,240,238,.06); + color: rgba(234,240,238,.7); +} +.plan-feats li:first-child { border-top: none; } +.plan-feats li::before { content: '— '; color: var(--rosa); opacity: 0.5; } +.plan-cta { + font-family: var(--font-ui); font-size: 12px; font-weight: 500; + letter-spacing: .04em; text-align: center; padding: 12px; + border-radius: 2px; text-decoration: none; + border: 1px solid rgba(234,240,238,.15); color: var(--claro); + transition: .25s; +} +.plan-cta:hover { border-color: var(--rosa); color: var(--rosa); } +.plan-main-cta { display: block; max-width: 300px; margin: 0 auto; text-align: center; } + +/* ===== ACCORDION ===== */ +.acc { border-top: 1px solid rgba(234,240,238,.06); } +.acc-item { border-bottom: 1px solid rgba(234,240,238,.06); } +.acc-head { + display: flex; align-items: center; justify-content: space-between; + padding: 20px 4px; cursor: pointer; +} +.acc-head-left { display: flex; align-items: center; gap: 16px; } +.acc-num { font-family: var(--font-display); font-style: italic; color: var(--dusty); font-size: 14px; opacity: 0.5; } +.acc-name { font-family: var(--font-ui); font-size: 26px; font-weight: 400; } +.acc-toggle { + width: 26px; height: 26px; border-radius: 100px; + border: 1px solid rgba(234,240,238,.15); + display: flex; align-items: center; justify-content: center; + font-size: 14px; color: var(--rosa); transition: .25s; +} +.acc-item.open .acc-toggle { transform: rotate(45deg); border-color: var(--bordo); color: var(--bordo); } +.acc-body { max-height: 0; overflow: hidden; transition: .35s ease; } +.acc-item.open .acc-body { max-height: 600px; } +.acc-body-inner { padding: 0 4px 26px; display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } +.service-row { + font-family: var(--font-ui); font-size: 16px; color: rgba(234,240,238,.6); + padding: 12px 0; border-top: 1px solid rgba(234,240,238,.04); +} +.service-row b { display: block; color: var(--claro); font-size: 14px; margin-bottom: 3px; font-weight: 500; } +.service-row .price { color: var(--rosa); font-family: var(--font-display); font-style: italic; font-size: 13px; } + +/* ===== PROCESO ===== */ +.arcade-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; } +.column { position: relative; padding: 0 6px; text-align: center; } +.column:not(:last-child)::after { + content: ''; position: absolute; right: -7px; top: 14%; bottom: 44px; + width: 1px; background: rgba(234,240,238,.06); +} +.arch { + width: 100%; aspect-ratio: 1/1; max-height: 100px; + border: 2px solid rgba(234,240,238,.06); border-bottom: none; + border-radius: 100px 100px 0 0; + display: flex; align-items: flex-end; justify-content: center; + padding-bottom: 16px; margin-bottom: 16px; + transition: .3s; +} +.column:hover .arch { border-color: rgba(223,189,185,.3); } +.arch .num { font-family: var(--font-display); font-style: italic; font-weight: 500; font-size: 20px; color: var(--rosa); opacity: 0.5; } +.arch.final { background: var(--bordo); border-color: var(--bordo); } +.arch.final svg { width: 22px; height: 22px; stroke: #fff; fill: none; stroke-width: 2; } +.cat { + font-family: var(--font-ui); font-size: 10px; letter-spacing: .14em; + text-transform: uppercase; color: var(--ligth-teal); font-weight: 500; + margin-bottom: 6px; +} +.column:nth-child(even) .cat { color: var(--bordo); } +.step-title { + font-family: var(--font-display); font-style: italic; font-weight: 500; + font-size: 15px; margin-bottom: 6px; +} +.step-desc { + font-family: var(--font-ui); font-size: 12px; line-height: 1.5; + color: var(--dusty); +} + +/* ===== CIFRAS ===== */ +.cifras-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; } +.cifra-card { + background: rgba(234,240,238,.02); border: 1px solid rgba(234,240,238,.06); + border-radius: 10px; padding: 34px 30px; text-align: center; +} +.cifra-icon { + width: 44px; height: 56px; margin: 0 auto 20px; + border: 2px solid rgba(234,240,238,.06); border-bottom: none; + border-radius: 22px 22px 0 0; +} +.cifra-label { + font-family: var(--font-ui); font-size: 16px; letter-spacing: .18em; + text-transform: uppercase; color: var(--ligth-teal); font-weight: 500; margin-bottom: 12px; +} +.cifra-value { + font-family: var(--font-display); font-style: italic; font-weight: 500; + font-size: 24px; color: var(--claro); margin-bottom: 14px; +} +.cifra-desc { + font-family: var(--font-body); font-size: 16px; line-height: 1.6; + color: var(--dusty); +} +.cifra-desc b { color: var(--claro); font-weight: 400; } + +/* ===== COTIZADOR ===== */ +.card { + background: rgba(234,240,238,.03); border: 1px solid rgba(234,240,238,.06); + border-radius: 14px; padding: 48px 52px; max-width: 800px; margin: 0 auto; +} +.progress { display: flex; gap: 6px; margin-bottom: 36px; } +.progress .seg { height: 3px; flex: 1; background: rgba(234,240,238,.1); border-radius: 100px; overflow: hidden; } +.progress .seg i { display: block; height: 100%; width: 0%; background: linear-gradient(90deg, var(--rose-gold-3), var(--bordo)); transition: .4s; } +.step { display: none; } +.step.active { display: block; animation: fade .35s ease; } +.q-num { font-family: var(--font-display); font-style: italic; color: var(--rosa); font-size: 14px; margin-bottom: 10px; } +.q-title { font-family: var(--font-display); font-style: italic; font-weight: 500; font-size: 22px; margin-bottom: 28px; } +.options { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 32px; } +.opt { + border: 1px solid rgba(234,240,238,.1); border-radius: 8px; padding: 18px 20px; + cursor: pointer; transition: .2s; font-family: var(--font-ui); +} +.opt:hover { border-color: rgba(223,189,185,.3); } +.opt.selected { border-color: var(--bordo); background: rgba(186,18,77,.05); } +.opt-title { font-size: 14px; font-weight: 500; color: var(--claro); margin-bottom: 3px; } +.opt-sub { font-size: 12px; color: var(--dusty); } +.nav-row { display: flex; justify-content: space-between; align-items: center; } +.btn-cotizador { + font-family: var(--font-ui); font-size: 13px; font-weight: 500; + padding: 12px 28px; border-radius: 4px; cursor: pointer; transition: .3s; + border: 1px solid var(--bordo); background: transparent; color: var(--bordo); +} +.btn-cotizador:hover:not(:disabled) { background: var(--bordo); color: #fff; } +.btn-cotizador:disabled { opacity: .2; cursor: not-allowed; border-color: #444; color: #666; } +.btn-cotizador-back { + font-family: var(--font-ui); font-size: 13px; font-weight: 500; + padding: 12px 28px; border-radius: 4px; cursor: pointer; transition: .3s; + border: 1px solid transparent; background: transparent; color: var(--dusty); +} +.btn-cotizador-back:hover { color: var(--claro); border-color: rgba(234,240,238,.1); } +.btn-cotizador-restart { + font-family: var(--font-ui); font-size: 13px; font-weight: 500; + padding: 12px 28px; border-radius: 4px; cursor: pointer; transition: .3s; + border: 1px solid var(--bordo); background: transparent; color: var(--bordo); +} +.btn-cotizador-restart:hover { background: var(--bordo); color: #fff; } +.result { display: none; text-align: center; padding-top: 10px; } +.result.active { display: block; animation: fade .4s ease; } +.result-badge { + font-family: var(--font-ui); font-size: 11px; letter-spacing: .14em; + text-transform: uppercase; color: var(--bordo); margin-bottom: 10px; +} +.result-plan { font-family: var(--font-display); font-style: italic; font-weight: 500; font-size: 32px; margin-bottom: 6px; } +.result-price { font-family: var(--font-display); font-style: italic; font-weight: 500; font-size: 22px; color: var(--bordo); margin-bottom: 18px; } +.result-desc { font-family: var(--font-body); font-size: 14px; color: var(--dusty); max-width: 400px; margin: 0 auto 30px; line-height: 1.6; } +.result-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; } + +/* ===== NOSOTROS ===== */ +.team-grid-simple { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; max-width: 800px; margin: 0 auto; } +.team-card-simple { + background: rgba(234,240,238,.02); border: 1px solid rgba(234,240,238,.06); + border-radius: 12px; padding: 40px 32px; text-align: center; +} +.team-name { + font-family: var(--font-name); font-weight: 500; + font-size: 30px; margin-bottom: 8px; +} +.team-role { + font-family: var(--font-ui); font-size: 13px; font-weight: 400; + letter-spacing: .06em; text-transform: uppercase; color: var(--rosa); + margin-bottom: 12px; opacity: 0.6; +} +.team-detail { + font-family: var(--font-ui); font-size: 13px; line-height: 1.6; + color: var(--claro); +} +.team-footer { + margin-top: 10px; padding-top: 32px; + border-top: 1px solid rgba(234,240,238,.04); + text-align: center; +} +.team-footer-text { + font-family: var(--font-ui); font-size: 16px; color: var(--dusty); + max-width: 700px; margin: 0 auto; line-height: 1.6; +} + +/* ===== FAQ ===== */ +.faq-grid { border-top: 1px solid rgba(234,240,238,.06); max-width: 900px; margin: 0 auto; } +.faq-item { border-bottom: 1px solid rgba(234,240,238,.06); } +.faq-head { + display: flex; justify-content: space-between; align-items: center; + padding: 20px 4px; cursor: pointer; transition: .2s; +} +.faq-head:hover .faq-q { color: var(--rosa); } +.faq-q { + font-family: var(--font-ui); font-weight: 400; + font-size: 20px; transition: .2s; +} +.faq-toggle { + font-family: var(--font-body); font-size: 18px; font-weight: 300; + color: var(--rose-gold-1); width: 28px; height: 28px; + display: flex; align-items:center; justify-content: center; + border-radius: 100px; border: 1px solid rgba(234,240,238,.1); + transition: .25s; flex-shrink: 0; +} +.faq-item.open .faq-toggle { border-color: var(--bordo); color: var(--bordo); } +.faq-body { max-height: 0; overflow: hidden; transition: .4s ease; } +.faq-item.open .faq-body { max-height: 100px; } +.faq-body p { + font-family: var(--font-body); font-size: 15px; line-height: 1.7; + color: var(--claro); padding: 0 4px 14px; max-width: 800px; +} + +/* ===== CONTACTO ===== */ +.contact-grid { display: grid; grid-template-columns: 1.6fr 1fr; gap: 48px; align-items: start; } +.contact-form-wrap { + background: rgba(234,240,238,.02); border: 1px solid rgba(234,240,238,.06); + border-radius: 12px; padding: 40px 36px; +} +.contact-form { display: flex; flex-direction: column; gap: 18px; } +.form-row { display: flex; gap: 18px; } +.form-group { flex: 1; display: flex; flex-direction: column; gap: 4px; } +.form-group.full { flex: 0 0 100%; } +.form-group label { + font-family: var(--font-ui); font-size: 11px; font-weight: 500; + letter-spacing: .04em; text-transform: uppercase; color: rgba(234,240,238,.5); +} +.form-group .req { color: var(--bordo); } +.form-group input, .form-group textarea { + font-family: var(--font-body); font-size: 14px; font-weight: 300; + padding: 12px 14px; border-radius: 6px; + border: 1px solid rgba(234,240,238,.08); + background: rgba(234,240,238,.03); color: var(--claro); + transition: .25s; width: 100%; +} +.form-group input:focus, .form-group textarea:focus { + outline: none; border-color: var(--bordo); + background: rgba(234,240,238,.06); +} +.filter-tags { display: flex; flex-wrap: wrap; gap: 8px; } +.filter-tag { + font-family: var(--font-ui); font-size: 11px; font-weight: 500; + padding: 6px 16px; border-radius: 100px; + border: 1px solid rgba(234,240,238,.1); background: transparent; + color: rgba(234,240,238,.4); cursor: pointer; transition: .2s; +} +.filter-tag:hover { border-color: var(--rosa); color: var(--claro); } +.filter-tag.active { background: var(--bordo); border-color: var(--bordo); color: #fff; } +.checkbox-label { + display: flex; align-items: center; gap: 10px; + font-family: var(--font-body); font-size: 13px; color: rgba(234,240,238,.6); + cursor: pointer; +} +.checkbox-label input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--bordo); } + +.contact-direct { display: flex; flex-direction: column; gap: 16px; } +.contact-card { + display: flex; align-items: flex-start; gap: 16px; + padding: 18px 20px; background: rgba(234,240,238,.02); + border: 1px solid rgba(234,240,238,.04); border-radius: 10px; +} +.contact-icon { font-size: 20px; line-height: 1.2; flex-shrink: 0; margin-top: 2px; } +.contact-label { + font-family: var(--font-ui); font-size: 13px; font-weight: 500; + letter-spacing: .08em; text-transform: uppercase; color: var(--claro); opacity: 0.5; +} +.contact-info a { font-family: var(--font-body); font-size: 14px; color: var(--claro); text-decoration: none; } +.contact-info a:hover { color: var(--rosa); } +.contact-info span { font-family: var(--font-body); font-size: 13px; color: var(--claro); } +.contact-socials { display: flex; gap: 12px; } +.contact-socials a { + font-family: var(--font-ui); font-size: 13px; font-weight: 400; + color: var(--claro); text-decoration: none; opacity: 1; +} +.contact-socials a:hover { opacity: 1; } + +/* ===== FOOTER ===== */ +.footer { + background: rgba(0,0,0,.2); border-top: 1px solid rgba(234,240,238,.04); + padding: 60px 48px 20px; +} +.footer-inner { + max-width: 1200px; margin: 0 auto; display: grid; + grid-template-columns: 1fr 2fr; gap: 60px; padding-bottom: 40px; + border-bottom: 1px solid rgba(234,240,238,.04); +} +.footer-brand { display: flex; flex-direction: column; gap: 12px; } +.footer-desc { font-family: var(--font-display); font-size: 16px; line-height: 1.6; color: var(--dusty); max-width: 320px; } +.footer-links { display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px; } +.footer-col h4 { + font-family: var(--font-ui); font-size: 10px; font-weight: 700; + letter-spacing: .1em; text-transform: uppercase; color: var(--rosa); opacity: 0.4; + margin-bottom: 12px; +} +.footer-col a { + display: block; font-family: var(--font-body); font-size: 13px; + color: var(--dusty); text-decoration: none; padding: 4px 0; + transition: .2s; +} +.footer-col a:hover { color: var(--claro); } +.footer-socials { display: flex; gap: 12px; margin-top: 8px; } +.footer-socials a { + font-family: var(--font-ui); font-size: 11px; font-weight: 500; + color: var(--claro); text-decoration: none; padding: 4px 12px; + border: 1px solid rgba(234,240,238,.08); border-radius: 100px; + transition: .2s; +} +.footer-socials a:hover { border-color: var(--rosa); color: var(--rosa); } +.footer-bottom { + max-width: 1200px; margin: 0 auto; padding-top: 20px; + display: flex; justify-content: space-between; align-items: center; + flex-wrap: wrap; gap: 12px; +} +.footer-bottom span { font-family: var(--font-body); font-size: 12px; color: rgba(234,240,238,.2); } +.footer-legal { display: flex; gap: 20px; } +.footer-legal a { font-family: var(--font-body); font-size: 12px; color: rgba(234,240,238,.2); text-decoration: none; } +.footer-legal a:hover { color: rgba(234,240,238,.5); } + +/* ===== RESPONSIVE ===== */ +@media (max-width: 980px) { + .plans-grid { grid-template-columns: 1fr 1fr; } + .arcade-grid { grid-template-columns: repeat(3, 1fr); row-gap: 40px; } + .column:nth-child(3)::after, .column:nth-child(6)::after { display: none; } + .cifras-grid { grid-template-columns: 1fr; } +} +@media (max-width: 820px) { + .sheet-body { grid-template-columns: 1fr 1fr; } + .sheet-col:last-child { grid-column: 1 / -1; } + .contact-grid { grid-template-columns: 1fr; } + .footer-inner { grid-template-columns: 1fr; gap: 40px; } + .team-grid-simple { grid-template-columns: 1fr; } +} +@media (max-width: 720px) { + header { padding: 20px 22px; } + .hero { padding: 0 30px; } + section { padding: 60px 22px; } + .panel.active { grid-template-columns: 1fr; gap: 30px; } + .plans-grid { grid-template-columns: 1fr; } + .arcade-grid { grid-template-columns: 1fr 1fr; row-gap: 30px; } + .column:nth-child(2n)::after { display: none; } + .options { grid-template-columns: 1fr; } + .card { padding: 32px 24px; } + .acc-body-inner { grid-template-columns: 1fr; } + .footer-links { grid-template-columns: 1fr 1fr; gap: 24px; } + .footer { padding: 40px 22px 16px; } + .footer-bottom { flex-direction: column; text-align: center; } +} +@media (max-width: 520px) { + .sheet { max-height: 70vh; padding: 10px 16px 20px; border-radius: 16px 16px 0 0; } + .sheet-body { grid-template-columns: 1fr; gap: 16px; } + .sheet-col:last-child { grid-column: auto; } + .home-btn { right: 12px; bottom: 12px; font-size: 10px; } +} \ No newline at end of file