Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
document.documentElement.style.colorScheme = isDark ? "dark" : "light";
document
.querySelector('meta[name="theme-color"]')
?.setAttribute("content", isDark ? "#050505" : "#ffffff");
?.setAttribute("content", isDark ? "#000000" : "#ffffff");
})();
</script>
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
Expand Down
9 changes: 9 additions & 0 deletions src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,15 @@ describe("App", () => {
name: /turn signals into evidence/i,
}),
).toBeInTheDocument();
for (const nextStep of [
"Design a bounded pilot",
"Publish what happened",
"Choose the next move",
]) {
expect(
within(howItWorks).getByRole("heading", { name: nextStep }),
).toBeInTheDocument();
}
expect(howItWorks).toHaveTextContent(/no payment or commitment/i);
expect(
within(howItWorks).getByRole("link", {
Expand Down
37 changes: 32 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,27 @@ const validationSteps = [
description:
"Use aggregate demand—not private member activity—to decide whether to refine, pause, or prepare a small, permission-based pilot.",
},
{
icon: Wrench,
number: "04",
title: "Design a bounded pilot",
description:
"Define the participants, permissions, safeguards, measures, and stop conditions before testing the idea in the real world.",
},
{
icon: BookOpen,
number: "05",
title: "Publish what happened",
description:
"Share outcomes, limits, surprises, and participant feedback without exposing private member activity or sensitive data.",
},
{
icon: ArrowRight,
number: "06",
title: "Choose the next move",
description:
"Use the evidence to refine, repeat, pause, or archive the concept. Nothing advances automatically and every decision stays explainable.",
},
];

const ideaTerrains = [
Expand Down Expand Up @@ -216,7 +237,7 @@ function HomePage() {
showExplore={false}
showStartIdea={false}
/>
<main className="field-grid contour-field min-h-screen overflow-hidden text-foreground">
<main className="min-h-screen overflow-hidden text-foreground">
<div className="site-shell flex min-h-screen flex-col">
<InterestModeNotice
className="mt-5"
Expand Down Expand Up @@ -425,16 +446,22 @@ function HomePage() {
{validationSteps.map(
({ icon: Icon, number, title, description }) => (
<article
className="relative border-border p-7 md:border-r md:last:border-r-0 sm:p-9"
className="relative border-b border-border p-7 last:border-b-0 sm:p-9 md:border-r md:[&:nth-child(3n)]:border-r-0 md:[&:nth-last-child(-n+3)]:border-b-0"
key={title}
>
<div className="flex items-center justify-between">
<span className="grid size-10 place-items-center border border-primary/30 text-primary">
<Icon className="size-5" aria-hidden="true" />
</span>
<span className="font-mono text-xs font-semibold text-primary/55">
{number}
</span>
<div className="flex items-center gap-3">
<span
className="size-2 bg-signal"
aria-hidden="true"
/>
<span className="font-mono text-xs font-semibold text-primary/55">
{number}
</span>
</div>
</div>
<h3 className="mt-8 text-xl font-semibold tracking-tight">
{title}
Expand Down
2 changes: 1 addition & 1 deletion src/features/admin/admin-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ function AdminDashboard({
return (
<>
<SiteHeader exploreLabel="Concept catalog" />
<main className="field-grid contour-field min-h-screen overflow-hidden bg-background text-foreground">
<main className="min-h-screen overflow-hidden bg-background text-foreground">
<div className="site-shell">
<section className="border-x border-b border-border bg-background/94 px-6 pb-12 pt-12 sm:px-10 sm:pb-16 sm:pt-16 lg:px-12 lg:pt-20">
<div className="max-w-4xl">
Expand Down
2 changes: 1 addition & 1 deletion src/features/auth/auth-callback-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from "@/features/auth/auth-return-path";

const pageClassName =
"field-grid contour-field relative grid min-h-screen place-items-center overflow-hidden bg-background px-6 py-12 text-foreground";
"relative grid min-h-screen place-items-center overflow-hidden bg-background px-6 py-12 text-foreground";
const backdropClassName = "hidden";
const cardClassName =
"field-panel relative w-full max-w-md border-t-4 border-t-primary p-8 text-center";
Expand Down
2 changes: 1 addition & 1 deletion src/features/auth/auth-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export function AuthPage({ mode }: AuthPageProps) {
}

return (
<main className="field-grid contour-field relative grid min-h-screen place-items-center overflow-hidden bg-background px-6 py-12 text-foreground">
<main className="relative grid min-h-screen place-items-center overflow-hidden bg-background px-6 py-12 text-foreground">
<section className="field-panel relative w-full max-w-md border-t-4 border-t-primary p-8">
<Link
to="/"
Expand Down
2 changes: 1 addition & 1 deletion src/features/ideas/idea-detail-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export function IdeaDetailPage() {
return (
<>
<SiteHeader exploreLabel="Back to ideas" />
<main className="field-grid contour-field min-h-screen overflow-hidden text-foreground">
<main className="min-h-screen overflow-hidden text-foreground">
<div className="site-shell min-h-screen">
{ideaQuery.isPending ? (
<div
Expand Down
2 changes: 1 addition & 1 deletion src/features/ideas/idea-discovery-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export function IdeaDiscoveryPage() {
return (
<>
<SiteHeader showExplore={false} />
<main className="field-grid contour-field min-h-screen overflow-hidden text-foreground">
<main className="min-h-screen overflow-hidden text-foreground">
<div className="site-shell min-h-screen">
<section className="border-x border-b border-border bg-background/94 px-6 py-12 sm:px-10 sm:py-16 lg:px-12 lg:py-20">
<div className="grid items-end gap-8 border-b border-border pb-10 lg:grid-cols-[1fr_auto]">
Expand Down
2 changes: 1 addition & 1 deletion src/features/ideas/idea-editor-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export function IdeaEditorPage() {

function EditorShell({ children }: { children: React.ReactNode }) {
return (
<main className="field-grid contour-field relative min-h-screen overflow-hidden bg-background px-6 py-12 text-foreground">
<main className="relative min-h-screen overflow-hidden bg-background px-6 py-12 text-foreground">
<section className="field-panel relative mx-auto w-full max-w-3xl border-t-4 border-t-primary p-8 sm:p-10">
<Link
to="/"
Expand Down
2 changes: 1 addition & 1 deletion src/features/pilots/pilot-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function PilotPage() {
exploreLabel="Concept preview"
exploreTo="/ideas/project-time-capsule"
/>
<main className="field-grid contour-field min-h-screen overflow-hidden text-foreground">
<main className="min-h-screen overflow-hidden text-foreground">
<div className="site-shell">
<section className="border-x border-b border-border bg-background/94 px-6 py-12 sm:px-10 sm:py-16 lg:px-12 lg:py-20">
<div className="max-w-4xl">
Expand Down
2 changes: 1 addition & 1 deletion src/features/profiles/profile-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getPublicProfile } from "@/features/profiles/profile-service";

function ProfileShell({ children }: { children: ReactNode }) {
return (
<main className="field-grid contour-field relative grid min-h-screen place-items-center overflow-hidden bg-background px-6 py-12 text-foreground">
<main className="relative grid min-h-screen place-items-center overflow-hidden bg-background px-6 py-12 text-foreground">
<section className="field-panel relative w-full max-w-2xl border-t-4 border-t-primary p-8 sm:p-10">
<Link
to="/"
Expand Down
2 changes: 1 addition & 1 deletion src/features/theme/theme-provider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe("theme controls", () => {
expect(window.localStorage.getItem("ideascape.theme")).toBe("dark");
expect(document.querySelector('meta[name="theme-color"]')).toHaveAttribute(
"content",
"#050505",
"#000000",
);
});

Expand Down
2 changes: 1 addition & 1 deletion src/features/theme/theme-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function ThemeProvider({ children }: PropsWithChildren) {
document.documentElement.style.colorScheme = theme;
document
.querySelector('meta[name="theme-color"]')
?.setAttribute("content", theme === "dark" ? "#050505" : "#ffffff");
?.setAttribute("content", theme === "dark" ? "#000000" : "#ffffff");
}, [theme]);

const value = useMemo(
Expand Down
61 changes: 7 additions & 54 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
--chart-4: #a3a3a3;
--chart-5: #e5e5e5;
--radius: 0.375rem;
--sidebar: #fafafa;
--sidebar: #ffffff;
--sidebar-foreground: #0a0a0a;
--sidebar-primary: #0a0a0a;
--sidebar-primary-foreground: #ffffff;
Expand All @@ -90,14 +90,14 @@
}

.dark {
--background: #050505;
--background: #000000;
--foreground: #fafafa;
--card: #171717;
--card: #000000;
--card-foreground: #fafafa;
--popover: #171717;
--popover: #000000;
--popover-foreground: #fafafa;
--primary: #fafafa;
--primary-foreground: #0a0a0a;
--primary-foreground: #000000;
--secondary: #262626;
--secondary-foreground: #fafafa;
--muted: #262626;
Expand All @@ -115,10 +115,10 @@
--chart-3: #a3a3a3;
--chart-4: #525252;
--chart-5: #262626;
--sidebar: #0a0a0a;
--sidebar: #000000;
--sidebar-foreground: #fafafa;
--sidebar-primary: #fafafa;
--sidebar-primary-foreground: #0a0a0a;
--sidebar-primary-foreground: #000000;
--sidebar-accent: #262626;
--sidebar-accent-foreground: #fafafa;
--sidebar-border: #737373;
Expand All @@ -144,53 +144,6 @@
}
}

.field-grid {
background-image:
linear-gradient(
to right,
color-mix(in oklch, var(--border), transparent 55%) 1px,
transparent 1px
),
linear-gradient(
to bottom,
color-mix(in oklch, var(--border), transparent 55%) 1px,
transparent 1px
);
background-size: 32px 32px;
}

.contour-field {
position: relative;
isolation: isolate;
}

.contour-field::before {
position: absolute;
z-index: -1;
inset: 0;
pointer-events: none;
content: "";
background-image:
radial-gradient(
ellipse at 108% 24%,
transparent 0 9rem,
color-mix(in oklch, var(--primary), transparent 82%) 9.05rem 9.12rem,
transparent 9.17rem 12rem,
color-mix(in oklch, var(--primary), transparent 88%) 12.05rem 12.12rem,
transparent 12.17rem 16rem,
var(--signal) 16.05rem 16.12rem,
transparent 16.17rem
),
radial-gradient(
ellipse at -8% 86%,
transparent 0 7rem,
color-mix(in oklch, var(--primary), transparent 88%) 7.05rem 7.12rem,
transparent 7.17rem 10rem,
color-mix(in oklch, var(--primary), transparent 91%) 10.05rem 10.12rem,
transparent 10.17rem
);
}

@layer components {
.site-shell {
@apply mx-auto w-full max-w-[90rem] px-4 sm:px-8 lg:px-12;
Expand Down
21 changes: 16 additions & 5 deletions src/theme-css.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ describe("civic field-notebook design system", () => {

it("uses only black, white, grayscale, and one bright orange signal", () => {
expect(rootTheme).toContain("--background: #ffffff");
expect(rootTheme).toContain("--card: #ffffff");
expect(rootTheme).toContain("--popover: #ffffff");
expect(rootTheme).toContain("--sidebar: #ffffff");
expect(rootTheme).toContain("--foreground: #0a0a0a");
expect(rootTheme).toContain("--primary: #0a0a0a");
expect(rootTheme).toContain("--signal: #ff5a1f");
Expand All @@ -302,7 +305,10 @@ describe("civic field-notebook design system", () => {
expect(rootTheme).toContain("--border: #737373");
expect(rootTheme).toContain("--input: #737373");
expect(rootTheme).toContain("--radius: 0.375rem");
expect(darkTheme).toContain("--background: #050505");
expect(darkTheme).toContain("--background: #000000");
expect(darkTheme).toContain("--card: #000000");
expect(darkTheme).toContain("--popover: #000000");
expect(darkTheme).toContain("--sidebar: #000000");
expect(darkTheme).toContain("--foreground: #fafafa");
expect(darkTheme).toContain("--primary: #fafafa");
expect(darkTheme).toContain("--signal: #ff5a1f");
Expand Down Expand Up @@ -398,10 +404,15 @@ describe("civic field-notebook design system", () => {
);
});

it("provides map-grid and contour-field composition primitives", () => {
expect(stylesheet).toMatch(/\.field-grid\s*\{[\s\S]*background-image:/);
expect(stylesheet).toMatch(
/\.contour-field::before\s*\{[\s\S]*background-image:/,
it("uses flat pure canvases without grid or contour backgrounds", () => {
const productionSources = listVisualSourceFiles(
resolve(process.cwd(), "src"),
).map((file) => readFileSync(file, "utf8"));
expect(stylesheet).not.toContain("background-image:");
expect(stylesheet).not.toContain(".field-grid");
expect(stylesheet).not.toContain(".contour-field");
expect(productionSources.join("\n")).not.toMatch(
/\b(?:field-grid|contour-field)\b/,
);
});

Expand Down
Loading