El criterio de un abogado senior,{' '}
amplificado por IA.
Mentaly analiza expedientes de hasta 10.000 páginas, contrasta jurisprudencia
del CENDOJ y razona con 4 perspectivas distintas.
Pregunta ahora, sin registro.
);
}
/* ============= DASHBOARD MOCKUP ============= */
function DashboardMockup() {
const [ref, shown] = useReveal(0.18);
const fullUserQ = "¿La cláusula suelo de esta escritura supera el doble control de transparencia o es nula?";
const fullAnswer = "La escritura no acredita información precontractual real sobre el riesgo de oscilación a la baja, por lo que no supera el doble control de transparencia exigido por el TS (STS 9/05/2013, Pleno).";
const [userChars, setUserChars] = useState(0);
const [aiChars, setAiChars] = useState(0);
const [pct, setPct] = useState(0);
const [stage, setStage] = useState(0); // 0=idle 1=upload 2=ocr 3=user 4=thinking 5=ai 6=done
const [uploadPct, setUploadPct] = useState(0);
const [ocrPage, setOcrPage] = useState(0);
const [sourcesShown, setSourcesShown] = useState(0);
useEffect(() => {
if (!shown) return;
let cancelled = false;
const timers = [];
const intervals = [];
const wait = (ms) => new Promise((res) => {
const t = setTimeout(() => res(), ms);
timers.push(t);
});
const runOnce = async () => {
// reset
setUserChars(0); setAiChars(0); setPct(0);
setUploadPct(0); setOcrPage(0); setSourcesShown(0);
// Stage 1 — upload PDF
setStage(1);
let up = 0;
await new Promise((res) => {
const id = setInterval(() => {
up += 4 + Math.random() * 6;
if (up >= 100) { up = 100; clearInterval(id); res(); }
setUploadPct(Math.round(up));
}, 40);
intervals.push(id);
});
if (cancelled) return;
await wait(280);
// Stage 2 — OCR
setStage(2);
const total = 412;
let p = 0;
await new Promise((res) => {
const id = setInterval(() => {
p += 28 + Math.floor(Math.random() * 14);
if (p >= total) { p = total; clearInterval(id); res(); }
setOcrPage(p);
}, 60);
intervals.push(id);
});
if (cancelled) return;
await wait(420);
// Stage 3 — User types
setStage(3);
await new Promise((res) => {
let u = 0;
const id = setInterval(() => {
u += 2;
setUserChars(u);
if (u >= fullUserQ.length) { clearInterval(id); res(); }
}, 22);
intervals.push(id);
});
if (cancelled) return;
await wait(320);
// Stage 4 — Thinking
setStage(4);
await wait(950);
if (cancelled) return;
// Stage 5 — AI answer (count-up + typewriter parallel)
setStage(5);
const cntId = setInterval(() => {
setPct(v => {
const nx = v + 2.6;
if (nx >= 84.7) { clearInterval(cntId); return 84.7; }
return nx;
});
}, 28);
intervals.push(cntId);
await new Promise((res) => {
let a = 0;
const id = setInterval(() => {
a += 3;
setAiChars(a);
if (a >= fullAnswer.length) { clearInterval(id); res(); }
}, 22);
intervals.push(id);
});
if (cancelled) return;
setStage(6);
// Stage 6 — sources fade-in
for (let i = 0; i < 4; i++) {
await wait(220);
if (cancelled) return;
setSourcesShown(n => Math.max(n, i+1));
}
// Pause then restart
await wait(3800);
if (!cancelled) runOnce();
};
runOnce();
return () => {
cancelled = true;
timers.forEach(clearTimeout);
intervals.forEach(clearInterval);
};
}, [shown]);
const userText = fullUserQ.slice(0, Math.min(userChars, fullUserQ.length));
const aiText = fullAnswer.slice(0, Math.min(aiChars, fullAnswer.length));
const aiDone = aiChars >= fullAnswer.length;
const ratioModes = [
{ id: 'general', icon: 'scale', tip: 'General', accent: 'indigo' },
{ id: 'devil', icon: 'gavel', tip: 'Adverso', accent: 'red' },
{ id: 'judge', icon: 'judge', tip: 'Juez', accent: 'amber' },
{ id: 'data', icon: 'chart', tip: 'Datos', accent: 'emerald' },
];
return (
Cada respuesta cita la fuente. Haz clic en cualquier organismo para visitar su web oficial.
);
}
/* ============= BENEFICIOS — 4 cards ============= */
function Beneficios() {
return (
Tareas que te ahorran horas esta semana.>}
kicker="No es un asistente más. Es un cambio en cómo abordas cada expediente."
>
}
/>
}
/>
}
/>
}
/>
);
}
function BeneCard({ title, desc, visual }) {
return (