diff --git a/next.config.ts b/next.config.ts index 97f0a3b..bdccdab 100644 --- a/next.config.ts +++ b/next.config.ts @@ -13,11 +13,6 @@ const nextConfig: NextConfig = { destination: "/builders/:slug", permanent: true, }, - { - source: "/toronto", - destination: "/toronto/memos", - permanent: false, - }, ]; }, async rewrites() { diff --git a/public/assets/images/toronto/gardiner.jpg b/public/assets/images/toronto/gardiner.jpg new file mode 100644 index 0000000..b1f543c Binary files /dev/null and b/public/assets/images/toronto/gardiner.jpg differ diff --git a/public/assets/images/toronto/hero.jpg b/public/assets/images/toronto/hero.jpg new file mode 100644 index 0000000..19ed43c Binary files /dev/null and b/public/assets/images/toronto/hero.jpg differ diff --git a/src/app/globals.css b/src/app/globals.css index 97161c6..138920d 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -267,6 +267,11 @@ html { @custom-variant cards (@media (min-width: 612px)); @custom-variant wide (@media (min-width: 956px)); +html { + /* Sticky nav (top-[10px] + ~64px nav) + breathing room. */ + scroll-padding-top: 90px; +} + body { background: var(--color-bg); color: var(--color-text); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index abb864e..d52d8d3 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,6 +4,7 @@ import "./globals.css"; import Navbar from "@/components/Navbar"; import Footer from "@/components/Footer"; import ScrollToTop from "@/components/ScrollToTop"; +import ThemeShell from "@/components/ThemeShell"; import { Toaster } from "sonner"; import { SubscribeModal } from "@/components/subscribe"; @@ -63,13 +64,13 @@ export default function RootLayout({ )} -
-
+ +
{children}
-
+
diff --git a/src/app/toronto/about/page.tsx b/src/app/toronto/about/page.tsx new file mode 100644 index 0000000..d2a8487 --- /dev/null +++ b/src/app/toronto/about/page.tsx @@ -0,0 +1,230 @@ +import type { Metadata } from "next"; +import SectionLabel from "@/components/SectionLabel"; +import QuickLinks from "@/app/about/QuickLinks"; +import QnaBlock from "@/app/about/QnaBlock"; + +export const metadata: Metadata = { + title: "About", + description: + "Build Canada - Toronto is a civic movement for a city that leads again — in ambition, in size, and in what a great city can be.", + alternates: { canonical: "/toronto/about" }, + openGraph: { + title: "About | Build Canada - Toronto", + description: + "Build Canada - Toronto is a civic movement for a city that leads again.", + type: "website", + }, +}; + +const quickLinks = [ + { label: "Principles", href: "#principles" }, + { label: "FAQs", href: "#faqs" }, +]; + +const principles: { title: string; body: string }[] = [ + { + title: "Toronto is Canada's launchpad.", + body: "Toronto should be where the country pilots its boldest ideas — the place national ambition takes shape first.", + }, + { + title: "Growth drives opportunity.", + body: "Cities don't succeed by saying no. Growth brings energy, investment, and the scale needed to support jobs, housing, and infrastructure.", + }, + { + title: "Toronto's future is urban.", + body: "We are a city — not a suburb — and we should plan, build, and govern like it. Density and walkability are features, not threats.", + }, + { + title: "Talent belongs here.", + body: "Toronto should keep its best — and attract the world's best — by being a place where ambitious people can live, work, and raise families.", + }, + { + title: "Cities are for people.", + body: "Toronto is the daily interface for millions of lives. It should feel like it was designed with them in mind: functional, beautiful, and human.", + }, + { + title: "Courage over caution.", + body: "Toronto has been governed too cautiously for too long — risk-averse, slow-moving, and captured by the status quo. We need decisive leadership.", + }, + { + title: "Make every dollar count.", + body: "We need to tax more efficiently and spend more effectively, focusing on outcomes that actually improve daily life.", + }, +]; + +const faqs = [ + { + id: "what-is-this", + question: "What is Build Canada — Toronto?", + answer: + "

Build Canada — Toronto is the first city project of Build Canada. It is a civic initiative focused on generating bold ideas that support Toronto's growth and prosperity.

", + order: 1, + active: true, + }, + { + id: "how-funded", + question: "How is this funded?", + answer: + "

Build Canada — Toronto is supported through Build Canada's network of donors and volunteers.

", + order: 3, + active: true, + }, + { + id: "what-activities", + question: "What sorts of activities are you doing?", + answer: + "

Our central activity is the release of frequent memos written by entrepreneurs, civic leaders, and policy thinkers. These memos focus on Toronto's biggest challenges — housing, governance, infrastructure, and economic competitiveness.

", + order: 4, + active: true, + }, + { + id: "why", + question: "Why are you doing this?", + answer: + "

Toronto is Canada's largest city and economic engine. Its success is critical to the success of the country. Yet Toronto has struggled with gridlock, unaffordability, and political inertia. We think it can be much, much better.

", + order: 5, + active: true, + }, + { + id: "partisan", + question: "Are you a partisan group?", + answer: + "

No. Like Build Canada, we are non-partisan. We focus on ideas, not parties.

", + order: 6, + active: true, + }, + { + id: "lobby", + question: "Is this a lobby group?", + answer: + "

No. This is not a lobby group. It does not represent specific industries or special interests.

", + order: 7, + active: true, + }, + { + id: "involved", + question: "How can I get involved?", + answer: + "

We welcome volunteers, entrepreneurs, and civic leaders who want to make a difference in Toronto. Email us at hi@buildcanada.com.

", + order: 8, + active: true, + }, + { + id: "stay-up-to-date", + question: "How can I stay up to date on your work?", + answer: + "

The best way to follow along is through this website and our social media accounts, as well as the Build Canada newsletter.

", + order: 9, + active: true, + }, + { + id: "contact", + question: "How can I contact you?", + answer: + "

Email us at hi@buildcanada.com, or connect with us on X, LinkedIn, Instagram, or BlueSky.

", + order: 10, + active: true, + }, + { + id: "why-start", + question: "Why did you start this group?", + answer: + "

To champion bold ideas and innovative solutions that support Toronto's — and Canada's — long-term prosperity.

", + order: 11, + active: true, + }, + { + id: "donate", + question: "Can I donate?", + answer: + "

Yes — please do! Donations directly fund our work to build a better Toronto. Donate here.

", + order: 12, + active: true, + }, +]; + +function HeroSection() { + return ( +
+
+
+

+ The Toronto we must build +

+
+
+
+

+ Toronto was never finished. It’s still being built — + by people who arrive with ambition, raise families with hope, + and believe things can keep getting better. This city + isn’t a museum piece. It’s a living project. And + its best days are still ahead. +

+

+ Housing is scarce. Transit is incomplete. Infrastructure lags + behind. City Hall still treats growth like a problem — + and too often, we cheer for mediocrity and call it progress. +

+

+ We believe Toronto can be something greater: a magnet for + talent, a home for young families, a global hub of culture, + capital, and connection. +

+
+
+
+
+ ); +} + +function PrinciplesSection() { + return ( +
+
+ Our principles +
    + {principles.map((p, i) => ( +
  1. + + {String(i + 1).padStart(2, "0")} + +
    +

    {p.title}

    +

    {p.body}

    +
    +
  2. + ))} +
+
+

+ Build Canada — Toronto is a civic movement for those who want to see this + city lead again. Not just in ambition. Not just in size. But in + what a great city can be. Let’s build the Toronto we know + is possible — the greatest and freest city on earth. +

+
+
+
+ ); +} + +export default function TorontoAboutPage() { + return ( +
+ + + + +
+ ); +} diff --git a/src/app/toronto/layout.tsx b/src/app/toronto/layout.tsx index 5244caf..9a45070 100644 --- a/src/app/toronto/layout.tsx +++ b/src/app/toronto/layout.tsx @@ -2,8 +2,8 @@ import type { Metadata } from "next"; export const metadata: Metadata = { title: { - default: "🏗️ Toronto", - template: "%s | 🏗️ Toronto", + default: "Build Canada - Toronto", + template: "%s | Build Canada - Toronto", }, description: "Memos and ideas for Toronto — a publication of Build Canada.", @@ -14,5 +14,5 @@ export default function TorontoLayout({ }: Readonly<{ children: React.ReactNode; }>) { - return
{children}
; + return
{children}
; } diff --git a/src/app/toronto/memos/page.tsx b/src/app/toronto/memos/page.tsx index 5c69f32..98c2fd9 100644 --- a/src/app/toronto/memos/page.tsx +++ b/src/app/toronto/memos/page.tsx @@ -10,13 +10,13 @@ export const metadata: Metadata = { "Bold thinking for Toronto. Read policy memos and ideas worth building on.", alternates: { canonical: "/toronto/memos" }, openGraph: { - title: "🏗️ Toronto — Memos", + title: "Memos | Build Canada - Toronto", description: "Bold thinking for Toronto.", type: "website", }, twitter: { card: "summary_large_image", - title: "🏗️ Toronto — Memos", + title: "Memos | Build Canada - Toronto", description: "Bold thinking for Toronto.", }, }; @@ -28,7 +28,7 @@ export default async function TorontoMemosPage() {
- 🏗️ Toronto · Memos + Build Canada — Toronto · Memos

Ideas for a Better Toronto

diff --git a/src/app/toronto/page.tsx b/src/app/toronto/page.tsx new file mode 100644 index 0000000..7d0b2e0 --- /dev/null +++ b/src/app/toronto/page.tsx @@ -0,0 +1,192 @@ +import type { Metadata } from "next"; +import Image from "next/image"; +import { fetchMemos } from "@/lib/api"; +import SectionLabel from "@/components/SectionLabel"; +import { LinkButton } from "@/components/ui/link-button"; +import { SubscribeButton } from "@/components/ui/subscribe-button"; +import { SectionHeader } from "@/components/ui/section-header"; +import PickCard from "@/components/PickCard"; + +export const metadata: Metadata = { + title: "Build Canada - Toronto", + description: + "Toronto is not the greatest city in the world. But it should be. Memos and ideas to help build a better Toronto.", + alternates: { canonical: "/toronto" }, + openGraph: { + title: "Build Canada - Toronto", + description: + "Build the Toronto you know is possible — bold ideas from notable Torontonians.", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "Build Canada - Toronto", + description: "Build the Toronto you know is possible.", + }, +}; + +function HeroSection() { + return ( +
+ Toronto skyline +
+
+

+ Build the Toronto +
+ You Know is Possible +

+
+ “Toronto is not the greatest city in the world. But it + should be.” +
+
+ + Get Updates + + + Get Involved + +
+
+
+
+ ); +} + +function MissionSection() { + return ( +
+
+ + Building a Better Toronto + +
+
+ Frederick Gardiner, First Chairman of Metropolitan Toronto + + Frederick Gardiner, +
+ First Chairman of +
+ Metropolitan Toronto +
+
+
+
+

+ We’re Torontonians who refuse to accept our city’s + managed decline. We believe Toronto should be something + greater: a city of energy and beauty, where world-class + infrastructure and serious governance let people thrive. +

+

+ We help notable Torontonians share their bold policy ideas to + make that a reality. +

+
+
+ + Learn More + +
+
+
+
+
+ ); +} + +function PolicyIdeasSection({ + memos, +}: { + memos: Awaited>; +}) { + const cards = memos.slice(0, 4); + + if (cards.length === 0) { + return ( +
+
+ +

+ New memos are on the way. Subscribe to be the first to read them. +

+
+
+ ); + } + + return ( +
+
+ + See more memos + + } + /> +

+ We help notable Torontonians share their bold ideas to grow our city. +

+
+ {cards.map((m) => ( + + ))} +
+ + See more memos + +
+
+ ); +} + +function SubscribeCTA() { + return ( +
+
+ Be first to know what’s possible +

+ Get new memos, updates from our team, and ideas to help Toronto grow. +

+
+ + Subscribe + +
+
+
+ ); +} + +export default async function TorontoHome() { + const memos = await fetchMemos({ publication: "build_toronto" }); + + return ( +
+ + + + +
+ ); +} diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 43ddbc3..9c278cc 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -6,7 +6,7 @@ import { usePathname } from "next/navigation"; import { useState, useEffect, useRef } from "react"; import { useSubscribeStore } from "@/components/subscribe/store"; import { SOCIALS } from "@/constants/socials"; -import { NAV_LINKS } from "@/constants/nav-links"; +import { NAV_LINKS, TORONTO_NAV_LINKS } from "@/constants/nav-links"; export default function Navbar() { const [menuOpen, setMenuOpen] = useState(false); @@ -15,6 +15,7 @@ export default function Navbar() { const openModal = useSubscribeStore((s) => s.openModal); const pathname = usePathname(); const isToronto = pathname?.startsWith("/toronto") ?? false; + const navLinks = isToronto ? TORONTO_NAV_LINKS : NAV_LINKS; useEffect(() => { const handleScroll = () => { @@ -35,11 +36,20 @@ export default function Navbar() { {/* Logo */} {isToronto ? ( + Build Canada +