feat: enhance landing page with new hero section, badges, and improved styles

This commit is contained in:
estifanos
2026-08-14 07:43:14 +00:00
parent de04ed64b3
commit cfbec948fb
3 changed files with 129 additions and 31 deletions

View File

@@ -251,51 +251,132 @@ function Header({
);
}
const HERO_BADGE_ICONS = {
secure: IconShieldCheck,
bilingual: IconLanguage,
tracking: IconActivity,
} as const;
function Hero({ primaryHref, isAuthed }: { primaryHref: string; isAuthed: boolean }) {
const { t } = useTranslation();
const theme = useMantineTheme();
const badgeKeys = Object.keys(HERO_BADGE_ICONS) as (keyof typeof HERO_BADGE_ICONS)[];
return (
<Box
id="home"
component="section"
pos="relative"
style={{
background: theme.other?.heroGradient as string,
color: 'white',
overflow: 'hidden',
}}
>
<Container size="xl" py={80}>
<Stack gap="md" align="center" ta="center" className="ema-landing-fade">
{/* Decorative glow circles — same device as AuthShell's hero panel. */}
<Box
pos="absolute"
top={-120}
right={-100}
w={420}
h={420}
style={{ borderRadius: '50%', background: 'rgba(255,255,255,0.08)' }}
/>
<Box
pos="absolute"
bottom={-160}
left={-120}
w={380}
h={380}
style={{ borderRadius: '50%', background: 'rgba(255,255,255,0.06)' }}
/>
<Box
pos="absolute"
top="30%"
left="8%"
w={140}
h={140}
visibleFrom="md"
style={{ borderRadius: '50%', background: 'rgba(255,255,255,0.05)' }}
/>
<Container size="xl" py={{ base: 88, sm: 120, lg: 148 }} pos="relative">
<Stack gap="lg" align="center" ta="center" className="ema-landing-fade">
<Text fz="sm" fw={600} style={{ opacity: 0.85, letterSpacing: 0.5 }}>
{t('landing.hero.eyebrow')}
</Text>
<Title order={1} fz={{ base: 32, sm: 44 }} maw={780}>
<Title order={1} fz={{ base: 34, sm: 48, lg: 56 }} lh={1.1} maw={860}>
{t('landing.hero.title')}
</Title>
<Text fz={{ base: 'md', sm: 'lg' }} maw={640} style={{ opacity: 0.92 }}>
<Text fz={{ base: 'md', sm: 'xl' }} fw={500} maw={680} style={{ opacity: 0.95 }}>
{t('landing.hero.subtitle')}
</Text>
<Group gap="md" mt="md" justify="center">
<Button
component={Link}
to={isAuthed ? primaryHref : '/signup'}
size="md"
variant="white"
color="dark"
leftSection={<IconShip size={18} />}
>
{t('landing.cta.seafarerPortal')}
</Button>
<Button
component={Link}
to={isAuthed ? primaryHref : '/signup'}
size="md"
variant="outline"
color="white"
leftSection={<IconAnchor size={18} />}
>
{t('landing.cta.vesselOwnerPortal')}
</Button>
<Text fz={{ base: 'sm', sm: 'md' }} maw={600} style={{ opacity: 0.82 }}>
{t('landing.hero.description')}
</Text>
<Group gap="md" mt="sm" justify="center">
{isAuthed ? (
<Button
component={Link}
to={primaryHref}
size="lg"
variant="white"
color="dark"
style={{ boxShadow: '0 8px 24px rgba(0,0,0,0.18)' }}
>
{t('landing.auth.dashboard')}
</Button>
) : (
<>
<Button
component={Link}
to="/signup"
size="lg"
variant="white"
color="dark"
leftSection={<IconShip size={20} />}
style={{ boxShadow: '0 8px 24px rgba(0,0,0,0.18)' }}
>
{t('landing.cta.iAmSeafarer')}
</Button>
<Button
component={Link}
to="/signup"
size="lg"
variant="outline"
color="white"
leftSection={<IconAnchor size={20} />}
>
{t('landing.cta.iAmVesselOwner')}
</Button>
</>
)}
</Group>
<Group gap="xs" mt="xl" justify="center" wrap="wrap">
{badgeKeys.map((key) => {
const Icon = HERO_BADGE_ICONS[key];
return (
<Group
key={key}
gap={6}
wrap="nowrap"
px="sm"
py={6}
style={{
borderRadius: 999,
background: 'rgba(255,255,255,0.12)',
border: '1px solid rgba(255,255,255,0.18)',
}}
>
<Icon size={14} />
<Text fz="xs" fw={600}>
{t(`landing.hero.badges.${key}`)}
</Text>
</Group>
);
})}
</Group>
</Stack>
</Container>

View File

@@ -31,6 +31,13 @@ export const landingEn = {
eyebrow: 'Federal Democratic Republic of Ethiopia',
title: 'Ethiopian Maritime Authority',
subtitle: 'Digital Maritime Services for Seafarers, Vessel Owners and Logistics Operators',
description:
'Apply, upload documents and track every application from one secure account — built for seafarers, vessel owners and logistics operators across Ethiopia.',
badges: {
secure: 'Secure & Verified',
bilingual: 'English & አማርኛ',
tracking: 'Real-Time Tracking',
},
},
about: {
@@ -44,8 +51,8 @@ export const landingEn = {
},
cta: {
seafarerPortal: 'Seafarer Portal',
vesselOwnerPortal: 'Vessel Owner Portal',
iAmSeafarer: "I'm a Seafarer",
iAmVesselOwner: "I'm a Vessel Owner",
},
quickAccess: {
@@ -212,6 +219,13 @@ export const landingAm: LandingCopy = {
eyebrow: 'የኢትዮጵያ ፌዴራላዊ ዲሞክራሲያዊ ሪፐብሊክ',
title: 'የኢትዮጵያ ማሪታይም ባለስልጣን',
subtitle: 'ለመርከበኞች፣ ለመርከብ ባለቤቶች እና ለሎጂስቲክስ ኦፕሬተሮች የተዘጋጁ ዲጂታል የባህር አገልግሎቶች',
description:
'ከመለያዎ ማመልከቻ ያስገቡ፣ ሰነድ ይስቀሉ እንዲሁም ሁኔታ ይከታተሉ — ለመርከበኞች፣ ለመርከብ ባለቤቶችና ለሎጂስቲክስ ኦፕሬተሮች የተዘጋጀ ነው።',
badges: {
secure: 'ደህንነቱ የተጠበቀ',
bilingual: 'አማርኛና እንግሊዝኛ',
tracking: 'የቀጥታ ክትትል',
},
},
about: {
@@ -225,8 +239,8 @@ export const landingAm: LandingCopy = {
},
cta: {
seafarerPortal: 'መርከበኞች ፖርታል',
vesselOwnerPortal: 'የመርከብ ባለቤቶች ፖርታል',
iAmSeafarer: 'መርከበኛ ነኝ',
iAmVesselOwner: 'የመርከብ ባለቤት ነኝ',
},
quickAccess: {

View File

@@ -3,7 +3,10 @@
.ema-hover-lift, portal's --ema-surface-*) is available here, so this file
is self-contained. */
.ema-landing {
/* `scroll-behavior` only affects the element that actually scrolls — for a
full page that's `html`, not this div, so it has to live here. Scoped with
:has() so it only applies while the landing page is mounted. */
html:has(.ema-landing) {
scroll-behavior: smooth;
}
@@ -45,7 +48,7 @@
}
@media (prefers-reduced-motion: reduce) {
.ema-landing {
html:has(.ema-landing) {
scroll-behavior: auto;
}
.ema-landing .ema-landing-fade,