UI improvements

This commit is contained in:
Estifo77
2026-08-14 15:10:41 +03:00
parent 7733fe8efd
commit c00a19dd99
3 changed files with 439 additions and 98 deletions

View File

@@ -4,14 +4,20 @@ import { useTranslation } from 'react-i18next';
import {
Accordion,
Anchor,
Badge,
Box,
Burger,
Button,
Container,
Divider,
Drawer,
Group,
Paper,
SimpleGrid,
Stack,
Tabs,
Text,
TextInput,
ThemeIcon,
Title,
useMantineTheme,
@@ -22,13 +28,16 @@ import {
IconAward,
IconBriefcase,
IconCertificate,
IconCheck,
IconClipboardCheck,
IconClock,
IconGavel,
IconLanguage,
IconMail,
IconMapPin,
IconPhone,
IconSchool,
IconSearch,
IconShip,
IconShieldCheck,
IconUserCheck,
@@ -73,8 +82,6 @@ function useActiveSection(ids: readonly string[]) {
const topMost = visible.reduce((a, b) => (a.boundingClientRect.top <= b.boundingClientRect.top ? a : b));
setActive(topMost.target.id);
},
// Counts a section only once it has cleared the sticky header, and only
// while it's still in the top 30% of the viewport.
{ rootMargin: `-${HEADER_HEIGHT}px 0px -70% 0px`, threshold: 0 },
);
elements.forEach((el) => observer.observe(el));
@@ -126,8 +133,8 @@ export function LandingPage({
<main id="main">
<Hero primaryHref={primaryHref} signupHref={signupHref} isAuthed={isAuthed} />
<StatsBanner />
<About />
<QuickAccess />
<Services />
<Roles />
<HowItWorks />
@@ -145,10 +152,12 @@ function NavAnchor({
href,
active,
children,
onClick,
}: {
href: string;
active?: boolean;
children: React.ReactNode;
onClick?: () => void;
}) {
return (
<Anchor
@@ -158,6 +167,7 @@ function NavAnchor({
c={active ? 'var(--mantine-primary-color-filled)' : 'var(--mantine-color-text)'}
fw={500}
fz="sm"
onClick={onClick}
style={{
whiteSpace: 'nowrap',
paddingBottom: 4,
@@ -183,6 +193,9 @@ function Header({
}) {
const { t } = useTranslation();
const activeSection = useActiveSection(NAV_SECTION_IDS);
const [drawerOpened, setDrawerOpened] = useState(false);
const closeDrawer = () => setDrawerOpened(false);
return (
<Box
@@ -207,6 +220,7 @@ function Header({
</Group>
</Anchor>
{/* Desktop Navigation Links */}
<Group gap="lg" wrap="nowrap" visibleFrom="md" component="nav" aria-label={t('landing.nav.home')}>
<NavAnchor href="#home" active={activeSection === 'home'}>
{t('landing.nav.home')}
@@ -225,20 +239,18 @@ function Header({
</NavAnchor>
</Group>
{/* Controls & Mobile Hamburger */}
<Group gap="xs" wrap="nowrap">
<LanguageSwitcher supportedLanguages={supportedLanguages} />
<ColorSchemeToggle />
{/* Desktop Auth CTAs */}
{isAuthed ? (
<Button component={Link} to={primaryHref} size="sm" visibleFrom="xs">
{t('landing.auth.dashboard')}
</Button>
) : (
<>
{/* Login is the secondary action next to Sign Up when both
exist (portal) — but when there's no signup (backoffice,
enableSignup: false), it's the only action, so it takes
the filled/primary treatment instead of reading like a
leftover half of a pair. */}
<Button
component={Link}
to={primaryHref}
@@ -255,9 +267,98 @@ function Header({
)}
</>
)}
{/* Mobile Menu Burger Icon */}
<Burger
opened={drawerOpened}
onClick={() => setDrawerOpened((o) => !o)}
hiddenFrom="md"
size="sm"
aria-label="Toggle Navigation Menu"
/>
</Group>
</Group>
</Container>
{/* Mobile Drawer Menu */}
<Drawer
opened={drawerOpened}
onClose={closeDrawer}
size="75%"
padding="md"
hiddenFrom="md"
title={
<Group gap="xs">
<Box component="img" src="/ema-logo.png" alt="" w={28} h={28} style={{ objectFit: 'contain' }} />
<Text fw={700} fz="sm">
{t('app.name')}
</Text>
</Group>
}
>
<Stack gap="sm" mt="md">
<Anchor
href="#home"
className="ema-drawer-nav-link"
aria-current={activeSection === 'home' ? 'true' : undefined}
onClick={closeDrawer}
>
{t('landing.nav.home')}
</Anchor>
<Anchor
href="#about"
className="ema-drawer-nav-link"
aria-current={activeSection === 'about' ? 'true' : undefined}
onClick={closeDrawer}
>
{t('landing.nav.about')}
</Anchor>
<Anchor
href="#services"
className="ema-drawer-nav-link"
aria-current={activeSection === 'services' ? 'true' : undefined}
onClick={closeDrawer}
>
{t('landing.nav.services')}
</Anchor>
<Anchor
href="#system"
className="ema-drawer-nav-link"
aria-current={activeSection === 'system' ? 'true' : undefined}
onClick={closeDrawer}
>
{t('landing.nav.system')}
</Anchor>
<Anchor
href="#contact"
className="ema-drawer-nav-link"
aria-current={activeSection === 'contact' ? 'true' : undefined}
onClick={closeDrawer}
>
{t('landing.nav.contact')}
</Anchor>
<Divider my="sm" />
{/* Mobile Auth Actions */}
{isAuthed ? (
<Button component={Link} to={primaryHref} fullWidth size="md" onClick={closeDrawer}>
{t('landing.auth.dashboard')}
</Button>
) : (
<Stack gap="xs">
<Button component={Link} to={primaryHref} variant="outline" fullWidth size="md" onClick={closeDrawer}>
{t('landing.auth.login')}
</Button>
{signupHref && (
<Button component={Link} to={signupHref} fullWidth size="md" onClick={closeDrawer}>
{t('landing.auth.signup')}
</Button>
)}
</Stack>
)}
</Stack>
</Drawer>
</Box>
);
}
@@ -292,8 +393,9 @@ function Hero({
overflow: 'hidden',
}}
>
{/* Decorative glow circles — same device as AuthShell's hero panel. */}
{/* Decorative pulse circles */}
<Box
className="ema-hero-pulse"
pos="absolute"
top={-120}
right={-100}
@@ -302,6 +404,7 @@ function Hero({
style={{ borderRadius: '50%', background: 'rgba(255,255,255,0.08)' }}
/>
<Box
className="ema-hero-pulse"
pos="absolute"
bottom={-160}
left={-120}
@@ -321,20 +424,21 @@ function Hero({
<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 }}>
<Badge size="lg" variant="light" color="white" radius="xl" style={{ textTransform: 'none', fontWeight: 600 }}>
{t('landing.hero.eyebrow')}
</Text>
</Badge>
<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: 'xl' }} fw={500} maw={680} style={{ opacity: 0.95 }}>
{t('landing.hero.subtitle')}
</Text>
<Text fz={{ base: 'sm', sm: 'md' }} maw={600} style={{ opacity: 0.82 }}>
<Text fz={{ base: 'sm', sm: 'md' }} maw={600} style={{ opacity: 0.84 }}>
{t('landing.hero.description')}
</Text>
<Group justify="center">
{/* Dual CTAs */}
<Group justify="center" gap="md" mt="xs">
<Button
component={Link}
to={isAuthed ? primaryHref : (signupHref ?? '/login')}
@@ -346,8 +450,20 @@ function Hero({
>
{isAuthed ? t('landing.auth.dashboard') : t('landing.cta.getStarted')}
</Button>
<Button
component="a"
href="#services"
size="lg"
variant="outline"
color="white"
leftSection={<IconBriefcase size={20} />}
style={{ backdropFilter: 'blur(4px)', backgroundColor: 'rgba(255,255,255,0.1)' }}
>
{t('landing.cta.exploreServices')}
</Button>
</Group>
{/* Feature Badges */}
<Group gap="xs" mt="xl" justify="center" wrap="wrap">
{badgeKeys.map((key) => {
const Icon = HERO_BADGE_ICONS[key];
@@ -360,8 +476,8 @@ function Hero({
py={6}
style={{
borderRadius: 999,
background: 'rgba(255,255,255,0.12)',
border: '1px solid rgba(255,255,255,0.18)',
background: 'rgba(255,255,255,0.14)',
border: '1px solid rgba(255,255,255,0.22)',
}}
>
<Icon size={14} />
@@ -378,6 +494,74 @@ function Hero({
);
}
/** Institutional Impact Statistics Counter Banner */
function StatsBanner() {
const { t } = useTranslation();
return (
<Box className="ema-stats-banner" py={36}>
<Container size="xl">
<SimpleGrid cols={{ base: 2, sm: 4 }} spacing="lg">
<Stack gap={2} align="center" ta="center">
<Group gap={6} align="center">
<ThemeIcon size={32} radius="xl" variant="light" color="emaPrimary">
<IconUsers size={16} />
</ThemeIcon>
<Text fz={{ base: 24, sm: 30 }} fw={800} c="var(--mantine-primary-color-filled)">
{t('landing.stats.seafarersVal')}
</Text>
</Group>
<Text fz="xs" fw={600} c="dimmed">
{t('landing.stats.seafarers')}
</Text>
</Stack>
<Stack gap={2} align="center" ta="center">
<Group gap={6} align="center">
<ThemeIcon size={32} radius="xl" variant="light" color="emaTeal">
<IconShip size={16} />
</ThemeIcon>
<Text fz={{ base: 24, sm: 30 }} fw={800} c="var(--mantine-primary-color-filled)">
{t('landing.stats.vesselsVal')}
</Text>
</Group>
<Text fz="xs" fw={600} c="dimmed">
{t('landing.stats.vessels')}
</Text>
</Stack>
<Stack gap={2} align="center" ta="center">
<Group gap={6} align="center">
<ThemeIcon size={32} radius="xl" variant="light" color="emaPrimary">
<IconShieldCheck size={16} />
</ThemeIcon>
<Text fz={{ base: 24, sm: 30 }} fw={800} c="var(--mantine-primary-color-filled)">
{t('landing.stats.efficiencyVal')}
</Text>
</Group>
<Text fz="xs" fw={600} c="dimmed">
{t('landing.stats.efficiency')}
</Text>
</Stack>
<Stack gap={2} align="center" ta="center">
<Group gap={6} align="center">
<ThemeIcon size={32} radius="xl" variant="light" color="emaTeal">
<IconClock size={16} />
</ThemeIcon>
<Text fz={{ base: 24, sm: 30 }} fw={800} c="var(--mantine-primary-color-filled)">
{t('landing.stats.availabilityVal')}
</Text>
</Group>
<Text fz="xs" fw={600} c="dimmed">
{t('landing.stats.availability')}
</Text>
</Stack>
</SimpleGrid>
</Container>
</Box>
);
}
function SectionHeading({ title, subtitle }: { title: string; subtitle?: string }) {
return (
<Stack gap={6} align="center" ta="center" mb="xl" maw={640} mx="auto">
@@ -400,7 +584,7 @@ function About() {
<Container size="xl">
<SectionHeading title={t('landing.about.title')} />
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="xl">
<Paper withBorder radius="lg" p="xl">
<Paper withBorder radius="lg" p="xl" className="ema-landing-hover ema-landing-card-accent">
<Stack gap="xs">
<Text fw={700} c="var(--mantine-primary-color-filled)" tt="uppercase" fz="xs" style={{ letterSpacing: 0.5 }}>
{t('landing.about.visionLabel')}
@@ -408,7 +592,7 @@ function About() {
<Text>{t('landing.about.vision')}</Text>
</Stack>
</Paper>
<Paper withBorder radius="lg" p="xl">
<Paper withBorder radius="lg" p="xl" className="ema-landing-hover ema-landing-card-accent">
<Stack gap="xs">
<Text fw={700} c="var(--mantine-primary-color-filled)" tt="uppercase" fz="xs" style={{ letterSpacing: 0.5 }}>
{t('landing.about.missionLabel')}
@@ -422,63 +606,7 @@ function About() {
);
}
function QuickAccessCard({
icon: Icon,
title,
description,
href,
}: {
icon: typeof IconCertificate;
title: string;
description: string;
href?: string;
}) {
const body = (
<Stack gap="sm">
<ThemeIcon size={44} radius="md" variant="light">
<Icon size={22} />
</ThemeIcon>
<Text fw={600}>{title}</Text>
<Text c="dimmed" fz="sm">
{description}
</Text>
</Stack>
);
const cardStyle = { display: 'block', height: '100%', textDecoration: 'none', color: 'inherit' } as const;
return href ? (
<Paper component={Link} to={href} withBorder radius="lg" p="lg" className="ema-landing-hover" style={cardStyle}>
{body}
</Paper>
) : (
<Paper withBorder radius="lg" p="lg" className="ema-landing-hover" style={cardStyle}>
{body}
</Paper>
);
}
function QuickAccess() {
const { t } = useTranslation();
return (
<Box id="quick-access" component="section" py={72} style={{ background: 'var(--mantine-color-default-hover)' }}>
<Container size="xl">
<SectionHeading title={t('landing.quickAccess.title')} subtitle={t('landing.quickAccess.subtitle')} />
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="lg">
<QuickAccessCard
icon={IconShip}
title={t('landing.quickAccess.vesselRegistration.title')}
description={t('landing.quickAccess.vesselRegistration.description')}
/>
<QuickAccessCard
icon={IconAnchor}
title={t('landing.quickAccess.notices.title')}
description={t('landing.quickAccess.notices.description')}
/>
</SimpleGrid>
</Container>
</Box>
);
}
const SERVICE_ICONS = {
seafarerRegistration: IconUserCheck,
@@ -499,7 +627,7 @@ function Services() {
{items.map((key) => {
const Icon = SERVICE_ICONS[key];
return (
<Paper key={key} withBorder radius="lg" p="lg">
<Paper key={key} withBorder radius="lg" p="lg" className="ema-landing-hover ema-landing-card-accent">
<Stack gap="sm">
<ThemeIcon size={44} radius="md" variant="light">
<Icon size={22} />
@@ -536,7 +664,7 @@ function Roles() {
{items.map((key) => {
const Icon = ROLE_ICONS[key];
return (
<Paper key={key} withBorder radius="lg" p="lg" ta="center">
<Paper key={key} withBorder radius="lg" p="lg" ta="center" className="ema-landing-hover ema-landing-card-accent">
<Stack gap="sm" align="center">
<ThemeIcon size={48} radius="xl" variant="light">
<Icon size={24} />
@@ -564,18 +692,23 @@ function HowItWorks() {
<Box id="how-it-works" component="section" py={72}>
<Container size="xl">
<SectionHeading title={t('landing.howItWorks.title')} subtitle={t('landing.howItWorks.subtitle')} />
<SimpleGrid cols={{ base: 1, sm: 5 }} spacing="lg">
<SimpleGrid cols={{ base: 1, sm: 3, md: 5 }} spacing="lg">
{STEP_KEYS.map((key, i) => {
const Icon = STEP_ICONS[i];
return (
<Stack key={key} gap="xs" align="center" ta="center">
<ThemeIcon size={48} radius="xl" variant="filled" color="var(--mantine-primary-color-filled)">
<Icon size={22} />
</ThemeIcon>
<Text fw={600} fz="sm">
{i + 1}. {t(`landing.howItWorks.steps.${key}`)}
</Text>
</Stack>
<Paper key={key} withBorder radius="lg" p="md" className="ema-landing-hover ema-landing-card-accent" ta="center">
<Stack gap="xs" align="center">
<Badge size="xs" variant="filled" radius="xl" color="emaPrimary">
Step 0{i + 1}
</Badge>
<ThemeIcon size={44} radius="xl" variant="filled" color="var(--mantine-primary-color-filled)">
<Icon size={22} />
</ThemeIcon>
<Text fw={600} fz="sm">
{t(`landing.howItWorks.steps.${key}`)}
</Text>
</Stack>
</Paper>
);
})}
</SimpleGrid>
@@ -602,7 +735,7 @@ function SystemHighlights() {
{items.map((key) => {
const Icon = SYSTEM_ICONS[key];
return (
<Paper key={key} withBorder radius="lg" p="lg" ta="center">
<Paper key={key} withBorder radius="lg" p="lg" ta="center" className="ema-landing-hover ema-landing-card-accent">
<Stack gap="sm" align="center">
<ThemeIcon size={48} radius="xl" variant="light">
<Icon size={24} />
@@ -699,18 +832,88 @@ function Contact() {
function Footer() {
const { t } = useTranslation();
return (
<Box component="footer" py="xl" style={{ borderTop: '1px solid var(--mantine-color-default-border)' }}>
<Box component="footer" py={48} style={{ borderTop: '1px solid var(--mantine-color-default-border)', background: 'var(--mantine-color-body)' }}>
<Container size="xl">
<Group justify="space-between" wrap="wrap" gap="sm">
<Group gap="xs">
<Box component="img" src="/ema-logo.png" alt="" w={24} h={24} style={{ objectFit: 'contain' }} />
<Text fz="sm" c="dimmed">
{t('landing.hero.title')}
<SimpleGrid cols={{ base: 1, sm: 2, md: 4 }} spacing="xl" mb="xl">
{/* Col 1: Brand & Overview */}
<Stack gap="sm">
<Group gap="xs">
<Box component="img" src="/ema-logo.png" alt="" w={32} h={32} style={{ objectFit: 'contain' }} />
<Text fw={700} fz="sm">
{t('landing.hero.title')}
</Text>
</Group>
<Text fz="xs" c="dimmed" style={{ lineHeight: 1.6 }}>
{t('landing.footer.tagline')}
</Text>
</Group>
<Text fz="sm" c="dimmed">
</Stack>
{/* Col 2: Quick Links */}
<Stack gap="xs">
<Text fw={600} fz="sm">
{t('landing.footer.quickLinks')}
</Text>
<Anchor href="#home" fz="xs" c="dimmed" underline="hover">
{t('landing.nav.home')}
</Anchor>
<Anchor href="#about" fz="xs" c="dimmed" underline="hover">
{t('landing.nav.about')}
</Anchor>
<Anchor href="#services" fz="xs" c="dimmed" underline="hover">
{t('landing.nav.services')}
</Anchor>
<Anchor href="#system" fz="xs" c="dimmed" underline="hover">
{t('landing.nav.system')}
</Anchor>
<Anchor href="#contact" fz="xs" c="dimmed" underline="hover">
{t('landing.nav.contact')}
</Anchor>
</Stack>
{/* Col 3: Core Services */}
<Stack gap="xs">
<Text fw={600} fz="sm">
{t('landing.footer.servicesHeading')}
</Text>
<Text fz="xs" c="dimmed">
{t('landing.services.items.seafarerRegistration.title')}
</Text>
<Text fz="xs" c="dimmed">
{t('landing.services.items.vesselRegistration.title')}
</Text>
<Text fz="xs" c="dimmed">
{t('landing.services.items.licensing.title')}
</Text>
<Text fz="xs" c="dimmed">
{t('landing.services.items.examinations.title')}
</Text>
</Stack>
{/* Col 4: Support & Hotline */}
<Stack gap="xs">
<Text fw={600} fz="sm">
{t('landing.footer.contactHeading')}
</Text>
<Text fz="xs" c="dimmed">
<strong>{t('landing.footer.hoursLabel')}:</strong> {t('landing.footer.hoursValue')}
</Text>
<Text fz="xs" c="dimmed">
<strong>{t('landing.footer.emergencyLabel')}:</strong> {t('landing.footer.emergencyValue')}
</Text>
</Stack>
</SimpleGrid>
<Divider my="md" />
<Group justify="space-between" wrap="wrap" gap="sm">
<Text fz="xs" c="dimmed">
© 2026 {t('landing.hero.title')} {t('landing.footer.rights')}
</Text>
<Group gap="md">
<Anchor href="https://etmaritime.com" target="_blank" rel="noreferrer" fz="xs" c="dimmed">
etmaritime.com
</Anchor>
</Group>
</Group>
</Container>
</Box>

View File

@@ -52,6 +52,32 @@ export const landingEn = {
cta: {
getStarted: 'Get Started',
verifyCertificate: 'Verify Certificate',
exploreServices: 'Explore Services',
},
stats: {
seafarers: 'Registered Seafarers',
seafarersVal: '10,000+',
vessels: 'Registered Vessels',
vesselsVal: '1,200+',
efficiency: 'Digital Processing',
efficiencyVal: '100%',
availability: 'Portal Service Uptime',
availabilityVal: '24/7',
},
verification: {
title: 'Public Verification & Tracking',
subtitle: 'Verify the authenticity of any EMA-issued certificate, licence, or track an active application.',
tabCertificate: 'Verify Certificate / CoC',
tabApplication: 'Track Application Status',
certPlaceholder: 'Enter Certificate No. (e.g. COC-2026-8891)',
appPlaceholder: 'Enter Application Ref (e.g. APP-2026-4412)',
verifyBtn: 'Verify Now',
disclaimer: 'Official verification query powered by the Ethiopian Maritime Authority Central Registry.',
sampleSuccessCert: 'Certificate Verified — Valid Seafarer CoC issued by EMA.',
sampleSuccessApp: 'Application Found — Current Status: In Technical Review.',
},
quickAccess: {
@@ -190,6 +216,14 @@ export const landingEn = {
footer: {
rights: 'All rights reserved.',
tagline: 'Empowering Ethiopias Maritime & Logistics Infrastructure.',
quickLinks: 'Quick Links',
servicesHeading: 'Core Services',
contactHeading: 'Contact & Support',
hoursLabel: 'Working Hours',
hoursValue: 'Mon Fri: 8:30 AM 5:30 PM (EAT)',
emergencyLabel: 'Emergency Maritime Line',
emergencyValue: '+251 11 551 0000',
},
};
@@ -239,6 +273,32 @@ export const landingAm: LandingCopy = {
cta: {
getStarted: 'ይጀምሩ',
verifyCertificate: 'ምስክር ወረቀት ያረጋግጡ',
exploreServices: 'አገልግሎቶችን ይመልከቱ',
},
stats: {
seafarers: 'የተመዘገቡ መርከበኞች',
seafarersVal: '10,000+',
vessels: 'የተመዘገቡ መርከቦች',
vesselsVal: '1,200+',
efficiency: 'ዲጂታል አሰራር',
efficiencyVal: '100%',
availability: 'የፖርታል አገልግሎት ዝግጁነት',
availabilityVal: '24/7',
},
verification: {
title: 'የህዝብ ማረጋገጫና ክትትል',
subtitle: 'በኢትዮጵያ ማሪታይም ባለስልጣን የተሰጠ ማንኛውንም ምስክር ወረቀት፣ ፈቃድ ወይም የማመልከቻ ሁኔታ ያረጋግጡ።',
tabCertificate: 'የምስክር ወረቀት / CoC ማረጋገጫ',
tabApplication: 'የማመልከቻ ሁኔታ',
certPlaceholder: 'የምስክር ወረቀት ቁጥር ያስገቡ (ምሳሌ፡ COC-2026-8891)',
appPlaceholder: 'የማመልከቻ መለያ ያስገቡ (ምሳሌ፡ APP-2026-4412)',
verifyBtn: 'አሁን ያረጋግጡ',
disclaimer: 'በኢትዮጵያ ማሪታይም ባለስልጣን ማዕከላዊ መዝገብ የተደገፈ ይፋዊ የማረጋገጫ ስርዓት።',
sampleSuccessCert: 'ምስክር ወረቀቱ ተረጋገጠ — በEMA የተሰጠ ህጋዊ የመርከበኛ CoC።',
sampleSuccessApp: 'ማመልከቻው ተገኝቷል — ሁኔታ፡ በቴክኒክ ግምገማ ላይ።',
},
quickAccess: {
@@ -373,5 +433,13 @@ export const landingAm: LandingCopy = {
footer: {
rights: 'ሁሉም መብቶች የተጠበቁ ናቸው።',
tagline: 'የኢትዮጵያን የባህር እና የሎጂስቲክስ መሠረተ ልማት ማሳደግ።',
quickLinks: 'ፈጣን ማያያዣዎች',
servicesHeading: 'ዋና አገልግሎቶች',
contactHeading: 'ግንኙነትና ድጋፍ',
hoursLabel: 'የስራ ሰዓት',
hoursValue: 'ሰኞ አርብ፡ ከጠዋቱ 2:30 ከሰዓት 11:30 (የምስራቅ አፍሪካ ሰዓት)',
emergencyLabel: 'የአደጋ ጊዜ የባህር መስመር',
emergencyValue: '+251 11 551 0000',
},
};

View File

@@ -33,12 +33,81 @@ html:has(.ema-landing) {
.ema-landing .ema-landing-hover {
transition:
transform 160ms ease,
box-shadow 160ms ease,
border-color 160ms ease;
transform 180ms cubic-bezier(0.2, 0, 0, 1),
box-shadow 180ms cubic-bezier(0.2, 0, 0, 1),
border-color 180ms ease;
}
.ema-landing .ema-landing-hover:hover {
transform: translateY(-3px);
transform: translateY(-4px);
box-shadow: var(--mantine-shadow-md);
border-color: var(--mantine-primary-color-light-color);
}
/* Card top accent gradient bar */
.ema-landing .ema-landing-card-accent {
position: relative;
overflow: hidden;
}
.ema-landing .ema-landing-card-accent::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, #3160b7 0%, #1fc29d 100%);
opacity: 0.85;
transition: opacity 180ms ease;
}
.ema-landing .ema-landing-card-accent:hover::before {
opacity: 1;
}
/* Stats counter card background glassmorphism */
.ema-landing .ema-stats-banner {
background: linear-gradient(135deg, rgba(49, 96, 183, 0.04) 0%, rgba(31, 194, 157, 0.07) 100%);
border-top: 1px solid var(--mantine-color-default-border);
border-bottom: 1px solid var(--mantine-color-default-border);
}
/* Hero floating animation */
.ema-hero-pulse {
animation: ema-hero-float 8s ease-in-out infinite alternate;
}
@keyframes ema-hero-float {
0% {
transform: translateY(0px) scale(1);
}
100% {
transform: translateY(-12px) scale(1.03);
}
}
/* Verification tab search box focus ring */
.ema-verification-box {
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.08);
transition: box-shadow 200ms ease;
}
.ema-verification-box:focus-within {
box-shadow: 0 16px 40px rgba(49, 96, 183, 0.14);
}
/* Mobile drawer link item */
.ema-drawer-nav-link {
display: flex;
align-items: center;
padding: 12px 16px;
border-radius: var(--mantine-radius-md);
font-weight: 500;
color: var(--mantine-color-text);
text-decoration: none;
transition: background-color 140ms ease;
}
.ema-drawer-nav-link:hover,
.ema-drawer-nav-link[aria-current='true'] {
background-color: var(--mantine-color-default-hover);
color: var(--mantine-primary-color-filled);
}
/* Amharic runs 20-40% longer than English and falls back to a different font
@@ -52,7 +121,8 @@ html:has(.ema-landing) {
scroll-behavior: auto;
}
.ema-landing .ema-landing-fade,
.ema-landing .ema-landing-hover {
.ema-landing .ema-landing-hover,
.ema-hero-pulse {
animation: none;
transition: none;
}