diff --git a/MOBILEPOLISH.md b/MOBILEPOLISH.md index 6cfc2c9..22103dc 100644 --- a/MOBILEPOLISH.md +++ b/MOBILEPOLISH.md @@ -1,5 +1,101 @@ # Mobile Polish — Conversion & Comfort Audit Tracker +## New Rescan Items — Mobile Conversion Opportunities + +Fresh opportunities from a second mobile-first pass over the main site. +These are intentionally only the new items, kept separate from the +existing audit below. + +### High — conversion strategy and flow + +- [ ] **Hero CTA hierarchy still prioritises browsing over booking** + - Files: `src/lib/content/homepage.ts:37-39`, `src/lib/components/HeroSection.svelte` + - Current: the yellow primary CTA is `Explore our services →`, while + `Book a Meet & Greet` is secondary. + - Why: on mobile, high-intent users should be able to choose the next + step immediately. Making the exploratory path more visually dominant + adds friction before the user reaches the lead form. + - Opportunity: test flipping the hierarchy on mobile so booking + becomes the primary CTA and service exploration becomes secondary. + +- [ ] **Homepage social proof appears too late in the scroll** + - File: `src/routes/+page.svelte:143-147` + - Current order: `Services -> Values -> Testimonials -> Booking`. + - Why: testimonials are one of the strongest conversion levers, but + on mobile they arrive after several large sections. Users are asked + to keep scrolling before seeing the strongest emotional proof. + - Opportunity: move testimonials above values on the homepage, or + surface one featured review snippet earlier in the page. + +- [ ] **Hero still relies on the next section for trust** + - Files: `src/lib/content/homepage.ts:43-49`, `src/lib/components/HeroSection.svelte`, + `src/lib/components/IntroStrip.svelte` + - Current: the hero presents the headline and CTAs, but the review + proof sits in the intro strip below. + - Why: on mobile, the hero needs to answer both "what is this?" and + "can I trust them?" before the user scrolls away. Separating those + two jobs weakens the first decision moment. + - Opportunity: add a compact review/trust chip directly under the + hero subtitle or near the hero CTAs on mobile. + +- [ ] **Booking flow asks for dog details before it captures the lead** + - File: `src/lib/components/BookingSection.svelte:298-441` + - Current: step 1 asks for dog name, location, message, and services; + contact details are only requested in step 2. + - Why: this is a higher-friction sequence on mobile. Users often feel + more comfortable giving owner details first, then expanding into pet + specifics once they have mentally committed. + - Opportunity: test reversing the step order so step 1 captures name, + email, and phone first, then dog details second. + +### Medium — mobile persuasion and CTA timing + +- [ ] **Sticky mobile CTA appears on a fixed pixel threshold rather than page context** + - File: `src/lib/components/MobileBookBar.svelte:26-37` + - Current: visibility is driven by `SHOW_AFTER_PX = 480` and + `HIDE_BELOW_PX = 120`. + - Why: a fixed threshold will feel early on some phones and late on + others. It also ignores whether the hero or booking section is + actually in view. + - Opportunity: switch to an `IntersectionObserver` tied to the hero or + booking section so the bar appears based on user context rather than + raw scroll position. + +- [ ] **Testimonials section pushes users off-site before finishing the proof story** + - File: `src/lib/components/TestimonialsSection.svelte:128-134` + - Current: the Instagram CTA appears near the top of the testimonials + section, before the user has fully consumed the review content. + - Why: on mobile, sending users to Instagram this early interrupts the + conversion journey and competes with the booking path. + - Opportunity: demote the Instagram CTA below the carousel, or replace + it with a tighter trust-oriented proof CTA higher up. + +- [ ] **Mobile pricing pages lose the consultative "not sure?" nudge** + - File: `src/lib/components/PricingPage.svelte:12-19` + - Current: the meet-and-greet reminder prompt is gated behind + `min-width: 769px`, so desktop gets a tailored nudge and mobile + does not. + - Why: mobile users are more likely to feel overwhelmed by stacked + pricing cards, not less. Removing the consultative reassurance on + the smallest screens is directionally backwards for conversion. + - Opportunity: add an inline mobile prompt after the first pricing + section that says, in effect, "Not sure which option fits? Book a + free Meet & Greet and we’ll help you choose." + +### Medium — stacked-page CTA noise + +- [ ] **Stacked pricing/service cards repeat the same CTA too many times** + - Files: `src/lib/components/PricingPage.svelte:141-159`, + `src/lib/components/ServiceLandingPage.svelte:94-112` + - Current: when cards collapse to one column on mobile, each card + keeps a full "Book a Meet & Greet" button. + - Why: the repetition turns persuasive choice architecture into visual + noise. Instead of helping the user decide, the page starts feeling + like a stack of repeated asks. + - Opportunity: treat this as a shared mobile pattern across pricing + and service pages. Keep one strong CTA per section, let the popular + card carry the primary action, and demote the rest. + Findings from a focused mobile-experience review (≤768px, with extra attention to 375px small-phones). Desktop is considered done. Each item records the where, why, and the concrete change. diff --git a/src/lib/components/BookingPage.svelte b/src/lib/components/BookingPage.svelte index e0f8459..f4a60ef 100644 --- a/src/lib/components/BookingPage.svelte +++ b/src/lib/components/BookingPage.svelte @@ -1,9 +1,11 @@ diff --git a/src/lib/components/PricingPage.svelte b/src/lib/components/PricingPage.svelte index f43daf4..7cdcba8 100644 --- a/src/lib/components/PricingPage.svelte +++ b/src/lib/components/PricingPage.svelte @@ -104,18 +104,25 @@ rel="noopener" aria-label="Read our 5-star Google reviews" > + - 30+ five-star Google reviews + 30+ 5-star Google reviews, trusted by Auckland dog owners - {#each pageContent.sections as section} + {#each pageContent.sections as section, index}
@@ -133,7 +140,8 @@ class={`btn pricing-section-link ${section.detailCta.variant === 'yellow' ? 'btn-yellow' : section.detailCta.variant === 'outline' ? 'btn-outline' : 'btn-green'}`} href={section.detailCta.href} > - {section.detailCta.label} + {section.detailCta.label} + {/if}
@@ -159,6 +167,25 @@ {/each}
+ + + Book a Meet & Greet + + + {#if index === 0} + + {/if}
{/each} @@ -238,6 +265,12 @@ transform 0.18s cubic-bezier(0.22, 1, 0.36, 1); } + .pricing-trust-logo { + width: 18px; + height: 19px; + flex: 0 0 auto; + } + .pricing-trust:hover { background: rgba(255, 255, 255, 0.18); transform: translateY(-1px); @@ -307,6 +340,9 @@ } .pricing-section-link { + display: inline-flex; + align-items: center; + gap: 10px; margin-top: 18px; } @@ -395,6 +431,11 @@ font-family: var(--font-head); } + .pricing-section-mobile-cta, + .pricing-mobile-consult { + display: none; + } + .meet-greet-prompt { position: fixed; right: 24px; @@ -559,6 +600,12 @@ font-size: 34px; } + .pricing-trust { + gap: 10px; + padding: 10px 14px; + font-size: 13px; + } + .pricing-section-heading h2 { font-size: 26px; } @@ -568,7 +615,7 @@ } .pricing-section-heading { - margin-bottom: 20px; + margin-bottom: 26px; } .pricing-section-blurb { @@ -576,6 +623,11 @@ line-height: 1.55; } + .pricing-section-link { + margin-top: 22px; + margin-bottom: 8px; + } + .pricing-plan-grid, .pricing-plan-grid-three { grid-template-columns: 1fr; @@ -594,6 +646,51 @@ font-size: 46px; } + .pricing-plan-cta { + display: none; + } + + .pricing-section-mobile-cta { + display: flex; + width: fit-content; + margin: 18px auto 0; + font-family: var(--font-head); + } + + .pricing-mobile-consult { + display: block; + margin-top: 18px; + padding: 22px 20px; + border-radius: 24px; + background: linear-gradient(180deg, #fffaf0 0%, #f9f4e7 100%); + box-shadow: 0 12px 28px rgba(17, 20, 24, 0.05); + text-align: left; + } + + .pricing-mobile-consult-kicker { + display: inline-flex; + align-items: center; + gap: 8px; + margin-bottom: 10px; + color: var(--green); + font-family: var(--font-head); + font-size: 12px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + } + + .pricing-mobile-consult p { + margin: 0; + color: #34363a; + font-size: 15px; + line-height: 1.55; + } + + .pricing-mobile-consult-cta { + margin-top: 16px; + } + .meet-greet-prompt { right: 16px; left: 16px; diff --git a/src/lib/components/ServiceLandingPage.svelte b/src/lib/components/ServiceLandingPage.svelte index 63efd20..aeb9fe6 100644 --- a/src/lib/components/ServiceLandingPage.svelte +++ b/src/lib/components/ServiceLandingPage.svelte @@ -125,6 +125,8 @@ Every booking starts with a free, no-obligation Meet & Greet.

+ Book a Meet & Greet + {#if pageContent.pricing.extras?.length}
Extras
@@ -566,6 +568,10 @@ font-family: var(--font-head); } + .service-plan-mobile-cta { + display: none; + } + .service-plan-reassurance, .service-plan-scarcity { display: flex; @@ -731,5 +737,16 @@ flex-direction: column; align-items: flex-start; } + + .service-plan-cta { + display: none; + } + + .service-plan-mobile-cta { + display: flex; + width: fit-content; + margin: 18px auto 0; + font-family: var(--font-head); + } } diff --git a/src/lib/components/ServicesSection.svelte b/src/lib/components/ServicesSection.svelte index fcceba4..cd658ce 100644 --- a/src/lib/components/ServicesSection.svelte +++ b/src/lib/components/ServicesSection.svelte @@ -27,7 +27,8 @@ {#if service.href} - See {service.title} pricing → + See {service.title} pricing + {/if}
diff --git a/src/lib/components/TestimonialsSection.svelte b/src/lib/components/TestimonialsSection.svelte index c970064..a9c1aac 100644 --- a/src/lib/components/TestimonialsSection.svelte +++ b/src/lib/components/TestimonialsSection.svelte @@ -127,10 +127,6 @@

{heading}

{blurb}

- - - {instagramLabel} -
{#if slides.length} @@ -190,13 +186,39 @@
+
+ + + +
+ - + 30+ five-star Google reviews @@ -214,6 +236,11 @@ {/if} + + + + {instagramLabel} + @@ -232,10 +259,11 @@ } .testimonials-instagram-link { - display: inline-flex; + display: flex; + width: fit-content; align-items: center; gap: 10px; - margin-top: 18px; + margin: 18px auto 0; padding: 10px 16px; border-radius: 999px; background: rgba(33, 48, 33, 0.06); @@ -283,7 +311,7 @@ } .testimonials-instagram-link { - margin-top: 14px; + margin: 14px auto 0; padding: 9px 14px; font-size: 15px; } @@ -443,14 +471,20 @@ box-shadow: 0 0 0 1px rgba(10, 48, 78, 0.06); } - .testimonial-google :global(.icon) { - font-size: 20px; + .testimonial-google-logo { + width: 18px; + height: 19px; + flex: 0 0 auto; } .testimonial-google:hover { background: #efe6d5; } + .testimonial-mobile-controls { + display: none; + } + .testimonial-woof { position: absolute; top: 40px; @@ -559,7 +593,7 @@ .testimonial-stage { min-height: unset; - padding-bottom: 116px; + padding-bottom: 0; } .testimonial-slide { @@ -605,8 +639,24 @@ margin-top: 28px; } + .testimonial-mobile-controls { + display: inline-flex; + align-items: center; + gap: 12px; + margin-top: 20px; + } + + .testimonial-arrow-inline { + position: static; + width: 48px; + height: 48px; + font-size: 18px; + transform: none; + box-shadow: 0 10px 22px rgba(20, 24, 20, 0.08); + } + .testimonial-google { - margin-top: 28px; + margin-top: 20px; font-size: 16px; gap: 10px; padding: 10px 14px; @@ -649,21 +699,9 @@ height: 8px; } - .testimonial-arrow { - top: auto; - bottom: 24px; - width: 54px; - height: 54px; - font-size: 20px; - transform: none; - } - - .testimonial-arrow-left { - left: 20px; - } - + .testimonial-arrow-left, .testimonial-arrow-right { - right: 20px; + display: none; } } diff --git a/src/lib/content/homepage.ts b/src/lib/content/homepage.ts index 8f4d7c4..886829c 100644 --- a/src/lib/content/homepage.ts +++ b/src/lib/content/homepage.ts @@ -35,8 +35,8 @@ export const homepageContent: HomePageContent = { highlight: "Your Dog's Day!", mobileTitle: "Unleashing Fun in\nYour Dog's Day!", subtitle: 'Trusted, on-time dog walking across Auckland Central — pack walks, 1:1 walks, and puppy visits.', - primaryCta: { label: 'Explore our services →', href: '#services', variant: 'yellow' }, - secondaryCta: { label: 'Book a Meet & Greet', href: '#newlead', variant: 'outline' }, + primaryCta: { label: 'Book a Meet & Greet', href: '#newlead', variant: 'yellow' }, + secondaryCta: { label: 'Explore our services →', href: '#services', variant: 'outline' }, imageUrl: '/images/auckland-dog-walking-happy-dog-hero.png', imageAlt: 'Happy dog ready for a professional pack walk with Goodwalk Auckland dog walking service' }, diff --git a/src/lib/server/content.test.ts b/src/lib/server/content.test.ts index 756b73e..2e362f3 100644 --- a/src/lib/server/content.test.ts +++ b/src/lib/server/content.test.ts @@ -76,6 +76,7 @@ describe('content server helpers', () => { services: homepageContent.services, testimonials: homepageContent.testimonials, booking: homepageContent.booking, + info: homepageContent.info, footer: homepageContent.footer }); }); @@ -89,6 +90,7 @@ describe('content server helpers', () => { expect(result.services).not.toBe(homepageContent.services); expect(result.testimonials).not.toBe(homepageContent.testimonials); expect(result.booking).not.toBe(homepageContent.booking); + expect(result.info).not.toBe(homepageContent.info); expect(result.footer).not.toBe(homepageContent.footer); }); }); diff --git a/src/lib/server/content.ts b/src/lib/server/content.ts index 1297362..65c16fe 100644 --- a/src/lib/server/content.ts +++ b/src/lib/server/content.ts @@ -55,6 +55,7 @@ export async function getSharedPageContent(): Promise { services: content.services, testimonials: content.testimonials, booking: content.booking, + info: content.info, footer: content.footer }; } diff --git a/src/lib/styles/forms.css b/src/lib/styles/forms.css index aa00466..23052a2 100644 --- a/src/lib/styles/forms.css +++ b/src/lib/styles/forms.css @@ -367,12 +367,21 @@ .booking-actions-next { justify-content: center; + flex-direction: column; } .booking-actions-final { justify-content: space-between; } +.booking-next-note { + margin: 10px 0 0; + text-align: center; + font-size: 13px; + line-height: 1.45; + color: #666; +} + .booking-next-button, .booking-submit-button { display: inline-flex; diff --git a/src/lib/styles/responsive.css b/src/lib/styles/responsive.css index 3f77ed4..285b424 100644 --- a/src/lib/styles/responsive.css +++ b/src/lib/styles/responsive.css @@ -195,7 +195,7 @@ .hero-inner { flex-direction: column; - gap: 24px; + gap: 18px; align-items: stretch; text-align: left; padding: 0; @@ -222,6 +222,12 @@ line-height: 1.5; } + .hero-trust-chip { + margin-bottom: 18px; + padding: 10px 14px; + font-size: 14px; + } + .hero-heading-desktop { display: none; } @@ -239,37 +245,38 @@ .hero-buttons { width: 100%; - justify-content: flex-start; - gap: 10px; - padding-right: 18px; + justify-content: space-between; + gap: 8px; + padding-right: 0; } .hero-buttons .btn { - flex: 0 0 auto; - width: auto; + flex: 1 1 0; + width: 0; min-width: 0; - padding: 17px 28px; - font-size: 15px; + padding: 15px 12px; + font-size: 13.5px; font-weight: 700; text-align: center; border-radius: 999px; + line-height: 1.25; -webkit-tap-highlight-color: transparent; touch-action: manipulation; } .hero-buttons .btn:last-child { - margin-right: 12px; + margin-right: 0; } .hero-buttons .btn-yellow { - background: #e8dbc1; + background: var(--yellow); color: #000; } .hero-buttons .btn-outline { - background: var(--yellow); - color: #000; - border: none; + background: transparent; + color: #fff; + border: 2px solid rgba(255, 255, 255, 0.84); } .hero-buttons .btn:active { @@ -277,11 +284,11 @@ } .hero-buttons .btn-yellow:active { - background: #dccdb1; + background: #e6bb00; } .hero-buttons .btn-outline:active { - background: #e6bb00; + background: rgba(255, 255, 255, 0.08); } .hero-img { @@ -295,7 +302,7 @@ } .hero-img img { - width: min(100%, 500px); + width: min(100%, 460px); max-width: 100%; margin: 0 auto -7px; object-fit: contain; diff --git a/src/lib/styles/sections.css b/src/lib/styles/sections.css index 8f5ecfd..6850aca 100644 --- a/src/lib/styles/sections.css +++ b/src/lib/styles/sections.css @@ -33,6 +33,49 @@ section { line-height: 1.55; } +.hero-trust-chip { + display: inline-flex; + align-items: center; + gap: 10px; + margin-bottom: 22px; + padding: 10px 16px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.14); + color: #fff; + font-size: 14px; + font-weight: 700; + text-decoration: none; + transition: + background 0.2s ease, + transform 0.16s cubic-bezier(0.22, 1, 0.36, 1); +} + +.hero-trust-logo { + width: 18px; + height: 19px; + flex: 0 0 auto; +} + +.hero-trust-stars { + display: inline-flex; + align-items: center; + gap: 2px; + color: var(--yellow); + font-size: 13px; +} + +@media (hover: hover) { + .hero-trust-chip:hover { + background: rgba(255, 255, 255, 0.16); + transform: translateY(-1px); + } +} + +.hero-trust-chip:active { + transform: translateY(1px) scale(0.99); +} + .hero-buttons { display: flex; gap: 16px; @@ -585,6 +628,12 @@ footer { transition: background 0.2s, opacity 0.2s; } +.footer-google-logo { + width: 16px; + height: 17px; + flex: 0 0 auto; +} + .footer-reviews:hover { background: rgba(255, 255, 255, 0.13); opacity: 1; diff --git a/src/lib/types.ts b/src/lib/types.ts index ffdadf8..91b5182 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -232,5 +232,6 @@ export interface SiteSharedContent { services: IconCard[]; testimonials: TestimonialContent[]; booking: BookingContent; + info: InfoContent; footer: FooterContent; } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 5e92f37..ff91b43 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -5,7 +5,6 @@ import HeroSection from '$lib/components/HeroSection.svelte'; import InfoSection from '$lib/components/InfoSection.svelte'; import InstagramSection from '$lib/components/InstagramSection.svelte'; - import IntroStrip from '$lib/components/IntroStrip.svelte'; import BookingSection from '$lib/components/BookingSection.svelte'; import PromiseSection from '$lib/components/PromiseSection.svelte'; import ServicesSection from '$lib/components/ServicesSection.svelte'; @@ -137,12 +136,11 @@ />
- - + - + diff --git a/src/routes/[slug]/+page.svelte b/src/routes/[slug]/+page.svelte index 816e83f..1db1d6c 100644 --- a/src/routes/[slug]/+page.svelte +++ b/src/routes/[slug]/+page.svelte @@ -218,7 +218,11 @@ {:else if data.slug === 'privacy-policy'} {:else if data.slug === 'contact-us'} - + {:else}
diff --git a/src/routes/[slug]/slug-page.test.ts b/src/routes/[slug]/slug-page.test.ts index 36779b4..725cd9b 100644 --- a/src/routes/[slug]/slug-page.test.ts +++ b/src/routes/[slug]/slug-page.test.ts @@ -60,4 +60,13 @@ describe('static slug route page', () => { expect(screen.queryByLabelText(/General enquiry/i)).not.toBeInTheDocument(); }); + + it('renders the shared FAQ section on the contact page', () => { + render(SlugPage, { + data: createStaticRouteData('contact-us') + }); + + expect(screen.getByText('FAQs')).toBeInTheDocument(); + expect(screen.getByText('Can any dog use your service?')).toBeInTheDocument(); + }); }); diff --git a/src/test/fixtures.ts b/src/test/fixtures.ts index 931e885..711eab2 100644 --- a/src/test/fixtures.ts +++ b/src/test/fixtures.ts @@ -6,6 +6,7 @@ export const sharedPageContent = { services: homepageContent.services, testimonials: homepageContent.testimonials, booking: homepageContent.booking, + info: homepageContent.info, footer: homepageContent.footer };