2026-05-15 16:27:39 +12:00
< 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 & 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" />
2026-05-18 22:25:43 +12:00
< span > Solo Walks</ span >
2026-05-15 16:27:39 +12:00
</ 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 & 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 >
We’ ll reply personally, talk through fit, and arrange the free Meet & Greet if it
feels like the right next step.
</ p >
</ article >
</ section >
{ /if }
< div class = "vf-footer" >
< p class = "vf-reassurance" >
Free Meet & 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 & Greet.
</ p >
{ /if }
</ form >
< style >
. vf-form {
display : grid ;
gap : 24 px ;
}
. vf-form--carded {
gap : 18 px ;
}
. vf-header {
display : grid ;
gap : 12 px ;
}
. vf-eyebrow {
display : inline-flex ;
align-items : center ;
width : fit-content ;
min-height : 30 px ;
padding : 0 12 px ;
border-radius : 999 px ;
background : rgba ( 33 , 48 , 33 , 0.07 );
box-shadow : inset 0 0 0 1 px rgba ( 17 , 20 , 24 , 0.05 );
color : var ( -- gw - green );
font-family : var ( -- font - head );
font-size : 11 px ;
font-weight : 700 ;
letter-spacing : 0.08 em ;
text-transform : uppercase ;
}
. vf-title {
margin : 0 ;
color : #11171b ;
font-family : var ( -- font - head );
font-size : clamp ( 26 px , 3 vw , 34 px );
line-height : 1.06 ;
letter-spacing : -0.04 em ;
}
. vf-subtitle {
margin : 0 ;
color : #59606d ;
font-size : 15 px ;
line-height : 1.6 ;
}
. vf-stepper {
display : flex ;
align-items : center ;
gap : 10 px ;
margin-top : 4 px ;
}
. vf-step {
display : inline-flex ;
align-items : center ;
gap : 10 px ;
min-height : 44 px ;
padding : 0 14 px 0 10 px ;
border : none ;
border-radius : 999 px ;
background : rgba ( 255 , 255 , 255 , 0.84 );
box-shadow : inset 0 0 0 1 px rgba ( 17 , 20 , 24 , 0.06 );
color : #50555c ;
cursor : pointer ;
transition :
background 0.18 s ease ,
color 0.18 s ease ,
transform 0.18 s 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 : 28 px ;
height : 28 px ;
border-radius : 10 px ;
background : var ( -- gw - green );
color : var ( -- yellow );
font-family : var ( -- font - head );
font-size : 12 px ;
font-weight : 700 ;
}
. vf-step-label {
font-family : var ( -- font - head );
font-size : 12 px ;
font-weight : 700 ;
line-height : 1.2 ;
}
. vf-step-divider {
width : 26 px ;
height : 1 px ;
background : rgba ( 17 , 20 , 24 , 0.1 );
}
. vf-lead {
margin : 0 ;
color : #4c5056 ;
font-size : 15 px ;
line-height : 1.65 ;
}
. vf-group {
display : grid ;
gap : 18 px ;
}
. vf-form--carded . vf-group {
padding : 22 px ;
border-radius : 24 px ;
background : linear-gradient ( 180 deg , rgba ( 251 , 251 , 251 , 0.98 ) 0 % , rgba ( 247 , 248 , 246 , 1 ) 100 % );
box-shadow :
inset 0 0 0 1 px rgba ( 17 , 20 , 24 , 0.06 ),
0 12 px 28 px rgba ( 17 , 20 , 24 , 0.06 );
}
. vf-group-head {
display : grid ;
gap : 6 px ;
}
. vf-kicker {
margin : 0 ;
color : var ( -- gw - green );
font-family : var ( -- font - head );
font-size : 11 px ;
font-weight : 700 ;
letter-spacing : 0.08 em ;
text-transform : uppercase ;
}
. vf-group-copy {
margin : 0 ;
color : #59606d ;
font-size : 14 px ;
line-height : 1.55 ;
}
. vf-grid {
display : grid ;
gap : 14 px ;
}
. vf-grid--two {
grid-template-columns : repeat ( 2 , minmax ( 0 , 1 fr ));
}
. vf-grid--three {
grid-template-columns : repeat ( 3 , minmax ( 0 , 1 fr ));
}
. vf-field {
display : grid ;
gap : 8 px ;
}
. vf-field--full {
width : 100 % ;
}
. vf-label ,
. vf-choice-group legend {
color : #16181d ;
font-size : 14 px ;
font-weight : 700 ;
line-height : 1.35 ;
}
. vf-field input ,
. vf-field textarea {
width : 100 % ;
border : none ;
border-radius : 18 px ;
background : rgba ( 255 , 255 , 255 , 0.96 );
box-shadow :
inset 0 0 0 1 px rgba ( 17 , 20 , 24 , 0.08 ),
0 1 px 0 rgba ( 255 , 255 , 255 , 0.3 );
color : #1f2421 ;
padding : 15 px 16 px ;
font-size : 15 px ;
line-height : 1.45 ;
outline : none ;
transition :
box-shadow 0.18 s ease ,
transform 0.18 s cubic-bezier ( 0.22 , 1 , 0.36 , 1 ),
background 0.18 s ease ;
}
. vf-field textarea {
resize : vertical ;
min-height : 132 px ;
}
. 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 2 px rgba ( 255 , 209 , 0 , 0.7 ),
0 0 0 4 px rgba ( 255 , 209 , 0 , 0.14 );
background : #fff ;
outline : none ;
}
. vf-choice-group {
display : grid ;
gap : 10 px ;
min-width : 0 ;
margin : 0 ;
padding : 0 ;
border : none ;
}
. vf-helper {
margin : 0 ;
color : #687076 ;
font-size : 13 px ;
line-height : 1.5 ;
}
. vf-choices {
display : flex ;
flex-wrap : wrap ;
gap : 10 px ;
}
. 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 : 42 px ;
padding : 0 14 px ;
border-radius : 999 px ;
background : rgba ( 255 , 255 , 255 , 0.9 );
box-shadow : inset 0 0 0 1 px rgba ( 17 , 20 , 24 , 0.08 );
color : #2f3134 ;
font-size : 13 px ;
font-weight : 600 ;
line-height : 1.2 ;
transition :
box-shadow 0.18 s ease ,
background 0.18 s ease ,
color 0.18 s ease ,
transform 0.18 s 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 1 px rgba ( 255 , 209 , 0 , 0.34 );
color : #16181d ;
}
. vf-note-row {
display : grid ;
grid-template-columns : repeat ( 2 , minmax ( 0 , 1 fr ));
gap : 12 px ;
}
. vf-note ,
. vf-owner-note {
padding : 16 px 16 px 15 px ;
border-radius : 20 px ;
}
. vf-note {
background : rgba ( 33 , 48 , 33 , 0.05 );
box-shadow : inset 0 0 0 1 px rgba ( 33 , 48 , 33 , 0.06 );
}
. vf-owner-note {
background : rgba ( 255 , 209 , 0 , 0.12 );
box-shadow : inset 0 0 0 1 px rgba ( 255 , 209 , 0 , 0.16 );
}
. vf-note-title {
display : block ;
margin-bottom : 6 px ;
color : var ( -- gw - green );
font-family : var ( -- font - head );
font-size : 11 px ;
font-weight : 700 ;
letter-spacing : 0.08 em ;
text-transform : uppercase ;
}
. vf-note p ,
. vf-owner-note p {
margin : 0 ;
color : #4c5056 ;
font-size : 13 px ;
line-height : 1.55 ;
}
. vf-footer {
display : grid ;
gap : 14 px ;
}
. vf-actions {
display : flex ;
align-items : center ;
gap : 12 px ;
}
. vf-reassurance {
margin : 0 ;
color : #59606d ;
font-size : 13 px ;
line-height : 1.5 ;
}
. vf-secondary {
display : inline-flex ;
align-items : center ;
justify-content : center ;
min-height : 52 px ;
padding : 0 18 px ;
border : none ;
border-radius : 999 px ;
background : rgba ( 33 , 48 , 33 , 0.07 );
box-shadow : inset 0 0 0 1 px rgba ( 17 , 20 , 24 , 0.06 );
color : var ( -- gw - green );
font-family : var ( -- font - head );
font-size : 14 px ;
font-weight : 700 ;
line-height : 1.2 ;
cursor : pointer ;
}
. vf-submit {
display : inline-flex ;
align-items : center ;
justify-content : center ;
min-height : 52 px ;
width : fit-content ;
padding : 0 22 px ;
border : none ;
border-radius : 999 px ;
background : linear-gradient ( 135 deg , #ffd54a , var ( -- yellow - soft ));
box-shadow :
inset 0 -2 px 0 rgba ( 0 , 0 , 0 , 0.08 ),
0 12 px 24 px rgba ( 17 , 20 , 24 , 0.1 );
color : #111 ;
font-family : var ( -- font - head );
font-size : 14 px ;
font-weight : 700 ;
line-height : 1.2 ;
cursor : pointer ;
transition :
transform 0.18 s cubic-bezier ( 0.22 , 1 , 0.36 , 1 ),
box-shadow 0.18 s ease ,
filter 0.18 s ease ;
}
. vf-submit : focus-visible {
outline : 3 px solid rgba ( 255 , 209 , 0 , 0.28 );
outline-offset : 3 px ;
}
. vf-success {
margin : 0 ;
padding : 14 px 16 px ;
border-radius : 18 px ;
background : rgba ( 33 , 48 , 33 , 0.07 );
box-shadow : inset 0 0 0 1 px rgba ( 33 , 48 , 33 , 0.08 );
color : var ( -- gw - green );
font-size : 14 px ;
line-height : 1.55 ;
}
. vf-form--conversational . vf-label ,
. vf-form--conversational . vf-choice-group legend {
font-family : var ( -- font - head );
font-size : 16 px ;
letter-spacing : -0.02 em ;
}
. vf-form--conversational . vf-field input ,
. vf-form--conversational . vf-field textarea {
border-radius : 22 px ;
padding : 17 px 18 px ;
font-size : 16 px ;
}
. 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 1 px rgba ( 17 , 20 , 24 , 0.06 );
}
@ media ( hover : hover ) {
. vf-step : hover ,
. vf-secondary : hover ,
. vf-choice : hover span ,
. vf-submit : hover {
transform : translateY ( -1 px );
}
. vf-submit : hover {
box-shadow :
inset 0 -2 px 0 rgba ( 0 , 0 , 0 , 0.08 ),
0 16 px 28 px 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 : 1 fr ;
}
. vf-stepper {
align-items : stretch ;
gap : 8 px ;
}
. vf-step {
flex : 1 1 0 ;
min-width : 0 ;
}
. vf-step-divider {
display : none ;
}
. vf-form {
gap : 20 px ;
}
. vf-form--carded . vf-group {
padding : 18 px ;
border-radius : 22 px ;
}
. vf-field input ,
. vf-field textarea {
font-size : 16 px ;
}
. vf-actions {
flex-direction : column-reverse ;
align-items : stretch ;
}
. vf-secondary ,
. vf-submit {
width : 100 % ;
}
}
</ style >