v4.0.6 - Simplicity focus
This commit is contained in:
@@ -0,0 +1,520 @@
|
||||
<script lang="ts">
|
||||
/*
|
||||
* Clarity homepage design (the live homepage as of the swap).
|
||||
*
|
||||
* Purpose-built green hero that says the specialism in the first second
|
||||
* (small & medium dogs), a founder philosophy strip, then offer-first order:
|
||||
* Services -> How it works -> Values -> the rest. A view component only: the
|
||||
* route that renders it owns data loading, SEO head, and structured data.
|
||||
*
|
||||
* Paired design: HomeClassic.svelte. To roll back, swap the component the
|
||||
* homepage route renders. See src/routes/+page.svelte.
|
||||
*/
|
||||
import Footer from '$lib/components/ui/Footer.svelte';
|
||||
import Header from '$lib/components/ui/Header.svelte';
|
||||
import Icon from '$lib/components/ui/Icon.svelte';
|
||||
import HowItWorksSection from '$lib/components/sections/HowItWorksSection.svelte';
|
||||
import InfoSection from '$lib/components/sections/InfoSection.svelte';
|
||||
import InstagramSection from '$lib/components/sections/InstagramSection.svelte';
|
||||
import BookingWizard from '$lib/components/sections/BookingWizard.svelte';
|
||||
import FounderStorySection from '$lib/components/sections/FounderStorySection.svelte';
|
||||
import ServicesSection from '$lib/components/sections/ServicesSection.svelte';
|
||||
import TestimonialsSection from '$lib/components/sections/TestimonialsSection.svelte';
|
||||
import ValuesSection from '$lib/components/sections/ValuesSection.svelte';
|
||||
import type { HomePageContent } from '$lib/types';
|
||||
import type { AbContext } from '$lib/ab';
|
||||
|
||||
export let content: HomePageContent;
|
||||
export let ab: AbContext | undefined = undefined;
|
||||
export let pagePath = '/';
|
||||
|
||||
const reviewCta = content.intro.reviewCta;
|
||||
const heroImage = '/images/smiling-happy-dog-maya.webp';
|
||||
const stars = Array.from({ length: 5 });
|
||||
</script>
|
||||
|
||||
<Header navigation={content.navigation} />
|
||||
|
||||
<section class="chero" data-track-location="hero" aria-label="Goodwalk dog walking">
|
||||
<div class="chero-inner">
|
||||
<div class="chero-copy">
|
||||
<p class="chero-eyebrow">Goodwalk · Auckland dog walking</p>
|
||||
<h1 class="chero-title">
|
||||
<span class="chero-title-lead">All we do is</span>
|
||||
<span class="chero-title-main">small & medium dogs</span>
|
||||
</h1>
|
||||
<p class="chero-lead">
|
||||
Real adventures with the Tiny Gang, in small, matched groups across
|
||||
Auckland. Same walker every time, so your dog comes home happy, tired,
|
||||
and ready to settle.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="chero-media">
|
||||
<div class="chero-photo">
|
||||
<img
|
||||
src={heroImage}
|
||||
alt="Maya, a happy small dog, after a Goodwalk walk in Auckland"
|
||||
width="1536"
|
||||
height="1024"
|
||||
loading="eager"
|
||||
fetchpriority="high"
|
||||
decoding="async"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chero-actions">
|
||||
<a
|
||||
href="#newlead"
|
||||
class="btn btn-yellow btn-with-arrow"
|
||||
data-track-event="cta_click"
|
||||
data-track-type="hero_primary"
|
||||
data-track-label="Book a free Meet & Greet"
|
||||
>
|
||||
Book a free Meet & Greet
|
||||
<Icon name="fas fa-arrow-right" />
|
||||
</a>
|
||||
|
||||
<a
|
||||
class="chero-reviews"
|
||||
href={reviewCta.href}
|
||||
target={reviewCta.external ? '_blank' : undefined}
|
||||
rel={reviewCta.external ? 'noopener' : undefined}
|
||||
aria-label="30+ five-star Google reviews"
|
||||
data-track-event="social_proof_click"
|
||||
data-track-type="hero_reviews"
|
||||
>
|
||||
<span class="chero-reviews-mark" aria-hidden="true">
|
||||
<img src="/images/google-g-logo.svg" alt="" width="15" height="16" />
|
||||
</span>
|
||||
<span class="chero-reviews-stars" aria-hidden="true">
|
||||
{#each stars as _}
|
||||
<Icon name="fas fa-star" />
|
||||
{/each}
|
||||
</span>
|
||||
<span class="chero-reviews-label">30+ five-star reviews</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Founder philosophy strip: the sincere counter to a competitor's
|
||||
"total freedom in nature" pitch. Frames the upside the owner is paying
|
||||
for: a day built around their dog, not a one-size pack. -->
|
||||
<section class="cfounder" aria-label="Our approach">
|
||||
<div class="cfounder-inner">
|
||||
<figure class="cfounder-quote">
|
||||
<blockquote class="cfounder-blockquote">
|
||||
<p class="cfounder-text">
|
||||
Every dog gets a walk built around them, not a one-size pack: the right
|
||||
small group, a walker who knows their name and their quirks, and
|
||||
adventures that grow with their confidence. That's how a walk becomes
|
||||
the best part of a dog's day.
|
||||
</p>
|
||||
</blockquote>
|
||||
<figcaption class="cfounder-attr">
|
||||
<img
|
||||
class="cfounder-avatar"
|
||||
src="/images/alessandra-goodwalk-founder-auckland.webp"
|
||||
alt="Alessandra, founder of Goodwalk, walking a dog in Auckland"
|
||||
width="96"
|
||||
height="96"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
<span class="cfounder-byline">
|
||||
<span class="cfounder-name">Alessandra</span>
|
||||
<span class="cfounder-role">Founder of Goodwalk</span>
|
||||
</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Offer first: what can I actually book, and roughly what does it cost? -->
|
||||
<ServicesSection services={content.services} />
|
||||
|
||||
<!-- Then the process: what happens after I enquire? Lowers the unknown. -->
|
||||
<HowItWorksSection content={content.howItWorks} />
|
||||
|
||||
<!-- Only now the reasons to choose us. -->
|
||||
<ValuesSection values={content.values} />
|
||||
|
||||
<TestimonialsSection testimonials={content.testimonials} seedKey={pagePath} />
|
||||
<FounderStorySection founderStory={content.founderStory} />
|
||||
<InfoSection info={content.info} />
|
||||
<BookingWizard booking={content.booking} {pagePath} {ab} />
|
||||
<InstagramSection instagram={content.instagram} />
|
||||
<Footer footer={content.footer} />
|
||||
|
||||
<style>
|
||||
/* ── Hero ──────────────────────────────────────────────────────────── */
|
||||
|
||||
.chero {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: var(--gw-green);
|
||||
color: var(--text-inverse);
|
||||
}
|
||||
|
||||
/* A single soft warm light, top-right, for depth. Not glassmorphism,
|
||||
not a gradient headline: just one breath of the brand yellow. */
|
||||
.chero::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -28%;
|
||||
right: -10%;
|
||||
width: 60%;
|
||||
height: 90%;
|
||||
background: radial-gradient(closest-side, rgba(var(--accent-rgb), 0.16), transparent 70%);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.chero-inner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: var(--max-w);
|
||||
min-height: clamp(500px, 68vh, 640px);
|
||||
margin: 0 auto;
|
||||
padding: clamp(48px, 5.5vw, 80px) var(--space-container-x);
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
|
||||
grid-template-areas:
|
||||
'copy media'
|
||||
'actions media';
|
||||
column-gap: clamp(32px, 5vw, 76px);
|
||||
row-gap: clamp(22px, 3vw, 32px);
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chero-copy {
|
||||
grid-area: copy;
|
||||
}
|
||||
|
||||
.chero-eyebrow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 0 0 18px;
|
||||
font-family: var(--font-body);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(var(--white-rgb), 0.7);
|
||||
}
|
||||
|
||||
.chero-eyebrow::before {
|
||||
content: '';
|
||||
width: 26px;
|
||||
height: 2px;
|
||||
border-radius: 2px;
|
||||
background: var(--yellow);
|
||||
}
|
||||
|
||||
.chero-title {
|
||||
margin: 0;
|
||||
font-family: var(--font-head);
|
||||
}
|
||||
|
||||
.chero-title-lead {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-size: clamp(18px, 2.1vw, 26px);
|
||||
font-weight: 600;
|
||||
line-height: 1.15;
|
||||
letter-spacing: -0.01em;
|
||||
color: rgba(var(--white-rgb), 0.82);
|
||||
}
|
||||
|
||||
.chero-title-main {
|
||||
display: block;
|
||||
font-size: clamp(42px, 6.4vw, 78px);
|
||||
font-weight: 800;
|
||||
line-height: 0.98;
|
||||
letter-spacing: -0.04em;
|
||||
color: var(--yellow);
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.chero-lead {
|
||||
margin: 22px 0 0;
|
||||
max-width: 40ch;
|
||||
font-size: clamp(16px, 1.3vw, 19px);
|
||||
line-height: 1.6;
|
||||
color: rgba(var(--white-rgb), 0.82);
|
||||
}
|
||||
|
||||
.chero-actions {
|
||||
grid-area: actions;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 16px 26px;
|
||||
}
|
||||
|
||||
.chero-reviews {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
text-decoration: none;
|
||||
color: rgba(var(--white-rgb), 0.9);
|
||||
transition: color var(--motion-fast);
|
||||
}
|
||||
|
||||
.chero-reviews-mark {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 50%;
|
||||
background: var(--surface-page);
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
|
||||
.chero-reviews-mark img {
|
||||
display: block;
|
||||
width: 15px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.chero-reviews-stars {
|
||||
display: inline-flex;
|
||||
gap: 2px;
|
||||
color: var(--yellow);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.chero-reviews-label {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: rgba(var(--white-rgb), 0.9);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.chero-reviews:hover {
|
||||
color: var(--text-inverse);
|
||||
}
|
||||
}
|
||||
|
||||
.chero-reviews:focus-visible {
|
||||
outline: 2px solid var(--yellow);
|
||||
outline-offset: 3px;
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
/* Media */
|
||||
.chero-media {
|
||||
grid-area: media;
|
||||
position: relative;
|
||||
justify-self: end;
|
||||
width: 100%;
|
||||
max-width: 460px;
|
||||
}
|
||||
|
||||
.chero-media::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 8% -6% -10% 10%;
|
||||
border-radius: var(--radius-xl);
|
||||
background: rgba(var(--accent-rgb), 0.22);
|
||||
filter: blur(46px);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.chero-photo {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
aspect-ratio: 4 / 5;
|
||||
border-radius: 28px;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
inset 0 0 0 1px rgba(var(--white-rgb), 0.12),
|
||||
var(--shadow-2xl);
|
||||
}
|
||||
|
||||
.chero-photo img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center 28%;
|
||||
}
|
||||
|
||||
/* ── Founder philosophy strip ──────────────────────────────────────── */
|
||||
|
||||
.cfounder {
|
||||
background: var(--surface-panel-cream);
|
||||
border-bottom: 1px solid var(--border-soft);
|
||||
}
|
||||
|
||||
.cfounder-inner {
|
||||
max-width: 940px;
|
||||
margin: 0 auto;
|
||||
padding: clamp(30px, 3.6vw, 48px) var(--space-container-x);
|
||||
}
|
||||
|
||||
.cfounder-quote {
|
||||
margin: 0;
|
||||
position: relative;
|
||||
padding-left: clamp(40px, 5vw, 60px);
|
||||
}
|
||||
|
||||
.cfounder-blockquote {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* A single large opening quote mark, brand yellow, as the editorial cue.
|
||||
Keeps the strip recognisably a statement without a repeated eyebrow. */
|
||||
.cfounder-quote::before {
|
||||
content: '\201C';
|
||||
position: absolute;
|
||||
left: -4px;
|
||||
top: -0.18em;
|
||||
font-family: var(--font-head);
|
||||
font-size: clamp(64px, 9vw, 104px);
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
color: var(--yellow);
|
||||
}
|
||||
|
||||
.cfounder-text {
|
||||
margin: 0;
|
||||
max-width: 46ch;
|
||||
font-family: var(--font-body);
|
||||
font-size: clamp(19px, 2.1vw, 27px);
|
||||
font-weight: 500;
|
||||
line-height: 1.45;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--text-heading);
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
.cfounder-attr {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
margin-top: clamp(18px, 2.4vw, 26px);
|
||||
}
|
||||
|
||||
.cfounder-avatar {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
object-position: center 25%;
|
||||
box-shadow:
|
||||
inset 0 0 0 2px rgba(var(--white-rgb), 0.9),
|
||||
var(--shadow-card);
|
||||
}
|
||||
|
||||
.cfounder-byline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.cfounder-name {
|
||||
font-family: var(--font-head);
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
.cfounder-role {
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Entrance */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.chero-eyebrow,
|
||||
.chero-title,
|
||||
.chero-lead,
|
||||
.chero-actions {
|
||||
animation: cheroRise 0.6s var(--ease-out-soft) both;
|
||||
}
|
||||
|
||||
.chero-title {
|
||||
animation-delay: 0.08s;
|
||||
}
|
||||
.chero-lead {
|
||||
animation-delay: 0.16s;
|
||||
}
|
||||
.chero-actions {
|
||||
animation-delay: 0.26s;
|
||||
}
|
||||
|
||||
.chero-photo {
|
||||
animation: cheroPhoto 0.8s var(--ease-out-soft) both;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cheroRise {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(14px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cheroPhoto {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(1.04);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Mobile: one calm column, message first, then Maya, then action ── */
|
||||
@media (max-width: 768px) {
|
||||
.chero-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
padding: 28px var(--space-container-x-mobile) 36px;
|
||||
row-gap: 0;
|
||||
}
|
||||
|
||||
.chero-copy {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.chero-media {
|
||||
order: 2;
|
||||
justify-self: stretch;
|
||||
max-width: none;
|
||||
margin: 24px 0 26px;
|
||||
}
|
||||
|
||||
.chero-actions {
|
||||
order: 3;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.chero-photo {
|
||||
aspect-ratio: 5 / 4;
|
||||
}
|
||||
|
||||
.chero-lead {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.chero-actions .btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cfounder-quote {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
.cfounder-quote::before {
|
||||
left: -2px;
|
||||
top: -0.08em;
|
||||
font-size: 52px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<script lang="ts">
|
||||
/*
|
||||
* Classic homepage design (the original layout, pre-clarity swap).
|
||||
*
|
||||
* Hero -> Values -> Services -> How it works -> Testimonials -> Founder ->
|
||||
* Info -> Booking -> Instagram. Preserved so the homepage can be rolled back
|
||||
* to it by swapping the component rendered in src/routes/+page.svelte.
|
||||
*
|
||||
* Paired design: HomeClarity.svelte. A view component only: the route that
|
||||
* renders it owns data loading, SEO head, and structured data.
|
||||
*/
|
||||
import Footer from '$lib/components/ui/Footer.svelte';
|
||||
import Header from '$lib/components/ui/Header.svelte';
|
||||
import HeroSection from '$lib/components/sections/HeroSection.svelte';
|
||||
import HowItWorksSection from '$lib/components/sections/HowItWorksSection.svelte';
|
||||
import InfoSection from '$lib/components/sections/InfoSection.svelte';
|
||||
import InstagramSection from '$lib/components/sections/InstagramSection.svelte';
|
||||
import BookingWizard from '$lib/components/sections/BookingWizard.svelte';
|
||||
import FounderStorySection from '$lib/components/sections/FounderStorySection.svelte';
|
||||
import ServicesSection from '$lib/components/sections/ServicesSection.svelte';
|
||||
import TestimonialsSection from '$lib/components/sections/TestimonialsSection.svelte';
|
||||
import ValuesSection from '$lib/components/sections/ValuesSection.svelte';
|
||||
import type { CallToAction, HomePageContent } from '$lib/types';
|
||||
import type { AbContext } from '$lib/ab';
|
||||
|
||||
export let content: HomePageContent;
|
||||
export let ab: AbContext | undefined = undefined;
|
||||
export let primaryCtaOverride: CallToAction | undefined = undefined;
|
||||
export let pagePath = '/';
|
||||
</script>
|
||||
|
||||
<Header navigation={content.navigation} />
|
||||
<HeroSection
|
||||
hero={content.hero}
|
||||
reviewCta={content.intro.reviewCta}
|
||||
{primaryCtaOverride}
|
||||
{ab}
|
||||
/>
|
||||
<ValuesSection values={content.values} />
|
||||
<ServicesSection services={content.services} />
|
||||
<HowItWorksSection content={content.howItWorks} />
|
||||
<TestimonialsSection testimonials={content.testimonials} seedKey={pagePath} />
|
||||
<FounderStorySection founderStory={content.founderStory} />
|
||||
<InfoSection info={content.info} />
|
||||
<BookingWizard booking={content.booking} {pagePath} {ab} />
|
||||
<InstagramSection instagram={content.instagram} />
|
||||
<Footer footer={content.footer} />
|
||||
@@ -0,0 +1,613 @@
|
||||
<script lang="ts">
|
||||
/*
|
||||
* Tiny Gang pack walks page — clarity redesign.
|
||||
*
|
||||
* One design system, one message. Replaces the multi-card / multi-gradient
|
||||
* ServiceLandingPage treatment (for this page) with a single green hero +
|
||||
* light body that shares the homepage's visual language. The message is said
|
||||
* once, in order: what it is -> what's different -> how joining works -> fit
|
||||
* -> proof -> price -> FAQ -> book.
|
||||
*
|
||||
* View component only: the route owns Header/Footer, SEO, and data.
|
||||
* Preview at /variants/tiny-gang. To go live, render this instead of
|
||||
* ServiceLandingPage for pack-walks in src/routes/[slug]/+page.svelte.
|
||||
*/
|
||||
import Icon from '$lib/components/ui/Icon.svelte';
|
||||
import ServiceHero from '$lib/components/sections/ServiceHero.svelte';
|
||||
import { reveal } from '$lib/actions/reveal';
|
||||
import BookingWizard from '$lib/components/sections/BookingWizard.svelte';
|
||||
import PricingPlanCard from '$lib/components/ui/PricingPlanCard.svelte';
|
||||
import TestimonialsSection from '$lib/components/sections/TestimonialsSection.svelte';
|
||||
import FaqSection from '$lib/components/sections/FaqSection.svelte';
|
||||
import { decoratePlans } from '$lib/utils/pricing';
|
||||
import type { ServicePageContent, SiteSharedContent } from '$lib/types';
|
||||
|
||||
export let content: SiteSharedContent;
|
||||
export let pageContent: ServicePageContent;
|
||||
export let currentPath = '/pack-walks';
|
||||
|
||||
// What makes Tiny Gang different. Said once here, not spread across a
|
||||
// 3-point highlight and a 6-card benefit grid that repeated each other.
|
||||
const traits = [
|
||||
{
|
||||
icon: 'fas fa-users',
|
||||
title: 'Carefully matched groups',
|
||||
body: '4 to 8 dogs, grouped by temperament, energy, and play style. Never a random pack.'
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-shield-dog',
|
||||
title: 'Small & medium dogs only',
|
||||
body: 'Pace, group size, and play style are all built for little dogs, so yours is never the odd one out.'
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-user-check',
|
||||
title: 'The same walker, every week',
|
||||
body: 'One familiar face at the door and on the walk. Your dog is known, not handed around.'
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-eye',
|
||||
title: 'Eyes on every dog',
|
||||
body: 'Small groups mean we notice the little things early, from recall to a quiet day.'
|
||||
}
|
||||
];
|
||||
|
||||
// How joining works. The process, made explicit instead of buried in a
|
||||
// paragraph and an FAQ.
|
||||
const steps = [
|
||||
{
|
||||
title: 'Free Meet & Greet',
|
||||
body: 'We come to your home, meet your dog, and talk honestly about whether the pack suits them.'
|
||||
},
|
||||
{
|
||||
title: 'Two assessment walks',
|
||||
body: 'Your dog settles in gently while we confirm the right group, pace, and handling.'
|
||||
},
|
||||
{
|
||||
title: 'Your weekly slot',
|
||||
body: 'A regular spot with the same little crew and the same walker, week in, week out.'
|
||||
}
|
||||
];
|
||||
|
||||
// Distilled pricing message. The title already says the routine and the
|
||||
// price says the cost, so each card keeps only the one or two lines that
|
||||
// actually differ. The universal "free pickup" promise is stated once under
|
||||
// the grid instead of repeated on every card.
|
||||
const planLines: Record<string, string[]> = {
|
||||
'1 Walk Per Week': [
|
||||
'A calm weekly outing',
|
||||
'Time to sniff, play, and unwind',
|
||||
'Great for dogs just starting out'
|
||||
],
|
||||
'2-3 Walks Per Week': [
|
||||
'Steady exercise and real social time',
|
||||
'A settled mid-week rhythm',
|
||||
'Our balanced routine'
|
||||
],
|
||||
'4-5 Walks Per Week': [
|
||||
'Best value per walk',
|
||||
'Maximum routine and structure',
|
||||
'For high-energy, social dogs'
|
||||
],
|
||||
'Casual Pack Walk': [
|
||||
'No weekly commitment',
|
||||
'Book only the weeks you need',
|
||||
'For dogs already in the pack'
|
||||
]
|
||||
};
|
||||
|
||||
$: pricingPlans = decoratePlans(
|
||||
pageContent.pricing.plans.map((plan) => ({
|
||||
...plan,
|
||||
features: planLines[plan.title] ?? plan.features
|
||||
}))
|
||||
);
|
||||
$: faqItems = pageContent.faq?.items?.slice(0, 6) ?? [];
|
||||
</script>
|
||||
|
||||
<main class="tg">
|
||||
<!-- Hero: the shared service-page hero (green copy left, photo bleeds
|
||||
right), identical to /dog-walking and /puppy-visits. -->
|
||||
<ServiceHero
|
||||
eyebrow={pageContent.hero.eyebrow}
|
||||
title={pageContent.hero.title}
|
||||
subtitle={pageContent.hero.subtitle}
|
||||
imageUrl={pageContent.hero.imageUrl}
|
||||
imageAlt={pageContent.hero.imageAlt}
|
||||
chips={pageContent.hero.chips ?? []}
|
||||
cta={pageContent.hero.cta}
|
||||
/>
|
||||
|
||||
<!-- What makes it different (said once) -->
|
||||
<section class="tg-section" use:reveal={{ distance: 0 }} aria-labelledby="tg-diff-heading">
|
||||
<div class="tg-inner">
|
||||
<div class="tg-head">
|
||||
<p class="tg-eyebrow">What makes Tiny Gang different</p>
|
||||
<h2 id="tg-diff-heading" class="tg-h2">Not a big anonymous pack. A small, matched crew.</h2>
|
||||
</div>
|
||||
<ul class="tg-traits">
|
||||
{#each traits as trait}
|
||||
<li class="tg-trait">
|
||||
<span class="tg-trait-icon" aria-hidden="true"><Icon name={trait.icon} /></span>
|
||||
<div>
|
||||
<h3>{trait.title}</h3>
|
||||
<p>{trait.body}</p>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- How joining works (the process, explicit) -->
|
||||
<section class="tg-section tg-section-steps" use:reveal={{ distance: 0 }} aria-labelledby="tg-steps-heading">
|
||||
<div class="tg-inner">
|
||||
<div class="tg-head">
|
||||
<p class="tg-eyebrow">How joining works</p>
|
||||
<h2 id="tg-steps-heading" class="tg-h2">Three simple steps, no pressure.</h2>
|
||||
</div>
|
||||
<ol class="tg-steps">
|
||||
{#each steps as step, i}
|
||||
<li class="tg-step">
|
||||
<span class="tg-step-num" aria-hidden="true">{i + 1}</span>
|
||||
<h3>{step.title}</h3>
|
||||
<p>{step.body}</p>
|
||||
</li>
|
||||
{/each}
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Is it right for your dog? -->
|
||||
{#if pageContent.decision}
|
||||
<section class="tg-section" use:reveal={{ distance: 0 }} aria-labelledby="tg-fit-heading">
|
||||
<div class="tg-inner">
|
||||
<div class="tg-head">
|
||||
<p class="tg-eyebrow">Honest fit check</p>
|
||||
<h2 id="tg-fit-heading" class="tg-h2">{pageContent.decision.title ?? 'Is Tiny Gang right for your dog?'}</h2>
|
||||
</div>
|
||||
<div class="tg-fit">
|
||||
<div class="tg-fit-col">
|
||||
<h3>{pageContent.decision.fitTitle ?? 'A great fit if your dog:'}</h3>
|
||||
<ul>
|
||||
{#each pageContent.decision.fitItems as item}
|
||||
<li>
|
||||
<span class="tg-fit-mark tg-fit-mark-yes" aria-hidden="true"><Icon name="fas fa-check" /></span>
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tg-fit-col">
|
||||
<h3>{pageContent.decision.notFitTitle ?? 'Better on a solo walk if your dog:'}</h3>
|
||||
<ul>
|
||||
{#each pageContent.decision.notFitItems as item}
|
||||
<li>
|
||||
<span class="tg-fit-mark tg-fit-mark-no" aria-hidden="true"><Icon name="fas fa-xmark" /></span>
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{#if pageContent.decision.footnote}
|
||||
<p class="tg-fit-note">{pageContent.decision.footnote}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- Proof: owners love it -->
|
||||
<TestimonialsSection
|
||||
heading="Owners love the Tiny Gang"
|
||||
testimonials={content.testimonials}
|
||||
seedKey={currentPath}
|
||||
/>
|
||||
|
||||
<!-- Price -->
|
||||
<section class="tg-section" use:reveal={{ distance: 0 }} aria-labelledby="tg-price-heading">
|
||||
<div class="tg-inner tg-inner-wide">
|
||||
<div class="tg-head">
|
||||
<p class="tg-eyebrow">Simple weekly pricing</p>
|
||||
<h2 id="tg-price-heading" class="tg-h2">{pageContent.pricing.title}</h2>
|
||||
<p class="tg-price-tagline">The best investment you'll ever make is in your dog's happiness.</p>
|
||||
{#if pageContent.pricing.intro}
|
||||
<p class="tg-head-sub">{pageContent.pricing.intro}</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="tg-plans">
|
||||
{#each pricingPlans as plan}
|
||||
<PricingPlanCard {plan} variant="service" hideCtaOnMobile={true} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="tg-price-notes">
|
||||
{#if pageContent.pricing.scarcityNote}
|
||||
<p class="tg-note tg-note-warm"><Icon name="fas fa-clock" />{pageContent.pricing.scarcityNote}</p>
|
||||
{/if}
|
||||
<p class="tg-note"><Icon name="fas fa-stopwatch" />60+ minute adventures.</p>
|
||||
<p class="tg-note"><Icon name="fas fa-car" />Free pickup & drop-off, every walk.</p>
|
||||
<p class="tg-note"><Icon name="fas fa-shield-heart" />Starts with a free Meet & Greet.</p>
|
||||
</div>
|
||||
|
||||
<a class="btn btn-yellow btn-mobile-center tg-plans-cta" href="#newlead">Book a Meet & Greet</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FAQ -->
|
||||
{#if faqItems.length}
|
||||
<section class="tg-section" use:reveal={{ distance: 0 }}>
|
||||
<div class="tg-inner tg-inner-narrow">
|
||||
<FaqSection
|
||||
title={pageContent.faq?.title ?? 'Tiny Gang FAQs'}
|
||||
intro={pageContent.faq?.intro}
|
||||
faqs={faqItems}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<BookingWizard booking={pageContent.booking} pagePath={currentPath} />
|
||||
</main>
|
||||
|
||||
<style>
|
||||
/* ── One system: tokens for this page ──────────────────────────────── */
|
||||
.tg {
|
||||
--tg-max: 1120px;
|
||||
--tg-narrow: 820px;
|
||||
background: var(--off-white);
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
.tg-inner {
|
||||
max-width: var(--tg-max);
|
||||
margin: 0 auto;
|
||||
padding-inline: var(--space-container-x);
|
||||
}
|
||||
|
||||
.tg-inner-narrow {
|
||||
max-width: var(--tg-narrow);
|
||||
}
|
||||
|
||||
/* Pricing breaks out wider than the body so four cards have real width
|
||||
(and the full CTA never clips). */
|
||||
.tg-inner-wide {
|
||||
max-width: 1280px;
|
||||
}
|
||||
|
||||
.tg-section {
|
||||
padding-block: clamp(48px, 6vw, 84px);
|
||||
}
|
||||
|
||||
/* Alternating surface for rhythm, not a new "system": just a tint. */
|
||||
.tg-section-steps {
|
||||
background: var(--surface-panel-cream);
|
||||
border-block: 1px solid var(--border-soft);
|
||||
}
|
||||
|
||||
/* One eyebrow, one heading, everywhere. */
|
||||
.tg-eyebrow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin: 0 0 14px;
|
||||
font-family: var(--font-body);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--gw-green);
|
||||
}
|
||||
|
||||
.tg-eyebrow::before {
|
||||
content: '';
|
||||
width: 24px;
|
||||
height: 2px;
|
||||
border-radius: 2px;
|
||||
background: var(--yellow);
|
||||
}
|
||||
|
||||
.tg-head {
|
||||
max-width: 40rem;
|
||||
margin: 0 auto clamp(28px, 4vw, 44px);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tg-h2 {
|
||||
margin: 0;
|
||||
font-family: var(--font-head);
|
||||
font-size: clamp(28px, 3.4vw, 42px);
|
||||
font-weight: 700;
|
||||
line-height: 1.08;
|
||||
letter-spacing: -0.03em;
|
||||
color: var(--text-heading);
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.tg-head-sub {
|
||||
margin: 14px auto 0;
|
||||
max-width: 36rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 17px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.tg-price-tagline {
|
||||
margin: 14px auto 0;
|
||||
max-width: 32rem;
|
||||
color: var(--gw-green);
|
||||
font-family: var(--font-head);
|
||||
font-size: clamp(17px, 1.7vw, 20px);
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
letter-spacing: -0.01em;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
/* ── Traits: a clean feature list, not boxed cards ─────────────────── */
|
||||
.tg-traits {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: clamp(24px, 3vw, 40px) clamp(32px, 5vw, 64px);
|
||||
}
|
||||
|
||||
.tg-trait {
|
||||
display: grid;
|
||||
grid-template-columns: 44px minmax(0, 1fr);
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.tg-trait-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 14px;
|
||||
background: var(--surface-brand);
|
||||
color: var(--yellow);
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.tg-trait h3 {
|
||||
margin: 2px 0 6px;
|
||||
font-family: var(--font-head);
|
||||
font-size: 19px;
|
||||
line-height: 1.2;
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
.tg-trait p {
|
||||
margin: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
max-width: 38ch;
|
||||
}
|
||||
|
||||
/* ── Steps: numerals, not cards ────────────────────────────────────── */
|
||||
.tg-steps {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: clamp(24px, 4vw, 48px);
|
||||
counter-reset: tg-step;
|
||||
}
|
||||
|
||||
.tg-step {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tg-step-num {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 50%;
|
||||
background: var(--yellow);
|
||||
color: var(--gw-green);
|
||||
font-family: var(--font-head);
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.tg-step h3 {
|
||||
margin: 0 0 8px;
|
||||
font-family: var(--font-head);
|
||||
font-size: 19px;
|
||||
line-height: 1.2;
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
.tg-step p {
|
||||
margin: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ── Fit check: one card, two columns ──────────────────────────────── */
|
||||
.tg-fit {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: clamp(20px, 3vw, 32px);
|
||||
padding: clamp(24px, 3vw, 40px);
|
||||
border-radius: var(--radius-xl);
|
||||
background: var(--surface-panel);
|
||||
box-shadow:
|
||||
inset 0 0 0 1px var(--border-soft-strong),
|
||||
var(--shadow-card);
|
||||
}
|
||||
|
||||
.tg-fit-col h3 {
|
||||
margin: 0 0 16px;
|
||||
font-family: var(--font-head);
|
||||
font-size: 17px;
|
||||
line-height: 1.3;
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
.tg-fit-col ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tg-fit-col li {
|
||||
display: grid;
|
||||
grid-template-columns: 22px minmax(0, 1fr);
|
||||
gap: 12px;
|
||||
align-items: start;
|
||||
color: var(--text-heading-soft);
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.tg-fit-mark {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-top: 1px;
|
||||
border-radius: 50%;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.tg-fit-mark-yes {
|
||||
background: rgba(var(--brand-rgb), 0.1);
|
||||
color: var(--gw-green);
|
||||
}
|
||||
|
||||
.tg-fit-mark-no {
|
||||
background: rgba(var(--ink-rgb), 0.06);
|
||||
color: var(--text-subtle);
|
||||
}
|
||||
|
||||
.tg-fit-note {
|
||||
margin: 22px auto 0;
|
||||
max-width: 48rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ── Pricing ───────────────────────────────────────────────────────── */
|
||||
.tg-plans {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
/* Roomier, consistent cards on this page, scoped via .tg-plans so the shared
|
||||
PricingPlanCard and other pages keep their own sizing. */
|
||||
.tg-plans :global(.plan-card) {
|
||||
padding: 44px 30px 38px;
|
||||
}
|
||||
|
||||
/* The featured card carries the "Goodwalk favourite" ribbon (absolute,
|
||||
top: 16px). Give it enough head room that the ribbon sits clearly above
|
||||
the title instead of over it. */
|
||||
.tg-plans :global(.plan-card--featured) {
|
||||
padding-top: 62px;
|
||||
}
|
||||
|
||||
/* Full-width CTA: never squished, and consistent across all four cards.
|
||||
display:flex (block-level) overrides the component's inline-flex +
|
||||
align-self:center, so the button always spans the full card width. */
|
||||
.tg-plans :global(.plan-card__cta) {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
padding-inline: 15px;
|
||||
}
|
||||
|
||||
.tg-price-notes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.tg-note {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin: 0;
|
||||
padding: 8px 16px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--surface-brand-soft);
|
||||
color: var(--gw-green);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tg-note-warm {
|
||||
background: var(--surface-accent-muted);
|
||||
color: #5a4500;
|
||||
}
|
||||
|
||||
:global(.tg-note .icon) {
|
||||
color: var(--yellow);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:global(.tg-note-warm .icon) {
|
||||
color: #b88800;
|
||||
}
|
||||
|
||||
.tg-plans-cta {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ── Responsive ────────────────────────────────────────────────────── */
|
||||
@media (max-width: 980px) {
|
||||
.tg-plans {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.tg-traits {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.tg-steps {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.tg-fit {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.tg-plans {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.tg-plans :global(.plan-card) {
|
||||
padding: 30px 24px 26px;
|
||||
}
|
||||
|
||||
.tg-plans :global(.plan-card--featured) {
|
||||
padding-top: 46px;
|
||||
}
|
||||
|
||||
.tg-plans-cta {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -36,9 +36,9 @@ export const homepageContent: HomePageContent = {
|
||||
title: 'Come home to a',
|
||||
highlight: 'calm, happy dog',
|
||||
mobileTitle: 'Come home to a\ncalm, happy dog',
|
||||
seoHeading: 'Dog walking for Auckland\'s small & medium dogs',
|
||||
seoHeading: 'Specialist dog walking for Auckland\'s small & medium dogs',
|
||||
subtitle:
|
||||
'The same trusted walker every time, in small groups. Never a rotating roster.',
|
||||
'Gentle pacing, familiar routines, and calmer evenings at home.',
|
||||
primaryCta: { label: 'Book a Meet & Greet', href: '#newlead', variant: 'yellow' },
|
||||
secondaryCta: {
|
||||
label: 'See how it works',
|
||||
|
||||
@@ -4,7 +4,7 @@ export const packWalksContent: ServicePageContent = {
|
||||
hero: {
|
||||
eyebrow: 'Tiny Gang Pack Walks',
|
||||
title: 'Pack walks that actually suit small & medium dogs',
|
||||
subtitle: 'Small groups, thoughtful matching, and off-leash freedom only when a dog is truly ready.',
|
||||
subtitle: 'A proper adventure with a pack of like-minded dogs their own size, so yours comes home happy, completely worn out, and done for the day.',
|
||||
introEyebrow: 'How Tiny Gang works',
|
||||
introHeading: 'What joining Tiny Gang actually looks like',
|
||||
detailHighlights: [
|
||||
|
||||
+7
-39
@@ -1,36 +1,19 @@
|
||||
<script lang="ts">
|
||||
import SeoHead from '$lib/components/ui/SeoHead.svelte';
|
||||
import Footer from '$lib/components/ui/Footer.svelte';
|
||||
import Header from '$lib/components/ui/Header.svelte';
|
||||
import HeroSection from '$lib/components/sections/HeroSection.svelte';
|
||||
import HowItWorksSection from '$lib/components/sections/HowItWorksSection.svelte';
|
||||
import InfoSection from '$lib/components/sections/InfoSection.svelte';
|
||||
import InstagramSection from '$lib/components/sections/InstagramSection.svelte';
|
||||
import BookingWizard from '$lib/components/sections/BookingWizard.svelte';
|
||||
import FounderStorySection from '$lib/components/sections/FounderStorySection.svelte';
|
||||
import ServicesSection from '$lib/components/sections/ServicesSection.svelte';
|
||||
import TestimonialsSection from '$lib/components/sections/TestimonialsSection.svelte';
|
||||
import ValuesSection from '$lib/components/sections/ValuesSection.svelte';
|
||||
import OnboardingPage from '$lib/components/pages/OnboardingPage.svelte';
|
||||
// ── Homepage design ──────────────────────────────────────────────────
|
||||
// The live homepage renders HomeClarity. To roll back to the original
|
||||
// layout, change the two references below to HomeClassic (its preview lives
|
||||
// at /variants/homepage-original). Both are pure view components; all data
|
||||
// loading, SEO head, and structured data stay here.
|
||||
import HomeClarity from '$lib/components/pages/HomeClarity.svelte';
|
||||
import { buildAreaServed } from '$lib/seo';
|
||||
import type { PageData } from './$types';
|
||||
import type { CallToAction } from '$lib/types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
const siteUrl = 'https://www.goodwalk.co.nz';
|
||||
|
||||
// hero_cta experiment: free_emphasis swaps "Book a Meet & Greet"
|
||||
// for "Book a FREE Meet & Greet". Same href, same destination.
|
||||
$: heroAb = data.ab;
|
||||
$: heroOverride =
|
||||
data.siteVariant === 'marketing' && data.content && heroAb?.variant === 'free_emphasis'
|
||||
? ({
|
||||
...data.content.hero.primaryCta,
|
||||
label: 'Book a FREE Meet & Greet',
|
||||
} satisfies CallToAction)
|
||||
: undefined;
|
||||
|
||||
function absoluteUrl(value: string) {
|
||||
if (value.startsWith('http://') || value.startsWith('https://')) {
|
||||
return value;
|
||||
@@ -166,20 +149,5 @@
|
||||
: ''}
|
||||
/>
|
||||
|
||||
<Header navigation={content.navigation} />
|
||||
<HeroSection
|
||||
hero={content.hero}
|
||||
reviewCta={content.intro.reviewCta}
|
||||
primaryCtaOverride={heroOverride}
|
||||
ab={heroAb}
|
||||
/>
|
||||
<ValuesSection values={content.values} />
|
||||
<ServicesSection services={content.services} />
|
||||
<HowItWorksSection content={content.howItWorks} />
|
||||
<TestimonialsSection testimonials={content.testimonials} seedKey="/" />
|
||||
<FounderStorySection founderStory={content.founderStory} />
|
||||
<InfoSection info={content.info} />
|
||||
<BookingWizard booking={content.booking} pagePath="/" ab={heroAb} />
|
||||
<InstagramSection instagram={content.instagram} />
|
||||
<Footer footer={content.footer} />
|
||||
<HomeClarity {content} ab={data.ab} pagePath="/" />
|
||||
{/if}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
/*
|
||||
* Preview of the clarity homepage design (currently live at /).
|
||||
* Single source of truth is HomeClarity.svelte. noindex so it does not
|
||||
* compete with the real homepage in search.
|
||||
*/
|
||||
import HomeClarity from '$lib/components/pages/HomeClarity.svelte';
|
||||
import { homepageContent } from '$lib/content/homepage';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Homepage clarity design (preview) | Goodwalk</title>
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
</svelte:head>
|
||||
|
||||
<HomeClarity content={homepageContent} pagePath="/variants/homepage-clarity" />
|
||||
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
/*
|
||||
* Preview of the original homepage design, kept for rollback reference.
|
||||
* Single source of truth is HomeClassic.svelte. To restore this as the live
|
||||
* homepage, render HomeClassic in src/routes/+page.svelte. noindex so it does
|
||||
* not compete with the real homepage in search.
|
||||
*/
|
||||
import HomeClassic from '$lib/components/pages/HomeClassic.svelte';
|
||||
import { homepageContent } from '$lib/content/homepage';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Homepage original design (preview) | Goodwalk</title>
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
</svelte:head>
|
||||
|
||||
<HomeClassic content={homepageContent} pagePath="/variants/homepage-original" />
|
||||
@@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
/*
|
||||
* Preview of the redesigned Tiny Gang pack walks page.
|
||||
* Single source of truth is TinyGangPage.svelte. noindex so it does not
|
||||
* compete with the live /pack-walks page in search.
|
||||
*
|
||||
* To go live: in src/routes/[slug]/+page.svelte, render TinyGangPage instead
|
||||
* of ServiceLandingPage in the `data.slug === 'pack-walks'` branch.
|
||||
*/
|
||||
import Header from '$lib/components/ui/Header.svelte';
|
||||
import Footer from '$lib/components/ui/Footer.svelte';
|
||||
import TinyGangPage from '$lib/components/pages/TinyGangPage.svelte';
|
||||
import { homepageContent } from '$lib/content/homepage';
|
||||
import { packWalksContent } from '$lib/content/pack-walks';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Tiny Gang pack walks (redesign preview) | Goodwalk</title>
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
</svelte:head>
|
||||
|
||||
<Header navigation={homepageContent.navigation} />
|
||||
<TinyGangPage content={homepageContent} pageContent={packWalksContent} currentPath="/variants/tiny-gang" />
|
||||
<Footer footer={homepageContent.footer} />
|
||||
Reference in New Issue
Block a user