148 lines
3.9 KiB
Svelte
148 lines
3.9 KiB
Svelte
|
|
<script lang="ts">
|
||
|
|
import { onMount } from 'svelte';
|
||
|
|
import { afterNavigate } from '$app/navigation';
|
||
|
|
import { page } from '$app/stores';
|
||
|
|
import Icon from '$lib/components/Icon.svelte';
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Sticky bottom CTA shown on mobile only.
|
||
|
|
*
|
||
|
|
* Pattern is the Airbnb-style "soft container, scroll-triggered" —
|
||
|
|
* - white card sits flush against the bottom edge with a thin top
|
||
|
|
* hairline and a soft shadow so it reads as a tray, not a banner;
|
||
|
|
* - the brand-yellow pill CTA lives inside the card so the action
|
||
|
|
* is unmistakable but the surrounding chrome stays calm;
|
||
|
|
* - the bar only appears after the user has scrolled roughly one
|
||
|
|
* viewport (~hero out of view), so it doesn't compete with the
|
||
|
|
* in-page hero CTA.
|
||
|
|
*
|
||
|
|
* Hidden on the contact / booking flows (no point reminding someone
|
||
|
|
* to book while they're already on the form).
|
||
|
|
*/
|
||
|
|
|
||
|
|
$: pathname = $page.url.pathname;
|
||
|
|
$: hidden = pathname === '/contact-us' || pathname === '/booking';
|
||
|
|
|
||
|
|
const SHOW_AFTER_PX = 480; // close to one mobile viewport
|
||
|
|
const HIDE_BELOW_PX = 120; // generous so the bar doesn't flicker near the top
|
||
|
|
|
||
|
|
let visible = false;
|
||
|
|
|
||
|
|
function evaluateVisibility() {
|
||
|
|
const y = window.scrollY;
|
||
|
|
if (y > SHOW_AFTER_PX) {
|
||
|
|
visible = true;
|
||
|
|
} else if (y < HIDE_BELOW_PX) {
|
||
|
|
visible = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
afterNavigate(() => {
|
||
|
|
visible = false;
|
||
|
|
});
|
||
|
|
|
||
|
|
onMount(() => {
|
||
|
|
evaluateVisibility();
|
||
|
|
window.addEventListener('scroll', evaluateVisibility, { passive: true });
|
||
|
|
window.addEventListener('resize', evaluateVisibility, { passive: true });
|
||
|
|
|
||
|
|
return () => {
|
||
|
|
window.removeEventListener('scroll', evaluateVisibility);
|
||
|
|
window.removeEventListener('resize', evaluateVisibility);
|
||
|
|
};
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
|
||
|
|
{#if !hidden}
|
||
|
|
<div
|
||
|
|
class="mobile-book-bar"
|
||
|
|
class:mobile-book-bar-visible={visible}
|
||
|
|
aria-hidden={!visible}
|
||
|
|
>
|
||
|
|
<a class="mobile-book-bar-cta" href="/contact-us" tabindex={visible ? 0 : -1}>
|
||
|
|
<Icon name="fas fa-paw" />
|
||
|
|
<span>Book a free Meet & Greet</span>
|
||
|
|
<Icon name="fas fa-arrow-right" className="mobile-book-bar-arrow" />
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
{/if}
|
||
|
|
|
||
|
|
<style>
|
||
|
|
.mobile-book-bar {
|
||
|
|
display: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (max-width: 768px) {
|
||
|
|
.mobile-book-bar {
|
||
|
|
position: fixed;
|
||
|
|
bottom: 0;
|
||
|
|
left: 0;
|
||
|
|
right: 0;
|
||
|
|
z-index: 50;
|
||
|
|
display: flex;
|
||
|
|
justify-content: center;
|
||
|
|
padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
|
||
|
|
background: rgba(255, 255, 255, 0.96);
|
||
|
|
backdrop-filter: blur(10px);
|
||
|
|
border-top: 1px solid rgba(17, 20, 24, 0.08);
|
||
|
|
box-shadow: 0 -10px 28px rgba(17, 20, 24, 0.1);
|
||
|
|
|
||
|
|
opacity: 0;
|
||
|
|
transform: translateY(110%);
|
||
|
|
pointer-events: none;
|
||
|
|
transition:
|
||
|
|
opacity 0.22s ease,
|
||
|
|
transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
.mobile-book-bar-visible {
|
||
|
|
opacity: 1;
|
||
|
|
transform: translateY(0);
|
||
|
|
pointer-events: auto;
|
||
|
|
}
|
||
|
|
|
||
|
|
.mobile-book-bar-cta {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
gap: 10px;
|
||
|
|
width: 100%;
|
||
|
|
max-width: 460px;
|
||
|
|
padding: 13px 22px;
|
||
|
|
border-radius: 999px;
|
||
|
|
background: var(--yellow);
|
||
|
|
color: #000;
|
||
|
|
font-family: var(--font-head);
|
||
|
|
font-size: 15px;
|
||
|
|
font-weight: 700;
|
||
|
|
letter-spacing: 0.01em;
|
||
|
|
text-decoration: none;
|
||
|
|
box-shadow: 0 8px 18px rgba(255, 209, 0, 0.4);
|
||
|
|
transition:
|
||
|
|
background 0.18s ease,
|
||
|
|
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
.mobile-book-bar-cta:active {
|
||
|
|
transform: translateY(1px) scale(0.995);
|
||
|
|
background: #e6bb00;
|
||
|
|
}
|
||
|
|
|
||
|
|
:global(.mobile-book-bar-cta .icon) {
|
||
|
|
font-size: 13px;
|
||
|
|
}
|
||
|
|
|
||
|
|
:global(.mobile-book-bar-cta .mobile-book-bar-arrow) {
|
||
|
|
font-size: 12px;
|
||
|
|
opacity: 0.75;
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (prefers-reduced-motion: reduce) {
|
||
|
|
.mobile-book-bar {
|
||
|
|
transition: opacity 0.22s ease;
|
||
|
|
transform: none;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|