Design language
This commit is contained in:
@@ -0,0 +1,293 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/components/Icon.svelte';
|
||||
import { getEnhancedImage } from '$lib/enhanced-images';
|
||||
import type { CallToAction, HeroChip } from '$lib/types';
|
||||
|
||||
export let eyebrow: string;
|
||||
export let title: string;
|
||||
export let subtitle: string | undefined = undefined;
|
||||
export let imageUrl: string;
|
||||
export let imageAlt: string;
|
||||
export let chips: HeroChip[] = [];
|
||||
export let cta: CallToAction | undefined = undefined;
|
||||
|
||||
const reviewHref = 'https://g.page/r/CUsvrWPhkYrAEB0/';
|
||||
|
||||
$: enhanced = getEnhancedImage(imageUrl);
|
||||
</script>
|
||||
|
||||
<section class="sh">
|
||||
|
||||
<!-- Left: brand green copy column -->
|
||||
<div class="sh-copy">
|
||||
<p class="sh-eyebrow">{eyebrow}</p>
|
||||
<h1 class="sh-title">{title}</h1>
|
||||
{#if subtitle}
|
||||
<p class="sh-subtitle">{subtitle}</p>
|
||||
{/if}
|
||||
|
||||
{#if chips.length}
|
||||
<div class="sh-chips">
|
||||
{#each chips as chip}
|
||||
<span class="sh-chip">
|
||||
<Icon name={chip.icon} />
|
||||
{chip.label}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="sh-actions">
|
||||
{#if cta}
|
||||
<a href={cta.href} class="btn btn-yellow sh-cta">{cta.label}</a>
|
||||
{/if}
|
||||
<a
|
||||
href={reviewHref}
|
||||
class="sh-trust"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
aria-label="Read our Google reviews"
|
||||
>
|
||||
<span class="sh-stars" aria-hidden="true">★★★★★</span>
|
||||
30+ five-star Google reviews
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: full-height photo, no card, no shadow, bleeds to viewport edge -->
|
||||
<div class="sh-media">
|
||||
{#if enhanced}
|
||||
<enhanced:img
|
||||
src={enhanced}
|
||||
alt={imageAlt}
|
||||
loading="eager"
|
||||
fetchpriority="high"
|
||||
decoding="async"
|
||||
/>
|
||||
{:else}
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt={imageAlt}
|
||||
loading="eager"
|
||||
fetchpriority="high"
|
||||
decoding="async"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<style>
|
||||
/* ── Full-bleed split — green bleeds left, photo bleeds right, content stays centred ── */
|
||||
.sh {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── Copy column ──
|
||||
Left padding uses --sh-copy-left-pad so ultrawide overrides can live
|
||||
entirely in responsive.css without touching this component. ── */
|
||||
.sh-copy {
|
||||
background: var(--gw-green);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding: 80px 56px 80px var(--sh-copy-left-pad, max(40px, calc(50vw - 596px)));
|
||||
}
|
||||
|
||||
/* Subtle yellow warmth on the copy side */
|
||||
.sh-copy::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(ellipse 90% 80% at 5% 65%, rgba(255, 209, 0, 0.09) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Service name in Goodwalk Yellow */
|
||||
.sh-eyebrow {
|
||||
margin: 0 0 16px;
|
||||
font-family: var(--font-head);
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--yellow);
|
||||
}
|
||||
|
||||
.sh-title {
|
||||
margin: 0 0 14px;
|
||||
font-family: var(--font-head);
|
||||
font-size: clamp(30px, 3.2vw, 50px);
|
||||
font-weight: 800;
|
||||
line-height: 1.04;
|
||||
letter-spacing: -0.04em;
|
||||
color: #fff;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.sh-subtitle {
|
||||
margin: 0 0 26px;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
color: rgba(255, 255, 255, 0.68);
|
||||
max-width: 38ch;
|
||||
}
|
||||
|
||||
/* ── Chips ── */
|
||||
.sh-chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.sh-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 7px 14px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.09);
|
||||
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||
color: #fff;
|
||||
font-family: var(--font-head);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
/* ── CTA row ── */
|
||||
.sh-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.sh-cta {
|
||||
font-size: 15px;
|
||||
padding: 12px 24px;
|
||||
}
|
||||
|
||||
.sh-trust {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: rgba(255, 255, 255, 0.62);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
line-height: 1.3;
|
||||
transition: color 0.18s ease;
|
||||
}
|
||||
|
||||
.sh-trust:hover {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.sh-stars {
|
||||
color: var(--yellow);
|
||||
letter-spacing: 2px;
|
||||
font-size: 12px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
/* ── Photo column — fills full height, bleeds to right viewport edge ── */
|
||||
.sh-media {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
/* Minimum height so the section never collapses on short copy */
|
||||
min-height: 480px;
|
||||
}
|
||||
|
||||
.sh-media::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0 auto 0 0;
|
||||
width: clamp(28px, 4.5vw, 76px);
|
||||
background:
|
||||
radial-gradient(circle at left center, rgba(255, 209, 0, 0.1) 0%, rgba(255, 209, 0, 0.04) 26%, transparent 62%),
|
||||
linear-gradient(90deg, rgba(33, 48, 33, 0.76) 0%, rgba(33, 48, 33, 0.34) 46%, rgba(33, 48, 33, 0.08) 78%, transparent 100%);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.sh-media :global(picture) {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sh-media :global(img) {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center 25%;
|
||||
transition: transform 0.8s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
.sh-media:hover :global(img) {
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
/* ── Tablet — formula already floors at 40px here, just reduce vertical padding ── */
|
||||
@media (max-width: 1024px) {
|
||||
.sh-copy {
|
||||
padding-top: 64px;
|
||||
padding-bottom: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Mobile — stack vertically, photo above copy ── */
|
||||
@media (max-width: 768px) {
|
||||
.sh {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
/* Photo goes first on mobile — visual hook before the pitch */
|
||||
.sh-media {
|
||||
order: 1;
|
||||
min-height: 0;
|
||||
aspect-ratio: 3 / 2;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sh-media::before {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.sh-copy {
|
||||
order: 2;
|
||||
padding: 44px 24px 48px;
|
||||
}
|
||||
|
||||
.sh-title {
|
||||
font-size: clamp(28px, 7.5vw, 38px);
|
||||
}
|
||||
|
||||
.sh-subtitle {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.sh-actions {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.sh-cta {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user