Design language tweaks v3

This commit is contained in:
2026-05-15 16:27:39 +12:00
parent 580d600c47
commit b950229003
14 changed files with 2746 additions and 246 deletions
+159 -21
View File
@@ -3,6 +3,7 @@
import { reveal } from '$lib/actions/reveal';
import CtaCard from '$lib/components/CtaCard.svelte';
import PageHeader from '$lib/components/PageHeader.svelte';
import Icon from '$lib/components/Icon.svelte';
import { getEnhancedImage } from '$lib/enhanced-images';
import type { AboutPageContent } from '$lib/types';
@@ -90,24 +91,49 @@
{/if}
</div>
<div class="about-founder-copy">
{#if founderSection.eyebrow}
<span class="about-eyebrow">{founderSection.eyebrow}</span>
{/if}
<h2 class="about-founder-heading">
<span class="about-founder-heading-desktop">
<span class="about-founder-title-main">{founderHeadingLead}</span>
<br />
<span class="about-founder-title-highlight">{founderHeadingHighlight}</span>
</span>
<span class="about-founder-heading-mobile">
<span class="about-founder-title-main">{founderHeadingLead}</span>
<span class="about-founder-title-highlight">{founderHeadingHighlight}</span>
</span>
</h2>
{#each founderSection.body as paragraph}
<p>{paragraph}</p>
{/each}
<a href="/contact-us" class="btn btn-green btn-mobile-center">Book a free Meet &amp; Greet</a>
<article class="about-founder-note">
{#if founderSection.eyebrow}
<span class="about-eyebrow about-founder-kicker">{founderSection.eyebrow}</span>
{/if}
<h2 class="about-founder-heading">
<span class="about-founder-heading-desktop">
<span class="about-founder-title-main">{founderHeadingLead}</span>
<br />
<span class="about-founder-title-highlight">{founderHeadingHighlight}</span>
</span>
<span class="about-founder-heading-mobile">
<span class="about-founder-title-main">{founderHeadingLead}</span>
<span class="about-founder-title-highlight">{founderHeadingHighlight}</span>
</span>
</h2>
<div class="about-founder-body">
{#each founderSection.body as paragraph}
<p>{paragraph}</p>
{/each}
</div>
<div class="about-founder-signoff">
<div class="about-founder-signoff-text">
<span class="about-founder-name">Aless</span>
<span class="about-founder-role">Goodwalk founder</span>
</div>
</div>
<a
class="about-founder-contact-note"
href="mailto:info@goodwalk.co.nz"
aria-label="Email Aless at Goodwalk"
>
<span class="about-founder-contact-icon">
<Icon name="fas fa-envelope" />
</span>
<span>If you are unsure about anything, feel free to email me anytime.</span>
</a>
<a href="/contact-us" class="btn btn-green btn-mobile-center about-founder-cta">
Book a free Meet &amp; Greet
</a>
</article>
</div>
</div>
</section>
@@ -267,6 +293,20 @@
color: #0d1a0d;
}
.about-founder-note {
padding: 40px 42px 34px;
background: linear-gradient(180deg, rgba(251, 251, 251, 0.98) 0%, rgba(247, 248, 246, 1) 100%);
border: 1px solid rgba(17, 20, 24, 0.08);
border-radius: 28px;
box-shadow: var(--shadow-panel-elevated);
}
.about-founder-kicker {
display: block;
width: fit-content;
margin-bottom: 18px;
}
.about-founder-heading-desktop {
display: block;
}
@@ -321,16 +361,91 @@
}
.about-founder-copy p {
margin: 14px 0 0;
margin: 0;
color: #34363a;
font-size: 17px;
line-height: 1.75;
}
.about-founder-copy .btn {
.about-founder-body {
display: grid;
gap: 14px;
}
.about-founder-signoff {
display: flex;
align-items: center;
gap: 14px;
margin-top: 28px;
padding-top: 24px;
border-top: 1px solid rgba(17, 20, 24, 0.08);
}
.about-founder-signoff-text {
display: flex;
flex-direction: column;
gap: 2px;
}
.about-founder-name {
color: #0d1a0d;
font-family: var(--font-head);
font-size: 16px;
font-weight: 700;
line-height: 1.2;
}
.about-founder-role {
color: var(--gray);
font-size: 13px;
line-height: 1.3;
}
.about-founder-contact-note {
display: inline-flex;
align-items: center;
gap: 10px;
margin-top: 20px;
padding: 12px 16px;
border-radius: 18px;
background: rgba(33, 48, 33, 0.05);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.06);
color: var(--gw-green);
font-size: 14px;
font-weight: 600;
line-height: 1.5;
text-decoration: none;
transition: background 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}
.about-founder-contact-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 50%;
background: #fff;
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.07);
color: var(--gw-green);
font-size: 13px;
flex: 0 0 auto;
}
.about-founder-cta {
display: flex;
width: fit-content;
margin: 28px auto 0;
margin: 28px 0 0;
}
@media (hover: hover) {
.about-founder-contact-note:hover {
background: rgba(33, 48, 33, 0.08);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.08),
0 10px 22px rgba(17, 20, 24, 0.05);
transform: translateY(-1px);
}
}
/* ── FAQs ── */
@@ -421,6 +536,11 @@
gap: 28px;
}
.about-founder-note {
padding: 26px 24px 24px;
border-radius: 24px;
}
.about-founder-copy h2 {
font-size: 26px;
line-height: 1.02;
@@ -440,6 +560,24 @@
line-height: 1.7;
}
.about-founder-kicker {
margin-left: auto;
margin-right: auto;
}
.about-founder-contact-note {
display: flex;
width: 100%;
font-size: 13px;
padding: 11px 14px;
text-align: center;
}
.about-founder-cta {
width: 100%;
justify-content: center;
}
.about-faq {
padding: 60px 0;
}
+1 -1
View File
@@ -34,7 +34,7 @@
</div>
</PageHeader>
<BookingSection {booking} {allowGeneralEnquiry} />
<BookingSection {booking} {allowGeneralEnquiry} variant="card-stepper" />
<InfoSection {info} />
</main>
+609 -36
View File
@@ -8,6 +8,7 @@
export let booking: BookingContent;
export let allowGeneralEnquiry = false;
export let variant: 'default' | 'minimal-premium' | 'card-stepper' = 'default';
type EnquiryType = 'booking' | 'general';
const visitStartedStorageKey = 'goodwalk_visit_started_at';
const journeyStorageKey = 'goodwalk_journey';
@@ -43,6 +44,11 @@
'Tell us your puppys age, routine, toilet needs, feeding schedule, and anything important we should know before visiting.'
}
};
const bookingAvatarDogs = [
{ image: '/images/archie-auckland-dog-walking-review.jpg', alt: 'Archie' },
{ image: '/images/monty-auckland-dog-walking-review.jpg', alt: 'Monty' },
{ image: '/images/otis-auckland-dog-walking-review.jpg', alt: 'Otis' }
];
let step = 1;
$: headingParts = splitBookingTitle(booking.title);
@@ -136,6 +142,7 @@
: activeServicePrompt?.messagePlaceholder || 'Describe your pet, any special needs, or anything we should know.';
$: successPetName = petName.trim() || 'your dog';
$: serviceChoiceLocked = !isGeneralEnquiry && selectedServices.length === 1 && !showServicePicker;
$: isCardStepper = variant === 'card-stepper';
onMount(() => {
const now = Date.now();
@@ -420,7 +427,13 @@
}
</script>
<section id="newlead" use:reveal={{ delay: 70 }} class="reveal-block">
<section
id="newlead"
use:reveal={{ delay: 70 }}
class="booking-shell reveal-block"
class:booking-shell--minimal-premium={variant === 'minimal-premium'}
class:booking-shell--card-stepper={variant === 'card-stepper'}
>
<div class="form-inner">
{#if submitted}
<SuccessModal
@@ -448,38 +461,40 @@
<span class="booking-title-highlight">{headingParts.highlight}</span>
</h2>
<p class="booking-intro">{bookingIntro}</p>
<div class="booking-trust-row" aria-label="Booking highlights">
<span class="booking-trust-chip">
<Icon name="fas fa-comment-dots" />
Reply within 24 hours
</span>
<span class="booking-trust-chip">
<Icon name="fas fa-paw" />
Free, no-obligation Meet &amp; Greet
</span>
</div>
{#if !isCardStepper}
<div class="booking-trust-row" aria-label="Booking highlights">
<span class="booking-trust-chip">
<Icon name="fas fa-comment-dots" />
Reply within 24 hours
</span>
<span class="booking-trust-chip">
<Icon name="fas fa-paw" />
Free, no-obligation Meet &amp; Greet
</span>
</div>
<div class="booking-stepper" aria-label="Booking form steps">
<button
type="button"
class:active={step === 1}
class="booking-step"
on:click={() => setStep(1, step !== 1)}
>
<span class="booking-step-number">1</span>
<span class="booking-step-label">{detailsStepLabel}</span>
</button>
<span class="booking-step-divider" aria-hidden="true"></span>
<button
type="button"
class:active={step === 2}
class="booking-step"
on:click={goToOwnerStep}
>
<span class="booking-step-number">2</span>
<span class="booking-step-label">{ownerStepLabel}</span>
</button>
</div>
<div class="booking-stepper" aria-label="Booking form steps">
<button
type="button"
class:active={step === 1}
class="booking-step"
on:click={() => setStep(1, step !== 1)}
>
<span class="booking-step-number">1</span>
<span class="booking-step-label">{detailsStepLabel}</span>
</button>
<span class="booking-step-divider" aria-hidden="true"></span>
<button
type="button"
class:active={step === 2}
class="booking-step"
on:click={goToOwnerStep}
>
<span class="booking-step-number">2</span>
<span class="booking-step-label">{ownerStepLabel}</span>
</button>
</div>
{/if}
</div>
<form
@@ -503,15 +518,53 @@
/>
</div>
<div class:booking-form-shell={isCardStepper}>
{#if isCardStepper}
<div class="booking-social-proof" aria-label="Goodwalk dog families">
<div class="booking-avatar-group" aria-hidden="true">
{#each bookingAvatarDogs as dog}
<span class="booking-avatar-bubble">
<img src={dog.image} alt="" loading="lazy" />
</span>
{/each}
</div>
<p>Join other happy Goodwalk dog owners across Auckland.</p>
</div>
<div class="booking-form-shell-top">
<div class="booking-stepper" aria-label="Booking form steps">
<button
type="button"
class:active={step === 1}
class="booking-step"
on:click={() => setStep(1, step !== 1)}
>
<span class="booking-step-number">1</span>
<span class="booking-step-label">{detailsStepLabel}</span>
</button>
<span class="booking-step-divider" aria-hidden="true"></span>
<button
type="button"
class:active={step === 2}
class="booking-step"
on:click={goToOwnerStep}
>
<span class="booking-step-number">2</span>
<span class="booking-step-label">{ownerStepLabel}</span>
</button>
</div>
</div>
{/if}
{#if step === 1}
<input type="hidden" name="enquiryType" value={enquiryType} />
<div class="booking-panel">
{#if detailsStepIntro}
{#if detailsStepIntro && !isCardStepper}
<div class="booking-panel-banner">{detailsStepIntro}</div>
{/if}
<div
class:booking-card-grid-with-banner={Boolean(detailsStepIntro)}
class:booking-card-grid-with-banner={Boolean(detailsStepIntro) && !isCardStepper}
class="booking-card-grid booking-card-grid-dog"
>
{#if allowGeneralEnquiry && !isGeneralEnquiry}
@@ -684,12 +737,12 @@
<input type="hidden" name="message" value={message} />
<div class="booking-panel">
{#if ownerIntro}
{#if ownerIntro && !isCardStepper}
<div class="booking-panel-banner">{ownerIntro}</div>
{/if}
<div
class:booking-card-grid-with-banner={Boolean(ownerIntro)}
class:booking-card-grid-with-banner={Boolean(ownerIntro) && !isCardStepper}
class="booking-card-grid booking-card-grid-owner"
>
<div class="booking-field-card booking-field-card-group booking-field-card-full">
@@ -791,6 +844,8 @@
</button>
</div>
{/if}
</div>
</form>
</div>
</section>
@@ -820,4 +875,522 @@
opacity: 0;
pointer-events: none;
}
.booking-shell--card-stepper {
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.7), rgba(248, 247, 242, 0.9));
}
.booking-shell--card-stepper :global(.form-inner) {
max-width: 1040px;
}
.booking-shell--card-stepper .booking-header {
max-width: 840px;
margin: 0 auto 34px;
}
.booking-shell--card-stepper .booking-eyebrow {
padding: 7px 14px;
background: rgba(33, 48, 33, 0.05);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.05);
text-transform: none;
letter-spacing: 0.04em;
}
.booking-shell--card-stepper .booking-title {
margin-bottom: 20px;
font-size: clamp(38px, 5vw, 58px);
line-height: 1.02;
}
.booking-shell--card-stepper .booking-intro {
max-width: 42rem;
margin: 0 auto;
}
.booking-shell--card-stepper .booking-form-shell {
max-width: 900px;
margin: 0 auto;
padding: 0 24px 28px;
border-radius: 30px;
background: rgba(255, 255, 255, 0.94);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.06),
0 22px 52px rgba(17, 20, 24, 0.08);
}
.booking-shell--card-stepper .booking-social-proof {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
margin: 0 -24px;
padding: 16px 24px;
border-radius: 30px 30px 0 0;
background:
linear-gradient(180deg, rgba(33, 48, 33, 0.06), rgba(33, 48, 33, 0.035));
box-shadow: inset 0 -1px 0 rgba(17, 20, 24, 0.08);
}
.booking-shell--card-stepper .booking-form-shell-top {
padding: 22px 0;
border-bottom: 1px solid rgba(17, 20, 24, 0.08);
}
.booking-shell--card-stepper .booking-social-proof p {
margin: 0;
color: var(--gw-green);
font-family: var(--font-head);
font-size: 17px;
font-weight: 600;
line-height: 1.3;
text-wrap: balance;
text-align: center;
}
.booking-shell--card-stepper .booking-avatar-group {
display: flex;
align-items: center;
flex: none;
padding-left: 14px;
}
.booking-shell--card-stepper .booking-avatar-bubble {
display: inline-flex;
width: 50px;
height: 50px;
margin-left: -14px;
overflow: hidden;
border: 3px solid rgba(255, 255, 255, 0.96);
border-radius: 50%;
background: rgba(255, 255, 255, 0.16);
box-shadow: 0 8px 18px rgba(17, 20, 24, 0.08);
}
.booking-shell--card-stepper .booking-avatar-bubble img {
width: 100%;
height: 100%;
object-fit: cover;
}
.booking-shell--card-stepper .booking-stepper {
gap: 20px;
margin-top: 24px;
padding: 0;
background: transparent;
box-shadow: none;
}
.booking-shell--card-stepper .booking-step {
min-height: 50px;
padding: 10px 16px;
border-radius: 999px;
}
.booking-shell--card-stepper .booking-step.active {
background: rgba(33, 48, 33, 0.06);
}
.booking-shell--card-stepper .booking-step-number {
width: 42px;
height: 42px;
border: none;
border-radius: 13px;
background: var(--gw-green);
color: var(--yellow);
font-size: 18px;
}
.booking-shell--card-stepper .booking-step.active .booking-step-number {
background: linear-gradient(135deg, var(--gw-green), var(--green-mid));
border: none;
}
.booking-shell--card-stepper .booking-step-divider {
width: 48px;
height: 1px;
background: rgba(17, 20, 24, 0.12);
}
.booking-shell--card-stepper .booking-panel {
max-width: none;
margin: 24px 0 0;
padding: 0;
background: transparent;
box-shadow: none;
}
.booking-shell--card-stepper .booking-panel-banner {
margin-bottom: 18px;
padding: 0;
border: none;
border-radius: 0;
background: transparent;
box-shadow: none;
color: #4f555c;
font-size: 16px;
line-height: 1.65;
}
.booking-shell--card-stepper .booking-card-grid {
gap: 14px;
}
.booking-shell--card-stepper .booking-card-grid-with-banner .booking-field-card {
border-radius: 26px;
}
.booking-shell--card-stepper .booking-field-card {
border-radius: 0;
background: transparent;
box-shadow: none;
padding: 0;
}
.booking-shell--card-stepper .booking-field-card-group {
padding: 0;
}
.booking-shell--card-stepper .booking-inline-switch {
justify-content: flex-start;
padding: 0 4px;
margin-bottom: 0;
color: #5d636a;
background: transparent;
box-shadow: none;
}
.booking-shell--card-stepper .booking-field-card label,
.booking-shell--card-stepper .booking-service-label {
color: #171b20;
font-size: 15px;
font-weight: 600;
letter-spacing: 0;
text-transform: none;
}
.booking-shell--card-stepper .booking-field-card input,
.booking-shell--card-stepper .booking-field-card textarea {
border: 1px solid rgba(17, 20, 24, 0.1);
background: #fff;
box-shadow: none;
}
.booking-shell--card-stepper .booking-field-card input:hover,
.booking-shell--card-stepper .booking-field-card textarea:hover {
border-color: rgba(17, 20, 24, 0.18);
background: #fff;
box-shadow: none;
}
.booking-shell--card-stepper .booking-field-card input:focus,
.booking-shell--card-stepper .booking-field-card textarea:focus {
border-color: rgba(33, 48, 33, 0.45);
background: #fff;
box-shadow: 0 0 0 4px rgba(255, 209, 0, 0.12);
}
.booking-shell--card-stepper .booking-selected-service-chip,
.booking-shell--card-stepper .booking-check-option {
background: transparent;
}
.booking-shell--card-stepper .booking-actions {
max-width: none;
margin-left: 0;
margin-right: 0;
}
.booking-shell--card-stepper .booking-actions-next,
.booking-shell--card-stepper .booking-actions-final {
margin-top: 18px;
}
.booking-shell--card-stepper .booking-next-note {
color: #687076;
}
.booking-shell--card-stepper .booking-submit-button,
.booking-shell--card-stepper .booking-next-button {
box-shadow:
inset 0 -2px 0 rgba(0, 0, 0, 0.08),
0 12px 24px rgba(17, 20, 24, 0.1);
}
.booking-shell--minimal-premium {
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.78), rgba(248, 247, 242, 0.72));
}
.booking-shell--minimal-premium :global(.form-inner) {
max-width: 980px;
}
.booking-shell--minimal-premium .booking-header {
max-width: 760px;
margin: 0 auto 34px;
}
.booking-shell--minimal-premium .booking-eyebrow {
background: rgba(33, 48, 33, 0.06);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.05);
color: var(--gw-green);
}
.booking-shell--minimal-premium .booking-title {
margin-bottom: 18px;
color: #11171b;
}
.booking-shell--minimal-premium .booking-intro {
max-width: 38rem;
margin: 0 auto;
color: #59606d;
font-size: 16px;
line-height: 1.65;
}
.booking-shell--minimal-premium .booking-trust-row {
justify-content: center;
gap: 10px;
margin-top: 20px;
}
.booking-shell--minimal-premium .booking-trust-chip {
background: rgba(33, 48, 33, 0.05);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.05);
color: var(--gw-green);
}
.booking-shell--minimal-premium .booking-stepper {
justify-content: center;
margin-top: 24px;
}
.booking-shell--minimal-premium .booking-step {
background: rgba(255, 255, 255, 0.86);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.06),
0 8px 18px rgba(17, 20, 24, 0.05);
}
.booking-shell--minimal-premium .booking-step.active {
background: rgba(33, 48, 33, 0.08);
}
.booking-shell--minimal-premium .booking-panel {
max-width: 860px;
margin: 0 auto;
padding: 30px;
border-radius: 32px;
background: rgba(251, 251, 251, 0.92);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.05),
0 22px 52px rgba(17, 20, 24, 0.08);
}
.booking-shell--minimal-premium .booking-panel-banner {
margin-bottom: 22px;
padding: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
color: #59606d;
font-size: 15px;
line-height: 1.65;
}
.booking-shell--minimal-premium .booking-card-grid {
gap: 18px;
}
.booking-shell--minimal-premium .booking-field-card,
.booking-shell--minimal-premium .booking-inline-switch {
border-radius: 24px;
background: linear-gradient(180deg, rgba(251, 251, 251, 0.98), rgba(247, 248, 246, 1));
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.05),
0 12px 26px rgba(17, 20, 24, 0.05);
}
.booking-shell--minimal-premium .booking-field-card {
padding: 24px;
}
.booking-shell--minimal-premium .booking-field-card-group {
padding: 24px;
}
.booking-shell--minimal-premium .booking-inline-switch {
padding: 16px 18px;
}
.booking-shell--minimal-premium .booking-field-card label,
.booking-shell--minimal-premium .booking-service-label {
color: #171b20;
}
.booking-shell--minimal-premium .booking-field-card input,
.booking-shell--minimal-premium .booking-field-card textarea {
border: none;
background: rgba(255, 255, 255, 0.96);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.08),
0 1px 0 rgba(255, 255, 255, 0.3);
}
.booking-shell--minimal-premium .booking-field-card input:hover,
.booking-shell--minimal-premium .booking-field-card textarea:hover {
border: none;
background: #fff;
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.12),
0 4px 12px rgba(17, 20, 24, 0.04);
}
.booking-shell--minimal-premium .booking-field-card input:focus,
.booking-shell--minimal-premium .booking-field-card textarea:focus {
border: none;
background: #fff;
box-shadow:
inset 0 0 0 2px rgba(255, 209, 0, 0.6),
0 0 0 4px rgba(255, 209, 0, 0.14);
}
.booking-shell--minimal-premium .booking-selected-service-chip,
.booking-shell--minimal-premium .booking-check-option {
background: rgba(255, 255, 255, 0.9);
}
.booking-shell--minimal-premium .booking-actions {
max-width: 860px;
margin-left: auto;
margin-right: auto;
}
.booking-shell--minimal-premium .booking-actions-next {
margin-top: 18px;
}
.booking-shell--minimal-premium .booking-actions-final {
margin-top: 18px;
}
.booking-shell--minimal-premium .booking-next-note {
color: #687076;
}
.booking-shell--minimal-premium .booking-submit-button,
.booking-shell--minimal-premium .booking-next-button {
box-shadow:
inset 0 -2px 0 rgba(0, 0, 0, 0.08),
0 12px 24px rgba(17, 20, 24, 0.1);
}
@media (max-width: 768px) {
.booking-shell--card-stepper .booking-header {
margin-bottom: 26px;
}
.booking-shell--card-stepper .booking-title {
margin-bottom: 18px;
font-size: 34px;
}
.booking-shell--card-stepper .booking-form-shell {
padding: 0 18px 22px;
border-radius: 24px;
}
.booking-shell--card-stepper .booking-social-proof {
align-items: center;
flex-direction: column;
gap: 10px;
margin: 0 -18px;
padding: 14px 18px;
border-radius: 24px 24px 0 0;
}
.booking-shell--card-stepper .booking-form-shell-top {
padding: 18px 0;
}
.booking-shell--card-stepper .booking-social-proof p {
font-size: 16px;
}
.booking-shell--card-stepper .booking-stepper {
gap: 10px;
padding: 0;
}
.booking-shell--card-stepper .booking-step {
min-width: 0;
flex: 1 1 0;
justify-content: center;
gap: 8px;
padding: 10px;
}
.booking-shell--card-stepper .booking-step-number {
width: 38px;
height: 38px;
font-size: 15px;
}
.booking-shell--card-stepper .booking-step-label {
font-size: 13px;
}
.booking-shell--card-stepper .booking-step-divider {
display: none;
}
.booking-shell--card-stepper .booking-panel {
margin-top: 20px;
}
.booking-shell--card-stepper .booking-panel-banner {
font-size: 15px;
}
.booking-shell--card-stepper .booking-field-card,
.booking-shell--card-stepper .booking-field-card-group {
padding: 0;
border-radius: 0;
}
.booking-shell--card-stepper .booking-inline-switch {
border-radius: 20px;
}
.booking-shell--minimal-premium .booking-header {
margin-bottom: 26px;
}
.booking-shell--minimal-premium .booking-intro {
font-size: 15px;
line-height: 1.58;
}
.booking-shell--minimal-premium .booking-stepper {
gap: 8px;
}
.booking-shell--minimal-premium .booking-panel {
padding: 18px;
border-radius: 24px;
}
.booking-shell--minimal-premium .booking-field-card,
.booking-shell--minimal-premium .booking-field-card-group {
padding: 20px;
border-radius: 22px;
}
.booking-shell--minimal-premium .booking-inline-switch {
border-radius: 20px;
}
}
</style>
+16 -10
View File
@@ -46,16 +46,22 @@
decoding="async"
/>
<p>{footer.brandText}</p>
<div class="social-links">
<a href={socialLinks[0].href} target="_blank" rel="noopener" aria-label="Instagram">
<Icon name="fab fa-instagram" />
</a>
<a href={socialLinks[1].href} target="_blank" rel="noopener" aria-label="Facebook">
<Icon name="fab fa-facebook-f" />
</a>
<a href={socialLinks[2].href} target="_blank" rel="noopener" aria-label="Google">
<Icon name="fab fa-google" />
</a>
<div class="footer-social-cluster">
<div class="footer-social-invite" aria-hidden="true">
<span>Join the fun</span>
<Icon name="fas fa-arrow-turn-down" className="footer-social-invite-arrow" />
</div>
<div class="social-links">
<a href={socialLinks[0].href} target="_blank" rel="noopener" aria-label="Instagram">
<Icon name="fab fa-instagram" />
</a>
<a href={socialLinks[1].href} target="_blank" rel="noopener" aria-label="Facebook">
<Icon name="fab fa-facebook-f" />
</a>
<a href={socialLinks[2].href} target="_blank" rel="noopener" aria-label="Google">
<Icon name="fab fa-google" />
</a>
</div>
</div>
{#if footer.email || footer.phone}
+116 -51
View File
@@ -14,14 +14,12 @@
let showMeetGreetPrompt = false;
let dismissMeetGreetPrompt = false;
let bookingInView = false;
let promptShown = false;
let canShowDesktopPrompt = false;
const desktopPromptMediaQuery = '(min-width: 769px)';
const scrollDepthThreshold = 0.65;
function revealMeetGreetPrompt() {
if (dismissMeetGreetPrompt || bookingInView || promptShown || !canShowDesktopPrompt) {
if (dismissMeetGreetPrompt || promptShown || !canShowDesktopPrompt) {
return;
}
@@ -39,7 +37,7 @@
dismissMeetGreetPrompt = true;
}
$: if ((!canShowDesktopPrompt || bookingInView) && showMeetGreetPrompt) {
$: if (!canShowDesktopPrompt && showMeetGreetPrompt) {
showMeetGreetPrompt = false;
}
@@ -51,42 +49,27 @@
canShowDesktopPrompt = event.matches;
};
const firstPricingSection = document.querySelector<HTMLElement>('.pricing-section');
const handleScroll = () => {
if (promptShown || dismissMeetGreetPrompt || bookingInView || !canShowDesktopPrompt) {
if (promptShown || dismissMeetGreetPrompt || !canShowDesktopPrompt || !firstPricingSection) {
return;
}
const scrollableHeight = document.documentElement.scrollHeight - window.innerHeight;
if (scrollableHeight <= 0) return;
const scrollPercent = window.scrollY / scrollableHeight;
if (scrollPercent >= scrollDepthThreshold) {
const firstSectionBottom = firstPricingSection.getBoundingClientRect().bottom;
if (firstSectionBottom <= 0) {
revealMeetGreetPrompt();
}
};
window.addEventListener('scroll', handleScroll, { passive: true });
const bookingSection = document.getElementById('newlead');
const bookingObserver = bookingSection
? new IntersectionObserver(
([entry]) => {
bookingInView = entry.isIntersecting;
},
{ threshold: 0.2 }
)
: null;
if (bookingObserver && bookingSection) {
bookingObserver.observe(bookingSection);
}
desktopPromptQuery.addEventListener('change', handleDesktopPromptViewportChange);
handleScroll();
return () => {
window.removeEventListener('scroll', handleScroll);
desktopPromptQuery.removeEventListener('change', handleDesktopPromptViewportChange);
bookingObserver?.disconnect();
};
});
</script>
@@ -174,7 +157,7 @@
testimonials={content.testimonials}
seedKey="/our-pricing"
/>
<BookingSection booking={pageContent.booking} />
<BookingSection booking={pageContent.booking} variant="card-stepper" />
{#if showMeetGreetPrompt}
<aside class="meet-greet-prompt" aria-label="Free meet and greet reminder">
@@ -183,16 +166,36 @@
</button>
<div class="meet-greet-copy">
<span class="meet-greet-kicker">
<Icon name="fas fa-comment-dots" />
Free Meet & Greet
</span>
<div class="meet-greet-top">
<span class="meet-greet-kicker">
<Icon name="fas fa-comment-dots" />
Free Meet & Greet
</span>
<div class="meet-greet-profile" aria-label="Aless from Goodwalk">
<img
class="meet-greet-avatar"
src="/images/founder-image-aless-goodwalk.jpg"
alt="Aless from Goodwalk"
width="52"
height="52"
/>
<div class="meet-greet-profile-copy">
<strong>Talk it through with Aless</strong>
<span>Founder, Goodwalk</span>
</div>
</div>
</div>
<p>
Not sure which option fits best? We can talk it through together and make sure your dog ends up happy.
Not sure which option fits best? Aless can help you choose the right walk or visit for your dog, routine, and temperament.
</p>
</div>
<a class="meet-greet-cta" href="#newlead" on:click={handleMeetGreetCta}>Book a Meet &amp; Greet</a>
<a class="meet-greet-cta" href="#newlead" on:click={handleMeetGreetCta}>
<span>Book a Meet &amp; Greet</span>
<Icon name="fas fa-arrow-right" />
</a>
</aside>
{/if}
</main>
@@ -276,8 +279,8 @@
height: 56px;
margin-bottom: 16px;
border-radius: 16px;
background: var(--gw-green);
color: #fff;
background: linear-gradient(135deg, var(--gw-green), var(--green-mid));
color: var(--yellow);
font-size: 22px;
}
@@ -304,6 +307,8 @@
.pricing-plan-grid-three {
grid-template-columns: repeat(3, minmax(0, 1fr));
max-width: calc(((var(--max-w) - (var(--space-container-x) * 2) - (22px * 3)) / 4 * 3) + (22px * 2));
margin-inline: auto;
}
.pricing-section-mobile-cta,
@@ -317,23 +322,30 @@
bottom: 24px;
z-index: 30;
display: flex;
align-items: flex-end;
flex-direction: column;
gap: 18px;
width: min(420px, calc(100vw - 32px));
padding: 18px 18px 18px 20px;
border-radius: 28px;
width: min(430px, calc(100vw - 32px));
padding: 20px;
border-radius: 30px;
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.98), rgba(247, 243, 232, 0.98));
radial-gradient(circle at top right, rgba(255, 209, 0, 0.12), rgba(255, 209, 0, 0) 34%),
linear-gradient(180deg, rgba(251, 251, 251, 0.98), rgba(247, 248, 246, 0.98));
box-shadow:
0 20px 40px rgba(17, 20, 24, 0.16),
0 0 0 1px rgba(17, 20, 24, 0.06);
0 20px 40px rgba(17, 20, 24, 0.14),
inset 0 0 0 1px rgba(17, 20, 24, 0.06);
animation: meet-greet-rise 0.28s cubic-bezier(0.22, 1, 0.36, 1);
backdrop-filter: blur(10px);
}
.meet-greet-copy {
min-width: 0;
flex: 1;
}
.meet-greet-top {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 14px;
}
.meet-greet-kicker {
@@ -355,22 +367,69 @@
font-size: 12px;
}
.meet-greet-profile {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
padding: 8px 10px 8px 8px;
border-radius: 18px;
background: rgba(33, 48, 33, 0.05);
box-shadow: inset 0 0 0 1px rgba(33, 48, 33, 0.07);
}
.meet-greet-avatar {
width: 52px;
height: 52px;
border-radius: 16px;
object-fit: cover;
object-position: center top;
flex: 0 0 auto;
box-shadow:
0 8px 20px rgba(17, 20, 24, 0.12),
inset 0 0 0 1px rgba(255, 255, 255, 0.18);
}
.meet-greet-profile-copy {
display: flex;
flex-direction: column;
min-width: 0;
}
.meet-greet-profile-copy strong {
color: #16181d;
font-family: var(--font-head);
font-size: 12px;
font-weight: 700;
line-height: 1.3;
}
.meet-greet-profile-copy span {
color: #59606d;
font-size: 12px;
line-height: 1.35;
}
.meet-greet-copy p {
margin: 0;
margin: 14px 0 0;
color: #2f3134;
font-size: 15px;
line-height: 1.55;
}
.meet-greet-cta {
flex-shrink: 0;
align-self: center;
padding: 12px 18px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
width: 100%;
padding: 14px 18px;
border-radius: 999px;
background: var(--yellow);
background: linear-gradient(135deg, #ffd54a, var(--yellow-soft));
color: #111;
font-family: var(--font-head);
font-size: 14px;
font-weight: 700;
text-decoration: none;
box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.08);
transition:
@@ -401,7 +460,7 @@
@media (hover: hover) {
.meet-greet-cta:hover {
transform: translateY(-2px);
background: #ffd100;
background: linear-gradient(135deg, #ffdc66, #f4c94f);
box-shadow:
inset 0 -2px 0 rgba(0, 0, 0, 0.08),
0 12px 24px rgba(17, 20, 24, 0.12);
@@ -434,6 +493,7 @@
.pricing-plan-grid,
.pricing-plan-grid-three {
grid-template-columns: repeat(2, minmax(0, 1fr));
max-width: none;
}
}
@@ -470,6 +530,7 @@
.pricing-plan-grid-three {
grid-template-columns: 1fr;
gap: 18px;
max-width: none;
}
.pricing-section-mobile-cta {
@@ -518,13 +579,17 @@
left: 16px;
bottom: 16px;
width: auto;
flex-direction: column;
align-items: stretch;
gap: 14px;
padding: 18px 18px 16px;
border-radius: 28px;
}
.meet-greet-top {
flex-direction: column;
align-items: stretch;
gap: 12px;
}
.meet-greet-copy p {
font-size: 14px;
line-height: 1.5;
@@ -532,7 +597,7 @@
}
.meet-greet-cta {
align-self: flex-start;
width: 100%;
}
}
</style>
+180 -33
View File
@@ -1,4 +1,6 @@
<script lang="ts">
import Icon from '$lib/components/Icon.svelte';
export let plan: {
title: string;
price: string;
@@ -18,16 +20,39 @@
style="--mobile-order:{plan.mobileOrder};"
>
{#if plan.isPopular}
<span class="plan-card__ribbon">Popular</span>
<span class="plan-card__ribbon">
<Icon name="fas fa-star" className="plan-card__ribbon-icon" />
Popular
</span>
{/if}
<h3>{plan.title}</h3>
<div class="plan-card__price">{plan.price}</div>
<p class="plan-card__period">{plan.period}</p>
<ul class="plan-card__features">
{#each plan.features as feature}
<li>{feature}</li>
{/each}
</ul>
<div class="plan-card__header">
<div class="plan-card__eyebrow">
<span class="plan-card__eyebrow-badge">
<Icon name={variant === 'pricing' ? 'fas fa-paw' : 'fas fa-leaf'} className="plan-card__eyebrow-icon" />
</span>
<span>{plan.isPopular ? 'Goodwalk favourite' : variant === 'pricing' ? 'Flexible routine' : 'Tailored support'}</span>
</div>
<h3>{plan.title}</h3>
<div class="plan-card__price">{plan.price}</div>
<p class="plan-card__period">{plan.period}</p>
</div>
<div class="plan-card__body">
<p class="plan-card__feature-label">
<Icon name="fas fa-circle-check" className="plan-card__feature-label-icon" />
What's included
</p>
<ul class="plan-card__features">
{#each plan.features as feature}
<li>
<Icon
name={variant === 'pricing' ? 'fas fa-check' : 'fas fa-paw'}
className="plan-card__feature-icon"
/>
<span>{feature}</span>
</li>
{/each}
</ul>
</div>
<a class="btn btn-yellow plan-card__cta" href="#newlead">Book a Meet &amp; Greet</a>
</article>
@@ -38,22 +63,37 @@
display: flex;
flex-direction: column;
border-radius: 28px;
padding: 30px 26px;
border: 1.5px solid rgba(17, 20, 24, 0.09);
padding: 38px 26px 30px;
overflow: visible;
transition:
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1),
box-shadow 0.22s ease,
border-color 0.22s ease;
border-color 0.22s ease,
filter 0.22s ease;
}
.plan-card::before {
content: '';
position: absolute;
inset: 0 0 auto;
height: 88px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0));
pointer-events: none;
}
.plan-card--popular {
border: 2px solid var(--yellow);
box-shadow:
inset 0 0 0 1px rgba(242, 191, 47, 0.45),
0 14px 34px rgba(17, 20, 24, 0.06);
}
/* ── Service variant ── */
.plan-card--service {
align-items: stretch;
height: 100%;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.96) 0%, rgba(247, 248, 246, 0.98) 100%);
background: linear-gradient(180deg, rgba(251, 251, 251, 0.98) 0%, rgba(247, 248, 246, 1) 100%);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.045),
0 8px 40px rgba(0, 0, 0, 0.06);
@@ -63,25 +103,80 @@
.plan-card--pricing {
align-items: center;
text-align: center;
background: #fff;
box-shadow: 0 14px 34px rgba(17, 20, 24, 0.05);
background: linear-gradient(180deg, rgba(251, 251, 251, 0.98) 0%, rgba(247, 248, 246, 1) 100%);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.045),
0 14px 34px rgba(17, 20, 24, 0.05);
}
.plan-card--pricing.plan-card--popular {
background:
radial-gradient(circle at top, rgba(255, 209, 0, 0.12), rgba(255, 209, 0, 0) 40%),
linear-gradient(180deg, rgba(251, 251, 251, 0.98) 0%, rgba(247, 248, 246, 1) 100%);
}
.plan-card__header,
.plan-card__body {
position: relative;
z-index: 1;
width: 100%;
}
/* ── Ribbon ── */
.plan-card__ribbon {
display: inline-flex;
align-items: center;
gap: 6px;
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%);
transform: translate(-50%, -40%);
padding: 6px 12px;
border-radius: 999px;
background: var(--yellow);
color: #000;
background: linear-gradient(135deg, var(--gw-green), var(--green-mid));
color: #fff;
font-family: var(--font-head);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
box-shadow: 0 10px 20px rgba(17, 20, 24, 0.08);
white-space: nowrap;
}
:global(.plan-card__ribbon-icon) {
color: var(--yellow);
font-size: 10px;
}
.plan-card__eyebrow {
display: inline-flex;
align-items: center;
gap: 10px;
margin-bottom: 18px;
color: var(--gw-green);
font-family: var(--font-head);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.plan-card__eyebrow-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
border-radius: 12px;
background: linear-gradient(135deg, var(--gw-green), var(--green-mid));
color: #fff;
box-shadow: 0 10px 22px rgba(33, 48, 33, 0.14);
}
:global(.plan-card__eyebrow-icon) {
color: var(--yellow);
font-size: 14px;
}
/* ── Heading ── */
@@ -90,7 +185,7 @@
font-family: var(--font-head);
font-size: 22px;
line-height: 1.2;
color: #000;
color: #16181d;
}
/* ── Price ── */
@@ -110,7 +205,7 @@
font-size: 52px;
line-height: 0.95;
letter-spacing: -0.05em;
color: #000;
color: #16181d;
}
/* ── Period ── */
@@ -131,9 +226,33 @@
letter-spacing: 0.08em;
}
.plan-card__body {
margin-top: 24px;
padding-top: 18px;
border-top: 1px solid rgba(17, 20, 24, 0.07);
}
.plan-card__feature-label {
display: inline-flex;
align-items: center;
gap: 8px;
margin: 0 0 14px;
color: #59606d;
font-family: var(--font-head);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
:global(.plan-card__feature-label-icon) {
color: var(--gw-green);
font-size: 12px;
}
/* ── Features ── */
.plan-card__features {
margin: 24px 0 0;
margin: 0;
padding: 0;
list-style: none;
}
@@ -144,8 +263,9 @@
/* Service: bullet style */
.plan-card--service .plan-card__features li {
position: relative;
padding-left: 24px;
display: grid;
grid-template-columns: 18px minmax(0, 1fr);
gap: 10px;
color: #34363a;
font-size: 15px;
line-height: 1.5;
@@ -155,14 +275,10 @@
margin-top: 12px;
}
.plan-card--service .plan-card__features li::before {
content: '•';
position: absolute;
left: 6px;
top: 0;
color: var(--yellow);
font-size: 20px;
line-height: 1;
:global(.plan-card--service .plan-card__feature-icon) {
margin-top: 3px;
color: var(--yellow-soft);
font-size: 12px;
}
/* Pricing: divider style */
@@ -171,11 +287,26 @@
}
.plan-card--pricing .plan-card__features li {
display: grid;
grid-template-columns: 18px minmax(0, 1fr);
gap: 10px;
padding: 15px 0;
border-top: 1px solid rgba(17, 20, 24, 0.08);
color: #34363a;
font-size: 16px;
line-height: 1.5;
text-align: left;
}
.plan-card--pricing .plan-card__features li:first-child {
padding-top: 0;
border-top: none;
}
:global(.plan-card--pricing .plan-card__feature-icon) {
margin-top: 3px;
color: var(--gw-green);
font-size: 12px;
}
/* ── CTA ── */
@@ -190,6 +321,7 @@
@media (hover: hover) {
.plan-card--service:hover {
transform: translateY(-2px);
border-color: rgba(17, 20, 24, 0.14);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.055),
0 10px 40px rgba(0, 0, 0, 0.08);
@@ -197,8 +329,16 @@
}
.plan-card--pricing:hover {
transform: translateY(-6px) scale(1.012);
box-shadow: 0 22px 44px rgba(17, 20, 24, 0.1);
transform: translateY(-2px);
border-color: rgba(17, 20, 24, 0.14);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.06),
0 18px 38px rgba(17, 20, 24, 0.08);
filter: brightness(1.012);
}
.plan-card--popular:hover {
border-color: var(--yellow);
}
}
@@ -210,7 +350,14 @@
@media (max-width: 768px) {
.plan-card {
order: var(--mobile-order, 0);
padding: 28px 22px;
width: min(100%, 420px);
margin-inline: auto;
padding: 36px 22px 28px;
}
.plan-card__body {
margin-top: 22px;
padding-top: 16px;
}
.plan-card--pricing .plan-card__price {
+1 -1
View File
@@ -358,7 +358,7 @@
testimonials={content.testimonials}
seedKey={currentPath}
/>
<BookingSection booking={pageContent.booking} />
<BookingSection booking={pageContent.booking} variant="card-stepper" />
</main>
<style>
+1 -1
View File
@@ -260,7 +260,7 @@
</div>
</section>
<BookingSection booking={content.booking} />
<BookingSection booking={content.booking} variant="card-stepper" />
</main>
<style>
@@ -201,13 +201,6 @@
</button>
<div bind:this={stageEl} class="testimonial-stage" on:scroll={handleStageScroll}>
<div class="testimonial-woof" aria-hidden="true">
<span class="testimonial-woof-text">WOOF</span>
<span class="testimonial-ray testimonial-ray-1"></span>
<span class="testimonial-ray testimonial-ray-2"></span>
<span class="testimonial-ray testimonial-ray-3"></span>
</div>
{#each slides as testimonial, index}
<article class:testimonial-slide-active={index === activeIndex} class="testimonial-slide">
<div class="testimonial-photo-wrap">
@@ -619,54 +612,6 @@
display: none;
}
.testimonial-woof {
position: absolute;
top: 40px;
right: 60px;
z-index: 2;
color: #2e3031;
transform: rotate(-6deg);
transform-origin: center center;
}
.testimonial-woof-text {
display: inline-block;
font-family: 'Fredoka', 'Fredoka One', var(--font-head), sans-serif;
font-size: 32px;
line-height: 1;
letter-spacing: 0.02em;
}
.testimonial-ray {
position: absolute;
border-radius: 999px;
background: #ffd100;
}
.testimonial-ray-1 {
top: -12px;
right: -48px;
width: 32px;
height: 11px;
transform: rotate(-35deg);
}
.testimonial-ray-2 {
top: 6px;
right: -60px;
width: 46px;
height: 13px;
transform: rotate(-35deg);
}
.testimonial-ray-3 {
top: 24px;
right: -50px;
width: 36px;
height: 11px;
transform: rotate(-35deg);
}
.testimonial-arrow {
position: absolute;
top: 50%;
@@ -715,9 +660,6 @@
font-size: 17px;
}
.testimonial-woof {
right: 40px;
}
}
@media (max-width: 767px) {
@@ -825,39 +767,6 @@
font-size: 20px;
}
.testimonial-woof {
top: 16px;
right: 18px;
}
.testimonial-woof-text {
font-size: 22px;
}
.testimonial-ray {
right: -28px;
width: 9px;
}
.testimonial-ray-1 {
top: -7px;
height: 34px;
}
.testimonial-ray-2 {
top: 10px;
right: -38px;
width: 34px;
height: 9px;
}
.testimonial-ray-3 {
top: 35px;
right: -28px;
width: 27px;
height: 8px;
}
.testimonial-arrow-left,
.testimonial-arrow-right {
display: none;
@@ -0,0 +1,694 @@
<script lang="ts">
export let idPrefix: string;
export let lead = '';
export let submitLabel = 'Book a free Meet & Greet';
export let tone: 'default' | 'conversational' | 'minimal' = 'default';
export let layout: 'stacked' | 'carded' = 'stacked';
export let eyebrow = 'Free Meet & Greet';
export let title = 'Tell us about your dog';
export let subtitle =
'A guided first step so we can recommend the right walk, visit, or calmer starting point.';
let submitted = false;
let step = 1;
let ownerStepUnlocked = false;
function handleSubmit() {
if (step === 1) {
ownerStepUnlocked = true;
step = 2;
return;
}
submitted = true;
}
function goToStep(nextStep: number) {
if (nextStep === 2 && !ownerStepUnlocked) {
return;
}
step = nextStep;
}
</script>
<form
class={`vf-form vf-form--${tone} vf-form--${layout}`}
on:submit|preventDefault={handleSubmit}
novalidate
>
<div class="vf-header">
<span class="vf-eyebrow">{eyebrow}</span>
<h3 class="vf-title">{title}</h3>
<p class="vf-subtitle">{subtitle}</p>
<div class="vf-stepper" aria-label="Application steps">
<button
type="button"
class:active={step === 1}
class="vf-step"
on:click={() => goToStep(1)}
>
<span class="vf-step-number">1</span>
<span class="vf-step-label">Dog details</span>
</button>
<span class="vf-step-divider" aria-hidden="true"></span>
<button
type="button"
class:active={step === 2}
class="vf-step"
disabled={!ownerStepUnlocked}
aria-disabled={!ownerStepUnlocked}
on:click={() => goToStep(2)}
>
<span class="vf-step-number">2</span>
<span class="vf-step-label">Owner details</span>
</button>
</div>
</div>
{#if lead}
<p class="vf-lead">{lead}</p>
{/if}
{#if step === 1}
<section class="vf-group" aria-labelledby={`${idPrefix}-dog-group`}>
<div class="vf-group-head">
<p class="vf-kicker" id={`${idPrefix}-dog-group`}>Dog details</p>
<p class="vf-group-copy">
A few calm details first so we can judge fit, pace, and the right kind of outing.
</p>
</div>
<div class="vf-grid vf-grid--two">
<label class="vf-field">
<span class="vf-label">Dog name</span>
<input id={`${idPrefix}-dog-name`} name="dogName" type="text" placeholder="Maya" />
</label>
<label class="vf-field">
<span class="vf-label">Suburb / location</span>
<input
id={`${idPrefix}-location`}
name="location"
type="text"
placeholder="Mt Eden, Grey Lynn, Ponsonby…"
/>
</label>
</div>
<fieldset class="vf-choice-group">
<legend class="vf-label">What feels closest right now?</legend>
<p class="vf-helper">
Not sure is completely fine. We can sort that out at the Meet &amp; Greet.
</p>
<div class="vf-choices">
<label class="vf-choice">
<input type="radio" name={`${idPrefix}-service-fit`} value="tiny-gang" />
<span>Tiny Gang Pack Walks</span>
</label>
<label class="vf-choice">
<input type="radio" name={`${idPrefix}-service-fit`} value="one-to-one" />
<span>1:1 Walks</span>
</label>
<label class="vf-choice">
<input type="radio" name={`${idPrefix}-service-fit`} value="puppy-visits" />
<span>Puppy Visits</span>
</label>
<label class="vf-choice">
<input type="radio" name={`${idPrefix}-service-fit`} value="not-sure" checked />
<span>Not sure yet</span>
</label>
</div>
</fieldset>
<label class="vf-field vf-field--full">
<span class="vf-label">About your dog</span>
<textarea
id={`${idPrefix}-about-dog`}
name="aboutDog"
rows="5"
placeholder="Tell us about age, size, confidence, leash manners, how they feel around other dogs, and anything that helps us make the right recommendation."
></textarea>
</label>
<div class="vf-note-row" aria-label="Helpful notes">
<article class="vf-note">
<span class="vf-note-title">Nervous dogs</span>
<p>
If your dog is shy, easily overwhelmed, or still building confidence, mention it here.
That often points us toward a calmer first step.
</p>
</article>
<article class="vf-note">
<span class="vf-note-title">Tiny Gang suitability</span>
<p>
Our small-group walks work best for dogs who enjoy the right company, gentle
structure, and a routine they can settle into.
</p>
</article>
</div>
</section>
{/if}
{#if step === 2}
<section class="vf-group" aria-labelledby={`${idPrefix}-owner-group`}>
<div class="vf-group-head">
<p class="vf-kicker" id={`${idPrefix}-owner-group`}>Owner details</p>
<p class="vf-group-copy">
So Aless can reply properly and arrange a free Meet &amp; Greet without back-and-forth.
</p>
</div>
<div class="vf-grid vf-grid--three">
<label class="vf-field">
<span class="vf-label">Owner name</span>
<input id={`${idPrefix}-owner-name`} name="ownerName" type="text" placeholder="Your full name" />
</label>
<label class="vf-field">
<span class="vf-label">Email</span>
<input id={`${idPrefix}-email`} name="email" type="email" placeholder="you@example.com" />
</label>
<label class="vf-field">
<span class="vf-label">Phone</span>
<input id={`${idPrefix}-phone`} name="phone" type="tel" placeholder="021 123 4567" />
</label>
</div>
<article class="vf-owner-note">
<span class="vf-note-title">What happens next</span>
<p>
Well reply personally, talk through fit, and arrange the free Meet &amp; Greet if it
feels like the right next step.
</p>
</article>
</section>
{/if}
<div class="vf-footer">
<p class="vf-reassurance">
Free Meet &amp; Greet. No pressure. Just the right starting point for your dog.
</p>
<div class="vf-actions">
{#if step === 2}
<button type="button" class="vf-secondary" on:click={() => goToStep(1)}>Back</button>
{/if}
<button type="submit" class="vf-submit">
{step === 1 ? 'Next: owner details' : submitLabel}
</button>
</div>
</div>
{#if submitted}
<p class="vf-success" role="status">
Demo only: this variant would hand the enquiry to Aless and prompt a free Meet &amp; Greet.
</p>
{/if}
</form>
<style>
.vf-form {
display: grid;
gap: 24px;
}
.vf-form--carded {
gap: 18px;
}
.vf-header {
display: grid;
gap: 12px;
}
.vf-eyebrow {
display: inline-flex;
align-items: center;
width: fit-content;
min-height: 30px;
padding: 0 12px;
border-radius: 999px;
background: rgba(33, 48, 33, 0.07);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.05);
color: var(--gw-green);
font-family: var(--font-head);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.vf-title {
margin: 0;
color: #11171b;
font-family: var(--font-head);
font-size: clamp(26px, 3vw, 34px);
line-height: 1.06;
letter-spacing: -0.04em;
}
.vf-subtitle {
margin: 0;
color: #59606d;
font-size: 15px;
line-height: 1.6;
}
.vf-stepper {
display: flex;
align-items: center;
gap: 10px;
margin-top: 4px;
}
.vf-step {
display: inline-flex;
align-items: center;
gap: 10px;
min-height: 44px;
padding: 0 14px 0 10px;
border: none;
border-radius: 999px;
background: rgba(255, 255, 255, 0.84);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.06);
color: #50555c;
cursor: pointer;
transition:
background 0.18s ease,
color 0.18s ease,
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1);
}
.vf-step.active {
background: rgba(33, 48, 33, 0.08);
color: #11171b;
}
.vf-step:disabled {
cursor: default;
opacity: 0.55;
}
.vf-step-number {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 10px;
background: var(--gw-green);
color: var(--yellow);
font-family: var(--font-head);
font-size: 12px;
font-weight: 700;
}
.vf-step-label {
font-family: var(--font-head);
font-size: 12px;
font-weight: 700;
line-height: 1.2;
}
.vf-step-divider {
width: 26px;
height: 1px;
background: rgba(17, 20, 24, 0.1);
}
.vf-lead {
margin: 0;
color: #4c5056;
font-size: 15px;
line-height: 1.65;
}
.vf-group {
display: grid;
gap: 18px;
}
.vf-form--carded .vf-group {
padding: 22px;
border-radius: 24px;
background: linear-gradient(180deg, rgba(251, 251, 251, 0.98) 0%, rgba(247, 248, 246, 1) 100%);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.06),
0 12px 28px rgba(17, 20, 24, 0.06);
}
.vf-group-head {
display: grid;
gap: 6px;
}
.vf-kicker {
margin: 0;
color: var(--gw-green);
font-family: var(--font-head);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.vf-group-copy {
margin: 0;
color: #59606d;
font-size: 14px;
line-height: 1.55;
}
.vf-grid {
display: grid;
gap: 14px;
}
.vf-grid--two {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.vf-grid--three {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.vf-field {
display: grid;
gap: 8px;
}
.vf-field--full {
width: 100%;
}
.vf-label,
.vf-choice-group legend {
color: #16181d;
font-size: 14px;
font-weight: 700;
line-height: 1.35;
}
.vf-field input,
.vf-field textarea {
width: 100%;
border: none;
border-radius: 18px;
background: rgba(255, 255, 255, 0.96);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.08),
0 1px 0 rgba(255, 255, 255, 0.3);
color: #1f2421;
padding: 15px 16px;
font-size: 15px;
line-height: 1.45;
outline: none;
transition:
box-shadow 0.18s ease,
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1),
background 0.18s ease;
}
.vf-field textarea {
resize: vertical;
min-height: 132px;
}
.vf-field input:focus-visible,
.vf-field textarea:focus-visible,
.vf-choice input:focus-visible + span,
.vf-step:focus-visible,
.vf-secondary:focus-visible {
box-shadow:
inset 0 0 0 2px rgba(255, 209, 0, 0.7),
0 0 0 4px rgba(255, 209, 0, 0.14);
background: #fff;
outline: none;
}
.vf-choice-group {
display: grid;
gap: 10px;
min-width: 0;
margin: 0;
padding: 0;
border: none;
}
.vf-helper {
margin: 0;
color: #687076;
font-size: 13px;
line-height: 1.5;
}
.vf-choices {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.vf-choice {
position: relative;
}
.vf-choice input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.vf-choice span {
display: inline-flex;
align-items: center;
min-height: 42px;
padding: 0 14px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.9);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.08);
color: #2f3134;
font-size: 13px;
font-weight: 600;
line-height: 1.2;
transition:
box-shadow 0.18s ease,
background 0.18s ease,
color 0.18s ease,
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1);
}
.vf-choice input:checked + span {
background: rgba(255, 209, 0, 0.18);
box-shadow: inset 0 0 0 1px rgba(255, 209, 0, 0.34);
color: #16181d;
}
.vf-note-row {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.vf-note,
.vf-owner-note {
padding: 16px 16px 15px;
border-radius: 20px;
}
.vf-note {
background: rgba(33, 48, 33, 0.05);
box-shadow: inset 0 0 0 1px rgba(33, 48, 33, 0.06);
}
.vf-owner-note {
background: rgba(255, 209, 0, 0.12);
box-shadow: inset 0 0 0 1px rgba(255, 209, 0, 0.16);
}
.vf-note-title {
display: block;
margin-bottom: 6px;
color: var(--gw-green);
font-family: var(--font-head);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.vf-note p,
.vf-owner-note p {
margin: 0;
color: #4c5056;
font-size: 13px;
line-height: 1.55;
}
.vf-footer {
display: grid;
gap: 14px;
}
.vf-actions {
display: flex;
align-items: center;
gap: 12px;
}
.vf-reassurance {
margin: 0;
color: #59606d;
font-size: 13px;
line-height: 1.5;
}
.vf-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 52px;
padding: 0 18px;
border: none;
border-radius: 999px;
background: rgba(33, 48, 33, 0.07);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.06);
color: var(--gw-green);
font-family: var(--font-head);
font-size: 14px;
font-weight: 700;
line-height: 1.2;
cursor: pointer;
}
.vf-submit {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 52px;
width: fit-content;
padding: 0 22px;
border: none;
border-radius: 999px;
background: linear-gradient(135deg, #ffd54a, var(--yellow-soft));
box-shadow:
inset 0 -2px 0 rgba(0, 0, 0, 0.08),
0 12px 24px rgba(17, 20, 24, 0.1);
color: #111;
font-family: var(--font-head);
font-size: 14px;
font-weight: 700;
line-height: 1.2;
cursor: pointer;
transition:
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1),
box-shadow 0.18s ease,
filter 0.18s ease;
}
.vf-submit:focus-visible {
outline: 3px solid rgba(255, 209, 0, 0.28);
outline-offset: 3px;
}
.vf-success {
margin: 0;
padding: 14px 16px;
border-radius: 18px;
background: rgba(33, 48, 33, 0.07);
box-shadow: inset 0 0 0 1px rgba(33, 48, 33, 0.08);
color: var(--gw-green);
font-size: 14px;
line-height: 1.55;
}
.vf-form--conversational .vf-label,
.vf-form--conversational .vf-choice-group legend {
font-family: var(--font-head);
font-size: 16px;
letter-spacing: -0.02em;
}
.vf-form--conversational .vf-field input,
.vf-form--conversational .vf-field textarea {
border-radius: 22px;
padding: 17px 18px;
font-size: 16px;
}
.vf-form--minimal .vf-group-copy,
.vf-form--minimal .vf-helper,
.vf-form--minimal .vf-reassurance,
.vf-form--minimal .vf-subtitle {
color: #6b7077;
}
.vf-form--minimal .vf-field input,
.vf-form--minimal .vf-field textarea,
.vf-form--minimal .vf-choice span {
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.06);
}
@media (hover: hover) {
.vf-step:hover,
.vf-secondary:hover,
.vf-choice:hover span,
.vf-submit:hover {
transform: translateY(-1px);
}
.vf-submit:hover {
box-shadow:
inset 0 -2px 0 rgba(0, 0, 0, 0.08),
0 16px 28px rgba(17, 20, 24, 0.12);
filter: brightness(1.02);
}
}
@media (max-width: 768px) {
.vf-grid--two,
.vf-grid--three,
.vf-note-row {
grid-template-columns: 1fr;
}
.vf-stepper {
align-items: stretch;
gap: 8px;
}
.vf-step {
flex: 1 1 0;
min-width: 0;
}
.vf-step-divider {
display: none;
}
.vf-form {
gap: 20px;
}
.vf-form--carded .vf-group {
padding: 18px;
border-radius: 22px;
}
.vf-field input,
.vf-field textarea {
font-size: 16px;
}
.vf-actions {
flex-direction: column-reverse;
align-items: stretch;
}
.vf-secondary,
.vf-submit {
width: 100%;
}
}
</style>