2026-05-02 08:26:18 +12:00
< script lang = "ts" >
2026-05-13 09:39:52 +12:00
import { onMount , tick } from 'svelte' ;
2026-05-02 08:26:18 +12:00
import { reveal } from '$lib/actions/reveal' ;
import Icon from '$lib/components/Icon.svelte' ;
2026-05-11 21:02:24 +12:00
import { getEnhancedImage } from '$lib/enhanced-images' ;
import { getSeededTestimonialIndex } from '$lib/testimonials' ;
2026-05-02 08:26:18 +12:00
import type { TestimonialContent } from '$lib/types' ;
export let testimonials : TestimonialContent [];
2026-05-07 21:47:42 +12:00
export let eyebrow = '30+ five-star reviews' ;
2026-05-15 01:28:10 +12:00
export let heading = 'What owners notice first' ;
export let blurb = 'Happier dogs. Calmer evenings. A routine that feels easier to trust and easier to keep.' ;
export let testimonialsHref = '/testimonials' ;
export let googleReviewsHref = 'https://g.page/r/CUsvrWPhkYrAEB0/' ;
2026-05-11 21:02:24 +12:00
export let seedKey = '' ;
2026-05-02 08:26:18 +12:00
type TestimonialSlide = TestimonialContent & { imageUrl : string };
const wordpressTestimonials : Record < string , TestimonialSlide > = {
Kate : {
reviewer : 'Kate' ,
detail : "Archie's mum" ,
quote :
'Love Aless! She is so amazing with my slightly hyper and anxious dog. She is great with communication if anything on either of our ends need to change. Archie love his walks, and I love the photos she posts of him.' ,
2026-05-12 00:45:02 +12:00
imageUrl : '/images/archie-auckland-dog-walking-review.jpg'
2026-05-02 08:26:18 +12:00
},
Estelle : {
reviewer : 'Estelle' ,
detail : "Monty's mum" ,
quote :
'GoodWalk was the best dog walking service for my little pooch ! Aless was very helpful - basically doubled as a second mum to Monty. She always provided feedback on his outings and assisted where possible with any additional training that she felt he could work on and made recommendations where necessary which i feel is what every dog mum wants and needs!' ,
2026-05-12 00:45:02 +12:00
imageUrl : '/images/monty-auckland-dog-walking-review.jpg'
2026-05-02 08:26:18 +12:00
},
Ross : {
reviewer : 'Ross' ,
2026-05-07 21:47:42 +12:00
detail : "Otis's dad" ,
2026-05-02 08:26:18 +12:00
quote :
'Truly the best dog walker in Auckland! I feel so lucky to have found Aless and my little terrier Otis absolutely adores her. He enjoys his regular weekly walks and always comes back happy & tired. Love the updates on social media so I can see how my dog is enjoying his day! Aless makes logistics so easy too. Highly highly recommend, there’ s a reason she has 5 stars!' ,
2026-05-12 00:45:02 +12:00
imageUrl : '/images/otis-auckland-dog-walking-review.jpg'
2026-05-02 08:26:18 +12:00
},
Nina : {
reviewer : 'Nina' ,
detail : "Wallace's mum" ,
quote :
'Alessandra has been walking and spending time with my pup since she was 10 weeks old, coming over and doing puppy visits through to transitioning her to pack walks with her little doggo friends. I know Alassandra loves and cares for my dog as much as I do and my dog has a great time! Cant recommend enough' ,
2026-05-12 00:45:02 +12:00
imageUrl : '/images/wallace-auckland-dog-walking-review.jpg'
2026-05-02 08:26:18 +12:00
}
};
let activeIndex = 0 ;
let paused = false ;
2026-05-03 11:49:59 +12:00
let inView = false ;
let prefersReducedMotion = false ;
let carouselEl : HTMLDivElement | undefined ;
2026-05-13 09:39:52 +12:00
let stageEl : HTMLDivElement | undefined ;
2026-05-11 21:02:24 +12:00
let slideSignature = '' ;
2026-05-02 08:26:18 +12:00
$ : slides = testimonials
. map (( testimonial ) => wordpressTestimonials [ testimonial . reviewer ] ?? testimonial )
. filter (( testimonial ) : testimonial is TestimonialSlide => Boolean ( testimonial . imageUrl ));
$ : if ( activeIndex >= slides . length ) {
activeIndex = 0 ;
}
2026-05-11 21:02:24 +12:00
$ : {
const nextSignature = ` ${ seedKey } : ${ slides . map (( slide ) => slide . reviewer ). join ( '|' ) } ` ;
if ( nextSignature !== slideSignature ) {
slideSignature = nextSignature ;
activeIndex = getSeededTestimonialIndex ( slides , seedKey );
}
}
2026-05-05 08:12:36 +12:00
function dogNameFromDetail ( detail : string ) {
const match = detail . match ( /^([^'’ ]+)/ );
return match ? match [ 1 ]. trim () : '' ;
}
function testimonialAlt ( testimonial : TestimonialSlide ) {
const dog = dogNameFromDetail ( testimonial . detail );
return dog
? ` ${ dog } , a happy Goodwalk dog walking client in Auckland`
: ` ${ testimonial . reviewer } 's dog after a Goodwalk Auckland dog walk` ;
}
2026-05-02 08:26:18 +12:00
function showPrevious() {
if ( ! slides . length ) {
return ;
}
activeIndex = ( activeIndex - 1 + slides . length ) % slides . length ;
2026-05-13 09:39:52 +12:00
syncMobileStage ();
2026-05-02 08:26:18 +12:00
}
function showNext() {
if ( ! slides . length ) {
return ;
}
activeIndex = ( activeIndex + 1 ) % slides . length ;
2026-05-13 09:39:52 +12:00
syncMobileStage ();
}
function isMobileViewport() {
return typeof window !== 'undefined' && window . innerWidth <= 767 ;
}
async function syncMobileStage ( behavior : ScrollBehavior = 'smooth' ) {
if ( ! stageEl || ! isMobileViewport ()) {
return ;
}
await tick ();
stageEl . scrollTo ({
left : stageEl.clientWidth * activeIndex ,
behavior
});
}
function handleStageScroll() {
if ( ! stageEl || ! isMobileViewport ()) {
return ;
}
const nextIndex = Math . round ( stageEl . scrollLeft / Math . max ( stageEl . clientWidth , 1 ));
if ( nextIndex !== activeIndex ) {
activeIndex = nextIndex ;
}
2026-05-02 08:26:18 +12:00
}
onMount (() => {
2026-05-03 11:49:59 +12:00
const motionQuery = window . matchMedia ( '(prefers-reduced-motion: reduce)' );
prefersReducedMotion = motionQuery . matches ;
const onMotionChange = ( event : MediaQueryListEvent ) => {
prefersReducedMotion = event . matches ;
};
motionQuery . addEventListener ( 'change' , onMotionChange );
const observer = carouselEl
? new IntersectionObserver (
([ entry ]) => {
inView = entry . isIntersecting ;
},
{ threshold : 0.25 }
)
: null ;
if ( observer && carouselEl ) {
observer . observe ( carouselEl );
}
2026-05-13 09:39:52 +12:00
const handleResize = () => {
syncMobileStage ( 'auto' );
};
window . addEventListener ( 'resize' , handleResize );
syncMobileStage ( 'auto' );
2026-05-02 08:26:18 +12:00
const interval = window . setInterval (() => {
2026-05-03 11:49:59 +12:00
if ( ! paused && ! prefersReducedMotion && inView && slides . length > 1 ) {
2026-05-02 08:26:18 +12:00
showNext ();
}
2026-05-03 11:49:59 +12:00
}, 9000 );
2026-05-02 08:26:18 +12:00
2026-05-03 11:49:59 +12:00
return () => {
window . clearInterval ( interval );
2026-05-13 09:39:52 +12:00
window . removeEventListener ( 'resize' , handleResize );
2026-05-03 11:49:59 +12:00
motionQuery . removeEventListener ( 'change' , onMotionChange );
observer ? . disconnect ();
};
2026-05-02 08:26:18 +12:00
});
</ script >
< section id = "testimonials" use:reveal = {{ delay : 40 }} class="reveal-block" >
< div class = "testimonials-inner" >
2026-05-07 21:47:42 +12:00
< span class = "testimonials-eyebrow" > { eyebrow } </ span >
2026-05-02 08:26:18 +12:00
< h2 class = "section-heading" > { heading } </ h2 >
< div class = "testimonials-intro" >
< p > { blurb } </ p >
</ div >
{ #if slides . length }
< div
2026-05-03 11:49:59 +12:00
bind:this = { carouselEl }
2026-05-02 08:26:18 +12:00
class="testimonials-carousel"
role = "region"
aria-label = "Customer testimonials"
on:mouseenter = {() => ( paused = true )}
on:mouseleave= {() => ( paused = false )}
2026-05-03 11:49:59 +12:00
on:focusin = {() => ( paused = true )}
on:focusout= {() => ( paused = false )}
2026-05-02 08:26:18 +12:00
>
< button
class = "testimonial-arrow testimonial-arrow-left"
type = "button"
aria-label = "Previous testimonial"
on:click = { showPrevious }
>
< Icon name = "fas fa-chevron-left" />
</ button >
2026-05-13 09:39:52 +12:00
< div bind:this = { stageEl } class="testimonial-stage" on:scroll = { handleStageScroll } >
2026-05-02 08:26:18 +12:00
{ #each slides as testimonial , index }
< article class:testimonial-slide-active = { index === activeIndex } class="testimonial-slide" >
< div class = "testimonial-photo-wrap" >
< div class = "testimonial-photo-frame" >
{ #if index === activeIndex }
2026-05-11 21:02:24 +12:00
{ @const enhancedPhoto = getEnhancedImage ( testimonial . imageUrl )}
{ #if enhancedPhoto }
< enhanced:img
class = "testimonial-photo"
src = { enhancedPhoto }
alt= { testimonialAlt ( testimonial )}
loading = "lazy"
decoding = "async"
/>
{ : else }
< img
class = "testimonial-photo"
src = { testimonial . imageUrl }
alt= { testimonialAlt ( testimonial )}
loading = "lazy"
decoding = "async"
/>
{ /if }
2026-05-02 08:26:18 +12:00
{ /if }
</ div >
</ div >
< div class = "testimonial-copy" >
< span class = "testimonial-quote-mark" > "</ span >
2026-05-03 11:49:59 +12:00
< blockquote class = "testimonial-quote" > { testimonial . quote } </ blockquote >
2026-05-02 08:26:18 +12:00
< div class = "testimonial-author" >
< span class = "testimonial-author-name" > { testimonial . reviewer } </ span >
< span class = "testimonial-author-detail" > { testimonial . detail } </ span >
</ div >
< div class = "testimonial-divider" ></ div >
2026-05-06 11:36:19 +12:00
< div class = "testimonial-mobile-controls" aria-label = "Testimonial navigation" >
< button
class = "testimonial-arrow testimonial-arrow-inline"
type = "button"
aria-label = "Previous testimonial"
on:click = { showPrevious }
>
< Icon name = "fas fa-chevron-left" />
</ button >
< button
class = "testimonial-arrow testimonial-arrow-inline"
type = "button"
aria-label = "Next testimonial"
on:click = { showNext }
>
< Icon name = "fas fa-chevron-right" />
</ button >
</ div >
2026-05-15 01:28:10 +12:00
< a class = "testimonial-google" href = { googleReviewsHref } target="_blank" rel = "noopener" >
2026-05-06 11:36:19 +12:00
< img
class = "testimonial-google-logo"
src = "/images/google-g-logo.svg"
alt = ""
width = "18"
height = "19"
/>
2026-05-06 08:27:24 +12:00
< span > 30+ five-star Google reviews</ span >
2026-05-02 08:26:18 +12:00
</ a >
</ div >
</ article >
{ /each }
</ div >
< button
class = "testimonial-arrow testimonial-arrow-right"
type = "button"
aria-label = "Next testimonial"
on:click = { showNext }
>
< Icon name = "fas fa-chevron-right" />
</ button >
</ div >
{ /if }
2026-05-06 11:36:19 +12:00
2026-05-15 01:28:10 +12:00
< div class = "testimonials-cta-row" >
< a href = { testimonialsHref } class="testimonials-cta testimonials-cta-primary " >
< Icon name = "fas fa-comment-dots" />
< span class = "testimonials-cta-label-desktop" > All testimonials</ span >
< span class = "testimonials-cta-label-mobile" > Testimonials</ span >
</ a >
< a href = { googleReviewsHref } target="_blank" rel = "noopener" class = "testimonials-cta testimonials-cta-secondary" >
< img class = "testimonials-cta-logo" src = "/images/google-g-logo.svg" alt = "" width = "16" height = "17" />
< span class = "testimonials-cta-label-desktop" > Google reviews</ span >
< span class = "testimonials-cta-label-mobile" > Google</ span >
</ a >
</ div >
2026-05-02 08:26:18 +12:00
</ div >
</ section >
< style >
2026-05-07 21:47:42 +12:00
. testimonials-eyebrow {
display : block ;
width : fit-content ;
margin : 0 auto 10 px ;
padding : 7 px 12 px ;
border-radius : 999 px ;
background : rgba ( 33 , 48 , 33 , 0.08 );
color : var ( -- gw - green );
font-size : 12 px ;
font-weight : 800 ;
letter-spacing : 0.08 em ;
text-transform : uppercase ;
box-shadow : inset 0 0 0 1 px rgba ( 17 , 20 , 24 , 0.05 );
}
2026-05-02 08:26:18 +12:00
. testimonials-intro {
max-width : 760 px ;
margin : 18 px auto 0 ;
text-align : center ;
}
. testimonials-intro p {
margin : 0 ;
color : #4c5056 ;
2026-05-15 01:28:10 +12:00
font-size : var ( -- body - lead - size );
2026-05-02 08:26:18 +12:00
line-height : 1.65 ;
}
2026-05-15 01:28:10 +12:00
. testimonials-cta-row {
display : grid ;
grid-template-columns : repeat ( 2 , max - content );
align-items : center ;
justify-content : center ;
2026-05-06 11:36:19 +12:00
width : fit-content ;
2026-05-15 01:28:10 +12:00
max-width : 100 % ;
gap : 12 px ;
margin : 22 px auto 0 ;
}
. testimonials-cta {
display : inline-flex ;
2026-05-02 08:26:18 +12:00
align-items : center ;
2026-05-15 01:28:10 +12:00
justify-content : center ;
gap : 9 px ;
min-height : 42 px ;
padding : 9 px 16 px ;
2026-05-02 08:26:18 +12:00
border-radius : 999 px ;
2026-05-15 01:28:10 +12:00
flex-shrink : 0 ;
2026-05-02 08:26:18 +12:00
text-decoration : none ;
2026-05-15 01:28:10 +12:00
font-size : 14 px ;
font-weight : 700 ;
line-height : 1.2 ;
white-space : nowrap ;
2026-05-02 08:26:18 +12:00
transition :
transform 0.16 s cubic-bezier ( 0.22 , 1 , 0.36 , 1 ),
background 0.2 s ease ,
box-shadow 0.2 s ease ;
}
2026-05-15 01:28:10 +12:00
. testimonials-cta-primary {
background : var ( -- gw - green );
color : #fff ;
box-shadow : 0 10 px 24 px rgba ( 33 , 48 , 33 , 0.16 );
}
. testimonials-cta-secondary {
background : rgba ( 33 , 48 , 33 , 0.06 );
color : var ( -- gw - green );
box-shadow : inset 0 0 0 1 px rgba ( 17 , 20 , 24 , 0.06 );
}
. testimonials-cta-logo {
flex : 0 0 auto ;
}
. testimonials-cta-label-mobile {
display : none ;
2026-05-02 08:26:18 +12:00
}
@ media ( hover : hover ) {
2026-05-15 01:28:10 +12:00
. testimonials-cta : hover {
2026-05-02 08:26:18 +12:00
transform : translateY ( -2 px );
2026-05-15 01:28:10 +12:00
}
. testimonials-cta-primary : hover {
box-shadow : 0 14 px 30 px rgba ( 33 , 48 , 33 , 0.2 );
}
. testimonials-cta-secondary : hover {
2026-05-02 08:26:18 +12:00
background : rgba ( 33 , 48 , 33 , 0.09 );
box-shadow :
inset 0 0 0 1 px rgba ( 17 , 20 , 24 , 0.06 ),
0 10 px 22 px rgba ( 17 , 20 , 24 , 0.08 );
}
}
. testimonials-carousel {
position : relative ;
margin-top : 48 px ;
2026-05-07 21:47:42 +12:00
padding : 0 38 px ;
2026-05-02 08:26:18 +12:00
}
@ media ( max-width : 768px ) {
2026-05-07 21:47:42 +12:00
. testimonials-eyebrow {
margin-bottom : 8 px ;
padding : 6 px 10 px ;
font-size : 11 px ;
}
2026-05-02 08:26:18 +12:00
. testimonials-intro {
margin-top : 14 px ;
}
. testimonials-intro p {
2026-05-15 01:28:10 +12:00
font-size : var ( -- body - lead - size - mobile );
2026-05-02 08:26:18 +12:00
line-height : 1.55 ;
}
2026-05-15 01:28:10 +12:00
. testimonials-cta-row {
gap : 8 px ;
margin-top : 16 px ;
}
. testimonials-cta {
min-height : 38 px ;
padding : 8 px 10 px ;
font-size : 11 px ;
gap : 7 px ;
}
. testimonials-cta-label-desktop {
display : none ;
}
. testimonials-cta-label-mobile {
display : inline ;
2026-05-02 08:26:18 +12:00
}
}
. testimonial-arrow {
transition :
transform 0.16 s cubic-bezier ( 0.22 , 1 , 0.36 , 1 ),
box-shadow 0.2 s ease ,
background 0.2 s ease ;
-webkit- tap-highlight-color : transparent ;
touch-action : manipulation ;
}
@ media ( hover : hover ) {
. testimonial-arrow : hover {
transform : translateY ( -50 % ) scale ( 1.05 );
box-shadow : 0 14 px 28 px rgba ( 17 , 20 , 24 , 0.12 );
}
}
. testimonial-arrow : active {
transform : translateY ( -50 % ) scale ( 0.95 );
}
: global ( . reveal-ready . reveal-block ) {
opacity : 0 ;
transform : translate3d ( 0 , var ( -- reveal - distance , 24 px ), 0 );
transition :
opacity 0.55 s ease ,
transform 0.7 s cubic-bezier ( 0.2 , 0.8 , 0.2 , 1 );
transition-delay : var ( -- reveal - delay , 0 ms );
}
: global ( . reveal-visible . reveal-block ) {
opacity : 1 ;
transform : translate3d ( 0 , 0 , 0 );
}
. testimonial-stage {
position : relative ;
overflow : hidden ;
2026-05-05 21:23:41 +12:00
border-radius : 28 px ;
2026-05-02 08:26:18 +12:00
background : #fff ;
box-shadow : 0 10 px 30 px rgba ( 20 , 24 , 20 , 0.06 );
min-height : 620 px ;
}
. testimonial-slide {
position : absolute ;
inset : 0 ;
display : grid ;
grid-template-columns : 45 % 55 % ;
align-items : stretch ;
opacity : 0 ;
pointer-events : none ;
transition :
opacity 0.35 s ease ,
transform 0.35 s ease ;
transform : translateX ( 18 px );
}
. testimonial-slide-active {
opacity : 1 ;
pointer-events : auto ;
transform : translateX ( 0 );
}
. testimonial-photo-wrap {
display : flex ;
align-items : flex-start ;
justify-content : center ;
padding : 32 px 24 px 0 24 px ;
}
. testimonial-photo-frame {
width : min ( 100 % , 340 px );
}
. testimonial-photo {
display : block ;
width : 100 % ;
margin : 0 auto ;
aspect-ratio : 1 / 1 ;
object-fit : cover ;
}
. testimonial-copy {
align-self : start ;
padding : 118 px 112 px 76 px 10 px ;
}
. testimonial-quote-mark {
display : block ;
font-family : Georgia , serif ;
font-size : 72 px ;
line-height : 0.6 ;
color : var ( -- yellow );
margin-bottom : 20 px ;
user-select : none ;
}
2026-05-03 11:49:59 +12:00
. testimonial-copy . testimonial-quote {
2026-05-02 08:26:18 +12:00
max-width : 500 px ;
margin : 0 ;
font-size : 17 px ;
font-style : italic ;
font-weight : 400 ;
line-height : 1.6 ;
letter-spacing : 0 ;
color : #2e3031 ;
}
. testimonial-author {
display : flex ;
align-items : center ;
gap : 10 px ;
margin-top : 24 px ;
}
. testimonial-author-name {
font-family : var ( -- font - head );
font-size : 15 px ;
font-weight : 700 ;
color : #1a1a1a ;
}
. testimonial-author-detail {
font-size : 14 px ;
color : #6b7280 ;
}
. testimonial-author-detail :: before {
content : '—' ;
margin-right : 6 px ;
}
. testimonial-divider {
width : 100 % ;
max-width : 690 px ;
height : 1 px ;
margin : 44 px 0 0 ;
background : #e7e7e7 ;
}
. testimonial-google {
display : inline-flex ;
align-items : center ;
gap : 12 px ;
margin-top : 28 px ;
padding : 10 px 20 px ;
border-radius : 999 px ;
background : #f8f8f8 ;
color : #0a304e ;
2026-05-11 21:02:24 +12:00
font-family : var ( -- font - head );
2026-05-02 08:26:18 +12:00
font-size : 14 px ;
2026-05-11 21:02:24 +12:00
font-weight : 700 ;
line-height : 1.2 ;
letter-spacing : 0.01 em ;
2026-05-02 08:26:18 +12:00
box-shadow : 0 0 0 1 px rgba ( 10 , 48 , 78 , 0.06 );
}
2026-05-06 11:36:19 +12:00
. testimonial-google-logo {
width : 18 px ;
height : 19 px ;
flex : 0 0 auto ;
2026-05-02 08:26:18 +12:00
}
. testimonial-google : hover {
background : #efe6d5 ;
}
2026-05-06 11:36:19 +12:00
. testimonial-mobile-controls {
display : none ;
}
2026-05-02 08:26:18 +12:00
. testimonial-arrow {
position : absolute ;
top : 50 % ;
z-index : 3 ;
display : inline-flex ;
align-items : center ;
justify-content : center ;
width : 58 px ;
height : 58 px ;
border : 1 px solid rgba ( 0 , 0 , 0 , 0.08 );
border-radius : 20 px ;
background : rgba ( 255 , 255 , 255 , 0.95 );
color : #111 ;
font-size : 22 px ;
transform : translateY ( -50 % );
box-shadow : 0 12 px 28 px rgba ( 20 , 24 , 20 , 0.07 );
}
. testimonial-arrow : hover {
background : #fff ;
}
. testimonial-arrow-left {
2026-05-07 21:47:42 +12:00
left : 0 ;
2026-05-02 08:26:18 +12:00
}
. testimonial-arrow-right {
2026-05-07 21:47:42 +12:00
right : 0 ;
2026-05-02 08:26:18 +12:00
}
@ media ( max-width : 1024px ) {
. testimonial-stage {
min-height : 560 px ;
}
. testimonial-photo-wrap {
padding : 88 px 16 px 64 px 44 px ;
}
. testimonial-copy {
padding : 96 px 72 px 64 px 8 px ;
}
2026-05-03 11:49:59 +12:00
. testimonial-copy . testimonial-quote {
2026-05-02 08:26:18 +12:00
max-width : 460 px ;
font-size : 17 px ;
}
}
@ media ( max-width : 767px ) {
. testimonials-carousel {
2026-05-15 01:28:10 +12:00
margin-top : 26 px ;
2026-05-07 21:47:42 +12:00
padding : 0 ;
2026-05-02 08:26:18 +12:00
}
. testimonial-stage {
min-height : unset ;
2026-05-13 09:39:52 +12:00
display : flex ;
2026-05-06 11:36:19 +12:00
padding-bottom : 0 ;
2026-05-13 09:39:52 +12:00
overflow-x : auto ;
overscroll-behavior-x : contain ;
scroll-snap-type : x proximity ;
scrollbar-width : none ;
-webkit- overflow-scrolling : touch ;
}
. testimonial-stage :: -webkit-scrollbar {
display : none ;
2026-05-02 08:26:18 +12:00
}
. testimonial-slide {
position : relative ;
2026-05-13 09:39:52 +12:00
display : grid ;
flex : 0 0 100 % ;
2026-05-02 08:26:18 +12:00
grid-template-columns : 1 fr ;
2026-05-13 09:39:52 +12:00
opacity : 1 ;
pointer-events : auto ;
2026-05-02 08:26:18 +12:00
transform : none ;
2026-05-13 09:39:52 +12:00
scroll-snap-align : start ;
2026-05-02 08:26:18 +12:00
}
. testimonial-slide-active {
display : grid ;
}
. testimonial-photo-wrap {
justify-content : center ;
2026-05-15 01:28:10 +12:00
padding : 34 px 20 px 12 px ;
2026-05-02 08:26:18 +12:00
}
. testimonial-photo-frame {
width : min ( 100 % , 220 px );
}
. testimonial-photo {
aspect-ratio : 1 / 1 ;
}
. testimonial-copy {
2026-05-15 01:28:10 +12:00
padding : 4 px 24 px 24 px ;
2026-05-02 08:26:18 +12:00
align-self : start ;
}
. testimonial-quote-mark {
font-size : 44 px ;
margin-bottom : 8 px ;
}
2026-05-03 11:49:59 +12:00
. testimonial-copy . testimonial-quote {
2026-05-02 08:26:18 +12:00
font-size : 16 px ;
line-height : 1.55 ;
}
. testimonial-divider {
margin-top : 28 px ;
}
2026-05-06 11:36:19 +12:00
. testimonial-mobile-controls {
display : inline-flex ;
align-items : center ;
2026-05-13 09:39:52 +12:00
justify-content : flex-end ;
width : 100 % ;
2026-05-06 11:36:19 +12:00
gap : 12 px ;
2026-05-15 01:28:10 +12:00
margin-top : 16 px ;
2026-05-06 11:36:19 +12:00
}
. testimonial-arrow-inline {
position : static ;
2026-05-13 09:39:52 +12:00
width : 46 px ;
height : 46 px ;
border : none ;
border-radius : 50 % ;
background : rgba ( 33 , 48 , 33 , 0.08 );
color : var ( -- gw - green );
2026-05-06 11:36:19 +12:00
font-size : 18 px ;
transform : none ;
2026-05-13 09:39:52 +12:00
box-shadow : none ;
}
. testimonial-arrow-inline : active {
transform : scale ( 0.95 );
2026-05-06 11:36:19 +12:00
}
2026-05-02 08:26:18 +12:00
. testimonial-google {
2026-05-15 01:28:10 +12:00
margin-top : 16 px ;
2026-05-02 08:26:18 +12:00
font-size : 16 px ;
gap : 10 px ;
padding : 10 px 14 px ;
}
. testimonial-google : global ( . icon ) {
font-size : 20 px ;
}
2026-05-06 11:36:19 +12:00
. testimonial-arrow-left ,
2026-05-02 08:26:18 +12:00
. testimonial-arrow-right {
2026-05-06 11:36:19 +12:00
display : none ;
2026-05-02 08:26:18 +12:00
}
}
</ style >