2026-05-18 22:25:43 +12:00
|
|
|
<script lang="ts">
|
|
|
|
|
import { onMount } from 'svelte';
|
|
|
|
|
import { fade } from 'svelte/transition';
|
|
|
|
|
import Icon from '$lib/components/Icon.svelte';
|
|
|
|
|
import { reveal } from '$lib/actions/reveal';
|
|
|
|
|
import type { BookingContent } from '$lib/types';
|
|
|
|
|
|
|
|
|
|
type SuccessModalComponentType = typeof import('$lib/components/SuccessModal.svelte').default;
|
|
|
|
|
type ErrorModalComponentType = typeof import('$lib/components/ErrorModal.svelte').default;
|
|
|
|
|
|
|
|
|
|
export let booking: BookingContent;
|
|
|
|
|
export let pagePath = '';
|
2026-05-19 23:36:58 +12:00
|
|
|
$: isCompactContactPage = pagePath === '/contact-us';
|
2026-05-18 22:25:43 +12:00
|
|
|
|
|
|
|
|
const defaultServices = ['Tiny Gang Pack Walks', 'Solo Walks', 'Puppy Visits', 'Other Services'];
|
|
|
|
|
$: serviceOptions = booking.serviceOptions && booking.serviceOptions.length > 0
|
|
|
|
|
? booking.serviceOptions
|
|
|
|
|
: defaultServices;
|
|
|
|
|
|
|
|
|
|
const serviceDescriptions: Record<string, string> = {
|
|
|
|
|
'Tiny Gang Pack Walks': 'Small-group dog walks for sociable dogs.',
|
|
|
|
|
'Solo Walks': 'One-on-one dog walking, your dog\'s pace.',
|
|
|
|
|
'Puppy Visits': 'In-home visits while you are at work.',
|
|
|
|
|
'Other Services': 'Something else? Tell us in the notes.'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const pathToService: Record<string, string> = {
|
|
|
|
|
'/pack-walks': 'Tiny Gang Pack Walks',
|
|
|
|
|
'/dog-walking': 'Solo Walks',
|
|
|
|
|
'/puppy-visits': 'Puppy Visits'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const avatarDogs = [
|
|
|
|
|
{ image: '/images/archie-goodwalk-dog-walking-review-auckland.webp', alt: 'Archie' },
|
|
|
|
|
{ image: '/images/monty-goodwalk-dog-walking-review-auckland.webp', alt: 'Monty' },
|
|
|
|
|
{ image: '/images/otis-goodwalk-dog-walking-review-auckland.webp', alt: 'Otis' }
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const visitStartedStorageKey = 'goodwalk_visit_started_at';
|
|
|
|
|
const journeyStorageKey = 'goodwalk_journey';
|
|
|
|
|
const maxJourneyEntries = 8;
|
|
|
|
|
|
|
|
|
|
let step = 1;
|
|
|
|
|
let petName = '';
|
|
|
|
|
let location = '';
|
|
|
|
|
let message = '';
|
|
|
|
|
let fullName = '';
|
|
|
|
|
let email = '';
|
|
|
|
|
let phone = '';
|
|
|
|
|
let selectedServices: string[] = [];
|
|
|
|
|
let website = '';
|
|
|
|
|
|
|
|
|
|
let formStartedAt = 0;
|
|
|
|
|
let visitStartedAt = 0;
|
|
|
|
|
let pageEnteredAt = 0;
|
|
|
|
|
let firstInteractionAt = 0;
|
|
|
|
|
let sendClickedAt = 0;
|
|
|
|
|
let stepChanges = 0;
|
|
|
|
|
let journey: string[] = [];
|
|
|
|
|
|
|
|
|
|
let errors: Record<string, string> = {};
|
|
|
|
|
let submitting = false;
|
|
|
|
|
let submitted = false;
|
|
|
|
|
let showErrorModal = false;
|
|
|
|
|
let submitErrorDetail = '';
|
|
|
|
|
|
|
|
|
|
let SuccessModalComponent: SuccessModalComponentType | null = null;
|
|
|
|
|
let ErrorModalComponent: ErrorModalComponentType | null = null;
|
|
|
|
|
|
|
|
|
|
$: headingParts = splitTitle(booking.title || "Let's meet!");
|
|
|
|
|
$: leadCopy = booking.subtitle && booking.subtitle.trim()
|
|
|
|
|
? booking.subtitle
|
|
|
|
|
: 'Start with a few details about your dog. We will come back within 24 hours with the right next step.';
|
|
|
|
|
$: trustTitle = petName.trim()
|
|
|
|
|
? `${capitalizeFirst(petName.trim())} could be a great fit for Goodwalk.`
|
|
|
|
|
: 'Trusted by Goodwalk dog owners across Auckland.';
|
|
|
|
|
$: trustNote = selectedServices.length === 1
|
|
|
|
|
? `${selectedServices[0]}, planned around your dog rather than a generic routine.`
|
|
|
|
|
: 'Tell us a little about your dog and we will help you find the right fit.';
|
|
|
|
|
|
|
|
|
|
$: if (submitted) ensureSuccessModal();
|
|
|
|
|
$: if (showErrorModal) ensureErrorModal();
|
|
|
|
|
|
|
|
|
|
onMount(() => {
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
formStartedAt = now;
|
|
|
|
|
pageEnteredAt = now;
|
|
|
|
|
visitStartedAt = readOrCreateVisitStartedAt(now);
|
|
|
|
|
journey = updateJourneySnapshot(window.location.pathname, window.location.search);
|
|
|
|
|
|
|
|
|
|
const path = pagePath || window.location.pathname;
|
|
|
|
|
const preset = pathToService[path];
|
|
|
|
|
if (preset && serviceOptions.includes(preset) && selectedServices.length === 0) {
|
|
|
|
|
selectedServices = [preset];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleRequestedService = (event: Event) => {
|
|
|
|
|
const detail = (event as CustomEvent<{ service?: string }>).detail;
|
|
|
|
|
const service = detail?.service?.trim();
|
|
|
|
|
if (service && serviceOptions.includes(service) && !selectedServices.includes(service)) {
|
|
|
|
|
selectedServices = [...selectedServices, service];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
window.addEventListener('goodwalk:service-selected', handleRequestedService as EventListener);
|
|
|
|
|
return () => {
|
|
|
|
|
window.removeEventListener('goodwalk:service-selected', handleRequestedService as EventListener);
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function splitTitle(title: string) {
|
|
|
|
|
const trimmed = title.trim();
|
|
|
|
|
const lastSpace = trimmed.lastIndexOf(' ');
|
|
|
|
|
if (lastSpace === -1) return { plain: trimmed, highlight: '' };
|
|
|
|
|
return {
|
|
|
|
|
plain: trimmed.slice(0, lastSpace),
|
|
|
|
|
highlight: trimmed.slice(lastSpace + 1)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function capitalizeFirst(value: string) {
|
|
|
|
|
if (!value) return value;
|
|
|
|
|
return value.charAt(0).toLocaleUpperCase() + value.slice(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function readOrCreateVisitStartedAt(fallback: number) {
|
|
|
|
|
try {
|
|
|
|
|
const raw = window.sessionStorage.getItem(visitStartedStorageKey);
|
|
|
|
|
const parsed = raw ? Number(raw) : NaN;
|
|
|
|
|
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
|
|
|
window.sessionStorage.setItem(visitStartedStorageKey, String(fallback));
|
|
|
|
|
} catch {
|
|
|
|
|
return fallback;
|
|
|
|
|
}
|
|
|
|
|
return fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateJourneySnapshot(pathname: string, search: string) {
|
|
|
|
|
const nextEntry = `${pathname}${search}`;
|
|
|
|
|
try {
|
|
|
|
|
const raw = window.sessionStorage.getItem(journeyStorageKey);
|
|
|
|
|
const previous = raw ? (JSON.parse(raw) as string[]) : [];
|
|
|
|
|
const cleaned = previous.filter((value) => typeof value === 'string' && value.trim());
|
|
|
|
|
const deduped = cleaned[cleaned.length - 1] === nextEntry ? cleaned : [...cleaned, nextEntry];
|
|
|
|
|
const nextJourney = deduped.slice(-maxJourneyEntries);
|
|
|
|
|
window.sessionStorage.setItem(journeyStorageKey, JSON.stringify(nextJourney));
|
|
|
|
|
return nextJourney;
|
|
|
|
|
} catch {
|
|
|
|
|
return [nextEntry];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function ensureSuccessModal() {
|
|
|
|
|
if (SuccessModalComponent) return;
|
|
|
|
|
SuccessModalComponent = (await import('$lib/components/SuccessModal.svelte')).default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function ensureErrorModal() {
|
|
|
|
|
if (ErrorModalComponent) return;
|
|
|
|
|
ErrorModalComponent = (await import('$lib/components/ErrorModal.svelte')).default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function noteInteraction() {
|
|
|
|
|
if (!firstInteractionAt) firstInteractionAt = Date.now();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function clearError(field: string) {
|
|
|
|
|
if (errors[field]) errors = { ...errors, [field]: '' };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toggleService(service: string) {
|
|
|
|
|
noteInteraction();
|
|
|
|
|
selectedServices = selectedServices.includes(service)
|
|
|
|
|
? selectedServices.filter((s) => s !== service)
|
|
|
|
|
: [...selectedServices, service];
|
|
|
|
|
clearError('services');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function validateEmail(raw: string): string {
|
|
|
|
|
const value = raw.trim();
|
|
|
|
|
if (!value) return 'Please enter your email';
|
|
|
|
|
if (!value.includes('@')) return 'Email is missing the @ sign';
|
|
|
|
|
const re = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
|
|
|
|
|
if (!re.test(value)) return "That email doesn't look quite right";
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function validateStep(target: number): boolean {
|
|
|
|
|
const next: Record<string, string> = {};
|
|
|
|
|
if (target === 1) {
|
|
|
|
|
if (!petName.trim()) next.petName = "Please tell us your dog's name.";
|
|
|
|
|
if (selectedServices.length === 0) next.services = 'Pick at least one service.';
|
|
|
|
|
if (message.trim().length < 10) {
|
|
|
|
|
next.message = 'Tell us a little about your dog so we can prepare properly.';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (target === 2) {
|
|
|
|
|
if (!fullName.trim()) next.fullName = 'Please enter your full name';
|
|
|
|
|
const emailErr = validateEmail(email);
|
|
|
|
|
if (emailErr) next.email = emailErr;
|
|
|
|
|
if (!phone.trim()) next.phone = 'Please enter your phone number';
|
|
|
|
|
if (!location.trim()) next.location = 'Please enter your suburb';
|
|
|
|
|
}
|
|
|
|
|
errors = next;
|
|
|
|
|
return Object.keys(next).length === 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 23:36:58 +12:00
|
|
|
function validateCompactContactForm(): boolean {
|
|
|
|
|
const next: Record<string, string> = {};
|
|
|
|
|
if (!petName.trim()) next.petName = "Please tell us your dog's name.";
|
|
|
|
|
if (selectedServices.length === 0) next.services = 'Pick at least one service.';
|
|
|
|
|
if (message.trim().length < 10) {
|
|
|
|
|
next.message = 'Tell us a little about your dog so we can prepare properly.';
|
|
|
|
|
}
|
|
|
|
|
if (!fullName.trim()) next.fullName = 'Please enter your full name';
|
|
|
|
|
const emailErr = validateEmail(email);
|
|
|
|
|
if (emailErr) next.email = emailErr;
|
|
|
|
|
if (!phone.trim()) next.phone = 'Please enter your phone number';
|
|
|
|
|
if (!location.trim()) next.location = 'Please enter your suburb';
|
|
|
|
|
errors = next;
|
|
|
|
|
return Object.keys(next).length === 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 22:25:43 +12:00
|
|
|
function goNext() {
|
|
|
|
|
noteInteraction();
|
|
|
|
|
if (!validateStep(step)) return;
|
|
|
|
|
step += 1;
|
|
|
|
|
stepChanges += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function goBack() {
|
|
|
|
|
noteInteraction();
|
|
|
|
|
if (step > 1) {
|
|
|
|
|
step -= 1;
|
|
|
|
|
stepChanges += 1;
|
|
|
|
|
errors = {};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function handleSubmit() {
|
|
|
|
|
noteInteraction();
|
2026-05-19 23:36:58 +12:00
|
|
|
if (isCompactContactPage) {
|
|
|
|
|
if (!validateCompactContactForm()) return;
|
|
|
|
|
} else if (!validateStep(2)) return;
|
2026-05-18 22:25:43 +12:00
|
|
|
|
|
|
|
|
submitting = true;
|
|
|
|
|
sendClickedAt = Date.now();
|
|
|
|
|
submitErrorDetail = '';
|
|
|
|
|
showErrorModal = false;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const res = await fetch('/api/submit', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
enquiryType: 'booking',
|
|
|
|
|
fullName,
|
|
|
|
|
email,
|
|
|
|
|
phone,
|
|
|
|
|
petName,
|
|
|
|
|
location,
|
|
|
|
|
message,
|
|
|
|
|
services: selectedServices,
|
|
|
|
|
website,
|
|
|
|
|
formStartedAt,
|
|
|
|
|
visitStartedAt,
|
|
|
|
|
pageEnteredAt,
|
|
|
|
|
firstInteractionAt,
|
|
|
|
|
sendClickedAt,
|
|
|
|
|
stepChanges,
|
|
|
|
|
journey,
|
|
|
|
|
referrer: typeof document !== 'undefined' ? document.referrer : '',
|
|
|
|
|
page: typeof window !== 'undefined' ? window.location.href : '',
|
2026-05-19 23:36:58 +12:00
|
|
|
variant: isCompactContactPage ? 'contact-compact' : 'booking-wizard'
|
2026-05-18 22:25:43 +12:00
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
const body = await res.json().catch(() => ({}));
|
|
|
|
|
const detail =
|
|
|
|
|
typeof body?.detail === 'string'
|
|
|
|
|
? body.detail
|
|
|
|
|
: body?.detail?.message ?? body?.message ?? `Server responded with ${res.status}`;
|
|
|
|
|
throw new Error(detail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
submitted = true;
|
|
|
|
|
} catch (err: unknown) {
|
|
|
|
|
submitErrorDetail = err instanceof Error ? err.message : String(err);
|
|
|
|
|
showErrorModal = true;
|
|
|
|
|
} finally {
|
|
|
|
|
submitting = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<section id="newlead" use:reveal={{ delay: 70 }} class="wiz reveal-block">
|
2026-05-19 23:36:58 +12:00
|
|
|
<div class="wiz-inner" class:wiz-inner--compact={isCompactContactPage}>
|
2026-05-18 22:25:43 +12:00
|
|
|
{#if submitted && SuccessModalComponent}
|
|
|
|
|
<svelte:component
|
|
|
|
|
this={SuccessModalComponent}
|
|
|
|
|
firstName={fullName.split(' ')[0]}
|
|
|
|
|
petName={petName.trim() ? capitalizeFirst(petName.trim()) : 'your dog'}
|
|
|
|
|
{email}
|
|
|
|
|
enquiryType="booking"
|
|
|
|
|
onClose={() => (submitted = false)}
|
|
|
|
|
/>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if showErrorModal && ErrorModalComponent}
|
|
|
|
|
<svelte:component
|
|
|
|
|
this={ErrorModalComponent}
|
|
|
|
|
detail={submitErrorDetail}
|
|
|
|
|
enquiryType="booking"
|
|
|
|
|
onClose={() => (showErrorModal = false)}
|
|
|
|
|
onRetry={() => (showErrorModal = false)}
|
|
|
|
|
/>
|
|
|
|
|
{/if}
|
|
|
|
|
|
2026-05-19 23:36:58 +12:00
|
|
|
{#if !isCompactContactPage}
|
|
|
|
|
<div class="wiz-header">
|
|
|
|
|
<span class="wiz-eyebrow">Free Meet & Greet</span>
|
|
|
|
|
<h2 class="wiz-title">
|
|
|
|
|
<span class="wiz-title-plain">{headingParts.plain}</span>
|
|
|
|
|
{#if headingParts.highlight}
|
|
|
|
|
{' '}
|
|
|
|
|
<span class="wiz-title-highlight">{headingParts.highlight}</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</h2>
|
|
|
|
|
<p class="wiz-lead">{leadCopy}</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="wiz-trust" aria-label="Goodwalk dog families">
|
|
|
|
|
<div class="wiz-avatars" aria-hidden="true">
|
|
|
|
|
{#each avatarDogs as dog}
|
|
|
|
|
<span class="wiz-avatar">
|
|
|
|
|
<img src={dog.image} alt="" loading="lazy" />
|
|
|
|
|
</span>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="wiz-trust-copy">
|
|
|
|
|
<p>{trustTitle}</p>
|
|
|
|
|
<span>{trustNote}</span>
|
|
|
|
|
</div>
|
2026-05-18 22:25:43 +12:00
|
|
|
</div>
|
2026-05-19 23:36:58 +12:00
|
|
|
|
|
|
|
|
<div class="wiz-progress" role="progressbar" aria-valuemin="1" aria-valuemax="2" aria-valuenow={step}>
|
|
|
|
|
<span class="wiz-step-mark" class:active={step >= 1} class:done={step > 1}>
|
|
|
|
|
<span class="wiz-step-num">1</span>
|
|
|
|
|
<span class="wiz-step-label">{booking.dogStepLabel || 'Your dog'}</span>
|
|
|
|
|
</span>
|
|
|
|
|
<span class="wiz-step-line" class:done={step > 1} aria-hidden="true"></span>
|
|
|
|
|
<span class="wiz-step-mark" class:active={step === 2}>
|
|
|
|
|
<span class="wiz-step-num">2</span>
|
|
|
|
|
<span class="wiz-step-label">{booking.ownerStepLabel || 'Your details'}</span>
|
|
|
|
|
</span>
|
2026-05-18 22:25:43 +12:00
|
|
|
</div>
|
2026-05-19 23:36:58 +12:00
|
|
|
{/if}
|
2026-05-18 22:25:43 +12:00
|
|
|
|
|
|
|
|
<form
|
|
|
|
|
class="wiz-form"
|
|
|
|
|
novalidate
|
|
|
|
|
on:submit|preventDefault={handleSubmit}
|
|
|
|
|
on:focusin={noteInteraction}
|
|
|
|
|
on:input={noteInteraction}
|
|
|
|
|
>
|
|
|
|
|
<div class="wiz-honeypot" aria-hidden="true">
|
|
|
|
|
<label for="website">Website</label>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={website}
|
|
|
|
|
type="text"
|
|
|
|
|
id="website"
|
|
|
|
|
name="website"
|
|
|
|
|
tabindex="-1"
|
|
|
|
|
autocomplete="new-password"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-05-19 23:36:58 +12:00
|
|
|
<article class="wiz-card" class:wiz-card--compact={isCompactContactPage}>
|
2026-05-18 22:25:43 +12:00
|
|
|
{#key step}
|
2026-05-19 23:36:58 +12:00
|
|
|
<div class="wiz-step" class:wiz-step--compact={isCompactContactPage} in:fade={{ duration: 200 }}>
|
|
|
|
|
{#if isCompactContactPage}
|
|
|
|
|
<span class="wiz-step-eyebrow">Start here</span>
|
|
|
|
|
<h3 class="wiz-step-heading">Tell us about your dog</h3>
|
|
|
|
|
<p class="wiz-step-helper">A few details now. We come back with the right next step.</p>
|
|
|
|
|
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-dog" /> Your dog's name
|
|
|
|
|
</span>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={petName}
|
|
|
|
|
on:input={() => clearError('petName')}
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="For example, Teddy"
|
|
|
|
|
class:invalid={errors.petName}
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
/>
|
|
|
|
|
{#if errors.petName}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.petName}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<fieldset class="wiz-fieldset">
|
|
|
|
|
<legend class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-paw" /> Which service are you interested in?
|
|
|
|
|
</legend>
|
|
|
|
|
<div
|
|
|
|
|
class="wiz-service-grid"
|
|
|
|
|
class:wiz-service-grid--compact={isCompactContactPage}
|
|
|
|
|
class:invalid={errors.services}
|
|
|
|
|
role="group"
|
|
|
|
|
aria-label="Service interest"
|
|
|
|
|
>
|
|
|
|
|
{#each serviceOptions as service}
|
|
|
|
|
{@const checked = selectedServices.includes(service)}
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
class="wiz-service"
|
|
|
|
|
class:wiz-service--compact={isCompactContactPage}
|
|
|
|
|
class:active={checked}
|
|
|
|
|
aria-pressed={checked}
|
|
|
|
|
on:click={() => toggleService(service)}
|
|
|
|
|
>
|
|
|
|
|
<span class="wiz-service-check" aria-hidden="true">
|
|
|
|
|
{#if checked}<Icon name="fas fa-check" />{/if}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="wiz-service-text">
|
|
|
|
|
<span class="wiz-service-label">{service}</span>
|
|
|
|
|
{#if serviceDescriptions[service]}
|
|
|
|
|
<span class="wiz-service-desc">{serviceDescriptions[service]}</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
{#if errors.services}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.services}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</fieldset>
|
|
|
|
|
|
|
|
|
|
<div class="wiz-grid-two">
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-user" /> Full name
|
|
|
|
|
</span>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={fullName}
|
|
|
|
|
on:input={() => clearError('fullName')}
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Your full name"
|
|
|
|
|
class:invalid={errors.fullName}
|
|
|
|
|
autocomplete="name"
|
|
|
|
|
/>
|
|
|
|
|
{#if errors.fullName}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.fullName}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-envelope" /> Email
|
|
|
|
|
</span>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={email}
|
|
|
|
|
on:input={() => clearError('email')}
|
|
|
|
|
type="email"
|
|
|
|
|
placeholder="you@example.com"
|
|
|
|
|
class:invalid={errors.email}
|
|
|
|
|
autocomplete="email"
|
|
|
|
|
inputmode="email"
|
|
|
|
|
/>
|
|
|
|
|
{#if errors.email}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.email}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-phone" /> Phone
|
|
|
|
|
</span>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={phone}
|
|
|
|
|
on:input={() => clearError('phone')}
|
|
|
|
|
type="tel"
|
|
|
|
|
placeholder="(021) 234 5678"
|
|
|
|
|
class:invalid={errors.phone}
|
|
|
|
|
autocomplete="tel"
|
|
|
|
|
inputmode="tel"
|
|
|
|
|
/>
|
|
|
|
|
{#if errors.phone}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.phone}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-location-dot" /> Your suburb
|
|
|
|
|
</span>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={location}
|
|
|
|
|
on:input={() => clearError('location')}
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="For example, Grey Lynn"
|
|
|
|
|
class:invalid={errors.location}
|
|
|
|
|
autocomplete="address-level2"
|
|
|
|
|
/>
|
|
|
|
|
{#if errors.location}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.location}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-comment" /> What else should we know?
|
|
|
|
|
</span>
|
|
|
|
|
<textarea
|
|
|
|
|
bind:value={message}
|
|
|
|
|
on:input={() => clearError('message')}
|
|
|
|
|
rows="3"
|
|
|
|
|
placeholder="Age, breed, temperament around other dogs, any health quirks, anything that helps us prepare."
|
|
|
|
|
class:invalid={errors.message}
|
|
|
|
|
></textarea>
|
|
|
|
|
{#if errors.message}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.message}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<div class="wiz-actions">
|
|
|
|
|
<button type="submit" class="wiz-btn wiz-btn-primary wiz-btn-primary--wide" disabled={submitting}>
|
|
|
|
|
{#if submitting}
|
|
|
|
|
Sending
|
|
|
|
|
{:else}
|
|
|
|
|
Send my details
|
|
|
|
|
{/if}
|
|
|
|
|
<Icon name="fas fa-paper-plane" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
{:else if step === 1}
|
2026-05-18 22:25:43 +12:00
|
|
|
<span class="wiz-step-eyebrow">Step one of two</span>
|
|
|
|
|
<h3 class="wiz-step-heading">Tell us about your dog</h3>
|
|
|
|
|
<p class="wiz-step-helper">Just the basics. Pick everything you are open to.</p>
|
|
|
|
|
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-dog" /> Your dog's name
|
|
|
|
|
</span>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={petName}
|
|
|
|
|
on:input={() => clearError('petName')}
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="For example, Teddy"
|
|
|
|
|
class:invalid={errors.petName}
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
/>
|
|
|
|
|
{#if errors.petName}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.petName}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<fieldset class="wiz-fieldset">
|
|
|
|
|
<legend class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-paw" /> Which service are you interested in?
|
|
|
|
|
</legend>
|
|
|
|
|
<div
|
|
|
|
|
class="wiz-service-grid"
|
|
|
|
|
class:invalid={errors.services}
|
|
|
|
|
role="group"
|
|
|
|
|
aria-label="Service interest"
|
|
|
|
|
>
|
|
|
|
|
{#each serviceOptions as service}
|
|
|
|
|
{@const checked = selectedServices.includes(service)}
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
class="wiz-service"
|
|
|
|
|
class:active={checked}
|
|
|
|
|
aria-pressed={checked}
|
|
|
|
|
on:click={() => toggleService(service)}
|
|
|
|
|
>
|
|
|
|
|
<span class="wiz-service-check" aria-hidden="true">
|
|
|
|
|
{#if checked}<Icon name="fas fa-check" />{/if}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="wiz-service-text">
|
|
|
|
|
<span class="wiz-service-label">{service}</span>
|
|
|
|
|
{#if serviceDescriptions[service]}
|
|
|
|
|
<span class="wiz-service-desc">{serviceDescriptions[service]}</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
{#if errors.services}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.services}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</fieldset>
|
|
|
|
|
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-comment" /> What else should we know?
|
|
|
|
|
</span>
|
|
|
|
|
<textarea
|
|
|
|
|
bind:value={message}
|
|
|
|
|
on:input={() => clearError('message')}
|
|
|
|
|
rows="3"
|
|
|
|
|
placeholder="Age, breed, temperament around other dogs, any health quirks, anything that helps us prepare."
|
|
|
|
|
class:invalid={errors.message}
|
|
|
|
|
></textarea>
|
|
|
|
|
{#if errors.message}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.message}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<div class="wiz-actions">
|
|
|
|
|
<button type="button" class="wiz-btn wiz-btn-primary" on:click={goNext}>
|
|
|
|
|
Continue
|
|
|
|
|
<Icon name="fas fa-arrow-right" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
{:else if step === 2}
|
|
|
|
|
<span class="wiz-step-eyebrow">Step two of two</span>
|
|
|
|
|
<h3 class="wiz-step-heading">How do we reach you?</h3>
|
|
|
|
|
<p class="wiz-step-helper">A real person replies within 24 hours, usually sooner.</p>
|
|
|
|
|
|
|
|
|
|
<div class="wiz-grid-two">
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-user" /> Full name
|
|
|
|
|
</span>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={fullName}
|
|
|
|
|
on:input={() => clearError('fullName')}
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Your full name"
|
|
|
|
|
class:invalid={errors.fullName}
|
|
|
|
|
autocomplete="name"
|
|
|
|
|
/>
|
|
|
|
|
{#if errors.fullName}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.fullName}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-envelope" /> Email
|
|
|
|
|
</span>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={email}
|
|
|
|
|
on:input={() => clearError('email')}
|
|
|
|
|
type="email"
|
|
|
|
|
placeholder="you@example.com"
|
|
|
|
|
class:invalid={errors.email}
|
|
|
|
|
autocomplete="email"
|
|
|
|
|
inputmode="email"
|
|
|
|
|
/>
|
|
|
|
|
{#if errors.email}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.email}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-phone" /> Phone
|
|
|
|
|
</span>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={phone}
|
|
|
|
|
on:input={() => clearError('phone')}
|
|
|
|
|
type="tel"
|
|
|
|
|
placeholder="(021) 234 5678"
|
|
|
|
|
class:invalid={errors.phone}
|
|
|
|
|
autocomplete="tel"
|
|
|
|
|
inputmode="tel"
|
|
|
|
|
/>
|
|
|
|
|
{#if errors.phone}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.phone}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<label class="wiz-field">
|
|
|
|
|
<span class="wiz-label">
|
|
|
|
|
<Icon name="fas fa-location-dot" /> Your suburb
|
|
|
|
|
</span>
|
|
|
|
|
<input
|
|
|
|
|
bind:value={location}
|
|
|
|
|
on:input={() => clearError('location')}
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="For example, Grey Lynn"
|
|
|
|
|
class:invalid={errors.location}
|
|
|
|
|
autocomplete="address-level2"
|
|
|
|
|
/>
|
|
|
|
|
{#if errors.location}
|
|
|
|
|
<span class="wiz-error">
|
|
|
|
|
<Icon name="fas fa-circle-exclamation" />
|
|
|
|
|
{errors.location}
|
|
|
|
|
</span>
|
|
|
|
|
{/if}
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="wiz-actions wiz-actions-split">
|
|
|
|
|
<button type="button" class="wiz-btn wiz-btn-back" on:click={goBack}>
|
|
|
|
|
<Icon name="fas fa-arrow-left" />
|
|
|
|
|
Back
|
|
|
|
|
</button>
|
|
|
|
|
<button type="submit" class="wiz-btn wiz-btn-primary" disabled={submitting}>
|
|
|
|
|
{#if submitting}
|
|
|
|
|
Sending
|
|
|
|
|
{:else}
|
|
|
|
|
Send my details
|
|
|
|
|
{/if}
|
|
|
|
|
<Icon name="fas fa-paper-plane" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
</div>
|
|
|
|
|
{/key}
|
|
|
|
|
</article>
|
|
|
|
|
</form>
|
|
|
|
|
|
2026-05-19 23:36:58 +12:00
|
|
|
<p class="wiz-reassurance" class:wiz-reassurance--compact={isCompactContactPage} aria-live="polite">
|
2026-05-18 22:25:43 +12:00
|
|
|
<Icon name="fas fa-bolt" />
|
|
|
|
|
A real reply within 24 hours, usually sooner.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
:global(#newlead).wiz {
|
|
|
|
|
padding-left: var(--space-container-x);
|
|
|
|
|
padding-right: var(--space-container-x);
|
|
|
|
|
background: transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-inner {
|
|
|
|
|
max-width: 56rem;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 23:36:58 +12:00
|
|
|
.wiz-inner--compact {
|
|
|
|
|
max-width: 60rem;
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 22:25:43 +12:00
|
|
|
.wiz-header {
|
|
|
|
|
text-align: center;
|
|
|
|
|
margin-bottom: 28px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-eyebrow {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
margin-bottom: 18px;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
background: rgba(var(--brand-rgb), 0.08);
|
|
|
|
|
color: var(--text-brand);
|
|
|
|
|
font-family: var(--font-head);
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
letter-spacing: 0.02em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-title {
|
|
|
|
|
margin: 0 0 32px;
|
|
|
|
|
font-family: var(--font-head);
|
|
|
|
|
font-size: clamp(36px, 5.4vw, 64px);
|
|
|
|
|
line-height: 1.05;
|
|
|
|
|
letter-spacing: -0.02em;
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-title-highlight {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
color: var(--yellow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-title-highlight::after {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: -8px;
|
|
|
|
|
bottom: -18px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 34' fill='none'%3E%3Cpath d='M4 24C67 10 131 4 198 5c43 1 82 6 118 18' stroke='%23192419' stroke-width='8' stroke-linecap='round'/%3E%3C/svg%3E")
|
|
|
|
|
center / contain no-repeat;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 640px) {
|
|
|
|
|
.wiz-title-highlight::after {
|
|
|
|
|
left: 12px;
|
|
|
|
|
right: 12px;
|
|
|
|
|
bottom: -14px;
|
|
|
|
|
height: 20px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-lead {
|
|
|
|
|
max-width: 38rem;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
color: var(--text-subtle);
|
|
|
|
|
font-size: clamp(15px, 1.4vw, 17px);
|
|
|
|
|
line-height: 1.55;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-trust {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: clamp(14px, 2vw, 22px);
|
|
|
|
|
max-width: 44rem;
|
|
|
|
|
margin: 0 auto 32px;
|
|
|
|
|
padding: 14px 18px;
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
background: rgba(var(--white-rgb), 0.72);
|
|
|
|
|
border: 1px solid rgba(var(--brand-rgb), 0.08);
|
|
|
|
|
box-shadow: 0 14px 32px rgba(var(--ink-rgb), 0.04);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-avatars {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-avatar {
|
|
|
|
|
width: 38px;
|
|
|
|
|
height: 38px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
border: 2px solid rgba(var(--white-rgb), 1);
|
|
|
|
|
box-shadow: 0 4px 10px rgba(var(--ink-rgb), 0.08);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-avatar + .wiz-avatar {
|
|
|
|
|
margin-left: -10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-avatar img {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-trust-copy p {
|
|
|
|
|
margin: 0 0 2px;
|
|
|
|
|
font-family: var(--font-head);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-trust-copy span {
|
|
|
|
|
color: var(--text-subtle);
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
line-height: 1.45;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-progress {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 14px;
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-mark {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
padding: 8px 16px 8px 8px;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
background: rgba(var(--brand-rgb), 0.04);
|
|
|
|
|
color: var(--text-subtle);
|
|
|
|
|
font-family: var(--font-head);
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
transition: background 200ms ease, color 200ms ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-mark.active {
|
|
|
|
|
background: rgba(var(--brand-rgb), 0.1);
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-mark.done {
|
|
|
|
|
background: rgba(var(--accent-rgb), 0.18);
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-num {
|
|
|
|
|
width: 26px;
|
|
|
|
|
height: 26px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: rgba(var(--white-rgb), 1);
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
box-shadow: inset 0 0 0 1px rgba(var(--brand-rgb), 0.12);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-mark.active .wiz-step-num {
|
|
|
|
|
background: var(--yellow);
|
|
|
|
|
box-shadow: inset 0 0 0 1px rgba(var(--brand-rgb), 0.18);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-mark.done .wiz-step-num {
|
|
|
|
|
background: var(--gw-green);
|
|
|
|
|
color: var(--text-inverse);
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-line {
|
|
|
|
|
flex: 0 0 36px;
|
|
|
|
|
height: 2px;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
background: rgba(var(--brand-rgb), 0.12);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-line.done {
|
|
|
|
|
background: var(--gw-green);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-form {
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-honeypot {
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: -10000px;
|
|
|
|
|
width: 1px;
|
|
|
|
|
height: 1px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-card {
|
|
|
|
|
position: relative;
|
|
|
|
|
max-width: 44rem;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
padding: clamp(24px, 3.5vw, 36px);
|
|
|
|
|
border-radius: 26px;
|
|
|
|
|
background: rgba(var(--white-rgb), 1);
|
|
|
|
|
border: 1px solid rgba(var(--brand-rgb), 0.08);
|
|
|
|
|
box-shadow: 0 30px 60px rgba(var(--ink-rgb), 0.08);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 23:36:58 +12:00
|
|
|
.wiz-card--compact {
|
|
|
|
|
max-width: 52rem;
|
|
|
|
|
padding: clamp(22px, 3vw, 32px);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 22:25:43 +12:00
|
|
|
.wiz-step {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 18px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 23:36:58 +12:00
|
|
|
.wiz-step--compact {
|
|
|
|
|
gap: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 22:25:43 +12:00
|
|
|
.wiz-step-eyebrow {
|
|
|
|
|
align-self: flex-start;
|
|
|
|
|
padding: 6px 12px;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
background: rgba(var(--brand-rgb), 0.06);
|
|
|
|
|
color: var(--text-subtle);
|
|
|
|
|
font-family: var(--font-head);
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-heading {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-family: var(--font-head);
|
|
|
|
|
font-size: clamp(24px, 3vw, 32px);
|
|
|
|
|
line-height: 1.15;
|
|
|
|
|
letter-spacing: -0.015em;
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-helper {
|
|
|
|
|
margin: 0 0 6px;
|
|
|
|
|
color: var(--text-subtle);
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-field {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-label {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
font-family: var(--font-head);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-field input,
|
|
|
|
|
.wiz-field textarea {
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding: 13px 14px;
|
|
|
|
|
border-radius: 14px;
|
|
|
|
|
border: 1px solid rgba(var(--brand-rgb), 0.14);
|
|
|
|
|
background: rgba(var(--white-rgb), 1);
|
|
|
|
|
font-family: var(--font-body);
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
transition: border-color 180ms ease, box-shadow 180ms ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-field textarea {
|
|
|
|
|
resize: vertical;
|
|
|
|
|
min-height: 88px;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-field input:focus,
|
|
|
|
|
.wiz-field textarea:focus {
|
|
|
|
|
outline: none;
|
|
|
|
|
border-color: var(--gw-green);
|
|
|
|
|
box-shadow: 0 0 0 4px rgba(var(--brand-rgb), 0.12);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-field input.invalid,
|
|
|
|
|
.wiz-field textarea.invalid {
|
|
|
|
|
border-color: oklch(0.55 0.18 27);
|
|
|
|
|
background: oklch(0.985 0.012 27);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-error {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
color: oklch(0.5 0.18 27);
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-fieldset {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
border: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(2, 1fr);
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service-grid.invalid {
|
|
|
|
|
outline: 1px solid oklch(0.55 0.18 27);
|
|
|
|
|
outline-offset: 4px;
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
padding: 14px 16px;
|
|
|
|
|
border-radius: 16px;
|
|
|
|
|
border: 1.5px solid rgba(var(--brand-rgb), 0.12);
|
|
|
|
|
background: rgba(var(--white-rgb), 1);
|
|
|
|
|
text-align: left;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: border-color 180ms ease, background 180ms ease, transform 180ms ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service-text {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service-desc {
|
|
|
|
|
font-family: var(--font-body);
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
color: var(--text-subtle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service.active .wiz-service-desc {
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service:hover {
|
|
|
|
|
border-color: rgba(var(--brand-rgb), 0.32);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 23:36:58 +12:00
|
|
|
.wiz-service--compact {
|
|
|
|
|
padding: 12px 14px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 22:25:43 +12:00
|
|
|
.wiz-service.active {
|
|
|
|
|
border-color: var(--gw-green);
|
|
|
|
|
background: rgba(var(--accent-rgb), 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service-check {
|
|
|
|
|
width: 22px;
|
|
|
|
|
height: 22px;
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
border: 1.5px solid rgba(var(--brand-rgb), 0.32);
|
|
|
|
|
background: rgba(var(--white-rgb), 1);
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
transition: background 180ms ease, border-color 180ms ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service.active .wiz-service-check {
|
|
|
|
|
background: var(--yellow);
|
|
|
|
|
border-color: var(--gw-green);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service-label {
|
|
|
|
|
font-family: var(--font-head);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
line-height: 1.25;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-grid-two {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(2, 1fr);
|
|
|
|
|
gap: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-actions-split {
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-btn {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
padding: 13px 22px;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
font-family: var(--font-head);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
border: 1.5px solid transparent;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: transform 180ms ease, background 180ms ease, border-color 180ms ease, color 180ms ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-btn:disabled {
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-btn-primary {
|
|
|
|
|
background: var(--yellow);
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
border-color: var(--yellow);
|
|
|
|
|
box-shadow: 0 10px 22px rgba(255, 209, 0, 0.32);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-btn-primary:hover:not(:disabled) {
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
background: oklch(0.88 0.18 95);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 23:36:58 +12:00
|
|
|
.wiz-btn-primary--wide {
|
|
|
|
|
width: 100%;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 22:25:43 +12:00
|
|
|
.wiz-btn-back {
|
|
|
|
|
background: transparent;
|
|
|
|
|
color: var(--text-subtle);
|
|
|
|
|
border-color: rgba(var(--brand-rgb), 0.18);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-reassurance {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
max-width: 44rem;
|
|
|
|
|
margin: 18px auto 0;
|
|
|
|
|
color: var(--text-subtle);
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 23:36:58 +12:00
|
|
|
.wiz-reassurance--compact {
|
|
|
|
|
margin-top: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 22:25:43 +12:00
|
|
|
.wiz-reassurance :global(.icon) {
|
|
|
|
|
color: var(--yellow);
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-btn-back:hover {
|
|
|
|
|
color: var(--text-heading);
|
|
|
|
|
border-color: rgba(var(--brand-rgb), 0.4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
:global(#newlead).wiz {
|
|
|
|
|
padding-left: var(--space-container-x-mobile);
|
|
|
|
|
padding-right: var(--space-container-x-mobile);
|
|
|
|
|
}
|
2026-05-19 23:36:58 +12:00
|
|
|
|
|
|
|
|
.wiz-inner--compact {
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
}
|
2026-05-18 22:25:43 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 640px) {
|
|
|
|
|
.wiz-trust {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
text-align: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-progress {
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-label {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-mark {
|
|
|
|
|
padding: 6px 10px 6px 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-step-line {
|
|
|
|
|
flex: 0 0 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service-grid {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 23:36:58 +12:00
|
|
|
.wiz-service-grid--compact {
|
|
|
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service--compact {
|
|
|
|
|
min-height: 100%;
|
|
|
|
|
padding: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-service--compact .wiz-service-desc {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-card--compact {
|
|
|
|
|
padding: 20px 18px;
|
|
|
|
|
border-radius: 22px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 22:25:43 +12:00
|
|
|
.wiz-grid-two {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-actions-split {
|
|
|
|
|
flex-direction: column-reverse;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wiz-btn {
|
|
|
|
|
width: 100%;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
|
|
|
.wiz-step-mark,
|
|
|
|
|
.wiz-service,
|
|
|
|
|
.wiz-btn {
|
|
|
|
|
transition: none;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|