bilhej/frontend/src/pages/HomePage.vue
Hermes Agent 48c2a50c5d
Some checks failed
CI / Lint, type check, unit tests, coverage (push) Successful in 2m43s
CI / E2E browser tests (push) Failing after 1m17s
fix(guest): route homepage CTA to guest checkout when unauthenticated
Guest checkout was unreachable: the homepage CTA "Fortsatt till brevet"
always linked to /compose (requiresAuth), so anonymous users hit the
router guard and got redirected to /logga-in instead of reaching the
guest checkout flow.

Changes:
- HomePage.vue: compute CTA target based on auth state. Authenticated
  users still go to /compose; unauthenticated users go to
  /gast-bestallning with the plate as a query param.
- GuestCheckoutPage.vue: accept plate from route.query.plate so the user
  does not have to re-enter it after looking it up on the homepage.
- HomePage.spec.ts: add Pinia setup + guest-checkout route; update CTA
  assertion to expect /gast-bestallning?plate=... when unauthenticated.

All 318 frontend tests pass, vue-tsc clean (exit 0).
2026-06-22 12:08:43 +00:00

1195 lines
31 KiB
Vue

<script setup lang="ts">
import { ref, computed } 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'
import { useAuthStore } from '@/stores/authStore'
const auth = useAuthStore()
// When unauthenticated, the homepage CTA sends the user to the guest
// checkout page instead of the auth-protected /compose route.
// Without this, anonymous users hit the router guard and are bounced
// to /logga-in — the guest flow we built was unreachable from the UI.
const ctaTarget = computed(() =>
auth.isAuthenticated
? { name: 'compose' as const, query: { plate: plate.value } }
: { name: 'guest-checkout' as const, query: { plate: plate.value } },
)
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="ctaTarget"
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>