diff --git a/apps/homepage/public/rentaldrivego.jpeg b/apps/homepage/public/rentaldrivego.jpeg
deleted file mode 100644
index 4547894..0000000
Binary files a/apps/homepage/public/rentaldrivego.jpeg and /dev/null differ
diff --git a/apps/homepage/src/app/[locale]/layout.tsx b/apps/homepage/src/app/[locale]/layout.tsx
index 20648d8..710a2a0 100644
--- a/apps/homepage/src/app/[locale]/layout.tsx
+++ b/apps/homepage/src/app/[locale]/layout.tsx
@@ -60,12 +60,13 @@ export default async function LocaleLayout({ children, params }: LocaleLayoutPro
dir={getDirection(locale)}
data-theme-preference={preference}
data-theme={resolvedTheme}
+ data-scroll-behavior="smooth"
suppressHydrationWarning
>
diff --git a/apps/homepage/src/app/icon.svg b/apps/homepage/src/app/icon.svg
new file mode 100644
index 0000000..d2dee78
--- /dev/null
+++ b/apps/homepage/src/app/icon.svg
@@ -0,0 +1,14 @@
+
diff --git a/apps/homepage/src/components/app-shell/AuthHeader.tsx b/apps/homepage/src/components/app-shell/AuthHeader.tsx
index 1b0c15a..57535ee 100644
--- a/apps/homepage/src/components/app-shell/AuthHeader.tsx
+++ b/apps/homepage/src/components/app-shell/AuthHeader.tsx
@@ -1,75 +1,18 @@
'use client';
-import { localizedPath, locales, type Locale } from '@/lib/localization/config';
-import { persistLocale } from '@/lib/localization/client';
-import { persistTheme, themePreferenceEvent } from '@/lib/theme/client';
+import { localizedPath, type Locale } from '@/lib/localization/config';
import { type ThemePreference } from '@/lib/theme/config';
import Image from 'next/image';
-import { usePathname, useRouter } from 'next/navigation';
-import { useEffect, useRef, useState } from 'react';
+import { LocalePill } from './LocalePill';
+import { ThemePill } from './ThemePill';
import styles from './AuthHeader.module.css';
-const langLabels: Record = { en: 'EN', fr: 'FR', ar: 'AR' };
-const langFlags: Record = { en: '🇺🇸', fr: '🇫🇷', ar: '🇲🇦' };
-
-const themeDict: Record> = {
- en: { theme: 'Theme', light: 'Light', dark: 'Dark' },
- fr: { theme: 'Mode', light: 'Clair', dark: 'Sombre' },
- ar: { theme: 'الوضع', light: 'فاتح', dark: 'داكن' },
-};
-
interface Props {
locale: Locale;
themePreference: ThemePreference;
}
-export function AuthHeader({ locale, themePreference: initialPref }: Props) {
- const pathname = usePathname();
- const router = useRouter();
- const [pref, setPref] = useState(initialPref);
-
- const t = themeDict[locale] ?? themeDict.en;
- const themeLabel = pref === 'dark' ? t.dark : t.light;
-
- const localeMenuRef = useRef(null);
- const [localeOpen, setLocaleOpen] = useState(false);
-
- useEffect(() => {
- setPref(initialPref);
- }, [initialPref]);
-
- useEffect(() => {
- const handler = () => {
- const stored = localStorage.getItem('hpc.theme.preference');
- if (stored === 'light' || stored === 'dark' || stored === 'system') setPref(stored);
- };
- window.addEventListener(themePreferenceEvent, handler);
- return () => window.removeEventListener(themePreferenceEvent, handler);
- }, []);
-
- useEffect(() => {
- function handleClick(e: MouseEvent) {
- if (!localeMenuRef.current?.contains(e.target as Node)) setLocaleOpen(false);
- }
- document.addEventListener('mousedown', handleClick);
- return () => document.removeEventListener('mousedown', handleClick);
- }, []);
-
- const cycleTheme = () => {
- const next = pref === 'dark' ? 'light' : 'dark';
- persistTheme(next);
- setPref(next);
- };
-
- const switchLocale = (next: Locale) => {
- setLocaleOpen(false);
- persistLocale(next);
- const segments = pathname.split('/').filter(Boolean);
- if (segments.length >= 2) segments[1] = next;
- router.push('/' + segments.join('/'));
- };
-
- const otherLocales = locales.filter((l) => l !== locale);
+export function AuthHeader({ locale, themePreference }: Props) {
const homePath = localizedPath('home', locale);
return (
@@ -92,54 +35,11 @@ export function AuthHeader({ locale, themePreference: initialPref }: Props) {
-
-
- {localeOpen && (
-
- {otherLocales.map((l) => (
-
- ))}
-
- )}
-
+
-
+
diff --git a/apps/homepage/src/components/app-shell/LocalePill.module.css b/apps/homepage/src/components/app-shell/LocalePill.module.css
new file mode 100644
index 0000000..01a80a6
--- /dev/null
+++ b/apps/homepage/src/components/app-shell/LocalePill.module.css
@@ -0,0 +1,108 @@
+.wrap {
+ position: relative;
+}
+
+.button {
+ display: inline-flex;
+ min-width: 4.75rem;
+ align-items: center;
+ justify-content: center;
+ gap: 0.375rem;
+ border-radius: 999px;
+ padding: 0.375rem 0.625rem;
+ font-size: 11px;
+ font-weight: 600;
+ color: #44403c;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ transition: background 0.15s;
+}
+
+.button:hover {
+ background: #f5f5f4;
+}
+
+:global(html[data-theme='dark']) .button {
+ color: #e7e5e4;
+}
+
+:global(html[data-theme='dark']) .button:hover {
+ background: rgb(30 58 138 / 0.4);
+}
+
+@media (min-width: 640px) {
+ .button {
+ min-width: 5.25rem;
+ padding: 0.5rem 0.75rem;
+ font-size: 0.75rem;
+ }
+}
+
+.chevron {
+ width: 0.875rem;
+ height: 0.875rem;
+ transition: transform 0.15s;
+}
+
+.chevronOpen {
+ transform: rotate(180deg);
+}
+
+.menu {
+ position: absolute;
+ top: 100%;
+ z-index: 30;
+ margin-top: 0.5rem;
+ width: 12rem;
+ max-width: calc(100vw - 2rem);
+ overflow: hidden;
+ border-radius: 1rem;
+ border: 1px solid #e7e5e4;
+ background: rgb(255 255 255 / 0.95);
+ text-align: left;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.12);
+ backdrop-filter: blur(16px);
+ left: 0;
+}
+
+@media (min-width: 640px) {
+ .menu {
+ left: auto;
+ right: 0;
+ }
+}
+
+:global(html[data-theme='dark']) .menu {
+ border-color: #1e3a8a;
+ background: rgb(23 37 84 / 0.95);
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
+}
+
+.option {
+ display: flex;
+ width: 100%;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.75rem 1rem;
+ font-size: 0.875rem;
+ color: #44403c;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ transition: background 0.15s, color 0.15s;
+}
+
+.option:hover {
+ background: #f5f5f4;
+ color: #1e3a8a;
+}
+
+:global(html[data-theme='dark']) .option {
+ color: #e7e5e4;
+}
+
+:global(html[data-theme='dark']) .option:hover {
+ background: rgb(30 58 138 / 0.4);
+ color: #fff;
+}
diff --git a/apps/homepage/src/components/app-shell/LocalePill.tsx b/apps/homepage/src/components/app-shell/LocalePill.tsx
new file mode 100644
index 0000000..8c8d313
--- /dev/null
+++ b/apps/homepage/src/components/app-shell/LocalePill.tsx
@@ -0,0 +1,87 @@
+'use client';
+
+import { persistLocale } from '@/lib/localization/client';
+import {
+ localeSwitchUrl,
+ locales,
+ routeIdFromPathname,
+ type Locale,
+} from '@/lib/localization/config';
+import { usePathname, useRouter } from 'next/navigation';
+import { useEffect, useRef, useState } from 'react';
+import styles from './LocalePill.module.css';
+
+const langLabels: Record = { en: 'EN', fr: 'FR', ar: 'AR' };
+const langFlags: Record = { en: '🇺🇸', fr: '🇫🇷', ar: '🇲🇦' };
+
+interface Props {
+ locale: Locale;
+}
+
+export function LocalePill({ locale }: Props) {
+ const pathname = usePathname();
+ const router = useRouter();
+ const menuRef = useRef(null);
+ const [open, setOpen] = useState(false);
+
+ useEffect(() => {
+ function handleClick(e: MouseEvent) {
+ if (!menuRef.current?.contains(e.target as Node)) setOpen(false);
+ }
+ document.addEventListener('mousedown', handleClick);
+ return () => document.removeEventListener('mousedown', handleClick);
+ }, []);
+
+ const switchLocale = (next: Locale) => {
+ setOpen(false);
+ persistLocale(next);
+ const routeId = routeIdFromPathname(pathname) ?? 'home';
+ router.push(
+ localeSwitchUrl(new URL(window.location.href), next, routeId).pathname +
+ new URL(window.location.href).search +
+ new URL(window.location.href).hash,
+ );
+ };
+
+ const otherLocales = locales.filter((l) => l !== locale);
+
+ return (
+
+
+ {open && (
+
+ {otherLocales.map((l) => (
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/apps/homepage/src/components/app-shell/SiteHeader.tsx b/apps/homepage/src/components/app-shell/SiteHeader.tsx
index 3cc7b5b..b89af35 100644
--- a/apps/homepage/src/components/app-shell/SiteHeader.tsx
+++ b/apps/homepage/src/components/app-shell/SiteHeader.tsx
@@ -5,10 +5,10 @@ import { ActionLink } from '@/components/actions/ActionLink';
import { accountCreateUrl } from '@/lib/account-urls';
import { BrandLink } from './BrandLink';
import { HeaderNavigation } from './HeaderNavigation';
-import { LocaleSelector } from './LocaleSelector';
+import { LocalePill } from './LocalePill';
import { MobileNavigation } from './MobileNavigation';
import styles from './SiteHeader.module.css';
-import { ThemeSelector } from './ThemeSelector';
+import { ThemePill } from './ThemePill';
function signInUrl(locale: Locale): string {
return localizedPath('sign-in', locale);
@@ -39,19 +39,8 @@ export function SiteHeader({ locale, messages, themePreference }: SiteHeaderProp
role="group"
aria-label={messages.header.controlsLabel}
>
-
-
+
+
{messages.header.login}
diff --git a/apps/homepage/src/components/app-shell/ThemePill.module.css b/apps/homepage/src/components/app-shell/ThemePill.module.css
new file mode 100644
index 0000000..3fb27d6
--- /dev/null
+++ b/apps/homepage/src/components/app-shell/ThemePill.module.css
@@ -0,0 +1,115 @@
+.wrap {
+ position: relative;
+}
+
+.button {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.375rem;
+ border-radius: 999px;
+ padding: 0.25rem 0.5rem;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ transition: background 0.15s;
+}
+
+.button:hover {
+ background: #f5f5f4;
+}
+
+:global(html[data-theme='dark']) .button:hover {
+ background: rgb(30 58 138 / 0.4);
+}
+
+@media (min-width: 640px) {
+ .button {
+ padding: 0.375rem 0.625rem;
+ }
+}
+
+.label {
+ border-radius: 999px;
+ background: #1e3a8a;
+ padding: 0.25rem 0.75rem;
+ font-size: 11px;
+ font-weight: 600;
+ color: #fff;
+}
+
+:global(html[data-theme='dark']) .label {
+ background: #fb923c;
+}
+
+@media (min-width: 640px) {
+ .label {
+ padding: 0.375rem 0.875rem;
+ font-size: 0.75rem;
+ }
+}
+
+.chevron {
+ width: 0.875rem;
+ height: 0.875rem;
+ color: #78716c;
+ transition: transform 0.15s;
+}
+
+:global(html[data-theme='dark']) .chevron {
+ color: #a8a29e;
+}
+
+.chevronOpen {
+ transform: rotate(180deg);
+}
+
+.menu {
+ position: absolute;
+ top: 100%;
+ z-index: 30;
+ right: 0;
+ margin-top: 0.5rem;
+ width: 11rem;
+ max-width: calc(100vw - 2rem);
+ overflow: hidden;
+ border-radius: 1rem;
+ border: 1px solid #e7e5e4;
+ background: rgb(255 255 255 / 0.95);
+ text-align: left;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.12);
+ backdrop-filter: blur(16px);
+}
+
+:global(html[data-theme='dark']) .menu {
+ border-color: #1e3a8a;
+ background: rgb(23 37 84 / 0.95);
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
+}
+
+.option {
+ display: flex;
+ width: 100%;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.75rem 1rem;
+ font-size: 0.875rem;
+ color: #44403c;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ transition: background 0.15s, color 0.15s;
+}
+
+.option:hover {
+ background: #f5f5f4;
+ color: #1e3a8a;
+}
+
+:global(html[data-theme='dark']) .option {
+ color: #e7e5e4;
+}
+
+:global(html[data-theme='dark']) .option:hover {
+ background: rgb(30 58 138 / 0.4);
+ color: #fff;
+}
diff --git a/apps/homepage/src/components/app-shell/ThemePill.tsx b/apps/homepage/src/components/app-shell/ThemePill.tsx
new file mode 100644
index 0000000..d283e1c
--- /dev/null
+++ b/apps/homepage/src/components/app-shell/ThemePill.tsx
@@ -0,0 +1,93 @@
+'use client';
+
+import { persistTheme, themePreferenceEvent } from '@/lib/theme/client';
+import { type ThemePreference } from '@/lib/theme/config';
+import { useEffect, useRef, useState } from 'react';
+import styles from './ThemePill.module.css';
+
+interface Props {
+ initialPreference: ThemePreference;
+}
+
+const labels: Record = {
+ light: 'Light',
+ dark: 'Dark',
+ system: 'System',
+};
+
+export function ThemePill({ initialPreference }: Props) {
+ const [pref, setPref] = useState(initialPreference);
+ const menuRef = useRef(null);
+ const [open, setOpen] = useState(false);
+
+ useEffect(() => {
+ setPref(initialPreference);
+ }, [initialPreference]);
+
+ useEffect(() => {
+ const handler = () => {
+ const stored = localStorage.getItem('hpc.theme.preference');
+ if (stored === 'light' || stored === 'dark' || stored === 'system') setPref(stored);
+ };
+ window.addEventListener(themePreferenceEvent, handler);
+ return () => window.removeEventListener(themePreferenceEvent, handler);
+ }, []);
+
+ useEffect(() => {
+ function handleClick(e: MouseEvent) {
+ if (!menuRef.current?.contains(e.target as Node)) setOpen(false);
+ }
+ document.addEventListener('mousedown', handleClick);
+ return () => document.removeEventListener('mousedown', handleClick);
+ }, []);
+
+ const selectTheme = (next: ThemePreference) => {
+ setOpen(false);
+ persistTheme(next);
+ setPref(next);
+ };
+
+ const options: ThemePreference[] = ['light', 'dark', 'system'];
+ const otherOptions = options.filter((o) => o !== pref);
+
+ const displayLabel = pref === 'dark' ? 'Dark' : 'Light';
+
+ return (
+
+
+ {open && (
+
+ {otherOptions.map((o) => (
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/apps/homepage/src/components/homepage/sections/FinalCtaSection.module.css b/apps/homepage/src/components/homepage/sections/FinalCtaSection.module.css
index c39defc..6589890 100644
--- a/apps/homepage/src/components/homepage/sections/FinalCtaSection.module.css
+++ b/apps/homepage/src/components/homepage/sections/FinalCtaSection.module.css
@@ -11,7 +11,6 @@
.brandImage {
object-fit: contain;
- background: var(--surface-page);
}
@media (max-width: 767px) {
diff --git a/apps/homepage/src/components/homepage/sections/HeroSection.module.css b/apps/homepage/src/components/homepage/sections/HeroSection.module.css
index 973515f..6f24d7a 100644
--- a/apps/homepage/src/components/homepage/sections/HeroSection.module.css
+++ b/apps/homepage/src/components/homepage/sections/HeroSection.module.css
@@ -28,7 +28,7 @@
.heroGrid {
display: grid;
- grid-template-columns: minmax(0, 1.02fr) minmax(22rem, 0.98fr);
+ grid-template-columns: minmax(0, 1fr) minmax(26rem, 1.15fr);
align-items: center;
gap: clamp(var(--space-10), 7vw, var(--space-20));
}
@@ -53,11 +53,10 @@
.previewStage {
position: relative;
min-inline-size: 0;
- padding: clamp(var(--space-3), 2.5vw, var(--space-6));
+ padding: clamp(var(--space-4), 3vw, var(--space-8));
}
-.previewStage::before,
-.previewStage::after {
+.previewStage::before {
position: absolute;
z-index: -1;
border-radius: var(--radius-md);
@@ -70,17 +69,9 @@
transform: rotate(-2deg);
}
-.previewStage::after {
- inset-block-start: 8%;
- inset-inline: 8%;
- block-size: 88%;
- border: 1px solid var(--border-strong);
- transform: rotate(2deg);
-}
-
@media (max-width: 1023px) {
.heroGrid {
- grid-template-columns: minmax(0, 1fr) minmax(18rem, 0.9fr);
+ grid-template-columns: minmax(0, 1fr) minmax(21rem, 1.05fr);
gap: var(--space-10);
}
}
@@ -103,8 +94,7 @@
padding-inline: 0;
}
- .previewStage::before,
- .previewStage::after {
+ .previewStage::before {
display: none;
}
}
@@ -116,15 +106,13 @@
}
@media (forced-colors: active) {
- .previewStage::before,
- .previewStage::after {
+ .previewStage::before {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
- .previewStage::before,
- .previewStage::after {
+ .previewStage::before {
transform: none;
}
}
diff --git a/apps/homepage/src/content/homepage-model.ts b/apps/homepage/src/content/homepage-model.ts
index 79ca8fc..2520246 100644
--- a/apps/homepage/src/content/homepage-model.ts
+++ b/apps/homepage/src/content/homepage-model.ts
@@ -441,7 +441,7 @@ export function buildHomepageContent(
image: localizedImage(
'rentaldrivego',
locale,
- 'jpeg',
+ 'png',
{ width: 1220, height: 1167 },
'RentalDriveGo',
),