4.2.1 polish
This commit is contained in:
@@ -86,7 +86,7 @@
|
||||
class="footer-reviews"
|
||||
>
|
||||
<Icon name="fab fa-google" />
|
||||
<span>See our 5★ Google reviews</span>
|
||||
<span>30+ five-star Google reviews</span>
|
||||
</a>
|
||||
|
||||
{#if footer.email || footer.phone}
|
||||
|
||||
@@ -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 & 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>
|
||||
@@ -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 & 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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 Auckland’s 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 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!',
|
||||
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',
|
||||
|
||||
@@ -58,8 +58,8 @@ nav {
|
||||
}
|
||||
|
||||
.nav-links > li > a.nav-link-active {
|
||||
background: #eadbbf;
|
||||
color: #000;
|
||||
background: #fff;
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
.mega-chevron {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -124,6 +124,7 @@ export interface ServicePageContent {
|
||||
intro?: string;
|
||||
plans: ServicePricingPlan[];
|
||||
extras?: ServiceExtra[];
|
||||
scarcityNote?: string;
|
||||
};
|
||||
benefits: {
|
||||
title: string;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user