Files
local-cal/docs/superpowers/plans/2026-04-21-local-cal-redesign.md
Dmytro Stanchiev 915e0b7cf8 feat: redesign
Signed-off-by: Dmytro Stanchiev <git@dmytros.dev>
2026-04-21 20:23:15 -04:00

23 KiB

Local Cal Redesign Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Rebuild local-cal as a Vercel-inspired product console with aligned desktop AI/timeline hierarchy, capture-first mobile behavior, redesigned primitives, and inline event validation states.

Architecture: Start at the token layer in src/app/globals.css and the shell contract in src/lib/ui-shell-contract.ts, then update shared ui/* primitives so pages compose the new visual system instead of layering overrides onto the old glass design. After the primitives are stable, reshape src/app/page.tsx, src/components/ai-toolbar.tsx, src/components/event-card.tsx, src/components/events-list.tsx, src/components/event-dialog.tsx, and src/components/settings-panel.tsx to match the approved desktop/mobile hierarchy and validation behavior.

Tech Stack: Next.js 15 App Router, React 19, Tailwind CSS v4, Bun test, Radix UI, Framer Motion


File Structure Map

Core styling and shell

  • Modify: src/app/globals.css
    • Replace glass-heavy tokens/utilities with the approved neutral product-console token system.
  • Modify: src/lib/ui-shell-contract.ts
    • Re-encode shell surface contracts for the new infrastructure bar, workspace surfaces, and utility controls.

Shared primitives

  • Modify: src/components/ui/button.tsx
    • Update button variants toward Vercel-like surface, spacing, and focus rules.
  • Modify: src/components/ui/card.tsx
    • Replace default bordered card treatment with shadow-as-border primitives.
  • Modify: src/components/ui/input.tsx
    • Update input treatment to the new console field styling.
  • Modify: src/components/ui/badge.tsx
    • Convert badges toward functional metadata accents instead of generic pills everywhere.
  • Modify: src/components/ui/dialog.tsx
    • Move modal framing from generic bordered surfaces to console-style sheets.
  • Modify: src/components/ui/dropdown-menu.tsx
    • Align menu surfaces with the new token system so More and event actions match the redesign.

Main app surfaces

  • Modify: src/app/page.tsx
    • Rebuild the page hierarchy into a thin infrastructure bar plus aligned top-row AI/timeline workspace.
  • Modify: src/components/ai-toolbar.tsx
    • Make text input and attachments co-equal and adapt desktop/mobile hierarchy.
  • Modify: src/components/events-list.tsx
    • Update empty state and list rhythm to match the new shell.
  • Modify: src/components/event-card.tsx
    • Replace workflow tags with functional metadata emphasis and inline warning treatment.
  • Modify: src/components/event-dialog.tsx
    • Redesign the dialog as a precise console form.
  • Modify: src/components/settings-panel.tsx
    • Demote settings to a secondary admin-like utility surface.

Validation helpers

  • Modify: src/lib/event-form.ts
    • Expose a reusable event-level validation result that timeline cards can consume.
  • Modify: src/lib/types.ts
    • Add any minimal derived UI type needed for validation summaries if existing types are insufficient.

Tests

  • Modify: tests/ui-shell-contract.test.ts
    • Lock down the new shell contract classes.
  • Modify: tests/ai-toolbar.test.ts
    • Update AI surface layout contracts around equal text/attachment importance.
  • Modify: tests/event-card.test.ts
    • Add timeline metadata and inline warning assertions.
  • Modify: tests/event-dialog.test.tsx
    • Add dialog contract checks for the redesigned grouping/surface.
  • Create: tests/home-page-layout.test.ts
    • Lock down the approved desktop/mobile hierarchy at the composition level.

Task 1: Replace global tokens and shell contracts

Files:

  • Modify: src/app/globals.css

  • Modify: src/lib/ui-shell-contract.ts

  • Modify: tests/ui-shell-contract.test.ts

  • Step 1: Write the failing shell contract test

import { describe, expect, test } from "bun:test";
import {
	APP_ACTION_BAR_CLASSES,
	APP_HEADER_SURFACE_CLASSES,
	APP_NAV_SURFACE_CLASSES,
	APP_SECTION_SURFACE_CLASSES,
} from "@/lib/ui-shell-contract";

describe("ui shell contract", () => {
	test("header surface is a thin structural bar instead of a glass panel", () => {
		expect(APP_HEADER_SURFACE_CLASSES).toContain("min-h-14");
		expect(APP_HEADER_SURFACE_CLASSES).toContain("border-b");
		expect(APP_HEADER_SURFACE_CLASSES).not.toContain("glass-surface");
	});

	test("section and action surfaces use tokenized shell classes instead of glass helpers", () => {
		expect(APP_SECTION_SURFACE_CLASSES).not.toContain("glass-panel");
		expect(APP_ACTION_BAR_CLASSES).not.toContain("glass-subtle");
		expect(APP_NAV_SURFACE_CLASSES).not.toContain("glass-surface");
	});
});
  • Step 2: Run test to verify it fails

Run: bun test tests/ui-shell-contract.test.ts Expected: FAIL because the shell contract still contains glass-* classes and lacks the new structural bar classes.

  • Step 3: Replace the shell classes with the approved product-console contract
import { cn } from "@/lib/utils";

export const APP_HEADER_SURFACE_CLASSES =
	"mb-6 flex min-h-14 items-center justify-between gap-3 border-b border-foreground/10 bg-background/95 px-4 py-3 sm:px-6";

export const APP_SECTION_SURFACE_CLASSES =
	"rounded-[10px] bg-card px-4 py-4 shadow-[0_0_0_1px_rgba(0,0,0,0.08),0_2px_2px_rgba(0,0,0,0.04),0_0_0_1px_#fafafa] sm:px-5";

export const APP_ACTION_BAR_CLASSES =
	"rounded-[10px] bg-card px-3 py-3 shadow-[0_0_0_1px_rgba(0,0,0,0.08)]";

export const APP_NAV_SURFACE_CLASSES =
	"fixed inset-x-4 bottom-4 mx-auto flex max-w-3xl items-center justify-between rounded-[10px] bg-background/95 px-3 py-2 shadow-[0_0_0_1px_rgba(0,0,0,0.08),0_8px_24px_rgba(0,0,0,0.08)] sm:inset-x-6 lg:hidden";

const CONNECTION_BADGE_BASE_CLASSES =
	"gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium shadow-[0_0_0_1px_rgba(0,0,0,0.08)]";

export const getConnectionBadgeClasses = (isOnline: boolean) =>
	cn(
		CONNECTION_BADGE_BASE_CLASSES,
		isOnline
			? "bg-[#ebf5ff] text-[#0068d6]"
			: "bg-muted text-muted-foreground",
	);
  • Step 4: Replace glass-centric global tokens with the approved light/dark console tokens
:root {
	--background: #ffffff;
	--foreground: #171717;
	--card: #ffffff;
	--card-foreground: #171717;
	--popover: #ffffff;
	--popover-foreground: #171717;
	--primary: #171717;
	--primary-foreground: #ffffff;
	--secondary: #fafafa;
	--secondary-foreground: #171717;
	--muted: #fafafa;
	--muted-foreground: #666666;
	--accent: #f5f5f5;
	--accent-foreground: #171717;
	--destructive: #ff5b4f;
	--destructive-foreground: #ffffff;
	--border: #ebebeb;
	--input: #ebebeb;
	--ring: hsla(212, 100%, 48%, 1);
	--radius: 0.625rem;
	--shadow-shell: 0 0 0 1px rgba(0, 0, 0, 0.08);
	--shadow-card:
		0 0 0 1px rgba(0, 0, 0, 0.08),
		0 2px 2px rgba(0, 0, 0, 0.04),
		0 8px 8px -8px rgba(0, 0, 0, 0.04),
		0 0 0 1px #fafafa;
}

.dark {
	--background: #111111;
	--foreground: #f5f5f5;
	--card: #171717;
	--card-foreground: #f5f5f5;
	--popover: #171717;
	--popover-foreground: #f5f5f5;
	--primary: #f5f5f5;
	--primary-foreground: #171717;
	--secondary: #1f1f1f;
	--secondary-foreground: #f5f5f5;
	--muted: #1a1a1a;
	--muted-foreground: #a1a1a1;
	--accent: #1f1f1f;
	--accent-foreground: #f5f5f5;
	--border: rgba(255, 255, 255, 0.1);
	--input: rgba(255, 255, 255, 0.12);
}
  • Step 5: Run test to verify it passes

Run: bun test tests/ui-shell-contract.test.ts Expected: PASS with the new shell contract assertions satisfied.

  • Step 6: Commit
git add src/app/globals.css src/lib/ui-shell-contract.ts tests/ui-shell-contract.test.ts
git commit -m "feat: add product console shell tokens"

Task 2: Redesign shared UI primitives

Files:

  • Modify: src/components/ui/button.tsx

  • Modify: src/components/ui/card.tsx

  • Modify: src/components/ui/input.tsx

  • Modify: src/components/ui/badge.tsx

  • Modify: src/components/ui/dialog.tsx

  • Modify: src/components/ui/dropdown-menu.tsx

  • Test: tests/event-dialog.test.tsx

  • Step 1: Write the failing dialog and primitive contract test

import { describe, expect, test } from "bun:test";
import { renderToStaticMarkup } from "react-dom/server";
import {
	DialogContent,
	DialogDescription,
	DialogHeader,
	DialogTitle,
} from "@/components/ui/dialog";

describe("dialog primitive redesign", () => {
	test("dialog content uses console surface classes instead of generic border+shadow", () => {
		const markup = renderToStaticMarkup(
			<DialogContent>
				<DialogHeader>
					<DialogTitle>New Event</DialogTitle>
					<DialogDescription>Console form</DialogDescription>
				</DialogHeader>
			</DialogContent>,
		);

		expect(markup).toContain("rounded-[10px]");
		expect(markup).toContain("shadow-[0_0_0_1px_rgba(0,0,0,0.08)");
		expect(markup).not.toContain("rounded-lg border p-6 shadow-lg");
	});
});
  • Step 2: Run test to verify it fails

Run: bun test tests/event-dialog.test.tsx Expected: FAIL because the dialog still renders the old generic bordered modal classes.

  • Step 3: Update buttons, cards, inputs, badges, and dialog primitives to the new system
const buttonVariants = cva(
	"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[6px] text-sm font-medium transition-[background-color,color,box-shadow] disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/30",
	{
		variants: {
			variant: {
				default: "bg-primary text-primary-foreground hover:opacity-92",
				outline:
					"bg-background text-foreground shadow-[0_0_0_1px_rgba(0,0,0,0.08)] hover:bg-accent",
				ghost: "text-muted-foreground hover:bg-accent hover:text-foreground",
				link: "text-[#0072f5] underline-offset-4 hover:underline",
			},
		},
	},
);

function Card({ className, ...props }: React.ComponentProps<"div">) {
	return (
		<div
			data-slot="card"
			className={cn(
				"bg-card text-card-foreground flex flex-col gap-6 rounded-[10px] py-6 shadow-[0_0_0_1px_rgba(0,0,0,0.08),0_2px_2px_rgba(0,0,0,0.04),0_0_0_1px_#fafafa]",
				className,
			)}
			{...props}
		/>
	);
}

function Input({ className, type, ...props }: React.ComponentProps<"input">) {
	return (
		<input
			type={type}
			data-slot="input"
			className={cn(
				"flex h-10 w-full min-w-0 rounded-[8px] bg-background px-3 py-2 text-sm shadow-[0_0_0_1px_rgba(0,0,0,0.08)] outline-none transition-[box-shadow,color] placeholder:text-muted-foreground focus-visible:ring-[3px] focus-visible:ring-ring/25",
				className,
			)}
			{...props}
		/>
	);
}
  • Step 4: Run test to verify it passes

Run: bun test tests/event-dialog.test.tsx Expected: PASS with the redesigned dialog surface contract in the markup.

  • Step 5: Commit
git add src/components/ui/button.tsx src/components/ui/card.tsx src/components/ui/input.tsx src/components/ui/badge.tsx src/components/ui/dialog.tsx src/components/ui/dropdown-menu.tsx tests/event-dialog.test.tsx
git commit -m "feat: redesign shared ui primitives"

Task 3: Rebuild the home page hierarchy

Files:

  • Modify: src/app/page.tsx

  • Create: tests/home-page-layout.test.ts

  • Step 1: Write the failing home page hierarchy test

import { describe, expect, test } from "bun:test";
import { readFileSync } from "node:fs";

describe("home page hierarchy", () => {
	test("desktop layout defines aligned AI and timeline top-row sections", () => {
		const source = readFileSync("src/app/page.tsx", "utf8");
		expect(source).toContain("lg:grid-cols-[minmax(0,0.75fr)_minmax(0,1.25fr)]");
		expect(source).toContain("AI capture");
		expect(source).toContain("Event timeline");
	});

	test("manual create is routed through a More menu instead of a primary mobile action", () => {
		const source = readFileSync("src/app/page.tsx", "utf8");
		expect(source).toContain("More");
		expect(source).not.toContain("New Event</button>");
	});
});
  • Step 2: Run test to verify it fails

Run: bun test tests/home-page-layout.test.ts Expected: FAIL because the page still uses the old stacked shell and direct nav buttons.

  • Step 3: Reshape page.tsx to the approved infrastructure bar plus aligned workspace
const APP_FRAME_CLASSES =
	"mx-auto flex min-h-screen w-full max-w-6xl flex-col px-4 pb-20 pt-4 sm:px-6 lg:px-8";

<main className={APP_FRAME_CLASSES}>
	<header className={APP_HEADER_SURFACE_CLASSES}>
		<div className="flex min-w-0 flex-col">
			<p className="font-mono text-[11px] uppercase text-muted-foreground">
				Local Calendar
			</p>
			<h1 className="text-[28px] font-semibold tracking-[-0.06em]">
				Event timeline
			</h1>
		</div>
		<div className="flex items-center gap-2">
			<Badge className={getConnectionBadgeClasses(isOnline)}>
				{isOnline ? "Online" : "Offline"}
			</Badge>
			<ModeToggle />
			<Button variant="outline" size="sm">Export</Button>
			<DropdownMenu>{/* More: Import, Manual create, Settings */}</DropdownMenu>
		</div>
	</header>

	<section className="grid items-start gap-4 lg:grid-cols-[minmax(0,0.75fr)_minmax(0,1.25fr)]">
		<div className="space-y-4">
			<AIToolbar {...aiProps} />
		</div>
		<div className="space-y-4">
			<section className={APP_SECTION_SURFACE_CLASSES}>{/* timeline */}</section>
		</div>
	</section>
</main>
  • Step 4: Run test to verify it passes

Run: bun test tests/home-page-layout.test.ts Expected: PASS with the aligned desktop workspace and secondary More path visible in source.

  • Step 5: Commit
git add src/app/page.tsx tests/home-page-layout.test.ts
git commit -m "feat: rebuild home page hierarchy"

Task 4: Redesign AI capture as a first-class dual-input surface

Files:

  • Modify: src/components/ai-toolbar.tsx

  • Modify: tests/ai-toolbar.test.ts

  • Step 1: Write the failing AI capture contract test

import { describe, expect, test } from "bun:test";
import { cn } from "@/lib/utils";

const COMPOSER_LAYOUT_CLASSES =
	"grid gap-3 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]";
const ATTACHMENTS_PANEL_CLASSES =
	"rounded-[10px] bg-card p-3 shadow-[0_0_0_1px_rgba(0,0,0,0.08)]";

describe("AI capture redesign", () => {
	test("desktop composer treats prompt and attachments as peer panels", () => {
		expect(cn(COMPOSER_LAYOUT_CLASSES)).toContain("lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]");
	});

	test("attachments panel is a first-class surfaced region, not an inline footer affordance", () => {
		expect(cn(ATTACHMENTS_PANEL_CLASSES)).toContain("rounded-[10px]");
		expect(cn(ATTACHMENTS_PANEL_CLASSES)).toContain("shadow-[0_0_0_1px_rgba(0,0,0,0.08)]");
	});
});
  • Step 2: Run test to verify it fails

Run: bun test tests/ai-toolbar.test.ts Expected: FAIL because the current toolbar still treats attachments as subordinate controls beneath the composer.

  • Step 3: Refactor the toolbar layout around peer prompt and attachment regions
<section className={APP_SECTION_SURFACE_CLASSES}>
	<div className="mb-4 flex items-start justify-between gap-3">
		<div>
			<p className="font-mono text-[11px] uppercase text-muted-foreground">
				AI capture
			</p>
			<h2 className="text-2xl font-semibold tracking-[-0.05em]">
				Describe or attach event details
			</h2>
		</div>
	</div>

	<div className="grid gap-3 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
		<div className="space-y-3">
			<Textarea value={aiPrompt} onChange={(event) => setAiPrompt(event.target.value)} />
			<Button onClick={onAiCreate}>Generate event</Button>
		</div>
		<div className="rounded-[10px] bg-card p-3 shadow-[0_0_0_1px_rgba(0,0,0,0.08)]">
			<ImagePicker onSelect={onImagesSelect} />
			<div className="mt-3 grid gap-2 sm:grid-cols-2">{/* previews */}</div>
		</div>
	</div>
</section>
  • Step 4: Run test to verify it passes

Run: bun test tests/ai-toolbar.test.ts Expected: PASS with the new equal-importance prompt/attachment contract.

  • Step 5: Commit
git add src/components/ai-toolbar.tsx tests/ai-toolbar.test.ts
git commit -m "feat: redesign ai capture surface"

Task 5: Redesign timeline cards and inline validation warnings

Files:

  • Modify: src/lib/event-form.ts

  • Modify: src/components/event-card.tsx

  • Modify: src/components/events-list.tsx

  • Modify: tests/event-card.test.ts

  • Step 1: Write the failing event card warning test

import { describe, expect, test } from "bun:test";
import { renderToStaticMarkup } from "react-dom/server";
import { EventCard } from "@/components/event-card";

describe("EventCard warning states", () => {
	test("renders inline warning copy for invalid event ranges", () => {
		const markup = renderToStaticMarkup(
			EventCard({
				event: {
					id: "evt_invalid",
					title: "Broken Event",
					start: "2026-04-09T11:00:00+00:00",
					end: "2026-04-09T10:00:00+00:00",
					allDay: false,
				},
				onEdit: () => {},
				onDelete: () => {},
			}),
		);

		expect(markup).toContain("Warning:");
		expect(markup).toContain("end time is before start time");
		expect(markup).not.toContain("Preview");
		expect(markup).not.toContain("Ship");
	});
});
  • Step 2: Run test to verify it fails

Run: bun test tests/event-card.test.ts Expected: FAIL because the card has no inline warning support and still assumes the old metadata hierarchy.

  • Step 3: Add reusable event-level validation and render it in the card
export function getEventValidationIssues(event: CalendarEvent): string[] {
	const issues: string[] = [];

	if (event.end && new Date(event.end).getTime() < new Date(event.start).getTime()) {
		issues.push("end time is before start time");
	}

	if (event.url && !URL.canParse(event.url)) {
		issues.push("link is invalid");
	}

	return issues;
}
const validationIssues = getEventValidationIssues(event);

{validationIssues.length > 0 && (
	<div className="rounded-[8px] bg-[#fff4f2] px-3 py-2 text-xs text-[#b42318] shadow-[inset_0_0_0_1px_rgba(180,35,24,0.14)] dark:bg-[#2a1715] dark:text-[#ff8a80]">
		Warning: {validationIssues[0]}.
	</div>
)}
  • Step 4: Run test to verify it passes

Run: bun test tests/event-card.test.ts Expected: PASS with the inline warning and workflow-tag absence confirmed.

  • Step 5: Commit
git add src/lib/event-form.ts src/components/event-card.tsx src/components/events-list.tsx tests/event-card.test.ts
git commit -m "feat: add inline timeline validation states"

Task 6: Redesign the event dialog and settings surface

Files:

  • Modify: src/components/event-dialog.tsx

  • Modify: src/components/settings-panel.tsx

  • Modify: tests/event-dialog.test.tsx

  • Step 1: Write the failing form-surface test

import { describe, expect, test } from "bun:test";
import { readFileSync } from "node:fs";

describe("event dialog redesign", () => {
	test("dialog source uses console section labels and grouped field regions", () => {
		const source = readFileSync("src/components/event-dialog.tsx", "utf8");
		expect(source).toContain("Event details");
		expect(source).toContain("Schedule");
		expect(source).toContain("Recurrence");
	});
});
  • Step 2: Run test to verify it fails

Run: bun test tests/event-dialog.test.tsx Expected: FAIL because the dialog still renders as one long glass-style form without the new grouped console structure.

  • Step 3: Group dialog fields into console-style sections and demote settings styling
<DialogContent className="max-w-2xl rounded-[10px] bg-card p-0 shadow-[0_0_0_1px_rgba(0,0,0,0.08),0_12px_40px_rgba(0,0,0,0.12)]">
	<DialogHeader className="border-b border-foreground/10 px-6 py-5">
		<DialogTitle className="text-[28px] tracking-[-0.06em]">{titleText}</DialogTitle>
		<DialogDescription>{descriptionText}</DialogDescription>
	</DialogHeader>
	<form className="grid gap-6 px-6 py-5">
		<section className="grid gap-3">
			<p className="font-mono text-[11px] uppercase text-muted-foreground">Event details</p>
			{/* title, description, url, location */}
		</section>
		<section className="grid gap-3">
			<p className="font-mono text-[11px] uppercase text-muted-foreground">Schedule</p>
			{/* start, end, all-day */}
		</section>
		<section className="grid gap-3">
			<p className="font-mono text-[11px] uppercase text-muted-foreground">Recurrence</p>
			{/* recurrence picker */}
		</section>
	</form>
</DialogContent>
const settingRowClasses =
	"rounded-[10px] bg-card p-4 shadow-[0_0_0_1px_rgba(0,0,0,0.08)]";
  • Step 4: Run test to verify it passes

Run: bun test tests/event-dialog.test.tsx Expected: PASS with the grouped console sections present in source.

  • Step 5: Commit
git add src/components/event-dialog.tsx src/components/settings-panel.tsx tests/event-dialog.test.tsx
git commit -m "feat: redesign forms and settings surfaces"

Task 7: Final responsive polish and regression verification

Files:

  • Modify: src/app/page.tsx

  • Modify: src/components/ai-toolbar.tsx

  • Modify: src/components/event-card.tsx

  • Modify: src/components/settings-panel.tsx

  • Test: tests/home-page-layout.test.ts

  • Test: tests/ai-toolbar.test.ts

  • Test: tests/event-card.test.ts

  • Test: tests/event-dialog.test.tsx

  • Step 1: Add final responsive contract assertions

test("mobile layout keeps capture before timeline and keeps manual create secondary", () => {
	const source = readFileSync("src/app/page.tsx", "utf8");
	expect(source).toContain("order-1 lg:order-none");
	expect(source).toContain("Import");
	expect(source).toContain("Manual create");
});
  • Step 2: Run the focused redesign suite

Run: bun test tests/ui-shell-contract.test.ts tests/home-page-layout.test.ts tests/ai-toolbar.test.ts tests/event-card.test.ts tests/event-dialog.test.tsx Expected: PASS for the redesigned shell, AI capture, timeline cards, and dialog contracts.

  • Step 3: Run the broader regression suite

Run: bun test Expected: PASS with no regressions in existing event, AI, auth, recurrence, and utility tests.

  • Step 4: Run build verification

Run: bun run build Expected: successful Next.js production build with no type or rendering regressions.

  • Step 5: Commit
git add src/app/page.tsx src/components/ai-toolbar.tsx src/components/event-card.tsx src/components/settings-panel.tsx tests/home-page-layout.test.ts tests/ai-toolbar.test.ts tests/event-card.test.ts tests/event-dialog.test.tsx
git commit -m "feat: finalize local cal redesign"

Self-Review

  • Spec coverage: the plan covers token replacement, shared primitives, aligned desktop hierarchy, capture-first mobile flow, co-equal attachments, inline validation warnings, dialog redesign, settings demotion, and verification in both light/dark responsive contexts.
  • Placeholder scan: no TODO, TBD, or open-ended implementation placeholders remain in the tasks.
  • Type consistency: the same approved concepts are used throughout the plan: aligned AI/timeline top row, timeline-dominant desktop layout, attachment-first AI capture, inline validation warnings, and secondary manual creation.