generated from marquez.juan/Ejercicio-inicial--git-y-HTML
feat(UI): Enhance form functionality and add progress bar for improved user experience
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<div id="progress-bar"></div>
|
||||||
<header id="site-header">
|
<header id="site-header">
|
||||||
<nav id="main-nav">
|
<nav id="main-nav">
|
||||||
<a href="#" class="nav-logo">Brew<span>Box</span></a>
|
<a href="#" class="nav-logo">Brew<span>Box</span></a>
|
||||||
@@ -135,6 +136,14 @@
|
|||||||
Completá el formulario y recibís tu primer envío en menos de 72hs.
|
Completá el formulario y recibís tu primer envío en menos de 72hs.
|
||||||
Estudiantes obtienen un descuento del 20%.
|
Estudiantes obtienen un descuento del 20%.
|
||||||
</p>
|
</p>
|
||||||
|
<div class="plan-preview" id="plan-preview">
|
||||||
|
<div class="plan-preview-inner">
|
||||||
|
<span class="plan-preview-label">Plan seleccionado</span>
|
||||||
|
<span class="plan-preview-name" id="plan-preview-name">—</span>
|
||||||
|
<span class="plan-preview-price" id="plan-preview-price"></span>
|
||||||
|
<ul class="plan-preview-features" id="plan-preview-features"></ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form action="#" method="post">
|
<form action="#" method="post">
|
||||||
@@ -144,15 +153,24 @@
|
|||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="name">Nombre</label>
|
<label for="name">Nombre</label>
|
||||||
<input type="text" id="name" name="name" required />
|
<input type="text" id="name" name="name" required />
|
||||||
|
<span class="field-error-msg"
|
||||||
|
>Ingresá al menos 2 caracteres</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="lastname">Apellido</label>
|
<label for="lastname">Apellido</label>
|
||||||
<input type="text" id="lastname" name="lastname" required />
|
<input type="text" id="lastname" name="lastname" required />
|
||||||
|
<span class="field-error-msg"
|
||||||
|
>Ingresá al menos 2 caracteres</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="email">Correo electrónico</label>
|
<label for="email">Correo electrónico</label>
|
||||||
<input type="email" id="email" name="email" required />
|
<input type="email" id="email" name="email" required />
|
||||||
|
<span class="field-error-msg"
|
||||||
|
>Ingresá un correo electrónico válido</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group checkbox-group">
|
<div class="field-group checkbox-group">
|
||||||
<input type="checkbox" id="student" name="student" />
|
<input type="checkbox" id="student" name="student" />
|
||||||
@@ -178,6 +196,7 @@
|
|||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="reason">¿Por qué te suscribís?</label>
|
<label for="reason">¿Por qué te suscribís?</label>
|
||||||
<textarea id="reason" name="reason" rows="4"></textarea>
|
<textarea id="reason" name="reason" rows="4"></textarea>
|
||||||
|
<span class="char-counter" id="reason-counter">0 / 300</span>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
@@ -191,6 +210,16 @@
|
|||||||
<option value="debit">Tarjeta de débito</option>
|
<option value="debit">Tarjeta de débito</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field-group" id="cuotas-group" style="display: none">
|
||||||
|
<label for="cuotas">Cantidad de cuotas</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="cuotas"
|
||||||
|
name="cuotas"
|
||||||
|
min="1"
|
||||||
|
max="12"
|
||||||
|
placeholder="1"
|
||||||
|
/>
|
||||||
<div class="field-group field-group--icon">
|
<div class="field-group field-group--icon">
|
||||||
<label for="card">Número de tarjeta</label>
|
<label for="card">Número de tarjeta</label>
|
||||||
<div class="input-icon-wrapper">
|
<div class="input-icon-wrapper">
|
||||||
@@ -213,10 +242,12 @@
|
|||||||
type="text"
|
type="text"
|
||||||
id="card"
|
id="card"
|
||||||
name="card"
|
name="card"
|
||||||
placeholder="1234 5678 9012 3456"
|
placeholder="XXXX-XXXX-XXXX-XXXX"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
<span class="card-brand" id="card-brand"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="field-error-msg">Número de tarjeta inválido</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-row">
|
<div class="field-row">
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
@@ -228,6 +259,7 @@
|
|||||||
placeholder="MM/AA"
|
placeholder="MM/AA"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
<span class="field-error-msg">Formato MM/AA</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="cvv">CVV</label>
|
<label for="cvv">CVV</label>
|
||||||
@@ -238,6 +270,7 @@
|
|||||||
placeholder="123"
|
placeholder="123"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
<span class="field-error-msg">Ingresá un CVV válido</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -314,6 +347,31 @@
|
|||||||
<button type="submit" class="cta-button">Suscribirme</button>
|
<button type="submit" class="cta-button">Suscribirme</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<div id="form-confirmation">
|
||||||
|
<div class="confirmation-icon">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<polyline points="20 6 9 17 4 12"></polyline>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p class="confirmation-title">¡Bienvenido a BrewBox!</p>
|
||||||
|
<p class="confirmation-text">
|
||||||
|
Tu suscripción fue registrada. En las próximas horas vas a recibir
|
||||||
|
un email de confirmación con los detalles de tu primer envío.
|
||||||
|
</p>
|
||||||
|
<p class="confirmation-detail">
|
||||||
|
Primer envío estimado: <span id="confirm-date"></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
308
src/script.js
308
src/script.js
@@ -0,0 +1,308 @@
|
|||||||
|
// Smooth scrolling for anchor links
|
||||||
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||||
|
anchor.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const target = document.querySelector(this.getAttribute('href'));
|
||||||
|
if (target) {
|
||||||
|
target.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Scroll progress bar
|
||||||
|
const progressBar = document.getElementById('progress-bar');
|
||||||
|
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
const scrollTop = window.scrollY;
|
||||||
|
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
||||||
|
const progress = (scrollTop / docHeight) * 100;
|
||||||
|
progressBar.style.width = progress + '%';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Student discount logic
|
||||||
|
const studentCheckbox = document.getElementById('student');
|
||||||
|
|
||||||
|
const plans = {
|
||||||
|
normal: { full: 10, label: 'Normal' },
|
||||||
|
premium: { full: 15, label: 'Premium' }
|
||||||
|
};
|
||||||
|
|
||||||
|
function updatePrices() {
|
||||||
|
const isStudent = studentCheckbox.checked;
|
||||||
|
|
||||||
|
document.querySelectorAll('.radio-group label').forEach(lbl => {
|
||||||
|
const input = lbl.querySelector('input[type="radio"]');
|
||||||
|
if (!input) return;
|
||||||
|
|
||||||
|
const plan = plans[input.value];
|
||||||
|
if (!plan) return;
|
||||||
|
|
||||||
|
const discounted = (plan.full * 0.7).toFixed(2);
|
||||||
|
|
||||||
|
if (isStudent) {
|
||||||
|
lbl.innerHTML = `
|
||||||
|
<input type="radio" name="plan" value="${input.value}" ${input.checked ? 'checked' : ''} ${input.required ? 'required' : ''}>
|
||||||
|
${plan.label} —
|
||||||
|
<span class="price-original">$${plan.full}/mes</span>
|
||||||
|
<span class="price-discount">20% OFF</span>
|
||||||
|
<span class="price-new">$${discounted}/mes</span>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
lbl.innerHTML = `
|
||||||
|
<input type="radio" name="plan" value="${input.value}" ${input.checked ? 'checked' : ''} ${input.required ? 'required' : ''}>
|
||||||
|
${plan.label} — $${plan.full}/mes
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
studentCheckbox.addEventListener('change', updatePrices);
|
||||||
|
|
||||||
|
// Character counter for textarea
|
||||||
|
const textarea = document.getElementById('reason');
|
||||||
|
const counter = document.getElementById('reason-counter');
|
||||||
|
const MAX_CHARS = 300;
|
||||||
|
|
||||||
|
textarea.addEventListener('input', () => {
|
||||||
|
const len = textarea.value.length;
|
||||||
|
counter.textContent = `${len} / ${MAX_CHARS}`;
|
||||||
|
|
||||||
|
counter.classList.remove('near-limit', 'at-limit');
|
||||||
|
|
||||||
|
if (len >= MAX_CHARS) {
|
||||||
|
textarea.value = textarea.value.slice(0, MAX_CHARS);
|
||||||
|
counter.classList.add('at-limit');
|
||||||
|
} else if (len >= MAX_CHARS * 0.8) {
|
||||||
|
counter.classList.add('near-limit');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Form validation
|
||||||
|
function setFieldState(input, isValid) {
|
||||||
|
const group = input.closest('.field-group');
|
||||||
|
if (!group) return;
|
||||||
|
group.classList.remove('error', 'success');
|
||||||
|
if (input.value.trim() === '') return;
|
||||||
|
group.classList.add(isValid ? 'success' : 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name and Lastname
|
||||||
|
['name', 'lastname'].forEach(id => {
|
||||||
|
document.getElementById(id).addEventListener('input', function() {
|
||||||
|
setFieldState(this, this.value.trim().length >= 2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Email
|
||||||
|
document.getElementById('email').addEventListener('input', function() {
|
||||||
|
const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.value);
|
||||||
|
setFieldState(this, valid);
|
||||||
|
});
|
||||||
|
|
||||||
|
// MMAA validation
|
||||||
|
document.getElementById('expiry').addEventListener('input', function() {
|
||||||
|
this.value = this.value.replace(/[^\d]/g, '').slice(0, 4);
|
||||||
|
if (this.value.length > 2) {
|
||||||
|
this.value = this.value.slice(0, 2) + '/' + this.value.slice(2);
|
||||||
|
}
|
||||||
|
const valid = /^(0[1-9]|1[0-2])\/\d{2}$/.test(this.value);
|
||||||
|
setFieldState(this, valid);
|
||||||
|
});
|
||||||
|
|
||||||
|
// CVV
|
||||||
|
document.getElementById('cvv').addEventListener('input', function() {
|
||||||
|
this.value = this.value.replace(/\D/g, '').slice(0, 4);
|
||||||
|
setFieldState(this, this.value.length >= 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Plan preview logic
|
||||||
|
const planData = {
|
||||||
|
normal: {
|
||||||
|
name: 'Normal',
|
||||||
|
price: '$10/mes',
|
||||||
|
priceStudent: '$7.00/mes',
|
||||||
|
features: [
|
||||||
|
'250 g por envío',
|
||||||
|
'1 café de origen único por mes',
|
||||||
|
'Envío gratuito',
|
||||||
|
'Guía de preparación'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
premium: {
|
||||||
|
name: 'Premium',
|
||||||
|
price: '$15/mes',
|
||||||
|
priceStudent: '$10.50/mes',
|
||||||
|
features: [
|
||||||
|
'500 g por envío',
|
||||||
|
'2 cafés de origen único por mes',
|
||||||
|
'Envío gratuito',
|
||||||
|
'Guía de preparación',
|
||||||
|
'Acceso a lotes exclusivos'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const planPreview = document.getElementById('plan-preview');
|
||||||
|
const planPreviewName = document.getElementById('plan-preview-name');
|
||||||
|
const planPreviewPrice = document.getElementById('plan-preview-price');
|
||||||
|
const planPreviewFeatures = document.getElementById('plan-preview-features');
|
||||||
|
|
||||||
|
function updatePlanPreview() {
|
||||||
|
const selected = document.querySelector('input[name="plan"]:checked');
|
||||||
|
if (!selected) {
|
||||||
|
planPreview.classList.remove('visible');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const plan = planData[selected.value];
|
||||||
|
const isStudent = document.getElementById('student').checked;
|
||||||
|
|
||||||
|
planPreviewName.textContent = plan.name;
|
||||||
|
planPreviewPrice.textContent = isStudent ? plan.priceStudent : plan.price;
|
||||||
|
|
||||||
|
planPreviewFeatures.innerHTML = plan.features
|
||||||
|
.map(f => `<li>${f}</li>`)
|
||||||
|
.join('');
|
||||||
|
|
||||||
|
planPreview.classList.add('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('input[name="plan"]').forEach(radio => {
|
||||||
|
radio.addEventListener('change', updatePlanPreview);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update prices and preview when student checkbox changes
|
||||||
|
studentCheckbox.addEventListener('change', () => {
|
||||||
|
updatePrices();
|
||||||
|
updatePlanPreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Credit card validation and brand detection
|
||||||
|
const cardInput = document.getElementById('card');
|
||||||
|
const cardBrand = document.getElementById('card-brand');
|
||||||
|
|
||||||
|
const cardPatterns = {
|
||||||
|
Visa: /^4/,
|
||||||
|
Mastercard: /^5[1-5]|^2[2-7]/,
|
||||||
|
Amex: /^3[47]/,
|
||||||
|
Naranja: /^589562/,
|
||||||
|
Cabal: /^604201|^589657/,
|
||||||
|
};
|
||||||
|
|
||||||
|
function detectCardBrand(number) {
|
||||||
|
const clean = number.replace(/\s/g, '');
|
||||||
|
for (const [brand, pattern] of Object.entries(cardPatterns)) {
|
||||||
|
if (pattern.test(clean)) return brand;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateCard(number) {
|
||||||
|
const clean = number.replace(/\s/g, '');
|
||||||
|
if (clean.length < 13) return false;
|
||||||
|
|
||||||
|
// Luhn algorithm
|
||||||
|
let sum = 0;
|
||||||
|
let alternate = false;
|
||||||
|
for (let i = clean.length - 1; i >= 0; i--) {
|
||||||
|
let n = parseInt(clean[i]);
|
||||||
|
if (alternate) {
|
||||||
|
n *= 2;
|
||||||
|
if (n > 9) n -= 9;
|
||||||
|
}
|
||||||
|
sum += n;
|
||||||
|
alternate = !alternate;
|
||||||
|
}
|
||||||
|
return sum % 10 === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cardInput.addEventListener('input', function() {
|
||||||
|
// Remove non-digits and limit to 16 characters
|
||||||
|
let val = this.value.replace(/\D/g, '').slice(0, 16);
|
||||||
|
this.value = val.replace(/(.{4})/g, '$1 ').trim();
|
||||||
|
|
||||||
|
const brand = detectCardBrand(this.value);
|
||||||
|
const valid = validateCard(this.value);
|
||||||
|
|
||||||
|
if (brand) {
|
||||||
|
cardBrand.textContent = brand;
|
||||||
|
cardBrand.classList.add('visible');
|
||||||
|
} else {
|
||||||
|
cardBrand.classList.remove('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
setFieldState(this, valid);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Payment cuotas logic
|
||||||
|
const paymentSelect = document.getElementById('payment');
|
||||||
|
const cuotasGroup = document.getElementById('cuotas-group');
|
||||||
|
const cuotasInput = document.getElementById('cuotas');
|
||||||
|
|
||||||
|
paymentSelect.addEventListener('change', function() {
|
||||||
|
if (this.value === 'credit') {
|
||||||
|
cuotasGroup.style.display = 'flex';
|
||||||
|
cuotasInput.setAttribute('required', '');
|
||||||
|
} else {
|
||||||
|
cuotasGroup.style.display = 'none';
|
||||||
|
cuotasInput.removeAttribute('required');
|
||||||
|
cuotasInput.value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cuotas validation
|
||||||
|
cuotasInput.addEventListener('input', function() {
|
||||||
|
const val = parseInt(this.value);
|
||||||
|
setFieldState(this, val >= 1 && val <= 12);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Form submission logic
|
||||||
|
const form = document.querySelector('form');
|
||||||
|
const submitBtn = document.querySelector('.cta-button[type="submit"]');
|
||||||
|
const confirmation = document.getElementById('form-confirmation');
|
||||||
|
const confirmDate = document.getElementById('confirm-date');
|
||||||
|
const subsCounter = document.querySelector('.hero-stats .stat-item:last-child .stat-number');
|
||||||
|
|
||||||
|
form.addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// loading state
|
||||||
|
submitBtn.classList.add('loading');
|
||||||
|
submitBtn.innerHTML = '<span class="spinner"></span> Procesando...';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// Hidden form
|
||||||
|
form.style.transition = 'opacity 0.3s ease';
|
||||||
|
form.style.opacity = '0';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
form.style.display = 'none';
|
||||||
|
|
||||||
|
// Calculate delivery date (7 days from now)
|
||||||
|
const delivery = new Date();
|
||||||
|
delivery.setDate(delivery.getDate() + 7);
|
||||||
|
confirmDate.textContent = delivery.toLocaleDateString('es-AR', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show confirmation
|
||||||
|
confirmation.style.display = 'flex';
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
confirmation.classList.add('visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// +1 to subscriber counter
|
||||||
|
if (subsCounter) {
|
||||||
|
const current = parseInt(subsCounter.textContent);
|
||||||
|
subsCounter.textContent = current + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
}, 2000); // simulate processing delay
|
||||||
|
});
|
||||||
255
src/styles.css
255
src/styles.css
@@ -23,6 +23,16 @@ a {
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="number"]::-webkit-outer-spin-button,
|
||||||
|
input[type="number"]::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"] {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
/* Navbar */
|
/* Navbar */
|
||||||
#site-header {
|
#site-header {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
@@ -537,6 +547,7 @@ label {
|
|||||||
|
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
input[type="email"],
|
input[type="email"],
|
||||||
|
input[type="number"],
|
||||||
textarea,
|
textarea,
|
||||||
select {
|
select {
|
||||||
background: rgba(255, 255, 255, 0.04);
|
background: rgba(255, 255, 255, 0.04);
|
||||||
@@ -595,6 +606,7 @@ textarea {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.6rem;
|
gap: 0.6rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
font-size: 0.88rem;
|
font-size: 0.88rem;
|
||||||
letter-spacing: 0.03em;
|
letter-spacing: 0.03em;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
@@ -735,6 +747,238 @@ textarea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.price-original {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: rgba(245, 240, 232, 0.25);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-discount {
|
||||||
|
color: #c8a96e;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
border: 1px solid rgba(200, 169, 110, 0.3);
|
||||||
|
padding: 0.1rem 0.4rem;
|
||||||
|
border-radius: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-new {
|
||||||
|
color: #f5f0e8;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.char-counter {
|
||||||
|
font-size: 0.68rem;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: rgba(245, 240, 232, 0.2);
|
||||||
|
text-align: right;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.char-counter.near-limit {
|
||||||
|
color: #c8a96e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.char-counter.at-limit {
|
||||||
|
color: rgba(220, 80, 80, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group.error input,
|
||||||
|
.field-group.error textarea,
|
||||||
|
.field-group.error select {
|
||||||
|
border-bottom-color: rgba(220, 80, 80, 0.6);
|
||||||
|
background: rgba(220, 80, 80, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group.success input,
|
||||||
|
.field-group.success textarea,
|
||||||
|
.field-group.success select {
|
||||||
|
border-bottom-color: rgba(100, 200, 130, 0.5);
|
||||||
|
background: rgba(100, 200, 130, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-error-msg {
|
||||||
|
font-size: 0.68rem;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
color: rgba(220, 80, 80, 0.7);
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group.error .field-error-msg {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-preview {
|
||||||
|
margin-top: 2rem;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(8px);
|
||||||
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-preview.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-preview-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.65rem;
|
||||||
|
letter-spacing: 0.2em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(245, 240, 232, 0.25);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-preview-name {
|
||||||
|
display: block;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #f5f0e8;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-preview-price {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #c8a96e;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-preview-features {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-preview-features li {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.75rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: rgba(245, 240, 232, 0.45);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-preview-features li::before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
width: 12px;
|
||||||
|
min-width: 12px;
|
||||||
|
height: 1px;
|
||||||
|
background: #c8a96e;
|
||||||
|
margin-top: 0.65em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-brand {
|
||||||
|
position: absolute;
|
||||||
|
right: 0.75rem;
|
||||||
|
font-size: 0.65rem;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #c8a96e;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-brand.visible {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading State */
|
||||||
|
.spinner {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border: 2px solid rgba(10, 10, 10, 0.3);
|
||||||
|
border-top-color: #0a0a0a;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.7s linear infinite;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-button.loading {
|
||||||
|
opacity: 0.8;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Confirmación */
|
||||||
|
#form-confirmation {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
padding: 3rem 0;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(12px);
|
||||||
|
transition: opacity 0.4s ease, transform 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#form-confirmation.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmation-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border: 1px solid rgba(200, 169, 110, 0.3);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmation-icon svg {
|
||||||
|
color: #c8a96e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmation-title {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #f5f0e8;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmation-text {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.8;
|
||||||
|
color: rgba(245, 240, 232, 0.4);
|
||||||
|
max-width: 38ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmation-detail {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(245, 240, 232, 0.2);
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmation-detail span {
|
||||||
|
color: #c8a96e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Footer */
|
/* Footer */
|
||||||
|
|
||||||
#site-footer {
|
#site-footer {
|
||||||
@@ -802,3 +1046,14 @@ textarea {
|
|||||||
gap: 1.25rem;
|
gap: 1.25rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#progress-bar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 0%;
|
||||||
|
height: 2px;
|
||||||
|
background: #c8a96e;
|
||||||
|
z-index: 9999;
|
||||||
|
transition: width 0.1s linear;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user