4.2.1 polish

This commit is contained in:
2026-05-06 08:27:24 +12:00
parent 55217f59bd
commit b8b9d12a82
16 changed files with 700 additions and 25 deletions
+236
View File
@@ -0,0 +1,236 @@
# Mobile Polish — Conversion & Comfort Audit Tracker
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.
> Important context for prioritisation: a dog-walking business is a
> jobs-to-be-done service that users research on the couch. Mobile
> conversion lower-bound is "they Meet & Greet". So the dial-movers
> are: thumb-reach for the booking CTA, legibility, friction-free
> form, and trust signals visible on the first scroll.
---
## High — direct conversion impact
- [x] **Hero buttons stack awkwardly at ~375px**
- File: `src/lib/styles/responsive.css` (new ≤480px block)
- Implementation: At `@media (max-width: 480px)` the hero buttons
flip to `flex-direction: column; gap: 12px;` and each button
becomes full-width with padding `16px 24px`. The 768px-specific
`padding-right: 18px` on the buttons row and the
`margin-right: 12px` on `:last-child` are both reset so the two
CTAs read as a clean stacked stack.
- Why: At 375px the side-by-side primary + outline CTAs were wrapping
unevenly and the primary's prominence collapsed.
- [x] **Mobile header phone button is low-contrast and small**
- File: `src/lib/styles/responsive.css:88-100`
- Implementation: Background bumped from `rgba(33, 48, 33, 0.06)`
`0.10`; padding 9px 12px → 11px 14px (and at ≤480px, 10px 12px);
`font-weight: 600`; `min-height: 44px` to meet the touch-target
minimum; icon size also bumped from 13px → 14px.
- Why: The tap-to-call on the mobile header is one of the highest
intent actions on the site. It now reads as a button rather than a
barely-visible label.
- [x] **Booking form inputs trigger iOS zoom-on-focus**
- File: `src/lib/styles/responsive.css:453-462`
- Implementation: Input `font-size: 15px``16px` at ≤768px. Comment
added explaining the iOS-Safari 16px threshold so a future edit
doesn't accidentally drop it again.
- Why: Below 16px Safari auto-zooms on focus; the page jolts every
time a field is tapped. Critical at the booking conversion step.
- [ ] **Testimonial carousel arrows hard to reach at 375px**
- File: `src/lib/components/TestimonialsSection.svelte` (mobile rules
lines ~640-660)
- Current: arrows pinned to `bottom: 24px` inside a stage with
`padding-bottom: 116px`. Visually at the bottom of a tall card —
requires deliberate stretching.
- Why: The carousel feels passive. Users don't realise they can advance
it; testimonials sell — losing that engagement matters.
- Fix: Lift arrows on small screens:
```css
@media (max-width: 480px) {
.testimonial-arrow { bottom: 80px; }
}
```
- [x] **No persistent "Book Meet & Greet" CTA on mobile after hero scrolls past**
- Files: `src/lib/components/MobileBookBar.svelte` (new),
`src/routes/+layout.svelte` (mount), `src/lib/styles/responsive.css`
(`body { padding-bottom: 64px }` at ≤768px).
- Implementation v2 — *Airbnb-style soft-container, scroll-triggered*:
- **Soft container**: a translucent white tray (`rgba(255, 255,
255, 0.96)` with backdrop-filter blur) sits flush at the bottom
with a thin top hairline and a soft shadow. The brand-yellow CTA
pill lives *inside* the tray, not as the tray itself — so the
action stays unmistakable but the surrounding chrome is calm.
- **Scroll-triggered**: the bar slides up + fades in only after the
user has scrolled ~480px (≈ one mobile viewport, hero out of
view). Slides back out below ~120px to avoid flicker near the
top. `prefers-reduced-motion` respected — the slide is dropped,
only the opacity fade remains.
- **Hidden on `/contact-us` and `/booking`** (already in the form).
- **Resets on navigation**: `afterNavigate` resets `visible = false`
so each new page starts hidden until the user has earned it again.
- **Accessibility**: `aria-hidden` toggles with visibility; CTA
`tabindex` flips to `-1` while hidden so keyboard users don't
stumble onto an off-screen control; `pointer-events: none` while
hidden so the user can't accidentally tap it during the fade.
- **Safe-area aware**: `env(safe-area-inset-bottom)` so iPhones
with the home-bar gesture area get correct spacing.
- Body bottom-padding of 64px on mobile keeps the footer from sitting
behind the bar when it's visible at the bottom of long pages.
- Why: Sticky mobile CTAs are a validated conversion pattern (Airbnb,
Booking.com, etc.), but the v1 full-yellow bar was tonally wrong
for Goodwalk — it competed with the calm brand voice and dominated
the screen permanently. v2 keeps the conversion benefit (one-tap
booking, always one swipe away after engagement) without yelling.
## Medium — legibility & polish
- [x] **Hero title can wrap awkwardly at 375px**
- File: `src/lib/styles/responsive.css` (≤480px block)
- Implementation: At ≤480px the desktop H1 drops to 32px /
line-height 1.12 and the dedicated `.hero-heading-mobile` element
drops to 30px / 1.12 (it was 33.5px). The two-line "Unleashing Fun
in / Your Dog's Day!" now sits comfortably with breathing room
above the subtitle.
- Why: Previous 38px / 33.5px sizings were a hair too big for 375px;
line-2 felt cramped against the subtitle.
- [x] **Body text 15px feels small on mobile (and on ultra-wide desktop)**
- File: `src/lib/styles/responsive.css` (≤768px body rule)
- Implementation: `body { font-size: 16px; }` at ≤768px, with a
comment noting the iOS-Safari 16px zoom threshold so this rule
isn't accidentally undone.
- Desktop (≥769px) still inherits 15px from `base.css` for now — the
ultra-wide bump is tracked separately at the bottom of this file
so it can be reasoned about independently (clamp vs. breakpoint).
- Why: 16px is the modern legibility standard on mobile, dovetails
with the iOS zoom-on-focus rule for inputs, and reduces read
fatigue on long pages (about, FAQ answers, legal).
- [ ] **FAQ summary tap target too small**
- File: `src/lib/styles/sections.css` (`.faq summary` rules)
- Current: just text height (~22-26px) — below the 44px minimum.
- Fix:
```css
.faq summary {
padding: 12px 0;
min-height: 44px;
display: flex; align-items: center;
}
```
- [ ] **Booking service-option chips wrap awkwardly at 375px**
- File: `src/lib/styles/responsive.css:468-470`
- Fix at ≤480px: 2-up grid with tighter gap:
```css
.booking-service-options { gap: 10px 12px; }
.booking-toggle-option { flex: 1 1 calc(50% - 6px); }
```
- [ ] **Footer social icons spaced too tight for thumbs**
- File: `src/lib/styles/sections.css` (`.social-links { gap: 14px }`)
- Current 14px gap with 40px icons → centres are 54px apart. Apple
HIG wants 8px+ between targets after the 44px minimum.
- Fix:
```css
@media (max-width: 768px) { .social-links { gap: 18px; } }
```
- [ ] **Pricing cards stack to 1-col with multiple "Book" buttons in a row**
- File: `src/lib/components/PricingPage.svelte` (and ServiceLandingPage
plan grids)
- Why: After stacking, the user sees Plan → Book → Plan → Book → Plan
→ Book in vertical sequence. The repetition reads as noise rather
than choice; the "popular" anchor disappears.
- Fix (opinionated): on mobile, only the popular plan keeps its CTA
button. Other plans show a smaller "Choose this plan" link instead,
or no per-card CTA at all (a single CTA appears under the grid):
```css
@media (max-width: 768px) {
.pricing-plan-card:not(.pricing-plan-popular) .pricing-plan-cta {
display: none;
}
}
```
Then keep the existing under-grid `.service-plan-reassurance` pill
and add a single "Book a Meet & Greet" button below it.
- [ ] **Mobile nav header is too tall — eats above-the-fold real estate**
- File: `src/lib/styles/responsive.css:74`
- Current: `nav { padding: 20px 24px; }` + 25px logo = ~65px header.
On iPhone 13 (844px), this leaves ~380px for hero before scroll —
less than what the Goodwalk dog-image needs to feel like a hero.
- Fix at ≤480px:
```css
nav { padding: 14px 20px; }
.logo img { height: 22px; }
```
## Low — incremental polish
- [ ] **Hero top padding generous at 375px**
- File: `src/lib/styles/responsive.css:179`
- Reduce hero `padding-top` from 50px to 32px at ≤480px.
- [ ] **Intro trust badge feels edge-to-edge at 375px**
- File: `src/lib/styles/responsive.css:296-301`
- Add `padding: 18px 16px; margin: 0 12px;` at ≤480px.
- [ ] **Testimonial quote mark too large at 375px**
- File: `src/lib/components/TestimonialsSection.svelte:~595`
- Current: 44px. Reduce to 36px at ≤480px.
- [ ] **Booking form labels could shrink slightly at 375px**
- File: `src/lib/styles/responsive.css:450`
- Optional: 16px → 15px at ≤480px to give field width back to the
input value (where it actually matters for legibility).
- [ ] **No scroll-to-top affordance on long pages (booking, pricing)**
- Currently absent. Low priority but helpful when users have scrolled
past the booking form and want to re-read service details. Could be
folded into the same sticky-book-bar work above (one bar, both jobs).
## Open from elsewhere
- [ ] **Ultra-wide desktop body font feels small** *(noted by user)*
- Currently `body { font-size: 15px }` ([base.css:15](src/lib/styles/base.css#L15)).
On ≥1800px screens with the `--max-w` already widening (per the
1800px breakpoint), 15px in long-form sections (about, FAQ answers,
legal) becomes uncomfortable.
- Suggested fix: bump to `clamp(15px, 0.95vw, 17px)` on `body`, OR
introduce a `@media (min-width: 1600px) { body { font-size: 17px; } }`.
Either keeps the desktop ≤1599px experience identical and only
expands type when there's genuinely more reading width.
## Deliberately not actioning
- **Drop reveal animations on mobile to "save bandwidth".** They're
IntersectionObserver-driven, cost nothing perceivable, and add brand
polish. Removing them would make the mobile site feel cheaper for no
measurable performance gain.
- **Replace the testimonial carousel with a stacked list on mobile.**
Tempting (carousels famously hide content), but the carousel is
central to the brand's "see real dogs" pitch. Better to fix the arrow
reachability and let the autoplay do the work.
---
## Suggested order of attack
If you want one batch that moves the dial: **High items 1, 2, 3, 5**
together is roughly an hour of work — they hit the hero, the header
tap-to-call, the booking form's biggest mobile bug (zoom-on-focus), and
add the sticky CTA. That's the package I'd ship first. Item 4 (carousel
arrows) is a one-line fix once you're already in `responsive.css`.
The Medium list is best as a second pass — body-text bump,
header-padding reduction, FAQ tap-target, and pricing-card-CTA dedupe
all compound into a noticeably more "intentional on mobile" feel
without any structural change.
+143
View File
@@ -0,0 +1,143 @@
# UX Polish — Conversion Audit Tracker
Findings from the senior-marketing-lens audit, with completion status. Each
item has a one-line rationale and the file/line where the change lives (or
will live).
> Only commit to "We'll reply within 24 hours" if Aless can actually hold
> to it. If response time is more like 1-2 business days, soften to
> "within one business day".
---
## High — direct conversion impact
- [x] **Hero primary CTA: "Learn more" → "Explore our services →"**
- File: `src/lib/content/homepage.ts:38`
- Why: "Learn more" is the lowest-intent CTA that exists.
- [x] **Promise CTA: "See our services" → "Book a free Meet & Greet"**
- File: `src/lib/content/homepage.ts:59`
- Also: target changed from `#services` to `/contact-us` so the CTA goes
to the booking page instead of bouncing back up to a service list.
- Why: After the value prop + happy-dog photo, sending visitors to the
services list is a step backwards. Push them to book.
- [x] **Booking subtitle now states response time**
- File: `src/lib/content/homepage.ts:159-162`
- Old: *"...so we can reach out to arrange your free, no-obligation Meet & Greet."*
- New: *"...We'll reply within 24 hours to arrange your free, no-obligation Meet & Greet."*
- General-enquiry variant updated to match.
- Why: Open-ended "we'll reach out" creates anxiety at submit time.
- [x] 1 **Pricing page — Google rating trust signal above plan grid**
- File: `src/lib/components/PricingPage.svelte`
- Implementation: Pill-styled trust badge inside the green hero,
directly under the subtitle — five yellow stars + "30+ five-star
Google reviews" label + arrow, links out to Google. Styled to read
against the green hero (semi-transparent white pill) rather than
reusing the cream IntroStrip, which would have clashed.
- Why: Visitors land on pricing mid-decision; trust signal now appears
before the plan grid.
- [x] 2 **Service plan CTAs — add free / no-obligation reassurance**
- File: `src/lib/components/ServiceLandingPage.svelte`
- Implementation: A subtle green pill *"Every booking starts with a
free, no-obligation Meet & Greet."* (yellow shield-heart icon) sits
centred directly under the plan grid on every service page, above the
Extras block. Reuses the brand-tinted-pill aesthetic so it feels
native, not tacked on.
- Why: The "Book a Meet & Greet" buttons under each plan didn't carry
risk-reversal phrasing in their immediate context. Now they do.
## Medium — trust + polish
- [x] 3 **Quantify the Google rating wherever it appears**
- Files: `src/lib/content/homepage.ts:46`,
`src/lib/components/Footer.svelte:89`,
`src/lib/components/TestimonialsSection.svelte:200`,
`src/lib/components/PricingPage.svelte` (new pricing-trust pill).
- Implementation: "All 5 star reviews on Google!" → "30+ five-star
Google reviews" everywhere. Aless confirmed 30+ as the count.
- Why: A specific number is dramatically more credible than "all".
- [x] 4 **Lean into the "limited spots" angle**
- File: `src/lib/content/pack-walks.ts` (added `scarcityNote` to the
`pricing` block); `src/lib/types.ts` (added optional `scarcityNote?:
string` to ServicePageContent.pricing); rendered in
`src/lib/components/ServiceLandingPage.svelte` directly under the
plan grid as a yellow-tinted pill with a clock icon.
- Copy: *"We keep packs small (4-8 dogs) — popular days fill up fast."*
- Only set on Pack Walks (the 4-8 number is specific to that service);
the field is optional so 1:1 Walks and Puppy Visits get nothing.
- Why: Real, honest scarcity. The 4-8 cap is already a fact; saying it
out loud nudges decision-making.
- [ ] **About page — quantify Aless's expertise**
- File: `src/lib/content/about.ts:29-30`
- Why: "years of experience" is the weakest possible claim. Replace with
concrete numbers Aless can stand behind: years operating, dogs in
rotation, first-aid certification.
- [x] 5 **Pack Walks pricing intro — lead with the differentiator**
- File: `src/lib/content/pack-walks.ts:23-24`
- Implementation: Old intro led with "Our pack walks are a permanent
booking of at least one walk day a week..." (commitment ask first).
New intro leads with the benefits: *"Small packs of 4-8 dogs, 2-hour
outings at Auckland's scenic dog parks and beaches, with free pick-up
and drop-off included. We reinforce recall, car manners, and leash
etiquette while your dog plays. Booked as a permanent weekly slot —
gift your dog the best life!"*
- Why: Buyers scan for benefits before commitments. Lead-with-policy
framing creates resistance; lead-with-benefit framing builds desire.
- [ ] **FAQs — reframe from policy to reassurance**
- File: `src/lib/content/homepage.ts:180-205`
- Why: Answers are correct but read like terms & conditions. Lead with
the *why* (the benefit/reassurance), then the *what*.
## Low — incremental polish
- [x] **Home services-card CTAs: "Learn more" → outcome-oriented**
- File: `src/lib/components/ServicesSection.svelte:29`
- Implementation: Visible label is now derived from the service title —
*"See Pack Walks pricing →"*, *"See 1:1 Walks pricing →"*, *"See
Puppy Visits pricing →"*. The previously-added screen-reader-only
"about <Service>" span was removed since the visible label now carries
that context for everyone, not just assistive tech users.
- Why: "Learn more" was the lowest-intent CTA on the page; the new
label states the destination and the next step.
- [x] **Testimonials intro blurb — sharper jobs-to-be-done framing**
- File: `src/lib/components/TestimonialsSection.svelte:10-11`
- Old: *"Happy owners, even happier dogs. Our Auckland dog walking
clients love what the Tiny Gang brings to their dog's routine — and
you can see why. Follow along on Instagram for daily adventures..."*
- New: *"Busy parents get peace of mind. Dogs come home tired and
happy. See why 30+ Auckland families trust the Tiny Gang — follow
along on Instagram for daily adventures, wagging tails and the odd
zoomie."*
- Why: Leads with the two outcomes buyers actually care about (peace of
mind for them, exercise for the dog), keeps the brand voice + 30+
review proof point, then makes the Instagram nudge feel like a
follow-on rather than the lead.
- [x] **Surface "Reliability / on-time" earlier**
- File: `src/lib/content/homepage.ts:37` (hero subtitle)
- Old: *"Trusted, professional dog walking across Auckland Central..."*
- New: *"Trusted, on-time dog walking across Auckland Central..."*
- Why: Reliability/punctuality is the #1 anxiety for busy parents
booking a service that visits their home. Pulling "on-time" into the
hero subtitle (one-word swap, no length cost) puts the reassurance
above the fold.
## Deliberately not actioning
- **Pack Walks H1 rewrite to "Small-dog pack walks designed for calm,
confident groups."** *"Join our Tiny Gang!"* is doing brand work — it's
memorable and reinforces a phrase used everywhere else. Rewriting kills
the most distinctive asset for marginal headline clarity.
- **Booking submit button: "Send" → "Book my Meet & Greet".** The form
also handles general enquiries, so a "book my…" label would feel wrong
on a complaint email. Better fix would be to switch the label by
`enquiryType` — keep "Send my booking" / "Send my enquiry" contextually.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "goodwalk-svelte-port",
"version": "4.2.0",
"version": "4.2.1",
"private": true,
"type": "module",
"scripts": {
+1 -1
View File
@@ -86,7 +86,7 @@
class="footer-reviews"
>
<Icon name="fab fa-google" />
<span>See our 5&#9733; Google reviews</span>
<span>30+ five-star Google reviews</span>
</a>
{#if footer.email || footer.phone}
+147
View File
@@ -0,0 +1,147 @@
<script lang="ts">
import { onMount } from 'svelte';
import { afterNavigate } from '$app/navigation';
import { page } from '$app/stores';
import Icon from '$lib/components/Icon.svelte';
/*
* Sticky bottom CTA shown on mobile only.
*
* Pattern is the Airbnb-style "soft container, scroll-triggered" —
* - white card sits flush against the bottom edge with a thin top
* hairline and a soft shadow so it reads as a tray, not a banner;
* - the brand-yellow pill CTA lives inside the card so the action
* is unmistakable but the surrounding chrome stays calm;
* - the bar only appears after the user has scrolled roughly one
* viewport (~hero out of view), so it doesn't compete with the
* in-page hero CTA.
*
* Hidden on the contact / booking flows (no point reminding someone
* to book while they're already on the form).
*/
$: pathname = $page.url.pathname;
$: hidden = pathname === '/contact-us' || pathname === '/booking';
const SHOW_AFTER_PX = 480; // close to one mobile viewport
const HIDE_BELOW_PX = 120; // generous so the bar doesn't flicker near the top
let visible = false;
function evaluateVisibility() {
const y = window.scrollY;
if (y > SHOW_AFTER_PX) {
visible = true;
} else if (y < HIDE_BELOW_PX) {
visible = false;
}
}
afterNavigate(() => {
visible = false;
});
onMount(() => {
evaluateVisibility();
window.addEventListener('scroll', evaluateVisibility, { passive: true });
window.addEventListener('resize', evaluateVisibility, { passive: true });
return () => {
window.removeEventListener('scroll', evaluateVisibility);
window.removeEventListener('resize', evaluateVisibility);
};
});
</script>
{#if !hidden}
<div
class="mobile-book-bar"
class:mobile-book-bar-visible={visible}
aria-hidden={!visible}
>
<a class="mobile-book-bar-cta" href="/contact-us" tabindex={visible ? 0 : -1}>
<Icon name="fas fa-paw" />
<span>Book a free Meet &amp; Greet</span>
<Icon name="fas fa-arrow-right" className="mobile-book-bar-arrow" />
</a>
</div>
{/if}
<style>
.mobile-book-bar {
display: none;
}
@media (max-width: 768px) {
.mobile-book-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 50;
display: flex;
justify-content: center;
padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
background: rgba(255, 255, 255, 0.96);
backdrop-filter: blur(10px);
border-top: 1px solid rgba(17, 20, 24, 0.08);
box-shadow: 0 -10px 28px rgba(17, 20, 24, 0.1);
opacity: 0;
transform: translateY(110%);
pointer-events: none;
transition:
opacity 0.22s ease,
transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.mobile-book-bar-visible {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
.mobile-book-bar-cta {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
width: 100%;
max-width: 460px;
padding: 13px 22px;
border-radius: 999px;
background: var(--yellow);
color: #000;
font-family: var(--font-head);
font-size: 15px;
font-weight: 700;
letter-spacing: 0.01em;
text-decoration: none;
box-shadow: 0 8px 18px rgba(255, 209, 0, 0.4);
transition:
background 0.18s ease,
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1);
}
.mobile-book-bar-cta:active {
transform: translateY(1px) scale(0.995);
background: #e6bb00;
}
:global(.mobile-book-bar-cta .icon) {
font-size: 13px;
}
:global(.mobile-book-bar-cta .mobile-book-bar-arrow) {
font-size: 12px;
opacity: 0.75;
}
@media (prefers-reduced-motion: reduce) {
.mobile-book-bar {
transition: opacity 0.22s ease;
transform: none;
}
}
}
</style>
+56
View File
@@ -96,6 +96,22 @@
{#if pageContent.subtitle}
<p class="pricing-page-sub">{pageContent.subtitle}</p>
{/if}
<a
class="pricing-trust"
href="https://g.page/r/CUsvrWPhkYrAEB0/"
target="_blank"
rel="noopener"
aria-label="Read our 5-star Google reviews"
>
<span class="pricing-trust-stars" aria-hidden="true">
{#each Array(5) as _}
<Icon name="fas fa-star" />
{/each}
</span>
<span class="pricing-trust-label">30+ five-star Google reviews</span>
<Icon name="fas fa-arrow-right" className="pricing-trust-arrow" />
</a>
</div>
</section>
@@ -204,6 +220,46 @@
color: rgba(255, 255, 255, 0.7);
}
.pricing-trust {
display: inline-flex;
align-items: center;
gap: 12px;
margin-top: 22px;
padding: 9px 18px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.18);
color: #fff;
font-size: 14px;
font-weight: 600;
text-decoration: none;
transition:
background 0.2s ease,
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1);
}
.pricing-trust:hover {
background: rgba(255, 255, 255, 0.18);
transform: translateY(-1px);
}
.pricing-trust-stars {
display: inline-flex;
align-items: center;
gap: 2px;
color: var(--yellow);
font-size: 13px;
}
.pricing-trust-label {
letter-spacing: 0.01em;
}
:global(.pricing-trust .pricing-trust-arrow) {
font-size: 12px;
opacity: 0.85;
}
.pricing-section-heading h2 {
margin: 0;
text-align: center;
@@ -113,6 +113,18 @@
{/each}
</div>
{#if pageContent.pricing.scarcityNote}
<p class="service-plan-scarcity">
<Icon name="fas fa-clock" />
{pageContent.pricing.scarcityNote}
</p>
{/if}
<p class="service-plan-reassurance">
<Icon name="fas fa-shield-heart" />
Every booking starts with a free, no-obligation Meet &amp; Greet.
</p>
{#if pageContent.pricing.extras?.length}
<div class="service-extras">
<div class="service-extras-heading">Extras</div>
@@ -554,6 +566,42 @@
font-family: var(--font-head);
}
.service-plan-reassurance,
.service-plan-scarcity {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
width: fit-content;
margin: 24px auto 0;
padding: 8px 16px;
border-radius: 999px;
background: rgba(33, 48, 33, 0.06);
color: var(--green);
font-size: 14px;
font-weight: 600;
}
.service-plan-scarcity {
background: rgba(255, 209, 0, 0.18);
color: #5a4500;
}
.service-plan-reassurance + .service-plan-reassurance,
.service-plan-scarcity + .service-plan-reassurance {
margin-top: 12px;
}
:global(.service-plan-reassurance .icon) {
color: var(--yellow);
font-size: 14px;
}
:global(.service-plan-scarcity .icon) {
color: #b88800;
font-size: 14px;
}
.service-extras {
margin-top: 30px;
border-radius: 28px;
+1 -1
View File
@@ -27,7 +27,7 @@
{#if service.href}
<a href={service.href} class="btn btn-green">
Learn more<span class="visually-hidden"> about {service.title}</span>
See {service.title} pricing →
</a>
{/if}
</div>
@@ -8,7 +8,7 @@
export let testimonials: TestimonialContent[];
export let heading = 'Why people choose us!';
export let blurb =
"Happy owners, even happier dogs. Our Auckland dog walking clients love what the Tiny Gang brings to their dog's routine — and you can see why. Follow along on Instagram for daily adventures, wagging tails and the odd zoomie";
'Busy parents get peace of mind. Dogs come home tired and happy. See why 30+ Auckland families trust the Tiny Gang — follow along on Instagram for daily adventures, wagging tails and the odd zoomie.';
export let instagramHref = 'https://www.instagram.com/goodwalk.nz/';
export let instagramLabel = 'goodwalk.nz';
@@ -197,7 +197,7 @@
rel="noopener"
>
<Icon name="fab fa-google" />
<span>All 5 star reviews on Google!</span>
<span>30+ five-star Google reviews</span>
</a>
</div>
</article>
+6 -6
View File
@@ -34,8 +34,8 @@ export const homepageContent: HomePageContent = {
title: 'Unleashing Fun in',
highlight: "Your Dog's Day!",
mobileTitle: "Unleashing Fun in\nYour Dog's Day!",
subtitle: 'Trusted, professional dog walking across Auckland Central — pack walks, 1:1 walks, and puppy visits.',
primaryCta: { label: 'Learn more', href: '#services', variant: 'yellow' },
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' },
imageUrl: '/images/auckland-dog-walking-happy-dog-hero.png',
imageAlt: 'Happy dog ready for a professional pack walk with Goodwalk Auckland dog walking service'
@@ -43,7 +43,7 @@ export const homepageContent: HomePageContent = {
intro: {
text: 'Trusted by Auckland dog parents.',
reviewCta: {
label: 'All 5 star reviews on Google!',
label: '30+ five-star Google reviews',
href: 'https://g.page/r/CUsvrWPhkYrAEB0/',
external: true
}
@@ -56,7 +56,7 @@ export const homepageContent: HomePageContent = {
'Professional dog walking across Auckland for small, medium and large breeds, with tailored pack walks for smaller dogs and one-on-one walks for larger breeds — giving every dog the personalised attention they deserve. Ready to join our'
],
emphasis: 'TINY GANG?',
cta: { label: 'See our services', href: '#services', variant: 'green' },
cta: { label: 'Book a free Meet & Greet', href: '/contact-us', variant: 'green' },
imageUrl: '/images/auckland-dog-walking-happy-dogs-happy-humans.webp',
imageAlt: 'Woman cuddling a dog for Goodwalk Auckland dog walking services'
},
@@ -157,9 +157,9 @@ export const homepageContent: HomePageContent = {
booking: {
title: "Let's meet!",
subtitle:
"Almost there — just your contact details so we can reach out to arrange your free, no-obligation Meet & Greet.",
"Almost there — just your contact details. We'll reply within 24 hours to arrange your free, no-obligation Meet & Greet.",
generalSubtitle:
"Almost there — just your contact details so we can reply properly to your message.",
"Almost there — just your contact details. We'll reply within 24 hours.",
formAction: '/contact-us',
serviceOptions: ['Pack Walks', '1:1 Walks', 'Puppy Visits', 'Other Services'],
ownerStepLabel: 'Your details',
+3 -2
View File
@@ -21,7 +21,7 @@ export const packWalksContent: ServicePageContent = {
pricing: {
title: 'Tiny Gang Prices',
intro:
'Our pack walks are a permanent booking of at least one walk day a week. Our Tiny Gang pack outing typically lasts 2 hours or more, including a one-hour walk at one of Aucklands scenic dog parks or beaches. Additionally, pick-up and drop-off services are provided for your convenience. We assist in reinforcing basic training, including recall, car manners, and leash etiquette. Gift your dog the best life!',
'Small packs of 4-8 dogs, 2-hour outings at Aucklands scenic dog parks and beaches, with free pick-up and drop-off included. We reinforce recall, car manners, and leash etiquette while your dog plays. Booked as a permanent weekly slot — gift your dog the best life!',
plans: [
{
title: '1 Walk',
@@ -53,7 +53,8 @@ export const packWalksContent: ServicePageContent = {
{ label: 'Extra Dog', note: 'From same household', price: '$35' },
{ label: 'Muddy Wash', price: '$35' },
{ label: '5 Hour Day Out', note: 'Not suitable for all dogs', price: '$90' }
]
],
scarcityNote: 'We keep packs small (4-8 dogs) — popular days fill up fast.'
},
benefits: {
title: 'Tiny Gang membership benefits',
+2 -2
View File
@@ -58,8 +58,8 @@ nav {
}
.nav-links > li > a.nav-link-active {
background: #eadbbf;
color: #000;
background: #fff;
color: var(--green);
}
.mega-chevron {
+48 -5
View File
@@ -35,6 +35,18 @@
}
@media (max-width: 768px) {
/*
* Mobile body type bumps to 16px modern legibility standard, and
* matches the iOS-Safari zoom-on-focus threshold. Reserve room at
* the bottom of the page for the sticky MobileBookBar so the footer
* never sits behind it; the bar adds its own safe-area-inset
* padding on top of this.
*/
body {
font-size: 16px;
padding-bottom: 64px;
}
@keyframes mobileMenuBounceIn {
0% {
opacity: 0;
@@ -89,14 +101,16 @@
display: inline-flex;
justify-self: center;
align-self: center;
padding: 9px 12px;
background: rgba(33, 48, 33, 0.06);
min-height: 44px;
padding: 11px 14px;
background: rgba(33, 48, 33, 0.1);
color: var(--green);
font-size: 13px;
font-weight: 600;
}
.mobile-phone .icon {
font-size: 13px;
font-size: 14px;
}
.nav-links {
@@ -453,7 +467,13 @@
.booking-field-card input,
.booking-field-card textarea {
padding: 14px 18px;
font-size: 15px;
/*
* 16px is the iOS-Safari threshold for triggering auto-zoom on
* focus. Anything smaller and the page jolts every time a field
* is tapped kills the form's perceived quality at the most
* critical conversion step.
*/
font-size: 16px;
border-width: 2px;
border-radius: 18px;
}
@@ -554,11 +574,34 @@
@media (max-width: 480px) {
.mobile-phone {
gap: 6px;
padding: 9px 10px;
padding: 10px 12px;
font-size: 12px;
}
.mobile-phone span {
letter-spacing: -0.01em;
}
.hero-text h1,
.hero-heading {
font-size: 32px;
line-height: 1.12;
}
.hero-text h1 .hero-heading-mobile {
font-size: 30px;
line-height: 1.12;
}
.hero-buttons {
flex-direction: column;
gap: 12px;
padding-right: 0;
}
.hero-buttons .btn {
width: 100%;
margin-right: 0 !important;
padding: 16px 24px;
}
}
+2 -5
View File
@@ -213,12 +213,8 @@ section {
background: #fff;
}
.promise-text h2 {
text-align: center;
}
.promise-text p {
margin-bottom: 28px;
margin: 0 auto 28px;
font-size: 16px;
max-width: 520px;
}
@@ -231,6 +227,7 @@ section {
.promise-text {
order: 2;
text-align: center;
}
.promise-img {
+1
View File
@@ -124,6 +124,7 @@ export interface ServicePageContent {
intro?: string;
plans: ServicePricingPlan[];
extras?: ServiceExtra[];
scarcityNote?: string;
};
benefits: {
title: string;
+3
View File
@@ -3,6 +3,7 @@
import { navigating, page } from '$app/stores';
import { afterNavigate, disableScrollHandling } from '$app/navigation';
import { initClickTracking, trackPageView } from '$lib/analytics';
import MobileBookBar from '$lib/components/MobileBookBar.svelte';
import RouteSkeleton from '$lib/components/RouteSkeleton.svelte';
import '$lib/styles/variables.css';
import '$lib/styles/base.css';
@@ -72,6 +73,8 @@
{/if}
</div>
<MobileBookBar />
<style>
.layout-shell {
position: relative;