¿Listo para llevar tu marca al siguiente nivel?

En vectorstands, entendemos que tu presencia en una feria o congreso es la inversión más importante para tu negocio. Por eso, ponemos a tu disposición un equipo de élite, liderado por profesionales certificados, comprometidos con la excelencia, la responsabilidad y el detalle impecable.
No solo construimos stands; creamos plataformas de éxito que proyectan la calidad de tu empresa en cada rincón de México y Latinoamérica. Ya sea que necesites una solución llave en mano o una producción audiovisual de vanguardia, estamos aquí para hacerlo realidad con el más alto estándar de profesionalismo.
Hablemos hoy mismo. Tu próxima gran experiencia comienza con una llamada.
contamos con taller fisico en los principales destinos del pais
MEXICO Direccion> Tercera Cerrada de, Filemón Morlan, San Mateo Ixtacalco, 54840 Cuautitlán, Méx.
GUADALAJARA Direccion>
PUERTO VALLARTA Direccion>
CANCUN Direccion>
CANCUN – PUERTO VALLARTA – CIUDAD DE MEXICO – GUADALAJARA
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cotizador VectorStands</title>
<style>
:root {
--primary: #1a1a2e;
--accent: #e94560;
--gold: #f5a623;
--light: #f8f9fa;
--border: #dee2e6;
--text: #2d2d2d;
--muted: #6c757d;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Segoe UI', Arial, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
min-height: 100vh;
color: var(--text);
padding: 30px 16px;
}
/* ─── HEADER ─────────────────────────────────────────── */
.header {
text-align: center;
margin-bottom: 36px;
}
.logo-box {
display: inline-flex;
align-items: center;
gap: 14px;
background: rgba(255,255,255,0.07);
border: 1px solid rgba(255,255,255,0.15);
border-radius: 16px;
padding: 18px 36px;
backdrop-filter: blur(10px);
}
.logo-icon {
width: 56px; height: 56px;
background: linear-gradient(135deg, var(--accent), var(--gold));
border-radius: 12px;
display: flex; align-items: center; justify-content: center;
font-size: 28px;
}
.logo-text h1 {
font-size: 2rem; font-weight: 800;
color: #fff; letter-spacing: 1px;
}
.logo-text p {
font-size: 0.85rem; color: rgba(255,255,255,0.6);
letter-spacing: 2px; text-transform: uppercase;
}
/* ─── MAIN CARD ──────────────────────────────────────── */
.card {
background: #fff;
border-radius: 20px;
box-shadow: 0 24px 64px rgba(0,0,0,0.35);
max-width: 900px;
margin: 0 auto 30px;
overflow: hidden;
}
.card-header {
background: linear-gradient(90deg, var(--primary), #0f3460);
color: #fff;
padding: 22px 32px;
display: flex; align-items: center; gap: 12px;
}
.card-header svg { width: 22px; height: 22px; opacity: .8; }
.card-header h2 { font-size: 1.1rem; font-weight: 600; }
.card-body { padding: 32px; }
/* ─── FORM ────────────────────────────────────────────── */
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 28px;
}
.form-full { grid-column: 1 / -1; }
label {
display: block;
font-size: 0.8rem; font-weight: 600;
color: var(--muted);
text-transform: uppercase;
letter-spacing: .6px;
margin-bottom: 6px;
}
input, select, textarea {
width: 100%;
padding: 11px 14px;
border: 1.5px solid var(--border);
border-radius: 10px;
font-size: 0.95rem;
color: var(--text);
transition: border-color .2s, box-shadow .2s;
outline: none;
background: #fafafa;
}
input:focus, select:focus, textarea:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(233,69,96,0.12);
background: #fff;
}
textarea { resize: vertical; min-height: 70px; }
/* ─── STANDS SELECTOR ────────────────────────────────── */
.stands-selector {
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
border: 2px dashed #ced4da;
border-radius: 16px;
padding: 28px;
text-align: center;
margin-bottom: 28px;
}
.stands-selector h3 {
font-size: 1rem; font-weight: 700;
color: var(--primary); margin-bottom: 20px;
text-transform: uppercase; letter-spacing: .8px;
}
.counter-row {
display: flex;
align-items: center;
justify-content: center;
gap: 0;
}
.btn-counter {
width: 50px; height: 50px;
border: none; cursor: pointer;
background: var(--primary); color: #fff;
font-size: 1.6rem; font-weight: 300;
border-radius: 12px 0 0 12px;
transition: background .2s;
display: flex; align-items: center; justify-content: center;
line-height: 1;
}
.btn-counter.plus { border-radius: 0 12px 12px 0; }
.btn-counter:hover { background: var(--accent); }
#standCount {
width: 90px; height: 50px;
border: 2px solid var(--primary);
border-left: none; border-right: none;
border-radius: 0;
text-align: center;
font-size: 1.5rem; font-weight: 800;
color: var(--primary);
background: #fff;
padding: 0;
}
.stand-type-row {
margin-top: 18px;
display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;
}
.type-chip {
padding: 8px 20px;
border-radius: 50px;
border: 2px solid var(--border);
background: #fff;
cursor: pointer;
font-size: 0.85rem; font-weight: 600;
color: var(--muted);
transition: all .2s;
}
.type-chip.active, .type-chip:hover {
border-color: var(--accent);
background: var(--accent);
color: #fff;
}
/* ─── QUOTE PREVIEW ──────────────────────────────────── */
#quotePreview { display: none; }
.quote-section-title {
font-size: 0.75rem; font-weight: 700;
text-transform: uppercase; letter-spacing: 1px;
color: var(--muted);
margin-bottom: 14px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border);
}
.materials-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 24px;
font-size: 0.92rem;
}
.materials-table thead th {
background: var(--primary);
color: #fff;
padding: 12px 16px;
text-align: left;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: .5px;
}
.materials-table thead th:last-child { text-align: right; }
.materials-table tbody tr:nth-child(even) { background: #f8f9fa; }
.materials-table tbody tr:hover { background: #fff3f5; }
.materials-table tbody td {
padding: 13px 16px;
border-bottom: 1px solid #f0f0f0;
color: var(--text);
}
.materials-table tbody td:last-child {
text-align: right; font-weight: 600;
}
.materials-table tbody td:nth-child(2),
.materials-table tbody td:nth-child(3) {
text-align: center; color: var(--muted);
}
.mat-icon {
display: inline-block;
width: 28px; height: 28px;
border-radius: 6px;
margin-right: 8px;
vertical-align: middle;
line-height: 28px;
text-align: center;
font-size: 14px;
}
/* ─── TOTALS ─────────────────────────────────────────── */
.totals-box {
background: linear-gradient(135deg, var(--primary), #0f3460);
border-radius: 14px;
padding: 24px 28px;
color: #fff;
margin-bottom: 24px;
}
.totals-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
font-size: 0.95rem;
}
.totals-row.divider {
border-top: 1px solid rgba(255,255,255,0.2);
margin-top: 8px;
padding-top: 16px;
}
.totals-row.grand {
font-size: 1.3rem; font-weight: 800;
color: var(--gold);
}
.totals-row span:first-child { opacity: .8; }
.totals-row span:last-child { font-weight: 600; }
/* ─── NOTES ──────────────────────────────────────────── */
.notes-box {
background: #fffbf0;
border: 1px solid #f5e0a0;
border-radius: 10px;
padding: 16px 20px;
margin-bottom: 24px;
font-size: 0.85rem;
color: #7a6a20;
}
.notes-box strong { display: block; margin-bottom: 6px; font-size: 0.8rem; text-transform: uppercase; letter-spacing: .5px; }
/* ─── ACTIONS ────────────────────────────────────────── */
.actions {
display: flex; gap: 14px; flex-wrap: wrap;
justify-content: flex-end;
}
.btn {
padding: 13px 28px;
border-radius: 10px;
font-size: 0.9rem; font-weight: 700;
cursor: pointer; border: none;
display: inline-flex; align-items: center; gap: 8px;
transition: all .2s;
text-transform: uppercase; letter-spacing: .5px;
}
.btn-primary {
background: linear-gradient(135deg, var(--accent), #c0392b);
color: #fff;
box-shadow: 0 4px 15px rgba(233,69,96,0.4);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(233,69,96,0.5);
}
.btn-outline {
background: transparent;
border: 2px solid var(--primary);
color: var(--primary);
}
.btn-outline:hover { background: var(--primary); color: #fff; }
/* ─── QUOTE HEADER (inside preview) ─────────────────── */
.quote-header-box {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 28px;
padding-bottom: 20px;
border-bottom: 2px solid var(--border);
}
.company-info h3 {
font-size: 1.3rem; font-weight: 800;
color: var(--primary); margin-bottom: 4px;
}
.company-info p { font-size: 0.82rem; color: var(--muted); line-height: 1.6; }
.quote-meta { text-align: right; }
.quote-meta .quote-num {
font-size: 1rem; font-weight: 700;
color: var(--accent); margin-bottom: 8px;
}
.quote-meta p { font-size: 0.82rem; color: var(--muted); line-height: 1.7; }
.badge {
display: inline-block;
padding: 4px 12px;
border-radius: 50px;
font-size: 0.72rem; font-weight: 700;
text-transform: uppercase; letter-spacing: .5px;
}
.badge-valid { background: #d4edda; color: #155724; }
.client-info-box {
background: #f8f9fa;
border-radius: 12px;
padding: 18px 22px;
margin-bottom: 24px;
}
.client-info-box .ci-title {
font-size: 0.75rem; font-weight: 700;
color: var(--muted); text-transform: uppercase;
letter-spacing: .8px; margin-bottom: 10px;
}
.client-info-box .ci-name {
font-size: 1.05rem; font-weight: 700; color: var(--primary);
}
.client-info-box .ci-detail {
font-size: 0.85rem; color: var(--muted); margin-top: 2px;
}
/* ─── PRINT ──────────────────────────────────────────── */
@media print {
body {
background: #fff !important;
padding: 0 !important;
}
.header, .card-header svg,
#formSection, .actions, .stands-selector { display: none !important; }
.card {
box-shadow: none;
border-radius: 0;
max-width: 100%;
}
.card-header { display: none !important; }
#quotePreview { display: block !important; }
.totals-box {
background: var(--primary) !important;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.materials-table thead th {
background: var(--primary) !important;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}
@media (max-width: 600px) {
.form-grid { grid-template-columns: 1fr; }
.quote-header-box { grid-template-columns: 1fr; }
.quote-meta { text-align: left; }
.actions { justify-content: center; }
}
</style>
</head>
<body>
<!-- HEADER -->
<div class="header">
<div class="logo-box">
<div class="logo-icon">🏗️</div>
<div class="logo-text">
<h1>VectorStands</h1>
<p>Sistema de Cotización</p>
</div>
</div>
</div>
<!-- FORM CARD -->
<div class="card" id="formSection">
<div class="card-header">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"/>
<rect x="9" y="3" width="6" height="4" rx="1"/>
</svg>
<h2>Datos del Cliente</h2>
</div>
<div class="card-body">
<div class="form-grid">
<div>
<label>Nombre / Empresa</label>
<input type="text" id="clientName" placeholder="Ej. Distribuidora García S.A." />
</div>
<div>
<label>RFC</label>
<input type="text" id="clientRFC" placeholder="Ej. GAR123456789" />
</div>
<div>
<label>Teléfono</label>
<input type="text" id="clientPhone" placeholder="Ej. 55 1234 5678" />
</div>
<div>
<label>Correo Electrónico</label>
<input type="email" id="clientEmail" placeholder="cliente@empresa.com" />
</div>
<div class="form-full">
<label>Notas / Observaciones (opcional)</label>
<textarea id="clientNotes" placeholder="Ej. Entrega en bodega, color especial, fecha requerida..."></textarea>
</div>
</div>
<!-- STANDS SELECTOR -->
<div class="stands-selector">
<h3>¿Cuántos Stands necesita?</h3>
<div class="counter-row">
<button class="btn-counter minus" onclick="changeCount(-1)">−</button>
<input type="number" id="standCount" value="1" min="1" max="999" onchange="updateQuote()" oninput="updateQuote()" />
<button class="btn-counter plus" onclick="changeCount(1)">+</button>
</div>
<div class="stand-type-row">
<div class="type-chip active" onclick="selectType(this, '3x3')">Stand 3×3 m</div>
</div>
</div>
<div class="actions">
<button class="btn btn-primary" onclick="generateQuote()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
</svg>
Generar Cotización
</button>
</div>
</div>
</div>
<!-- QUOTE CARD -->
<div class="card">
<div class="card-header" id="quoteCardHeader" style="display:none;">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
<polyline points="14 2 14 8 20 8"/>
</svg>
<h2>Cotización Formal</h2>
</div>
<div class="card-body">
<div id="quotePreview">
<!-- Quote Header -->
<div class="quote-header-box">
<div class="company-info">
<h3>VectorStands</h3>
<p>
Fabricación y Venta de Stands<br/>
Perfiles · Postes · Mamparas · Marquesinas<br/>
<strong>vectorstands@gmail.com</strong>
</p>
</div>
<div class="quote-meta">
<div class="quote-num" id="quoteNumber">COTIZACIÓN #VS-0001</div>
<p id="quoteDateRange"></p>
<span class="badge badge-valid">Válida 30 días</span>
</div>
</div>
<!-- Client Info -->
<div class="client-info-box">
<div class="ci-title">Cliente</div>
<div class="ci-name" id="ciName">—</div>
<div class="ci-detail" id="ciDetail">—</div>
</div>
<!-- Materials Table -->
<p class="quote-section-title">Desglose de Materiales y Componentes</p>
<table class="materials-table">
<thead>
<tr>
<th>Producto / Componente</th>
<th style="text-align:center">Cant. / Stand</th>
<th style="text-align:center">Total Piezas</th>
<th style="text-align:center">P. Unit. (sin IVA)</th>
<th style="text-align:right">Subtotal</th>
</tr>
</thead>
<tbody id="materialsBody"></tbody>
</table>
<!-- Totals -->
<div class="totals-box">
<div class="totals-row">
<span>Subtotal (sin IVA)</span>
<span id="totalSub">$0.00</span>
</div>
<div class="totals-row">
<span>IVA (16%)</span>
<span id="totalIVA">$0.00</span>
</div>
<div class="totals-row divider grand">
<span>TOTAL</span>
<span id="totalFinal">$0.00</span>
</div>
</div>
<!-- Notes -->
<div class="notes-box">
<strong>📋 Condiciones de la Cotización</strong>
Precios en Moneda Nacional (MXN) · IVA incluido en el total · Vigencia: 30 días naturales ·
Tiempo de entrega según volumen · Instalación no incluida a menos que se indique ·
Precios sujetos a cambio sin previo aviso fuera del período de vigencia.
</div>
<div id="clientNotesBox" style="display:none;" class="notes-box" style="background:#f0f8ff; border-color:#b0d4f0; color:#1a4a7a;">
<strong>📝 Notas del Cliente</strong>
<span id="clientNotesText"></span>
</div>
<!-- Signature -->
<div style="display:grid; grid-template-columns:1fr 1fr; gap:40px; margin-top:40px;">
<div style="text-align:center;">
<div style="border-top:1.5px solid #ccc; padding-top:10px; font-size:0.8rem; color:#999;">
Firma y Sello VectorStands
</div>
</div>
<div style="text-align:center;">
<div style="border-top:1.5px solid #ccc; padding-top:10px; font-size:0.8rem; color:#999;">
Autorización del Cliente
</div>
</div>
</div>
</div><!-- /quotePreview -->
<!-- Actions (post-generate) -->
<div class="actions" id="quoteActions" style="display:none; margin-top:24px;">
<button class="btn btn-outline" onclick="editForm()">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
<path d="M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4Z"/>
</svg>
Editar
</button>
<button class="btn btn-primary" onclick="window.print()">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/>
<rect x="6" y="14" width="12" height="8"/>
</svg>
Imprimir / PDF
</button>
</div>
</div>
</div>
<script>
// ── PRICE DATA (from Excel) ─────────────────────────────
const PRODUCTS = [
{
name: 'Travesaño (Perfil) para Stand',
model: '950 mm',
unit: 443.20,
perStand: 18,
icon: '━',
color: '#3498db'
},
{
name: 'Poste para Stand',
model: '2,500 mm',
unit: 1428.60,
perStand: 10,
icon: '|',
color: '#2ecc71'
},
{
name: 'Marquesina para Stand',
model: '3,000 mm',
unit: 1520.20,
perStand: 1,
icon: '▬',
color: '#e67e22'
},
{
name: 'Mampara Blanca 2C de 5mm',
model: '96×241×5 mm',
unit: 780.00,
perStand: 9,
icon: '▪',
color: '#9b59b6'
}
];
const IVA_RATE = 0.16;
// ── HELPERS ─────────────────────────────────────────────
function mxn(n) {
return new Intl.NumberFormat('es-MX', { style:'currency', currency:'MXN' }).format(n);
}
function getCount() {
const v = parseInt(document.getElementById('standCount').value) || 1;
return Math.max(1, v);
}
function changeCount(delta) {
const el = document.getElementById('standCount');
const newVal = Math.max(1, (parseInt(el.value) || 1) + delta);
el.value = newVal;
updateQuote();
}
function selectType(el, type) {
document.querySelectorAll('.type-chip').forEach(c => c.classList.remove('active'));
el.classList.add('active');
updateQuote();
}
// ── LIVE COUNTER UPDATE ──────────────────────────────────
function updateQuote() {
// Only update if quote is already visible
if (document.getElementById('quotePreview').style.display !== 'none') {
renderQuote();
}
}
// ── GENERATE QUOTE ───────────────────────────────────────
let quoteGenCount = 1;
function generateQuote() {
const name = document.getElementById('clientName').value.trim();
if (!name) {
document.getElementById('clientName').focus();
document.getElementById('clientName').style.borderColor = '#e94560';
document.getElementById('clientName').placeholder = '⚠ Ingresa el nombre del cliente';
return;
}
document.getElementById('clientName').style.borderColor = '';
renderQuote();
document.getElementById('quotePreview').style.display = 'block';
document.getElementById('quoteCardHeader').style.display = 'flex';
document.getElementById('quoteActions').style.display = 'flex';
document.getElementById('quotePreview').scrollIntoView({ behavior:'smooth', block:'start' });
}
function renderQuote() {
const n = getCount();
const name = document.getElementById('clientName').value.trim() || '—';
const rfc = document.getElementById('clientRFC').value.trim();
const phone = document.getElementById('clientPhone').value.trim();
const email = document.getElementById('clientEmail').value.trim();
const notes = document.getElementById('clientNotes').value.trim();
// Quote number & date
const now = new Date();
const dateStr = now.toLocaleDateString('es-MX', { day:'2-digit', month:'long', year:'numeric' });
const expDate = new Date(now); expDate.setDate(expDate.getDate()+30);
const expStr = expDate.toLocaleDateString('es-MX', { day:'2-digit', month:'long', year:'numeric' });
const qNum = 'VS-' + String(now.getFullYear()).slice(-2) +
String(now.getMonth()+1).padStart(2,'0') +
String(now.getDate()).padStart(2,'0') + '-' +
String(n).padStart(2,'0');
document.getElementById('quoteNumber').textContent = 'COTIZACIÓN #' + qNum;
document.getElementById('quoteDateRange').innerHTML =
`Fecha: <strong>${dateStr}</strong><br/>Vigencia: <strong>${expStr}</strong>`;
// Client info
document.getElementById('ciName').textContent = name;
const details = [rfc&&'RFC: '+rfc, phone&&'Tel: '+phone, email].filter(Boolean).join(' · ');
document.getElementById('ciDetail').textContent = details || 'Sin datos adicionales';
// Materials table
let subtotal = 0;
const tbody = document.getElementById('materialsBody');
tbody.innerHTML = '';
PRODUCTS.forEach(p => {
const totalPcs = p.perStand * n;
const lineTotal = p.unit * totalPcs;
subtotal += lineTotal;
const tr = document.createElement('tr');
tr.innerHTML = `
<td>
<span class="mat-icon" style="background:${p.color}20; color:${p.color}">${p.icon}</span>
<strong>${p.name}</strong><br/>
<span style="font-size:0.78rem; color:#999; margin-left:36px;">${p.model}</span>
</td>
<td>${p.perStand}</td>
<td><strong>${totalPcs}</strong></td>
<td>${mxn(p.unit)}</td>
<td>${mxn(lineTotal)}</td>
`;
tbody.appendChild(tr);
});
// Totals
const iva = subtotal * IVA_RATE;
const total = subtotal + iva;
document.getElementById('totalSub').textContent = mxn(subtotal);
document.getElementById('totalIVA').textContent = mxn(iva);
document.getElementById('totalFinal').textContent = mxn(total);
// Notes
if (notes) {
document.getElementById('clientNotesBox').style.display = 'block';
document.getElementById('clientNotesText').textContent = notes;
} else {
document.getElementById('clientNotesBox').style.display = 'none';
}
}
function editForm() {
document.getElementById('formSection').scrollIntoView({ behavior:'smooth' });
}
// Init
document.getElementById('standCount').addEventListener('change', updateQuote);
</script>
</body>
</html>