+ Por debajo de tu baremo ({formatEuros(baremoMinimo)})
+
+ )}
+ {/* Baremo de rentabilidad */}
+
+
Baremo de rentabilidad
+
+ Importe mínimo de un trabajo para que te resulte rentable. Es solo orientativo para ti: en la
+ ficha de cada lead verás marcados en otro color los presupuestos que no lleguen a este valor.
+ No afecta a lo que ve el cliente ni a la conversación de los agentes. Déjalo vacío para no usar
+ baremo.
+
+
+
+
{/* Extras fijos */}
Extras fijos
diff --git a/mvp/b2c/src/budget/types.ts b/mvp/b2c/src/budget/types.ts
index 0ddd0ae..8e45856 100644
--- a/mvp/b2c/src/budget/types.ts
+++ b/mvp/b2c/src/budget/types.ts
@@ -46,6 +46,7 @@ export interface PricingConfig {
factorZona: Record; // provincia -> multiplicador
manoObra: Record; // céntimos por m² de suelo
extras?: ExtrasFijos; // importes fijos en céntimos
+ baremoMinimo?: number | null; // céntimos; trabajo mínimo rentable (informativo, no lo usan los agentes)
}
export interface BudgetInputs {
diff --git a/mvp/b2c/src/db/pricing-queries.ts b/mvp/b2c/src/db/pricing-queries.ts
index cbcc46a..1e5810a 100644
--- a/mvp/b2c/src/db/pricing-queries.ts
+++ b/mvp/b2c/src/db/pricing-queries.ts
@@ -42,6 +42,7 @@ export async function getPricingConfigFor(tenantId: string): Promise) },
extras: { ...EXTRAS_DEFAULT, ...(row.extras ?? {}) },
+ baremoMinimo: row.baremoMinimo ?? null,
};
}
diff --git a/mvp/b2c/src/db/schema.ts b/mvp/b2c/src/db/schema.ts
index 3810d73..f1ec578 100644
--- a/mvp/b2c/src/db/schema.ts
+++ b/mvp/b2c/src/db/schema.ts
@@ -396,6 +396,10 @@ export const pricingConfig = pgTable('pricing_config', {
.$type<{ tuberias: number; boletin: number; distribucion: number }>()
.notNull()
.default({ tuberias: 0, boletin: 0, distribucion: 0 }),
+ // Baremo de rentabilidad (céntimos): importe mínimo que el reformista considera rentable. Solo
+ // informativo en el panel (marca en otro color los leads por debajo); los agentes NO lo usan para
+ // decidir nada. Null = sin baremo configurado.
+ baremoMinimo: integer('baremo_minimo'),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
});
From d92d5e2f1250bb8b01bcb04e81bce5cb109c5c1d Mon Sep 17 00:00:00 2001
From: Carlos Narro
Date: Thu, 11 Jun 2026 17:19:01 +0200
Subject: [PATCH 2/6] =?UTF-8?q?A=C3=B1ade=20onboarding=20guiado=20del=20pa?=
=?UTF-8?q?nel=20(tour=20con=20driver.js)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Tour por pestañas que explica los puntos clave: en Leads recorre la navegación
(las pestañas secundarias de pasada) + filtros y tabla; en la ficha del lead el
presupuesto/baremo, estado, render y desglose; en Precios el baremo, la mano de
obra y el catálogo. Auto-arranca la primera vez por pestaña (flag en localStorage)
y deja un botón flotante "❓ Tour" para repetir. Pasos sin elemento visible se
descartan (degrada en móvil).
- Dependencia: driver.js (librería estándar de tours, ~5kb, sin más deps;
evita reinventar overlay/posicionamiento/foco/accesibilidad).
- src/lib/onboarding/panel-tour.ts: pasos por ruta. PanelTour.tsx: cliente que
lanza driver.js. data-tour en nav, leads, ficha y precios.
- Copy en COPY-GUIDE.md (sección "Onboarding del panel").
Co-Authored-By: Claude Opus 4.8
---
copy/COPY-GUIDE.md | 34 +++++
mvp/b2c/package-lock.json | 7 +
mvp/b2c/package.json | 1 +
mvp/b2c/src/app/panel/[id]/page.tsx | 33 +++--
mvp/b2c/src/app/panel/layout.tsx | 2 +
mvp/b2c/src/app/panel/page.tsx | 6 +-
mvp/b2c/src/app/panel/precios/page.tsx | 6 +-
mvp/b2c/src/components/AppNav.tsx | 1 +
mvp/b2c/src/components/panel/PanelTour.tsx | 71 ++++++++++
mvp/b2c/src/lib/onboarding/panel-tour.ts | 147 +++++++++++++++++++++
10 files changed, 293 insertions(+), 15 deletions(-)
create mode 100644 mvp/b2c/src/components/panel/PanelTour.tsx
create mode 100644 mvp/b2c/src/lib/onboarding/panel-tour.ts
diff --git a/copy/COPY-GUIDE.md b/copy/COPY-GUIDE.md
index 8463403..91d9769 100644
--- a/copy/COPY-GUIDE.md
+++ b/copy/COPY-GUIDE.md
@@ -714,6 +714,40 @@ del espacio. `[url]` apunta a su formulario personal del funnel. Tono: una sola
---
+## Onboarding del panel (tour guiado)
+
+> Tooltips del tour del panel (driver.js). Tono cercano y útil, una idea por paso, frases cortas. Las pestañas secundarias se explican "de pasada" (una línea). Copy usado en `src/lib/onboarding/panel-tour.ts`.
+
+### Pestaña Leads (`/panel`)
+
+- **Intro** — *Tu panel de Reformix* · "Te enseño en 30 segundos qué hay en cada sitio. Puedes saltártelo cuando quieras con la X."
+- **Leads** — "Aquí caen tus clientes con su presupuesto y su render ya preparados. Es tu día a día."
+- **Precios y baremo** — "Tu tabla de precios, la mano de obra y el baremo de rentabilidad. De aquí salen los presupuestos."
+- **Galería** — "Tus fotos de trabajos para enseñar en la web."
+- **Opiniones** — "Reseñas de tus clientes; las apruebas tú antes de publicarlas."
+- **Empresa** — "Tu marca, logo y datos de contacto."
+- **Filtra por estado** — "Nuevos, contactados, presupuestados… para centrarte en lo que toca ahora."
+- **Tus leads** — "Cada cliente con su estado y su presupuesto. Ábrelo para ver el detalle completo."
+
+### Ficha del lead (`/panel/{id}`)
+
+- **Presupuesto estimado** — "Lo que costaría la reforma según tu catálogo. Si no llega a tu baremo, lo verás en rojo."
+- **Estado del lead** — "Avanza el lead por el funnel: contactado, presupuestado, ganado…"
+- **Render de la reforma** — "La imagen del «después» que ve tu cliente, generada a partir de su foto y sus gustos."
+- **Presupuesto desglosado** — "Edita las partidas, recalcula desde el catálogo y envíaselo al cliente por WhatsApp."
+
+### Precios y baremo (`/panel/precios`)
+
+- **Baremo de rentabilidad** — "El trabajo mínimo que te compensa. Solo te avisa a ti: marca en rojo los leads por debajo."
+- **Mano de obra** — "Tus precios de mano de obra por m². El motor los usa para calcular cada presupuesto."
+- **Tu catálogo** — "Materiales y precios por calidad. Puedes importarlos en bloque por CSV."
+
+### Botón para repetir
+
+- **Botón flotante** — "❓ Tour" (relanza el tour de la pestaña actual).
+
+---
+
## Principios aplicados en todo el documento
1. **Beneficios > Features** — "Tus clientes verán su reforma" > "Render IA con SDXL"
diff --git a/mvp/b2c/package-lock.json b/mvp/b2c/package-lock.json
index 4ca9c0f..6c0a51a 100644
--- a/mvp/b2c/package-lock.json
+++ b/mvp/b2c/package-lock.json
@@ -11,6 +11,7 @@
"@react-pdf/renderer": "^4.5.1",
"@tailwindcss/postcss": "^4.3.0",
"bcryptjs": "^3.0.3",
+ "driver.js": "^1.4.0",
"drizzle-orm": "^0.45.2",
"next": "16.2.6",
"nodemailer": "^8.0.10",
@@ -4546,6 +4547,12 @@
"url": "https://dotenvx.com"
}
},
+ "node_modules/driver.js": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/driver.js/-/driver.js-1.4.0.tgz",
+ "integrity": "sha512-Gm64jm6PmcU+si21sQhBrTAM1JvUrR0QhNmjkprNLxohOBzul9+pNHXgQaT9lW84gwg9GMLB3NZGuGolsz5uew==",
+ "license": "MIT"
+ },
"node_modules/drizzle-kit": {
"version": "0.31.10",
"resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.10.tgz",
diff --git a/mvp/b2c/package.json b/mvp/b2c/package.json
index 8199518..962d3cb 100644
--- a/mvp/b2c/package.json
+++ b/mvp/b2c/package.json
@@ -21,6 +21,7 @@
"@react-pdf/renderer": "^4.5.1",
"@tailwindcss/postcss": "^4.3.0",
"bcryptjs": "^3.0.3",
+ "driver.js": "^1.4.0",
"drizzle-orm": "^0.45.2",
"next": "16.2.6",
"nodemailer": "^8.0.10",
diff --git a/mvp/b2c/src/app/panel/[id]/page.tsx b/mvp/b2c/src/app/panel/[id]/page.tsx
index ada2a1c..d4e30ba 100644
--- a/mvp/b2c/src/app/panel/[id]/page.tsx
+++ b/mvp/b2c/src/app/panel/[id]/page.tsx
@@ -20,9 +20,20 @@ import type { BudgetResult } from '@/budget/types';
export const dynamic = 'force-dynamic';
-function Section({ title, children }: { title: string; children: React.ReactNode }) {
+function Section({
+ title,
+ children,
+ tour,
+}: {
+ title: string;
+ children: React.ReactNode;
+ tour?: string;
+}) {
return (
-
+
);
}
diff --git a/mvp/b2c/src/app/panel/precios/page.tsx b/mvp/b2c/src/app/panel/precios/page.tsx
index c45415b..d8bee7c 100644
--- a/mvp/b2c/src/app/panel/precios/page.tsx
+++ b/mvp/b2c/src/app/panel/precios/page.tsx
@@ -82,7 +82,7 @@ export default async function PreciosPage() {
{/* Config general */}
-
+
Configuración general
{/* Baremo de rentabilidad */}
-
+
Baremo de rentabilidad
Importe mínimo de un trabajo para que te resulte rentable. Es solo orientativo para ti: en la
@@ -285,7 +285,7 @@ export default async function PreciosPage() {
})}
{/* Import CSV */}
-
+
Importar catálogo (CSV)
Cabecera: categoria,nombre,calidad,precio,unidad,descriptor_render,sku. El
diff --git a/mvp/b2c/src/components/AppNav.tsx b/mvp/b2c/src/components/AppNav.tsx
index 2d47f81..953fbd8 100644
--- a/mvp/b2c/src/components/AppNav.tsx
+++ b/mvp/b2c/src/components/AppNav.tsx
@@ -116,6 +116,7 @@ export default function AppNav({ links }: { links: readonly AppNavLink[] }) {
{l.label}
diff --git a/mvp/b2c/src/components/panel/PanelTour.tsx b/mvp/b2c/src/components/panel/PanelTour.tsx
new file mode 100644
index 0000000..9e62b38
--- /dev/null
+++ b/mvp/b2c/src/components/panel/PanelTour.tsx
@@ -0,0 +1,71 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import { usePathname } from 'next/navigation';
+import { driver, type DriveStep } from 'driver.js';
+import 'driver.js/dist/driver.css';
+import { tourForPath } from '@/lib/onboarding/panel-tour';
+
+const SEEN_PREFIX = 'reformix_tour_v1_';
+
+// Onboarding del panel con driver.js. Lanza el tour de la pestaña actual la primera vez que se
+// visita (flag por pestaña en localStorage) y deja un botón flotante para repetirlo. Los pasos
+// cuyo elemento no exista o esté oculto (p. ej. la nav de escritorio en móvil) se descartan.
+export default function PanelTour() {
+ const pathname = usePathname();
+ const [hayTour, setHayTour] = useState(false);
+
+ useEffect(() => {
+ const tour = tourForPath(pathname);
+ setHayTour(Boolean(tour));
+ if (!tour) return;
+ if (localStorage.getItem(SEEN_PREFIX + tour.key) === '1') return;
+
+ // Espera a que el contenido de la página esté montado antes de resaltar.
+ const t = setTimeout(() => {
+ localStorage.setItem(SEEN_PREFIX + tour.key, '1');
+ lanzar(tour.steps);
+ }, 700);
+ return () => clearTimeout(t);
+ }, [pathname]);
+
+ function visibles(steps: DriveStep[]): DriveStep[] {
+ return steps.filter((s) => {
+ const sel = s.element;
+ if (!sel || typeof sel !== 'string') return true; // paso centrado (intro)
+ const el = document.querySelector(sel) as HTMLElement | null;
+ return !!el && el.offsetParent !== null;
+ });
+ }
+
+ function lanzar(steps: DriveStep[]) {
+ const pasos = visibles(steps);
+ if (pasos.length === 0) return;
+ driver({
+ showProgress: true,
+ overlayColor: '#0b1220',
+ nextBtnText: 'Siguiente',
+ prevBtnText: 'Atrás',
+ doneBtnText: 'Listo',
+ progressText: '{{current}} de {{total}}',
+ steps: pasos,
+ }).drive();
+ }
+
+ function repetir() {
+ const tour = tourForPath(pathname);
+ if (tour) lanzar(tour.steps);
+ }
+
+ if (!hayTour) return null;
+ return (
+
+ );
+}
diff --git a/mvp/b2c/src/lib/onboarding/panel-tour.ts b/mvp/b2c/src/lib/onboarding/panel-tour.ts
new file mode 100644
index 0000000..8a634bd
--- /dev/null
+++ b/mvp/b2c/src/lib/onboarding/panel-tour.ts
@@ -0,0 +1,147 @@
+import type { DriveStep } from 'driver.js';
+
+// Pasos del onboarding del panel, por pestaña. El copy vive también en copy/COPY-GUIDE.md
+// (sección "Onboarding del panel"). Los pasos cuyo elemento no exista o no esté visible se
+// descartan en PanelTour (degrada con naturalidad en móvil o si una sección no aparece).
+
+const PASOS_PANEL: DriveStep[] = [
+ {
+ popover: {
+ title: 'Tu panel de Reformix',
+ description:
+ 'Te enseño en 30 segundos qué hay en cada sitio. Puedes saltártelo cuando quieras con la X.',
+ },
+ },
+ {
+ element: '[data-tour="nav-leads"]',
+ popover: {
+ title: 'Leads',
+ description:
+ 'Aquí caen tus clientes con su presupuesto y su render ya preparados. Es tu día a día.',
+ side: 'bottom',
+ },
+ },
+ {
+ element: '[data-tour="nav-precios"]',
+ popover: {
+ title: 'Precios y baremo',
+ description:
+ 'Tu tabla de precios, la mano de obra y el baremo de rentabilidad. De aquí salen los presupuestos.',
+ side: 'bottom',
+ },
+ },
+ {
+ element: '[data-tour="nav-galeria"]',
+ popover: { title: 'Galería', description: 'Tus fotos de trabajos para enseñar en la web.', side: 'bottom' },
+ },
+ {
+ element: '[data-tour="nav-opiniones"]',
+ popover: {
+ title: 'Opiniones',
+ description: 'Reseñas de tus clientes; las apruebas tú antes de publicarlas.',
+ side: 'bottom',
+ },
+ },
+ {
+ element: '[data-tour="nav-empresa"]',
+ popover: { title: 'Empresa', description: 'Tu marca, logo y datos de contacto.', side: 'bottom' },
+ },
+ {
+ element: '[data-tour="leads-filtros"]',
+ popover: {
+ title: 'Filtra por estado',
+ description: 'Nuevos, contactados, presupuestados… para centrarte en lo que toca ahora.',
+ side: 'bottom',
+ },
+ },
+ {
+ element: '[data-tour="leads-tabla"]',
+ popover: {
+ title: 'Tus leads',
+ description: 'Cada cliente con su estado y su presupuesto. Ábrelo para ver el detalle completo.',
+ side: 'top',
+ },
+ },
+];
+
+const PASOS_FICHA: DriveStep[] = [
+ {
+ element: '[data-tour="ficha-presupuesto"]',
+ popover: {
+ title: 'Presupuesto estimado',
+ description:
+ 'Lo que costaría la reforma según tu catálogo. Si no llega a tu baremo, lo verás en rojo.',
+ side: 'bottom',
+ },
+ },
+ {
+ element: '[data-tour="ficha-estado"]',
+ popover: {
+ title: 'Estado del lead',
+ description: 'Avanza el lead por el funnel: contactado, presupuestado, ganado…',
+ side: 'bottom',
+ },
+ },
+ {
+ element: '[data-tour="ficha-render"]',
+ popover: {
+ title: 'Render de la reforma',
+ description:
+ 'La imagen del “después” que ve tu cliente, generada a partir de su foto y sus gustos.',
+ side: 'top',
+ },
+ },
+ {
+ element: '[data-tour="ficha-desglose"]',
+ popover: {
+ title: 'Presupuesto desglosado',
+ description:
+ 'Edita las partidas, recalcula desde el catálogo y envíaselo al cliente por WhatsApp.',
+ side: 'top',
+ },
+ },
+];
+
+const PASOS_PRECIOS: DriveStep[] = [
+ {
+ element: '[data-tour="precios-baremo"]',
+ popover: {
+ title: 'Baremo de rentabilidad',
+ description:
+ 'El trabajo mínimo que te compensa. Solo te avisa a ti: marca en rojo los leads por debajo.',
+ side: 'bottom',
+ },
+ },
+ {
+ element: '[data-tour="precios-config"]',
+ popover: {
+ title: 'Mano de obra',
+ description: 'Tus precios de mano de obra por m². El motor los usa para calcular cada presupuesto.',
+ side: 'top',
+ },
+ },
+ {
+ element: '[data-tour="precios-catalogo"]',
+ popover: {
+ title: 'Tu catálogo',
+ description: 'Materiales y precios por calidad. Puedes importarlos en bloque por CSV.',
+ side: 'top',
+ },
+ },
+];
+
+export interface PanelTour {
+ key: string;
+ steps: DriveStep[];
+}
+
+// Devuelve el tour que corresponde a la ruta actual del panel, o null si esa ruta no tiene tour.
+export function tourForPath(pathname: string): PanelTour | null {
+ if (pathname === '/panel') return { key: 'panel', steps: PASOS_PANEL };
+ if (pathname === '/panel/precios') return { key: 'precios', steps: PASOS_PRECIOS };
+ const m = pathname.match(/^\/panel\/([^/]+)\/?$/);
+ if (m && !['precios', 'galeria', 'opiniones', 'empresa'].includes(m[1])) {
+ return { key: 'ficha', steps: PASOS_FICHA };
+ }
+ return null;
+}
From df700bcbfb73b2849737bf21504e77a23eb77d6c Mon Sep 17 00:00:00 2001
From: Carlos Narro
Date: Thu, 11 Jun 2026 17:25:15 +0200
Subject: [PATCH 3/6] =?UTF-8?q?Landing=20B2B:=20galer=C3=ADa=20de=20ejempl?=
=?UTF-8?q?os=20apaisada=20con=20lightbox=20y=20quita=20"Entrar"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Nueva sección "Ejemplos reales": 3 renders (cocina, baño, salón-comedor) en
formato apaisado (16/10), clicables.
- Lightbox reutilizable (data-zoom + data-gallery): amplía la imagen con
navegación ‹ ›, Esc y clic fuera para cerrar; agrupa por galería.
- El antes/después de "Cómo funciona" pasa a apaisado (4/3) y también abre el
lightbox (grupo cocina).
- Quita el botón "Entrar" del header (queda solo "Empezar gratis").
Co-Authored-By: Claude Opus 4.8
---
mvp/b2c/public/b2b.html | 126 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 121 insertions(+), 5 deletions(-)
diff --git a/mvp/b2c/public/b2b.html b/mvp/b2c/public/b2b.html
index cd026cb..21a2773 100644
--- a/mvp/b2c/public/b2b.html
+++ b/mvp/b2c/public/b2b.html
@@ -1472,8 +1472,8 @@ h3, h4, h5, h6 {
overflow: hidden;
border: 1px solid var(--surface-border);
}
- .step .ba figure { position: relative; margin: 0; aspect-ratio: 1 / 1; }
- .step .ba img { width: 100%; height: 100%; object-fit: cover; display: block; }
+ .step .ba figure { position: relative; margin: 0; aspect-ratio: 4 / 3; }
+ .step .ba img { width: 100%; height: 100%; object-fit: cover; display: block; cursor: zoom-in; }
.step .ba figcaption {
position: absolute; top: 6px; left: 6px;
background: rgba(0, 0, 0, 0.6); color: #fff;
@@ -1482,6 +1482,34 @@ h3, h4, h5, h6 {
}
.step .ba figure.after figcaption { background: var(--color-primary-600); }
+ /* Galería de ejemplos (apaisada, ampliable) */
+ .gallery-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-4); margin-top: var(--space-8); }
+ @media (min-width: 640px) { .gallery-grid { grid-template-columns: repeat(2, 1fr); } }
+ @media (min-width: 1024px) { .gallery-grid { grid-template-columns: repeat(3, 1fr); gap: var(--space-6); } }
+ .gcard {
+ position: relative; margin: 0; border-radius: 14px; overflow: hidden;
+ border: 1px solid var(--surface-border); cursor: zoom-in;
+ background: var(--color-neutral-100, #f4f4f5);
+ }
+ .gcard img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; display: block; transition: transform .45s ease; }
+ .gcard:hover img { transform: scale(1.04); }
+ .gcard figcaption {
+ position: absolute; left: 10px; bottom: 10px;
+ background: rgba(0, 0, 0, 0.62); color: #fff;
+ font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.06em;
+ padding: 3px 8px; border-radius: 6px;
+ }
+
+ /* Lightbox */
+ #lightbox { position: fixed; inset: 0; z-index: 100; display: none; align-items: center; justify-content: center; padding: 24px; background: rgba(8, 12, 20, 0.92); }
+ #lightbox.open { display: flex; }
+ #lightbox .lb-img { max-width: min(1100px, 94vw); max-height: 86vh; width: auto; height: auto; border-radius: 12px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); }
+ #lightbox .lb-cap { position: absolute; bottom: 18px; left: 0; right: 0; text-align: center; color: rgba(255, 255, 255, 0.85); font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.06em; padding: 0 16px; }
+ #lightbox .lb-close { position: absolute; top: 16px; right: 18px; width: 40px; height: 40px; border: none; border-radius: 50%; background: rgba(255, 255, 255, 0.12); color: #fff; font-size: 22px; line-height: 1; cursor: pointer; }
+ #lightbox .lb-btn { position: absolute; top: 50%; transform: translateY(-50%); width: 46px; height: 46px; border: none; border-radius: 50%; background: rgba(255, 255, 255, 0.12); color: #fff; font-size: 26px; line-height: 1; cursor: pointer; }
+ #lightbox .lb-prev { left: 18px; } #lightbox .lb-next { right: 18px; }
+ #lightbox .lb-close:hover, #lightbox .lb-btn:hover { background: rgba(255, 255, 255, 0.26); }
+
/* Bajo reduced-motion: mostrar todo sin animar */
@media (prefers-reduced-motion: reduce) {
html.js .reveal,
@@ -1508,7 +1536,6 @@ h3, h4, h5, h6 {
Preguntas
A partir de las fotos del cliente, Reformix devuelve 3 propuestas visuales con calidades básica, media y premium. Ya tienes de qué hablar en la visita.
- ANTES
- DESPUÉS
+ ANTES
+ DESPUÉS
⌁ Render IA
@@ -1726,6 +1753,33 @@ h3, h4, h5, h6 {
+
+
+
+
+
Ejemplos reales
+
El «después» que tu cliente ve antes de que vayas.
+
Una foto del espacio y Reformix devuelve el render orientativo. Toca cualquiera para ampliarla.