diff --git a/app/src/components/Footer.tsx b/app/src/components/Footer.tsx
index ee0976d79..b2ad953bc 100644
--- a/app/src/components/Footer.tsx
+++ b/app/src/components/Footer.tsx
@@ -10,7 +10,7 @@ import {
import { Anchor, Box, Container, Group, SimpleGrid, Stack, Text } from '@mantine/core';
import type { CountryId } from '@/api/report';
import FooterSubscribe from '@/components/FooterSubscribe';
-import { colors, spacing, typography } from '@/designTokens';
+import { spacing, typography } from '@/designTokens';
import { useCurrentCountry } from '@/hooks/useCurrentCountry';
const PolicyEngineLogo = '/assets/logos/policyengine/white.svg';
@@ -20,8 +20,6 @@ const getContactLinks = (countryId: CountryId) => ({
donate: `/${countryId}/donate`,
privacy: `/${countryId}/privacy`,
terms: `/${countryId}/terms`,
- // TODO: Add developer-tools page once it's built out
- // developerTools: `/${countryId}/developer-tools`,
});
const SOCIAL_LINKS = [
@@ -37,13 +35,34 @@ const SOCIAL_LINKS = [
export default function Footer() {
const countryId = useCurrentCountry();
const CONTACT_LINKS = getContactLinks(countryId);
+
return (
-
+ {/* Decorative gradient orb */}
+
+
+
{
+ e.currentTarget.style.color = '#4FD1C5';
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.color = 'rgba(255, 255, 255, 0.8)';
+ }}
>
About us
{
+ e.currentTarget.style.color = '#4FD1C5';
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.color = 'rgba(255, 255, 255, 0.8)';
+ }}
>
Donate
{
+ e.currentTarget.style.color = '#4FD1C5';
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.color = 'rgba(255, 255, 255, 0.8)';
+ }}
>
Privacy policy
{
+ e.currentTarget.style.color = '#4FD1C5';
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.color = 'rgba(255, 255, 255, 0.8)';
+ }}
>
Terms and conditions
- {/* TODO: Uncomment when developer-tools page is built
-
- Developer tools
-
- */}
{SOCIAL_LINKS.map(({ icon: Icon, href }, index) => (
-
-
+ {
+ e.currentTarget.style.color = '#4FD1C5';
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.color = 'rgba(255, 255, 255, 0.6)';
+ }}
+ >
+
))}
-
+
© {new Date().getFullYear()} PolicyEngine. All rights reserved.
diff --git a/app/src/components/FooterSubscribe.tsx b/app/src/components/FooterSubscribe.tsx
index ac8a8615f..ac83dc276 100644
--- a/app/src/components/FooterSubscribe.tsx
+++ b/app/src/components/FooterSubscribe.tsx
@@ -40,10 +40,19 @@ export default function FooterSubscribe() {
return (
-
+
Subscribe to PolicyEngine
-
+
Get the latest posts delivered right to your inbox.
@@ -54,17 +63,41 @@ export default function FooterSubscribe() {
value={email}
onChange={(event) => setEmail(event.currentTarget.value)}
styles={{
- input: { backgroundColor: colors.white, flex: 1 },
+ input: {
+ backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ border: '1px solid rgba(79, 209, 197, 0.3)',
+ color: '#ffffff',
+ '&::placeholder': {
+ color: 'rgba(255, 255, 255, 0.5)',
+ },
+ '&:focus': {
+ borderColor: '#4FD1C5',
+ },
+ },
}}
disabled={status === 'loading'}
/>
diff --git a/app/src/components/Sidebar.tsx b/app/src/components/Sidebar.tsx
index df3914b4c..8e27fed0d 100644
--- a/app/src/components/Sidebar.tsx
+++ b/app/src/components/Sidebar.tsx
@@ -13,7 +13,7 @@ import {
import { useLocation, useNavigate } from 'react-router-dom';
import { Box, Button, Stack } from '@mantine/core';
import { useCurrentCountry } from '@/hooks/useCurrentCountry';
-import { colors, spacing, typography } from '../designTokens';
+import { spacing, typography } from '../designTokens';
import SidebarDivider from './sidebar/SidebarDivider';
import SidebarNavItem from './sidebar/SidebarNavItem';
import SidebarSection from './sidebar/SidebarSection';
@@ -81,9 +81,9 @@ export default function Sidebar({ isOpen = true }: SidebarProps) {
return (
navigate(`/${countryId}/reports/create`)}
diff --git a/app/src/components/blog/BlogPostCard.tsx b/app/src/components/blog/BlogPostCard.tsx
index de428ae9b..d83219959 100644
--- a/app/src/components/blog/BlogPostCard.tsx
+++ b/app/src/components/blog/BlogPostCard.tsx
@@ -51,20 +51,24 @@ export function BlogPostCard({ item, countryId }: BlogPostCardProps) {
const cardContent = (
{
- e.currentTarget.style.boxShadow = `0 4px 12px ${colors.gray[300]}`;
+ e.currentTarget.style.boxShadow = '0 8px 30px rgba(79, 209, 197, 0.15)';
+ e.currentTarget.style.transform = 'translateY(-4px)';
+ e.currentTarget.style.borderColor = 'rgba(79, 209, 197, 0.4)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.boxShadow = 'none';
+ e.currentTarget.style.transform = 'translateY(0)';
+ e.currentTarget.style.borderColor = 'rgba(79, 209, 197, 0.2)';
}}
>
{/* Image */}
@@ -83,6 +87,7 @@ export function BlogPostCard({ item, countryId }: BlogPostCardProps) {
width: '100%',
height: '100%',
objectFit: 'cover',
+ transition: 'transform 0.3s ease',
}}
onError={(e) => {
// Hide broken images
@@ -105,7 +110,18 @@ export function BlogPostCard({ item, countryId }: BlogPostCardProps) {
{displayTags.map((tag) => (
-
+
{tag}
))}
@@ -130,8 +146,9 @@ export function BlogPostCard({ item, countryId }: BlogPostCardProps) {
size="sm"
mt="sm"
style={{
- color: colors.primary[600],
+ color: '#0d9488',
textAlign: 'right',
+ fontWeight: 500,
}}
>
{item.isApp ? 'Open →' : 'Read →'}
diff --git a/app/src/components/common/EmptyState.tsx b/app/src/components/common/EmptyState.tsx
index 6562cc0eb..5be421d31 100644
--- a/app/src/components/common/EmptyState.tsx
+++ b/app/src/components/common/EmptyState.tsx
@@ -1,4 +1,4 @@
-import { Stack, Text } from '@mantine/core';
+import { Box, Stack, Text } from '@mantine/core';
interface EmptyStateProps {
ingredient: string; // e.g., "Policy"
@@ -8,11 +8,26 @@ interface EmptyStateProps {
export default function EmptyState({ ingredient }: EmptyStateProps) {
return (
-
+
+
+ ?
+
+
+
No {ingredient.toLowerCase()} found.
- {/*
Please create {ingredient} to get started. */}
- {/* */}
);
}
diff --git a/app/src/components/flowView/CardListVariant.tsx b/app/src/components/flowView/CardListVariant.tsx
index f81f98467..e15a9a039 100644
--- a/app/src/components/flowView/CardListVariant.tsx
+++ b/app/src/components/flowView/CardListVariant.tsx
@@ -32,25 +32,50 @@ export default function CardListVariant({
return (
{paginatedItems.map((item: CardListItem, index: number) => {
- // Determine variant based on disabled state first, then selection
- let variant = 'cardList--inactive';
- if (item.isDisabled) {
- variant = 'cardList--disabled';
- } else if (item.isSelected) {
- variant = 'cardList--active';
- }
+ const isSelected = item.isSelected;
+ const isDisabled = item.isDisabled;
return (
{
+ if (!isDisabled) {
+ e.currentTarget.style.borderColor = '#4FD1C5';
+ e.currentTarget.style.boxShadow = '0 4px 12px rgba(79, 209, 197, 0.15)';
+ }
+ }}
+ onMouseLeave={(e) => {
+ if (!isDisabled) {
+ e.currentTarget.style.borderColor = isSelected
+ ? '#4FD1C5'
+ : 'rgba(79, 209, 197, 0.2)';
+ e.currentTarget.style.boxShadow = 'none';
+ }
+ }}
>
- {item.title}
+
+ {item.title}
+
{item.subtitle && (
{item.subtitle}
diff --git a/app/src/components/home/ActionCards.tsx b/app/src/components/home/ActionCards.tsx
index a97c434ca..bc9c26f0c 100644
--- a/app/src/components/home/ActionCards.tsx
+++ b/app/src/components/home/ActionCards.tsx
@@ -1,6 +1,6 @@
import { useNavigate } from 'react-router-dom';
-import { Card, Center, Container, Text } from '@mantine/core';
-import { colors, spacing, typography } from '@/designTokens';
+import { Box, Center, Container, Group } from '@mantine/core';
+import { spacing, typography } from '@/designTokens';
import { useCurrentCountry } from '@/hooks/useCurrentCountry';
export default function ActionCards() {
@@ -10,24 +10,71 @@ export default function ActionCards() {
return (
- navigate(`/${countryId}/reports`)}
+
-
- Enter PolicyEngine
-
-
+
+ {/* Primary CTA */}
+
+
+ {/* Secondary CTA */}
+
+
+
);
diff --git a/app/src/components/home/MainSection.tsx b/app/src/components/home/MainSection.tsx
index 24d0d3ad2..dbf49f497 100644
--- a/app/src/components/home/MainSection.tsx
+++ b/app/src/components/home/MainSection.tsx
@@ -1,5 +1,5 @@
-import { Container, Stack, Text, Title } from '@mantine/core';
-import { colors, spacing, typography } from '@/designTokens';
+import { Box, Container, Stack, Text } from '@mantine/core';
+import { spacing, typography } from '@/designTokens';
import { useCurrentCountry } from '@/hooks/useCurrentCountry';
export default function MainSection() {
@@ -15,37 +15,64 @@ export default function MainSection() {
maxWidth: spacing.layout.container,
}}
>
-
- Start simulating
-
+
+ Simulate the
+
+
+ future of policy
+
+
+
-
- Free, open-source tax and benefit analysis.
-
- {countryId === 'uk'
- ? 'Model policy reforms across the UK.'
- : 'Model policy reforms across all 50 states.'}
-
- Power benefit access tools with accurate rules.
-
+
+ Free, open-source tax and benefit analysis.
+
+ {countryId === 'uk'
+ ? 'Model policy reforms across the UK.'
+ : 'Model policy reforms across all 50 states.'}
+
+ Power benefit access tools with accurate rules.
+
+
);
diff --git a/app/src/components/home/OrgLogos.tsx b/app/src/components/home/OrgLogos.tsx
index f70e13b9e..b4ea5ea99 100644
--- a/app/src/components/home/OrgLogos.tsx
+++ b/app/src/components/home/OrgLogos.tsx
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Box, Flex, Text } from '@mantine/core';
import { CountryId, getOrgsForCountry, Organization } from '@/data/organizations';
-import { colors, spacing, typography } from '@/designTokens';
+import { spacing, typography } from '@/designTokens';
import { useCurrentCountry } from '@/hooks/useCurrentCountry';
const NUM_VISIBLE = 7;
@@ -104,14 +104,30 @@ export default function OrgLogos() {
const visibleOrgs = slotIndices.map((idx) => shuffledOrgs[idx]).filter(Boolean) as Organization[];
return (
-
+
{countryId === 'us'
? 'Trusted by researchers, policy organizations, and benefit platforms'
@@ -144,9 +160,19 @@ export default function OrgLogos() {
cursor: 'pointer',
width: '120px',
height: '100px',
- opacity: transitioningSlot === i ? 0 : 1,
+ opacity: transitioningSlot === i ? 0 : 0.6,
transition: 'opacity 0.3s ease-in-out',
}}
+ onMouseEnter={(e) => {
+ if (transitioningSlot !== i) {
+ e.currentTarget.style.opacity = '1';
+ }
+ }}
+ onMouseLeave={(e) => {
+ if (transitioningSlot !== i) {
+ e.currentTarget.style.opacity = '0.6';
+ }
+ }}
>