bilhej/frontend/src/pages/HomePage.vue
Joakim Mörling 7a95c1423c
Some checks failed
CI / Lint, type check, unit tests, coverage (pull_request) Successful in 2m22s
CI / E2E browser tests (pull_request) Failing after 1m3s
Make customer-facing UI usable on smartphones.
Mobile traffic was breaking on narrow viewports because the header nav
overflowed and several pages used desktop-only spacing. This adds a
shared phone breakpoint, a hamburger menu, and scroll-to-top on route
changes so footer and menu navigation always land at the top of the page.

- Add --page-gutter and max-width 639px rules in base.css
- AppHeader: hamburger panel on small screens; flat account links on mobile
- AppFooter: stack footer links vertically on phones
- Home, compose, edit order, orders, auth, and legal pages: tighter gutters
  and responsive layout (orders card actions stack; home grids single-column)
- Router scrollBehavior: scroll to top on navigation; restore on browser back
- Tests: AppHeader menu toggle, Router scrollBehavior, mobile Playwright checks

Admin page is intentionally unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 13:03:35 +02:00

1182 lines
31 KiB
Vue

<script setup lang="ts">
import { ref } from 'vue'
import { RouterLink } from 'vue-router'
import PlateInput from '@/components/PlateInput.vue'
import VehicleInfo from '@/components/VehicleInfo.vue'
import type { VehicleInfo as VehicleData } from '@/components/VehicleInfo.vue'
import { lookupVehicle } from '@/api/vehicles'
const useCases = [
{
variant: 'buy',
title: 'Vill köpa bilen',
description:
'Lämna ett vänligt bud eller dina kontaktuppgifter om du är intresserad av att köpa bilen, utan att stå kvar på parkeringen och vänta.',
templateName: 'Köpförfrågan',
},
{
variant: 'praise',
title: 'Vill säga något fint',
description:
'Beröm någon som uppenbarligen tar hand om sin bil. Ett oväntat brev som ofta uppskattas mer än man tror.',
templateName: 'Komplimang',
},
{
variant: 'tip',
title: 'Vill tipsa om något',
description:
'Säg till om trasig baklykta, slitna däck eller annat du märkt som ägaren kanske inte sett själv.',
templateName: 'Tips / servicebehov',
},
{
variant: 'driving',
title: 'Reagerade på körbeteende',
description:
'Om något i trafiken upprörde dig kan du nå föraren med ett sakligt och respektfullt meddelande, utan att eskalera på plats.',
templateName: 'Körbeteende',
},
{
variant: 'frustration',
title: 'Efter tuta eller irritation',
description:
'När det hettade till i trafiken kan ett lugnt brev vara bättre än att tuta eller gestikulera. Mallen hjälper dig att formulera det neutralt.',
templateName: 'Tuta / frustration',
},
{
variant: 'damage',
title: 'Orsakat en parkeringsskada',
description:
'Om du råkat skada bilen när du parkerade kan du be om ursäkt och lämna dina uppgifter så ni kan lösa det tillsammans.',
templateName: 'Mindre parkeringsskada',
},
] as const
const steps = [
{
number: 1,
variant: 'lookup',
title: 'Verifiera registreringsnumret',
description:
'Ange nummerplåten och kontrollera att märke och modell stämmer. Vi använder uppgifterna för att hitta rätt mottagare.',
},
{
number: 2,
variant: 'compose',
title: 'Skriv, granska och betala',
description:
'Välj en mall eller skriv fritt, justera texten och betala 49 kr via Swish. Du ser hela brevet innan det skickas.',
},
{
number: 3,
variant: 'send',
title: 'Vi postar brevet',
description:
'Efter betalning tar vi hand om utskick manuellt. Brevet postas vanligtvis inom några vardagar. I Mina beställningar ser du status och får spårningsnummer när det skickats.',
},
] as const
const trustPoints = [
{
variant: 'privacy',
title: 'Anonymt om du vill',
description:
'Du når bilägaren utan att automatiskt lämna ut namn, telefon eller adress. Vill du att de ska kunna svara lägger du till det i brevet.',
},
{
variant: 'convenience',
title: 'Registreringsnummer räcker',
description:
'Du behöver inte veta vem som äger bilen eller var personen bor. Skriv brevet här så sköter vi utskicket.',
},
{
variant: 'letter',
title: 'Fysiskt brev med spårning',
description:
'Meddelandet skickas som riktigt brev med posten, inte som SMS eller mejl. När brevet skickats kan du följa det med spårningsnummer.',
},
] as const
const plate = ref('')
const vehicle = ref<VehicleData | null>(null)
const notFound = ref(false)
const lookingUp = ref(false)
async function handleLookup(lookedUpPlate: string) {
lookingUp.value = true
notFound.value = false
vehicle.value = null
try {
const result = await lookupVehicle(lookedUpPlate)
vehicle.value = result
} catch (err: unknown) {
if (err instanceof Error && err.message.includes('Inget fordon')) {
notFound.value = true
} else {
vehicle.value = null
notFound.value = true
}
} finally {
lookingUp.value = false
}
}
</script>
<template>
<div class="home">
<section class="home__hero">
<div class="home__hero-glow" aria-hidden="true" />
<div class="home__hero-content">
<p class="home__eyebrow">Post till registreringsnummer</p>
<h1 class="home__headline">
Skicka ett brev<br />
<span class="home__headline-accent">till bilägaren</span>
</h1>
<p class="home__lead">
Skriv ditt meddelande och bilägaren via registreringsnumret, utan
att du behöver leta upp adress eller kontaktuppgifter själv.
</p>
<ul class="home__highlights">
<li>
<span class="home__highlight-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none">
<path
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
Brev skickas med posten och kan spåras
</li>
<li>
<span class="home__highlight-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none">
<path
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
Anonymt åt båda håll. Bara det ni skriver i brevet delas
</li>
<li>
<span class="home__highlight-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none">
<path
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
Enkelt att skriva och betala här. Vi sköter utskick och postgång
</li>
</ul>
</div>
<div class="home__card">
<div class="home__card-head">
<p class="home__card-label">Kom igång</p>
<span class="home__price">49 kr</span>
</div>
<p class="home__card-hint">
Kontrollera att registreringsnumret stämmer. Vi slår upp bilen och
förbereder utskick till ägaren.
</p>
<PlateInput v-model="plate" @lookup="handleLookup" />
<VehicleInfo
:vehicle="vehicle"
:loading="lookingUp"
:not-found="notFound"
:plate="plate"
/>
<RouterLink
v-if="vehicle"
:to="{ name: 'compose', query: { plate } }"
class="btn btn--primary btn--lg home__cta"
>
Fortsätt till brevet
</RouterLink>
</div>
</section>
<section class="home__uses">
<div class="home__uses-inner">
<p class="home__section-eyebrow">Varför folk skriver</p>
<h2 class="home__section-title">Vanliga anledningar</h2>
<p class="home__section-lead">
När du skriver brevet kan du välja en färdig mall och anpassa texten.
Här är vanliga situationer, kopplade till mallarna i tjänsten.
</p>
<div class="home__uses-grid">
<article
v-for="item in useCases"
:key="item.templateName"
class="home__use"
:class="`home__use--${item.variant}`"
>
<div
class="home__use-icon"
:class="`home__use-icon--${item.variant}`"
aria-hidden="true"
>
<svg
v-if="item.variant === 'buy'"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M7 17h10M7 17a2 2 0 01-2-2V9a2 2 0 012-2h10a2 2 0 012 2v6a2 2 0 01-2 2M7 17l-1 3m11-3l1 3M9 9h.01M15 9h.01"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<svg
v-else-if="item.variant === 'praise'"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<svg
v-else-if="item.variant === 'tip'"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M9 18h6M10 22h4M12 2a7 7 0 017 7c0 2.38-1.19 4.47-3 5.74V17H8v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 017-7z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<svg
v-else-if="item.variant === 'driving'"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
/>
</svg>
<svg
v-else-if="item.variant === 'frustration'"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M8 9h8M8 13h6M12 3a9 9 0 100 18 9 9 0 000-18z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<svg v-else viewBox="0 0 24 24" fill="none">
<path
d="M12 9v2m0 4h.01M5.07 19h13.86c1.54 0 2.5-1.67 1.73-3L13.73 4c-.77-1.33-2.69-1.33-3.46 0L3.34 16c-.77 1.33.19 3 1.73 3z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
<h3>{{ item.title }}</h3>
<p>{{ item.description }}</p>
<p class="home__use-template">
<span class="home__use-template-label">Mall</span>
{{ item.templateName }}
</p>
</article>
<article class="home__use home__use--custom home__use--wide">
<div
class="home__use-icon home__use-icon--custom"
aria-hidden="true"
>
<svg viewBox="0 0 24 24" fill="none">
<path
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
<div class="home__use-wide-content">
<h3>Något helt annat?</h3>
<p>
Passar ingen mall? Välj
<strong>Fritt meddelande</strong> och skriv precis det du vill,
till exempel om parkering, grannskap eller något vi inte har en
färdig mall för. Du kan också utgå från valfri mall och ändra
texten fritt.
</p>
<p class="home__use-template">
<span class="home__use-template-label">Mall</span>
Fritt meddelande
</p>
</div>
</article>
</div>
</div>
</section>
<section class="home__steps">
<div class="home__steps-inner">
<p class="home__section-eyebrow">Tre steg</p>
<h2 class="home__section-title"> fungerar det</h2>
<p class="home__section-lead">
Du skriver och betalar här några minuter. Därefter tar vi hand om
utskick och post, och håller dig uppdaterad under tiden.
</p>
<ol class="home__steps-list">
<li
v-for="step in steps"
:key="step.number"
class="home__step"
:class="`home__step--${step.variant}`"
>
<div class="home__step-head">
<div
class="home__step-icon"
:class="`home__step-icon--${step.variant}`"
aria-hidden="true"
>
<svg
v-if="step.variant === 'lookup'"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-4.172 4.172a2 2 0 01-2.828 0l-7-7A2 2 0 013 8v5c0 .512.195 1.024.586 1.414M7 7l3 3"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<svg
v-else-if="step.variant === 'compose'"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<svg v-else viewBox="0 0 24 24" fill="none">
<path
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
<span class="home__step-number">Steg {{ step.number }}</span>
</div>
<h3>{{ step.title }}</h3>
<p>{{ step.description }}</p>
</li>
</ol>
</div>
</section>
<section class="home__trust">
<div class="home__trust-inner">
<p class="home__section-eyebrow home__trust-eyebrow">Integritet</p>
<h2 class="home__trust-title">Trygg hantering</h2>
<p class="home__section-lead home__trust-lead">
Bilhej är byggt för svenska registreringsnummer och svensk post. Här
är vad tjänsten faktiskt gör för dig.
</p>
<div class="home__trust-grid">
<article
v-for="point in trustPoints"
:key="point.title"
class="home__trust-item"
:class="`home__trust-item--${point.variant}`"
>
<div
class="home__trust-icon"
:class="`home__trust-icon--${point.variant}`"
aria-hidden="true"
>
<svg
v-if="point.variant === 'privacy'"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<svg
v-else-if="point.variant === 'convenience'"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-4.172 4.172a2 2 0 01-2.828 0l-7-7A2 2 0 013 8v5c0 .512.195 1.024.586 1.414M7 7l3 3"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<svg v-else viewBox="0 0 24 24" fill="none">
<path
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
<h3>{{ point.title }}</h3>
<p>{{ point.description }}</p>
</article>
</div>
</div>
</section>
</div>
</template>
<style scoped>
.home__hero {
position: relative;
display: grid;
grid-template-columns: 1.05fr 0.95fr;
gap: var(--space-3xl);
align-items: center;
max-width: 72rem;
margin: var(--space-lg) auto 0;
padding: var(--space-3xl) var(--space-xl);
background: linear-gradient(
145deg,
var(--color-surface) 0%,
#f8faff 45%,
var(--color-paper) 100%
);
border: 1px solid var(--color-border);
border-radius: calc(var(--radius-xl) + 0.25rem);
box-shadow: var(--shadow-lg);
overflow: hidden;
}
.home__hero-glow {
position: absolute;
top: -30%;
right: -5%;
width: 22rem;
height: 22rem;
background: radial-gradient(
circle,
rgba(29, 78, 216, 0.12) 0%,
transparent 70%
);
pointer-events: none;
}
.home__hero-content {
position: relative;
}
.home__eyebrow {
display: inline-block;
font-size: 0.8125rem;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--color-primary-dark);
background: var(--color-primary-soft);
border: 1px solid #bfdbfe;
padding: 0.35rem 0.75rem;
border-radius: var(--radius-full);
margin: 0 0 var(--space-lg) 0;
}
.home__headline {
font-size: clamp(2.125rem, 5vw, 3.25rem);
font-weight: 800;
line-height: 1.08;
letter-spacing: -0.02em;
color: var(--color-ink);
margin: 0 0 var(--space-lg) 0;
}
.home__headline-accent {
color: var(--color-primary);
}
.home__lead {
font-size: 1.125rem;
color: var(--color-muted);
line-height: 1.75;
margin: 0 0 var(--space-xl) 0;
max-width: 32rem;
}
.home__highlights {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
.home__highlights li {
display: flex;
align-items: center;
gap: var(--space-sm);
font-size: 0.9375rem;
color: var(--color-ink);
}
.home__highlight-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.75rem;
height: 1.75rem;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-full);
color: var(--color-primary);
flex-shrink: 0;
}
.home__highlight-icon svg {
width: 0.9375rem;
height: 0.9375rem;
}
.home__card {
position: relative;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-xl);
padding: var(--space-xl);
box-shadow: var(--shadow-xl);
}
.home__card-head {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-md);
margin-bottom: var(--space-sm);
}
.home__card-label {
margin: 0;
font-size: 0.8125rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-muted);
}
.home__price {
display: inline-flex;
align-items: center;
padding: 0.25rem 0.75rem;
font-size: 0.875rem;
font-weight: 700;
color: var(--color-primary-dark);
background: var(--color-primary-soft);
border: 1px solid #bfdbfe;
border-radius: var(--radius-full);
}
.home__card-hint {
margin: 0 0 var(--space-lg) 0;
font-size: 0.9375rem;
color: var(--color-muted);
line-height: 1.5;
}
.home__cta {
display: flex;
margin-top: var(--space-lg);
}
.home__uses {
padding: var(--space-3xl) var(--space-lg);
}
.home__uses-inner,
.home__steps-inner {
max-width: 72rem;
margin: 0 auto;
}
.home__section-eyebrow {
margin: 0 0 var(--space-sm) 0;
text-align: center;
font-size: 0.8125rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--color-primary);
}
.home__section-title {
font-size: clamp(1.5rem, 3vw, 1.875rem);
font-weight: 800;
letter-spacing: -0.02em;
text-align: center;
margin: 0 0 var(--space-md) 0;
color: var(--color-ink);
}
.home__section-lead {
max-width: 36rem;
margin: 0 auto var(--space-2xl);
text-align: center;
font-size: 1rem;
color: var(--color-muted);
line-height: 1.7;
}
.home__uses-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--space-lg);
}
.home__use {
position: relative;
display: flex;
flex-direction: column;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-xl);
padding: var(--space-xl);
box-shadow: var(--shadow-card);
overflow: hidden;
transition:
transform var(--transition-base),
box-shadow var(--transition-base),
border-color var(--transition-base);
}
.home__use::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: var(--use-accent);
}
.home__use:hover {
transform: translateY(-3px);
box-shadow: var(--shadow-lg);
border-color: var(--use-border);
}
.home__use--buy {
--use-accent: linear-gradient(90deg, #1d4ed8, #60a5fa);
--use-border: #93c5fd;
}
.home__use--praise {
--use-accent: linear-gradient(90deg, #0f766e, #2dd4bf);
--use-border: #5eead4;
}
.home__use--tip {
--use-accent: linear-gradient(90deg, #b45309, #fbbf24);
--use-border: #fcd34d;
}
.home__use--driving {
--use-accent: linear-gradient(90deg, #4338ca, #818cf8);
--use-border: #a5b4fc;
}
.home__use--frustration {
--use-accent: linear-gradient(90deg, #c2410c, #fb923c);
--use-border: #fdba74;
}
.home__use--damage {
--use-accent: linear-gradient(90deg, #be123c, #fb7185);
--use-border: #fda4af;
}
.home__use--custom {
--use-accent: linear-gradient(90deg, #475569, #94a3b8);
--use-border: #cbd5e1;
}
.home__use--wide {
grid-column: 1 / -1;
flex-direction: row;
align-items: flex-start;
gap: var(--space-lg);
}
.home__use-wide-content {
flex: 1;
min-width: 0;
}
.home__use-icon {
display: flex;
align-items: center;
justify-content: center;
width: 3.5rem;
height: 3.5rem;
border-radius: 1rem;
margin-bottom: var(--space-md);
flex-shrink: 0;
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.12);
}
.home__use--wide .home__use-icon {
margin-bottom: 0;
}
.home__use-icon svg {
width: 1.625rem;
height: 1.625rem;
}
.home__use-icon--buy {
background: linear-gradient(135deg, #dbeafe 0%, #eff6ff 100%);
color: #1d4ed8;
border: 1px solid #93c5fd;
}
.home__use-icon--praise {
background: linear-gradient(135deg, #ccfbf1 0%, #ecfeff 100%);
color: #0f766e;
border: 1px solid #5eead4;
}
.home__use-icon--tip {
background: linear-gradient(135deg, #fef3c7 0%, #fffbeb 100%);
color: #b45309;
border: 1px solid #fcd34d;
}
.home__use-icon--driving {
background: linear-gradient(135deg, #e0e7ff 0%, #eef2ff 100%);
color: #4338ca;
border: 1px solid #a5b4fc;
}
.home__use-icon--frustration {
background: linear-gradient(135deg, #ffedd5 0%, #fff7ed 100%);
color: #c2410c;
border: 1px solid #fdba74;
}
.home__use-icon--damage {
background: linear-gradient(135deg, #ffe4e6 0%, #fff1f2 100%);
color: #be123c;
border: 1px solid #fda4af;
}
.home__use-icon--custom {
background: linear-gradient(135deg, #e2e8f0 0%, #f8fafc 100%);
color: #475569;
border: 1px solid #cbd5e1;
}
.home__use h3 {
margin: 0 0 var(--space-sm) 0;
font-size: 1.0625rem;
color: var(--color-ink);
}
.home__use p {
margin: 0;
font-size: 0.9375rem;
color: var(--color-muted);
line-height: 1.65;
}
.home__use-template {
display: inline-flex;
align-items: center;
gap: var(--space-sm);
margin-top: auto;
padding-top: var(--space-md);
font-size: 0.8125rem;
font-weight: 600;
color: var(--color-ink);
}
.home__use-template-label {
display: inline-flex;
padding: 0.15rem 0.5rem;
font-size: 0.6875rem;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--color-primary-dark);
background: var(--color-primary-soft);
border: 1px solid #bfdbfe;
border-radius: var(--radius-full);
}
@media (min-width: 1024px) {
.home__uses-grid {
grid-template-columns: repeat(3, 1fr);
}
}
.home__steps {
padding: var(--space-3xl) var(--space-lg);
background: linear-gradient(
180deg,
transparent 0%,
rgba(219, 234, 254, 0.22) 50%,
transparent 100%
);
}
.home__steps-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-lg);
}
.home__step {
position: relative;
display: flex;
flex-direction: column;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-xl);
padding: var(--space-xl);
text-align: left;
box-shadow: var(--shadow-card);
overflow: hidden;
transition:
transform var(--transition-base),
box-shadow var(--transition-base),
border-color var(--transition-base);
}
.home__step::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: var(--step-accent);
}
.home__step:hover {
transform: translateY(-3px);
box-shadow: var(--shadow-lg);
border-color: var(--step-border);
}
.home__step--lookup {
--step-accent: linear-gradient(90deg, #1d4ed8, #60a5fa);
--step-border: #93c5fd;
}
.home__step--compose {
--step-accent: linear-gradient(90deg, #4338ca, #818cf8);
--step-border: #a5b4fc;
}
.home__step--send {
--step-accent: linear-gradient(90deg, #0f766e, #2dd4bf);
--step-border: #5eead4;
}
.home__step-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-md);
margin-bottom: var(--space-md);
}
.home__step-icon {
display: flex;
align-items: center;
justify-content: center;
width: 3.25rem;
height: 3.25rem;
border-radius: 1rem;
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.12);
}
.home__step-icon svg {
width: 1.5rem;
height: 1.5rem;
}
.home__step-icon--lookup {
background: linear-gradient(135deg, #dbeafe 0%, #eff6ff 100%);
color: #1d4ed8;
border: 1px solid #93c5fd;
}
.home__step-icon--compose {
background: linear-gradient(135deg, #e0e7ff 0%, #eef2ff 100%);
color: #4338ca;
border: 1px solid #a5b4fc;
}
.home__step-icon--send {
background: linear-gradient(135deg, #ccfbf1 0%, #ecfeff 100%);
color: #0f766e;
border: 1px solid #5eead4;
}
.home__step-number {
display: inline-flex;
padding: 0.25rem 0.75rem;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--color-primary-dark);
background: var(--color-primary-soft);
border: 1px solid #bfdbfe;
border-radius: var(--radius-full);
}
.home__step h3 {
margin: 0 0 var(--space-sm) 0;
font-size: 1.0625rem;
color: var(--color-ink);
}
.home__step p {
margin: 0;
font-size: 0.9375rem;
color: var(--color-muted);
line-height: 1.65;
}
.home__trust {
padding: 0 var(--space-lg) var(--space-3xl);
}
.home__trust-inner {
max-width: 72rem;
margin: 0 auto;
padding: var(--space-2xl);
background: linear-gradient(
135deg,
var(--color-primary-soft) 0%,
#eef2ff 100%
);
border: 1px solid #bfdbfe;
border-radius: var(--radius-xl);
}
.home__trust-eyebrow {
color: var(--color-primary-dark);
}
.home__trust-title {
margin: 0 0 var(--space-md) 0;
text-align: center;
font-size: clamp(1.5rem, 3vw, 1.875rem);
font-weight: 800;
letter-spacing: -0.02em;
color: var(--color-primary-dark);
}
.home__trust-lead {
margin-bottom: var(--space-2xl);
color: var(--color-primary-dark);
opacity: 0.85;
}
.home__trust-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-lg);
}
.home__trust-item {
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-xl);
padding: var(--space-xl);
box-shadow: var(--shadow-card);
overflow: hidden;
transition:
transform var(--transition-base),
box-shadow var(--transition-base),
border-color var(--transition-base);
}
.home__trust-item::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: var(--trust-accent);
}
.home__trust-item:hover {
transform: translateY(-3px);
box-shadow: var(--shadow-lg);
border-color: var(--trust-border);
}
.home__trust-item--privacy {
--trust-accent: linear-gradient(90deg, #4338ca, #818cf8);
--trust-border: #a5b4fc;
}
.home__trust-item--convenience {
--trust-accent: linear-gradient(90deg, #1d4ed8, #60a5fa);
--trust-border: #93c5fd;
}
.home__trust-item--letter {
--trust-accent: linear-gradient(90deg, #0f766e, #2dd4bf);
--trust-border: #5eead4;
}
.home__trust-icon {
display: flex;
align-items: center;
justify-content: center;
width: 3.25rem;
height: 3.25rem;
border-radius: 1rem;
margin-bottom: var(--space-md);
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.12);
}
.home__trust-icon svg {
width: 1.5rem;
height: 1.5rem;
}
.home__trust-icon--privacy {
background: linear-gradient(135deg, #e0e7ff 0%, #eef2ff 100%);
color: #4338ca;
border: 1px solid #a5b4fc;
}
.home__trust-icon--convenience {
background: linear-gradient(135deg, #dbeafe 0%, #eff6ff 100%);
color: #1d4ed8;
border: 1px solid #93c5fd;
}
.home__trust-icon--letter {
background: linear-gradient(135deg, #ccfbf1 0%, #ecfeff 100%);
color: #0f766e;
border: 1px solid #5eead4;
}
.home__trust-item h3 {
margin: 0 0 var(--space-sm) 0;
font-size: 1.0625rem;
color: var(--color-ink);
}
.home__trust-item p {
margin: 0;
font-size: 0.9375rem;
color: var(--color-muted);
line-height: 1.65;
}
@media (max-width: 639px) {
.home__hero {
grid-template-columns: 1fr;
gap: var(--space-xl);
padding: var(--space-xl) var(--page-gutter);
margin-top: 0;
border-radius: 0;
border-left: none;
border-right: none;
}
.home__headline {
font-size: clamp(1.875rem, 6vw, 2.75rem);
}
.home__uses-grid,
.home__steps-list,
.home__trust-grid {
grid-template-columns: 1fr;
}
.home__use--wide {
flex-direction: column;
}
.home__use--wide .home__use-icon {
margin-bottom: var(--space-md);
}
.home__uses,
.home__steps,
.home__trust {
padding-inline: var(--page-gutter);
}
.home__trust-inner {
padding: var(--space-lg);
}
}
</style>