Initial import of HarborSmith website
Some checks failed
build-website / build (push) Failing after 1m2s

This commit is contained in:
2025-09-18 22:20:01 +02:00
commit ec72c5d62b
168 changed files with 65020 additions and 0 deletions

View File

@@ -0,0 +1,148 @@
<template>
<section class="hero-voyage" id="heroSection">
<div ref="videoContainer" class="hero-video-container">
<video
ref="videoElement"
autoplay
loop
muted
playsinline
class="hero-video"
@loadeddata="handleVideoLoaded"
>
<source
src="https://videos.pexels.com/video-files/3571264/3571264-uhd_2560_1440_30fps.mp4"
type="video/mp4"
>
</video>
<div
v-if="!videoLoaded"
class="hero-image-fallback"
:style="{ backgroundImage: 'url(/golden_gate.jpg)' }"
/>
<div class="hero-overlay gradient-warm" />
<div class="hero-overlay gradient-depth" />
</div>
<div class="hero-content">
<div class="hero-logo animate-fade-in">
<img src="/HARBOR-SMITH-white.png" alt="Harbor Smith" style="height: 250px; margin: 40px 0;">
</div>
<div class="trust-badge animate-fade-in">
<div class="stars">
<span class="stars-icons">
<LucideStar class="star-filled" />
<LucideStar class="star-filled" />
<LucideStar class="star-filled" />
<LucideStar class="star-filled" />
<LucideStar class="star-filled" />
</span>
</div>
<span>Trusted by 0+ seafarers</span>
</div>
<p class="hero-subtext animate-fade-up-delay">
<span style="font-size: 1.5rem; font-weight: 500; text-transform: none; letter-spacing: normal; margin-bottom: 10px; display: block;">
Personalized Service Maintenance for Your Boat
</span>
Keep your vessel pristine with San Francisco Bay's premier mobile boat maintenance service.
</p>
<div class="hero-actions animate-fade-up-delay-2">
<button class="btn-primary-warm" @click="handlePhoneClick">
<LucidePhone class="btn-icon" />
Call (510) 701-2535
</button>
<button class="btn-secondary-warm" @click="handleServicesClick">
<LucideWrench class="btn-icon" />
View Our Services
</button>
</div>
<div class="scroll-indicator">
<span>Scroll to explore</span>
<div class="scroll-arrow">
<LucideChevronDown />
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useParallax } from '~/composables/useParallax'
import { useIntersectionAnimations } from '~/composables/useIntersectionAnimations'
import { useRipple } from '~/composables/useRipple'
const videoLoaded = ref(false)
const videoContainer = ref<HTMLElement | null>(null)
const videoElement = ref<HTMLVideoElement | null>(null)
useParallax(videoContainer, 0.5)
useIntersectionAnimations()
useRipple()
const handleVideoLoaded = () => {
videoLoaded.value = true
}
const handlePhoneClick = () => {
window.location.href = 'tel:510-701-2535'
}
const handleServicesClick = () => {
const element = document.querySelector('#services')
if (element) {
element.scrollIntoView({ behavior: 'smooth' })
}
}
const handleVideoVisibility = () => {
if (!videoElement.value) {
return
}
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
videoElement.value?.play()
} else {
videoElement.value?.pause()
}
})
}, { threshold: 0.25 })
observer.observe(videoElement.value)
return () => observer.disconnect()
}
const initSmoothScroll = () => {
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener('click', (event) => {
event.preventDefault()
const href = anchor.getAttribute('href')
if (!href) {
return
}
const target = document.querySelector(href)
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
})
})
}
onMounted(() => {
handleVideoVisibility()
initSmoothScroll()
})
</script>
<style scoped>
/* Additional animations defined in voyage-layout.css */
</style>