diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx
index b19b876..02a1a73 100644
--- a/frontend/src/app/page.tsx
+++ b/frontend/src/app/page.tsx
@@ -1,43 +1,239 @@
'use client';
-
+
import Link from 'next/link';
+import { useRef } from 'react';
+import { motion, useInView, useMotionValue, useTransform, animate } from 'motion/react';
import {
- Badge,
+ Anchor,
Button,
- Card,
- Center,
Container,
Grid,
Group,
- Paper,
SimpleGrid,
Stack,
Text,
- ThemeIcon,
Title,
Box,
} from '@mantine/core';
import {
- IconMap2,
- IconChartBar,
- IconDownload,
- IconHeartHandshake,
- IconDatabase,
- IconBuildingCommunity,
- IconPencil,
-} from '@tabler/icons-react';
-
+ MapTrifoldIcon,
+ ChartBarIcon,
+ DownloadSimpleIcon,
+ HandHeartIcon,
+ PencilSimpleIcon,
+ GithubLogoIcon,
+ UsersThreeIcon,
+ CheckCircleIcon,
+ XCircleIcon,
+ ArrowRightIcon,
+ NewspaperIcon
+} from '@phosphor-icons/react';
+
import { useProfile } from '@/components/profile/profileStore';
-
-interface HeroSectionProps {
- myLocation: any;
- comparison: any;
- interests: string[];
- yearMin: number;
- yearMax: number;
- openProfileModal: () => void;
+
+/* ---------------------------------------------------------------
+ Design tokens
+ Palette drawn from Vermont's own landscape rather than a generic
+ SaaS gradient: deep spruce, granite slate, birch cream, and a
+ maple-syrup amber for the single warm accent.
+----------------------------------------------------------------*/
+const COLOR = {
+ spruce: '#1B3A2F',
+ spruceDeep: '#122820',
+ slate: '#40525A',
+ birch: '#F6F5EF',
+ birchDim: '#EEEBE0',
+ ink: '#1B211D',
+ amber: '#dd9a2f',
+ amberSoft: '#E7B563',
+ amberYellow: '#FFD100',
+ line: 'rgba(27, 58, 47, 0.14)',
+};
+
+const FONT_DISPLAY = "'Fraunces', 'Iowan Old Style', serif";
+const FONT_BODY = "'General Sans', 'Inter', sans-serif";
+const FONT_MONO = "'IBM Plex Mono', ui-monospace, monospace";
+
+/* ---------------------------------------------------------------
+ Hero title typewriter timing
+----------------------------------------------------------------*/
+const HERO_LINE_1 = 'See Vermont,';
+const HERO_LINE_2 = 'town by town.';
+const TYPE_CHAR_DELAY = 0.045;
+const TYPE_LINE1_START = 0.3;
+const TYPE_LINE1_DURATION = HERO_LINE_1.length * TYPE_CHAR_DELAY;
+const TYPE_LINE2_START = TYPE_LINE1_START + TYPE_LINE1_DURATION + 0.15;
+const TYPE_LINE2_DURATION = HERO_LINE_2.length * TYPE_CHAR_DELAY;
+
+/* ---------------------------------------------------------------
+ TypewriterLine — reveals a line character by character, as if
+ it were being typed live.
+----------------------------------------------------------------*/
+function TypewriterLine({
+ text,
+ startDelay,
+ charDelay = TYPE_CHAR_DELAY,
+}: {
+ text: string;
+ startDelay: number;
+ charDelay?: number;
+}) {
+ return (
+
+ {Array.from(text).map((char, i) => (
+
+ {char}
+
+ ))}
+
+ );
}
-
+
+/* ---------------------------------------------------------------
+ BlinkingCursor — a typed-text caret that starts blinking once
+ the line above it has finished typing.
+----------------------------------------------------------------*/
+function BlinkingCursor({ delay }: { delay: number }) {
+ return (
+
+ );
+}
+
+/* ---------------------------------------------------------------
+ Reveal — scroll-triggered fade/slide wrapper. Used sparingly:
+ each section reveals once, as a whole, rather than every child
+ animating independently (that reads as noisy/AI-generated).
+----------------------------------------------------------------*/
+function Reveal({
+ children,
+ delay = 0,
+ y = 24,
+}: {
+ children: React.ReactNode;
+ delay?: number;
+ y?: number;
+}) {
+ return (
+
+ {children}
+
+ );
+}
+
+/* ---------------------------------------------------------------
+ StatNumber — counts up from 0 when it scrolls into view.
+----------------------------------------------------------------*/
+function StatNumber({
+ value,
+ suffix = '',
+ prefix = '',
+}: {
+ value: number;
+ suffix?: string;
+ prefix?: string;
+}) {
+ const ref = useRef(null);
+ const inView = useInView(ref, { once: true, margin: '-40px' });
+ const count = useMotionValue(0);
+ const rounded = useTransform(count, (v) => `${prefix}${Math.round(v).toLocaleString()}${suffix}`);
+
+ if (inView) {
+ animate(count, value, { duration: 1.4, ease: [0.22, 1, 0.36, 1] });
+ }
+
+ return (
+
+ {rounded}
+
+ );
+}
+
+/* ---------------------------------------------------------------
+ Topographic contour — the page's one signature motif. Vermont
+ is read through its elevation lines; a single set of contour
+ paths draws itself in on load, then sits quietly as texture.
+----------------------------------------------------------------*/
+function ContourMark({ width = 260, height = 300 }: { width?: number; height?: number }) {
+ const paths = [
+ 'M10,220 C60,180 90,240 140,200 C190,160 220,210 250,180',
+ 'M0,180 C50,150 80,190 130,160 C180,130 210,170 260,150',
+ 'M20,140 C60,115 90,145 130,120 C170,95 200,125 240,110',
+ 'M30,100 C60,82 90,102 120,85 C150,68 175,90 210,75',
+ ];
+ return (
+
+ );
+}
+
+/* ---------------------------------------------------------------
+ Hero
+----------------------------------------------------------------*/
function HeroSection({
myLocation,
comparison,
@@ -45,395 +241,564 @@ function HeroSection({
yearMin,
yearMax,
openProfileModal,
-}: HeroSectionProps) {
+}: {
+ myLocation: any;
+ comparison: any;
+ interests: string[];
+ yearMin: number;
+ yearMax: number;
+ openProfileModal: () => void;
+}) {
return (
- <>
-
-
+ // Full-bleed: breaks out of the page's centered Container so the hero
+ // touches both edges of the viewport instead of floating as a card.
+
+
+
+
-
-
- Vermont Open Data Platform
-
-
-
+
- Vermont Data Explorer
-
-
-
- Explore Vermont through interactive maps, data analysis,
- downloadable datasets, and planning tools. Built to help
- communities better understand local conditions.
+ A project of the Vermont Data Collaborative
-
-
-
-
+
- Analyze Data
+ Run an analysis
-
+
-
+
+ {/* Profile panel — sits beside the title, always on the dark
+ background so the light text stays legible. */}
-
- {/* Vermont outline */}
-
-
-
-
-
- Your Profile
-
+
+
+ Your profile
+
}
+ rightSection={}
+ styles={{ root: { color: COLOR.amberSoft } }}
>
Edit
-
-
- LOCATION
-
-
- {myLocation.name}
-
-
-
-
- COMPARISON AREA
-
-
- {comparison.name}
-
-
-
-
- ANALYSIS PERIOD
-
-
-
- {yearMin}–{yearMax}
-
-
-
-
-
- INTERESTS
-
-
-
+
+
+
+
+
+
+ Interests
{interests.length > 0 ? (
- interests.map((interest) => (
-
- {interest}
-
- ))
- ) : (
-
- No interests selected
+
+ {interests.join(' · ')}
+ ) : (
+ None selected
)}
-
-
-
-
+
+
+
+
-
- >
+
+
);
}
-function FeatureCards() {
+
+function FieldLabel({ children }: { children: React.ReactNode }) {
return (
- <>
-
-
- Explore the Platform
-
+
+ {children}
+
+ );
+}
-
- {
- e.currentTarget.style.transform = 'translateY(-2px)';
- e.currentTarget.style.boxShadow =
- '0 4px 12px rgba(0, 0, 0, 0.15)';
- }}
- onMouseLeave={(e) => {
- e.currentTarget.style.transform = 'none';
- e.currentTarget.style.boxShadow = 'sm';
- }}
- >
-
-
-
-
- Mapping
-
-
- Explore zoning, flood risk, wastewater suitability, and other
- Vermont spatial datasets.
+function ProfileField({ label, value }: { label: string; value?: string }) {
+ return (
+
+ {label}
+ {value || '—'}
+
+ );
+}
+
+/* ---------------------------------------------------------------
+ Stat strip
+----------------------------------------------------------------*/
+function StatStrip() {
+ const stats = [
+ { value: 251, label: 'towns & cities mapped' },
+ { value: 20, label: 'open datasets', suffix: '' },
+ { value: 15, label: 'years of data', suffix: '+' },
+ { value: 100, label: 'free to use', suffix: '%' },
+ ];
+ return (
+
+
+ {stats.map((s) => (
+
+
+
+ {s.label}
-
+
+ ))}
+
+
+ );
+}
- {
- e.currentTarget.style.transform = 'translateY(-2px)';
- e.currentTarget.style.boxShadow =
- '0 4px 12px rgba(0, 0, 0, 0.15)';
- }}
- onMouseLeave={(e) => {
- e.currentTarget.style.transform = 'none';
- e.currentTarget.style.boxShadow = 'sm';
- }}
- >
-
-
-
-
- Data Analysis
-
+function CapabilityFlow() {
+ const items = [
+ {
+ icon: MapTrifoldIcon,
+ title: 'Mapping',
+ body: 'Statewide zoning, flood risk, wastewater, and other spatial layers at the county and town level.',
+ href: '/mapping',
+ },
+ {
+ icon: ChartBarIcon,
+ title: 'Data Analysis',
+ body: 'Explore charts, tables, and comparisons across towns, counties, or the whole state.',
+ href: '/data-viewer',
+ },
+ {
+ icon: NewspaperIcon,
+ title: 'Reporting',
+ body: 'Gather data visualizations and tables for community decisions.',
+ href: '/working-report',
+ },
+ {
+ icon: DownloadSimpleIcon,
+ title: 'Open Data Export',
+ body: 'Download clean, analysis-ready datasets with readable variable names.',
+ href: '/data-export',
+ },
+ {
+ icon: HandHeartIcon,
+ title: 'Benefits Estimator',
+ body: 'Check eligibility for Vermont assistance programs with a few questions.',
+ href: '/tools/benefits-estimator',
+ },
+ ];
-
- Generate charts, summaries, and comparisons from Vermont datasets.
-
-
+ return (
+
+
+
+ Explore Data Your Way
+
+
+
+
+
+
+ {items.map((item, i) => {
+ const Icon = item.icon;
+ return (
+
+
+
+
+
- {
- e.currentTarget.style.transform = 'translateY(-2px)';
- e.currentTarget.style.boxShadow =
- '0 4px 12px rgba(0, 0, 0, 0.15)';
- }}
- onMouseLeave={(e) => {
- e.currentTarget.style.transform = 'none';
- e.currentTarget.style.boxShadow = 'sm';
- }}
- >
-
-
-
+
+
+ {item.title}
+
+
+ {item.body}
+
+
+
+
+ );
+ })}
+
+
+
+ );
+}
-
- Data Export
-
-
- Download clean, analysis-ready datasets with readable variable
- names.
-
-
+function FeaturedDatasets() {
+ const datasets = [
+ 'Zoning',
+ 'Flood Risk',
+ 'Wastewater',
+ 'Housing',
+ 'Demographics',
+ 'Labor & Economy',
+ 'Transportation',
+ 'Community Health',
+ ];
-
+
+
+ {
- e.currentTarget.style.transform = 'translateY(-2px)';
- e.currentTarget.style.boxShadow =
- '0 4px 12px rgba(0, 0, 0, 0.15)';
+ fontFamily: FONT_MONO,
+ fontSize: 14,
+ letterSpacing: '0.1em',
+ textTransform: 'uppercase',
+ color: COLOR.amber,
+ textAlign: 'center',
}}
- onMouseLeave={(e) => {
- e.currentTarget.style.transform = 'none';
- e.currentTarget.style.boxShadow = 'sm';
+ mb={20}
+ >
+ Featured data
+
+
+
+
-
-
-
-
-
- Benefits Estimator
-
-
-
- Estimate eligibility for Vermont assistance programs and benefits.
-
-
-
-
- >
+ {datasets.map((d, i) => (
+
+
+ {d}
+
+ {i < datasets.length - 1 && (
+ ·
+ )}
+
+ ))}
+
+
+
+
);
}
-function FeaturedDatasets() {
+/* ---------------------------------------------------------------
+ Value-adds — a direct comparison, not another card grid.
+----------------------------------------------------------------*/
+function ValueAdds() {
+ const rows = [
+ { label: 'Free, no account required', us: true, others: false },
+ { label: 'Plain-language field names', us: true, others: false },
+ { label: 'Vermont-specific, town-level detail', us: true, others: false },
+ { label: 'Maintained with community input', us: true, others: false },
+ { label: 'Updated on a public schedule', us: true, others: false },
+ ];
+
return (
- <>
- {/* Featured Datasets */}
-
-
- Featured Data
+
+
+
+ What's different here
+
+
+ Compared to a typical government data portal.
+
+
+
+
+
+
+
+
+ Our platform
+
+
+ Typical portal
+
+
-
-
- Zoning
-
-
- Flood Risk
-
-
- Wastewater
-
-
- Housing
-
-
- Demographics
-
-
- Economy
-
-
- Transportation
-
-
- Community Data
-
-
-
-
- >
+ {rows.map((row, i) => (
+
+
+ {row.label}
+
+
+
+
+
+
+
+
+ ))}
+
+
+
);
}
-function PlatformValue() {
+/* ---------------------------------------------------------------
+ Open source & community
+----------------------------------------------------------------*/
+function CommunitySection() {
return (
- <>
- {/* Paste Platform Value exactly as-is */}
-
-
-
-
-
-
-
-
- Integrated Data
-
-
-
- Census, planning, environmental, infrastructure, and community
- datasets are brought together into a single platform for
- exploration and analysis.
-
-
-
-
-
-
-
-
-
-
-
- Built for Vermont
-
-
-
- Designed to support residents, municipalities, planners,
- researchers, and organizations working throughout Vermont.
-
-
-
-
- >
+
+
+
+
+
+
+
+ Built in the open, by Vermonters
+
+
+ The platform's code and data pipelines are open source. Towns,
+ researchers, and volunteers help shape what gets added next —
+ and anyone can inspect exactly how a number was calculated.
+
+
+
+ }
+ styles={{
+ root: { borderColor: 'rgba(246,245,239,0.4)', color: COLOR.birch },
+ }}
+ >
+ View the source
+
+
+ How to contribute →
+
+
+
+
+
);
}
+/* ---------------------------------------------------------------
+ Page
+----------------------------------------------------------------*/
function App() {
const {
myLocation,
@@ -446,7 +811,7 @@ function App() {
return (
-
+
-
-
+
+
-
+
+
);
}
-export default App;
+export default App;
\ No newline at end of file