Logo. Hasta ahora no había: era el texto "uM" en un cuadrado, con tamaño y esquinas distintas en cada pantalla. Ahora hay un mark — la "u" del nombre con el punto de pulso integrado — probado a 16px, 20px y 32px sobre claro y oscuro, que es donde se cae la mayoría de los logos. Reemplaza el texto en el portal, el header, el sidebar del Studio y la promo, y va también de favicon. Plantillas por rubro. Un agente recién creado no sabía nada, y escribir el conocimiento desde cero frente a un campo en blanco es donde la mayoría abandona. Al crear uno se puede elegir un punto de partida —tienda, restaurante, consultorio, servicios, peluquería o la base genérica— y arranca con sus notas ya escritas, listas para editar. Los valores de ejemplo van entre corchetes a propósito, para que se vea que hay que reemplazarlos: una nota que parezca dato real termina en boca del asistente. Un test lo verifica nota por nota. Y la plantilla de salud abre con el límite — el asistente no da consejo médico ni interpreta síntomas — porque es el único rubro donde una respuesta inventada hace daño de verdad. El catálogo vive en código, no en base: es contenido editorial que mejoramos nosotros, no configuración que cada instalación toca por su lado. Las notas se cargan en segundo plano porque cada una necesita sus embeddings, y hacer esperar el alta sería castigar justo al que eligió plantilla. Con las opciones nuevas el formulario pasaba de largo la pantalla y el botón de guardar quedaba abajo del borde: el modal ahora tiene su propio scroll. Y la página promocional de uMind queda servida en /umind.html. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1292 lines
47 KiB
HTML
1292 lines
47 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>uMind — U-SITE</title>
|
|
<meta name="description" content="El asistente que responde por tu negocio en WhatsApp, Telegram y tu sitio. No adivina: busca en tu información." />
|
|
|
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 96 96'%3E%3Crect width='96' height='96' rx='22' fill='%238eb02f'/%3E%3Cpath d='M32,42 V58 A14,14 0 0 0 60,58 V42' fill='none' stroke='%23fff' stroke-width='10' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M60,58 V64' fill='none' stroke='%23fff' stroke-width='10' stroke-linecap='round'/%3E%3Ccircle cx='60' cy='28' r='7' fill='%23fff'/%3E%3C/svg%3E" />
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@400;600;800&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap" />
|
|
|
|
<style>
|
|
/* ── Sistema ───────────────────────────────────────────────────────────────
|
|
Página de un solo mundo visual: oscura, como el sitio de U-SITE. Todo color
|
|
se declara explícitamente para que no dependa del tema del navegador.
|
|
Dos acentos con trabajos distintos y sin mezclarse:
|
|
cian = la máquina (proceso, latencia, vectores)
|
|
lima = tu información y lo que salió bien (verde de marca U-SITE) */
|
|
:root {
|
|
--void: #05070D;
|
|
--ground: #080B14;
|
|
--panel: #0D1220;
|
|
--panel-2: #121A2B;
|
|
--line: #1B2437;
|
|
--line-soft: #141C2C;
|
|
|
|
--text: #E6EDF7;
|
|
--text-soft: #8C9BB5;
|
|
--text-dim: #58657E;
|
|
|
|
--cyan: #3DDCE8;
|
|
--cyan-deep: #1B94A6;
|
|
--lime: #A9D340;
|
|
--lime-deep: #8EB02F;
|
|
--amber: #E0AF4A;
|
|
|
|
--display: "Sora", "Trebuchet MS", sans-serif;
|
|
--body: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
--mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
html { scroll-behavior: smooth; }
|
|
|
|
body {
|
|
margin: 0;
|
|
background: var(--ground);
|
|
color: var(--text);
|
|
font-family: var(--body);
|
|
font-size: 16.5px;
|
|
line-height: 1.65;
|
|
-webkit-font-smoothing: antialiased;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* Retícula de fondo: la rejilla de un plano técnico, muy tenue. */
|
|
body::before {
|
|
content: "";
|
|
position: fixed;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
background-image:
|
|
linear-gradient(var(--line-soft) 1px, transparent 1px),
|
|
linear-gradient(90deg, var(--line-soft) 1px, transparent 1px);
|
|
background-size: 64px 64px;
|
|
mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 20%, transparent 75%);
|
|
opacity: 0.55;
|
|
}
|
|
|
|
/* Grano muy leve: sin él, los degradés oscuros bandean en pantallas de
|
|
8 bits y el panel se ve a plástico en vez de a vidrio. */
|
|
body::after {
|
|
content: "";
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 2;
|
|
pointer-events: none;
|
|
opacity: 0.035;
|
|
mix-blend-mode: overlay;
|
|
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23g)'/></svg>");
|
|
}
|
|
|
|
/* ── Revelado al hacer scroll ────────────────────────────────────────────
|
|
Los bloques entran cuando aparecen en pantalla — no todos a la vez, ni
|
|
siempre, sólo la primera vez que se muestran. */
|
|
[data-revela] {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
transition: opacity .7s cubic-bezier(.16,.8,.24,1), transform .7s cubic-bezier(.16,.8,.24,1);
|
|
transition-delay: var(--retraso, 0ms);
|
|
}
|
|
|
|
[data-revela].visto { opacity: 1; transform: none; }
|
|
|
|
.capa { position: relative; z-index: 1; }
|
|
|
|
.marco { max-width: 1140px; margin: 0 auto; padding: 0 24px; }
|
|
|
|
/* ── Tipografía ─────────────────────────────────────────────────────────── */
|
|
h1, h2, h3 {
|
|
font-family: var(--display);
|
|
margin: 0;
|
|
letter-spacing: -0.03em;
|
|
text-wrap: balance;
|
|
line-height: 1.04;
|
|
}
|
|
|
|
h1 { font-size: clamp(2.25rem, 4.6vw, 3.4rem); font-weight: 800; }
|
|
h2 { font-size: clamp(1.75rem, 3.4vw, 2.6rem); font-weight: 800; }
|
|
h3 { font-size: 1.02rem; font-weight: 600; letter-spacing: -0.01em; line-height: 1.35; }
|
|
|
|
p { margin: 0; }
|
|
strong { font-weight: 600; color: var(--text); }
|
|
|
|
.eyebrow {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-family: var(--mono);
|
|
font-size: 0.71rem;
|
|
letter-spacing: 0.16em;
|
|
text-transform: uppercase;
|
|
color: var(--cyan);
|
|
}
|
|
|
|
.eyebrow::before {
|
|
content: "";
|
|
width: 22px;
|
|
height: 1px;
|
|
background: var(--cyan);
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.plomo { color: var(--text-soft); font-size: 1.12rem; }
|
|
|
|
/* ── Barra superior ─────────────────────────────────────────────────────── */
|
|
.barra {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 22px 0;
|
|
border-bottom: 1px solid var(--line-soft);
|
|
}
|
|
|
|
.logo {
|
|
width: 30px; height: 30px;
|
|
border-radius: 8px;
|
|
background: linear-gradient(140deg, var(--lime), var(--lime-deep));
|
|
display: grid; place-items: center;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.logo svg { width: 100%; height: 100%; display: block; }
|
|
|
|
.logo-txt { font-family: var(--display); font-weight: 600; letter-spacing: -0.02em; }
|
|
|
|
.chip {
|
|
font-family: var(--mono);
|
|
font-size: 0.66rem;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--cyan);
|
|
border: 1px solid rgba(61, 220, 232, 0.3);
|
|
background: rgba(61, 220, 232, 0.07);
|
|
padding: 3px 9px;
|
|
border-radius: 999px;
|
|
}
|
|
|
|
.barra-fin { margin-left: auto; font-family: var(--mono); font-size: 0.72rem; color: var(--text-dim); }
|
|
|
|
/* ── Escenario: fondo animado detrás de la nav y la portada ────────────────
|
|
Un campo de puntos que deriva despacio, como el espacio vectorial del que
|
|
habla la página, pero ambiental — sin buscar nada todavía. Eso lo hace
|
|
luego, explícito, el mapa de la sección "mecánica". Acá es sólo clima. */
|
|
.escenario {
|
|
position: relative;
|
|
overflow: hidden;
|
|
border-radius: 0 0 22px 22px;
|
|
}
|
|
|
|
.campo {
|
|
position: absolute;
|
|
inset: 0;
|
|
z-index: 0;
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
-webkit-mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
|
|
mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
|
|
}
|
|
|
|
/* Mancha de luz que sigue el cursor — sutil, se apaga sola en touch. */
|
|
.foco {
|
|
position: absolute;
|
|
inset: 0;
|
|
z-index: 0;
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
transition: opacity .4s ease;
|
|
background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 20%),
|
|
rgba(61, 220, 232, 0.09), rgba(169, 211, 64, 0.05) 46%, transparent 72%);
|
|
}
|
|
|
|
.escenario.con-foco .foco { opacity: 1; }
|
|
|
|
.escenario > .barra,
|
|
.escenario > .portada { position: relative; z-index: 1; }
|
|
|
|
@media (hover: none) { .foco { display: none; } }
|
|
|
|
|
|
/* ── Portada ────────────────────────────────────────────────────────────── */
|
|
.portada {
|
|
display: grid;
|
|
gap: 48px;
|
|
padding: 72px 0 56px;
|
|
align-items: center;
|
|
}
|
|
|
|
@media (min-width: 980px) {
|
|
.portada { grid-template-columns: 1fr 1.05fr; gap: 60px; padding: 80px 0 76px; }
|
|
}
|
|
|
|
.portada h1 { margin-top: 20px; }
|
|
.portada h1 em { font-style: normal; color: var(--cyan); }
|
|
.portada .plomo { margin-top: 22px; max-width: 31rem; }
|
|
|
|
.acciones { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 32px; }
|
|
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 9px;
|
|
padding: 13px 22px;
|
|
border-radius: 10px;
|
|
font-family: var(--display);
|
|
font-weight: 600;
|
|
font-size: 0.95rem;
|
|
text-decoration: none;
|
|
border: 1px solid transparent;
|
|
transition: transform .15s ease, box-shadow .2s ease, border-color .2s ease, color .2s ease;
|
|
}
|
|
|
|
.btn-solido {
|
|
background: var(--lime);
|
|
color: #06180A;
|
|
box-shadow: 0 10px 34px -14px rgba(169, 211, 64, 0.7);
|
|
}
|
|
|
|
.btn-solido:hover { transform: translateY(-2px); box-shadow: 0 16px 42px -14px rgba(169, 211, 64, 0.8); }
|
|
|
|
.btn-linea { color: var(--text); border-color: var(--line); }
|
|
.btn-linea:hover { border-color: var(--cyan); color: var(--cyan); }
|
|
|
|
a:focus-visible, button:focus-visible {
|
|
outline: 2px solid var(--cyan);
|
|
outline-offset: 3px;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.sello {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 18px;
|
|
margin-top: 34px;
|
|
font-family: var(--mono);
|
|
font-size: 0.72rem;
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
.sello span { display: inline-flex; align-items: center; gap: 7px; }
|
|
.sello span::before {
|
|
content: "";
|
|
width: 5px; height: 5px;
|
|
border-radius: 50%;
|
|
background: var(--lime);
|
|
box-shadow: 0 0 8px var(--lime);
|
|
}
|
|
|
|
/* ── Traza: el corazón de la página ─────────────────────────────────────
|
|
Un chatbot cualquiera improvisa. uMind hace un recorrido concreto por tu
|
|
información, y ese recorrido es lo que se muestra acá. */
|
|
.traza {
|
|
position: relative;
|
|
background: linear-gradient(180deg, var(--panel) 0%, var(--void) 100%);
|
|
border: 1px solid var(--line);
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
box-shadow: 0 40px 90px -50px rgba(61, 220, 232, 0.45);
|
|
}
|
|
|
|
/* Barrido de escaneo: una línea que recorre el panel, como si el proceso
|
|
siguiera activo aunque la traza ya haya terminado de escribirse. */
|
|
.traza::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 0; right: 0; top: 0;
|
|
height: 90px;
|
|
background: linear-gradient(180deg, rgba(61, 220, 232, 0.1), transparent);
|
|
transform: translateY(-100%);
|
|
animation: barrido 7s ease-in-out infinite;
|
|
animation-delay: 3.6s;
|
|
pointer-events: none;
|
|
}
|
|
|
|
@keyframes barrido {
|
|
0% { transform: translateY(-100%); }
|
|
30% { transform: translateY(520%); }
|
|
100% { transform: translateY(520%); }
|
|
}
|
|
|
|
.traza-barra {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 12px 16px;
|
|
background: var(--panel-2);
|
|
border-bottom: 1px solid var(--line);
|
|
font-family: var(--mono);
|
|
font-size: 0.7rem;
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
.pulso {
|
|
width: 7px; height: 7px;
|
|
border-radius: 50%;
|
|
background: var(--lime);
|
|
box-shadow: 0 0 0 0 rgba(169, 211, 64, 0.6);
|
|
animation: pulso 2.4s infinite;
|
|
}
|
|
|
|
@keyframes pulso {
|
|
0% { box-shadow: 0 0 0 0 rgba(169, 211, 64, 0.5); }
|
|
70% { box-shadow: 0 0 0 9px rgba(169, 211, 64, 0); }
|
|
100% { box-shadow: 0 0 0 0 rgba(169, 211, 64, 0); }
|
|
}
|
|
|
|
.traza-cuerpo { padding: 14px 16px 18px; font-family: var(--mono); font-size: 0.79rem; }
|
|
|
|
.paso {
|
|
display: grid;
|
|
grid-template-columns: 1fr auto;
|
|
gap: 6px 14px;
|
|
padding: 8px 0;
|
|
border-bottom: 1px dashed var(--line-soft);
|
|
opacity: 0;
|
|
transform: translateY(6px);
|
|
}
|
|
|
|
.paso.on { opacity: 1; transform: none; transition: opacity .35s ease, transform .35s ease; }
|
|
.paso:last-of-type { border-bottom: 0; }
|
|
|
|
.paso-nombre { color: var(--text); display: flex; align-items: center; gap: 9px; }
|
|
.paso-nombre b { font-weight: 500; }
|
|
|
|
.marca-paso { color: var(--cyan); font-weight: 500; }
|
|
.ms { color: var(--text-dim); font-variant-numeric: tabular-nums; }
|
|
|
|
.detalle {
|
|
grid-column: 1 / -1;
|
|
padding-left: 22px;
|
|
color: var(--text-soft);
|
|
display: grid;
|
|
gap: 3px;
|
|
}
|
|
|
|
.frag { display: flex; justify-content: space-between; gap: 12px; }
|
|
.frag em { font-style: normal; color: var(--lime); }
|
|
.frag-nombre { color: var(--text-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
|
|
.salida {
|
|
margin-top: 14px;
|
|
padding: 14px 15px;
|
|
background: rgba(169, 211, 64, 0.06);
|
|
border: 1px solid rgba(169, 211, 64, 0.25);
|
|
border-radius: 11px;
|
|
font-family: var(--body);
|
|
font-size: 0.92rem;
|
|
line-height: 1.55;
|
|
color: var(--text);
|
|
opacity: 0;
|
|
transform: translateY(6px);
|
|
}
|
|
|
|
.salida.on { opacity: 1; transform: none; transition: opacity .4s ease, transform .4s ease; }
|
|
.salida-rotulo {
|
|
display: block;
|
|
font-family: var(--mono);
|
|
font-size: 0.65rem;
|
|
letter-spacing: 0.13em;
|
|
text-transform: uppercase;
|
|
color: var(--lime);
|
|
margin-bottom: 7px;
|
|
}
|
|
|
|
.cursor {
|
|
display: inline-block;
|
|
width: 7px;
|
|
height: 1em;
|
|
background: var(--lime);
|
|
vertical-align: -2px;
|
|
animation: parpadeo 1s steps(2) infinite;
|
|
}
|
|
|
|
@keyframes parpadeo { 50% { opacity: 0; } }
|
|
|
|
/* ── Secciones ──────────────────────────────────────────────────────────── */
|
|
section { padding: 84px 0; border-top: 1px solid var(--line-soft); }
|
|
.cabeza { max-width: 36rem; display: grid; gap: 16px; }
|
|
|
|
.rejilla { display: grid; gap: 1px; margin-top: 48px; background: var(--line-soft); border: 1px solid var(--line-soft); border-radius: 14px; overflow: hidden; }
|
|
@media (min-width: 820px) { .rejilla.tres { grid-template-columns: repeat(3, 1fr); } }
|
|
|
|
.celda {
|
|
background: var(--ground);
|
|
padding: 26px 24px;
|
|
display: grid;
|
|
gap: 9px;
|
|
align-content: start;
|
|
transition: background .2s ease;
|
|
}
|
|
|
|
.celda:hover { background: var(--panel); }
|
|
.celda p { color: var(--text-soft); font-size: 0.95rem; }
|
|
.celda .clave { font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--cyan); }
|
|
|
|
.duo { display: grid; gap: 44px; align-items: center; }
|
|
@media (min-width: 980px) { .duo { grid-template-columns: 1fr 1fr; gap: 64px; } }
|
|
.duo.espejo > :first-child { order: 2; }
|
|
@media (max-width: 979px) { .duo.espejo > :first-child { order: 0; } }
|
|
|
|
.texto-bloque { display: grid; gap: 18px; }
|
|
.texto-bloque p { color: var(--text-soft); }
|
|
|
|
/* ── Mapa vectorial ─────────────────────────────────────────────────────── */
|
|
.mapa {
|
|
position: relative;
|
|
background: var(--panel);
|
|
border: 1px solid var(--line);
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
aspect-ratio: 4 / 3;
|
|
transform: perspective(900px) rotateX(0deg) rotateY(0deg);
|
|
transition: transform .25s ease, border-color .25s ease;
|
|
will-change: transform;
|
|
}
|
|
|
|
@media (hover: hover) and (pointer: fine) {
|
|
.mapa:hover { border-color: rgba(61, 220, 232, 0.4); }
|
|
}
|
|
|
|
.mapa canvas { display: block; width: 100%; height: 100%; }
|
|
|
|
.mapa-pie {
|
|
position: absolute;
|
|
left: 0; right: 0; bottom: 0;
|
|
padding: 11px 15px;
|
|
font-family: var(--mono);
|
|
font-size: 0.68rem;
|
|
color: var(--text-dim);
|
|
background: linear-gradient(0deg, var(--panel) 30%, transparent);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
|
|
.mapa-pie em { font-style: normal; color: var(--lime); }
|
|
|
|
/* ── Tabla de fuentes ───────────────────────────────────────────────────── */
|
|
.tabla {
|
|
background: var(--panel);
|
|
border: 1px solid var(--line);
|
|
border-radius: 14px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.tabla-cab {
|
|
display: flex; align-items: center; gap: 8px;
|
|
padding: 12px 16px;
|
|
border-bottom: 1px solid var(--line);
|
|
font-family: var(--mono);
|
|
font-size: 0.68rem;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
.fila {
|
|
display: flex; align-items: center; justify-content: space-between;
|
|
gap: 14px;
|
|
padding: 14px 16px;
|
|
border-bottom: 1px solid var(--line-soft);
|
|
}
|
|
|
|
.fila:last-child { border-bottom: 0; }
|
|
.fila-nombre { font-size: 0.92rem; }
|
|
.fila-sub { font-family: var(--mono); font-size: 0.68rem; color: var(--text-dim); margin-top: 2px; }
|
|
.estado { font-family: var(--mono); font-size: 0.69rem; white-space: nowrap; }
|
|
.ok { color: var(--lime); }
|
|
.viejo { color: var(--amber); }
|
|
|
|
/* ── Métricas ───────────────────────────────────────────────────────────── */
|
|
.metricas { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--line-soft); border: 1px solid var(--line); border-radius: 14px; overflow: hidden; }
|
|
.metrica { background: var(--panel); padding: 22px; display: grid; gap: 5px; }
|
|
.metrica .n { font-family: var(--display); font-weight: 800; font-size: 2rem; letter-spacing: -0.04em; font-variant-numeric: tabular-nums; }
|
|
.metrica .n.cyan { color: var(--cyan); }
|
|
.metrica .n.lima { color: var(--lime); }
|
|
.metrica .t { font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-dim); line-height: 1.5; }
|
|
|
|
/* ── Cierre ─────────────────────────────────────────────────────────────── */
|
|
.cierre {
|
|
margin: 0 0 80px;
|
|
padding: 68px 32px;
|
|
text-align: center;
|
|
background:
|
|
radial-gradient(ellipse 60% 100% at 50% 0%, rgba(61, 220, 232, 0.1), transparent 70%),
|
|
var(--panel);
|
|
border: 1px solid var(--line);
|
|
border-radius: 22px;
|
|
}
|
|
|
|
.cierre p { margin: 18px auto 0; max-width: 32rem; color: var(--text-soft); }
|
|
.cierre .acciones { justify-content: center; }
|
|
|
|
footer {
|
|
padding: 34px 0 46px;
|
|
border-top: 1px solid var(--line-soft);
|
|
font-family: var(--mono);
|
|
font-size: 0.71rem;
|
|
color: var(--text-dim);
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px 20px;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
footer a { color: var(--text-soft); text-decoration: none; }
|
|
footer a:hover { color: var(--cyan); }
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
html { scroll-behavior: auto; }
|
|
*, *::before, *::after {
|
|
animation-duration: .001ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: .001ms !important;
|
|
}
|
|
.paso, .salida { opacity: 1 !important; transform: none !important; }
|
|
[data-revela] { opacity: 1 !important; transform: none !important; }
|
|
.campo, .foco, .traza::after { display: none !important; }
|
|
.mapa { transform: none !important; }
|
|
}
|
|
|
|
/* Pantallas chicas: el escenario no necesita tanto aire arriba, y el campo
|
|
de fondo se apaga antes para no competir con el texto. */
|
|
@media (max-width: 640px) {
|
|
.campo { -webkit-mask-image: linear-gradient(to bottom, #000 40%, transparent 78%);
|
|
mask-image: linear-gradient(to bottom, #000 40%, transparent 78%); }
|
|
.portada { padding-top: 44px; }
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="capa">
|
|
<div class="marco">
|
|
|
|
<div class="escenario" id="escenario">
|
|
<canvas id="campo" class="campo" aria-hidden="true"></canvas>
|
|
<div class="foco" id="foco" aria-hidden="true"></div>
|
|
|
|
<nav class="barra">
|
|
<div class="logo"><svg viewBox="0 0 96 96" aria-hidden="true"><path d="M32,42 V58 A14,14 0 0 0 60,58 V42" fill="none" stroke="#06180A" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M60,58 V64" fill="none" stroke="#06180A" stroke-width="10" stroke-linecap="round"/><circle cx="60" cy="28" r="7" fill="#06180A"/></svg></div>
|
|
<div class="logo-txt">uMind</div>
|
|
<div class="chip">IA aplicada</div>
|
|
<div class="barra-fin">U‑SITE</div>
|
|
</nav>
|
|
|
|
<!-- ── Portada ──────────────────────────────────────────────────── -->
|
|
<div class="portada">
|
|
<div>
|
|
<div class="eyebrow">Asistente conversacional</div>
|
|
<h1>No improvisa.<br /><em>Busca en tu información</em> y después contesta.</h1>
|
|
<p class="plomo">
|
|
uMind atiende WhatsApp, Telegram y tu sitio con lo que vos le cargaste:
|
|
tus precios, tus condiciones, tus horarios. Cada respuesta sale de un
|
|
fragmento tuyo, no de lo que el modelo cree recordar.
|
|
</p>
|
|
<div class="acciones">
|
|
<a class="btn btn-solido" href="mailto:soporte@u-site.app?subject=Quiero%20activar%20uMind">Quiero activarlo</a>
|
|
<a class="btn btn-linea" href="#mecanica">Ver cómo funciona</a>
|
|
</div>
|
|
<div class="sello">
|
|
<span>Notas de voz</span>
|
|
<span>Fotos y PDF</span>
|
|
<span>Respuestas con fuente</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- El recorrido real de una consulta, con sus tiempos. -->
|
|
<div class="traza" id="traza">
|
|
<div class="traza-barra">
|
|
<span class="pulso"></span>
|
|
<span>consulta en vivo</span>
|
|
<span style="margin-left:auto">whatsapp · 23:41</span>
|
|
</div>
|
|
|
|
<div class="traza-cuerpo">
|
|
<div class="paso" data-i="0">
|
|
<div class="paso-nombre"><span class="marca-paso">›</span> <b>Mensaje entrante</b></div>
|
|
<div class="ms">nota de voz · 0:07</div>
|
|
</div>
|
|
|
|
<div class="paso" data-i="1">
|
|
<div class="paso-nombre"><span class="marca-paso">›</span> <b>Audio a texto</b></div>
|
|
<div class="ms">1.4 s</div>
|
|
<div class="detalle">“¿hacen envíos a Chía y cuánto sale el combo de dos?”</div>
|
|
</div>
|
|
|
|
<div class="paso" data-i="2">
|
|
<div class="paso-nombre"><span class="marca-paso">›</span> <b>Consulta a vector</b></div>
|
|
<div class="ms">1536 dim · 89 ms</div>
|
|
</div>
|
|
|
|
<div class="paso" data-i="3">
|
|
<div class="paso-nombre"><span class="marca-paso">›</span> <b>Búsqueda en tu conocimiento</b></div>
|
|
<div class="ms">3 de 68 · 24 ms</div>
|
|
<div class="detalle">
|
|
<div class="frag"><span class="frag-nombre">lista-precios-marzo.pdf</span> <em>0.91</em></div>
|
|
<div class="frag"><span class="frag-nombre">Horarios y zonas de entrega</span> <em>0.87</em></div>
|
|
<div class="frag"><span class="frag-nombre">tunegocio.com/envios</span> <em>0.79</em></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="paso" data-i="4">
|
|
<div class="paso-nombre"><span class="marca-paso">›</span> <b>Respuesta generada</b></div>
|
|
<div class="ms">1.1 s</div>
|
|
</div>
|
|
|
|
<div class="salida" id="salida">
|
|
<span class="salida-rotulo">Lo que recibe tu cliente</span>
|
|
<span id="texto-salida"></span><span class="cursor" id="cursor"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Mecánica ───────────────────────────────────────────────────── -->
|
|
<section id="mecanica">
|
|
<div class="duo">
|
|
<div class="texto-bloque" data-revela style="--retraso:0ms">
|
|
<div class="eyebrow">Por qué no inventa</div>
|
|
<h2>Tu información, convertida en algo que se puede buscar</h2>
|
|
<p>
|
|
Cuando cargás una fuente, uMind la parte en fragmentos y convierte cada uno
|
|
en un vector: una posición en un espacio donde lo que significa parecido
|
|
queda cerca.
|
|
</p>
|
|
<p>
|
|
Cuando llega una pregunta, hace lo mismo con la pregunta y se queda con los
|
|
fragmentos <strong>más cercanos</strong>. El modelo redacta la respuesta,
|
|
pero solo con eso. Si tu información no lo dice, no lo dice.
|
|
</p>
|
|
<p>
|
|
Es la diferencia entre un asistente que suena bien y uno en el que podés
|
|
confiar el precio.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="mapa" data-revela style="--retraso:140ms">
|
|
<canvas id="lienzo" aria-label="Representación del espacio vectorial: la consulta encuentra los fragmentos más cercanos"></canvas>
|
|
<div class="mapa-pie">
|
|
<span>espacio vectorial · 68 fragmentos</span>
|
|
<span><em>3 vecinos</em> más cercanos</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ── Fuentes ────────────────────────────────────────────────────── -->
|
|
<section>
|
|
<div class="cabeza" data-revela>
|
|
<div class="eyebrow">Carga de conocimiento</div>
|
|
<h2>Tres formas de enseñarle, ninguna técnica</h2>
|
|
<p class="plomo" style="font-size:1rem">
|
|
Sin entrenar modelos ni escribir reglas. Se carga información y contesta con eso.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="rejilla tres" data-revela style="--retraso:90ms">
|
|
<div class="celda">
|
|
<div class="clave">Escribir</div>
|
|
<h3>Lo que solo sabés vos</h3>
|
|
<p>
|
|
Horarios reales, a qué zonas no llegás, la respuesta que das quince veces
|
|
por día. Lo más valioso de tu negocio no está en ningún documento: acá lo
|
|
escribís una vez y queda.
|
|
</p>
|
|
</div>
|
|
<div class="celda">
|
|
<div class="clave">Subir</div>
|
|
<h3>Los archivos que ya tenés</h3>
|
|
<p>
|
|
PDF, Word, texto, o una foto del papel. La lista de precios, las condiciones
|
|
de garantía, la tabla de tallas. Se leen solos, incluso escaneados.
|
|
</p>
|
|
</div>
|
|
<div class="celda">
|
|
<div class="clave">Conectar</div>
|
|
<h3>Tu sitio web</h3>
|
|
<p>
|
|
Lee tu página entera y aprende de ahí. Y la vuelve a leer sola cada semana,
|
|
así que cuando cambiás un precio, deja de dar el viejo.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ── Frescura ───────────────────────────────────────────────────── -->
|
|
<section>
|
|
<div class="duo espejo">
|
|
<div class="texto-bloque" data-revela style="--retraso:0ms">
|
|
<div class="eyebrow">Trazabilidad</div>
|
|
<h2>Sabés de cuándo es cada cosa que sabe</h2>
|
|
<p>
|
|
El riesgo de un asistente no es que falle: es que conteste con la información
|
|
del año pasado y suene igual de seguro. Por eso cada fuente lleva la fecha en
|
|
que se leyó por última vez, y la que se puso vieja se marca sola.
|
|
</p>
|
|
<p>
|
|
Un botón la actualiza en el momento. Tu sitio se relee solo todas las semanas,
|
|
de madrugada.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="tabla" data-revela style="--retraso:140ms">
|
|
<div class="tabla-cab">base de conocimiento · agente principal</div>
|
|
<div class="fila">
|
|
<div>
|
|
<div class="fila-nombre">Horarios y zonas de entrega</div>
|
|
<div class="fila-sub">nota · 2 fragmentos</div>
|
|
</div>
|
|
<div class="estado ok">leído ayer</div>
|
|
</div>
|
|
<div class="fila">
|
|
<div>
|
|
<div class="fila-nombre">lista-precios-marzo.pdf</div>
|
|
<div class="fila-sub">archivo · 18 fragmentos</div>
|
|
</div>
|
|
<div class="estado ok">leído hace 4 días</div>
|
|
</div>
|
|
<div class="fila">
|
|
<div>
|
|
<div class="fila-nombre">tunegocio.com</div>
|
|
<div class="fila-sub">sitio · 42 fragmentos · auto</div>
|
|
</div>
|
|
<div class="estado ok">leído hoy</div>
|
|
</div>
|
|
<div class="fila">
|
|
<div>
|
|
<div class="fila-nombre">condiciones-garantia.docx</div>
|
|
<div class="fila-sub">archivo · 6 fragmentos</div>
|
|
</div>
|
|
<div class="estado viejo">leído hace 3 meses</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ── Entradas ───────────────────────────────────────────────────── -->
|
|
<section>
|
|
<div class="cabeza" data-revela>
|
|
<div class="eyebrow">Entradas</div>
|
|
<h2>Tus clientes no escriben prolijo</h2>
|
|
<p class="plomo" style="font-size:1rem">
|
|
Mandan audios caminando por la calle, fotos del producto y la factura en PDF.
|
|
uMind convierte todo eso en texto antes de responder.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="rejilla tres" data-revela style="--retraso:90ms">
|
|
<div class="celda">
|
|
<div class="clave">Audio</div>
|
|
<h3>Notas de voz</h3>
|
|
<p>Se transcriben con Whisper y entran a la conversación como si las hubieran escrito. Sin pedirle a nadie que las repita.</p>
|
|
</div>
|
|
<div class="celda">
|
|
<div class="clave">Imagen</div>
|
|
<h3>Fotos y capturas</h3>
|
|
<p>OCR sobre lo que mandan: el comprobante de pago, la etiqueta del producto, la pantalla del error.</p>
|
|
</div>
|
|
<div class="celda">
|
|
<div class="clave">Archivo</div>
|
|
<h3>PDF y Word</h3>
|
|
<p>Abre el adjunto, lee lo que hay adentro y responde sobre eso — con el nombre del archivo y lo que la persona escribió al lado.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ── Canales y control ──────────────────────────────────────────── -->
|
|
<section>
|
|
<div class="duo">
|
|
<div class="texto-bloque" data-revela style="--retraso:0ms">
|
|
<div class="eyebrow">Canales y control</div>
|
|
<h2>El mismo asistente en los tres lados</h2>
|
|
<p>
|
|
<strong>WhatsApp</strong> con tu número de siempre, <strong>Telegram</strong>
|
|
con tu propio bot, y una <strong>burbuja en tu web</strong> que se instala con
|
|
una línea de código. Lo que aprende en uno lo sabe en todos.
|
|
</p>
|
|
<p>
|
|
Desde el panel ves todas las conversaciones y qué contestó en cada una. Si algo
|
|
no te gustó, corregís el conocimiento y desde ese momento responde distinto.
|
|
Cuando hace falta una persona, lo dice y te pasa la conversación.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="metricas" data-revela style="--retraso:140ms" id="metricas">
|
|
<div class="metrica">
|
|
<div class="n" data-cuenta="312">0</div>
|
|
<div class="t">consultas respondidas<br />últimos 7 días</div>
|
|
</div>
|
|
<div class="metrica">
|
|
<div class="n cyan" data-cuenta="41" data-sufijo=" %">0</div>
|
|
<div class="t">fuera del horario<br />de atención</div>
|
|
</div>
|
|
<div class="metrica">
|
|
<div class="n lima" data-cuenta="57">0</div>
|
|
<div class="t">notas de voz<br />entendidas</div>
|
|
</div>
|
|
<div class="metrica">
|
|
<div class="n" data-cuenta="9">0</div>
|
|
<div class="t">pasadas a<br />una persona</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ── Cierre ─────────────────────────────────────────────────────── -->
|
|
<div class="cierre" data-revela>
|
|
<div class="eyebrow" style="justify-content:center">Puesta en marcha</div>
|
|
<h2 style="margin-top:16px">Lo dejamos andando con tu información</h2>
|
|
<p>
|
|
Cargamos lo que ya tenés, lo conectamos a tu WhatsApp y lo probás con tus propias
|
|
preguntas antes de que lo vea un cliente.
|
|
</p>
|
|
<div class="acciones">
|
|
<a class="btn btn-solido" href="mailto:soporte@u-site.app?subject=Quiero%20activar%20uMind">Quiero activarlo</a>
|
|
<a class="btn btn-linea" href="https://u-site.app">Conocer U‑SITE</a>
|
|
</div>
|
|
</div>
|
|
|
|
<footer>
|
|
<span>uMind · producto de <a href="https://u-site.app">U‑SITE S.A.S BIC</a></span>
|
|
<span>soporte@u-site.app</span>
|
|
</footer>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
"use strict";
|
|
|
|
var quieto = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
var toco = window.matchMedia("(hover: none)").matches;
|
|
|
|
/* ── Titular: decodificación ────────────────────────────────────────────
|
|
Cada nodo de texto del h1 se resuelve de izquierda a derecha, como una
|
|
transmisión que se sintoniza. Una vez, al cargar, nunca más — repetirlo
|
|
por sección sería ruido, acá es la apertura de la página. */
|
|
(function () {
|
|
var h1 = document.querySelector(".portada h1");
|
|
if (!h1 || quieto) return;
|
|
|
|
var GLIFOS = "01AEIOU#/·-_{}<>";
|
|
var nodos = [];
|
|
(function juntar(nodo) {
|
|
for (var i = 0; i < nodo.childNodes.length; i++) {
|
|
var hijo = nodo.childNodes[i];
|
|
if (hijo.nodeType === 3 && hijo.textContent.trim() !== "") nodos.push(hijo);
|
|
else if (hijo.nodeType === 1) juntar(hijo);
|
|
}
|
|
})(h1);
|
|
|
|
var VELOCIDAD = 13; // ms por carácter de retraso entre letras
|
|
var acumulado = 0;
|
|
|
|
nodos.forEach(function (nodo) {
|
|
var original = nodo.textContent;
|
|
var largo = original.length;
|
|
|
|
for (var i = 0; i < largo; i++) {
|
|
(function (indice) {
|
|
var retraso = acumulado + indice * VELOCIDAD;
|
|
var vueltas = 5;
|
|
var paso = 0;
|
|
function tick() {
|
|
var actual = nodo.textContent.split("");
|
|
if (paso < vueltas) {
|
|
actual[indice] = original[indice] === " " ? " " : GLIFOS[(Math.random() * GLIFOS.length) | 0];
|
|
nodo.textContent = actual.join("");
|
|
paso++;
|
|
setTimeout(tick, 34);
|
|
} else {
|
|
actual[indice] = original[indice];
|
|
nodo.textContent = actual.join("");
|
|
}
|
|
}
|
|
// Arranca en blanco (no en texto final) para que el efecto se vea.
|
|
setTimeout(function () {
|
|
var relleno = nodo.textContent.split("");
|
|
relleno[indice] = original[indice] === " " ? " " : GLIFOS[0];
|
|
nodo.textContent = relleno.join("");
|
|
tick();
|
|
}, retraso);
|
|
})(i);
|
|
}
|
|
acumulado += largo * VELOCIDAD + 60;
|
|
});
|
|
|
|
// Mientras decodifica, todos los nodos parten en blanco para que no se
|
|
// vea el texto final "de golpe" antes de que arranque la animación.
|
|
nodos.forEach(function (nodo) { nodo.textContent = nodo.textContent.replace(/[^\s]/g, " "); });
|
|
})();
|
|
|
|
/* ── Campo ambiental ─────────────────────────────────────────────────────
|
|
Puntos que derivan despacio detrás de la portada: el mismo lenguaje visual
|
|
del mapa vectorial de más abajo, pero acá sin buscar nada — es sólo el
|
|
clima de la página, no la demostración. */
|
|
(function () {
|
|
var lienzo = document.getElementById("campo");
|
|
var escenario = document.getElementById("escenario");
|
|
var foco = document.getElementById("foco");
|
|
if (!lienzo || !escenario) return;
|
|
var ctx = lienzo.getContext("2d");
|
|
|
|
var puntos = [];
|
|
var caja = { w: 0, h: 0 };
|
|
|
|
function medir() {
|
|
var r = escenario.getBoundingClientRect();
|
|
var dpr = Math.min(window.devicePixelRatio || 1, 1.6);
|
|
caja.w = r.width; caja.h = r.height;
|
|
lienzo.width = Math.max(1, Math.round(caja.w * dpr));
|
|
lienzo.height = Math.max(1, Math.round(caja.h * dpr));
|
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
}
|
|
|
|
function sembrar() {
|
|
var densidad = caja.w < 640 ? 15000 : 9000;
|
|
var n = Math.max(24, Math.min(70, Math.round((caja.w * caja.h) / densidad)));
|
|
puntos = [];
|
|
for (var i = 0; i < n; i++) {
|
|
puntos.push({
|
|
x: Math.random() * caja.w,
|
|
y: Math.random() * caja.h,
|
|
vx: (Math.random() - 0.5) * 0.10,
|
|
vy: (Math.random() - 0.5) * 0.10,
|
|
r: 1 + Math.random() * 1.3
|
|
});
|
|
}
|
|
}
|
|
|
|
var RADIO_ENLACE = 118;
|
|
|
|
function pintar() {
|
|
ctx.clearRect(0, 0, caja.w, caja.h);
|
|
for (var i = 0; i < puntos.length; i++) {
|
|
var a = puntos[i];
|
|
a.x += a.vx; a.y += a.vy;
|
|
if (a.x < -20) a.x = caja.w + 20; if (a.x > caja.w + 20) a.x = -20;
|
|
if (a.y < -20) a.y = caja.h + 20; if (a.y > caja.h + 20) a.y = -20;
|
|
|
|
for (var j = i + 1; j < puntos.length; j++) {
|
|
var b = puntos[j];
|
|
var dx = a.x - b.x, dy = a.y - b.y;
|
|
var d = Math.sqrt(dx * dx + dy * dy);
|
|
if (d < RADIO_ENLACE) {
|
|
ctx.strokeStyle = "rgba(140, 155, 181, " + (0.12 * (1 - d / RADIO_ENLACE)) + ")";
|
|
ctx.lineWidth = 1;
|
|
ctx.beginPath();
|
|
ctx.moveTo(a.x, a.y);
|
|
ctx.lineTo(b.x, b.y);
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
}
|
|
for (var k = 0; k < puntos.length; k++) {
|
|
var p = puntos[k];
|
|
ctx.beginPath();
|
|
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
|
|
ctx.fillStyle = "rgba(150, 168, 196, 0.4)";
|
|
ctx.fill();
|
|
}
|
|
if (!quieto) requestAnimationFrame(pintar);
|
|
}
|
|
|
|
medir(); sembrar();
|
|
if (quieto) { pintar(); } else { requestAnimationFrame(pintar); }
|
|
|
|
var espera1;
|
|
window.addEventListener("resize", function () {
|
|
clearTimeout(espera1);
|
|
espera1 = setTimeout(function () { medir(); sembrar(); if (quieto) pintar(); }, 200);
|
|
});
|
|
|
|
// El foco que sigue el mouse: sólo con puntero fino, y desactivado si el
|
|
// usuario pidió menos movimiento.
|
|
if (!toco && !quieto && foco) {
|
|
escenario.addEventListener("pointermove", function (e) {
|
|
var r = escenario.getBoundingClientRect();
|
|
var mx = ((e.clientX - r.left) / r.width) * 100;
|
|
var my = ((e.clientY - r.top) / r.height) * 100;
|
|
escenario.style.setProperty("--mx", mx + "%");
|
|
escenario.style.setProperty("--my", my + "%");
|
|
escenario.classList.add("con-foco");
|
|
});
|
|
escenario.addEventListener("pointerleave", function () {
|
|
escenario.classList.remove("con-foco");
|
|
});
|
|
}
|
|
})();
|
|
|
|
/* ── Revelado por scroll ─────────────────────────────────────────────────
|
|
Cada bloque marcado con data-revela entra la primera vez que aparece en
|
|
pantalla; una vez visto, queda visto — no vuelve a esconderse al subir. */
|
|
(function () {
|
|
var bloques = document.querySelectorAll("[data-revela]");
|
|
if (!bloques.length) return;
|
|
|
|
if (quieto || typeof IntersectionObserver === "undefined") {
|
|
bloques.forEach(function (b) { b.classList.add("visto"); });
|
|
return;
|
|
}
|
|
|
|
var obs = new IntersectionObserver(function (entradas) {
|
|
entradas.forEach(function (entrada) {
|
|
if (entrada.isIntersecting) {
|
|
entrada.target.classList.add("visto");
|
|
obs.unobserve(entrada.target);
|
|
}
|
|
});
|
|
}, { threshold: 0.16, rootMargin: "0px 0px -8% 0px" });
|
|
|
|
bloques.forEach(function (b) { obs.observe(b); });
|
|
})();
|
|
|
|
/* ── Métricas: conteo ────────────────────────────────────────────────────
|
|
Los números suben desde cero cuando el panel entra en pantalla, una sola
|
|
vez — no cuenta de nuevo si el visitante vuelve a pasar por ahí. */
|
|
(function () {
|
|
var panel = document.getElementById("metricas");
|
|
if (!panel) return;
|
|
var celdas = panel.querySelectorAll("[data-cuenta]");
|
|
|
|
function contar() {
|
|
celdas.forEach(function (celda) {
|
|
var meta = parseInt(celda.getAttribute("data-cuenta"), 10);
|
|
var sufijo = celda.getAttribute("data-sufijo") || "";
|
|
if (quieto) { celda.textContent = meta + sufijo; return; }
|
|
var t0 = performance.now();
|
|
var dur = 900;
|
|
function paso(ahora) {
|
|
var f = Math.min(1, (ahora - t0) / dur);
|
|
var f2 = 1 - Math.pow(1 - f, 3);
|
|
celda.textContent = Math.round(meta * f2) + sufijo;
|
|
if (f < 1) requestAnimationFrame(paso);
|
|
}
|
|
requestAnimationFrame(paso);
|
|
});
|
|
}
|
|
|
|
if (typeof IntersectionObserver === "undefined") { contar(); return; }
|
|
var obs2 = new IntersectionObserver(function (entradas) {
|
|
entradas.forEach(function (entrada) {
|
|
if (entrada.isIntersecting) { contar(); obs2.unobserve(entrada.target); }
|
|
});
|
|
}, { threshold: 0.4 });
|
|
obs2.observe(panel);
|
|
})();
|
|
|
|
/* ── La traza ────────────────────────────────────────────────────────────
|
|
Los pasos aparecen con la latencia que dicen mostrar: si la búsqueda dice
|
|
24 ms y tarda dos segundos en aparecer, la página se contradice sola. */
|
|
(function () {
|
|
var pasos = document.querySelectorAll(".paso");
|
|
var salida = document.getElementById("salida");
|
|
var destino = document.getElementById("texto-salida");
|
|
var cursor = document.getElementById("cursor");
|
|
var frase =
|
|
"Sí, hacemos envíos a Chía: llega al día siguiente y el envío cuesta $12.000. " +
|
|
"El combo de dos está en $89.000.";
|
|
|
|
if (quieto) {
|
|
for (var i = 0; i < pasos.length; i++) pasos[i].classList.add("on");
|
|
salida.classList.add("on");
|
|
destino.textContent = frase;
|
|
cursor.style.display = "none";
|
|
return;
|
|
}
|
|
|
|
var demoras = [500, 1900, 2400, 2700, 3900];
|
|
demoras.forEach(function (ms, i) {
|
|
setTimeout(function () { pasos[i].classList.add("on"); }, ms);
|
|
});
|
|
|
|
setTimeout(function () {
|
|
salida.classList.add("on");
|
|
var n = 0;
|
|
var reloj = setInterval(function () {
|
|
destino.textContent = frase.slice(0, ++n);
|
|
if (n >= frase.length) { clearInterval(reloj); cursor.style.display = "none"; }
|
|
}, 22);
|
|
}, 4400);
|
|
})();
|
|
|
|
/* ── Mapa vectorial ──────────────────────────────────────────────────────
|
|
Canvas y no SVG a mano: son ~70 puntos con posiciones generadas, y
|
|
dibujarlos como paths sería una tira de números que nadie puede leer.
|
|
Muestra lo que hace el buscador: llega la consulta, se iluminan los tres
|
|
fragmentos más cercanos. */
|
|
(function () {
|
|
var lienzo = document.getElementById("lienzo");
|
|
if (!lienzo) return;
|
|
var ctx = lienzo.getContext("2d");
|
|
|
|
var puntos = [];
|
|
var consulta = null;
|
|
var vecinos = [];
|
|
var t0 = 0;
|
|
|
|
function medir() {
|
|
var r = lienzo.getBoundingClientRect();
|
|
var dpr = Math.min(window.devicePixelRatio || 1, 2);
|
|
lienzo.width = Math.max(1, Math.round(r.width * dpr));
|
|
lienzo.height = Math.max(1, Math.round(r.height * dpr));
|
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
return { w: r.width, h: r.height };
|
|
}
|
|
|
|
var caja = medir();
|
|
|
|
// Tres racimos: los fragmentos de una misma fuente caen cerca entre sí,
|
|
// que es exactamente lo que hace que la búsqueda funcione.
|
|
function sembrar() {
|
|
puntos = [];
|
|
var centros = [
|
|
{ x: 0.30, y: 0.34 },
|
|
{ x: 0.68, y: 0.30 },
|
|
{ x: 0.52, y: 0.70 }
|
|
];
|
|
for (var c = 0; c < centros.length; c++) {
|
|
for (var i = 0; i < 22; i++) {
|
|
var a = Math.random() * Math.PI * 2;
|
|
var r = Math.pow(Math.random(), 0.6) * 0.17;
|
|
puntos.push({
|
|
x: (centros[c].x + Math.cos(a) * r) * caja.w,
|
|
y: (centros[c].y + Math.sin(a) * r * 1.15) * caja.h,
|
|
f: 0.4 + Math.random() * 0.6,
|
|
d: Math.random() * Math.PI * 2
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function lanzarConsulta() {
|
|
// La consulta se ubica a cierta distancia de todo: si cae encima de un
|
|
// racimo, los tres vecinos quedan pisados y no se ve que los está
|
|
// buscando, que es justamente lo que la animación tiene que mostrar.
|
|
var minimo = caja.w * 0.11;
|
|
for (var intento = 0; intento < 24; intento++) {
|
|
consulta = { x: (0.22 + Math.random() * 0.56) * caja.w, y: (0.22 + Math.random() * 0.56) * caja.h };
|
|
var masCerca = Infinity;
|
|
for (var j = 0; j < puntos.length; j++) {
|
|
var ax = puntos[j].x - consulta.x, ay = puntos[j].y - consulta.y;
|
|
masCerca = Math.min(masCerca, Math.sqrt(ax * ax + ay * ay));
|
|
}
|
|
if (masCerca > minimo) break;
|
|
}
|
|
|
|
var orden = puntos
|
|
.map(function (p, i) {
|
|
var dx = p.x - consulta.x, dy = p.y - consulta.y;
|
|
return { i: i, d: dx * dx + dy * dy };
|
|
})
|
|
.sort(function (a, b) { return a.d - b.d; });
|
|
vecinos = [orden[0].i, orden[1].i, orden[2].i];
|
|
t0 = performance.now();
|
|
}
|
|
|
|
function pintar(ahora) {
|
|
ctx.clearRect(0, 0, caja.w, caja.h);
|
|
var fase = (ahora - t0) / 1000;
|
|
|
|
// Enlaces a los vecinos, dibujados a medida que "viaja" la búsqueda.
|
|
if (consulta) {
|
|
for (var k = 0; k < vecinos.length; k++) {
|
|
var p = puntos[vecinos[k]];
|
|
var avance = Math.max(0, Math.min(1, (fase - 0.25 - k * 0.16) * 2.4));
|
|
if (avance <= 0) continue;
|
|
ctx.beginPath();
|
|
ctx.moveTo(consulta.x, consulta.y);
|
|
ctx.lineTo(consulta.x + (p.x - consulta.x) * avance, consulta.y + (p.y - consulta.y) * avance);
|
|
ctx.strokeStyle = "rgba(169, 211, 64, " + (0.5 * avance) + ")";
|
|
ctx.lineWidth = 1;
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < puntos.length; i++) {
|
|
var q = puntos[i];
|
|
var cerca = consulta && vecinos.indexOf(i) >= 0;
|
|
var llego = cerca && fase > 0.25 + vecinos.indexOf(i) * 0.16 + 0.4;
|
|
var brillo = 0.30 + Math.sin(ahora / 900 + q.d) * 0.07;
|
|
ctx.beginPath();
|
|
ctx.arc(q.x, q.y, llego ? 4 : 2, 0, Math.PI * 2);
|
|
ctx.fillStyle = llego
|
|
? "rgba(169, 211, 64, 0.95)"
|
|
: "rgba(150, 168, 196, " + (brillo + q.f * 0.22) + ")";
|
|
ctx.fill();
|
|
if (llego) {
|
|
ctx.beginPath();
|
|
ctx.arc(q.x, q.y, 10, 0, Math.PI * 2);
|
|
ctx.strokeStyle = "rgba(169, 211, 64, 0.32)";
|
|
ctx.lineWidth = 1;
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
|
|
if (consulta) {
|
|
var pulso = 5 + Math.sin(ahora / 220) * 1.4;
|
|
ctx.beginPath();
|
|
ctx.arc(consulta.x, consulta.y, pulso, 0, Math.PI * 2);
|
|
ctx.fillStyle = "rgba(61, 220, 232, 0.95)";
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
ctx.arc(consulta.x, consulta.y, pulso + 8, 0, Math.PI * 2);
|
|
ctx.strokeStyle = "rgba(61, 220, 232, 0.28)";
|
|
ctx.lineWidth = 1;
|
|
ctx.stroke();
|
|
}
|
|
|
|
if (fase > 4.5) lanzarConsulta();
|
|
if (!quieto) requestAnimationFrame(pintar);
|
|
}
|
|
|
|
sembrar();
|
|
lanzarConsulta();
|
|
|
|
if (quieto) {
|
|
pintar(performance.now() + 2000);
|
|
} else {
|
|
requestAnimationFrame(pintar);
|
|
}
|
|
|
|
var espera;
|
|
window.addEventListener("resize", function () {
|
|
clearTimeout(espera);
|
|
espera = setTimeout(function () {
|
|
caja = medir();
|
|
sembrar();
|
|
lanzarConsulta();
|
|
if (quieto) pintar(performance.now() + 2000);
|
|
}, 180);
|
|
});
|
|
})();
|
|
|
|
/* ── Inclinación del panel del mapa ──────────────────────────────────────
|
|
Un tilt de un par de grados hacia el cursor — sólo con mouse, nunca en
|
|
touch, donde no significa nada y sólo estorbaría el scroll. */
|
|
(function () {
|
|
if (quieto || toco) return;
|
|
var panel = document.querySelector(".mapa");
|
|
if (!panel) return;
|
|
panel.addEventListener("pointermove", function (e) {
|
|
var r = panel.getBoundingClientRect();
|
|
var px = (e.clientX - r.left) / r.width - 0.5;
|
|
var py = (e.clientY - r.top) / r.height - 0.5;
|
|
panel.style.transform = "perspective(900px) rotateX(" + (py * -5) + "deg) rotateY(" + (px * 6) + "deg)";
|
|
});
|
|
panel.addEventListener("pointerleave", function () {
|
|
panel.style.transform = "perspective(900px) rotateX(0deg) rotateY(0deg)";
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|