214 lines
4.6 KiB
Svelte
214 lines
4.6 KiB
Svelte
<script lang="ts">
|
||
import { page } from '$app/stores';
|
||
|
||
$: status = $page.status;
|
||
$: errorMessage = $page.error?.message ?? '';
|
||
$: isNotFound = status === 404;
|
||
$: heading = isNotFound ? 'This page wandered off' : 'Something went wrong';
|
||
$: subtitle = isNotFound
|
||
? 'The page you were looking for can’t be found. It may have moved, or the link might be a little chewed up.'
|
||
: 'We hit a snag loading this page. Try heading home and we’ll get you back on the trail.';
|
||
</script>
|
||
|
||
<svelte:head>
|
||
<title>{status} · GoodWalk</title>
|
||
<meta name="robots" content="noindex" />
|
||
</svelte:head>
|
||
|
||
<main class="error-page">
|
||
<div class="error-bg" aria-hidden="true">
|
||
<span class="paw paw-1">🐾</span>
|
||
<span class="paw paw-2">🐾</span>
|
||
<span class="paw paw-3">🐾</span>
|
||
<span class="paw paw-4">🐾</span>
|
||
<span class="paw paw-5">🐾</span>
|
||
</div>
|
||
|
||
<div class="error-content">
|
||
<div class="error-status">{status}</div>
|
||
<h1 class="error-heading">{heading}</h1>
|
||
<p class="error-subtitle">{subtitle}</p>
|
||
{#if errorMessage && errorMessage !== heading}
|
||
<p class="error-detail">{errorMessage}</p>
|
||
{/if}
|
||
|
||
<div class="error-actions">
|
||
<a href="/" class="btn btn-yellow">Take me home</a>
|
||
<a href="/contact-us" class="btn btn-outline">Book a Meet & Greet</a>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<style>
|
||
.error-page {
|
||
position: relative;
|
||
min-height: 100dvh;
|
||
display: grid;
|
||
place-items: center;
|
||
background:
|
||
radial-gradient(circle at 20% 15%, #2a3e2a 0%, transparent 55%),
|
||
radial-gradient(circle at 80% 85%, #1a261a 0%, transparent 55%),
|
||
var(--gw-green);
|
||
color: #fff;
|
||
overflow: hidden;
|
||
padding: 48px 24px;
|
||
}
|
||
|
||
.error-bg {
|
||
position: absolute;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.paw {
|
||
position: absolute;
|
||
user-select: none;
|
||
color: var(--yellow);
|
||
opacity: 0.07;
|
||
line-height: 1;
|
||
}
|
||
|
||
.paw-1 {
|
||
top: 6%;
|
||
left: 5%;
|
||
font-size: 110px;
|
||
transform: rotate(-18deg);
|
||
}
|
||
.paw-2 {
|
||
top: 14%;
|
||
right: 8%;
|
||
font-size: 140px;
|
||
transform: rotate(22deg);
|
||
}
|
||
.paw-3 {
|
||
bottom: 10%;
|
||
left: 9%;
|
||
font-size: 160px;
|
||
transform: rotate(35deg);
|
||
}
|
||
.paw-4 {
|
||
bottom: 6%;
|
||
right: 6%;
|
||
font-size: 120px;
|
||
transform: rotate(-12deg);
|
||
}
|
||
.paw-5 {
|
||
top: 48%;
|
||
left: 50%;
|
||
font-size: 80px;
|
||
transform: translate(-50%, -50%) rotate(8deg);
|
||
opacity: 0.04;
|
||
}
|
||
|
||
.error-content {
|
||
position: relative;
|
||
text-align: center;
|
||
max-width: 640px;
|
||
z-index: 1;
|
||
animation: rise 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||
}
|
||
|
||
.error-status {
|
||
font-family: var(--font-head);
|
||
font-size: clamp(120px, 22vw, 220px);
|
||
font-weight: 700;
|
||
color: var(--yellow);
|
||
line-height: 0.9;
|
||
letter-spacing: -0.04em;
|
||
margin-bottom: 16px;
|
||
text-shadow: 0 6px 32px rgba(0, 0, 0, 0.25);
|
||
}
|
||
|
||
.error-heading {
|
||
font-family: var(--font-head);
|
||
font-size: clamp(28px, 4vw, 40px);
|
||
font-weight: 600;
|
||
line-height: 1.2;
|
||
margin: 0 0 16px;
|
||
color: #fff;
|
||
}
|
||
|
||
.error-subtitle {
|
||
font-size: clamp(15px, 1.6vw, 17px);
|
||
line-height: 1.65;
|
||
color: rgba(255, 255, 255, 0.78);
|
||
margin: 0 auto 16px;
|
||
max-width: 520px;
|
||
}
|
||
|
||
.error-detail {
|
||
display: inline-block;
|
||
font-size: 12px;
|
||
letter-spacing: 0.04em;
|
||
color: rgba(255, 255, 255, 0.55);
|
||
margin: 0 0 32px;
|
||
padding: 6px 14px;
|
||
background: rgba(255, 255, 255, 0.06);
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
border-radius: 100px;
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||
}
|
||
|
||
.error-content :not(.error-detail) + .error-actions {
|
||
margin-top: 36px;
|
||
}
|
||
|
||
.error-actions {
|
||
display: flex;
|
||
gap: 14px;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
@keyframes rise {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(12px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.error-content {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 560px) {
|
||
.error-page {
|
||
padding: 32px 20px;
|
||
}
|
||
.paw-1 {
|
||
font-size: 70px;
|
||
}
|
||
.paw-2 {
|
||
font-size: 86px;
|
||
}
|
||
.paw-3 {
|
||
font-size: 96px;
|
||
}
|
||
.paw-4 {
|
||
font-size: 76px;
|
||
}
|
||
.paw-5 {
|
||
display: none;
|
||
}
|
||
.error-actions {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
width: 100%;
|
||
max-width: 320px;
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
}
|
||
.error-actions :global(.btn) {
|
||
width: 100%;
|
||
}
|
||
}
|
||
</style>
|