commit b7ea05f150597869f2094b924ee301954dfdde41 Author: ponzischeme89 Date: Sat May 2 08:26:18 2026 +1200 Initial commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..32e6ac5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +.svelte-kit +build +npm-debug.log* +.git diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..439bcde --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules +.svelte-kit +build +.env +.env.* +npm-debug.log* +migration-backups diff --git a/CLAUDE.MD b/CLAUDE.MD new file mode 100644 index 0000000..e268074 --- /dev/null +++ b/CLAUDE.MD @@ -0,0 +1,94 @@ +# AGENTS.md + +## Goodwalk Consumer facing brand website + +Convert the existing dog walking business website from WordPress/Elementor to a SvelteKit frontend served behind NGINX, with a PostgreSQL backend for editable text/content. + +The visual design must remain almost identical to the current WordPress site. + +# Goodwalk brand colours +#213021 - Goodwalk Green +#FFD100 - Goodwalk Yellow for buttons + +## Non-negotiables + +- Do not redesign the site. +- Preserve the same colours, fonts, spacing, layout, section order, and visual hierarchy. +- Any visual changes must be minimal and justified by technical necessity. +- Match the existing site as closely as possible, including mobile layout. +- Move all CSS into its own organised directory. +- Use SvelteKit for the frontend. +- Use PostgreSQL for editable site text/content. +- Use NGINX as the public web server/reverse proxy. +- Keep the stack suitable for Docker deployment. + +## Frontend requirements + +- Build with SvelteKit. +- Use component-based structure. +- Keep page layout faithful to the WordPress/Elementor original. +- CSS must not be scattered randomly inside components unless scoped component styling is genuinely clearer. +- Create a dedicated CSS structure, for example: + +src/lib/styles/ + base.css + variables.css + layout.css + typography.css + buttons.css + forms.css + sections.css + responsive.css + +- Centralise colours, fonts, spacing, border radius, and shadows in variables. +- Do not introduce a new design system. +- Recreate the existing one cleanly. + +## Backend/content requirements + +- PostgreSQL should store editable text/content. +- Content should be structured so business owners can later edit: + - homepage hero text + - service descriptions + - pricing text if applicable + - about text + - contact details + - call-to-action text + - SEO title/description fields + +## Migration approach + +1. Inspect the existing WordPress/Elementor site. +2. Identify all pages, sections, colours, fonts, spacing, and content blocks. +3. Rebuild the frontend in SvelteKit. +4. Extract repeated UI into reusable components. +5. Move CSS into the dedicated styles directory. +6. Add PostgreSQL-backed content models. +7. Keep the first version visually static if needed, then wire content fields to the database. +8. Do not change wording unless explicitly asked. + +## Quality bar + +- The finished site should look like the current site, not a “modernised” reinterpretation. +- Prioritise visual fidelity over clever abstractions. +- Keep code clean, simple, and easy to maintain. +- Avoid unnecessary dependencies. +- Ensure the site works well on desktop and mobile. +- Use semantic HTML where practical. +- Keep accessibility in mind without altering the visual design. + +## Docker/infra expectation + +The final stack should support: + +- SvelteKit frontend +- backend/API if needed +- PostgreSQL database - Might not be required, maybe a simple json file. +- NGINX reverse proxy +- environment-based configuration +- production-ready Docker Compose setup + +## When uncertain + +If a design detail is unclear, preserve the WordPress/Elementor behaviour as closely as possible. +Do not make creative design decisions without being asked. \ No newline at end of file diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..be8e72b --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,45 @@ +# Deployment + +## What the scripts do + +- `scripts/migrate-wordpress.ps1` + - Dumps the existing WordPress MySQL database to `migration-backups//wordpress.sql` + - Copies `wp-content/uploads` out of the legacy WordPress container into `static/wp-content/uploads` + - Keeps an archive copy of the uploads in `migration-backups//uploads` + +- `scripts/deploy.ps1` + - Optionally runs the migration step first + - Optionally shuts down the legacy compose stack + - Validates the new compose file + - Builds and starts the new stack + - Waits for `http://localhost/api/health` to return success + +## Before cutover + +1. Fill in `.env` from `.env.example` +2. Make sure the legacy WordPress stack is still running +3. Identify: + - the legacy WordPress container name + - the legacy MySQL container name + - the legacy compose file path if you want the deploy script to shut it down for you + - the WordPress MySQL database name, user, and password + +## Example + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\deploy.ps1 ` + -RunMigration ` + -LegacyComposeFile C:\deploy\wordpress\docker-compose.yml ` + -LegacyProjectName goodwalk-wordpress ` + -LegacyWordPressContainer goodwalk-wordpress-1 ` + -LegacyDatabaseContainer goodwalk-db-1 ` + -MySqlDatabase wordpress ` + -MySqlUser wordpress ` + -MySqlPassword 'replace-me' +``` + +## Notes + +- The new app now uses root-relative `/wp-content/uploads/...` paths, so the copied uploads are served by the SvelteKit stack after cutover. +- The deployment script does not destroy the legacy database dump. It writes a fresh backup on every migration run. +- If you want to keep the legacy stack running while testing, omit `-LegacyComposeFile` or add `-SkipLegacyShutdown`. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..daa5c7e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM node:22-alpine AS builder + +WORKDIR /app + +COPY package.json ./ +RUN npm install + +COPY . . +RUN npm run build + +FROM node:22-alpine AS runner + +WORKDIR /app +ENV NODE_ENV=production + +COPY --from=builder /app/package.json ./ +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/build ./build + +EXPOSE 3000 + +CMD ["node", "build"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c78850f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,49 @@ +services: + app: + build: + context: . + environment: + DATABASE_URL: postgresql://${POSTGRES_USER:-goodwalk}:${POSTGRES_PASSWORD:-goodwalk}@db:5432/${POSTGRES_DB:-goodwalk} + NODE_ENV: production + PORT: ${APP_PORT:-3000} + depends_on: + - db + restart: unless-stopped + + mail-api: + build: + context: ./mail-api + environment: + RESEND_API_KEY: ${RESEND_API_KEY} + OWNER_EMAIL: ${OWNER_EMAIL} + FROM_EMAIL: ${FROM_EMAIL:-GoodWalk } + REPLY_TO: ${REPLY_TO:-aless@goodwalk.co.nz} + restart: unless-stopped + + db: + image: postgres:16-alpine + environment: + POSTGRES_DB: ${POSTGRES_DB:-goodwalk} + POSTGRES_USER: ${POSTGRES_USER:-goodwalk} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-goodwalk} + volumes: + - postgres_data:/var/lib/postgresql/data + - ./docker/postgres/init:/docker-entrypoint-initdb.d:ro + restart: unless-stopped + + nginx: + image: nginx:1.27-alpine + depends_on: + - app + - mail-api + ports: + - '80:80' + - '443:443' + volumes: + - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro + - /etc/letsencrypt:/etc/letsencrypt:ro + - /var/www/certbot:/var/www/certbot:ro + restart: unless-stopped + +volumes: + postgres_data: diff --git a/docker/gw-wordpress-composes/mysql/docker-compose.yml b/docker/gw-wordpress-composes/mysql/docker-compose.yml new file mode 100644 index 0000000..3f3d285 --- /dev/null +++ b/docker/gw-wordpress-composes/mysql/docker-compose.yml @@ -0,0 +1,15 @@ +services: + mysql: + image: mysql:8.0 + container_name: mysql + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: "qX7-mK9-vP2-nL4-wR8-xJ5" + volumes: + - /docker/mysql/data:/var/lib/mysql + networks: + - webnet + +networks: + webnet: + external: true diff --git a/docker/gw-wordpress-composes/nginx/docker-compose.yml b/docker/gw-wordpress-composes/nginx/docker-compose.yml new file mode 100644 index 0000000..4b146d1 --- /dev/null +++ b/docker/gw-wordpress-composes/nginx/docker-compose.yml @@ -0,0 +1,19 @@ +services: + nginx: + image: nginx:alpine + container_name: nginx + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - /docker/nginx/conf.d:/etc/nginx/conf.d:ro + - /docker/certbot/conf:/etc/letsencrypt:ro + - /docker/certbot/www:/var/www/certbot:ro + - /docker/wordpress/goodwalk.co.nz/html:/var/www/html:ro + networks: + - webnet + +networks: + webnet: + external: true diff --git a/docker/gw-wordpress-composes/nginx/goodwalk.co.nz.conf b/docker/gw-wordpress-composes/nginx/goodwalk.co.nz.conf new file mode 100644 index 0000000..765ca32 --- /dev/null +++ b/docker/gw-wordpress-composes/nginx/goodwalk.co.nz.conf @@ -0,0 +1,116 @@ +limit_req_zone $binary_remote_addr zone=wp_limit:10m rate=20r/s; + +server { + listen 80; + server_name goodwalk.co.nz www.goodwalk.co.nz; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + try_files $uri =404; + } + + location / { + return 301 https://www.goodwalk.co.nz$request_uri; + } +} + +server { + listen 443 ssl; + server_name goodwalk.co.nz; + ssl_certificate /etc/letsencrypt/live/goodwalk.co.nz/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/goodwalk.co.nz/privkey.pem; + return 301 https://www.goodwalk.co.nz$request_uri; +} + +server { + listen 443 ssl; + server_name www.goodwalk.co.nz; + + ssl_certificate /etc/letsencrypt/live/goodwalk.co.nz/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/goodwalk.co.nz/privkey.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + ssl_session_tickets off; + + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options SAMEORIGIN always; + add_header X-Content-Type-Options nosniff always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss image/svg+xml; + + root /var/www/html; + index index.php; + + location ~* /\.(git|env|htaccess) { + deny all; + } + + location = /xmlrpc.php { + deny all; + } + + location = /wp-login.php { + limit_req zone=wp_limit burst=5 nodelay; + fastcgi_pass wp_goodwalk_co_nz:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name; + fastcgi_param SCRIPT_NAME $fastcgi_script_name; + fastcgi_param QUERY_STRING $query_string; + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param CONTENT_TYPE $content_type; + fastcgi_param CONTENT_LENGTH $content_length; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param DOCUMENT_URI $document_uri; + fastcgi_param DOCUMENT_ROOT /var/www/html; + fastcgi_param SERVER_PROTOCOL $server_protocol; + fastcgi_param GATEWAY_INTERFACE CGI/1.1; + fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + fastcgi_param REMOTE_ADDR $remote_addr; + fastcgi_param SERVER_ADDR $server_addr; + fastcgi_param SERVER_PORT $server_port; + fastcgi_param SERVER_NAME $server_name; + fastcgi_param HTTP_HOST $host; + } + + location / { + try_files $uri $uri/ /index.php?$args; + } + + location ~ \.php$ { + fastcgi_pass wp_goodwalk_co_nz:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name; + fastcgi_param SCRIPT_NAME $fastcgi_script_name; + fastcgi_param QUERY_STRING $query_string; + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param CONTENT_TYPE $content_type; + fastcgi_param CONTENT_LENGTH $content_length; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param DOCUMENT_URI $document_uri; + fastcgi_param DOCUMENT_ROOT /var/www/html; + fastcgi_param SERVER_PROTOCOL $server_protocol; + fastcgi_param GATEWAY_INTERFACE CGI/1.1; + fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + fastcgi_param REMOTE_ADDR $remote_addr; + fastcgi_param SERVER_ADDR $server_addr; + fastcgi_param SERVER_PORT $server_port; + fastcgi_param SERVER_NAME $server_name; + fastcgi_param HTTP_HOST $host; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + add_header Access-Control-Allow-Origin "*"; + log_not_found off; + } +} diff --git a/docker/gw-wordpress-composes/wordpress/docker-compose.yml b/docker/gw-wordpress-composes/wordpress/docker-compose.yml new file mode 100644 index 0000000..99ddcce --- /dev/null +++ b/docker/gw-wordpress-composes/wordpress/docker-compose.yml @@ -0,0 +1,18 @@ +services: + wordpress_goodwalk_co_nz: + image: wordpress:php8.2-fpm + container_name: wp_goodwalk_co_nz + restart: unless-stopped + environment: + WORDPRESS_DB_HOST: mysql + WORDPRESS_DB_NAME: wp_sx2iw + WORDPRESS_DB_USER: wp_zl29k + WORDPRESS_DB_PASSWORD: 2@T0JdS1lm^c*Q~J + volumes: + - /docker/wordpress/goodwalk.co.nz/html:/var/www/html + networks: + - webnet + +networks: + webnet: + external: true diff --git a/docker/postgres/init/001-site-content.sql b/docker/postgres/init/001-site-content.sql new file mode 100644 index 0000000..755225f --- /dev/null +++ b/docker/postgres/init/001-site-content.sql @@ -0,0 +1,200 @@ +create table if not exists site_content ( + key text primary key, + value jsonb not null, + updated_at timestamptz not null default now() +); + +insert into site_content (key, value) +values ( + 'homepage', + '{ + "seo": { + "title": "Home | Auckland Dog Walking | Goodwalk", + "description": "At Goodwalk, we offer Tiny Gang pack walks and one on one dog walking services throughout Auckland. Give your dog his best life with Goodwalk!" + }, + "navigation": { + "desktopLinks": [ + { "label": "Our Services", "href": "#services" }, + { "label": "Our Pricing", "href": "/our-pricing" }, + { "label": "About Us", "href": "/about" } + ], + "mobileLinks": [ + { "label": "Home", "href": "/" }, + { "label": "Pack Walks", "href": "/pack-walks" }, + { "label": "1:1 Walks", "href": "/dog-walking" }, + { "label": "Puppy Visits", "href": "/puppy-visits" }, + { "label": "Our Pricing", "href": "/our-pricing" }, + { "label": "About Us", "href": "/about" }, + { "label": "Contact Us", "href": "/booking" } + ], + "cta": { "label": "Book Now", "href": "/booking", "variant": "green" } + }, + "hero": { + "title": "Unleashing Fun in", + "highlight": "Your Dog''s Day!", + "mobileTitle": "Unleashing Fun in\nYour Dog''s Day!", + "primaryCta": { "label": "Learn more", "href": "#services", "variant": "yellow" }, + "secondaryCta": { "label": "Enroll today", "href": "#reservation", "variant": "outline" }, + "imageUrl": "/images/auckland-dog-walking-happy-dog-hero.png", + "imageAlt": "Happy dog on a walk with Goodwalk" + }, + "intro": { + "text": "Goodwalk delivers trusted, professional dog walking services across Auckland Central.", + "reviewCta": { + "label": "All 5 star reviews on Google!", + "href": "https://g.page/r/CUsvrWPhkYrAEB0/", + "external": true + } + }, + "promise": { + "title": "Happy pets,", + "subtitle": "happy humans", + "body": "Offering tailored pack walks for small and medium dogs, and one-on-one walks for large breeds. Our walkers give personalised attention to each dog, easing stress, anxiety and ensuring a quality experience. Our expertise in small-medium breeds ensures tailored care for their unique needs. Join our", + "emphasis": "TINY GANG!", + "cta": { "label": "Book now", "href": "#reservation", "variant": "green" }, + "imageUrl": "/images/auckland-dog-walking-happy-dogs-happy-humans.png", + "imageAlt": "Woman cuddling a dog for Goodwalk Auckland dog walking services" + }, + "services": [ + { + "icon": "fas fa-dog", + "title": "Pack Walks", + "body": "Small group walks of 4-8 dogs - calm, social, and full of fun for your pup.", + "href": "/pack-walks" + }, + { + "icon": "fas fa-person-walking", + "title": "1:1 Walks", + "body": "One-on-one walks tailored to your dog''s individual pace, personality, and needs.", + "href": "/dog-walking" + }, + { + "icon": "fas fa-house", + "title": "Puppy Visits", + "body": "In-home visits to check in on your puppy, play, and keep them company.", + "href": "/puppy-visits" + } + ], + "values": [ + { + "icon": "fas fa-heart", + "title": "Kindness", + "body": "With gentle care and genuine affection, we make every walk a calm, happy experience. We use positive reinforcement to encourage good behavior because kindness is at the heart of everything we do." + }, + { + "icon": "fas fa-camera", + "title": "Daily Updates", + "body": "Catch your pup in action with daily social updates - showcasing their walks, playtime, and mischief with the Tiny Gang. It''s your window into their happiest moments." + }, + { + "icon": "fas fa-users", + "title": "Small Pack Sizes", + "body": "With just 4-8 dogs per group, our walks are calm, controlled, and respectful of public spaces - ensuring every dog gets the attention and care they deserve." + }, + { + "icon": "fas fa-shield-heart", + "title": "Safety", + "body": "Our team is fully pet first aid certified and trained to handle any situation calmly and confidently. With proactive safety protocols and constant situational awareness, we create a secure environment for every walk." + }, + { + "icon": "fas fa-calendar-check", + "title": "Flexibility", + "body": "We know life gets busy - so while we specialise in regular, permanent walks, we''re always happy to adapt. Just give us a little notice, and we''ll do our best to accommodate your changing schedule." + }, + { + "icon": "fas fa-clock", + "title": "Reliability", + "body": "We guarantee punctuality and consistency, so you can count on us. With clear communication, you''ll always be in the loop - and your dog''s needs will always be our top priority." + } + ], + "testimonials": [ + { + "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.", + "reviewer": "Kate", + "detail": "Archie''s mum", + "imageUrl": "/images/archie-auckland-dog-walking-review.jpg" + }, + { + "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!", + "reviewer": "Estelle", + "detail": "Monty''s mum", + "imageUrl": "/images/monty-auckland-dog-walking-review.jpg" + }, + { + "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!", + "reviewer": "Ross", + "detail": "Otis''s Dad", + "imageUrl": "/images/otis-auckland-dog-walking-review.jpg" + }, + { + "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", + "reviewer": "Nina", + "detail": "Wallace''s mum", + "imageUrl": "/images/wallace-auckland-dog-walking-review.jpg" + } + ], + "booking": { + "title": "Let''s meet!", + "subtitle": "Ready to get started? Book your free, no-obligation Meet & Greet today — just enter your details below", + "formAction": "/booking", + "serviceOptions": ["Pack Walks", "1:1 Walks", "Puppy Visits", "Other Services"] + }, + "info": { + "title": "Locations & Hours", + "intro": "We cover most of Auckland Central''s suburbs:", + "suburbs": "Morningside, Kingsland, Ponsonby, Grey Lynn, Mt Albert, Mt Eden, Sandringham, Mt Roskill, Arch Hill, Freemans Bay, Herne Bay, Pt Chevalier, Avondale, Three Kings, Hillsborough, Eden Terrace, Balmoral.", + "nearbyText": "Live in a nearby suburb?", + "nearbyCta": { "label": "Get in touch!", "href": "#reservation" }, + "hoursLabel": "Opening Hours", + "hours": "Monday to Friday, 8am - 4pm.", + "faqTitle": "FAQ''s", + "faqs": [ + { + "question": "What happens if the weather is bad?", + "answer": "We operate in all weather conditions, except when there is a danger to the dog''s health and safety." + }, + { + "question": "What requirements does my dog need?", + "answer": "All dogs onboarding with Goodwalk need to have a current Auckland Council dog registration and be up to date with vaccinations to ensure the health and safety of other dogs." + }, + { + "question": "Can any dog use your service?", + "answer": "All dogs that are onboarded with us must go through our screening process, which includes a minimum of two assessment walks." + }, + { + "question": "How does payment work?", + "answer": "All walks are paid for a week in advance, via invoice." + }, + { + "question": "Do you have insurance or First Aid training?", + "answer": "All walkers are covered by public liability insurance, and all walkers hold a current First Aid training certificate." + } + ] + }, + "instagram": { + "title": "Follow us on Instagram", + "label": "@goodwalk.nz", + "href": "https://www.instagram.com/goodwalk.nz/", + "variant": "green", + "external": true + }, + "footer": { + "brandText": "Professional Dog Walking Services\nAuckland Central", + "navigationLinks": [ + { "label": "Home", "href": "/" }, + { "label": "Pack Walks", "href": "/pack-walks" }, + { "label": "1:1 Walks", "href": "/dog-walking" }, + { "label": "Puppy Visits", "href": "/puppy-visits" }, + { "label": "Our Pricing", "href": "/our-pricing" }, + { "label": "Contact Us", "href": "/booking" } + ], + "contactLinks": [ + { "label": "Book a walk", "href": "/booking" }, + { "label": "Instagram", "href": "https://www.instagram.com/goodwalk.nz/", "external": true }, + { "label": "Google Reviews", "href": "https://g.page/r/CUsvrWPhkYrAEB0", "external": true } + ], + "copyright": "© 2024 Goodwalk. All rights reserved." + } + }'::jsonb +) +on conflict (key) do nothing; diff --git a/index.html b/index.html new file mode 100644 index 0000000..2949c02 --- /dev/null +++ b/index.html @@ -0,0 +1,769 @@ + + + + + + Home | Auckland Dog Walking | Goodwalk + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+
+

Unleashing Fun in
Your Dog's Day!

+ +
+
+ Happy dog on a walk with Goodwalk +
+
+
+ + +
+

Goodwalk delivers trusted, professional dog walking services across Auckland Central.

+ + All 5 star reviews on Google! + +
+ + +
+
+
+

Happy pets,
happy humans

+

Offering tailored pack walks for small & medium dogs, and one-on-one walks for large breeds! Our walkers give personalized attention to each dog, easing stress, anxiety and ensuring a quality experience. Our expertise in small-medium breeds ensures tailored care for their unique needs. Join our TINY GANG!

+ Book now +
+
+ Dog enjoying a walk with Goodwalk +
+
+
+ + +
+
+

What we do

+
+
+ +

Pack Walks

+

Small group walks of 4–8 dogs — calm, social, and full of fun for your pup.

+
+ Learn more → +
+
+ +

1:1 Walks

+

One-on-one walks tailored to your dog's individual pace, personality, and needs.

+
+ Learn more → +
+
+ +

Puppy Visits

+

In-home visits to check in on your puppy, play, and keep them company.

+
+ Learn more → +
+
+
+
+ + +
+
+

Where dogs come first

+
+
+ +

Kindness

+

With gentle care and genuine affection, we make every walk a calm, happy experience. We use positive reinforcement to encourage good behavior — because kindness is at the heart of everything we do.

+
+
+ +

Daily Updates

+

Catch your pup in action with daily social updates — showcasing their walks, playtime, and mischief with the Tiny Gang. It's your window into their happiest moments.

+
+
+ +

Small Pack Sizes

+

With just 4–8 dogs per group, our walks are calm, controlled, and respectful of public spaces — ensuring every dog gets the attention and care they deserve.

+
+
+ +

Safety

+

Our team is fully pet first aid certified and trained to handle any situation calmly and confidently. With proactive safety protocols and constant situational awareness, we create a secure environment for every walk.

+
+
+ +

Flexibility

+

We know life gets busy — so while we specialise in regular, permanent walks, we're always happy to adapt. Just give us a little notice, and we'll do our best to accommodate your changing schedule.

+
+
+ +

Reliability

+

We guarantee punctuality and consistency, so you can count on us. With clear communication, you'll always be in the loop — and your dog's needs will always be our top priority.

+
+
+
+
+ + +
+
+

Why people choose us!

+
+
+
+
"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 loves his walks, and I love the photos she posts of him."
+

Kate — Archie's mum

+
+
+
+
"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, which I feel is what every dog mum wants and needs!"
+

Estelle — Monty's mum

+
+
+
+
"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! Highly highly recommend."
+

Ross — Otis's dad

+
+
+
+
"Alessandra has been walking and spending time with my pup since she was 10 weeks old, coming over for puppy visits through to transitioning her to pack walks with her little doggo friends. I know Alessandra loves and cares for my dog as much as I do. Can't recommend enough!"
+

Nina — Wallace's mum

+
+
+
+
+ + +
+
+

Let's meet!

+

Tell us about yourself and your dog and we'll be in touch.

+ +
+
+
1. Your Details
+
2. Your Dog
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+ +
+ + + + + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+ + +
+
+
+
+
+ + +
+
+ +
+

Locations & Hours

+

We cover most of Auckland Central's suburbs:

+

Morningside, Kingsland, Ponsonby, Grey Lynn, Mt Albert, Mt Eden, Sandringham, Mt Roskill, Arch Hill, Freemans Bay, Herne Bay, Pt Chevalier, Avondale, Three Kings, Hillsborough, Eden Terrace, Balmoral.

+

Live in a nearby suburb? Get in touch!

+

Opening Hours

+

Monday to Friday, 8am – 4pm.

+
+ +
+

FAQ's

+
+
+ What happens if the weather is bad? +

We operate in all weather conditions, except when there is a danger to the dog's health & safety.

+
+
+ What requirements does my dog need? +

All dogs onboarding with Goodwalk need to have a current Auckland Council dog registration and be up to date with vaccinations to ensure the health and safety of other dogs.

+
+
+ Can any dog use your service? +

All dogs that are onboarded with us must go through our screening process, which includes a minimum of two assessment walks.

+
+
+ How does payment work? +

All walks are paid for a week in advance, via invoice.

+
+
+ Do you have insurance or First Aid training? +

All walkers are covered by public liability insurance, and all walkers hold a current First Aid training certificate.

+
+
+
+ +
+
+ + +
+

Follow us on Instagram

+ + @goodwalk.nz + +
+ + + + + + +OK","has_counter":true},"tumblr":{"title":"Tumblr"},"digg":{"title":"Digg"},"skype":{"title":"Skype"},"stumbleupon":{"title":"StumbleUpon","has_counter":true},"mix":{"title":"Mix"},"telegram":{"title":"Telegram"},"pocket":{"title":"Pocket","has_counter":true},"xing":{"title":"XING","has_counter":true},"whatsapp":{"title":"WhatsApp"},"email":{"title":"Email"},"print":{"title":"Print"}},"facebook_sdk":{"lang":"en_NZ","app_id":""},"lottie":{"defaultAnimationUrl":"https:\/\/www.goodwalk.co.nz\/wp-content\/plugins\/elementor-pro\/modules\/lottie\/assets\/animations\/default.json"}}; +//# sourceURL=elementor-pro-frontend-js-before +/* ]]> */ + + + + + + + diff --git a/mail-api/Dockerfile b/mail-api/Dockerfile new file mode 100644 index 0000000..330f254 --- /dev/null +++ b/mail-api/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY main.py . + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/mail-api/__pycache__/main.cpython-313.pyc b/mail-api/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..604215d Binary files /dev/null and b/mail-api/__pycache__/main.cpython-313.pyc differ diff --git a/mail-api/main.py b/mail-api/main.py new file mode 100644 index 0000000..1f470b7 --- /dev/null +++ b/mail-api/main.py @@ -0,0 +1,459 @@ +import os +import logging +from datetime import datetime + +import resend +from fastapi import FastAPI, HTTPException, Request +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel, EmailStr + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +resend.api_key = os.environ["RESEND_API_KEY"] + +OWNER_EMAIL = os.environ["OWNER_EMAIL"] +FROM_EMAIL = os.environ.get("FROM_EMAIL", "GoodWalk ") +REPLY_TO = os.environ.get("REPLY_TO", "aless@goodwalk.co.nz") + +LOGO_URL = "https://www.goodwalk.co.nz/wp-content/uploads/2022/06/logo-v6.png" + +app = FastAPI() + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_methods=["POST"], + allow_headers=["*"], +) + + +class BookingSubmission(BaseModel): + fullName: str + email: EmailStr + phone: str + petName: str + location: str + message: str = "" + services: list[str] = [] + referrer: str = "" + page: str = "" + + +# ── Helpers ────────────────────────────────────────────────────────────────── + +def _get_ip(request: Request) -> str: + forwarded = request.headers.get("x-forwarded-for") + if forwarded: + return forwarded.split(",")[0].strip() + return request.client.host if request.client else "unknown" + + +def _parse_ua(ua: str) -> str: + if not ua: + return "Unknown" + browsers = [("Edg/", "Edge"), ("OPR/", "Opera"), ("Chrome/", "Chrome"), + ("Firefox/", "Firefox"), ("Safari/", "Safari")] + systems = [("Windows NT 10", "Windows 10/11"), ("Windows NT 6", "Windows 8"), + ("Mac OS X", "macOS"), ("iPhone", "iPhone"), ("iPad", "iPad"), + ("Android", "Android"), ("Linux", "Linux")] + browser = next((n for p, n in browsers if p in ua), "Unknown browser") + system = next((n for p, n in systems if p in ua), "Unknown OS") + return f"{browser} on {system}" + + +def _detail_row(label: str, value: str) -> str: + if not value: + return "" + return f""" + + {label} + {value} + """ + + +def _meta_row(label: str, value: str) -> str: + if not value: + return "" + return f""" + + {label} + {value} + """ + + +# ── Email templates ────────────────────────────────────────────────────────── + +def _logo_header(badge_html: str = "", subtitle: str = "") -> str: + badge = f'
{badge_html}
' if badge_html else "" + sub = f"""
+ {subtitle}
""" if subtitle else "" + return f""" + + + GoodWalk + {sub} + {badge} + + """ + + +def client_email(data: BookingSubmission) -> str: + services_text = ", ".join(data.services) if data.services else "Not specified" + message_row = _detail_row("About the dog", data.message) if data.message else "" + + return f""" + + + + + We received your enquiry + + + + + +
+ + + + {_logo_header(subtitle="Auckland’s favourite dog walking service")} + + + + + + + + + + + +
+ +

+ Thanks, {data.fullName.split()[0]}! 🐾 +

+

+ We’ve received your enquiry and Aless will be in touch shortly to arrange + a Meet & Greet with you and + {data.petName}. +

+ + + + + + +
+
+ Your enquiry summary +
+ + {_detail_row("Your name", data.fullName)} + {_detail_row("Email", data.email)} + {_detail_row("Phone", data.phone)} + {_detail_row("Dog’s name", data.petName)} + {_detail_row("Location", data.location)} + {_detail_row("Services", services_text)} + {message_row} +
+
+ + + + + + +
+
+ What happens next? +
+
+ Aless will review your details and reach out within 1–2 business days + to schedule a free Meet & Greet. No commitment required — just a + chance for {data.petName} to make a new best friend. +
+
+ +

+ Questions? Just reply to this email or reach us at + {REPLY_TO}. +

+ +
+
+ GoodWalk · Auckland, New Zealand
+ + goodwalk.co.nz + +
+
+
+ + +""" + + +def owner_email(data: BookingSubmission, ip: str, browser: str) -> str: + services_text = ", ".join(data.services) if data.services else "—" + now = datetime.now() + submitted_at = now.strftime("%d %b %Y at %I:%M %p").lstrip("0") + + message_block = f""" + + +
About the dog
+
{data.message}
+ + """ if data.message else "" + + badge = """
+ + 📩  New lead! + +
+
+ Submitted {submitted_at} +
""".format(submitted_at=submitted_at) + + referrer_row = _meta_row("Came from", data.referrer) if data.referrer else _meta_row("Came from", "Direct / bookmark") + page_row = _meta_row("Page", data.page) if data.page else "" + + return f""" + + + + + New GoodWalk Lead + + + + + +
+ + + + {_logo_header(badge_html=badge)} + + + + + + + + + + + +
+ + +
Owner details
+ + + +
+ + + + + + + + + + + + + +
Name{data.fullName}
Email + {data.email} +
Phone + {data.phone} +
+
+ + +
Dog & services
+ + + +
+ + + + + + + + + + + + + + {message_block} +
Dog{data.petName}
Location{data.location}
Services{services_text}
+
+ + + + + + + +
+ + Reply to {data.fullName.split()[0]} + + + + Call {data.phone} + +
+ + + + +
+
Session info
+ + {_meta_row("IP address", ip)} + {_meta_row("Browser", browser)} + {referrer_row} + {page_row} +
+
+ +
+
+ Sent automatically by GoodWalk booking form +
+
+
+ + +""" + + +# ── Route ──────────────────────────────────────────────────────────────────── + +@app.post("/submit") +async def submit_booking(data: BookingSubmission, request: Request): + ip = _get_ip(request) + browser = _parse_ua(request.headers.get("user-agent", "")) + logger.info("Booking from %s (%s, %s) for dog %s", data.email, ip, browser, data.petName) + + errors = [] + + try: + resend.Emails.send({ + "from": FROM_EMAIL, + "to": [data.email], + "reply_to": REPLY_TO, + "subject": f"We received your enquiry, {data.fullName.split()[0]}! 🐾", + "html": client_email(data), + }) + logger.info("Client confirmation sent to %s", data.email) + except Exception as exc: + logger.error("Failed to send client email: %s", exc) + errors.append("client_email") + + try: + resend.Emails.send({ + "from": FROM_EMAIL, + "to": [OWNER_EMAIL], + "reply_to": data.email, + "subject": f"New GoodWalk lead — {data.fullName} ({data.petName})", + "html": owner_email(data, ip, browser), + }) + logger.info("Owner notification sent to %s", OWNER_EMAIL) + except Exception as exc: + logger.error("Failed to send owner email: %s", exc) + errors.append("owner_email") + + if "client_email" in errors and "owner_email" in errors: + raise HTTPException(status_code=500, detail="Failed to send emails. Please try again.") + + return {"ok": True} diff --git a/mail-api/requirements.txt b/mail-api/requirements.txt new file mode 100644 index 0000000..cd7bfe1 --- /dev/null +++ b/mail-api/requirements.txt @@ -0,0 +1,4 @@ +fastapi>=0.115 +uvicorn[standard]>=0.32 +resend>=2.0 +pydantic[email]>=2.10 diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..371ee75 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,51 @@ +# Redirect all HTTP to HTTPS, except ACME challenges (certbot renewal) +server { + listen 80; + server_name goodwalk.co.nz www.goodwalk.co.nz; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_name goodwalk.co.nz www.goodwalk.co.nz; + + ssl_certificate /etc/letsencrypt/live/goodwalk.co.nz/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/goodwalk.co.nz/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options SAMEORIGIN always; + add_header X-Content-Type-Options nosniff always; + add_header Referrer-Policy strict-origin-when-cross-origin always; + + gzip on; + gzip_types text/plain text/css application/javascript application/json image/svg+xml; + + location /api/submit { + proxy_pass http://mail-api:8000/submit; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + proxy_pass http://app:3000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6b1764b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3238 @@ +{ + "name": "goodwalk-svelte-port", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "goodwalk-svelte-port", + "version": "0.1.0", + "dependencies": { + "canvas-confetti": "^1.9.4", + "pg": "^8.13.1" + }, + "devDependencies": { + "@sveltejs/adapter-node": "^5.2.11", + "@sveltejs/kit": "^2.17.1", + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/svelte": "^5.3.1", + "@types/canvas-confetti": "^1.9.0", + "@types/node": "^22.10.5", + "@types/pg": "^8.11.10", + "jsdom": "^29.1.1", + "svelte": "^5.16.0", + "svelte-check": "^4.1.1", + "typescript": "^5.6.3", + "vite": "^6.0.7", + "vitest": "^4.1.5" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz", + "integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz", + "integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.2.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz", + "integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "29.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.2.tgz", + "integrity": "sha512-S/ggWH1LU7jTyi9DxZOKyxpVd4hF/OZ0JrEbeLjXk/DFXwRny0tjD2c992zOUYQobLrVkRVMDdmHP16HKP7GRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", + "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", + "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-node": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.5.4.tgz", + "integrity": "sha512-45X92CXW+2J8ZUzPv3eLlKWEzINKiiGeFWTjyER4ZN4sGgNoaoeSkCY/QYNxHpPXy71QPsctwccBo9jJs0ySPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-commonjs": "^29.0.0", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.0", + "rollup": "^4.59.0" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.4.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.58.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.58.0.tgz", + "integrity": "sha512-kT9GCN8yJTkCK1W+Gi/bvGooWAM7y7WXP+yd+rf6QOIjyoK1ERPrMwSufXJUNu2pMWIqruhFvmz+LbOqsEmKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", + "cookie": "^0.6.0", + "devalue": "^5.6.4", + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "set-cookie-parser": "^3.0.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": "^5.3.3 || ^6.0.0", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.1.1.tgz", + "integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", + "debug": "^4.4.1", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.17", + "vitefu": "^1.0.6" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite": "^6.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-4.0.1.tgz", + "integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.7" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "svelte": "^5.0.0", + "vite": "^6.0.0" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/dom/node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/svelte": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/svelte/-/svelte-5.3.1.tgz", + "integrity": "sha512-8Ez7ZOqW5geRf9PF5rkuopODe5RGy3I9XR+kc7zHh26gBiktLaxTfKmhlGaSHYUOTQE7wFsLMN9xCJVCszw47w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@testing-library/dom": "9.x.x || 10.x.x", + "@testing-library/svelte-core": "1.0.0" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", + "vite": "*", + "vitest": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@testing-library/svelte-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/svelte-core/-/svelte-core-1.0.0.tgz", + "integrity": "sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/canvas-confetti": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@types/canvas-confetti/-/canvas-confetti-1.9.0.tgz", + "integrity": "sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", + "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/pg": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/expect": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", + "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", + "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", + "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.5", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", + "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "@vitest/utils": "4.1.5", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aria-query": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/canvas-confetti": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.4.tgz", + "integrity": "sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==", + "license": "ISC", + "funding": { + "type": "donate", + "url": "https://www.paypal.me/kirilvatev" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devalue": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz", + "integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esrap": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.5.tgz", + "integrity": "sha512-/yLB1538mag+dn0wsePTe8C0rDIjUOaJpMs2McodSzmM2msWcZsBSdRtg6HOBt0A/r82BN+Md3pgwSc/uWt2Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "peerDependencies": { + "@typescript-eslint/types": "^8.2.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/types": { + "optional": true + } + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pg": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.12.0", + "pg-pool": "^3.13.0", + "pg-protocol": "^1.13.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.3.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", + "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz", + "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz", + "integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz", + "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rollup": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", + "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.2", + "@rollup/rollup-android-arm64": "4.60.2", + "@rollup/rollup-darwin-arm64": "4.60.2", + "@rollup/rollup-darwin-x64": "4.60.2", + "@rollup/rollup-freebsd-arm64": "4.60.2", + "@rollup/rollup-freebsd-x64": "4.60.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", + "@rollup/rollup-linux-arm-musleabihf": "4.60.2", + "@rollup/rollup-linux-arm64-gnu": "4.60.2", + "@rollup/rollup-linux-arm64-musl": "4.60.2", + "@rollup/rollup-linux-loong64-gnu": "4.60.2", + "@rollup/rollup-linux-loong64-musl": "4.60.2", + "@rollup/rollup-linux-ppc64-gnu": "4.60.2", + "@rollup/rollup-linux-ppc64-musl": "4.60.2", + "@rollup/rollup-linux-riscv64-gnu": "4.60.2", + "@rollup/rollup-linux-riscv64-musl": "4.60.2", + "@rollup/rollup-linux-s390x-gnu": "4.60.2", + "@rollup/rollup-linux-x64-gnu": "4.60.2", + "@rollup/rollup-linux-x64-musl": "4.60.2", + "@rollup/rollup-openbsd-x64": "4.60.2", + "@rollup/rollup-openharmony-arm64": "4.60.2", + "@rollup/rollup-win32-arm64-msvc": "4.60.2", + "@rollup/rollup-win32-ia32-msvc": "4.60.2", + "@rollup/rollup-win32-x64-gnu": "4.60.2", + "@rollup/rollup-win32-x64-msvc": "4.60.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/set-cookie-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svelte": { + "version": "5.55.5", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.5.tgz", + "integrity": "sha512-2uCs/LZ9us+AktdzYJM8OcxQ8qnPS1kpaO7syGT/MgO+6Qr1Ybl+TqPq+97u7PHqmmMlye5ZkoyXONy5mjjAbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/estree": "^1.0.5", + "@types/trusted-types": "^2.0.7", + "acorn": "^8.12.1", + "aria-query": "5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.6.4", + "esm-env": "^1.2.1", + "esrap": "^2.2.4", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-check": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.4.6.tgz", + "integrity": "sha512-kP1zG81EWaFe9ZyTv4ZXv44Csi6Pkdpb7S3oj6m+K2ec/IcDg/a8LsFsnVLqm2nxtkSwsd5xPj/qFkTBgXHXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "node_modules/svelte/node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.29.tgz", + "integrity": "sha512-JIXCerhudr/N6OWLwLF1HVsTTUo7ry6qHa5eWZEkiMuxsIiAACL55tGLfqfHfoH7QaMQUW8fngD7u7TxWexYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.29" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.29.tgz", + "integrity": "sha512-W99NuU7b1DcG3uJ3v9k9VztCH3WialNbBkBft5wCs8V8mexu0XQqaZEYb9l9RNNzK8+3EJ9PKWB0/RUtTQ/o+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", + "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.5", + "@vitest/mocker": "4.1.5", + "@vitest/pretty-format": "4.1.5", + "@vitest/runner": "4.1.5", + "@vitest/snapshot": "4.1.5", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.5", + "@vitest/browser-preview": "4.1.5", + "@vitest/browser-webdriverio": "4.1.5", + "@vitest/coverage-istanbul": "4.1.5", + "@vitest/coverage-v8": "4.1.5", + "@vitest/ui": "4.1.5", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c56103c --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "goodwalk-svelte-port", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev --host 10.0.0.73", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "test": "vitest run", + "test:watch": "vitest" + }, + "dependencies": { + "canvas-confetti": "^1.9.4", + "pg": "^8.13.1" + }, + "devDependencies": { + "@sveltejs/adapter-node": "^5.2.11", + "@sveltejs/kit": "^2.17.1", + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/svelte": "^5.3.1", + "@types/canvas-confetti": "^1.9.0", + "@types/node": "^22.10.5", + "@types/pg": "^8.11.10", + "jsdom": "^29.1.1", + "svelte": "^5.16.0", + "svelte-check": "^4.1.1", + "typescript": "^5.6.3", + "vite": "^6.0.7", + "vitest": "^4.1.5" + } +} diff --git a/post-1564.css b/post-1564.css new file mode 100644 index 0000000..b054bb4 --- /dev/null +++ b/post-1564.css @@ -0,0 +1,21 @@ +.elementor-1564 .elementor-element.elementor-element-6bf5de6 > .elementor-container{min-height:80vh;}.elementor-1564 .elementor-element.elementor-element-6bf5de6:not(.elementor-motion-effects-element-type-background), .elementor-1564 .elementor-element.elementor-element-6bf5de6 > .elementor-motion-effects-container > .elementor-motion-effects-layer{background-color:#213021;}.elementor-1564 .elementor-element.elementor-element-6bf5de6{transition:background 0.3s, border 0.3s, border-radius 0.3s, box-shadow 0.3s;margin-top:-105px;margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-6bf5de6 > .elementor-background-overlay{transition:background 0.3s, border-radius 0.3s, opacity 0.3s;}.elementor-bc-flex-widget .elementor-1564 .elementor-element.elementor-element-4651da9.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-1564 .elementor-element.elementor-element-4651da9.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-1564 .elementor-element.elementor-element-4651da9 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:50px;}.elementor-1564 .elementor-element.elementor-element-4651da9 > .elementor-element-populated{margin:0px 0px 0px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:0px 1px 20px 1px;}.elementor-1564 .elementor-element.elementor-element-b754a13 .elementor-heading-title{color:#FFFFFF;font-family:var( --e-global-typography-vamtam_h1-font-family ), Sans-serif;font-size:var( --e-global-typography-vamtam_h1-font-size );font-weight:var( --e-global-typography-vamtam_h1-font-weight );text-transform:var( --e-global-typography-vamtam_h1-text-transform );line-height:var( --e-global-typography-vamtam_h1-line-height );}.elementor-1564 .elementor-element.elementor-element-b754a13 > .elementor-widget-container{padding:50px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-b754a13.elementor-element{--align-self:flex-start;--flex-grow:0;--flex-shrink:1;}.elementor-1564 .elementor-element.elementor-element-ac8ee2b .elementor-button{fill:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_6 );background-color:var( --e-global-color-vamtam_accent_4 );}.elementor-1564 .elementor-element.elementor-element-ac8ee2b{width:auto;max-width:auto;}.elementor-1564 .elementor-element.elementor-element-53ea529 .elementor-button{fill:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_6 );background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-53ea529 > .elementor-widget-container{margin:0px 0px 0px 15px;}.elementor-1564 .elementor-element.elementor-element-53ea529{width:auto;max-width:auto;}.elementor-1564 .elementor-element.elementor-element-1a029bc{--spacer-size:50px;}.elementor-bc-flex-widget .elementor-1564 .elementor-element.elementor-element-65ef1c5.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-1564 .elementor-element.elementor-element-65ef1c5.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-1564 .elementor-element.elementor-element-65ef1c5 > .elementor-element-populated{transition:background 0.3s, border 0.3s, border-radius 0.3s, box-shadow 0.3s;margin:120px 0px 0px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;}.elementor-1564 .elementor-element.elementor-element-65ef1c5 > .elementor-element-populated > .elementor-background-overlay{transition:background 0.3s, border-radius 0.3s, opacity 0.3s;}.elementor-1564 .elementor-element.elementor-element-f3f0e11 img{border-radius:00000000px 00000000px 00000000px 00000000px;}.elementor-1564 .elementor-element.elementor-element-f3f0e11 > .elementor-widget-container{margin:0px 0px -10px -30px;}.elementor-1564 .elementor-element.elementor-element-f3f0e11{width:auto;max-width:auto;align-self:flex-end;}.elementor-1564 .elementor-element.elementor-element-f474cc3 > .elementor-container{min-height:80vh;}.elementor-1564 .elementor-element.elementor-element-f474cc3:not(.elementor-motion-effects-element-type-background), .elementor-1564 .elementor-element.elementor-element-f474cc3 > .elementor-motion-effects-container > .elementor-motion-effects-layer{background-color:#213021;}.elementor-1564 .elementor-element.elementor-element-f474cc3{transition:background 0.3s, border 0.3s, border-radius 0.3s, box-shadow 0.3s;margin-top:-105px;margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-f474cc3 > .elementor-background-overlay{transition:background 0.3s, border-radius 0.3s, opacity 0.3s;}.elementor-1564 .elementor-element.elementor-element-57b2228 .elementor-heading-title{color:var( --e-global-color-vamtam_accent_5 );}.elementor-1564 .elementor-element.elementor-element-8eb92d6 .elementor-button{fill:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_6 );background-color:var( --e-global-color-vamtam_accent_4 );}.elementor-1564 .elementor-element.elementor-element-8eb92d6{width:auto;max-width:auto;}.elementor-1564 .elementor-element.elementor-element-f497fe5 .elementor-button .elementor-align-icon-right{margin-left:0px;}.elementor-1564 .elementor-element.elementor-element-f497fe5 .elementor-button .elementor-align-icon-left{margin-right:0px;}.elementor-1564 .elementor-element.elementor-element-f497fe5 .elementor-button{fill:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_6 );background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-f497fe5{width:auto;max-width:auto;}.elementor-1564 .elementor-element.elementor-element-cc6139b{margin-top:20px;margin-bottom:140px;}.elementor-1564 .elementor-element.elementor-element-cee656c > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-fb8c1cf{font-family:var( --e-global-typography-vamtam_h6-font-family ), Sans-serif;font-size:var( --e-global-typography-vamtam_h6-font-size );font-weight:var( --e-global-typography-vamtam_h6-font-weight );line-height:var( --e-global-typography-vamtam_h6-line-height );}.elementor-1564 .elementor-element.elementor-element-90ee838 .elementor-icon-list-icon i{color:#E00706;transition:color 0.3s;}.elementor-1564 .elementor-element.elementor-element-90ee838 .elementor-icon-list-icon svg{fill:#E00706;transition:fill 0.3s;}.elementor-1564 .elementor-element.elementor-element-90ee838{--e-icon-list-icon-size:20px;--icon-vertical-offset:0px;width:auto;max-width:auto;}.elementor-1564 .elementor-element.elementor-element-90ee838 .elementor-icon-list-icon{padding-right:0px;}.elementor-1564 .elementor-element.elementor-element-90ee838 .elementor-icon-list-text{transition:color 0.3s;}.elementor-1564 .elementor-element.elementor-element-90ee838 > .elementor-widget-container{margin:0px 0px 0px -10px;padding:5px 15px 5px 15px;background-color:var( --e-global-color-vamtam_accent_5 );border-radius:18px 18px 18px 18px;}.elementor-1564 .elementor-element.elementor-element-90ee838:hover .elementor-widget-container{background-color:var( --e-global-color-vamtam_accent_4 );}.elementor-1564 .elementor-element.elementor-element-c9abac0 > .elementor-element-populated{padding:0px 0px 0px 60px;}.elementor-1564 .elementor-element.elementor-element-5a8121e .elementor-icon-wrapper{text-align:center;}.elementor-1564 .elementor-element.elementor-element-5a8121e.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-5a8121e.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-5a8121e.elementor-view-default .elementor-icon{color:var( --e-global-color-vamtam_accent_1 );border-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-5a8121e.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-5a8121e.elementor-view-default .elementor-icon svg{fill:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-5a8121e .elementor-icon{font-size:120px;}.elementor-1564 .elementor-element.elementor-element-5a8121e .elementor-icon svg{height:120px;}.elementor-1564 .elementor-element.elementor-element-5a8121e{width:auto;max-width:auto;top:12%;z-index:1;}body:not(.rtl) .elementor-1564 .elementor-element.elementor-element-5a8121e{left:-10px;}body.rtl .elementor-1564 .elementor-element.elementor-element-5a8121e{right:-10px;}.elementor-1564 .elementor-element.elementor-element-ad07384 img{width:100%;max-width:560px;border-radius:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-458cac8 img{width:100%;max-width:62%;border-radius:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-458cac8{--vamtam-fg-layer-bg-color:var( --e-global-color-vamtam_accent_4 );--vamtam-fg-layer-mask-image:url('https://petmania.vamtam.com/wp-content/uploads/2022/06/bg-oval-shape.svg');--vamtam-fg-layer-mask-size:contain;--vamtam-fg-layer-mask-position:top center;--vamtam-fg-layer-mask-repeat:no-repeat;top:0px;z-index:-1;}body:not(.rtl) .elementor-1564 .elementor-element.elementor-element-458cac8{left:0px;}body.rtl .elementor-1564 .elementor-element.elementor-element-458cac8{right:0px;}.elementor-1564 .elementor-element.elementor-element-93a53bf > .elementor-element-populated{margin:0px 0px 0px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-df6189c{--alignment:center;--width:100%;font-family:"Readex Pro", Sans-serif;font-size:26px;font-weight:500;line-height:1.2em;letter-spacing:0px;--transition:0.3s;}.elementor-1564 .elementor-element.elementor-element-df6189c > .elementor-widget-container{margin:-125px 0px 5px 0px;}.elementor-1564 .elementor-element.elementor-element-63fa649{text-align:center;}.elementor-1564 .elementor-element.elementor-element-63fa649 .elementor-heading-title{color:var( --e-global-color-vamtam_accent_6 );font-family:"Unbounded", Sans-serif;font-size:63px;line-height:1.1em;}.elementor-1564 .elementor-element.elementor-element-63fa649 > .elementor-widget-container{margin:0px 0px 15px 0px;}.elementor-1564 .elementor-element.elementor-element-9d42e1f{font-family:"Readex Pro", Sans-serif;font-size:16px;font-weight:normal;line-height:1.6em;}.elementor-1564 .elementor-element.elementor-element-9d42e1f > .elementor-widget-container{padding:0% 12% 0% 12%;}.elementor-1564 .elementor-element.elementor-element-a988055 .elementor-button{fill:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_6 );background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-a988055 > .elementor-widget-container{margin:0px 0px 0px 15px;}.elementor-1564 .elementor-element.elementor-element-a988055{width:var( --container-widget-width, 99.194% );max-width:99.194%;--container-widget-width:99.194%;--container-widget-flex-grow:0;}.elementor-1564 .elementor-element.elementor-element-a988055.elementor-element{--flex-grow:0;--flex-shrink:0;}.elementor-1564 .elementor-element.elementor-element-4a5833c:not(.elementor-motion-effects-element-type-background), .elementor-1564 .elementor-element.elementor-element-4a5833c > .elementor-motion-effects-container > .elementor-motion-effects-layer{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/06/shape-5.svg");background-position:top right;background-repeat:no-repeat;}.elementor-1564 .elementor-element.elementor-element-4a5833c{transition:background 0.3s, border 0.3s, border-radius 0.3s, box-shadow 0.3s;}.elementor-1564 .elementor-element.elementor-element-4a5833c > .elementor-background-overlay{transition:background 0.3s, border-radius 0.3s, opacity 0.3s;}.elementor-bc-flex-widget .elementor-1564 .elementor-element.elementor-element-a07ac88.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-1564 .elementor-element.elementor-element-a07ac88.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-1564 .elementor-element.elementor-element-a07ac88.elementor-column > .elementor-widget-wrap{justify-content:center;}.elementor-1564 .elementor-element.elementor-element-fdd8a42{--spacer-size:90px;}.elementor-1564 .elementor-element.elementor-element-b1717a8{text-align:center;width:auto;max-width:auto;}.elementor-1564 .elementor-element.elementor-element-b1717a8 .elementor-heading-title{font-family:"Unbounded", Sans-serif;font-size:56px;font-weight:bold;text-transform:none;line-height:1.2em;}.elementor-1564 .elementor-element.elementor-element-b1717a8 > .elementor-widget-container{margin:40px -15px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-84435ab .elementor-icon-wrapper{text-align:center;}.elementor-1564 .elementor-element.elementor-element-84435ab.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-84435ab.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-84435ab.elementor-view-default .elementor-icon{color:var( --e-global-color-vamtam_accent_1 );border-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-84435ab.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-84435ab.elementor-view-default .elementor-icon svg{fill:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-84435ab .elementor-icon{font-size:85px;}.elementor-1564 .elementor-element.elementor-element-84435ab .elementor-icon svg{height:85px;}.elementor-1564 .elementor-element.elementor-element-84435ab > .elementor-widget-container{margin:0px 0px 15px 20px;padding:1px 1px 1px 0px;}.elementor-1564 .elementor-element.elementor-element-84435ab{width:auto;max-width:auto;z-index:1;}.elementor-1564 .elementor-element.elementor-element-d320bdc{margin-top:70px;margin-bottom:90px;}.elementor-1564 .elementor-element.elementor-element-2a6ab41 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-db3b998 .elementor-icon-wrapper{text-align:center;}.elementor-1564 .elementor-element.elementor-element-db3b998 .elementor-icon{font-size:74px;}.elementor-1564 .elementor-element.elementor-element-db3b998 .elementor-icon svg{height:74px;}.elementor-1564 .elementor-element.elementor-element-db3b998 > .elementor-widget-container{margin:0px 0px 15px 0px;padding:15% 15% 15% 15%;background-color:var( --e-global-color-vamtam_accent_4 );}.elementor-1564 .elementor-element.elementor-element-db3b998:hover .elementor-widget-container{background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-db3b998:not( .elementor-widget-image ) .elementor-widget-container{-webkit-mask-image:url( https://www.goodwalk.co.nz/wp-content/uploads/2022/06/bg-shape.svg );-webkit-mask-size:contain;-webkit-mask-position:center center;-webkit-mask-repeat:no-repeat;}.elementor-1564 .elementor-element.elementor-element-db3b998.elementor-widget-image .elementor-widget-container img{-webkit-mask-image:url( https://www.goodwalk.co.nz/wp-content/uploads/2022/06/bg-shape.svg );-webkit-mask-size:contain;-webkit-mask-position:center center;-webkit-mask-repeat:no-repeat;}.elementor-1564 .elementor-element.elementor-element-87282ee{text-align:center;}.elementor-1564 .elementor-element.elementor-element-87282ee .elementor-heading-title{-webkit-text-stroke-width:0px;stroke-width:0px;-webkit-text-stroke-color:var( --e-global-color-vamtam_accent_1 );stroke:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-fe27970 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-edec58c .elementor-icon-wrapper{text-align:center;}.elementor-1564 .elementor-element.elementor-element-edec58c .elementor-icon{font-size:74px;}.elementor-1564 .elementor-element.elementor-element-edec58c .elementor-icon svg{height:74px;}.elementor-1564 .elementor-element.elementor-element-edec58c > .elementor-widget-container{margin:0px 0px 15px 0px;padding:15% 15% 15% 15%;background-color:var( --e-global-color-vamtam_accent_4 );}.elementor-1564 .elementor-element.elementor-element-edec58c:hover .elementor-widget-container{background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-edec58c:not( .elementor-widget-image ) .elementor-widget-container{-webkit-mask-image:url( https://www.goodwalk.co.nz/wp-content/uploads/2022/06/bg-shape.svg );-webkit-mask-size:contain;-webkit-mask-position:center center;-webkit-mask-repeat:no-repeat;}.elementor-1564 .elementor-element.elementor-element-edec58c.elementor-widget-image .elementor-widget-container img{-webkit-mask-image:url( https://www.goodwalk.co.nz/wp-content/uploads/2022/06/bg-shape.svg );-webkit-mask-size:contain;-webkit-mask-position:center center;-webkit-mask-repeat:no-repeat;}.elementor-1564 .elementor-element.elementor-element-8fa0233{text-align:center;}.elementor-1564 .elementor-element.elementor-element-3f48df4 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-58122d9 .elementor-icon-wrapper{text-align:center;}.elementor-1564 .elementor-element.elementor-element-58122d9 .elementor-icon{font-size:74px;}.elementor-1564 .elementor-element.elementor-element-58122d9 .elementor-icon svg{height:74px;}.elementor-1564 .elementor-element.elementor-element-58122d9 > .elementor-widget-container{margin:0px 0px 15px 0px;padding:15% 15% 15% 15%;background-color:var( --e-global-color-vamtam_accent_4 );}.elementor-1564 .elementor-element.elementor-element-58122d9:hover .elementor-widget-container{background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-58122d9:not( .elementor-widget-image ) .elementor-widget-container{-webkit-mask-image:url( https://www.goodwalk.co.nz/wp-content/uploads/2022/06/bg-shape.svg );-webkit-mask-size:contain;-webkit-mask-position:center center;-webkit-mask-repeat:no-repeat;}.elementor-1564 .elementor-element.elementor-element-58122d9.elementor-widget-image .elementor-widget-container img{-webkit-mask-image:url( https://www.goodwalk.co.nz/wp-content/uploads/2022/06/bg-shape.svg );-webkit-mask-size:contain;-webkit-mask-position:center center;-webkit-mask-repeat:no-repeat;}.elementor-1564 .elementor-element.elementor-element-3d1c34b{text-align:center;}.elementor-1564 .elementor-element.elementor-element-5a39dcf > .elementor-container{max-width:920px;}.elementor-1564 .elementor-element.elementor-element-5a39dcf:not(.elementor-motion-effects-element-type-background), .elementor-1564 .elementor-element.elementor-element-5a39dcf > .elementor-motion-effects-container > .elementor-motion-effects-layer{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/06/shape-6.svg");background-position:top left;background-repeat:no-repeat;background-size:12% auto;}.elementor-1564 .elementor-element.elementor-element-5a39dcf{transition:background 0.3s, border 0.3s, border-radius 0.3s, box-shadow 0.3s;}.elementor-1564 .elementor-element.elementor-element-5a39dcf > .elementor-background-overlay{transition:background 0.3s, border-radius 0.3s, opacity 0.3s;}.elementor-bc-flex-widget .elementor-1564 .elementor-element.elementor-element-dd41dfd.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-1564 .elementor-element.elementor-element-dd41dfd.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-1564 .elementor-element.elementor-element-dd41dfd.elementor-column > .elementor-widget-wrap{justify-content:center;}.elementor-1564 .elementor-element.elementor-element-f9c04cf{text-align:center;}.elementor-1564 .elementor-element.elementor-element-f9c04cf .elementor-heading-title{font-family:"Unbounded", Sans-serif;font-size:56px;font-weight:bold;text-transform:none;line-height:1.2em;}.elementor-1564 .elementor-element.elementor-element-f9c04cf > .elementor-widget-container{margin:20px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-7e41d2b{margin-top:50px;margin-bottom:90px;}.elementor-1564 .elementor-element.elementor-element-4b5e9d9 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-4b5e9d9 > .elementor-element-populated{padding:0% 10% 0% 0%;}.elementor-1564 .elementor-element.elementor-element-e9f2f9b.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_5 );fill:#192419;color:#192419;}.elementor-1564 .elementor-element.elementor-element-e9f2f9b.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-e9f2f9b.elementor-view-default .elementor-icon{fill:var( --e-global-color-vamtam_accent_5 );color:var( --e-global-color-vamtam_accent_5 );border-color:var( --e-global-color-vamtam_accent_5 );}.elementor-1564 .elementor-element.elementor-element-e9f2f9b.elementor-view-framed .elementor-icon{background-color:#192419;}.elementor-1564 .elementor-element.elementor-element-e9f2f9b{--icon-box-icon-margin:30px;}.elementor-1564 .elementor-element.elementor-element-e9f2f9b .elementor-icon{font-size:30px;padding:16px;border-radius:24px 24px 24px 24px;}.elementor-1564 .elementor-element.elementor-element-e9f2f9b .elementor-icon-box-title{margin-bottom:10px;}.elementor-1564 .elementor-element.elementor-element-e9f2f9b .elementor-icon-box-description{font-family:var( --e-global-typography-f942773-font-family ), Sans-serif;font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-e9f2f9b > .elementor-widget-container{margin:0px 0px 20px 0px;}.elementor-1564 .elementor-element.elementor-element-1c1c157.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_5 );fill:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-1c1c157.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-1c1c157.elementor-view-default .elementor-icon{fill:var( --e-global-color-vamtam_accent_5 );color:var( --e-global-color-vamtam_accent_5 );border-color:var( --e-global-color-vamtam_accent_5 );}.elementor-1564 .elementor-element.elementor-element-1c1c157.elementor-view-framed .elementor-icon{background-color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-1c1c157{--icon-box-icon-margin:30px;}.elementor-1564 .elementor-element.elementor-element-1c1c157 .elementor-icon{font-size:30px;padding:16px;border-radius:24px 24px 24px 24px;}.elementor-1564 .elementor-element.elementor-element-1c1c157 .elementor-icon-box-title{margin-bottom:10px;}.elementor-1564 .elementor-element.elementor-element-1c1c157 .elementor-icon-box-description{font-family:var( --e-global-typography-f942773-font-family ), Sans-serif;font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-1c1c157 > .elementor-widget-container{margin:0px 0px 20px 0px;}.elementor-1564 .elementor-element.elementor-element-3a5e159.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_5 );fill:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-3a5e159.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-3a5e159.elementor-view-default .elementor-icon{fill:var( --e-global-color-vamtam_accent_5 );color:var( --e-global-color-vamtam_accent_5 );border-color:var( --e-global-color-vamtam_accent_5 );}.elementor-1564 .elementor-element.elementor-element-3a5e159.elementor-view-framed .elementor-icon{background-color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-3a5e159{--icon-box-icon-margin:30px;}.elementor-1564 .elementor-element.elementor-element-3a5e159 .elementor-icon{font-size:30px;padding:16px;border-radius:24px 24px 24px 24px;}.elementor-1564 .elementor-element.elementor-element-3a5e159 .elementor-icon-box-title{margin-bottom:10px;}.elementor-1564 .elementor-element.elementor-element-3a5e159 .elementor-icon-box-description{font-family:var( --e-global-typography-f942773-font-family ), Sans-serif;font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-3a5e159 > .elementor-widget-container{margin:0px 0px 20px 0px;}.elementor-1564 .elementor-element.elementor-element-e7e5c18 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-e7e5c18 > .elementor-element-populated{padding:0% 0% 0% 10%;}.elementor-1564 .elementor-element.elementor-element-4a7cad2.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_5 );fill:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-4a7cad2.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-4a7cad2.elementor-view-default .elementor-icon{fill:var( --e-global-color-vamtam_accent_5 );color:var( --e-global-color-vamtam_accent_5 );border-color:var( --e-global-color-vamtam_accent_5 );}.elementor-1564 .elementor-element.elementor-element-4a7cad2.elementor-view-framed .elementor-icon{background-color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-4a7cad2{--icon-box-icon-margin:30px;}.elementor-1564 .elementor-element.elementor-element-4a7cad2 .elementor-icon{font-size:30px;padding:16px;border-radius:24px 24px 24px 24px;}.elementor-1564 .elementor-element.elementor-element-4a7cad2 .elementor-icon-box-title{margin-bottom:10px;}.elementor-1564 .elementor-element.elementor-element-4a7cad2 .elementor-icon-box-description{font-family:var( --e-global-typography-f942773-font-family ), Sans-serif;font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-4a7cad2 > .elementor-widget-container{margin:0px 0px 20px 0px;}.elementor-1564 .elementor-element.elementor-element-d2d936f.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_5 );fill:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-d2d936f.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-d2d936f.elementor-view-default .elementor-icon{fill:var( --e-global-color-vamtam_accent_5 );color:var( --e-global-color-vamtam_accent_5 );border-color:var( --e-global-color-vamtam_accent_5 );}.elementor-1564 .elementor-element.elementor-element-d2d936f.elementor-view-framed .elementor-icon{background-color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-d2d936f{--icon-box-icon-margin:30px;}.elementor-1564 .elementor-element.elementor-element-d2d936f .elementor-icon{font-size:30px;padding:16px;border-radius:24px 24px 24px 24px;}.elementor-1564 .elementor-element.elementor-element-d2d936f .elementor-icon-box-title{margin-bottom:10px;}.elementor-1564 .elementor-element.elementor-element-d2d936f .elementor-icon-box-description{font-family:var( --e-global-typography-f942773-font-family ), Sans-serif;font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-d2d936f > .elementor-widget-container{margin:0px 0px 20px 0px;}.elementor-1564 .elementor-element.elementor-element-6b7387e.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_5 );fill:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-6b7387e.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-6b7387e.elementor-view-default .elementor-icon{fill:var( --e-global-color-vamtam_accent_5 );color:var( --e-global-color-vamtam_accent_5 );border-color:var( --e-global-color-vamtam_accent_5 );}.elementor-1564 .elementor-element.elementor-element-6b7387e.elementor-view-framed .elementor-icon{background-color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-6b7387e{--icon-box-icon-margin:30px;}.elementor-1564 .elementor-element.elementor-element-6b7387e .elementor-icon{font-size:30px;padding:16px;border-radius:24px 24px 24px 24px;}.elementor-1564 .elementor-element.elementor-element-6b7387e .elementor-icon-box-title{margin-bottom:10px;}.elementor-1564 .elementor-element.elementor-element-6b7387e .elementor-icon-box-description{font-family:var( --e-global-typography-f942773-font-family ), Sans-serif;font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-6b7387e > .elementor-widget-container{margin:0px 0px 20px 0px;}.elementor-1564 .elementor-element.elementor-element-a9c7d5e > .elementor-container{max-width:1100px;}.elementor-1564 .elementor-element.elementor-element-a9c7d5e > .elementor-container > .elementor-column > .elementor-widget-wrap{align-content:flex-end;align-items:flex-end;}.elementor-1564 .elementor-element.elementor-element-a9c7d5e{overflow:hidden;padding:0px 30px 120px 30px;}.elementor-1564 .elementor-element.elementor-element-ae66e34.elementor-column > .elementor-widget-wrap{justify-content:flex-end;}.elementor-1564 .elementor-element.elementor-element-ae66e34 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-52a342a{text-align:center;}.elementor-1564 .elementor-element.elementor-element-52a342a .elementor-heading-title{font-family:"Unbounded", Sans-serif;font-size:56px;font-weight:bold;text-transform:none;line-height:1.2em;}.elementor-1564 .elementor-element.elementor-element-52a342a > .elementor-widget-container{margin:10px 0px 60px 0px;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-wrapper{text-align:right;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-a2b2f6b.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-a2b2f6b.elementor-view-default .elementor-icon{fill:var( --e-global-color-vamtam_accent_1 );color:var( --e-global-color-vamtam_accent_1 );border-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-a2b2f6b{--icon-box-icon-margin:0px;width:auto;max-width:auto;z-index:2;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon{font-size:80px;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon i{transform:rotate(14deg);}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-title{margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-title, .elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-title a{font-family:"Fredoka One", Sans-serif;font-size:22px;text-transform:uppercase;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-description{font-family:"Fredoka One", Sans-serif;font-size:24px;font-weight:bold;text-transform:none;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b > .elementor-widget-container{margin:0px -40px -60px 0px;--e-transform-rotateZ:-6deg;}.elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-arrows-yes .elementor-main-swiper{width:calc( 100% - 40px );}.elementor-1564 .elementor-element.elementor-element-86ac69f .elementor-main-swiper{width:100%;}.elementor-1564 .elementor-element.elementor-element-86ac69f .elementor-main-swiper .swiper-slide{background-color:var( --e-global-color-vamtam_accent_5 );border-width:0px 0px 0px 0px;border-radius:18px;padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_inline .elementor-testimonial__footer, + .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_stacked .elementor-testimonial__footer{margin-top:0px;}.elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_above .elementor-testimonial__footer{margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_left .elementor-testimonial__footer{padding-right:0px;}.elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_right .elementor-testimonial__footer{padding-left:0px;}.elementor-1564 .elementor-element.elementor-element-86ac69f .elementor-testimonial__text{text-transform:none;font-style:normal;}.elementor-1564 .elementor-element.elementor-element-86ac69f .elementor-testimonial__image img{width:0px;height:0px;border-radius:0px;}.elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_left .elementor-testimonial__content:after, + .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_right .elementor-testimonial__content:after{top:calc( 20px + (0px / 2) - 8px );}body:not(.rtl) .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_stacked:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-right) .elementor-testimonial__content:after, + body:not(.rtl) .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_inline:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-right) .elementor-testimonial__content:after, + .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_stacked.elementor-testimonial--align-left .elementor-testimonial__content:after, + .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_inline.elementor-testimonial--align-left .elementor-testimonial__content:after{left:calc( 20px + (0px / 2) - 8px );right:auto;}body.rtl .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_stacked:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-left) .elementor-testimonial__content:after, + body.rtl .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_inline:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-left) .elementor-testimonial__content:after, + .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_stacked.elementor-testimonial--align-right .elementor-testimonial__content:after, + .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_inline.elementor-testimonial--align-right .elementor-testimonial__content:after{right:calc( 20px + (0px / 2) - 8px );left:auto;}body:not(.rtl) .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_above:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-right) .elementor-testimonial__content:after, + .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_above.elementor-testimonial--align-left .elementor-testimonial__content:after{left:calc( 20px + (0px / 2) - 8px );right:auto;}body.rtl .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_above:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-left) .elementor-testimonial__content:after, + .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_above.elementor-testimonial--align-right .elementor-testimonial__content:after{right:calc( 20px + (0px / 2) - 8px );left:auto;}body.rtl .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_inline.elementor-testimonial--align-left .elementor-testimonial__image + cite, + body.rtl .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_above.elementor-testimonial--align-left .elementor-testimonial__image + cite, + body:not(.rtl) .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_inline .elementor-testimonial__image + cite, + body:not(.rtl) .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_above .elementor-testimonial__image + cite{margin-left:0px;margin-right:0;}body:not(.rtl) .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_inline.elementor-testimonial--align-right .elementor-testimonial__image + cite, + body:not(.rtl) .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_above.elementor-testimonial--align-right .elementor-testimonial__image + cite, + body.rtl .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_inline .elementor-testimonial__image + cite, + body.rtl .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_above .elementor-testimonial__image + cite{margin-right:0px;margin-left:0;}.elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_stacked .elementor-testimonial__image + cite, + .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_left .elementor-testimonial__image + cite, + .elementor-1564 .elementor-element.elementor-element-86ac69f.elementor-testimonial--layout-image_right .elementor-testimonial__image + cite{margin-top:0px;}.elementor-1564 .elementor-element.elementor-element-86ac69f .elementor-swiper-button{font-size:20px;color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-86ac69f .elementor-swiper-button svg{fill:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-86ac69f > .elementor-widget-container{background-color:var( --e-global-color-vamtam_accent_5 );border-radius:18px 18px 18px 18px;box-shadow:0px 30px 20px 0px rgba(0, 0, 0, 0.06);}.elementor-1564 .elementor-element.elementor-element-e7d440f > .elementor-container > .elementor-column > .elementor-widget-wrap{align-content:center;align-items:center;}.elementor-bc-flex-widget .elementor-1564 .elementor-element.elementor-element-993e265.elementor-column .elementor-widget-wrap{align-items:flex-start;}.elementor-1564 .elementor-element.elementor-element-993e265.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-start;align-items:flex-start;}.elementor-1564 .elementor-element.elementor-element-993e265 > .elementor-element-populated{border-style:solid;border-width:1px 0px 0px 0px;border-color:var( --e-global-color-vamtam_accent_7 );transition:background 0.3s, border 0.3s, border-radius 0.3s, box-shadow 0.3s;margin:-110px 45px 0px 0px;--e-column-margin-right:45px;--e-column-margin-left:0px;padding:40px 0px 40px 0px;}.elementor-1564 .elementor-element.elementor-element-993e265 > .elementor-element-populated > .elementor-background-overlay{transition:background 0.3s, border-radius 0.3s, opacity 0.3s;}.elementor-1564 .elementor-element.elementor-element-993e265{z-index:2;}.elementor-1564 .elementor-element.elementor-element-fbb5f97 .elementor-icon-list-icon i{color:var( --e-global-color-vamtam_accent_2 );transition:color 0.3s;}.elementor-1564 .elementor-element.elementor-element-fbb5f97 .elementor-icon-list-icon svg{fill:var( --e-global-color-vamtam_accent_2 );transition:fill 0.3s;}.elementor-1564 .elementor-element.elementor-element-fbb5f97{--e-icon-list-icon-size:20px;--icon-vertical-offset:0px;width:auto;max-width:auto;}.elementor-1564 .elementor-element.elementor-element-fbb5f97 .elementor-icon-list-icon{padding-right:0px;}.elementor-1564 .elementor-element.elementor-element-fbb5f97 .elementor-icon-list-text{transition:color 0.3s;}.elementor-1564 .elementor-element.elementor-element-fbb5f97 > .elementor-widget-container{margin:0px 30px 0px 0px;padding:5px 15px 5px 15px;background-color:var( --e-global-color-vamtam_accent_8 );border-radius:18px 18px 18px 18px;}.elementor-1564 .elementor-element.elementor-element-fbb5f97:hover .elementor-widget-container{background-color:var( --e-global-color-vamtam_accent_4 );}.elementor-1564 .elementor-element.elementor-element-c1116cc > .elementor-container{max-width:860px;}.elementor-1564 .elementor-element.elementor-element-c1116cc:not(.elementor-motion-effects-element-type-background), .elementor-1564 .elementor-element.elementor-element-c1116cc > .elementor-motion-effects-container > .elementor-motion-effects-layer{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/06/shape-8.svg");background-position:0px 130px;background-repeat:no-repeat;}.elementor-1564 .elementor-element.elementor-element-c1116cc > .elementor-background-overlay{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/06/shape-7.svg");background-position:bottom right;background-repeat:no-repeat;opacity:1;transition:background 0.3s, border-radius 0.3s, opacity 0.3s;}.elementor-1564 .elementor-element.elementor-element-c1116cc{transition:background 0.3s, border 0.3s, border-radius 0.3s, box-shadow 0.3s;margin-top:0px;margin-bottom:90px;}.elementor-1564 .elementor-element.elementor-element-f8b2d62{--iteration-count:infinite;--animation-duration:1200ms;--dynamic-text-color:#FFD100;}.elementor-1564 .elementor-element.elementor-element-f8b2d62 .elementor-headline{text-align:center;font-family:var( --e-global-typography-vamtam_h1-font-family ), Sans-serif;font-size:var( --e-global-typography-vamtam_h1-font-size );font-weight:var( --e-global-typography-vamtam_h1-font-weight );text-transform:var( --e-global-typography-vamtam_h1-text-transform );line-height:var( --e-global-typography-vamtam_h1-line-height );}.elementor-1564 .elementor-element.elementor-element-f8b2d62 .elementor-headline-dynamic-wrapper path{stroke:#192419;}.elementor-1564 .elementor-element.elementor-element-f8b2d62 .elementor-headline .elementor-headline-dynamic-wrapper{-webkit-text-stroke-color:#000;stroke:#000;}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group{padding-right:calc( 20px/2 );padding-left:calc( 20px/2 );margin-bottom:15px;}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-form-fields-wrapper{margin-left:calc( -20px/2 );margin-right:calc( -20px/2 );margin-bottom:-15px;}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group.recaptcha_v3-bottomleft, .elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group.recaptcha_v3-bottomright{margin-bottom:0;}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-mark-required .elementor-field-label:after{color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group > label{font-family:var( --e-global-typography-vamtam_h6-font-family ), Sans-serif;font-size:var( --e-global-typography-vamtam_h6-font-size );font-weight:var( --e-global-typography-vamtam_h6-font-weight );line-height:var( --e-global-typography-vamtam_h6-line-height );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group .elementor-field, .elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-subgroup label{font-family:var( --e-global-typography-e3d0827-font-family ), Sans-serif;font-size:var( --e-global-typography-e3d0827-font-size );font-weight:var( --e-global-typography-e3d0827-font-weight );line-height:var( --e-global-typography-e3d0827-line-height );letter-spacing:var( --e-global-typography-e3d0827-letter-spacing );word-spacing:var( --e-global-typography-e3d0827-word-spacing );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group:not(.elementor-field-type-upload) .elementor-field:not(.elementor-select-wrapper):hover, + .elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group .elementor-select-wrapper:hover select{background-color:var( --e-global-color-vamtam_accent_8 );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group:not(.elementor-field-type-upload) .elementor-field:not(.elementor-select-wrapper):focus, + .elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group .elementor-select-wrapper:focus select{background-color:var( --e-global-color-vamtam_accent_4 );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-button{font-family:var( --e-global-typography-vamtam_h6-font-family ), Sans-serif;font-size:var( --e-global-typography-vamtam_h6-font-size );font-weight:var( --e-global-typography-vamtam_h6-font-weight );line-height:var( --e-global-typography-vamtam_h6-line-height );padding:18px 40px 18px 40px;}.elementor-1564 .elementor-element.elementor-element-986ea87 .e-form__buttons__wrapper__button-next{background-color:var( --e-global-color-vamtam_accent_1 );color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-button[type="submit"]{background-color:var( --e-global-color-vamtam_accent_1 );color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-button[type="submit"] svg *{fill:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-986ea87 .e-form__buttons__wrapper__button-previous{color:#ffffff;}.elementor-1564 .elementor-element.elementor-element-986ea87 .e-form__buttons__wrapper__button-next:hover{background-color:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_5 );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-button[type="submit"]:hover{background-color:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_5 );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-button[type="submit"]:hover svg *{fill:var( --e-global-color-vamtam_accent_5 );}.elementor-1564 .elementor-element.elementor-element-986ea87 .e-form__buttons__wrapper__button-previous:hover{color:#ffffff;}.elementor-1564 .elementor-element.elementor-element-986ea87 .e-form__indicators__indicator, .elementor-1564 .elementor-element.elementor-element-986ea87 .e-form__indicators__indicator__label{font-family:var( --e-global-typography-8069482-font-family ), Sans-serif;font-size:var( --e-global-typography-8069482-font-size );font-weight:var( --e-global-typography-8069482-font-weight );line-height:var( --e-global-typography-8069482-line-height );letter-spacing:var( --e-global-typography-8069482-letter-spacing );word-spacing:var( --e-global-typography-8069482-word-spacing );}.elementor-1564 .elementor-element.elementor-element-986ea87{--e-form-steps-indicators-spacing:40px;--e-form-steps-indicator-padding:40px;--e-form-steps-indicator-inactive-primary-color:var( --e-global-color-vamtam_accent_6 );--e-form-steps-indicator-inactive-secondary-color:#FFFFFF00;--e-form-steps-indicator-active-primary-color:var( --e-global-color-vamtam_accent_6 );--e-form-steps-indicator-active-secondary-color:var( --e-global-color-vamtam_accent_4 );--e-form-steps-indicator-completed-primary-color:var( --e-global-color-vamtam_accent_6 );--e-form-steps-indicator-completed-secondary-color:var( --e-global-color-vamtam_accent_5 );--e-form-steps-divider-width:1px;--e-form-steps-divider-gap:5px;}.elementor-1564 .elementor-element.elementor-element-14d7e43{margin-top:160px;margin-bottom:50px;}.elementor-1564 .elementor-element.elementor-element-73a8300 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:10px;}.elementor-1564 .elementor-element.elementor-element-73a8300 > .elementor-element-populated{margin:80px 30px 0px 0px;--e-column-margin-right:30px;--e-column-margin-left:0px;}.elementor-1564 .elementor-element.elementor-element-49eb98a.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-49eb98a.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-49eb98a.elementor-view-default .elementor-icon{fill:var( --e-global-color-vamtam_accent_1 );color:var( --e-global-color-vamtam_accent_1 );border-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-49eb98a{--icon-box-icon-margin:25px;}.elementor-1564 .elementor-element.elementor-element-49eb98a .elementor-icon{font-size:50px;}.elementor-1564 .elementor-element.elementor-element-6b1ab84{text-align:right;}.elementor-1564 .elementor-element.elementor-element-6b1ab84 > .elementor-widget-container{margin:20px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-f98d7b8{text-align:right;font-family:"Readex Pro", Sans-serif;font-size:14px;font-weight:normal;line-height:1.4em;}.elementor-1564 .elementor-element.elementor-element-4e2182d{text-align:right;}.elementor-1564 .elementor-element.elementor-element-4e2182d > .elementor-widget-container{margin:20px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-0a58fb9{text-align:right;font-family:var( --e-global-typography-f942773-font-family ), Sans-serif;font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-1ac85c1 .elementor-icon-wrapper{text-align:right;}.elementor-1564 .elementor-element.elementor-element-1ac85c1.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-1ac85c1.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-1ac85c1.elementor-view-default .elementor-icon{color:var( --e-global-color-vamtam_accent_1 );border-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-1ac85c1.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-1ac85c1.elementor-view-default .elementor-icon svg{fill:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-1ac85c1 .elementor-icon{font-size:90px;}.elementor-1564 .elementor-element.elementor-element-1ac85c1 .elementor-icon svg{height:90px;}.elementor-1564 .elementor-element.elementor-element-1ac85c1{width:auto;max-width:auto;top:-3%;z-index:1;}body:not(.rtl) .elementor-1564 .elementor-element.elementor-element-1ac85c1{right:0px;}body.rtl .elementor-1564 .elementor-element.elementor-element-1ac85c1{left:0px;}.elementor-1564 .elementor-element.elementor-element-65d7b70 img{border-radius:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-65d7b70 > .elementor-widget-container{margin:60px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-325e0fc img{width:100%;max-width:80%;}.elementor-1564 .elementor-element.elementor-element-325e0fc{--vamtam-fg-layer-bg-color:var( --e-global-color-vamtam_accent_4 );--vamtam-fg-layer-mask-image:url('https://petmania.vamtam.com/wp-content/uploads/2022/06/bg-oval-shape.svg');--vamtam-fg-layer-mask-size:contain;--vamtam-fg-layer-mask-position:top center;--vamtam-fg-layer-mask-repeat:no-repeat;top:0px;z-index:-1;}body:not(.rtl) .elementor-1564 .elementor-element.elementor-element-325e0fc{left:0px;}body.rtl .elementor-1564 .elementor-element.elementor-element-325e0fc{right:0px;}.elementor-1564 .elementor-element.elementor-element-1989a7a > .elementor-element-populated{margin:80px 0px 0px 30px;--e-column-margin-right:0px;--e-column-margin-left:30px;}.elementor-1564 .elementor-element.elementor-element-eab90c9.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-eab90c9.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-eab90c9.elementor-view-default .elementor-icon{fill:var( --e-global-color-vamtam_accent_1 );color:var( --e-global-color-vamtam_accent_1 );border-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-eab90c9{--icon-box-icon-margin:25px;}.elementor-1564 .elementor-element.elementor-element-eab90c9 .elementor-icon{font-size:50px;}.elementor-1564 .elementor-element.elementor-element-7094ea7 .elementor-tab-title{border-width:0px;border-color:var( --e-global-color-vamtam_accent_7 );padding:18px 20px 18px 20px;}.elementor-1564 .elementor-element.elementor-element-7094ea7 .elementor-tab-content{border-width:0px;border-bottom-color:var( --e-global-color-vamtam_accent_7 );font-family:var( --e-global-typography-f942773-font-family ), Sans-serif;font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );padding:0px 30px 5px 40px;}.elementor-1564 .elementor-element.elementor-element-7094ea7 .elementor-toggle-item:not(:last-child){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-7094ea7 .elementor-toggle-title{font-size:14px;font-weight:500;line-height:1.1em;}.elementor-1564 .elementor-element.elementor-element-7094ea7 .elementor-tab-title.elementor-active .elementor-toggle-icon i:before{color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-7094ea7 .elementor-tab-title.elementor-active .elementor-toggle-icon svg{fill:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-7094ea7 .elementor-toggle-icon.elementor-toggle-icon-left{margin-right:5px;}.elementor-1564 .elementor-element.elementor-element-7094ea7 .elementor-toggle-icon.elementor-toggle-icon-right{margin-left:5px;}.elementor-1564 .elementor-element.elementor-element-17ec1c7{overflow:hidden;margin-top:0px;margin-bottom:-100px;}.elementor-1564 .elementor-element.elementor-element-90edd5d > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-46d8b34.elementor-view-stacked .elementor-icon{background-color:var( --e-global-color-vamtam_accent_1 );fill:var( --e-global-color-vamtam_accent_6 );color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-46d8b34.elementor-view-framed .elementor-icon, .elementor-1564 .elementor-element.elementor-element-46d8b34.elementor-view-default .elementor-icon{fill:var( --e-global-color-vamtam_accent_1 );color:var( --e-global-color-vamtam_accent_1 );border-color:var( --e-global-color-vamtam_accent_1 );}.elementor-1564 .elementor-element.elementor-element-46d8b34.elementor-view-framed .elementor-icon{background-color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-46d8b34{--icon-box-icon-margin:15px;}.elementor-1564 .elementor-element.elementor-element-46d8b34 .elementor-icon{font-size:20px;border-radius:18px 18px 18px 18px;}.elementor-1564 .elementor-element.elementor-element-46d8b34 .elementor-icon-box-title{color:var( --e-global-color-vamtam_accent_6 );}.elementor-1564 .elementor-element.elementor-element-46d8b34 .elementor-icon-box-title, .elementor-1564 .elementor-element.elementor-element-46d8b34 .elementor-icon-box-title a{font-family:var( --e-global-typography-e3d0827-font-family ), Sans-serif;font-size:var( --e-global-typography-e3d0827-font-size );font-weight:var( --e-global-typography-e3d0827-font-weight );line-height:var( --e-global-typography-e3d0827-line-height );letter-spacing:var( --e-global-typography-e3d0827-letter-spacing );word-spacing:var( --e-global-typography-e3d0827-word-spacing );}.elementor-1564 .elementor-element.elementor-element-e51473d{--display:flex;--flex-direction:row;--container-widget-width:initial;--container-widget-height:100%;--container-widget-flex-grow:1;--container-widget-align-self:stretch;--background-transition:0.3s;}:root{--page-title-display:none;}@media(max-width:1024px){.elementor-1564 .elementor-element.elementor-element-6bf5de6 > .elementor-container{min-height:50vh;}.elementor-1564 .elementor-element.elementor-element-6bf5de6{margin-top:0px;margin-bottom:0px;}.elementor-bc-flex-widget .elementor-1564 .elementor-element.elementor-element-4651da9.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-1564 .elementor-element.elementor-element-4651da9.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-1564 .elementor-element.elementor-element-4651da9 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:40px;}.elementor-1564 .elementor-element.elementor-element-b754a13 .elementor-heading-title{font-size:var( --e-global-typography-vamtam_h1-font-size );line-height:var( --e-global-typography-vamtam_h1-line-height );}.elementor-1564 .elementor-element.elementor-element-ac8ee2b > .elementor-widget-container{margin:0px 0px 10px 0px;}.elementor-1564 .elementor-element.elementor-element-ac8ee2b{width:100%;max-width:100%;}.elementor-1564 .elementor-element.elementor-element-53ea529 > .elementor-widget-container{margin:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-53ea529{width:100%;max-width:100%;}.elementor-1564 .elementor-element.elementor-element-1a029bc{--spacer-size:70px;}.elementor-1564 .elementor-element.elementor-element-f3f0e11 img{max-width:150px;}.elementor-1564 .elementor-element.elementor-element-f474cc3 > .elementor-container{min-height:50vh;}.elementor-1564 .elementor-element.elementor-element-f474cc3{margin-top:0px;margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-8eb92d6 > .elementor-widget-container{margin:0px 0px 10px 0px;}.elementor-1564 .elementor-element.elementor-element-8eb92d6{width:100%;max-width:100%;}.elementor-1564 .elementor-element.elementor-element-f497fe5 > .elementor-widget-container{margin:0px 0px 10px 0px;}.elementor-1564 .elementor-element.elementor-element-f497fe5{width:100%;max-width:100%;}.elementor-1564 .elementor-element.elementor-element-cc6139b{margin-top:20px;margin-bottom:70px;}.elementor-1564 .elementor-element.elementor-element-fb8c1cf{font-size:var( --e-global-typography-vamtam_h6-font-size );line-height:var( --e-global-typography-vamtam_h6-line-height );}.elementor-bc-flex-widget .elementor-1564 .elementor-element.elementor-element-c9abac0.elementor-column .elementor-widget-wrap{align-items:center;}.elementor-1564 .elementor-element.elementor-element-c9abac0.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:center;align-items:center;}.elementor-1564 .elementor-element.elementor-element-c9abac0 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-c9abac0 > .elementor-element-populated{padding:0px 0px 0px 30px;}.elementor-1564 .elementor-element.elementor-element-5a8121e .elementor-icon{font-size:60px;}.elementor-1564 .elementor-element.elementor-element-5a8121e .elementor-icon svg{height:60px;}.elementor-1564 .elementor-element.elementor-element-5a8121e{top:30%;}.elementor-1564 .elementor-element.elementor-element-ad07384 img{width:100%;}.elementor-1564 .elementor-element.elementor-element-458cac8 img{max-width:65%;}.elementor-1564 .elementor-element.elementor-element-458cac8{top:15%;}.elementor-1564 .elementor-element.elementor-element-68ce02c img{width:50px;}.elementor-1564 .elementor-element.elementor-element-68ce02c > .elementor-widget-container{margin:0px 0px 20px 0px;}.elementor-1564 .elementor-element.elementor-element-df6189c{--width:500%;}.elementor-1564 .elementor-element.elementor-element-63fa649{text-align:center;}.elementor-1564 .elementor-element.elementor-element-63fa649 .elementor-heading-title{font-size:50px;}.elementor-1564 .elementor-element.elementor-element-63fa649 > .elementor-widget-container{padding:0px 0px 0px 30px;}.elementor-1564 .elementor-element.elementor-element-9d42e1f > .elementor-widget-container{padding:0px 30px 0px 30px;}.elementor-1564 .elementor-element.elementor-element-a988055 > .elementor-widget-container{margin:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-a988055{width:100%;max-width:100%;}.elementor-1564 .elementor-element.elementor-element-4a5833c:not(.elementor-motion-effects-element-type-background), .elementor-1564 .elementor-element.elementor-element-4a5833c > .elementor-motion-effects-container > .elementor-motion-effects-layer{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/06/shape-5.svg");background-size:150px auto;}.elementor-1564 .elementor-element.elementor-element-fdd8a42{--spacer-size:70px;}.elementor-1564 .elementor-element.elementor-element-b1717a8 .elementor-heading-title{font-size:45px;line-height:1.4em;}.elementor-1564 .elementor-element.elementor-element-b1717a8 > .elementor-widget-container{margin:30px -15px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-84435ab .elementor-icon{font-size:60px;}.elementor-1564 .elementor-element.elementor-element-84435ab .elementor-icon svg{height:60px;}.elementor-1564 .elementor-element.elementor-element-84435ab > .elementor-widget-container{margin:0px 0px 15px 0px;}.elementor-1564 .elementor-element.elementor-element-d320bdc{margin-top:40px;margin-bottom:70px;}.elementor-1564 .elementor-element.elementor-element-5a39dcf:not(.elementor-motion-effects-element-type-background), .elementor-1564 .elementor-element.elementor-element-5a39dcf > .elementor-motion-effects-container > .elementor-motion-effects-layer{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/06/shape-6.svg");background-size:150px auto;}.elementor-1564 .elementor-element.elementor-element-5a39dcf{margin-top:0px;margin-bottom:70px;}.elementor-1564 .elementor-element.elementor-element-f9c04cf .elementor-heading-title{font-size:45px;line-height:1.4em;}.elementor-1564 .elementor-element.elementor-element-7e41d2b{margin-top:40px;margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-4b5e9d9 > .elementor-element-populated{padding:0px 15px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-e9f2f9b .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-1c1c157 .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-3a5e159 .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-e7e5c18 > .elementor-element-populated{padding:0px 0px 0px 15px;}.elementor-1564 .elementor-element.elementor-element-4a7cad2 .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-d2d936f .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-6b7387e .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-a9c7d5e{padding:0px 30px 70px 30px;}.elementor-1564 .elementor-element.elementor-element-ae66e34 > .elementor-element-populated{padding:0px 30px 0px 30px;}.elementor-1564 .elementor-element.elementor-element-52a342a .elementor-heading-title{font-size:45px;line-height:1.4em;}.elementor-1564 .elementor-element.elementor-element-52a342a > .elementor-widget-container{margin:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon{font-size:60px;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-title, .elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-title a{font-size:16px;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-description{font-size:20px;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b > .elementor-widget-container{margin:0px -30px -45px 0px;}.elementor-1564 .elementor-element.elementor-element-993e265 > .elementor-element-populated{margin:-135px 0px 0px 30px;--e-column-margin-right:0px;--e-column-margin-left:30px;padding:30px 0px 30px 0px;}.elementor-1564 .elementor-element.elementor-element-fbb5f97 > .elementor-widget-container{margin:0px 0px 10px 0px;}.elementor-1564 .elementor-element.elementor-element-c1116cc:not(.elementor-motion-effects-element-type-background), .elementor-1564 .elementor-element.elementor-element-c1116cc > .elementor-motion-effects-container > .elementor-motion-effects-layer{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/06/shape-8.svg");background-position:0px 0px;background-size:100px auto;}.elementor-1564 .elementor-element.elementor-element-c1116cc > .elementor-background-overlay{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/06/shape-7.svg");background-size:100px auto;}.elementor-1564 .elementor-element.elementor-element-c1116cc{margin-top:30px;margin-bottom:70px;padding:0px 40px 0px 40px;}.elementor-1564 .elementor-element.elementor-element-f8b2d62 .elementor-headline{font-size:var( --e-global-typography-vamtam_h1-font-size );line-height:var( --e-global-typography-vamtam_h1-line-height );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group > label{font-size:var( --e-global-typography-vamtam_h6-font-size );line-height:var( --e-global-typography-vamtam_h6-line-height );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group .elementor-field, .elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-subgroup label{font-size:var( --e-global-typography-e3d0827-font-size );line-height:var( --e-global-typography-e3d0827-line-height );letter-spacing:var( --e-global-typography-e3d0827-letter-spacing );word-spacing:var( --e-global-typography-e3d0827-word-spacing );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-button{font-size:var( --e-global-typography-vamtam_h6-font-size );line-height:var( --e-global-typography-vamtam_h6-line-height );}.elementor-1564 .elementor-element.elementor-element-986ea87 .e-form__indicators__indicator, .elementor-1564 .elementor-element.elementor-element-986ea87 .e-form__indicators__indicator__label{font-size:var( --e-global-typography-8069482-font-size );line-height:var( --e-global-typography-8069482-line-height );letter-spacing:var( --e-global-typography-8069482-letter-spacing );word-spacing:var( --e-global-typography-8069482-word-spacing );}.elementor-1564 .elementor-element.elementor-element-986ea87{--e-form-steps-divider-gap:5px;}.elementor-1564 .elementor-element.elementor-element-14d7e43{margin-top:15px;margin-bottom:65px;}.elementor-bc-flex-widget .elementor-1564 .elementor-element.elementor-element-73a8300.elementor-column .elementor-widget-wrap{align-items:center;}.elementor-1564 .elementor-element.elementor-element-73a8300.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:center;align-items:center;}.elementor-1564 .elementor-element.elementor-element-73a8300 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-73a8300 > .elementor-element-populated{margin:0px 30px 0px 0px;--e-column-margin-right:30px;--e-column-margin-left:0px;}.elementor-1564 .elementor-element.elementor-element-6b1ab84 > .elementor-widget-container{margin:10px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-0a58fb9{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-1ac85c1 .elementor-icon{font-size:60px;}.elementor-1564 .elementor-element.elementor-element-1ac85c1 .elementor-icon svg{height:60px;}.elementor-bc-flex-widget .elementor-1564 .elementor-element.elementor-element-1989a7a.elementor-column .elementor-widget-wrap{align-items:center;}.elementor-1564 .elementor-element.elementor-element-1989a7a.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:center;align-items:center;}.elementor-1564 .elementor-element.elementor-element-1989a7a > .elementor-element-populated{margin:0px 0px 0px 30px;--e-column-margin-right:0px;--e-column-margin-left:30px;}.elementor-1564 .elementor-element.elementor-element-7094ea7 .elementor-tab-title{padding:15px 20px 15px 20px;}.elementor-1564 .elementor-element.elementor-element-7094ea7 .elementor-tab-content{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-17ec1c7{margin-top:0px;margin-bottom:-70px;}.elementor-1564 .elementor-element.elementor-element-46d8b34 .elementor-icon-box-title, .elementor-1564 .elementor-element.elementor-element-46d8b34 .elementor-icon-box-title a{font-size:var( --e-global-typography-e3d0827-font-size );line-height:var( --e-global-typography-e3d0827-line-height );letter-spacing:var( --e-global-typography-e3d0827-letter-spacing );word-spacing:var( --e-global-typography-e3d0827-word-spacing );}}@media(max-width:767px){.elementor-1564 .elementor-element.elementor-element-6bf5de6{margin-top:0px;margin-bottom:120px;padding:50px 20px 0px 20px;}.elementor-1564 .elementor-element.elementor-element-4651da9 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:25px;}.elementor-1564 .elementor-element.elementor-element-b754a13 .elementor-heading-title{font-size:var( --e-global-typography-vamtam_h1-font-size );line-height:var( --e-global-typography-vamtam_h1-line-height );}.elementor-1564 .elementor-element.elementor-element-b754a13 > .elementor-widget-container{padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-1a029bc{--spacer-size:20px;}.elementor-1564 .elementor-element.elementor-element-65ef1c5.elementor-column > .elementor-widget-wrap{justify-content:flex-start;}.elementor-1564 .elementor-element.elementor-element-65ef1c5 > .elementor-element-populated{margin:0px 0px 10px 30px;--e-column-margin-right:0px;--e-column-margin-left:30px;padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-f3f0e11 img{max-width:95px;}.elementor-1564 .elementor-element.elementor-element-f474cc3{margin-top:0px;margin-bottom:120px;padding:50px 20px 0px 20px;}.elementor-1564 .elementor-element.elementor-element-8eb92d6{width:auto;max-width:auto;}.elementor-1564 .elementor-element.elementor-element-f497fe5 > .elementor-widget-container{margin:0px 0px 0px 10px;}.elementor-1564 .elementor-element.elementor-element-f497fe5{width:auto;max-width:auto;}.elementor-1564 .elementor-element.elementor-element-e547760 > .elementor-widget-container{margin:0px 0px -7px 0px;}.elementor-1564 .elementor-element.elementor-element-cc6139b{margin-top:-100px;margin-bottom:50px;}.elementor-1564 .elementor-element.elementor-element-fb8c1cf{font-size:var( --e-global-typography-vamtam_h6-font-size );line-height:var( --e-global-typography-vamtam_h6-line-height );}.elementor-1564 .elementor-element.elementor-element-90ee838 > .elementor-widget-container{margin:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-c9abac0 > .elementor-element-populated{margin:0px 0px 20px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;}.elementor-1564 .elementor-element.elementor-element-5a8121e{top:15%;}.elementor-1564 .elementor-element.elementor-element-458cac8{top:0%;}.elementor-1564 .elementor-element.elementor-element-93a53bf > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-df6189c{--alignment:center;--width:500%;}.elementor-1564 .elementor-element.elementor-element-df6189c > .elementor-widget-container{margin:-105px 0px 20px 0px;}.elementor-1564 .elementor-element.elementor-element-63fa649{text-align:center;}.elementor-1564 .elementor-element.elementor-element-63fa649 .elementor-heading-title{font-size:38px;}.elementor-1564 .elementor-element.elementor-element-63fa649 > .elementor-widget-container{margin:0px 0px 20px 0px;padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-9d42e1f{text-align:left;font-size:16px;line-height:1.5em;}.elementor-1564 .elementor-element.elementor-element-9d42e1f > .elementor-widget-container{margin:0px 0px 15px 0px;padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-4a5833c:not(.elementor-motion-effects-element-type-background), .elementor-1564 .elementor-element.elementor-element-4a5833c > .elementor-motion-effects-container > .elementor-motion-effects-layer{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/06/shape-5.svg");background-size:50px auto;}.elementor-1564 .elementor-element.elementor-element-a07ac88.elementor-column > .elementor-widget-wrap{justify-content:center;}.elementor-1564 .elementor-element.elementor-element-a07ac88 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-a07ac88 > .elementor-element-populated{margin:0px 0px 0px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-fdd8a42{--spacer-size:45px;}.elementor-1564 .elementor-element.elementor-element-b1717a8{text-align:center;}.elementor-1564 .elementor-element.elementor-element-b1717a8 .elementor-heading-title{font-size:35px;}.elementor-1564 .elementor-element.elementor-element-b1717a8 > .elementor-widget-container{margin:0px -15px 0px 0px;padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-84435ab .elementor-icon-wrapper{text-align:left;}.elementor-1564 .elementor-element.elementor-element-84435ab .elementor-icon{font-size:83px;}.elementor-1564 .elementor-element.elementor-element-84435ab .elementor-icon svg{height:83px;}.elementor-1564 .elementor-element.elementor-element-84435ab .elementor-icon i, .elementor-1564 .elementor-element.elementor-element-84435ab .elementor-icon svg{transform:rotate(262deg);}.elementor-1564 .elementor-element.elementor-element-84435ab > .elementor-widget-container{margin:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-d320bdc{margin-top:30px;margin-bottom:40px;padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-2a6ab41{width:50%;}.elementor-1564 .elementor-element.elementor-element-db3b998 .elementor-icon{font-size:60px;}.elementor-1564 .elementor-element.elementor-element-db3b998 .elementor-icon svg{height:60px;}.elementor-1564 .elementor-element.elementor-element-fe27970{width:50%;}.elementor-1564 .elementor-element.elementor-element-edec58c .elementor-icon{font-size:60px;}.elementor-1564 .elementor-element.elementor-element-edec58c .elementor-icon svg{height:60px;}.elementor-1564 .elementor-element.elementor-element-3f48df4{width:50%;}.elementor-1564 .elementor-element.elementor-element-58122d9 .elementor-icon{font-size:60px;}.elementor-1564 .elementor-element.elementor-element-58122d9 .elementor-icon svg{height:60px;}.elementor-1564 .elementor-element.elementor-element-5a39dcf:not(.elementor-motion-effects-element-type-background), .elementor-1564 .elementor-element.elementor-element-5a39dcf > .elementor-motion-effects-container > .elementor-motion-effects-layer{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/06/shape-6.svg");background-size:50px auto;}.elementor-1564 .elementor-element.elementor-element-5a39dcf{margin-top:0px;margin-bottom:40px;}.elementor-1564 .elementor-element.elementor-element-dd41dfd > .elementor-element-populated{margin:0px 0px 0px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:50px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-f9c04cf .elementor-heading-title{font-size:34px;}.elementor-1564 .elementor-element.elementor-element-f9c04cf > .elementor-widget-container{margin:0px 0px 10px 0px;}.elementor-1564 .elementor-element.elementor-element-7e41d2b{margin-top:0px;margin-bottom:0px;padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-4b5e9d9 > .elementor-element-populated{padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-e9f2f9b{--icon-box-icon-margin:0px;}.elementor-1564 .elementor-element.elementor-element-e9f2f9b .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-1c1c157{--icon-box-icon-margin:0px;}.elementor-1564 .elementor-element.elementor-element-1c1c157 .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-3a5e159{--icon-box-icon-margin:0px;}.elementor-1564 .elementor-element.elementor-element-3a5e159 .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-e7e5c18 > .elementor-element-populated{padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-4a7cad2{--icon-box-icon-margin:0px;}.elementor-1564 .elementor-element.elementor-element-4a7cad2 .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-d2d936f{--icon-box-icon-margin:0px;}.elementor-1564 .elementor-element.elementor-element-d2d936f .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-6b7387e{--icon-box-icon-margin:0px;}.elementor-1564 .elementor-element.elementor-element-6b7387e .elementor-icon-box-description{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-6b7387e > .elementor-widget-container{margin:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-a9c7d5e{padding:0px 20px 50px 20px;}.elementor-1564 .elementor-element.elementor-element-ae66e34 > .elementor-element-populated{padding:0px 20px 0px 20px;}.elementor-1564 .elementor-element.elementor-element-52a342a .elementor-heading-title{font-size:34px;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-wrapper{text-align:justify;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b{--icon-box-icon-margin:-10px;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon{font-size:60px;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-title{margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-title, .elementor-1564 .elementor-element.elementor-element-a2b2f6b .elementor-icon-box-title a{font-size:12px;line-height:2.1em;}.elementor-1564 .elementor-element.elementor-element-a2b2f6b > .elementor-widget-container{margin:0px -30px -95px 0px;}.elementor-1564 .elementor-element.elementor-element-e7d440f, .elementor-1564 .elementor-element.elementor-element-e7d440f > .elementor-background-overlay{border-radius:0px 0px 18px 18px;}.elementor-1564 .elementor-element.elementor-element-e7d440f{padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-993e265 > .elementor-element-populated{margin:-120px 40px 0px 40px;--e-column-margin-right:40px;--e-column-margin-left:40px;padding:20px 0px 20px 0px;}.elementor-1564 .elementor-element.elementor-element-fbb5f97 > .elementor-widget-container{padding:5px 10px 5px 10px;}.elementor-1564 .elementor-element.elementor-element-c1116cc:not(.elementor-motion-effects-element-type-background), .elementor-1564 .elementor-element.elementor-element-c1116cc > .elementor-motion-effects-container > .elementor-motion-effects-layer{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/06/shape-8.svg");background-position:0px 0px;background-size:60px auto;}.elementor-1564 .elementor-element.elementor-element-c1116cc > .elementor-background-overlay{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://petmania.vamtam.com/wp-content/uploads/2022/07/arrow.svg");background-size:60px auto;}.elementor-1564 .elementor-element.elementor-element-c1116cc{margin-top:0px;margin-bottom:0px;padding:0px 32px 70px 32px;}.elementor-1564 .elementor-element.elementor-element-f8b2d62 .elementor-headline{font-size:var( --e-global-typography-vamtam_h1-font-size );line-height:var( --e-global-typography-vamtam_h1-line-height );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group > label{font-size:var( --e-global-typography-vamtam_h6-font-size );line-height:var( --e-global-typography-vamtam_h6-line-height );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-group .elementor-field, .elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-field-subgroup label{font-size:var( --e-global-typography-e3d0827-font-size );line-height:var( --e-global-typography-e3d0827-line-height );letter-spacing:var( --e-global-typography-e3d0827-letter-spacing );word-spacing:var( --e-global-typography-e3d0827-word-spacing );}.elementor-1564 .elementor-element.elementor-element-986ea87 .elementor-button{font-size:var( --e-global-typography-vamtam_h6-font-size );line-height:var( --e-global-typography-vamtam_h6-line-height );}.elementor-1564 .elementor-element.elementor-element-986ea87 .e-form__indicators__indicator, .elementor-1564 .elementor-element.elementor-element-986ea87 .e-form__indicators__indicator__label{font-size:var( --e-global-typography-8069482-font-size );line-height:var( --e-global-typography-8069482-line-height );letter-spacing:var( --e-global-typography-8069482-letter-spacing );word-spacing:var( --e-global-typography-8069482-word-spacing );}.elementor-1564 .elementor-element.elementor-element-986ea87{--e-form-steps-divider-gap:5px;}.elementor-1564 .elementor-element.elementor-element-14d7e43{margin-top:0px;margin-bottom:30px;}.elementor-1564 .elementor-element.elementor-element-73a8300 > .elementor-element-populated{margin:0px 0px 20px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;}.elementor-1564 .elementor-element.elementor-element-49eb98a{--icon-box-icon-margin:0px;}.elementor-1564 .elementor-element.elementor-element-6b1ab84{text-align:left;}.elementor-1564 .elementor-element.elementor-element-6b1ab84 > .elementor-widget-container{margin:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-f98d7b8{text-align:left;}.elementor-1564 .elementor-element.elementor-element-f98d7b8 > .elementor-widget-container{margin:0px 0px 10px 0px;}.elementor-1564 .elementor-element.elementor-element-4e2182d{text-align:left;}.elementor-1564 .elementor-element.elementor-element-4e2182d > .elementor-widget-container{margin:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-0a58fb9{text-align:left;font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-1989a7a > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-1564 .elementor-element.elementor-element-1989a7a > .elementor-element-populated{margin:0px 0px 0px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;}.elementor-1564 .elementor-element.elementor-element-7094ea7 .elementor-tab-content{font-size:var( --e-global-typography-f942773-font-size );line-height:var( --e-global-typography-f942773-line-height );letter-spacing:var( --e-global-typography-f942773-letter-spacing );word-spacing:var( --e-global-typography-f942773-word-spacing );}.elementor-1564 .elementor-element.elementor-element-7094ea7 > .elementor-widget-container{padding:0px 0px 0px 0px;}.elementor-1564 .elementor-element.elementor-element-46d8b34 .elementor-icon-box-wrapper{text-align:left;}.elementor-1564 .elementor-element.elementor-element-46d8b34{--icon-box-icon-margin:10px;}.elementor-1564 .elementor-element.elementor-element-46d8b34 .elementor-icon-box-title, .elementor-1564 .elementor-element.elementor-element-46d8b34 .elementor-icon-box-title a{font-size:var( --e-global-typography-e3d0827-font-size );line-height:var( --e-global-typography-e3d0827-line-height );letter-spacing:var( --e-global-typography-e3d0827-letter-spacing );word-spacing:var( --e-global-typography-e3d0827-word-spacing );}}@media(min-width:768px){.elementor-1564 .elementor-element.elementor-element-4651da9{width:40%;}.elementor-1564 .elementor-element.elementor-element-65ef1c5{width:60%;}}@media(max-width:1024px) and (min-width:768px){.elementor-1564 .elementor-element.elementor-element-5691ef1{width:45%;}.elementor-1564 .elementor-element.elementor-element-993e265{width:55%;}} diff --git a/post-6541.css b/post-6541.css new file mode 100644 index 0000000..9951472 --- /dev/null +++ b/post-6541.css @@ -0,0 +1 @@ +.elementor-6541 .elementor-element.elementor-element-fb6e69b, .elementor-6541 .elementor-element.elementor-element-fb6e69b > .elementor-background-overlay{border-radius:18px 18px 18px 18px;}.elementor-6541 .elementor-element.elementor-element-fb6e69b{transition:background 0.3s, border 0.3s, border-radius 0.3s, box-shadow 0.3s;padding:0px 0px 0px 0px;}.elementor-6541 .elementor-element.elementor-element-fb6e69b > .elementor-background-overlay{transition:background 0.3s, border-radius 0.3s, opacity 0.3s;}.elementor-bc-flex-widget .elementor-6541 .elementor-element.elementor-element-1302c36.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-6541 .elementor-element.elementor-element-1302c36.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-6541 .elementor-element.elementor-element-1302c36 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-6541 .elementor-element.elementor-element-1302c36 > .elementor-element-populated{padding:30px 0px 0px 0px;}.elementor-6541 .elementor-element.elementor-element-e2bdea1{--spacer-size:440px;}.elementor-6541 .elementor-element.elementor-element-e2bdea1 > .elementor-widget-container{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://www.goodwalk.co.nz/wp-content/uploads/2022/08/review-archive-2.jpg");background-position:bottom center;background-repeat:no-repeat;background-size:contain;}.elementor-bc-flex-widget .elementor-6541 .elementor-element.elementor-element-f7837e0.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-6541 .elementor-element.elementor-element-f7837e0.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-6541 .elementor-element.elementor-element-f7837e0 > .elementor-element-populated{padding:0px 30px 145px 50px;}.elementor-6541 .elementor-element.elementor-element-717a84c > .elementor-widget-container{margin:0px 0px 15px 0px;}.elementor-6541 .elementor-element.elementor-element-717a84c{width:var( --container-widget-width, 390px );max-width:390px;--container-widget-width:390px;--container-widget-flex-grow:0;}.elementor-6541 .elementor-element.elementor-element-62bcb5c .elementor-heading-title{font-weight:500;}.elementor-6541 .elementor-element.elementor-element-62bcb5c{width:var( --container-widget-width, 390px );max-width:390px;--container-widget-width:390px;--container-widget-flex-grow:0;}@media(max-width:1024px){.elementor-6541 .elementor-element.elementor-element-1302c36 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-6541 .elementor-element.elementor-element-e2bdea1{--spacer-size:430px;}.elementor-6541 .elementor-element.elementor-element-f7837e0 > .elementor-element-populated{padding:0px 30px 160px 30px;}.elementor-6541 .elementor-element.elementor-element-717a84c{width:100%;max-width:100%;}.elementor-6541 .elementor-element.elementor-element-62bcb5c{width:100%;max-width:100%;}}@media(max-width:767px){.elementor-6541 .elementor-element.elementor-element-fb6e69b{padding:0px 0px 150px 0px;}.elementor-6541 .elementor-element.elementor-element-1302c36 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-6541 .elementor-element.elementor-element-1302c36 > .elementor-element-populated{margin:0px 0px 20px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:20px 20px 0px 20px;}.elementor-6541 .elementor-element.elementor-element-e2bdea1{--spacer-size:240px;}.elementor-6541 .elementor-element.elementor-element-e2bdea1 > .elementor-widget-container{background-size:contain;}.elementor-bc-flex-widget .elementor-6541 .elementor-element.elementor-element-f7837e0.elementor-column .elementor-widget-wrap{align-items:flex-start;}.elementor-6541 .elementor-element.elementor-element-f7837e0.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-start;align-items:flex-start;}.elementor-6541 .elementor-element.elementor-element-f7837e0 > .elementor-element-populated{margin:0px 0px 0px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:0px 40px 0px 40px;}}@media(min-width:768px){.elementor-6541 .elementor-element.elementor-element-1302c36{width:45%;}.elementor-6541 .elementor-element.elementor-element-f7837e0{width:55%;}} diff --git a/post-6631.css b/post-6631.css new file mode 100644 index 0000000..d08aaf4 --- /dev/null +++ b/post-6631.css @@ -0,0 +1 @@ +.elementor-6631 .elementor-element.elementor-element-ddfb808, .elementor-6631 .elementor-element.elementor-element-ddfb808 > .elementor-background-overlay{border-radius:18px 18px 18px 18px;}.elementor-6631 .elementor-element.elementor-element-ddfb808{transition:background 0.3s, border 0.3s, border-radius 0.3s, box-shadow 0.3s;padding:0px 0px 0px 0px;}.elementor-6631 .elementor-element.elementor-element-ddfb808 > .elementor-background-overlay{transition:background 0.3s, border-radius 0.3s, opacity 0.3s;}.elementor-bc-flex-widget .elementor-6631 .elementor-element.elementor-element-a62debb.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-6631 .elementor-element.elementor-element-a62debb.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-6631 .elementor-element.elementor-element-a62debb > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-6631 .elementor-element.elementor-element-a62debb > .elementor-element-populated{padding:30px 0px 0px 0px;}.elementor-6631 .elementor-element.elementor-element-e20c5d3{--spacer-size:440px;}.elementor-6631 .elementor-element.elementor-element-e20c5d3 > .elementor-widget-container{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://www.goodwalk.co.nz/wp-content/uploads/2022/08/review-monty.jpg");background-position:bottom center;background-repeat:no-repeat;background-size:contain;}.elementor-bc-flex-widget .elementor-6631 .elementor-element.elementor-element-24afe2c.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-6631 .elementor-element.elementor-element-24afe2c.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-6631 .elementor-element.elementor-element-24afe2c > .elementor-element-populated{padding:0px 30px 145px 50px;}.elementor-6631 .elementor-element.elementor-element-25d4f7c > .elementor-widget-container{margin:0px 0px 15px 0px;}.elementor-6631 .elementor-element.elementor-element-25d4f7c{width:var( --container-widget-width, 390px );max-width:390px;--container-widget-width:390px;--container-widget-flex-grow:0;}.elementor-6631 .elementor-element.elementor-element-41f2fa8 .elementor-heading-title{font-weight:300;}.elementor-6631 .elementor-element.elementor-element-41f2fa8{width:var( --container-widget-width, 390px );max-width:390px;--container-widget-width:390px;--container-widget-flex-grow:0;}@media(max-width:1024px){.elementor-6631 .elementor-element.elementor-element-a62debb > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-6631 .elementor-element.elementor-element-e20c5d3{--spacer-size:430px;}.elementor-6631 .elementor-element.elementor-element-24afe2c > .elementor-element-populated{padding:0px 30px 160px 30px;}.elementor-6631 .elementor-element.elementor-element-25d4f7c{width:100%;max-width:100%;}.elementor-6631 .elementor-element.elementor-element-41f2fa8{width:100%;max-width:100%;}}@media(max-width:767px){.elementor-6631 .elementor-element.elementor-element-ddfb808{padding:0px 0px 150px 0px;}.elementor-6631 .elementor-element.elementor-element-a62debb > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-6631 .elementor-element.elementor-element-a62debb > .elementor-element-populated{margin:0px 0px 20px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:20px 20px 0px 20px;}.elementor-6631 .elementor-element.elementor-element-e20c5d3{--spacer-size:240px;}.elementor-6631 .elementor-element.elementor-element-e20c5d3 > .elementor-widget-container{background-size:contain;}.elementor-bc-flex-widget .elementor-6631 .elementor-element.elementor-element-24afe2c.elementor-column .elementor-widget-wrap{align-items:flex-start;}.elementor-6631 .elementor-element.elementor-element-24afe2c.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-start;align-items:flex-start;}.elementor-6631 .elementor-element.elementor-element-24afe2c > .elementor-element-populated{margin:0px 0px 0px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:0px 40px 0px 40px;}}@media(min-width:768px){.elementor-6631 .elementor-element.elementor-element-a62debb{width:45%;}.elementor-6631 .elementor-element.elementor-element-24afe2c{width:55%;}} diff --git a/post-988267.css b/post-988267.css new file mode 100644 index 0000000..2f1dc39 --- /dev/null +++ b/post-988267.css @@ -0,0 +1 @@ +.elementor-988267 .elementor-element.elementor-element-fb6e69b, .elementor-988267 .elementor-element.elementor-element-fb6e69b > .elementor-background-overlay{border-radius:18px 18px 18px 18px;}.elementor-988267 .elementor-element.elementor-element-fb6e69b{transition:background 0.3s, border 0.3s, border-radius 0.3s, box-shadow 0.3s;padding:0px 0px 0px 0px;}.elementor-988267 .elementor-element.elementor-element-fb6e69b > .elementor-background-overlay{transition:background 0.3s, border-radius 0.3s, opacity 0.3s;}.elementor-bc-flex-widget .elementor-988267 .elementor-element.elementor-element-1302c36.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-988267 .elementor-element.elementor-element-1302c36.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-988267 .elementor-element.elementor-element-1302c36 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-988267 .elementor-element.elementor-element-1302c36 > .elementor-element-populated{padding:30px 0px 0px 0px;}.elementor-988267 .elementor-element.elementor-element-e2bdea1{--spacer-size:440px;}.elementor-988267 .elementor-element.elementor-element-e2bdea1 > .elementor-widget-container{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://www.goodwalk.co.nz/wp-content/uploads/2024/03/otis.jpg");background-position:bottom center;background-repeat:no-repeat;background-size:contain;}.elementor-bc-flex-widget .elementor-988267 .elementor-element.elementor-element-f7837e0.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-988267 .elementor-element.elementor-element-f7837e0.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-988267 .elementor-element.elementor-element-f7837e0 > .elementor-element-populated{padding:0px 30px 145px 50px;}.elementor-988267 .elementor-element.elementor-element-717a84c > .elementor-widget-container{margin:0px 0px 15px 0px;}.elementor-988267 .elementor-element.elementor-element-717a84c{width:var( --container-widget-width, 390px );max-width:390px;--container-widget-width:390px;--container-widget-flex-grow:0;}.elementor-988267 .elementor-element.elementor-element-62bcb5c .elementor-heading-title{font-weight:500;}.elementor-988267 .elementor-element.elementor-element-62bcb5c{width:var( --container-widget-width, 390px );max-width:390px;--container-widget-width:390px;--container-widget-flex-grow:0;}@media(max-width:1024px){.elementor-988267 .elementor-element.elementor-element-1302c36 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-988267 .elementor-element.elementor-element-e2bdea1{--spacer-size:430px;}.elementor-988267 .elementor-element.elementor-element-f7837e0 > .elementor-element-populated{padding:0px 30px 160px 30px;}.elementor-988267 .elementor-element.elementor-element-717a84c{width:100%;max-width:100%;}.elementor-988267 .elementor-element.elementor-element-62bcb5c{width:100%;max-width:100%;}}@media(max-width:767px){.elementor-988267 .elementor-element.elementor-element-fb6e69b{padding:0px 0px 150px 0px;}.elementor-988267 .elementor-element.elementor-element-1302c36 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-988267 .elementor-element.elementor-element-1302c36 > .elementor-element-populated{margin:0px 0px 20px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:20px 20px 0px 20px;}.elementor-988267 .elementor-element.elementor-element-e2bdea1{--spacer-size:240px;}.elementor-988267 .elementor-element.elementor-element-e2bdea1 > .elementor-widget-container{background-size:contain;}.elementor-bc-flex-widget .elementor-988267 .elementor-element.elementor-element-f7837e0.elementor-column .elementor-widget-wrap{align-items:flex-start;}.elementor-988267 .elementor-element.elementor-element-f7837e0.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-start;align-items:flex-start;}.elementor-988267 .elementor-element.elementor-element-f7837e0 > .elementor-element-populated{margin:0px 0px 0px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:0px 40px 0px 40px;}}@media(min-width:768px){.elementor-988267 .elementor-element.elementor-element-1302c36{width:45%;}.elementor-988267 .elementor-element.elementor-element-f7837e0{width:55%;}} diff --git a/post-988273.css b/post-988273.css new file mode 100644 index 0000000..6e5afc6 --- /dev/null +++ b/post-988273.css @@ -0,0 +1 @@ +.elementor-988273 .elementor-element.elementor-element-fb6e69b, .elementor-988273 .elementor-element.elementor-element-fb6e69b > .elementor-background-overlay{border-radius:18px 18px 18px 18px;}.elementor-988273 .elementor-element.elementor-element-fb6e69b{transition:background 0.3s, border 0.3s, border-radius 0.3s, box-shadow 0.3s;padding:0px 0px 0px 0px;}.elementor-988273 .elementor-element.elementor-element-fb6e69b > .elementor-background-overlay{transition:background 0.3s, border-radius 0.3s, opacity 0.3s;}.elementor-bc-flex-widget .elementor-988273 .elementor-element.elementor-element-1302c36.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-988273 .elementor-element.elementor-element-1302c36.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-988273 .elementor-element.elementor-element-1302c36 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-988273 .elementor-element.elementor-element-1302c36 > .elementor-element-populated{padding:30px 0px 0px 0px;}.elementor-988273 .elementor-element.elementor-element-e2bdea1{--spacer-size:440px;}.elementor-988273 .elementor-element.elementor-element-e2bdea1 > .elementor-widget-container{background-image:var(--e-bg-lazyload-loaded);--e-bg-lazyload:url("https://www.goodwalk.co.nz/wp-content/uploads/2024/04/wallace-v2.jpg");background-position:bottom center;background-repeat:no-repeat;background-size:contain;}.elementor-bc-flex-widget .elementor-988273 .elementor-element.elementor-element-f7837e0.elementor-column .elementor-widget-wrap{align-items:flex-end;}.elementor-988273 .elementor-element.elementor-element-f7837e0.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-end;align-items:flex-end;}.elementor-988273 .elementor-element.elementor-element-f7837e0 > .elementor-element-populated{padding:0px 30px 145px 50px;}.elementor-988273 .elementor-element.elementor-element-717a84c > .elementor-widget-container{margin:0px 0px 15px 0px;}.elementor-988273 .elementor-element.elementor-element-717a84c{width:var( --container-widget-width, 390px );max-width:390px;--container-widget-width:390px;--container-widget-flex-grow:0;}.elementor-988273 .elementor-element.elementor-element-62bcb5c .elementor-heading-title{font-weight:500;}.elementor-988273 .elementor-element.elementor-element-62bcb5c{width:var( --container-widget-width, 390px );max-width:390px;--container-widget-width:390px;--container-widget-flex-grow:0;}@media(max-width:1024px){.elementor-988273 .elementor-element.elementor-element-1302c36 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-988273 .elementor-element.elementor-element-e2bdea1{--spacer-size:430px;}.elementor-988273 .elementor-element.elementor-element-f7837e0 > .elementor-element-populated{padding:0px 30px 160px 30px;}.elementor-988273 .elementor-element.elementor-element-717a84c{width:100%;max-width:100%;}.elementor-988273 .elementor-element.elementor-element-62bcb5c{width:100%;max-width:100%;}}@media(max-width:767px){.elementor-988273 .elementor-element.elementor-element-fb6e69b{padding:0px 0px 150px 0px;}.elementor-988273 .elementor-element.elementor-element-1302c36 > .elementor-widget-wrap > .elementor-widget:not(.elementor-widget__width-auto):not(.elementor-widget__width-initial):not(:last-child):not(.elementor-absolute){margin-bottom:0px;}.elementor-988273 .elementor-element.elementor-element-1302c36 > .elementor-element-populated{margin:0px 0px 20px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:20px 20px 0px 20px;}.elementor-988273 .elementor-element.elementor-element-e2bdea1{--spacer-size:240px;}.elementor-988273 .elementor-element.elementor-element-e2bdea1 > .elementor-widget-container{background-size:contain;}.elementor-bc-flex-widget .elementor-988273 .elementor-element.elementor-element-f7837e0.elementor-column .elementor-widget-wrap{align-items:flex-start;}.elementor-988273 .elementor-element.elementor-element-f7837e0.elementor-column.elementor-element[data-element_type="column"] > .elementor-widget-wrap.elementor-element-populated{align-content:flex-start;align-items:flex-start;}.elementor-988273 .elementor-element.elementor-element-f7837e0 > .elementor-element-populated{margin:0px 0px 0px 0px;--e-column-margin-right:0px;--e-column-margin-left:0px;padding:0px 40px 0px 40px;}}@media(min-width:768px){.elementor-988273 .elementor-element.elementor-element-1302c36{width:45%;}.elementor-988273 .elementor-element.elementor-element-f7837e0{width:55%;}} diff --git a/scripts/common.ps1 b/scripts/common.ps1 new file mode 100644 index 0000000..a17e4f0 --- /dev/null +++ b/scripts/common.ps1 @@ -0,0 +1,102 @@ +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Write-Step { + param([string]$Message) + Write-Host "==> $Message" -ForegroundColor Cyan +} + +function Write-Note { + param([string]$Message) + Write-Host " -> $Message" -ForegroundColor DarkGray +} + +function Assert-Command { + param([string]$Name) + + if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { + throw "Required command '$Name' was not found in PATH." + } +} + +function Resolve-AbsolutePath { + param( + [Parameter(Mandatory = $true)][string]$BasePath, + [Parameter(Mandatory = $true)][string]$Path + ) + + if ([System.IO.Path]::IsPathRooted($Path)) { + return [System.IO.Path]::GetFullPath($Path) + } + + return [System.IO.Path]::GetFullPath((Join-Path $BasePath $Path)) +} + +function Invoke-Checked { + param( + [Parameter(Mandatory = $true)][string]$FilePath, + [Parameter(Mandatory = $true)][string[]]$Arguments, + [string]$WorkingDirectory + ) + + if ($WorkingDirectory) { + Push-Location $WorkingDirectory + } + + try { + & $FilePath @Arguments + + if ($LASTEXITCODE -ne 0) { + throw "Command failed: $FilePath $($Arguments -join ' ')" + } + } finally { + if ($WorkingDirectory) { + Pop-Location + } + } +} + +function Invoke-DockerCompose { + param( + [Parameter(Mandatory = $true)][string]$ComposeFile, + [Parameter(Mandatory = $true)][string[]]$Arguments, + [string]$ProjectName, + [string]$WorkingDirectory + ) + + $dockerArgs = @('compose', '-f', $ComposeFile) + + if ($ProjectName) { + $dockerArgs += @('-p', $ProjectName) + } + + $dockerArgs += $Arguments + + Invoke-Checked -FilePath 'docker' -Arguments $dockerArgs -WorkingDirectory $WorkingDirectory +} + +function Wait-ForHttpOk { + param( + [Parameter(Mandatory = $true)][string]$Url, + [int]$TimeoutSeconds = 120 + ) + + $deadline = (Get-Date).AddSeconds($TimeoutSeconds) + + while ((Get-Date) -lt $deadline) { + try { + $response = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 10 + + if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 300) { + return + } + } catch { + Start-Sleep -Seconds 2 + continue + } + + Start-Sleep -Seconds 2 + } + + throw "Timed out waiting for a healthy response from $Url." +} diff --git a/scripts/deploy.ps1 b/scripts/deploy.ps1 new file mode 100644 index 0000000..6b372f7 --- /dev/null +++ b/scripts/deploy.ps1 @@ -0,0 +1,76 @@ +[CmdletBinding()] +param( + [string]$ComposeFile = 'docker-compose.yml', + [string]$ProjectName = 'goodwalk', + [switch]$RunMigration, + [string]$LegacyComposeFile, + [string]$LegacyProjectName = 'legacy-goodwalk', + [string]$LegacyWordPressContainer, + [string]$LegacyDatabaseContainer, + [string]$LegacyUploadsPath = '/var/www/html/wp-content/uploads', + [string]$MySqlDatabase, + [string]$MySqlUser, + [string]$MySqlPassword, + [switch]$SkipLegacyShutdown, + [switch]$SkipBuild, + [int]$HealthTimeoutSeconds = 120, + [string]$HealthUrl = 'http://localhost/api/health' +) + +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +. (Join-Path $scriptDir 'common.ps1') + +$repoRoot = Split-Path -Parent $scriptDir +$composeFilePath = Resolve-AbsolutePath -BasePath $repoRoot -Path $ComposeFile + +Write-Step 'Validating deployment prerequisites' +Assert-Command docker +Invoke-DockerCompose -ComposeFile $composeFilePath -ProjectName $ProjectName -WorkingDirectory $repoRoot -Arguments @('config') + +if ($RunMigration) { + Write-Step 'Running the WordPress migration step' + $migrationScript = Join-Path $scriptDir 'migrate-wordpress.ps1' + $migrationArgs = @( + '-LegacyWordPressContainer', $LegacyWordPressContainer, + '-LegacyUploadsPath', $LegacyUploadsPath + ) + + if ($LegacyDatabaseContainer) { + $migrationArgs += @('-LegacyDatabaseContainer', $LegacyDatabaseContainer) + } else { + $migrationArgs += '-SkipDatabaseDump' + } + + if ($MySqlDatabase) { $migrationArgs += @('-MySqlDatabase', $MySqlDatabase) } + if ($MySqlUser) { $migrationArgs += @('-MySqlUser', $MySqlUser) } + if ($MySqlPassword) { $migrationArgs += @('-MySqlPassword', $MySqlPassword) } + + & $migrationScript @migrationArgs + + if ($LASTEXITCODE -ne 0) { + throw 'The WordPress migration step failed.' + } +} + +if ($LegacyComposeFile -and -not $SkipLegacyShutdown) { + $legacyComposeFilePath = Resolve-AbsolutePath -BasePath $repoRoot -Path $LegacyComposeFile + Write-Step 'Stopping the legacy WordPress stack' + Invoke-DockerCompose -ComposeFile $legacyComposeFilePath -ProjectName $LegacyProjectName -WorkingDirectory $repoRoot -Arguments @('down') +} + +Write-Step 'Building and starting the new stack' +$upArgs = @('up', '-d', '--remove-orphans') +if (-not $SkipBuild) { + $upArgs += '--build' +} + +Invoke-DockerCompose -ComposeFile $composeFilePath -ProjectName $ProjectName -WorkingDirectory $repoRoot -Arguments $upArgs + +Write-Step 'Waiting for the new site to become healthy' +Wait-ForHttpOk -Url $HealthUrl -TimeoutSeconds $HealthTimeoutSeconds + +Write-Step 'Current container status' +Invoke-DockerCompose -ComposeFile $composeFilePath -ProjectName $ProjectName -WorkingDirectory $repoRoot -Arguments @('ps') + +Write-Step 'Deployment completed' +Write-Host "Health check passed at $HealthUrl" diff --git a/scripts/migrate-wordpress.ps1 b/scripts/migrate-wordpress.ps1 new file mode 100644 index 0000000..12df603 --- /dev/null +++ b/scripts/migrate-wordpress.ps1 @@ -0,0 +1,78 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)][string]$LegacyWordPressContainer, + [string]$LegacyDatabaseContainer, + [string]$LegacyUploadsPath = '/var/www/html/wp-content/uploads', + [string]$StaticUploadsTarget = 'static/wp-content/uploads', + [string]$BackupRoot = 'migration-backups', + [string]$MySqlDatabase, + [string]$MySqlUser, + [string]$MySqlPassword, + [switch]$SkipUploads, + [switch]$SkipDatabaseDump +) + +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +. (Join-Path $scriptDir 'common.ps1') + +$repoRoot = Split-Path -Parent $scriptDir +$backupRootPath = Resolve-AbsolutePath -BasePath $repoRoot -Path $BackupRoot +$staticUploadsPath = Resolve-AbsolutePath -BasePath $repoRoot -Path $StaticUploadsTarget +$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss' +$runRoot = Join-Path $backupRootPath $timestamp +$stagingRoot = Join-Path $runRoot 'uploads-staging' +$uploadsArchivePath = Join-Path $runRoot 'uploads' +$sqlDumpPath = Join-Path $runRoot 'wordpress.sql' + +Write-Step 'Preparing WordPress migration workspace' +Assert-Command docker +New-Item -ItemType Directory -Force -Path $runRoot | Out-Null + +if (-not $SkipDatabaseDump) { + if (-not $LegacyDatabaseContainer) { + throw 'LegacyDatabaseContainer is required unless -SkipDatabaseDump is used.' + } + + if (-not $MySqlDatabase -or -not $MySqlUser -or -not $MySqlPassword) { + throw 'MySqlDatabase, MySqlUser, and MySqlPassword are required unless -SkipDatabaseDump is used.' + } + + Write-Step 'Dumping the legacy WordPress database' + $dumpCommand = "exec mysqldump --single-transaction --quick --lock-tables=false -u$MySqlUser -p`"$MySqlPassword`" $MySqlDatabase" + $dumpOutput = & docker exec $LegacyDatabaseContainer sh -lc $dumpCommand + + if ($LASTEXITCODE -ne 0) { + throw 'mysqldump failed.' + } + + [System.IO.File]::WriteAllText($sqlDumpPath, ($dumpOutput -join [Environment]::NewLine)) + Write-Note "Database dump saved to $sqlDumpPath" +} + +if (-not $SkipUploads) { + Write-Step 'Copying wp-content/uploads from the legacy WordPress container' + New-Item -ItemType Directory -Force -Path $stagingRoot | Out-Null + + $sourceSpec = '{0}:{1}' -f $LegacyWordPressContainer, $LegacyUploadsPath + Invoke-Checked -FilePath 'docker' -Arguments @('cp', $sourceSpec, $stagingRoot) + + $copiedUploads = Join-Path $stagingRoot 'uploads' + + if (-not (Test-Path $copiedUploads)) { + throw "Expected copied uploads at $copiedUploads but it was not found." + } + + New-Item -ItemType Directory -Force -Path (Split-Path -Parent $staticUploadsPath) | Out-Null + if (Test-Path $staticUploadsPath) { + Remove-Item -LiteralPath $staticUploadsPath -Recurse -Force + } + + Move-Item -LiteralPath $copiedUploads -Destination $uploadsArchivePath + Copy-Item -LiteralPath $uploadsArchivePath -Destination $staticUploadsPath -Recurse -Force + + Write-Note "Uploads copied into $staticUploadsPath" + Write-Note "Archive copy saved to $uploadsArchivePath" +} + +Write-Step 'Migration artifacts prepared' +Write-Host "Backup folder: $runRoot" diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000..2e40ce0 --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,9 @@ +declare global { + namespace App { + interface PageData { + content: import('$lib/types').HomePageContent | import('$lib/types').SiteSharedContent; + } + } +} + +export {}; diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000..0b8bb65 --- /dev/null +++ b/src/app.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/lib/actions/reveal.ts b/src/lib/actions/reveal.ts new file mode 100644 index 0000000..967fe44 --- /dev/null +++ b/src/lib/actions/reveal.ts @@ -0,0 +1,53 @@ +type RevealOptions = { + delay?: number; + distance?: number; + threshold?: number; +}; + +const defaultOptions: Required = { + delay: 0, + distance: 24, + threshold: 0.18 +}; + +export function reveal(node: HTMLElement, options: RevealOptions = {}) { + const settings = { ...defaultOptions, ...options }; + const media = window.matchMedia('(prefers-reduced-motion: reduce)'); + + if (media.matches) { + node.classList.add('reveal-visible'); + return { + destroy() {} + }; + } + + node.style.setProperty('--reveal-delay', `${settings.delay}ms`); + node.style.setProperty('--reveal-distance', `${settings.distance}px`); + node.classList.add('reveal-ready'); + + const observer = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + if (!entry.isIntersecting) { + continue; + } + + node.classList.add('reveal-visible'); + observer.disconnect(); + break; + } + }, + { + threshold: settings.threshold, + rootMargin: '0px 0px -8% 0px' + } + ); + + observer.observe(node); + + return { + destroy() { + observer.disconnect(); + } + }; +} diff --git a/src/lib/components/AboutPage.svelte b/src/lib/components/AboutPage.svelte new file mode 100644 index 0000000..fce2e2b --- /dev/null +++ b/src/lib/components/AboutPage.svelte @@ -0,0 +1,334 @@ + + +
+
+
+

{pageContent.title}

+
+
+ + {#each pageContent.sections as section} +
+
+
+

{section.title}

+ {#each section.body as paragraph} +

{paragraph}

+ {/each} +
+ +
+ {section.imageAlt} +
+
+
+ {/each} + +
+
+
+

{pageContent.servicesTitle}

+
+ +
+ {#each content.services as service} + + + {service.title} + + {/each} +
+
+
+ +
+ +
+
+ + diff --git a/src/lib/components/BookingPage.svelte b/src/lib/components/BookingPage.svelte new file mode 100644 index 0000000..2d2bf20 --- /dev/null +++ b/src/lib/components/BookingPage.svelte @@ -0,0 +1,112 @@ + + +
+
+
+

Book a Meet & Greet

+

Fill in the form below and we'll be in touch to arrange a free introduction.

+ +
+
+ + +
+ + diff --git a/src/lib/components/BookingSection.svelte b/src/lib/components/BookingSection.svelte new file mode 100644 index 0000000..435e034 --- /dev/null +++ b/src/lib/components/BookingSection.svelte @@ -0,0 +1,416 @@ + + +
+
+ + {#if submitted} + (submitted = false)} + /> + {/if} + +
+

+ {headingParts.plain} + {headingParts.highlight} +

+ +
+ + + +
+
+ +
+ {#if step === 1} +
+ {#if hasBanner} +
{booking.subtitle}
+ {/if} + +
+
+
+
+ + clearError('fullName')} + /> + {#if errors.fullName} +

+ + {errors.fullName} +

+ {/if} +
+ +
+ + clearError('email')} + /> + {#if errors.email} +

+ + {errors.email} +

+ {/if} +
+ +
+ + clearError('phone')} + /> + {#if errors.phone} +

+ + {errors.phone} +

+ {/if} +
+
+
+
+ + {#if hasServices} +
+  Services +
+ {#each booking.serviceOptions as service} + + {/each} +
+
+ {/if} +
+ +
+ +
+ {:else} + + + + {#each selectedServices as service} + + {/each} + +
+ {#if dogIntro} +
{dogIntro}
+ {/if} + +
+
+ + clearError('petName')} + /> + {#if errors.petName} +

+ + {errors.petName} +

+ {/if} +
+ +
+ + clearError('location')} + /> + {#if errors.location} +

+ + {errors.location} +

+ {/if} +
+ +
+ + +
+
+
+ +
+ + +
+ + {#if submitError} +

+ + {submitError} +

+ {/if} + {/if} +
+
+
+ + diff --git a/src/lib/components/BookingSection.test.ts b/src/lib/components/BookingSection.test.ts new file mode 100644 index 0000000..0f84386 --- /dev/null +++ b/src/lib/components/BookingSection.test.ts @@ -0,0 +1,152 @@ +import { fireEvent, render, screen, waitFor } from '@testing-library/svelte'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import BookingSection from './BookingSection.svelte'; +import { homepageContent } from '$lib/content/homepage'; + +describe('BookingSection', () => { + beforeEach(() => { + Object.defineProperty(document, 'referrer', { + configurable: true, + value: 'https://www.google.com/' + }); + }); + + it('validates the owner details step before progressing', async () => { + const { container } = render(BookingSection, { + booking: homepageContent.booking + }); + + await fireEvent.click(container.querySelector('.booking-next-button')!); + + expect(screen.getByText('Please enter your full name')).toBeInTheDocument(); + expect(screen.getByText('Please enter a valid email address')).toBeInTheDocument(); + expect(screen.getByText('Please enter your contact number')).toBeInTheDocument(); + }); + + it('validates the dog details step before submitting', async () => { + const { container } = render(BookingSection, { + booking: homepageContent.booking + }); + + await fireEvent.input(screen.getByLabelText(/Full Name/i), { + target: { value: 'Alex Walker' } + }); + await fireEvent.input(screen.getByLabelText(/^Email/i), { + target: { value: 'alex@example.com' } + }); + await fireEvent.input(screen.getByLabelText(/Contact #/i), { + target: { value: '021 123 4567' } + }); + + await fireEvent.click(container.querySelector('.booking-next-button')!); + await fireEvent.click(container.querySelector('.booking-submit-button')!); + + expect(screen.getByText("Please enter your dog's name")).toBeInTheDocument(); + expect(screen.getByText('Please enter your location')).toBeInTheDocument(); + }); + + it('submits the completed booking flow and shows the success modal', async () => { + const fetchMock = vi.fn().mockResolvedValue({ + ok: true, + json: vi.fn().mockResolvedValue({}) + }); + vi.stubGlobal('fetch', fetchMock); + + const { container } = render(BookingSection, { + booking: homepageContent.booking + }); + + await fireEvent.click(screen.getByLabelText('Pack Walks')); + await fireEvent.click(screen.getByLabelText('Other Services')); + + await fireEvent.input(screen.getByLabelText(/Full Name/i), { + target: { value: 'Alex Walker' } + }); + await fireEvent.input(screen.getByLabelText(/^Email/i), { + target: { value: 'alex@example.com' } + }); + await fireEvent.input(screen.getByLabelText(/Contact #/i), { + target: { value: '021 123 4567' } + }); + + await fireEvent.click(container.querySelector('.booking-next-button')!); + + await fireEvent.input(screen.getByLabelText(/Pet's Name/i), { + target: { value: 'Maya' } + }); + await fireEvent.input(screen.getByLabelText(/Location/i), { + target: { value: 'Kingsland' } + }); + await fireEvent.input(screen.getByLabelText(/About Your Dog/i), { + target: { value: 'Loves small group walks.' } + }); + + await fireEvent.click(container.querySelector('.booking-submit-button')!); + + await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1)); + expect(fetchMock).toHaveBeenCalledWith( + '/api/submit', + expect.objectContaining({ + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }) + ); + + const payload = JSON.parse(fetchMock.mock.calls[0][1].body as string); + expect(payload).toMatchObject({ + fullName: 'Alex Walker', + email: 'alex@example.com', + phone: '021 123 4567', + petName: 'Maya', + location: 'Kingsland', + message: 'Loves small group walks.', + services: ['Pack Walks', 'Other Services'], + referrer: 'https://www.google.com/' + }); + + expect(screen.getByRole('dialog', { name: /Booking confirmed/i })).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: /on our radar/i })).toBeInTheDocument(); + + await fireEvent.click(screen.getByRole('button', { name: /Sounds great!/i })); + + await waitFor(() => + expect(screen.queryByRole('dialog', { name: /Booking confirmed/i })).not.toBeInTheDocument() + ); + }); + + it('shows the API error message when submission fails', async () => { + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue({ + ok: false, + json: vi.fn().mockResolvedValue({ detail: 'Mail API unavailable' }) + }) + ); + + const { container } = render(BookingSection, { + booking: homepageContent.booking + }); + + await fireEvent.input(screen.getByLabelText(/Full Name/i), { + target: { value: 'Alex Walker' } + }); + await fireEvent.input(screen.getByLabelText(/^Email/i), { + target: { value: 'alex@example.com' } + }); + await fireEvent.input(screen.getByLabelText(/Contact #/i), { + target: { value: '021 123 4567' } + }); + await fireEvent.click(container.querySelector('.booking-next-button')!); + + await fireEvent.input(screen.getByLabelText(/Pet's Name/i), { + target: { value: 'Maya' } + }); + await fireEvent.input(screen.getByLabelText(/Location/i), { + target: { value: 'Kingsland' } + }); + + await fireEvent.click(container.querySelector('.booking-submit-button')!); + + expect(await screen.findByText('Mail API unavailable')).toBeInTheDocument(); + }); +}); diff --git a/src/lib/components/Footer.svelte b/src/lib/components/Footer.svelte new file mode 100644 index 0000000..f54424f --- /dev/null +++ b/src/lib/components/Footer.svelte @@ -0,0 +1,97 @@ + + + diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte new file mode 100644 index 0000000..a2d8dda --- /dev/null +++ b/src/lib/components/Header.svelte @@ -0,0 +1,174 @@ + + +
+ + +
+ {#each navigation.mobileLinks as link} + + {link.label} + + {/each} +
+
diff --git a/src/lib/components/Header.test.ts b/src/lib/components/Header.test.ts new file mode 100644 index 0000000..0db7176 --- /dev/null +++ b/src/lib/components/Header.test.ts @@ -0,0 +1,43 @@ +import { fireEvent, render } from '@testing-library/svelte'; +import { describe, expect, it } from 'vitest'; +import Header from './Header.svelte'; +import { homepageContent } from '$lib/content/homepage'; +import { setMockPage } from '../../test/mocks/app-stores'; + +describe('Header', () => { + it('marks the services link active for service detail pages', () => { + setMockPage('https://www.goodwalk.co.nz/pack-walks'); + + const { container } = render(Header, { + navigation: homepageContent.navigation + }); + + expect(container.querySelector('a.nav-link-active[href="#services"]')).toBeTruthy(); + }); + + it('opens and closes the mobile menu', async () => { + Object.defineProperty(window, 'innerWidth', { + configurable: true, + writable: true, + value: 390 + }); + + const { container } = render(Header, { + navigation: homepageContent.navigation + }); + + const menuToggle = container.querySelector('.hamburger') as HTMLButtonElement; + const mobileMenu = container.querySelector('.mobile-menu') as HTMLDivElement; + const firstMobileLink = mobileMenu.querySelector('a') as HTMLAnchorElement; + + expect(menuToggle).toHaveAttribute('aria-expanded', 'false'); + + await fireEvent.click(menuToggle); + expect(menuToggle).toHaveAttribute('aria-expanded', 'true'); + expect(mobileMenu.classList.contains('open')).toBe(true); + + await fireEvent.click(firstMobileLink); + expect(menuToggle).toHaveAttribute('aria-expanded', 'false'); + expect(mobileMenu.classList.contains('open')).toBe(false); + }); +}); diff --git a/src/lib/components/HeroSection.svelte b/src/lib/components/HeroSection.svelte new file mode 100644 index 0000000..8dd23e6 --- /dev/null +++ b/src/lib/components/HeroSection.svelte @@ -0,0 +1,57 @@ + + +
+
+
+

+ + {titleParts.lead} + {#if titleParts.connector} + {titleParts.connector} + {/if} +
+ {hero.highlight} +
+ {mobileTitle} +

+ + +
+ +
+ {hero.imageAlt} +
+
+
diff --git a/src/lib/components/Icon.svelte b/src/lib/components/Icon.svelte new file mode 100644 index 0000000..8e76cc1 --- /dev/null +++ b/src/lib/components/Icon.svelte @@ -0,0 +1,20 @@ + + + + + diff --git a/src/lib/components/InfoSection.svelte b/src/lib/components/InfoSection.svelte new file mode 100644 index 0000000..489efbd --- /dev/null +++ b/src/lib/components/InfoSection.svelte @@ -0,0 +1,34 @@ + + +
+
+
+

{info.title}

+

{info.intro}

+

{info.suburbs}

+

+ {info.nearbyText} + {info.nearbyCta.label} +

+

{info.hoursLabel}

+

{info.hours}

+
+ +
+

{info.faqTitle}

+
+ {#each info.faqs as faq} +
+ {faq.question} +

{faq.answer}

+
+ {/each} +
+
+
+
diff --git a/src/lib/components/InstagramSection.svelte b/src/lib/components/InstagramSection.svelte new file mode 100644 index 0000000..31f6a87 --- /dev/null +++ b/src/lib/components/InstagramSection.svelte @@ -0,0 +1,15 @@ + + +
+

{instagram.title}

+

See our dogs in action — walks, play, and happy pups

+ + + {instagram.label} + +
diff --git a/src/lib/components/IntroStrip.svelte b/src/lib/components/IntroStrip.svelte new file mode 100644 index 0000000..8f6fa4b --- /dev/null +++ b/src/lib/components/IntroStrip.svelte @@ -0,0 +1,32 @@ + + +
+
+ + +
+

{intro.text}

+ +
+
+ {#each stars as _, index} + + {/each} +
+ + + {intro.reviewCta.label} + +
+
+
+
diff --git a/src/lib/components/LegalPage.svelte b/src/lib/components/LegalPage.svelte new file mode 100644 index 0000000..f4a75e0 --- /dev/null +++ b/src/lib/components/LegalPage.svelte @@ -0,0 +1,187 @@ + + +
+ + + +
+ + diff --git a/src/lib/components/PricingPage.svelte b/src/lib/components/PricingPage.svelte new file mode 100644 index 0000000..1b1efb1 --- /dev/null +++ b/src/lib/components/PricingPage.svelte @@ -0,0 +1,316 @@ + + +
+
+
+

{pageContent.title}

+ {#if pageContent.subtitle} +

{pageContent.subtitle}

+ {/if} +
+
+ + {#each pageContent.sections as section} +
+
+
+ {#if section.icon} +
+ +
+ {/if} +

{section.title}

+ {#if section.blurb} +

{section.blurb}

+ {/if} + {#if section.detailCta} + + {section.detailCta.label} + + {/if} +
+ +
+ {#each section.plans as plan} +
+ {#if plan.popular} + Popular + {/if} + +

{plan.title}

+
{plan.price}
+

{plan.period}

+ +
    + {#each plan.features as feature} +
  • {feature}
  • + {/each} +
+ + Book Now +
+ {/each} +
+
+
+ {/each} + + + +
+ + diff --git a/src/lib/components/PromiseSection.svelte b/src/lib/components/PromiseSection.svelte new file mode 100644 index 0000000..743b48b --- /dev/null +++ b/src/lib/components/PromiseSection.svelte @@ -0,0 +1,27 @@ + + +
+
+
+

+ {promise.title}
+ {promise.subtitle} +

+ +

+ {promise.body} + {promise.emphasis} +

+ + {promise.cta.label} +
+ +
+ {promise.imageAlt} +
+
+
diff --git a/src/lib/components/SeoHead.svelte b/src/lib/components/SeoHead.svelte new file mode 100644 index 0000000..11281f0 --- /dev/null +++ b/src/lib/components/SeoHead.svelte @@ -0,0 +1,75 @@ + + + + {pageTitle} + + + + + + + + {#if preloadImage} + + {/if} + + + + + + + + + + + + + + + + + + + + {#each structuredData as schema} + {@html ``} + {/each} + diff --git a/src/lib/components/ServiceLandingPage.svelte b/src/lib/components/ServiceLandingPage.svelte new file mode 100644 index 0000000..b1cf55a --- /dev/null +++ b/src/lib/components/ServiceLandingPage.svelte @@ -0,0 +1,482 @@ + + +
+
+
+
+

{pageContent.hero.eyebrow}

+

{pageContent.hero.title}

+ + {#each pageContent.hero.paragraphs as paragraph} +

{paragraph}

+ {/each} +
+ +
+ {pageContent.hero.imageAlt} +
+
+
+ + {#if pageContent.highlight} +
+
+

{pageContent.highlight.eyebrow}

+

{pageContent.highlight.title}

+
+ +
+
+ {pageContent.highlight.imageAlt} +
+
+
+ {/if} + +
+
+
+

{pageContent.pricing.title}

+ {#if pageContent.pricing.intro} +

{pageContent.pricing.intro}

+ {/if} +
+ +
+ {#each pageContent.pricing.plans as plan} +
+ {#if plan.popular} + Popular + {/if} + +

{plan.title}

+
{plan.price}
+

{plan.period}

+ +
    + {#each plan.features as feature} +
  • {feature}
  • + {/each} +
+ + Book Now +
+ {/each} +
+ + {#if pageContent.pricing.extras?.length} +
+
Extras
+ {#each pageContent.pricing.extras as extra} +
+ + {extra.label} + {#if extra.note} + {extra.note} + {/if} + + {extra.price} +
+ {/each} +
+ {/if} +
+
+ +
+
+
+

{pageContent.benefits.title}

+
+ +
+ {#each pageContent.benefits.items as benefit} +
+ +

{benefit.title}

+

{benefit.body}

+
+ {/each} +
+
+
+ + + +
+ + diff --git a/src/lib/components/ServicesSection.svelte b/src/lib/components/ServicesSection.svelte new file mode 100644 index 0000000..df0d9d4 --- /dev/null +++ b/src/lib/components/ServicesSection.svelte @@ -0,0 +1,29 @@ + + +
+
+

What we do

+ +
+ {#each services as service} +
+
+ +
+

{service.title}

+

{service.body}

+ + {#if service.href} + Learn more + {/if} +
+ {/each} +
+
+
diff --git a/src/lib/components/SuccessModal.svelte b/src/lib/components/SuccessModal.svelte new file mode 100644 index 0000000..f1c975f --- /dev/null +++ b/src/lib/components/SuccessModal.svelte @@ -0,0 +1,202 @@ + + + + + diff --git a/src/lib/components/TestimonialsSection.svelte b/src/lib/components/TestimonialsSection.svelte new file mode 100644 index 0000000..c131e3f --- /dev/null +++ b/src/lib/components/TestimonialsSection.svelte @@ -0,0 +1,623 @@ + + +
+
+

{heading}

+ + + {#if slides.length} + + {/if} +
+
+ + diff --git a/src/lib/components/TestimonialsSection.test.ts b/src/lib/components/TestimonialsSection.test.ts new file mode 100644 index 0000000..1792334 --- /dev/null +++ b/src/lib/components/TestimonialsSection.test.ts @@ -0,0 +1,40 @@ +import { fireEvent, render } from '@testing-library/svelte'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import TestimonialsSection from './TestimonialsSection.svelte'; +import { homepageContent } from '$lib/content/homepage'; + +describe('TestimonialsSection', () => { + afterEach(() => { + vi.useRealTimers(); + }); + + it('uses the mapped local image assets for known testimonials', () => { + const { container } = render(TestimonialsSection, { + testimonials: homepageContent.testimonials + }); + + const activeImage = container.querySelector('.testimonial-slide-active img') as HTMLImageElement; + + expect(activeImage.getAttribute('src')).toBe('/images/archie-auckland-dog-walking-review.jpg'); + }); + + it('moves to the next testimonial on arrow click and auto-rotation', async () => { + vi.useFakeTimers(); + + const { container } = render(TestimonialsSection, { + testimonials: homepageContent.testimonials + }); + + const nextButton = container.querySelector('.testimonial-arrow-right') as HTMLButtonElement; + const activeReviewer = () => + (container.querySelector('.testimonial-slide-active h6 strong') as HTMLElement).textContent; + + expect(activeReviewer()).toBe('Kate'); + + await fireEvent.click(nextButton); + expect(activeReviewer()).toBe('Estelle'); + + await vi.advanceTimersByTimeAsync(5000); + expect(activeReviewer()).toBe('Ross'); + }); +}); diff --git a/src/lib/components/ValuesSection.svelte b/src/lib/components/ValuesSection.svelte new file mode 100644 index 0000000..328288a --- /dev/null +++ b/src/lib/components/ValuesSection.svelte @@ -0,0 +1,22 @@ + + +
+
+

Where dogs come first

+ +
+ {#each values as value} +
+ +

{value.title}

+

{value.body}

+
+ {/each} +
+
+
diff --git a/src/lib/content/about.ts b/src/lib/content/about.ts new file mode 100644 index 0000000..8059cea --- /dev/null +++ b/src/lib/content/about.ts @@ -0,0 +1,47 @@ +import type { AboutPageContent } from '$lib/types'; + +export const aboutPageContent: AboutPageContent = { + title: 'About Us', + sections: [ + { + title: 'Who we are', + body: [ + "At GoodWalk, we're not your average dog walking service. We're a team of passionate dog lovers dedicated to providing top-notch care for your furry friends. Specializing in small dogs, we understand their unique needs firsthand, being small dog owners ourselves!", + "Our commitment to excellence has quickly made us a leader in Auckland Central's dog-walking scene. From pack walks to one-on-one sessions, we ensure the happiness and well-being of every dog in our care." + ], + imageUrl: '/images/auckland-pack-walk-dog.jpg', + imageAlt: 'Dog on a Goodwalk pack walk' + }, + { + title: 'Our impact', + body: [ + "At GoodWalk, we believe in positive reinforcement training to help your dog thrive in the world. Safety, professionalism, well-being, fun, structure, and compassion are the cornerstones of our business ethos.", + "When you choose GoodWalk, you're choosing a partner who will treat your dog like family, because that's exactly what they are to us." + ], + imageUrl: '/images/auckland-dog-group-outing.jpg', + imageAlt: 'Goodwalk dogs enjoying an outing together', + reverse: true, + accent: 'gradient' + }, + { + title: 'Meet the team', + body: [ + 'Behind GoodWalk is Alessandra, an Italian who has a deep passion for dogs. With her love for animals and years of experience, Alessandra leads our team with dedication and expertise, ensuring that every dog receives the love and attention they deserve.', + "And let's not forget about Maya, our marketing manager! A Cavalier King Charles cross Shih Tzu, Maya is full of sass and personality, bringing a touch of charm and flair to everything we do." + ], + imageUrl: '/images/goodwalk-dog-walker-alessandra.png', + imageAlt: 'Goodwalk staff member Aless' + } + ], + servicesTitle: 'Explore our services', + contact: { + title: "Let's get started!", + email: 'info@goodwalk.co.nz', + phone: '(022) 642 1011', + cta: { + label: 'Contact us', + href: '/booking', + variant: 'yellow' + } + } +}; diff --git a/src/lib/content/dog-walking.ts b/src/lib/content/dog-walking.ts new file mode 100644 index 0000000..35738d4 --- /dev/null +++ b/src/lib/content/dog-walking.ts @@ -0,0 +1,84 @@ +import type { ServicePageContent } from '$lib/types'; + +export const dogWalkingContent: ServicePageContent = { + hero: { + eyebrow: '1:1 Walks', + title: 'Walks for larger breeds, too!', + paragraphs: [ + 'Looking for personal attention for your big pup? Our professional dog walking service is perfect for larger dogs who walk well on leash and have good recall. Safety is paramount, so we only accommodate dogs with no reactivity issues. At Goodwalk, we pride ourselves on building relationships with both you and your furry family member.', + "Give your dog his best life while joining our growing community of happy pet parents! For those seeking extra care, we offer specialized one-on-one walks tailored to your dog's individual needs and personality" + ], + imageUrl: + '/images/auckland-large-dog-one-on-one-walk.jpg', + imageAlt: 'Large breed dog enjoying a Goodwalk one on one dog walk' + }, + highlight: { + eyebrow: '▼・ᴥ・▼', + title: 'Personalized adventures for your dog!', + imageUrl: '/images/auckland-dogs-outdoor-pack.jpg', + imageAlt: 'Goodwalk dogs gathered together outdoors' + }, + pricing: { + title: '1:1 Large Dog Breed Prices', + plans: [ + { + title: '30 Minutes', + price: '$45', + period: 'Per Walk', + features: ['Free pickup/dropoff', '30 minute walk', 'Social media updates', 'Basic training'] + }, + { + title: '45 Minutes', + price: '$55', + period: 'Per Walk', + popular: true, + features: ['Free pickup/dropoff', '45 minute walk', 'Social media updates', 'Basic training'] + }, + { + title: '60 Minutes', + price: '$65', + period: 'Per Walk', + features: ['Free pickup/dropoff', '60 minute walk', 'Social media updates', 'Basic training'] + } + ] + }, + benefits: { + title: 'Benefits of our 1:1 walks', + items: [ + { + title: 'Individualized Attention', + body: 'Large breeds receive personalized care and undivided attention from the walker, addressing their unique needs and preferences without competition from other dogs.' + }, + { + title: 'Tailored Exercise', + body: 'Walkers can customize the pace, duration, and route of the walk to accommodate the energy levels and physical abilities of large breeds, providing an appropriate level of exercise tailored to their specific needs.' + }, + { + title: 'Bonding and Socialization', + body: 'During one-on-one walks, large breeds bond closely with their walker and socialize with people and animals encountered, promoting confidence and social skills' + }, + { + title: 'Enhanced safety', + body: "With one-on-one walks, there's reduced risk of potential conflicts or incidents that may arise in group settings, ensuring a safer walking experience for large breeds." + }, + { + title: 'Training Opportunities', + body: 'One-on-one walks offer dedicated time for training and reinforcement of good behaviors, such as loose leash walking or obedience commands, helping large breeds develop and maintain positive habits.' + }, + { + title: 'Stress Reduction', + body: 'Large breeds may feel more relaxed and comfortable during one-on-one walks, as they can explore and enjoy their surroundings without the potential stressors of a group dynamic, leading to a more positive walking experience overall.' + } + ] + }, + testimonialsHeading: 'What our clients say', + booking: { + title: "Let's meet!", + subtitle: 'Fill out your details below, and we can arrange a Meet & Greet for a one on one walk!', + formAction: '/booking', + serviceOptions: [], + ownerStepLabel: 'Your details', + dogStepLabel: 'Your dog', + dogIntro: 'Tell us about your dog, your area, and anything important we should know before arranging a one on one Meet & Greet.' + } +}; diff --git a/src/lib/content/homepage.ts b/src/lib/content/homepage.ts new file mode 100644 index 0000000..40601a4 --- /dev/null +++ b/src/lib/content/homepage.ts @@ -0,0 +1,217 @@ +import type { HomePageContent } from '$lib/types'; + +export const homepageContent: HomePageContent = { + seo: { + title: 'Home | Auckland Dog Walking | Goodwalk', + description: + 'At Goodwalk, we offer Tiny Gang pack walks and one on one dog walking services throughout Auckland. Give your dog his best life with Goodwalk!' + }, + navigation: { + desktopLinks: [ + { label: 'Our Services', href: '#services' }, + { label: 'Our Pricing', href: '/our-pricing' }, + { label: 'About Us', href: '/about' } + ], + mobileLinks: [ + { label: 'Home', href: '/' }, + { label: 'Pack Walks', href: '/pack-walks' }, + { label: '1:1 Walks', href: '/dog-walking' }, + { label: 'Puppy Visits', href: '/puppy-visits' }, + { label: 'Our Pricing', href: '/our-pricing' }, + { label: 'About Us', href: '/about' }, + { label: 'Contact Us', href: '/booking' } + ], + cta: { label: 'Contact Us', href: '/booking', variant: 'yellow' }, + instagram: { href: 'https://www.instagram.com/goodwalk.nz/', external: true }, + megaMenuServices: [ + { icon: 'fas fa-paw', label: 'Pack Walks', description: 'Group outdoor adventures', href: '/pack-walks' }, + { icon: 'fas fa-person-walking', label: '1:1 Walks', description: 'Personalised solo walks', href: '/dog-walking' }, + { icon: 'fas fa-dog', label: 'Puppy Visits', description: 'Home visits for young pups', href: '/puppy-visits' } + ] + }, + hero: { + title: 'Unleashing Fun in', + highlight: "Your Dog's Day!", + mobileTitle: "Unleashing Fun in\nYour Dog's Day!", + primaryCta: { label: 'Learn more', href: '#services', variant: 'yellow' }, + secondaryCta: { label: 'Enroll today', href: '#reservation', 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' + }, + intro: { + text: 'Goodwalk delivers trusted, professional dog walking services across Auckland Central.', + reviewCta: { + label: 'All 5 star reviews on Google!', + href: 'https://g.page/r/CUsvrWPhkYrAEB0/', + external: true + } + }, + promise: { + title: 'Happy pets,', + subtitle: 'happy humans', + body: + 'Offering tailored pack walks for small and medium dogs, and one-on-one walks for large breeds. Our walkers give personalised attention to each dog, easing stress, anxiety and ensuring a quality experience. Our expertise in small-medium breeds ensures tailored care for their unique needs. Join our', + emphasis: 'TINY GANG!', + cta: { label: 'Book now', href: '#reservation', variant: 'green' }, + imageUrl: '/images/auckland-dog-walking-happy-dogs-happy-humans.png', + imageAlt: 'Woman cuddling a dog for Goodwalk Auckland dog walking services' + }, + services: [ + { + icon: 'fas fa-dog', + title: 'Pack Walks', + body: 'Small group walks of 4-8 dogs - calm, social, and full of fun for your pup.', + href: '/pack-walks' + }, + { + icon: 'fas fa-person-walking', + title: '1:1 Walks', + body: "One-on-one walks tailored to your dog's individual pace, personality, and needs.", + href: '/dog-walking' + }, + { + icon: 'fas fa-house', + title: 'Puppy Visits', + body: 'In-home visits to check in on your puppy, play, and keep them company.', + href: '/puppy-visits' + } + ], + values: [ + { + icon: 'fas fa-heart', + title: 'Kindness', + body: + 'With gentle care and genuine affection, we make every walk a calm, happy experience. We use positive reinforcement to encourage good behavior because kindness is at the heart of everything we do.' + }, + { + icon: 'fas fa-camera', + title: 'Daily Updates', + body: + "Catch your pup in action with daily social updates - showcasing their walks, playtime, and mischief with the Tiny Gang. It's your window into their happiest moments." + }, + { + icon: 'fas fa-users', + title: 'Small Pack Sizes', + body: + 'With just 4-8 dogs per group, our walks are calm, controlled, and respectful of public spaces - ensuring every dog gets the attention and care they deserve.' + }, + { + icon: 'fas fa-shield-heart', + title: 'Safety', + body: + 'Our team is fully pet first aid certified and trained to handle any situation calmly and confidently. With proactive safety protocols and constant situational awareness, we create a secure environment for every walk.' + }, + { + icon: 'fas fa-calendar-check', + title: 'Flexibility', + body: + "We know life gets busy - so while we specialise in regular, permanent walks, we're always happy to adapt. Just give us a little notice, and we'll do our best to accommodate your changing schedule." + }, + { + icon: 'fas fa-clock', + title: 'Reliability', + body: + "We guarantee punctuality and consistency, so you can count on us. With clear communication, you'll always be in the loop - and your dog's needs will always be our top priority." + } + ], + testimonials: [ + { + 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.', + reviewer: 'Kate', + detail: "Archie's mum", + imageUrl: '/images/archie-auckland-dog-walking-review.jpg' + }, + { + 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!', + reviewer: 'Estelle', + detail: "Monty's mum", + imageUrl: '/images/monty-auckland-dog-walking-review.jpg' + }, + { + 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!', + reviewer: 'Ross', + detail: "Otis's Dad", + imageUrl: '/images/otis-auckland-dog-walking-review.jpg' + }, + { + 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', + reviewer: 'Nina', + detail: "Wallace's mum", + imageUrl: '/images/wallace-auckland-dog-walking-review.jpg' + } + ], + booking: { + title: "Let's meet!", + subtitle: + 'Ready to get started? Book your free, no-obligation Meet & Greet today — just enter your details below', + formAction: '/booking', + serviceOptions: ['Pack Walks', '1:1 Walks', 'Puppy Visits', 'Other Services'] + }, + info: { + title: 'Locations & Hours', + intro: "We cover most of Auckland Central's suburbs:", + suburbs: + 'Morningside, Kingsland, Ponsonby, Grey Lynn, Mt Albert, Mt Eden, Sandringham, Mt Roskill, Arch Hill, Freemans Bay, Herne Bay, Pt Chevalier, Avondale, Three Kings, Hillsborough, Eden Terrace, Balmoral.', + nearbyText: 'Live in a nearby suburb?', + nearbyCta: { label: 'Get in touch!', href: '#reservation' }, + hoursLabel: 'Opening Hours', + hours: 'Monday to Friday, 8am - 4pm.', + faqTitle: "FAQ's", + faqs: [ + { + question: 'What happens if the weather is bad?', + answer: + "We operate in all weather conditions, except when there is a danger to the dog's health and safety." + }, + { + question: 'What requirements does my dog need?', + answer: + 'All dogs onboarding with Goodwalk need to have a current Auckland Council dog registration and be up to date with vaccinations to ensure the health and safety of other dogs.' + }, + { + question: 'Can any dog use your service?', + answer: + 'All dogs that are onboarded with us must go through our screening process, which includes a minimum of two assessment walks.' + }, + { + question: 'How does payment work?', + answer: 'All walks are paid for a week in advance, via invoice.' + }, + { + question: 'Do you have insurance or First Aid training?', + answer: + 'All walkers are covered by public liability insurance, and all walkers hold a current First Aid training certificate.' + } + ] + }, + instagram: { + title: 'Follow us on Instagram', + label: '@goodwalk.nz', + href: 'https://www.instagram.com/goodwalk.nz/', + variant: 'green', + external: true + }, + footer: { + brandText: 'Professional Dog Walking Services\nAuckland Central', + navigationLinks: [ + { label: 'Home', href: '/' }, + { label: 'Pack Walks', href: '/pack-walks' }, + { label: '1:1 Walks', href: '/dog-walking' }, + { label: 'Puppy Visits', href: '/puppy-visits' }, + { label: 'Our Pricing', href: '/our-pricing' }, + { label: 'Contact Us', href: '/booking' } + ], + contactLinks: [ + { label: 'Book a walk', href: '/booking' }, + { label: 'Instagram', href: 'https://www.instagram.com/goodwalk.nz/', external: true }, + { label: 'Google Reviews', href: 'https://g.page/r/CUsvrWPhkYrAEB0', external: true } + ], + copyright: '© 2026 Goodwalk', + email: 'info@goodwalk.co.nz', + phone: '(022) 642 1011' + } +}; diff --git a/src/lib/content/our-pricing.ts b/src/lib/content/our-pricing.ts new file mode 100644 index 0000000..40e7869 --- /dev/null +++ b/src/lib/content/our-pricing.ts @@ -0,0 +1,58 @@ +import type { PricingPageContent } from '$lib/types'; +import { dogWalkingContent } from './dog-walking'; +import { packWalksContent } from './pack-walks'; +import { puppyVisitsContent } from './puppy-visits'; + +export const ourPricingContent: PricingPageContent = { + title: 'Our Pricing', + subtitle: 'Simple, transparent pricing — no lock-in contracts.', + sections: [ + { + title: 'Pack Walks', + icon: 'fas fa-paw', + blurb: + 'Small group adventures for calm, social dogs who thrive with structure, play, and regular weekly outings.', + detailCta: { + label: 'View Pack Walks', + href: '/pack-walks', + variant: 'green' + }, + plans: packWalksContent.pricing.plans + }, + { + title: '1:1 Walks', + icon: 'fas fa-person-walking', + blurb: + 'One-on-one walks tailored to your dog’s pace, confidence, and personality for a more focused outing.', + detailCta: { + label: 'View 1:1 Walks', + href: '/dog-walking', + variant: 'green' + }, + plans: dogWalkingContent.pricing.plans + }, + { + title: 'Puppy Visits', + icon: 'fas fa-dog', + blurb: + 'Short home visits for young pups who need company, enrichment, toilet breaks, and gentle routine support.', + detailCta: { + label: 'View Puppy Visits', + href: '/puppy-visits', + variant: 'green' + }, + plans: puppyVisitsContent.pricing.plans + } + ], + testimonialsHeading: 'What our clients say', + booking: { + title: 'Ready to join the Tiny Gang?', + subtitle: '', + formAction: '/booking', + serviceOptions: [], + ownerStepLabel: 'Your details', + dogStepLabel: 'Dog details', + dogIntro: + 'Tell us about your dog, where you are based, and anything important we should know before we arrange a Meet & Greet.' + } +}; diff --git a/src/lib/content/pack-walks.ts b/src/lib/content/pack-walks.ts new file mode 100644 index 0000000..064166c --- /dev/null +++ b/src/lib/content/pack-walks.ts @@ -0,0 +1,97 @@ +import type { ServicePageContent } from '$lib/types'; + +export const packWalksContent: ServicePageContent = { + hero: { + eyebrow: 'Pack Walks', + title: 'Join our Tiny Gang!', + paragraphs: [ + 'Fun, safe, and specially designed for little paws, these adventures help your dog build friendships and confidence in a calm, friendly group.', + 'We only welcome sociable dogs, so every outing feels secure and stress-free. As small dog owners ourselves, we know just what it takes to help your pup feel relaxed, happy, and right at home.', + 'Join the Tiny Gang today—because your dog deserves more than just a walk. They deserve a tail-wagging good time!' + ], + imageUrl: '/images/auckland-small-dog-pack-walk.jpg', + imageAlt: 'Small dogs together on a Goodwalk Tiny Gang pack walk' + }, + highlight: { + eyebrow: '▼・ᴥ・▼', + title: 'Goodwalk is the best choice for small and medium size dogs!', + imageUrl: '/images/tiny-gang-auckland-dog-pack.jpg', + imageAlt: 'Goodwalk Tiny Gang dogs gathered together in Auckland' + }, + 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!', + plans: [ + { + title: '1 Walk', + price: '$58', + period: 'Per Walk', + features: ['Free pickup/dropoff', '1 hour adventure', 'Social media updates', 'Basic training'] + }, + { + title: '2-3 Walks', + price: '$55', + period: 'Per Walk', + popular: true, + features: ['Free pickup/dropoff', '1 hour adventure', 'Social media updates', 'Basic training'] + }, + { + title: '4-5 Walks', + price: '$49.50', + period: 'Per Walk', + features: ['Free pickup/dropoff', '1 hour adventure', 'Social media updates', 'Basic training'] + }, + { + title: 'Casual Walk', + price: '$65', + period: 'Per Walk', + features: ['Free pickup/dropoff', '1 hour adventure', 'Social media updates', 'Basic training'] + } + ], + extras: [ + { 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' } + ] + }, + benefits: { + title: 'Tiny Gang membership benefits', + items: [ + { + title: 'Socialization with other dogs', + body: 'Tiny Gang pack walks help small and medium-sized dogs mingle and learn social skills from each other, boosting their confidence and positive behavior.' + }, + { + title: 'Taliored peace', + body: 'Our handlers can adjust the pace and intensity of the walk to suit the energy levels and abilities of small and medium-sized dogs, ensuring a pleasant and enjoyable experience for all participants.' + }, + { + title: 'Comfort', + body: 'Smaller groups create a more relaxed and comfortable atmosphere for dogs, allowing them to explore and enjoy the walk without feeling overwhelmed by larger dogs.' + }, + { + title: 'Increased bonding', + body: 'Tiny Gang pack walks foster stronger bonds between dogs and their walker, as well as between the dogs themselves, enhancing trust and companionship among the group.' + }, + { + title: 'Individualized attention', + body: 'Small pack sizes allow for more personalized care and attention from the walker, addressing the unique needs and preferences of small and medium-sized breeds.' + }, + { + title: 'Safety', + body: "With a smaller group composed of dogs of similar sizes, there's reduced risk of accidental injury or intimidation, ensuring a safer walking environment." + } + ] + }, + testimonialsHeading: 'What our clients say', + booking: { + title: 'Join the Tiny Gang!', + subtitle: '', + formAction: '/booking', + serviceOptions: [], + ownerStepLabel: 'Your details', + dogStepLabel: 'Dog details', + dogIntro: 'Tell us about your dog and where you are based so we can plan the right Tiny Gang Meet & Greet.' + } +}; diff --git a/src/lib/content/privacy-policy.ts b/src/lib/content/privacy-policy.ts new file mode 100644 index 0000000..04d148c --- /dev/null +++ b/src/lib/content/privacy-policy.ts @@ -0,0 +1,199 @@ +import type { LegalPageContent } from '$lib/types'; + +export const privacyPolicyContent: LegalPageContent = { + title: 'Privacy Policy', + sections: [ + { + title: 'Who we are', + blocks: [ + { + type: 'paragraph', + content: + 'Goodwalk Limited (we, us or our) is committed to protecting and maintaining the privacy, accuracy and security of your personal information. This privacy policy sets out details of how we collect, use, store and disclose your personal information. By accessing and using our website or our products and services, you consent to the collection, use, storage and disclosure of your personal information in accordance with this policy.' + } + ] + }, + { + title: 'How we collect your information', + blocks: [ + { + type: 'paragraph', + content: + 'We will only collect personal information relevant to our business relationship with you. The personal information we collect will generally include your: name, address, telephone numbers, email address, date of birth, vet contact and address, emergency contact. You may choose to not provide us with this information, but not doing so may affect our ability to provide you with our products and services. We will collect this information directly from you when you:' + }, + { + type: 'list', + content: [ + 'Register to use our products and services;', + 'Use our products and services; or', + 'Communicate or interact with us, whether by email, telephone or otherwise. You are required to keep us informed of changes to your information to enable us to have proper administrative processes.' + ] + }, + { + type: 'paragraph', + content: + 'We may also collect aggregated information generated by our system, which tracks your use of our products and services but does not identify you personally.' + } + ] + }, + { + title: 'Cookies', + blocks: [ + { + type: 'paragraph', + content: + 'Our website uses server logs and web analytic tools (such as cookies). Cookies are small text files that are downloaded to your device by websites that you visit. When you use our website, these tools collect information such as the browser and operating system that you use, the internet protocol address of the device you use to access the site, search terms, your location and the content that you view when visiting the website. You can set your browser to block all cookies, including cookies associated with our website, or to indicate when a cookie is being set by us. If you set your browser to reject cookies you may not be able to access all the features of the website.' + } + ] + }, + { + title: 'How we use your information', + blocks: [ + { + type: 'paragraph', + content: 'We may use your personal information to:' + }, + { + type: 'list', + content: [ + 'identify you, so we can ensure that your account is secure;', + 'provide our products and services to you;', + 'communicate with you;', + 'carry out our business which includes maintaining your account, planning, training, product development, research and analysis;', + 'enforce our agreement with you in any way;', + 'fulfil our legal requirements (for example, disclosure to law enforcement agencies or the courts); or', + 'for any related purpose in connection with the above.' + ] + }, + { + type: 'paragraph', + content: + 'We may use any information that we collect from you that is not personal information, so you cannot be personally identified from it, for our business purposes, including:' + }, + { + type: 'list', + content: [ + 'to assess how our customers use our products and services;', + 'to improve our products and services; and', + 'for marketing and promotional purposes.' + ] + } + ] + }, + { + title: 'Marketing', + blocks: [ + { + type: 'paragraph', + content: + 'We may use your information to offer you products and services that we believe meet your needs. You can notify us at any time if you do not wish to receive these offers by emailing us at info@goodwalk.co.nz or writing to us at Goodwalk, 8/54 Finch Street, Morningside, Auckland 1022. We will act promptly on any such request.' + } + ] + }, + { + title: 'Who we may disclose your information to', + blocks: [ + { + type: 'paragraph', + content: + 'We may disclose information we retain about you, including your personal information, to the following persons or their agents:' + }, + { + type: 'list', + content: [ + 'our employees, contractors, and our related entities;', + 'third parties engaged by us to perform specific functions for our business;', + 'service providers engaged by us, such as data storage, IT, software management, insurers and financial services;', + 'Government departments or law enforcement agencies, including the Police;', + 'liquidators, administrators or other persons appointed to administer your financial affairs;', + 'debt collection services or credit reporting agencies.', + 'Some of the persons listed above may be located overseas. We will only transfer your personal information to a recipient that is obliged to protect your personal information with comparable safeguards to those contained in the Privacy Act 2020, or otherwise we will obtain your express consent to transfer or store the personal information. We may disclose, sell or transfer to third parties any non-personal, aggregated information that we collect from you and our other customers.' + ] + } + ] + }, + { + title: 'Information Security', + blocks: [ + { + type: 'paragraph', + content: + 'We will take reasonable steps to keep your personal information secure and confidential. This includes the following:' + }, + { + type: 'list', + content: [ + 'only our staff and those who perform services on our behalf, and are authorised to handle your information, will have access to your personal information;', + 'we will not retain any of your information for any longer than it is required by us except to fulfil our legal obligations or where you have consented; and', + 'we will, with your help, keep your personal information accurate, complete and up-to-date.' + ] + } + ] + }, + { + title: 'Advertising/Third Party Links', + blocks: [ + { + type: 'paragraph', + content: + 'Our website may contain links to a variety of advertising and third party website sources. Some of these links may request or record information from users or use cookies or other methods to collect information from you. We have no control over the content or privacy policy practices of those sites and encourage you to review the privacy policies of those sites before using them.' + } + ] + }, + { + title: 'Information Access', + blocks: [ + { + type: 'paragraph', + content: + 'You can access most of the personal information we hold about you and request corrections. This right is subject to some exceptions, for example, you may not obtain access to information relating to existing or anticipated legal proceedings.' + }, + { + type: 'paragraph', + content: + 'You can request access to your information by emailing us at info@goodwalk.co.nz or writing to us at Goodwalk, 8/54 Finch Street, Morningside, Auckland 1022. This service is free unless the information you request requires significant research or preparation time. Before we act upon requests of this nature, we will tell you how much this service will cost.' + } + ] + }, + { + title: "What to do if you think we've made an error", + blocks: [ + { + type: 'paragraph', + content: + 'We are committed to protecting your privacy and our policies, processes and systems have been developed with this in mind. However, if you think we have made an error, please email us at info@goodwalk.co.nz or write to us at Goodwalk, 8/54 Finch Street, Morningside, Auckland 1022, to let us know. Where we have made an error, we will endeavour to correct the error as soon as reasonably practicable.' + } + ] + }, + { + title: 'Questions & Compliants', + blocks: [ + { + type: 'paragraph', + content: + 'If you have a question or complaint about the way we have dealt with your personal information, please contact us by email or in writing at the addresses above. We will endeavour to respond promptly to your question or complaint.' + } + ] + }, + { + title: 'Privacy Breaches', + blocks: [ + { + type: 'paragraph', + content: + 'We take our privacy responsibilities seriously. In the unlikely event that a suspected or actual breach of your personal data occurs, we will investigate the breach. We will notify you and the Privacy Commissioner if we reasonably believe that the breach has caused you serious harm, or is likely to cause you serious harm.' + } + ] + }, + { + title: 'Privacy Policy Changes', + blocks: [ + { + type: 'paragraph', + content: + 'We may change this policy at any time by publishing the amended policy on our website. We will endeavour to inform you of any changes to the policy by email or on the website.' + } + ] + } + ] +}; diff --git a/src/lib/content/puppy-visits.ts b/src/lib/content/puppy-visits.ts new file mode 100644 index 0000000..f04fe61 --- /dev/null +++ b/src/lib/content/puppy-visits.ts @@ -0,0 +1,67 @@ +import type { ServicePageContent } from '$lib/types'; + +export const puppyVisitsContent: ServicePageContent = { + hero: { + eyebrow: 'Puppy Visits', + title: 'Introducing Puppy Visits: Building strong foundations for our pack walks!', + paragraphs: [ + "We love puppies! Our puppy home visits are perfect for young pups not quite ready to join the pack and busy owners with hectic schedules. We lay the groundwork for future pack walks, including fun games, potty breaks, and even feeding if required. Let us help your furry friend thrive while you're away!" + ], + imageUrl: '/images/auckland-puppy-home-visit.jpg', + imageAlt: 'Puppy Visits page splash image' + }, + pricing: { + title: 'Puppy Visits', + plans: [ + { + title: '20 Minutes', + price: '$39', + period: 'Per Visit', + features: ['Bathroom break', 'Pet feed', 'Basic training', 'Enrichment games'] + }, + { + title: '45 Minutes', + price: '$49', + period: 'Per Visit', + features: ['Bathroom break', 'Pet feed', 'Basic training', 'Enrichment games'] + }, + { + title: '1 Hour', + price: '$55', + period: 'Per Visit', + features: ['Bathroom break', 'Pet feed', 'Basic training', 'Enrichment games'] + } + ] + }, + benefits: { + title: 'Puppy Visits benefits', + items: [ + { + title: 'Enrichment', + body: 'From stimulating games to sensory toys, we keep those curious minds engaged and little tails wagging.' + }, + { + title: 'Setting up the basics for pack walks', + body: "Lay the groundwork for your pup's adult life. We'll guide you through setting the right tone, offering basic training tips and tricks along the way." + }, + { + title: 'Reduce anxiety', + body: "With time your pup will know when to expect a visit, reducing the chances of accidents while you're away. With regular visits, your pup will feel loved and secure, minimizing any time spent at home alone." + }, + { + title: 'Expert advise', + body: "As experienced dog pawrents, we've been through it all with many adorable puppies. Consider us your go-to for any questions or concerns as your furry friend grows up." + } + ] + }, + testimonialsHeading: 'What our clients say', + booking: { + title: 'Ready to join the Tiny Gang?', + subtitle: '', + formAction: '/booking', + serviceOptions: [], + ownerStepLabel: 'Your details', + dogStepLabel: 'Dog details', + dogIntro: 'Tell us about your puppy, your area, and any special needs so we can plan the right visit.' + } +}; diff --git a/src/lib/content/static-pages.ts b/src/lib/content/static-pages.ts new file mode 100644 index 0000000..c313ac0 --- /dev/null +++ b/src/lib/content/static-pages.ts @@ -0,0 +1,55 @@ +export const staticPages = { + 'pack-walks': { + title: 'Pack Walks | Join Our Tiny Gang', + description: + 'Join our Tiny Gang pack walks. We take our dogs to beautiful parks and beaches around the Auckland region.', + canonicalPath: '/pack-walks' + }, + 'dog-walking': { + title: '1 on 1 Walks | Professional Dog Walking | Auckland Wide', + description: + 'Our 1:1 (one on one) are perfect for dogs with great recall and leash manners, our walks guarantee a stress-free experience!', + canonicalPath: '/dog-walking' + }, + 'puppy-visits': { + title: 'Puppy Visits | Puppy Training', + description: + 'Puppy Visits Introducing Puppy Visits: Building strong foundations for our pack walks! We love puppies! Our puppy home visits are perfect for young pups not quite ready to join the pack and busy owners with hectic schedules. We lay the groundwork for future pack walks, including fun games, potty breaks, and even feeding if required. Let us help your furry friend thrive while you are away!', + canonicalPath: '/puppy-visits' + }, + 'our-pricing': { + title: 'Our Pricing', + description: + 'Learn more about the pricing for Goodwalk. Prices for our Tiny Gang pack walks and 1 on 1 solo walks.', + canonicalPath: '/our-pricing' + }, + about: { + title: 'About Us | Dog Walkers', + description: + 'Learn more about Kingsland based dog walking company Goodwalk. We offer our Tiny Gang pack walks throughout the Auckland region. Solo (1:1 walks), homestays and more.', + canonicalPath: '/about' + }, + 'about-us': { + title: 'About Us | Dog Walkers', + description: + 'Learn more about Kingsland based dog walking company Goodwalk. We offer our Tiny Gang pack walks throughout the Auckland region. Solo (1:1 walks), homestays and more.', + canonicalPath: '/about' + }, + booking: { + title: 'Booking', + description: 'Book a Meet & Greet with Goodwalk Auckland dog walking services.', + canonicalPath: '/booking' + }, + 'terms-and-conditions': { + title: 'Terms & Conditions', + description: 'Terms and conditions for Goodwalk Auckland dog walking services.', + canonicalPath: '/terms-and-conditions' + }, + 'privacy-policy': { + title: 'Privacy Policy', + description: 'Privacy policy for Goodwalk Auckland dog walking services.', + canonicalPath: '/privacy-policy' + } +} as const; + +export type StaticPageSlug = keyof typeof staticPages; diff --git a/src/lib/content/terms-and-conditions.ts b/src/lib/content/terms-and-conditions.ts new file mode 100644 index 0000000..09807bd --- /dev/null +++ b/src/lib/content/terms-and-conditions.ts @@ -0,0 +1,382 @@ +import type { LegalPageContent } from '$lib/types'; + +export const termsAndConditionsContent: LegalPageContent = { + title: 'Terms & Conditions', + sections: [ + { + title: '1. Application of Terms', + blocks: [ + { + type: 'list', + content: [ + '1.1 Good Walk Limited (we, us or our) provides dog walking and other services relating to dogs (services) to our clients (you or your).', + '1.2 These terms and conditions (terms) set out the terms upon which we will provide the services to you. By registering for or using our services, you acknowledge and agree that you have read, understood and accepted these terms and agree to be bound by them.', + '1.3 If you do not agree to these terms, you must cease to use our services immediately.', + '1.4 We may change these terms at any time by notifying you of the change by email or on our website. Any change to these terms will take effect from the date set out in the notice. By continuing to access or use our services, you agree to be bound by the amended terms.' + ] + } + ] + }, + { + title: '2. Services', + blocks: [ + { + type: 'list', + content: [ + '2.1 We will provide a dog walker (dog walker) or other person with suitable experience to provide the services. As far as reasonably practicable, we will endeavour to ensure that the same dog walker(s) is available to provide the services to you.', + '2.2 We will provide the services at the location specified by us unless otherwise agreed in writing with you.', + '2.3 In providing our services to you, we will act with due care and skill and in accordance with applicable laws.', + '2.4 We reserve the right to change the services from time to time, including the date or time for a walk (for example because of bad weather conditions), the dog walker or the location of the services. We will endeavour to notify you of a change to the services and to agree to the change with you by email or text.', + '2.5 We will keep safe and confidential all your keys, remote control entry devices, access codes and return the same to you when we cease providing services to you, or immediately upon demand.', + '2.6 We carry out a police check on each dog walker.', + '2.7 You agree that we may take photos and/or videos of your dogs during or after our walks. We own the intellectual property rights in these photos and/or videos, and we may post or use the image for any purpose whatsoever, including on our website, social media accounts and promotional materials. If you do not want us to use photos and/or videos then please contact us to have it removed from our website, social media accounts and promotional materials.' + ] + } + ] + }, + { + title: '3. Registration', + blocks: [ + { + type: 'list', + content: [ + '3.1 You may register for our services on our website or directly with us by email. If we require you to pay a deposit, your registration cannot be confirmed until you have paid the required deposit to our nominated bank account.', + '3.2 Your registration is only confirmed once we send you an email to this effect. We reserve the right to reject your registration.' + ] + } + ] + }, + { + title: '4. Assesement & Admission of Dogs', + blocks: [ + { + type: 'list', + content: [ + '4.1 We assess each dog for suitability for our services. We may refuse to provide services to you at our sole discretion and at any time if we consider that any dog is not suitable.', + '4.2 We only provide pack walks services in relation to small and medium sized dog breeds.', + '4.3 We admit dogs to our pack walks based on the following criteria:' + ] + }, + { + type: 'list', + content: [ + '(a) the dog is not aggressive and/or does not bite (and has no history of aggression and/or biting);', + '(b) the dog is good natured;', + '(c) the dog is suitably trained and has good recall skills;', + '(d) the dog is up to date with vaccinations (and you must be able to provide evidence of this to us);', + '(e) the dog is microchipped;', + '(f) the dog is spayed or neutered;', + '(g) the dog is regularly treated for fleas and ticks;', + '(h) you hold a current council registration for the dog; and', + '(i) the dog is over 6 months of age.' + ] + }, + { + type: 'list', + content: [ + '4.4 We conduct assessment/onboarding walks with each dog that you ask us to assess (assessment walks). We require that each dog has a minimum of 2 to 4 assessment walks with a senior handler before we commence providing booked walks.', + '4.5 Assessment walks last for around 30 minutes or such other period that we think is appropriate. The cost of each assessment walk will be notified to you by email or on our website. We require you to book a minimum of two assessment walks per week unless we agree otherwise.' + ] + } + ] + }, + { + title: '5. Walks', + blocks: [ + { + type: 'list', + content: [ + '5.1 Following the assessment walks, you must select your preferred permanent days of the week for walks, with a minimum commitment of one walk per week. Dogs must attend on these chosen days regularly for a minimum period of at least 6 months. Walks for dogs that show aggressive behavior may be cancelled with immediate effect.', + '5.2 Walks will not take place during severe weather conditions such as high winds, heavy snow, heavy rain, thunder, and lightning. In these cases, your dog will be returned to your residence, or the walk may be shortened, cancelled or rescheduled.', + '5.3 If you decide to cancel a walk due to bad weather or heat, you agree to pay for the cancelled walk in full. However, we will endeavour to walk dogs in heavy rain or hot weather as long as we consider that it is safe for the dogs and our dog walkers.', + '5.4 As part of our service, we will work with you to reinforce recall training, leash training, and car behaviour for your dog, using positive reinforcement methods. However, we do not provide individual training sessions and are not responsible for training your dog or for its behaviour.', + '5.5 Walks for female dogs and puppies experiencing heat will be paused for three weeks from the start of the heat cycle, with a payment of one walk per week required to hold the spot for the dog during this period.', + '5.6 We may provide services at our sole discretion through one-on-one walks in relation to dogs with anxiety, young puppies, mild reactivity, or large size dogs. If the dog shows progress and is suitable for pack walks, then they may at our discretion be admitted to pack walks.', + '5.7 We will provide leads, treats and waste bags. The walker will remove dogs’ faeces from all public places.', + '5.8 If you would like to suspend walks for a holiday period exceeding two weeks, we may ask you to pay a fee of $25 per week to hold the dog’s spot.', + '5.9 You acknowledge that your dog may be let off the leash unless otherwise agreed. If a dog runs away, the walker is not liable for any reason.', + '5.10 For repeated change of frequency or suspensions throughout the year, we require your dog to come for at least one walk per week to maintain their spot or otherwise we will provide the services on a casual basis.' + ] + } + ] + }, + { + title: '5.11. Pack Walks', + blocks: [ + { + type: 'list', + content: [ + '5.11 Pack walks are scheduled on recurring days. We offer two shifts: AM and PM, Monday to Friday, with walks lasting one hour plus pick-up and drop-off, typically totalling an average of 2+ hours. The location will be at one of Auckland’s parks or beaches and is at the discretion of the dog walker.', + '(a) AM pack: 9:00 AM – 11:30 AM.', + '(b) PM pack: 12:30 PM – 2:30 PM.', + '5.12 Pack walks cannot be booked by shift. Your dog should be available for collection at any time between the hours of 9:00 AM and 2:30 PM.', + '5.13 We reserve the right to walk other compatible dogs at the same time but will limit the number of dogs walked with one person to 8 dogs.' + ] + } + ] + }, + { + title: '5.14. One-on-One Walks (regular, casual)', + blocks: [ + { + type: 'list', + content: [ + '5.14 One on one walks are 30,45 or 60 minutes long (see our website, goodwalk.co.nz).', + '5.15 One-On-One Walks (and Puppy Visits) are offered Monday to Friday:', + '(a) AM: 7:00 AM – 9:00 AM.', + '(b) PM: 2:30 PM – 3:30 PM.', + '5.16 You must select your preferred permanent days of the week for one-on-one walks (and Puppy Visits), with a minimum commitment of one walk per week. Dogs/puppies will attend on these chosen days regularly for a minimum period of at least 6 months.' + ] + } + ] + }, + { + title: '5.17. Homestays', + blocks: [ + { + type: 'list', + content: [ + '5.17 We provide homestay services for owners of dogs that are walked by us, for a minimum of one walk per week (regular client) at the discretion of the dog walker and depending on availability. Dogs need to be crate trained and comply with all our other requirements as set out on our website at goodwalk.co.nz.', + '5.18 Homestays during public holidays will incur a 15% surcharge.', + '5.19 We may cancel with immediate effect homestays for any dog that shows aggressive behaviour including biting.' + ] + } + ] + }, + { + title: '5.20. Puppy Visits', + blocks: [ + { + type: 'list', + content: [ + '5.20 Puppy visits are 20,45 or 60 minutes long and will be conducted at your residence, with walks outside only when puppies are fully vaccinated and registered.' + ] + } + ] + }, + { + title: '5.21. Pet Taxi and Pet Wash', + blocks: [ + { + type: 'list', + content: [ + '5.21 Pet Taxi and Pet Wash services are exclusively reserved for our regular clients. These services are tailored to each individual case, and prices may vary accordingly. We will notify you in advance of the price before you are charged.', + '5.22 Pet Taxi and Pet Wash services can be booked after 3:30pm.' + ] + } + ] + }, + { + title: '5.23. Casual Walks', + blocks: [ + { + type: 'list', + content: [ + '5.23 Owners who require more flexibility with days and frequency may be accommodated through our Casual Walk service.' + ] + } + ] + }, + { + title: '6. Collection of your Dog', + blocks: [ + { + type: 'list', + content: [ + '6.1 We will arrange to collect your dog from the agreed address. You agree that we may enter the agreed address as needed to perform the necessary pick up and drop off services.', + '6.2 To facilitate this process (and unless otherwise agreed in writing), you must provide us with:' + ] + }, + { + type: 'list', + content: [ + '(a) a copy of your house key;', + '(b) any necessary building access swipe card/codes; and', + '(c) parking instructions.' + ] + }, + { + type: 'list', + content: [ + '6.3 We can provide you with a lockbox to be kept at your property, containing a copy of your key. If you opt for the lockbox option, we will require a $50 deposit. This deposit will be reimbursed to you upon termination of our services. The deposit and cost of the key cut will be added to your first invoice.' + ] + } + ] + }, + { + title: '7. Our Fees', + blocks: [ + { + type: 'list', + content: [ + '7.1 You must pay us our fees for the services in accordance with these terms. Unless otherwise agreed by us in writing, all fees are payable in New Zealand dollars. The fees exclude any goods and services tax or other sales tax, unless otherwise stated.', + '7.2 We will provide you with details of our bank account for payment. You should include your dog’s name and surname as a payment reference when you make the payment and follow payment instructions.', + '7.3 The fees for our services will be notified to you by email on our website at goodwalk.co.nz.', + '7.4 We reserve the right to offer a price which is different from the one displayed on our website, at our sole discretion. Any such price will be agreed with you before we provide the services.', + '7.5 For any extra services requested by you, a discretionary charge will be applied. This charge will be agreed with you before we provide the services.', + '7.6 If we ask you for a deposit, you must pay this at the time of registration.', + '7.7 We generally send out invoices on the Monday of the then current walking week. Payment is due within 7 days from the date of the invoice.', + '7.8 We offer various payment options in relation to our fees. The options are currently payment by electronic transfer following receipt of our invoice. If you pay any fees by credit card, a surcharge will apply as notified to you at the time of payment.', + '7.9 If we cannot enter your residence to collect the dog, or cannot find the dog, and/or cannot make contact with you for a period of time not exceeding 10 minutes, the walk will be cancelled and you will be liable to make payment in full for the walk.' + ] + } + ] + }, + { + title: '8. Late Payment & Cancellation', + blocks: [ + { + type: 'list', + content: [ + '8.1 We may charge a late payment fee of 15% of the invoiced amount after your third reminder, and walks will be suspended until the payment is made.', + '8.2 We operate on a 48-hour cancellation policy. Cancellations made with less than 48 hours’ notice will incur a charge of 50% of the fee for the relevant services. Cancellations on the same day as the scheduled walk will be invoiced (and you will pay for them) in full.', + '8.3 We may cancel the services at any time and with immediate effect for dogs that show aggressive behaviour such as biting.', + '8.4 We do not provide any services on public holidays, and accordingly all walks are automatically cancelled without notice, and can be rescheduled to a different day of the same week (subject to availability).' + ] + } + ] + }, + { + title: '9. Your Obligations', + blocks: [ + { + type: 'list', + content: [ + '9.1 For group walks, you must notify us in advance of any injury, virus or illness your dog experiences before their scheduled walk. This enables us to assess whether the walk needs to be paused for the health and safety of the entire pack.', + '9.2 You will provide all information to the dog walker that may be relevant to the care or wellbeing of your dog or the pack.', + '9.3 You will notify the dog walker of any concerns, before or as soon as reasonably possible after any walks.', + '9.4 You will provide suitable harnesses, collars and leads as approved by the dog walker as well as coats or muzzles if required.', + '9.5 You acknowledge that your dog may return dirty, muddy, or wet from our walks. We will endeavour to towel dry dogs before returning them to your residence.' + ] + } + ] + }, + { + title: '10. Emergencies', + blocks: [ + { + type: 'list', + content: [ + '10.1 In the event of an emergency, we will contact you on the telephone numbers provided by you to confirm your choice of action.', + '10.2 If you cannot be reached timeously, you authorise us to:' + ] + }, + { + type: 'list', + content: [ + '(a) transport the dog(s) to a veterinarian;', + '(b) request on site treatment from a veterinarian; or', + '(c) transport the dog(s) to an emergency clinic if the previous two options are not feasible.' + ] + }, + { + type: 'list', + content: ['10.3 You are fully responsible for the veterinarian’s or clinic’s costs.'] + } + ] + }, + { + title: '11. Liability', + blocks: [ + { + type: 'list', + content: [ + '11.1 Notwithstanding any other provision set out in these terms, our total aggregate liability for all claims relating to these terms is limited to the greater of $100 and the amount you have paid for the services in the immediately preceding 1 month to which the claim relates.', + '11.2 We are not liable for any indirect, consequential or special loss, damage, cost or expense incurred by you as a result of a breach by us of these terms.', + '11.3 We are not liable to you for any failure to provide the services or to meet any of our obligations under these terms where that failure is caused by an event or circumstance outside of our reasonable control.', + '11.4 Our liability for any claim relating to these terms will be reduced to the extent that you or any other person or organisation associated with you contributed to the loss arising from the claim.', + '11.5 We and our dog walkers accept no responsibility and/or liability whatsoever for:' + ] + }, + { + type: 'list', + content: [ + '(a) any breach of security or loss of or damage to your property if any other person has access to the property at the time of the loss or damage;', + '(b) any costs, expenses or damages associated with changing your locks or which you incur as a result of a lost key;', + '(c) any injury or damage caused to your dog, including by another dog or other animal, any person, or due to natural or environmental causes;', + '(d) any medical event occurring in relation to the dog;', + '(e) your dog escaping or getting lost or injured if the dog runs away from the dog walker;', + '(f) dog diseases, dog fights, dog bites and other dog injuries; and', + '(g) injuries or damage caused by your dog to humans, other dogs or any property.' + ] + }, + { + type: 'list', + content: [ + '11.6 You acknowledge that if your dog bites or injures another dog, we will be responsible for reporting this to the relevant authorities.' + ] + } + ] + }, + { + title: '12. Warrantities', + blocks: [ + { + type: 'paragraph', + content: + 'We do not warrant or guarantee that your use of our services will be error free or will meet your purposes or expectations. To the maximum extent permitted by law, we exclude all such warranties or guarantees.' + }, + { + type: 'list', + content: [ + '12.2 Other than as expressly stated in these terms and to the maximum extent permitted by law, we do not give any warranties or guarantees in relation to our services.' + ] + } + ] + }, + { + title: '13. Termination', + blocks: [ + { + type: 'list', + content: [ + '13.1 You may terminate the supply of the services under these terms by giving to us not less than two weeks’ written notice without incurring any charges. If you give us less than two weeks’ written notice of termination, you will pay for any services that we have agreed to provide to you up to the date of termination.', + '13.2 We may terminate the supply of the services under these terms by giving to you not less than two weeks’ written notice.', + '13.3 If you breach these terms, provide us with incorrect or misleading information, or your dog becomes aggressive or dangerous, we may terminate the services under these terms by giving you written notice with immediate effect.', + '13.4 Alternatively, we may suspend the services if you breach these terms or provide us with incorrect or misleading information, until any such breach has been rectified by you.', + '13.5 We will give you written notice that services to you under these terms have been suspended or terminated but we do not have to provide to you any reasons for the suspension or termination.' + ] + } + ] + }, + { + title: '14. Disputes', + blocks: [ + { + type: 'paragraph', + content: + 'If you have a complaint relating to our performances or otherwise to these terms, you should contact us by email at info@goodwalk.co.nz.' + } + ] + }, + { + title: '15. Notices', + blocks: [ + { + type: 'list', + content: [ + '15.1 You may send a notice to us under or in connection with these terms by emailing us at info@goodwalk.co.nz.', + '15.2 We may send a notice to you under or in connection with these terms by emailing you at the last email address you have provided to us.', + '15.3 You or we will be deemed to have received a notice sent by email at the time that you or we sent it, unless we know or ought reasonably to know that the email was not delivered.' + ] + } + ] + }, + { + title: '16. General', + blocks: [ + { + type: 'list', + content: [ + '16.1 These terms constitute the entire agreement between you and us relating to your use of the services. These terms replace any previous related agreements and understandings between you and us.', + '16.2 We may assign our rights and obligations under these terms at any time to any other person.', + '16.3 We may subcontract all or any of our obligations under these terms to a third party. If we do this, we will remain liable to you for the performance of the subcontractor’s obligations.', + '16.4 No delay, neglect or forbearance in taking enforcement action in relation to any provision of these terms by a party will be a waiver, or in any way prejudice any right, of that party.', + '16.5 If any part of these terms is held to be invalid, illegal or unenforceable, that part will be severed and the remainder of these terms will remain in full force and have full effect.', + '16.6 These terms will be governed by the exclusive law of New Zealand.' + ] + } + ] + } + ] +}; diff --git a/src/lib/server/content.test.ts b/src/lib/server/content.test.ts new file mode 100644 index 0000000..008b218 --- /dev/null +++ b/src/lib/server/content.test.ts @@ -0,0 +1,82 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { homepageContent } from '$lib/content/homepage'; + +vi.mock('$lib/server/db', () => ({ + getPool: vi.fn() +})); + +import { getPool } from '$lib/server/db'; +import { getHomepageContent, getSharedPageContent, saveHomepageContent } from './content'; + +describe('content server helpers', () => { + beforeEach(() => { + vi.mocked(getPool).mockReset(); + }); + + it('returns a cloned homepage fallback when the database is disabled', async () => { + vi.mocked(getPool).mockReturnValue(null); + + const result = await getHomepageContent(); + + expect(result).toEqual(homepageContent); + expect(result).not.toBe(homepageContent); + }); + + it('returns homepage content from the database when present', async () => { + const dbContent = { + ...homepageContent, + seo: { + ...homepageContent.seo, + title: 'Updated title' + } + }; + + vi.mocked(getPool).mockReturnValue({ + query: vi.fn().mockResolvedValue({ + rowCount: 1, + rows: [{ value: dbContent }] + }) + } as never); + + await expect(getHomepageContent()).resolves.toEqual(dbContent); + }); + + it('falls back to file content when the database query fails', async () => { + vi.mocked(getPool).mockReturnValue({ + query: vi.fn().mockRejectedValue(new Error('db unavailable')) + } as never); + + await expect(getHomepageContent()).resolves.toEqual(homepageContent); + }); + + it('persists homepage content when the database is enabled', async () => { + const query = vi.fn().mockResolvedValue({}); + vi.mocked(getPool).mockReturnValue({ query } as never); + + const result = await saveHomepageContent(homepageContent); + + expect(result).toEqual(homepageContent); + expect(query).toHaveBeenCalledWith(expect.stringContaining('insert into site_content'), [ + 'homepage', + JSON.stringify(homepageContent) + ]); + }); + + it('throws when trying to save content without a database connection', async () => { + vi.mocked(getPool).mockReturnValue(null); + + await expect(saveHomepageContent(homepageContent)).rejects.toThrow('DATABASE_URL is not configured.'); + }); + + it('returns only shared page content for secondary pages', async () => { + vi.mocked(getPool).mockReturnValue(null); + + await expect(getSharedPageContent()).resolves.toEqual({ + navigation: homepageContent.navigation, + services: homepageContent.services, + testimonials: homepageContent.testimonials, + booking: homepageContent.booking, + footer: homepageContent.footer + }); + }); +}); diff --git a/src/lib/server/content.ts b/src/lib/server/content.ts new file mode 100644 index 0000000..1297362 --- /dev/null +++ b/src/lib/server/content.ts @@ -0,0 +1,60 @@ +import { homepageContent } from '$lib/content/homepage'; +import type { HomePageContent, SiteSharedContent } from '$lib/types'; +import { getPool } from '$lib/server/db'; + +const CONTENT_KEY = 'homepage'; + +export async function getHomepageContent(): Promise { + const pool = getPool(); + + if (!pool) { + return structuredClone(homepageContent); + } + + try { + const result = await pool.query<{ value: HomePageContent }>( + 'select value from site_content where key = $1 limit 1', + [CONTENT_KEY] + ); + + if (result.rowCount && result.rows[0]) { + return result.rows[0].value; + } + } catch (error) { + console.error('Failed to read homepage content from PostgreSQL.', error); + } + + return structuredClone(homepageContent); +} + +export async function saveHomepageContent(content: HomePageContent) { + const pool = getPool(); + + if (!pool) { + throw new Error('DATABASE_URL is not configured.'); + } + + await pool.query( + ` + insert into site_content (key, value) + values ($1, $2::jsonb) + on conflict (key) + do update set value = excluded.value, updated_at = now() + `, + [CONTENT_KEY, JSON.stringify(content)] + ); + + return content; +} + +export async function getSharedPageContent(): Promise { + const content = await getHomepageContent(); + + return { + navigation: content.navigation, + services: content.services, + testimonials: content.testimonials, + booking: content.booking, + footer: content.footer + }; +} diff --git a/src/lib/server/db.ts b/src/lib/server/db.ts new file mode 100644 index 0000000..75decba --- /dev/null +++ b/src/lib/server/db.ts @@ -0,0 +1,24 @@ +import { Pool } from 'pg'; + +let pool: Pool | null = null; + +function useSsl(connectionString: string) { + return !connectionString.includes('localhost') && !connectionString.includes('@db:'); +} + +export function getPool() { + const connectionString = process.env.DATABASE_URL; + + if (!connectionString) { + return null; + } + + if (!pool) { + pool = new Pool({ + connectionString, + ssl: useSsl(connectionString) ? { rejectUnauthorized: false } : undefined + }); + } + + return pool; +} diff --git a/src/lib/styles/base.css b/src/lib/styles/base.css new file mode 100644 index 0000000..0fb17c4 --- /dev/null +++ b/src/lib/styles/base.css @@ -0,0 +1,36 @@ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: var(--font-body); + font-size: 15px; + line-height: 1.6; + color: var(--text); + background: var(--off-white); +} + +img { + display: block; + max-width: 100%; + height: auto; +} + +a { + color: inherit; + text-decoration: none; +} + +button, +input, +textarea { + font: inherit; +} diff --git a/src/lib/styles/buttons.css b/src/lib/styles/buttons.css new file mode 100644 index 0000000..f1a2885 --- /dev/null +++ b/src/lib/styles/buttons.css @@ -0,0 +1,73 @@ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 12px; + padding: 14px 32px; + font-size: 15px; + font-weight: 600; + border-radius: 40px; + cursor: pointer; + transition: + background 0.2s, + color 0.2s, + border-color 0.2s, + transform 0.16s cubic-bezier(0.22, 1, 0.36, 1), + box-shadow 0.2s ease, + filter 0.2s ease; + border: none; + -webkit-tap-highlight-color: transparent; + touch-action: manipulation; +} + +@media (hover: hover) { + .btn:hover { + transform: translateY(-2px) scale(1.012); + box-shadow: 0 12px 24px rgba(17, 20, 24, 0.14); + } +} + +.btn:active { + transform: translateY(1px) scale(0.985); + box-shadow: 0 4px 10px rgba(17, 20, 24, 0.12); + filter: saturate(1.03); +} + +.btn-green { + background: var(--green); + color: #fff; +} + +.btn-green:hover { + background: #172217; +} + +.btn-yellow { + background: var(--yellow); + color: #000; +} + +.btn-yellow:hover { + background: #e6bb00; +} + +.btn-outline { + background: transparent; + color: #fff; + border: 2px solid #fff; +} + +.btn-outline:hover { + background: #fff; + color: var(--green); +} + +.btn-outline-green { + color: var(--green); + border-color: var(--green); +} + +.btn-outline-green:hover { + background: var(--green); + color: #fff; +} diff --git a/src/lib/styles/forms.css b/src/lib/styles/forms.css new file mode 100644 index 0000000..a84f4d4 --- /dev/null +++ b/src/lib/styles/forms.css @@ -0,0 +1,359 @@ +.form-inner { + max-width: 920px; +} + +.booking-header { + text-align: center; + margin-bottom: 44px; +} + +.booking-title { + display: inline-flex; + align-items: baseline; + justify-content: center; + gap: 14px; + margin-bottom: 28px; + font-family: var(--font-head); + font-size: 68px; + font-weight: 800; + line-height: 0.96; + letter-spacing: -0.04em; +} + +.booking-title-plain { + color: #000; +} + +.booking-title-highlight { + position: relative; + color: var(--yellow); +} + +.booking-title-highlight::after { + content: ''; + position: absolute; + left: 0; + right: -8px; + bottom: -18px; + height: 28px; + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 34' fill='none'%3E%3Cpath d='M4 24C67 10 131 4 198 5c43 1 82 6 118 18' stroke='%23192419' stroke-width='8' stroke-linecap='round'/%3E%3C/svg%3E") + center/contain no-repeat; +} + +.booking-stepper { + display: flex; + align-items: center; + justify-content: center; + gap: 28px; +} + +.booking-step { + display: inline-flex; + align-items: center; + gap: 12px; + border: 0; + background: transparent; + color: #000; + cursor: pointer; + padding: 0; + transition: transform 0.14s cubic-bezier(0.22, 1, 0.36, 1); + -webkit-tap-highlight-color: transparent; + touch-action: manipulation; +} + +.booking-step-number { + width: 56px; + height: 56px; + border: 3px solid #111; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + font-family: var(--font-head); + font-size: 24px; + font-weight: 700; + background: #fff; + transition: + background 0.2s, + border-color 0.2s, + transform 0.16s cubic-bezier(0.22, 1, 0.36, 1), + box-shadow 0.2s ease; +} + +.booking-step.active .booking-step-number { + background: #eadbbf; +} + +@media (hover: hover) { + .booking-step:hover .booking-step-number { + transform: translateY(-2px) scale(1.04); + box-shadow: 0 10px 20px rgba(17, 20, 24, 0.1); + } +} + +.booking-step:active .booking-step-number { + transform: translateY(1px) scale(0.96); +} + +.booking-step-label { + font-family: var(--font-head); + font-size: 18px; + font-weight: 700; +} + +.booking-step-divider { + width: 1px; + height: 92px; + background: #cfd2d6; +} + +.booking-form { + display: flex; + flex-direction: column; + align-items: stretch; +} + +.booking-panel { + display: flex; + flex-direction: column; + gap: 18px; +} + +.booking-panel-banner { + background: #eadbbf; + color: #34363a; + border-radius: 30px 30px 0 0; + padding: 28px 32px 42px; + text-align: center; + font-family: var(--font-body); + font-size: 15px; + line-height: 1.35; +} + +.booking-card-grid { + display: grid; + gap: 0; +} + +.booking-card-grid-with-banner { + margin-top: -30px; +} + +.booking-card-grid-owner { + grid-template-columns: repeat(3, 1fr); +} + +.booking-card-grid-dog { + grid-template-columns: repeat(2, 1fr); +} + +.booking-field-card { + background: #fff; + border-radius: 28px; + padding: 32px 38px 30px; + box-shadow: 0 10px 30px rgba(17, 20, 24, 0.04); +} + +.booking-field-card-group { + padding: 30px 32px; +} + +.booking-field-card-wide, +.booking-field-card-full { + grid-column: span 1; +} + +.booking-field-card-full { + grid-column: 1 / -1; +} + +.booking-field-group { + display: grid; + gap: 24px; +} + +.booking-field-group-owner { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.booking-field-stack { + min-width: 0; +} + +.booking-field-card label, +.booking-service-label { + display: inline-flex; + align-items: center; + gap: 7px; + margin-bottom: 10px; + font-family: var(--font-head); + font-size: 15px; + font-weight: 700; + color: #34363a; +} + +.booking-required { + color: var(--yellow); +} + +.booking-field-card input, +.booking-field-card textarea { + width: 100%; + border: 3px solid #111; + border-radius: 20px; + background: #fff; + padding: 16px 28px; + font-size: 16px; + line-height: 1.2; + color: #34363a; + transition: + background 0.2s, + border-color 0.2s; +} + +.booking-field-card input:hover, +.booking-field-card textarea:hover { + background: #f6f0e5; +} + +.booking-field-card input:focus, +.booking-field-card textarea:focus { + outline: none; + border-color: var(--green); + background: #f6f0e5; +} + +.booking-field-card textarea { + min-height: 140px; + resize: vertical; +} + +.booking-service-row { + display: grid; + grid-template-columns: 220px 1fr; + align-items: center; + gap: 24px; + background: #fff; + border-radius: 28px; + padding: 22px 28px; + box-shadow: 0 10px 30px rgba(17, 20, 24, 0.04); +} + + +.booking-service-options { + display: flex; + flex-wrap: wrap; + gap: 16px 22px; +} + +.booking-check-option { + display: inline-flex; + align-items: center; + gap: 14px; + font-family: var(--font-head); + font-size: 16px; + font-weight: 600; + color: #34363a; + cursor: pointer; +} + +.booking-check-option input { + position: absolute; + opacity: 0; + pointer-events: none; +} + +.booking-check-box { + width: 30px; + height: 30px; + border: 3px solid #111; + border-radius: 8px; + display: inline-flex; + align-items: center; + justify-content: center; + flex: none; + transition: + background 0.2s, + border-color 0.2s; +} + +.booking-check-option input:checked + .booking-check-box { + background: var(--yellow); + border-color: #111; +} + +.booking-check-option input:checked + .booking-check-box::after { + content: ''; + width: 10px; + height: 16px; + border-right: 3px solid #111; + border-bottom: 3px solid #111; + transform: rotate(40deg) translate(-1px, -2px); +} + +.booking-actions { + display: flex; + align-items: center; + margin-top: 28px; +} + +.booking-actions-next { + justify-content: center; +} + +.booking-actions-final { + justify-content: space-between; +} + +.booking-next-button, +.booking-submit-button { + display: inline-flex; + align-items: center; + gap: 14px; + padding: 18px 34px; + font-family: var(--font-head); + font-size: 18px; + font-weight: 700; +} + +.booking-next-button .icon, +.booking-submit-button .icon { + font-size: 18px; +} + +.booking-field-card input.input-invalid, +.booking-field-card textarea.input-invalid { + border-color: #c94040; +} + +.booking-field-card-invalid, +.booking-field-stack-invalid { + box-shadow: 0 10px 30px rgba(201, 64, 64, 0.08); +} + +.field-error { + display: flex; + align-items: center; + gap: 7px; + margin: 10px 0 0; + color: #c94040; + font-size: 13px; + font-weight: 600; + animation: fieldErrorIn 0.16s ease; +} + +.field-error .icon { + font-size: 13px; + flex-shrink: 0; +} + +@keyframes fieldErrorIn { + from { + opacity: 0; + transform: translateY(-3px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/src/lib/styles/layout.css b/src/lib/styles/layout.css new file mode 100644 index 0000000..52fb8c2 --- /dev/null +++ b/src/lib/styles/layout.css @@ -0,0 +1,311 @@ +header { + z-index: 100; + background: var(--green); +} + +nav, +.mobile-menu, +.hero-inner, +.promise-inner, +.services-inner, +.values-inner, +.testimonials-inner, +.info-inner, +.form-inner, +.footer-inner, +.footer-bottom { + max-width: var(--max-w); + margin: 0 auto; +} + +nav { + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: center; + padding: 16px 50px; +} + +.nav-links { + display: flex; + gap: 4px; + align-items: center; + list-style: none; +} + +.nav-links li { + position: relative; +} + +.nav-links > li > a { + color: #fff; + font-weight: 500; + display: flex; + align-items: center; + gap: 6px; + white-space: nowrap; + padding: 8px 16px; + border-radius: 40px; + transition: background 0.15s, color 0.15s; +} + +.nav-links > li > a:hover { + background: #fff; + color: var(--green); +} + +.nav-links > li > a.nav-link-active { + background: #eadbbf; + color: #000; +} + +.mega-chevron { + font-size: 11px; + transition: transform 0.2s; +} + +.has-mega:hover .mega-chevron { + transform: rotate(180deg); +} + +.mega-menu { + position: absolute; + top: 100%; + left: 0; + padding-top: 12px; + z-index: 200; + opacity: 0; + visibility: hidden; + transform: translateY(-6px); + transition: + opacity 0.18s ease, + transform 0.18s ease, + visibility 0.18s; +} + +.mega-menu-inner { + background: #fff; + border-radius: 20px; + padding: 16px; + display: flex; + flex-direction: row; + gap: 6px; + box-shadow: + 0 4px 6px rgba(0, 0, 0, 0.04), + 0 16px 40px rgba(0, 0, 0, 0.12); + min-width: 400px; +} + +.has-mega:hover .mega-menu { + opacity: 1; + visibility: visible; + transform: translateY(0); +} + +.mega-service { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + padding: 16px 14px 14px; + border-radius: 14px; + flex: 1; + text-decoration: none; + color: var(--text); + transition: background 0.15s; +} + +.mega-service:hover { + background: rgba(33, 48, 33, 0.05); +} + +.mega-icon { + width: 64px; + height: 64px; + background: var(--green); + border-radius: 16px; + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; + color: #fff; + transition: background 0.15s; +} + +.mega-service:hover .mega-icon { + background: #2d4230; +} + +.mega-service-label { + font-weight: 700; + font-size: 14px; + color: #000; + white-space: nowrap; +} + +.mega-service-desc { + font-size: 12px; + font-weight: 400; + color: var(--gray); + text-align: center; + line-height: 1.3; + margin-top: -4px; +} + +.nav-right { + display: flex; + align-items: center; + gap: 20px; + justify-content: flex-end; +} + +.mobile-phone { + display: none; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.08); + color: #fff; + font-size: 14px; + font-weight: 700; + line-height: 1; + text-decoration: none; + white-space: nowrap; +} + +.mobile-phone .icon { + font-size: 14px; +} + +.instagram-icon { + color: #fff; + font-size: 26px; + line-height: 1; + display: flex; + align-items: center; + justify-content: center; +} + +.instagram-icon:hover { + color: var(--yellow); +} + +.mobile-menu { + display: none; + flex-direction: column; + gap: 0; + background: #fff; + padding: 0 0 18px; +} + +.mobile-menu.open { + display: flex; +} + +.mobile-menu a { + color: #0a304e; + font-weight: 700; + opacity: 0; +} + +.mobile-menu a:hover { + color: #0a304e; +} + +.mobile-menu a.mobile-link-active { + background: #eadbbf; + color: #0a304e; +} + +.hamburger { + display: none; + align-items: center; + justify-content: center; + cursor: pointer; + background: none; + border: none; + padding: 4px; + color: #fff; + font-size: 26px; + transition: + transform 0.14s cubic-bezier(0.22, 1, 0.36, 1), + opacity 0.2s ease; + -webkit-tap-highlight-color: transparent; + touch-action: manipulation; +} + +.hamburger .icon { + display: block; + line-height: 1; +} + +@media (hover: hover) { + .hamburger:hover { + transform: translateY(-1px) scale(1.06); + } +} + +.hamburger:active { + transform: translateY(1px) scale(0.94); +} + +.hero-inner, +.promise-inner { + display: flex; + align-items: center; + gap: 60px; + width: 100%; +} + +.promise-inner, +.services-inner, +.values-inner, +.testimonials-inner, +.info-inner, +.form-inner { + padding: 0 50px; +} + +.services-grid, +.values-grid, +.testimonials-grid { + margin-top: 48px; +} + +.services-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 32px; +} + +.values-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 28px; +} + +.testimonials-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 28px; +} + +.info-inner { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 60px; + align-items: start; +} + +.footer-inner { + display: grid; + grid-template-columns: 2fr 1.4fr 2fr; + gap: 60px; + margin-bottom: 48px; + align-items: start; +} + +.social-links { + display: flex; + gap: 14px; +} diff --git a/src/lib/styles/responsive.css b/src/lib/styles/responsive.css new file mode 100644 index 0000000..a884e11 --- /dev/null +++ b/src/lib/styles/responsive.css @@ -0,0 +1,543 @@ +@media (max-width: 1024px) { + nav, + .mobile-menu { + padding-left: 30px; + padding-right: 30px; + } + + #hero { + padding-left: 30px; + padding-right: 30px; + } + + .promise-inner, + .services-inner, + .values-inner, + .testimonials-inner, + .info-inner, + .form-inner { + padding-left: 30px; + padding-right: 30px; + } + + footer { + padding-left: 30px; + padding-right: 30px; + } + + .hero-text h1 { + font-size: 40px; + } + + .values-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 768px) { + @keyframes mobileMenuBounceIn { + 0% { + opacity: 0; + transform: translateY(-10px) scaleY(0.98); + } + + 70% { + opacity: 1; + transform: translateY(2px) scaleY(1.01); + } + + 100% { + opacity: 1; + transform: translateY(0) scaleY(1); + } + } + + @keyframes mobileMenuItemIn { + 0% { + opacity: 0; + transform: translateY(-6px); + } + + 100% { + opacity: 1; + transform: translateY(0); + } + } + + header { + background: #fff; + border-bottom: 1px solid rgba(0, 0, 0, 0.08); + } + + nav { + grid-template-columns: auto minmax(0, 1fr) auto auto; + padding: 20px 24px; + column-gap: 12px; + } + + .logo { + justify-content: flex-start; + justify-self: start; + } + + .logo img { + height: 25px; + width: auto; + } + + .mobile-phone { + display: inline-flex; + justify-self: center; + align-self: center; + padding: 9px 12px; + background: rgba(33, 48, 33, 0.06); + color: var(--green); + font-size: 13px; + } + + .mobile-phone .icon { + font-size: 13px; + } + + .nav-links { + display: none; + } + + .nav-right { + display: flex; + gap: 18px; + justify-content: flex-end; + grid-column: 3; + } + + .nav-right .btn { + display: none; + } + + .instagram-icon { + color: #0a304e; + font-size: 24px; + } + + .hamburger { + display: flex; + color: #2e3031; + grid-column: 4; + justify-self: end; + } + + .mobile-menu { + max-width: none; + padding-left: 0; + padding-right: 0; + transform-origin: top center; + } + + .mobile-menu.open { + animation: mobileMenuBounceIn 220ms cubic-bezier(0.22, 1, 0.36, 1); + } + + .mobile-menu a { + display: block; + padding: 14px 24px; + border-bottom: none; + font-size: 16px; + line-height: 1.35; + animation: mobileMenuItemIn 220ms ease-out forwards; + } + + .mobile-menu.open a:nth-child(1) { + animation-delay: 30ms; + } + + .mobile-menu.open a:nth-child(2) { + animation-delay: 60ms; + } + + .mobile-menu.open a:nth-child(3) { + animation-delay: 90ms; + } + + .mobile-menu.open a:nth-child(4) { + animation-delay: 120ms; + } + + .mobile-menu.open a:nth-child(5) { + animation-delay: 150ms; + } + + .mobile-menu.open a:nth-child(6) { + animation-delay: 180ms; + } + + .mobile-menu.open a:nth-child(7) { + animation-delay: 210ms; + } + + #hero { + min-height: auto; + padding: 50px 20px 0; + } + + .hero-inner { + flex-direction: column; + gap: 24px; + align-items: stretch; + text-align: left; + padding: 0; + } + + .hero-text { + display: flex; + flex-direction: column; + align-items: flex-start; + width: 100%; + } + + .hero-text h1, + .hero-heading { + margin-bottom: 22px; + font-size: 38px; + line-height: 1.08; + } + + .hero-heading-desktop { + display: none; + } + + .hero-text h1 .hero-heading-mobile { + display: block; + color: #fff; + font-family: var(--font-head); + font-size: 33.5px; + font-weight: 800; + line-height: 1.08; + letter-spacing: -0.04em; + white-space: pre-line; + } + + .hero-buttons { + width: 100%; + justify-content: flex-start; + gap: 10px; + padding-right: 18px; + } + + .hero-buttons .btn { + flex: 0 0 auto; + width: auto; + min-width: 0; + padding: 17px 28px; + font-size: 15px; + font-weight: 700; + text-align: center; + border-radius: 999px; + -webkit-tap-highlight-color: transparent; + touch-action: manipulation; + } + + .hero-buttons .btn:last-child { + margin-right: 12px; + } + + .hero-buttons .btn-yellow { + background: #e8dbc1; + color: #000; + } + + .hero-buttons .btn-outline { + background: var(--yellow); + color: #000; + border: none; + } + + .hero-buttons .btn:active { + transform: translateY(1px) scale(0.985); + } + + .hero-buttons .btn-yellow:active { + background: #dccdb1; + } + + .hero-buttons .btn-outline:active { + background: #e6bb00; + } + + .hero-img { + flex: none; + width: 100%; + max-width: none; + margin: 0; + display: flex; + justify-content: center; + align-items: flex-end; + } + + .hero-img img { + width: min(100%, 500px); + max-width: 100%; + margin: 0 auto -7px; + object-fit: contain; + object-position: center top; + transform: none; + } + + #intro { + padding: 30px 24px 24px; + } + + .intro-trust-badge { + grid-template-columns: 1fr; + gap: 14px; + padding: 18px 18px 16px; + border-radius: 22px; + } + + .intro-trust-mark { + width: 48px; + height: 48px; + font-size: 21px; + } + + #intro p { + font-size: 17px; + line-height: 1.45; + font-weight: 600; + margin-bottom: 0; + } + + .intro-trust-meta { + flex-direction: column; + align-items: flex-start; + gap: 10px; + } + + #intro a { + gap: 8px; + padding: 8px 14px; + font-size: 15px; + } + + .intro-trust-stars { + font-size: 13px; + } + + .promise-inner { + flex-direction: column; + padding: 0 24px; + } + + .promise-text { + order: 1; + } + + .promise-img { + order: 2; + flex: none; + width: 100%; + max-width: 320px; + margin: 0 auto; + } + + .services-grid, + .values-grid, + .testimonials-grid, + .info-inner, + .field-group, + .footer-inner { + grid-template-columns: 1fr; + } + + .service-icon-bubble { + width: 78px; + height: 78px; + margin-bottom: 20px; + } + + .service-card .service-card-icon { + font-size: 30px; + } + + .mobile-menu a.mobile-link-active { + background: var(--yellow); + color: #0a304e; + } + + .booking-header { + margin-bottom: 34px; + } + + .booking-title { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 2px; + width: 100%; + font-size: 34px; + line-height: 0.98; + margin-bottom: 24px; + text-align: center; + } + + .booking-title-highlight::after { + left: 12px; + right: 12px; + bottom: -14px; + height: 20px; + } + + .booking-stepper { + gap: 12px; + align-items: flex-start; + } + + .booking-step { + flex-direction: column; + gap: 8px; + flex: 1; + } + + .booking-step-number { + width: 46px; + height: 46px; + font-size: 20px; + } + + .booking-step-label { + font-size: 15px; + text-align: center; + } + + .booking-step-divider { + margin-top: 22px; + height: 1px; + width: 36px; + } + + .booking-panel-banner { + padding: 22px 18px 34px; + border-radius: 24px 24px 0 0; + font-size: 15px; + line-height: 1.45; + } + + .booking-card-grid-owner, + .booking-card-grid-dog { + grid-template-columns: 1fr; + } + + .booking-field-group-owner { + grid-template-columns: 1fr; + gap: 18px; + } + + .booking-field-card { + padding: 24px 22px; + border-radius: 24px; + } + + .booking-field-card-group { + padding: 24px 22px; + } + + .booking-field-card label, + .booking-service-label { + font-size: 16px; + } + + .booking-field-card input, + .booking-field-card textarea { + padding: 14px 18px; + font-size: 15px; + border-width: 2px; + border-radius: 18px; + } + + .booking-service-row { + grid-template-columns: 1fr; + gap: 16px; + padding: 22px; + border-radius: 24px; + } + + .booking-service-options { + gap: 12px 18px; + } + + .booking-check-option { + font-size: 15px; + } + + .booking-check-box { + width: 24px; + height: 24px; + border-width: 2px; + border-radius: 6px; + } + + .booking-actions-final { + gap: 14px; + flex-direction: column-reverse; + align-items: stretch; + } + + .booking-actions-final .btn, + .booking-actions-next .btn { + justify-content: center; + } + + .services-inner, + .values-inner, + .testimonials-inner, + .info-inner, + .form-inner { + padding: 0 24px; + } + + .section-heading { + font-size: 30px; + } + + footer { + padding: 48px 24px 28px; + } + + .footer-inner { + gap: 36px; + } + + .footer-action { + order: -1; + } + + .footer-book-note { + text-align: left; + } + + .footer-nav a { + padding: 11px 0; + } + + .footer-bottom { + flex-direction: column; + align-items: flex-start; + gap: 10px; + } + + #instagram { + padding: 48px 24px; + } +} + +@media (max-width: 480px) { + .mobile-phone { + gap: 6px; + padding: 9px 10px; + font-size: 12px; + } + + .mobile-phone span { + letter-spacing: -0.01em; + } +} diff --git a/src/lib/styles/sections.css b/src/lib/styles/sections.css new file mode 100644 index 0000000..1df543a --- /dev/null +++ b/src/lib/styles/sections.css @@ -0,0 +1,537 @@ +section { + padding: 80px 0; +} + +#hero { + background: var(--green); + color: #fff; + padding: 44px 50px 0; + display: flex; + align-items: center; + min-height: 500px; + overflow: hidden; +} + +.hero-inner { + align-items: flex-end; +} + +.hero-text, +.promise-text { + flex: 1; +} + +.hero-text { + padding-bottom: 44px; +} + +.hero-buttons { + display: flex; + gap: 16px; + flex-wrap: wrap; +} + +.hero-img { + flex: 0 0 46%; + display: flex; + justify-content: flex-end; + align-self: flex-end; + margin-top: 0; +} + +.hero-img img { + width: min(100%, 530px); + margin: -12px -18px -72px 0; +} + +#intro { + background: #fff; + padding: 8px 50px 26px; + text-align: left; +} + +.intro-trust-badge { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 18px; + align-items: center; + width: 100%; + max-width: 960px; + margin: 0; + padding: 18px 22px; + border-radius: 26px; + background: linear-gradient(180deg, #ffffff 0%, #fbf8f2 100%); + box-shadow: + 0 1px 0 rgba(17, 20, 24, 0.04), + 0 14px 34px rgba(17, 20, 24, 0.06); +} + +.intro-trust-mark { + display: inline-flex; + align-items: center; + justify-content: center; + width: 56px; + height: 56px; + border-radius: 50%; + background: #fff; + color: #e00706; + font-size: 24px; + box-shadow: inset 0 0 0 1px rgba(14, 27, 41, 0.08); +} + +.intro-trust-copy { + min-width: 0; +} + +#intro p { + font-size: 18px; + font-weight: 600; + line-height: 1.35; + max-width: none; + margin: 0; + color: #34363a; +} + +.intro-trust-meta { + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; + margin-top: 12px; +} + +.intro-trust-stars { + display: inline-flex; + align-items: center; + gap: 6px; + color: var(--yellow); + font-size: 14px; +} + +#intro a { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 9px 16px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.92); + color: var(--green); + font-size: 16px; + font-weight: 700; + text-decoration: none; + box-shadow: inset 0 0 0 1px rgba(14, 27, 41, 0.08); +} + +#intro a .icon { + color: #e00706; + font-size: 20px; +} + +#promise, +#testimonials, +#info { + background: var(--off-white); +} + +#services, +#reservation { + background: #fff; +} + +.promise-text h2 { + text-align: center; +} + +.promise-text p { + margin-bottom: 28px; + font-size: 16px; + max-width: 520px; +} + +.promise-text .btn { + display: block; + width: fit-content; + margin: 0 auto; +} + +.promise-text { + order: 2; +} + +.promise-img { + order: 1; + flex: 0 0 48%; + max-width: 560px; + display: flex; + justify-content: center; + align-items: flex-end; +} + +.promise-img img { + width: 100%; + max-width: 560px; +} + +.service-card { + background: var(--off-white); + border-radius: 20px; + padding: 40px 32px; + text-align: center; + transition: + transform 0.18s cubic-bezier(0.22, 1, 0.36, 1), + box-shadow 0.22s ease; +} + +.service-icon-bubble { + display: inline-flex; + align-items: center; + justify-content: center; + width: 88px; + height: 88px; + margin: 0 auto 24px; + border-radius: 50%; + box-shadow: + inset 0 0 0 1px rgba(17, 20, 24, 0.05), + 0 10px 24px rgba(17, 20, 24, 0.08); +} + +.service-icon-bubble { + background: linear-gradient(180deg, #ffe173 0%, #ffd54a 100%); +} + +@media (hover: hover) { + .service-card:hover { + transform: translateY(-6px) scale(1.01); + box-shadow: 0 18px 38px rgba(0, 0, 0, 0.1); + } +} + +.service-card:active { + transform: translateY(-1px) scale(0.992); +} + +.service-card .service-card-icon { + font-size: 34px; + color: var(--green); + margin-bottom: 0; +} + + +.service-card a.btn { + margin-top: 18px; +} + +#values, +footer { + background: var(--green); + color: #fff; +} + +#values .section-heading, +#testimonials .section-heading { + text-align: center; +} + +#services .section-heading { + text-align: center; +} + +footer { + padding: 60px 50px 32px; +} + +.values-inner .section-heading { + color: #fff; +} + +.value-card { + background: rgba(255, 255, 255, 0.07); + border-radius: 16px; + padding: 32px 28px; + border: 1px solid rgba(255, 255, 255, 0.12); + transition: + transform 0.18s cubic-bezier(0.22, 1, 0.36, 1), + box-shadow 0.22s ease, + border-color 0.22s ease; +} + +@media (hover: hover) { + .value-card:hover { + transform: translateY(-5px); + box-shadow: 0 18px 34px rgba(0, 0, 0, 0.14); + border-color: rgba(255, 255, 255, 0.18); + } +} + +.value-card:active { + transform: translateY(-1px) scale(0.994); +} + +.value-card .value-card-icon { + font-size: 28px; + color: var(--yellow); + margin-bottom: 16px; +} + +.value-card p { + font-size: 14px; + line-height: 1.7; + opacity: 0.85; +} + +.testimonial-card { + background: #fff; + border-radius: 20px; + padding: 36px 32px; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06); + transition: + transform 0.18s cubic-bezier(0.22, 1, 0.36, 1), + box-shadow 0.22s ease; +} + +@media (hover: hover) { + .testimonial-card:hover { + transform: translateY(-4px); + box-shadow: 0 18px 34px rgba(0, 0, 0, 0.09); + } +} + +.testimonial-card:active { + transform: translateY(-1px) scale(0.994); +} + +.stars { + color: var(--yellow); + font-size: 18px; + margin-bottom: 14px; +} + +.reviewer { + font-weight: 700; + font-size: 14px; +} + +.reviewer span { + font-weight: 400; + color: var(--gray); +} + +.info-copy { + margin-top: 10px; +} + +.info-copy a { + color: var(--green); + font-weight: 600; +} + +.faq details { + border-bottom: 1px solid #ddd; + padding: 16px 0; +} + +.faq summary { + font-weight: 600; + cursor: pointer; + list-style: none; + display: flex; + justify-content: space-between; + align-items: center; +} + +.faq summary::-webkit-details-marker { + display: none; +} + +.faq summary::after { + content: '+'; + font-size: 20px; + color: var(--green); +} + +.faq details[open] summary::after { + content: '−'; +} + +.faq details p { + margin-top: 10px; + color: var(--gray); +} + +#instagram { + background: var(--yellow); + text-align: center; + padding: 60px 50px; +} + +.instagram-blurb { + margin: -8px 0 24px; + font-size: 16px; + color: rgba(0, 0, 0, 0.6); +} + +#instagram .btn { + background: var(--green); + color: #fff; +} + +.footer-logo { + display: block; + margin-bottom: 20px; +} + +.footer-brand p { + font-size: 14px; + line-height: 1.6; + opacity: 0.65; + margin-bottom: 24px; + white-space: pre-line; +} + +.social-links a { + width: 40px; + height: 40px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + transition: background 0.2s, color 0.2s; +} + +.social-links a:hover { + background: var(--yellow); + color: #000; +} + +.footer-col-label { + margin: 0 0 16px; + font-family: var(--font-head); + font-size: 10px; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; + opacity: 0.45; +} + +.footer-nav { + list-style: none; +} + +.footer-nav li { + margin: 0; +} + +.footer-nav a { + display: block; + padding: 8px 0; + font-size: 15px; + font-weight: 500; + opacity: 0.75; + transition: opacity 0.18s; +} + +.footer-nav a:hover { + opacity: 1; +} + +.footer-book-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + width: 100%; + padding: 15px 22px; + border-radius: 999px; + background: var(--yellow); + color: #000; + font-weight: 700; + font-size: 15px; + transition: background 0.2s, transform 0.15s; + margin-bottom: 10px; +} + +.footer-book-btn:hover { + background: #ffe033; + transform: translateY(-1px); +} + +.footer-book-note { + margin: 0 0 20px; + font-size: 13px; + opacity: 0.5; + text-align: center; +} + +.footer-reviews { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 9px 16px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.07); + border: 1px solid rgba(255, 255, 255, 0.12); + font-size: 13px; + opacity: 0.8; + transition: background 0.2s, opacity 0.2s; +} + +.footer-reviews:hover { + background: rgba(255, 255, 255, 0.13); + opacity: 1; +} + +.footer-contact { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 16px; + padding-top: 16px; + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +.footer-contact-link { + display: inline-flex; + align-items: center; + gap: 8px; + font-size: 13px; + font-weight: 500; + opacity: 0.7; + transition: opacity 0.18s; +} + +.footer-contact-link:hover { + opacity: 1; +} + +.footer-contact-link .icon { + font-size: 12px; + opacity: 0.8; +} + +.footer-bottom { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 12px; + border-top: 1px solid rgba(255, 255, 255, 0.1); + padding-top: 24px; + font-size: 13px; + opacity: 0.5; +} + +.footer-legal { + display: flex; + gap: 20px; +} + +.footer-legal a { + opacity: 0.8; + transition: opacity 0.2s; +} + +.footer-legal a:hover { + opacity: 1; +} diff --git a/src/lib/styles/typography.css b/src/lib/styles/typography.css new file mode 100644 index 0000000..f9ef9eb --- /dev/null +++ b/src/lib/styles/typography.css @@ -0,0 +1,108 @@ +.logo { + display: flex; + align-items: center; + justify-content: center; +} + +.logo img, +.footer-logo { + display: block; +} + +.logo img { + height: 30px; + width: auto; +} + +.nav-links a, +.mobile-menu a { + font-weight: 500; +} + +.section-heading, +.promise-text h2, +.info-block h2, +#instagram h2 { + font-family: var(--font-head); + font-weight: 700; +} + +.section-heading { + font-size: 42px; + color: #000; + margin-bottom: 20px; +} + +.hero-text h1 { + font-family: var(--font-head); + font-size: 50.2px; + font-weight: 800; + line-height: 1.1; + margin-bottom: 28px; + color: #fff; +} + +.hero-text h1 .hero-heading-mobile { + display: none; +} + +.hero-title-main, +.hero-title-connector, +.hero-title-highlight { + color: #fff; +} + +.promise-text h2 { + font-size: 42px; + margin-bottom: 20px; +} + +.promise-text p, +.info-block p, +.faq details p, +.testimonial-card blockquote { + font-size: 15px; + line-height: 1.7; +} + +.service-card h3, +.value-card h3 { + font-family: var(--font-head); + font-weight: 700; +} + +.service-card h3 { + font-size: 20px; + margin-bottom: 12px; +} + +.value-card h3 { + font-size: 18px; + margin-bottom: 10px; +} + +.info-block h2 { + font-size: 30px; + margin-bottom: 16px; +} + +.info-block h3 { + font-size: 16px; + font-weight: 700; + margin: 20px 0 8px; +} + +#instagram h2 { + font-size: 36px; + margin-bottom: 20px; +} + +footer h4 { + font-family: var(--font-head); + font-size: 14px; + font-weight: 700; + margin-bottom: 16px; + letter-spacing: 0.5px; + opacity: 0.6; + text-transform: uppercase; +} diff --git a/src/lib/styles/variables.css b/src/lib/styles/variables.css new file mode 100644 index 0000000..1e98c1a --- /dev/null +++ b/src/lib/styles/variables.css @@ -0,0 +1,14 @@ +:root { + --green: #213021; + --yellow: #ffd100; + --gray: #59606d; + --beige: #e5d6c2; + --off-white: #fbfbfb; + --text: #2e3031; + --max-w: 1280px; + --font-body: 'Readex Pro', sans-serif; + --font-head: 'Unbounded', sans-serif; + + /* Legacy "navy" tokens now intentionally render as Goodwalk green. */ + --navy: var(--green); +} diff --git a/src/lib/types.ts b/src/lib/types.ts new file mode 100644 index 0000000..43a3e78 --- /dev/null +++ b/src/lib/types.ts @@ -0,0 +1,229 @@ +export interface LinkItem { + label: string; + href: string; + external?: boolean; +} + +export interface CallToAction { + label: string; + href: string; + variant?: 'green' | 'yellow' | 'outline'; + external?: boolean; +} + +export interface SeoContent { + title: string; + description: string; +} + +export interface MegaMenuService { + icon: string; + label: string; + description?: string; + href: string; +} + +export interface NavigationContent { + desktopLinks: LinkItem[]; + mobileLinks: LinkItem[]; + cta: CallToAction; + instagram?: { href: string; external?: boolean }; + megaMenuServices?: MegaMenuService[]; +} + +export interface HeroContent { + title: string; + highlight: string; + mobileTitle?: string; + primaryCta: CallToAction; + secondaryCta: CallToAction; + imageUrl: string; + imageAlt: string; +} + +export interface IntroContent { + text: string; + reviewCta: CallToAction; +} + +export interface PromiseContent { + title: string; + subtitle: string; + body: string; + emphasis: string; + cta: CallToAction; + imageUrl: string; + imageAlt: string; +} + +export interface IconCard { + icon: string; + title: string; + body: string; + href?: string; +} + +export interface TestimonialContent { + quote: string; + reviewer: string; + detail: string; + imageUrl?: string; +} + +export interface BookingContent { + title: string; + subtitle: string; + formAction: string; + serviceOptions: string[]; + ownerStepLabel?: string; + dogStepLabel?: string; + dogIntro?: string; +} + +export interface ServicePricingPlan { + title: string; + price: string; + period: string; + popular?: boolean; + features: string[]; +} + +export interface ServiceExtra { + label: string; + price: string; + note?: string; +} + +export interface ServiceBenefit { + title: string; + body: string; +} + +export interface ServicePageContent { + hero: { + eyebrow: string; + title: string; + paragraphs: string[]; + imageUrl: string; + imageAlt: string; + }; + highlight?: { + eyebrow: string; + title: string; + imageUrl: string; + imageAlt: string; + }; + pricing: { + title: string; + intro?: string; + plans: ServicePricingPlan[]; + extras?: ServiceExtra[]; + }; + benefits: { + title: string; + items: ServiceBenefit[]; + }; + testimonialsHeading: string; + booking: BookingContent; +} + +export interface PricingPageSection { + title: string; + icon?: string; + blurb?: string; + detailCta?: CallToAction; + plans: ServicePricingPlan[]; +} + +export interface PricingPageContent { + title: string; + subtitle?: string; + sections: PricingPageSection[]; + testimonialsHeading: string; + booking: BookingContent; +} + +export interface AboutPageSection { + title: string; + body: string[]; + imageUrl: string; + imageAlt: string; + reverse?: boolean; + accent?: 'plain' | 'gradient'; +} + +export interface AboutPageContent { + title: string; + sections: AboutPageSection[]; + servicesTitle: string; + contact: { + title: string; + email: string; + phone: string; + cta: CallToAction; + }; +} + +export interface LegalPageBlock { + type: 'paragraph' | 'list'; + content: string | string[]; +} + +export interface LegalPageSection { + title: string; + blocks: LegalPageBlock[]; +} + +export interface LegalPageContent { + title: string; + sections: LegalPageSection[]; +} + +export interface FaqItem { + question: string; + answer: string; +} + +export interface InfoContent { + title: string; + intro: string; + suburbs: string; + nearbyText: string; + nearbyCta: CallToAction; + hoursLabel: string; + hours: string; + faqTitle: string; + faqs: FaqItem[]; +} + +export interface FooterContent { + brandText: string; + navigationLinks: LinkItem[]; + contactLinks: LinkItem[]; + copyright: string; + email?: string; + phone?: string; +} + +export interface HomePageContent { + seo: SeoContent; + navigation: NavigationContent; + hero: HeroContent; + intro: IntroContent; + promise: PromiseContent; + services: IconCard[]; + values: IconCard[]; + testimonials: TestimonialContent[]; + booking: BookingContent; + info: InfoContent; + instagram: CallToAction & { title: string }; + footer: FooterContent; +} + +export interface SiteSharedContent { + navigation: NavigationContent; + services: IconCard[]; + testimonials: TestimonialContent[]; + booking: BookingContent; + footer: FooterContent; +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..c01f588 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,35 @@ + + + diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts new file mode 100644 index 0000000..dbfef69 --- /dev/null +++ b/src/routes/+page.server.ts @@ -0,0 +1,7 @@ +import { getHomepageContent } from '$lib/server/content'; + +export async function load() { + return { + content: await getHomepageContent() + }; +} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..3294fdc --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,121 @@ + + + + +
+ + + + + + + + + +