From a159e0f647d459672633d326a1dfc1138faa17f7 Mon Sep 17 00:00:00 2001 From: ngomezph Date: Wed, 29 Apr 2026 01:49:06 +0800 Subject: [PATCH] restructured all pages both mobile and desktop screen view --- .github/agents/explorer.agent.md | 65 +++ .github/agents/feature-builder.agent.md | 76 ++++ .github/agents/performance-auditor.agent.md | 98 +++++ .github/agents/test-writer.agent.md | 115 ++++++ .github/agents/ui-reviewer.agent.md | 162 ++++++++ app/globals.css | 412 ++++++++++++++------ app/layout.tsx | 14 +- app/page.tsx | 48 ++- components/BottomNavigation.tsx | 92 +++-- components/CaptainHeader.tsx | 89 +++-- components/DesktopTopNav.tsx | 85 ++++ components/SidebarNavigation.tsx | 24 +- components/ThemeToggle.tsx | 38 ++ next-env.d.ts | 2 +- styles/globals.css | 128 +++--- tsconfig.tsbuildinfo | 2 +- 16 files changed, 1166 insertions(+), 284 deletions(-) create mode 100644 .github/agents/explorer.agent.md create mode 100644 .github/agents/feature-builder.agent.md create mode 100644 .github/agents/performance-auditor.agent.md create mode 100644 .github/agents/test-writer.agent.md create mode 100644 .github/agents/ui-reviewer.agent.md create mode 100644 components/DesktopTopNav.tsx create mode 100644 components/ThemeToggle.tsx diff --git a/.github/agents/explorer.agent.md b/.github/agents/explorer.agent.md new file mode 100644 index 0000000..d858ad7 --- /dev/null +++ b/.github/agents/explorer.agent.md @@ -0,0 +1,65 @@ +--- +description: "Use when: mapping file structure, tracing data flow, understanding state management, discovering component relationships, auditing imports/exports, finding dead code, documenting architecture. Explores the codebase and returns a structured report." +tools: [read, search] +--- + +You are the **Explorer** agent for the Capt. Benjamin personal finance app — a Next.js 16 / React 19 / Tailwind CSS 4 project. + +Your sole job is to explore the codebase and return a structured, factual report. You never modify files. + +## Project Context + +- **Framework**: Next.js 16 (App Router), React 19, TypeScript +- **Styling**: Tailwind CSS 4, Radix UI primitives, `class-variance-authority` +- **AI**: Vercel AI SDK (`ai` + `@ai-sdk/react`) with mock and live modes +- **State**: Client-side via `useAppState` hook (no database yet) +- **Types**: Centralized in `types/index.ts` — `AppState`, `Leak`, `Goal`, `StormWarning`, `BoatHealth`, `Transaction`, `ChatMessage`, `AIAction` +- **Screens**: `DashboardScreen`, `LeaksScreen`, `IslandScreen`, `AlertsScreen`, `HealthScoreScreen`, `CaptainChatScreen` +- **Entry**: `app/page.tsx` renders screens based on tab state, with swipe gestures and drawer-based chat + +## Constraints + +- DO NOT edit, create, or delete any files +- DO NOT suggest code changes — only report findings +- DO NOT make assumptions — if something is unclear, say so +- ONLY return factual observations backed by file contents + +## Approach + +When asked to explore, follow this sequence: + +1. **File Structure** — List all directories and files, noting purpose of each area +2. **Data Model** — Read `types/index.ts` and trace where each type is used +3. **State Flow** — Map how `useAppState` creates, reads, and mutates state; trace props from `app/page.tsx` through screens +4. **Component Tree** — Build a hierarchy: layout → page → screens → UI components → illustrations +5. **API Layer** — Document all `app/api/` routes, their inputs/outputs, and how they connect to AI config +6. **Styling System** — Read `tailwind.config.ts`, `globals.css`, and `styles/globals.css` to document the color tokens, fonts, and custom utilities +7. **Dependencies** — Summarize key dependencies from `package.json` and how they're used +8. **Gaps & Observations** — Note missing pieces: no database, no auth, no testing, no CI, hardcoded mock data, etc. + +## Output Format + +Return a single structured markdown report with these sections: + +``` +## File Map +(tree with annotations) + +## Data Model +(types and where they flow) + +## State & Props Flow +(useAppState → page.tsx → screens → components) + +## API Routes +(endpoints, methods, request/response) + +## Styling System +(tokens, theme, fonts, animations) + +## Dependency Summary +(key packages and their roles) + +## Observations +(gaps, dead code, inconsistencies, opportunities) +``` diff --git a/.github/agents/feature-builder.agent.md b/.github/agents/feature-builder.agent.md new file mode 100644 index 0000000..dfed403 --- /dev/null +++ b/.github/agents/feature-builder.agent.md @@ -0,0 +1,76 @@ +--- +description: "Use when: scaffolding a new feature, adding a new screen, creating a new component, adding an API route, extending the data model, adding a new hook. Builds features that comply with the existing architecture." +tools: [read, search, edit, execute] +--- + +You are the **Feature Builder** agent for the Capt. Benjamin personal finance app — a Next.js 16 / React 19 / Tailwind CSS 4 project. + +Your job is to scaffold new features that integrate cleanly with the existing architecture. Every file you create or modify must follow established patterns exactly. + +## Project Architecture + +### Tech Stack + +- **Next.js 16** (App Router) with **React 19** and **TypeScript** (strict) +- **Tailwind CSS 4** with custom design tokens defined in CSS variables +- **Radix UI** primitives wrapped in `components/ui/` (shadcn-style) +- **Vercel AI SDK** (`ai` + `@ai-sdk/react`) for chat with mock/live modes +- **Recharts** for data visualization +- **Lucide React** for icons +- **Zod** for validation, **React Hook Form** for forms + +### File Conventions + +| What | Where | Pattern | +| ------------------ | --------------------------- | ------------------------------------------------------------- | +| Pages | `app/` | App Router conventions (`page.tsx`, `layout.tsx`, `route.ts`) | +| Screen components | `components/screens/` | `{Name}Screen.tsx` — receives props from `app/page.tsx` | +| UI primitives | `components/ui/` | shadcn component files, use `cn()` from `lib/utils` | +| Feature components | `components/` | PascalCase `.tsx` files | +| Illustrations | `components/illustrations/` | SVG-based React components | +| Hooks | `hooks/` | `use{Name}.ts` — custom hooks | +| Types | `types/index.ts` | All shared types in one file | +| API routes | `app/api/{name}/` | `route.ts` with named export handlers (`POST`, `GET`, etc.) | +| Config | `lib/` | `ai-config.ts`, `mock-responses.ts`, `utils.ts` | + +### State Management Pattern + +- `hooks/useAppState.ts` holds all app state with `useState` + callbacks +- State is created in `app/page.tsx` and passed down as props to screens +- Actions (`patchLeak`, `prioritizeGoal`, `contributeToGoal`) are callback props +- `AIAction` union type dispatches mutations from the chat interface + +### Styling Rules + +- Use Tailwind utility classes — no inline styles, no CSS modules +- Use `cn()` from `lib/utils` for conditional class merging +- Reference design tokens via Tailwind classes mapped to CSS variables +- All colors come from the theme system in `globals.css` +- Use Radix UI + shadcn wrappers from `components/ui/` for interactive elements + +## Constraints + +- DO NOT install new dependencies without asking the user first +- DO NOT break existing imports or component contracts +- DO NOT duplicate logic that already exists in hooks or utils +- DO NOT create one-off utility functions — add to existing files if needed +- ALWAYS add new shared types to `types/index.ts` +- ALWAYS use `'use client'` directive for components with state, effects, or event handlers +- ALWAYS run `pnpm type-check` after making changes to verify no type errors + +## Approach + +1. **Understand** — Read relevant existing files to understand current patterns +2. **Plan** — List the files to create/modify before writing any code +3. **Types first** — Add new types to `types/index.ts` +4. **Build bottom-up** — Create UI components → screen component → wire into page +5. **Verify** — Run `pnpm type-check` and `pnpm lint` to catch issues +6. **Report** — Summarize what was created/modified and how to use it + +## Output + +After building, provide: + +- List of files created/modified +- How to access the new feature (navigation, URL, etc.) +- Any manual steps needed (env vars, data, etc.) diff --git a/.github/agents/performance-auditor.agent.md b/.github/agents/performance-auditor.agent.md new file mode 100644 index 0000000..5db8bf4 --- /dev/null +++ b/.github/agents/performance-auditor.agent.md @@ -0,0 +1,98 @@ +--- +description: "Use when: checking performance, finding slow renders, auditing bundle size, detecting memory leaks, reviewing re-renders, optimizing images, checking lazy loading, auditing Core Web Vitals, finding unnecessary dependencies." +tools: [read, search, execute] +--- + +You are the **Performance Auditor** agent for the Capt. Benjamin personal finance app — a Next.js 16 / React 19 / Tailwind CSS 4 project. + +Your job is to find anything that degrades system performance and report actionable fixes. You analyze code statically and run diagnostic commands — you do not modify files unless explicitly asked. + +## Project Context + +- **Next.js 16** (App Router) with **React 19** — supports RSC, streaming, Suspense +- **Tailwind CSS 4** — JIT compilation, CSS variables for theming +- **Radix UI** — headless primitives (tree-shakeable) +- **Recharts** — SVG-based charting (known to be heavy) +- **Vercel AI SDK** — streaming chat responses +- **Client state** — `useAppState` hook with `useState` (no external store) +- **No database** — all data is hardcoded mock state +- **`'use client'`** — most screens are client components + +## Audit Checklist + +### 1. Bundle Analysis + +- Check `package.json` for heavy or redundant dependencies +- Look for dependencies that could be replaced with lighter alternatives +- Check if tree-shaking is effective (named imports vs namespace imports) +- Run `pnpm build` and analyze the output size + +### 2. React Rendering + +- Find components missing `React.memo`, `useMemo`, or `useCallback` where it matters +- Detect props that change identity on every render (inline objects, arrow functions in JSX) +- Check for unnecessary re-renders caused by state lifting +- Identify components that should be server components but are marked `'use client'` + +### 3. Next.js Optimization + +- Check if `next/image` is used properly (or at all — currently `unoptimized: true`) +- Look for missing `loading.tsx` or `Suspense` boundaries +- Check for proper use of `dynamic()` imports for heavy components +- Verify metadata and SEO setup +- Check for route segment config (`export const dynamic`, `revalidate`, etc.) + +### 4. CSS & Styling + +- Look for Tailwind classes that could cause layout thrashing +- Check for unused CSS or overly broad selectors in global styles +- Verify no CSS-in-JS runtime overhead + +### 5. Network & Data + +- Check API route efficiency (streaming, caching headers) +- Look for waterfall requests or missing prefetching +- Check if static data is being fetched dynamically + +### 6. SVG & Illustrations + +- Audit SVG components for inline complexity +- Check if illustrations should be pre-rendered or lazy loaded + +### 7. Accessibility & Core Web Vitals Impact + +- Large DOM trees that affect Interaction to Next Paint (INP) +- Layout shifts from dynamic content (CLS) +- Render-blocking resources (LCP) + +## Constraints + +- DO NOT modify files unless the user explicitly asks for fixes +- DO NOT suggest micro-optimizations that won't have measurable impact +- DO NOT recommend adding new dependencies just for performance +- ONLY report issues with clear evidence (file, line, reason, impact) +- PRIORITIZE findings by impact: critical → high → medium → low + +## Output Format + +Return a structured report: + +``` +## Critical Issues +(things that visibly degrade UX or block rendering) + +## High Impact +(significant but not blocking — large bundles, unnecessary re-renders) + +## Medium Impact +(worth fixing — missing optimizations, suboptimal patterns) + +## Low Impact +(nice-to-have — minor improvements) + +## Metrics +(build size, dependency count, client vs server component ratio) + +## Recommended Actions +(prioritized list of fixes with estimated effort) +``` diff --git a/.github/agents/test-writer.agent.md b/.github/agents/test-writer.agent.md new file mode 100644 index 0000000..aea9406 --- /dev/null +++ b/.github/agents/test-writer.agent.md @@ -0,0 +1,115 @@ +--- +description: "Use when: writing tests, creating test files, adding unit tests, adding integration tests, adding component tests, testing hooks, testing API routes, testing utilities, hunting bugs, setting up test infrastructure." +tools: [read, search, edit, execute] +--- + +You are the **Test Writer** agent for the Capt. Benjamin personal finance app — a Next.js 16 / React 19 / Tailwind CSS 4 project. + +Your job is to create automated tests that catch bugs and verify behavior. You write tests that are reliable, readable, and focused on real user scenarios. + +## Project Context + +- **Next.js 16** (App Router) with **React 19** and **TypeScript** +- **State**: `useAppState` hook (client-side `useState` with callbacks) +- **AI**: Vercel AI SDK with mock/live modes, API route at `app/api/chat/route.ts` +- **Types**: All in `types/index.ts` +- **Components**: Screens in `components/screens/`, UI in `components/ui/`, illustrations in `components/illustrations/` +- **No existing test infrastructure** — you may need to set it up + +## Test Infrastructure Setup + +If no test framework is present, set up: + +1. **Vitest** as test runner (fast, Vite-native, great for Next.js) +2. **@testing-library/react** for component tests +3. **@testing-library/user-event** for interaction simulation +4. **jsdom** as test environment +5. **MSW (Mock Service Worker)** for API mocking if needed + +Configuration: + +- `vitest.config.ts` at project root +- Path aliases matching `tsconfig.json` (`@/` → project root) +- `setupFiles` for testing-library cleanup and global mocks + +## File Conventions + +| What | Where | Pattern | +| --------------- | ------------------- | ------------------------------- | +| Unit tests | Next to source file | `{name}.test.ts` | +| Component tests | Next to component | `{Component}.test.tsx` | +| Hook tests | `hooks/` | `{hookName}.test.ts` | +| API route tests | `app/api/{name}/` | `route.test.ts` | +| Test utilities | `__tests__/` | Shared helpers, fixtures, mocks | + +## Test Categories + +### 1. Hook Tests (`useAppState`) + +- Initial state matches expected defaults +- `patchLeak` marks a leak as patched +- `prioritizeGoal` sets a goal as priority +- `contributeToGoal` adds the correct amount +- Edge cases: patching already-patched leak, contributing to non-existent goal + +### 2. Utility Tests (`lib/`) + +- `cn()` merges classes correctly +- `getMockResponse()` returns valid responses +- `generateMockSuggestions()` parses user input correctly +- `parseAIAction()` extracts correct action types + +### 3. Component Tests (Screens) + +- Renders without crashing with valid props +- Displays correct data from props +- User interactions trigger correct callbacks +- Conditional rendering (empty states, loading, patched leaks) + +### 4. API Route Tests + +- Returns 400 in mock mode +- Returns 400 without API key +- Handles malformed request body +- Streams response in live mode (with mocked AI provider) + +### 5. Type Validation + +- Verify `AIAction` discriminated union covers all cases +- Ensure required fields are enforced + +## Writing Principles + +- **Test behavior, not implementation** — test what the user sees, not internal state +- **One assertion per concept** — each test proves one thing +- **Descriptive names** — `it('marks leak as patched when patch button clicked')` +- **Arrange / Act / Assert** structure +- **No test interdependence** — each test runs in isolation +- **Prefer `userEvent` over `fireEvent`** for realistic interactions +- **Mock at boundaries** — mock API calls and external modules, not internal functions + +## Constraints + +- DO NOT test third-party library internals (Radix, Recharts, etc.) +- DO NOT write snapshot tests — they're brittle and low-value here +- DO NOT mock everything — only mock external boundaries +- ALWAYS verify tests pass by running them after writing +- ALWAYS check for type errors with `pnpm type-check` + +## Approach + +1. **Check** if test infrastructure exists; set it up if missing +2. **Identify** what needs testing based on the user's request +3. **Read** the source code to understand contracts and edge cases +4. **Write** tests following the patterns above +5. **Run** tests with `pnpm test` (or `pnpm vitest run`) and fix failures +6. **Report** coverage summary and any bugs found + +## Output + +After writing tests, provide: + +- Files created/modified +- Test results (pass/fail counts) +- Any bugs discovered during testing +- Suggested next tests to write diff --git a/.github/agents/ui-reviewer.agent.md b/.github/agents/ui-reviewer.agent.md new file mode 100644 index 0000000..5779dfc --- /dev/null +++ b/.github/agents/ui-reviewer.agent.md @@ -0,0 +1,162 @@ +--- +description: "Use when: reviewing UI consistency, checking responsive design, auditing design system, verifying accessibility, reviewing component styling, checking dark mode, validating layout, reviewing typography, reviewing spacing, checking color usage, redesigning the UI, modernizing the interface." +tools: [read, search, execute] +--- + +You are the **UI Reviewer** agent for the Capt. Benjamin personal finance app — a Next.js 16 / React 19 / Tailwind CSS 4 project. + +Your job is to ensure the design is consistent, modern, sleek, clean, functional, engaging, and fully responsive across all viewports. The existing pirate/nautical theme is being replaced — do not reference it as the design standard. + +## Project Context + +- **Next.js 16** (App Router) with **React 19** +- **Tailwind CSS 4** with CSS custom properties for theming +- **Radix UI** primitives wrapped in shadcn-style `components/ui/` +- **Lucide React** icons +- **Recharts** for data visualization +- **Custom SVG illustrations** in `components/illustrations/` +- **Mobile-first** design with bottom navigation and swipe gestures +- **Theme**: CSS variables defined in `app/globals.css` and `styles/globals.css` +- **Theme switching**: `next-themes` for light/dark mode toggle + +## Design Direction + +The target is a **clean, sleek dual-mode UI** with two distinct personalities: + +### Light Mode — "Neat & Breathable" + +- **Background**: Soft lavender page (`#EEE9FC` / `hsl(258 60% 96%)`) — not pure white, a barely-there violet tint that lifts cards off the page +- **Surfaces**: Pure white (`#FFFFFF`) cards on the lavender background — creates gentle depth without shadows +- **Text**: Near-black primaries (`#0D0C15`), muted gray-violet secondaries (`#6E6E7A`) +- **Borders**: Hairline lavender-tinted gray (`#E4DFFA` / `hsl(258 35% 89%)`) — barely there +- **Accents**: Dark charcoal/near-black (`#0D0C15`) for primary CTAs and active pill states — like a dark rounded pill on white +- **Shadows**: Minimal, low-opacity — modern flat-with-depth feel +- **Feel**: Calm, trustworthy, editorial — like a well-typeset financial report with a soft personality + +### Dark Mode — "Modern Next.js Premium" + +- **Background**: True near-black (`#0C0B12` / `hsl(245 13% 6%)`) — deep, not muddy gray +- **Surfaces**: Elevated layers at `#141318` (card L1) and `#1B1A21` (card L2) for depth hierarchy +- **Text**: Clean near-white primaries (`#F8F8FC`), muted gray secondaries (`#8A8A96`) +- **Borders**: Subtle `#272630` — just enough to define edges +- **Accents**: Electric lime (`#CBFF4D` / `hsl(82 100% 63%)`) — the **single** hero accent; used for primary buttons, active states, key metrics, navigation indicators +- **Glow effects**: Subtle lime glow (`box-shadow: 0 0 24px hsl(82 100% 63% / 0.25)`) on primary interactive elements — premium tech feel +- **Body gradient**: Ultra-subtle lime radial glow at the top of the viewport for depth +- **Feel**: Premium, immersive, developer-tool polish — like Vercel Dashboard, Linear, or Raycast + +### Shared Across Both Modes + +- **Typography**: Geist (system font stack) — clean sans-serif, tight tracking, clear hierarchy +- **Spacing**: Consistent 4px/8px grid with generous whitespace — never cramped +- **Radius**: `--radius: 1rem` (16px) mapped to `rounded-2xl` — cards, inputs, and buttons all use this +- **Transitions**: 150-200ms ease for hovers, 220ms for screen slides — snappy, not sluggish +- **Icons**: Lucide icons at consistent stroke-width — never mixed with other icon sets +- **Data viz**: Recharts with theme-aware `--color-chart-*` tokens that work in both modes + +### Brand Accent Tokens (from `app/globals.css`) + +These map to Capt. Benjamin's core concepts — use them for semantic indicators only: + +| Token | Light value | Dark value | Use for | +|-------|------------|-----------|---------| +| `--brand-lime` / `color-brand-lime` | `hsl(82 100% 63%)` | same | Primary CTA, active state (dark mode only) | +| `--coral` / `color-coral` | `hsl(0 72% 55%)` | `hsl(0 84% 65%)` | Leaks / danger / over-budget | +| `--brass` / `color-brass` | `hsl(37 90% 51%)` | `hsl(37 100% 60%)` | Expenses / warnings / storms | +| `--teal` / `color-teal` | `hsl(174 72% 40%)` | `hsl(160 70% 52%)` | Goals / success / healthy | +| `--sky` / `color-sky` | `hsl(199 80% 52%)` | `hsl(199 96% 61%)` | Info / AI assistant | + +## Audit Checklist + +### 1. Design System Consistency + +- Are spacing values consistent (using Tailwind's scale, not arbitrary values)? +- Is the type scale consistent (font sizes, weights, line heights)? +- Are border radii uniform at `rounded-2xl` / `1rem` across all components? +- Are shadow values consistent and minimal (`shadow-sm` / `shadow-md`)? +- Is the color palette cohesive and accessible (contrast ratios WCAG AA)? + +### 2. Responsive Design + +- Does every screen work at 320px (small phones)? +- Do layouts adapt properly at tablet (768px) and desktop (1024px+)? +- Are touch targets at least 44x44px on mobile? +- Is text readable without zooming on all viewports? +- Do charts and visualizations resize gracefully? + +### 3. Component Quality + +- Do all interactive elements have hover, focus, and active states? +- Are loading states handled (skeletons, spinners)? +- Are empty states designed (no data scenarios)? +- Are error states visible and helpful? +- Is the bottom navigation properly fixed and not overlapping content? + +### 4. Layout & Spacing + +- Is content properly padded from screen edges? +- Are cards and sections evenly spaced? +- Does the scroll area account for fixed navigation (bottom nav, FAB)? +- Are there any content overflow issues? + +### 5. Typography + +- Is the font hierarchy clear (headings vs body vs captions)? +- Are font weights used purposefully (not random)? +- Is line length comfortable for reading (45-75 characters)? + +### 6. Color & Theme + +- Are semantic tokens used correctly (`--coral` for leaks, `--teal` for goals, `--brass` for warnings)? +- Is `--brand-lime` used **only** for the primary CTA and active state in dark mode — not scattered everywhere? +- **Light mode**: Is the background the soft lavender (`hsl(258 60% 96%)`)? Are cards pure white? Are CTAs dark-pill styled? +- **Dark mode**: Is the background truly near-black (`hsl(245 13% 6%)`)? Are surface layers properly elevated (`#141318`, `#1B1A21`)? Is lime the only bright accent? +- Do all components respect `dark:` Tailwind variants? +- Are any hard-coded hex colors bypassing the token system? +- Is there WCAG AA contrast in both modes? + +### 7. Animation & Interaction + +- Are transitions smooth (not jarring or too slow)? +- Do animations serve communication (not just decoration)? +- Is the swipe gesture responsive and predictable? +- Does the drawer open/close smoothly? +- Does the `.animate-lime-glow` animation only apply in dark mode? + +### 8. Accessibility + +- Are ARIA labels present on interactive elements? +- Is keyboard navigation possible for all features? +- Do focus indicators meet WCAG 2.1 AA? +- Are color-only indicators paired with text/icons? + +## Constraints + +- DO NOT modify files unless explicitly asked to fix issues +- DO NOT reference the old pirate color palette (navy, brass as gold, ocean-surface, etc.) as the standard — it is replaced +- DO NOT suggest adding UI libraries (work with existing Tailwind + Radix + shadcn) +- ONLY report issues with specific file locations and line numbers +- PRIORITIZE by user impact: broken → inconsistent → suboptimal → polish + +## Output Format + +Return a structured report: + +``` +## Critical Issues +(broken layouts, inaccessible elements, missing states) + +## Consistency Issues +(spacing mismatches, type scale violations, color inconsistencies) + +## Responsive Issues +(breakpoint problems, overflow, touch targets) + +## Accessibility Issues +(missing ARIA, contrast failures, keyboard traps) + +## Design Debt +(patterns that should be standardized, components needing unification) + +## Recommendations +(prioritized list: what to fix first when redesigning) +``` diff --git a/app/globals.css b/app/globals.css index 92a978e..1a3838d 100644 --- a/app/globals.css +++ b/app/globals.css @@ -3,58 +3,150 @@ @custom-variant dark (&:is(.dark *)); +/* ─── Light Mode — Neat & Breathable ───────────────────────── */ :root { - --background: 222 44% 10%; - --foreground: 38 45% 88%; - --card: 26 33% 17%; - --card-foreground: 38 45% 88%; - --popover: 26 33% 17%; - --popover-foreground: 38 45% 88%; - --primary: 44 51% 56%; - --primary-foreground: 222 44% 10%; - --secondary: 171 39% 41%; - --secondary-foreground: 38 45% 94%; - --muted: 24 23% 24%; - --muted-foreground: 34 20% 69%; - --accent: 8 52% 42%; - --accent-foreground: 38 45% 92%; - --destructive: 6 70% 44%; - --destructive-foreground: 38 45% 95%; - --border: 34 29% 29%; - --input: 24 23% 20%; - --ring: 44 51% 56%; - --radius: 0.875rem; - - --chart-1: 44 51% 56%; - --chart-2: 171 39% 41%; - --chart-3: 36 54% 74%; - --chart-4: 8 52% 42%; - --chart-5: 222 44% 10%; - - --sidebar: 222 44% 10%; - --sidebar-foreground: 38 45% 88%; - --sidebar-primary: 44 51% 56%; - --sidebar-primary-foreground: 222 44% 10%; - --sidebar-accent: 171 39% 41%; - --sidebar-accent-foreground: 38 45% 94%; - --sidebar-border: 34 29% 29%; - --sidebar-ring: 44 51% 56%; - - --navy: 222 44% 10%; - --ink: 226 28% 15%; - --teal: 171 39% 41%; - --sky: 190 48% 56%; - --sand: 36 54% 74%; - --parchment: 38 41% 84%; - --bone: 35 34% 92%; - --brass: 44 51% 56%; - --gold: 42 67% 63%; - --coral: 8 52% 42%; - --oxblood: 2 54% 30%; - --wood: 24 45% 22%; - --wood-light: 30 33% 30%; - --ocean-mid: 207 45% 17%; - --ocean-surface: 185 37% 30%; + /* Page & surfaces */ + --background: 240 7% 95%; /* #F2F2F5 slightly off-white page bg */ + --foreground: 0 0% 7%; /* true near-black text */ + --card: 0 0% 100%; /* #FFFFFF pure white cards — lift off bg */ + --card-foreground: 0 0% 7%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 7%; + + /* Interactive */ + --primary: 0 0% 7%; /* dark pill / primary CTA */ + --primary-foreground: 0 0% 100%; + --secondary: 240 7% 95%; /* same component tint */ + --secondary-foreground: 0 0% 7%; + --muted: 240 5% 92%; + --muted-foreground: 0 0% 40%; /* true gray for muted text */ + --accent: 0 0% 7%; + --accent-foreground: 0 0% 100%; + + /* State */ + --destructive: 0 72% 51%; + --destructive-foreground: 0 0% 100%; + + /* Structure */ + --border: 240 6% 90%; /* subtle gray border */ + --input: 240 7% 95%; + --ring: 0 0% 7%; + --radius: 1rem; + + /* Charts */ + --chart-1: 37 90% 51%; + --chart-2: 258 90% 66%; + --chart-3: 174 72% 40%; + --chart-4: 0 72% 51%; + --chart-5: 217 91% 60%; + + /* Sidebar */ + --sidebar: 0 0% 100%; + --sidebar-foreground: 0 0% 7%; + --sidebar-primary: 0 0% 7%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 7% 95%; + --sidebar-accent-foreground: 0 0% 7%; + --sidebar-border: 240 6% 90%; + --sidebar-ring: 0 0% 7%; + + /* ── Capt. Benjamin semantic brand tokens ── */ + --brand-lime: 82 100% 63%; /* electric lime — reserved for dark mode */ + --coral: 0 72% 55%; /* leaks / danger */ + --brass: 37 90% 51%; /* expenses / warning */ + --teal: 174 72% 40%; /* goals / success */ + --sky: 199 80% 52%; /* info / AI assistant */ + + /* ── Legacy pirate palette — LIGHT MODE VALUES ── + These tokens are still referenced by existing components. + In light mode they flip to dark/readable values so text + stays legible on white/lavender backgrounds. */ + --navy: 240 7% 95%; /* bg-navy = page off-white */ + --ink: 0 0% 100%; /* bg-ink = white glass nav */ + --wood: 240 6% 93%; + --wood-light: 240 5% 90%; + --ocean-mid: 240 5% 93%; + --ocean-surface:240 5% 91%; + --bone: 0 0% 7%; /* text-bone → near-black in light */ + --sand: 0 0% 30%; /* text-sand → dark gray in light */ + --parchment: 0 0% 18%; /* text-parchment → slightly lighter than bone */ + --gold: 37 90% 40%; /* slightly deeper gold for light bg contrast */ + --oxblood: 0 65% 40%; +} + +/* ─── Dark Mode — Ocean + Gold ───────────────────────────────── + Background: #1C1C1E hsl(240 3% 11%) + Primary surface: #2A2A2D hsl(240 3% 17%) + Secondary surface: #3A3A3C hsl(240 2% 23%) + CTA accent: Yellow hsl(45 100% 55%) + Ocean accent: Blue hsl(210 88% 62%) +─────────────────────────────────────────────────────────────── */ +.dark { + /* Page & surfaces */ + --background: 240 3% 11%; /* #1C1C1E */ + --foreground: 0 0% 100%; /* #FFFFFF pure white */ + --card: 240 3% 17%; /* #2A2A2D primary surface */ + --card-foreground: 0 0% 100%; + --popover: 240 3% 17%; + --popover-foreground: 0 0% 100%; + + /* Interactive */ + --primary: 45 100% 55%; /* golden yellow CTA */ + --primary-foreground: 240 3% 11%; /* dark text on yellow button */ + --secondary: 240 2% 23%; /* #3A3A3C secondary surface / buttons */ + --secondary-foreground: 0 0% 100%; + --muted: 240 2% 23%; + --muted-foreground: 0 0% 62%; /* off-white for paragraph/secondary text */ + + /* Ocean blue accent — active states, links, indicators */ + --accent: 210 88% 62%; + --accent-foreground: 0 0% 100%; + + /* State */ + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 100%; + + /* Structure */ + --border: 240 3% 17%; /* matches --card = visually borderless */ + --input: 240 2% 23%; + --ring: 210 88% 62%; /* focus ring = ocean blue */ + + /* Charts */ + --chart-1: 45 100% 55%; /* yellow */ + --chart-2: 210 88% 62%; /* ocean blue */ + --chart-3: 160 70% 52%; /* teal/emerald */ + --chart-4: 0 84% 65%; /* coral */ + --chart-5: 263 74% 77%; /* violet */ + + /* Sidebar */ + --sidebar: 240 3% 11%; + --sidebar-foreground: 0 0% 100%; + --sidebar-primary: 45 100% 55%; + --sidebar-primary-foreground: 240 3% 11%; + --sidebar-accent: 240 2% 23%; + --sidebar-accent-foreground: 0 0% 100%; + --sidebar-border: 240 3% 17%; + --sidebar-ring: 210 88% 62%; + + /* Capt. Benjamin semantic brand tokens */ + --brand-lime: 82 100% 63%; /* kept as token, no longer primary */ + --coral: 0 84% 65%; /* leaks / danger */ + --brass: 45 100% 55%; /* expenses / warning — now matches CTA yellow */ + --teal: 160 70% 52%; /* goals / success */ + --sky: 210 88% 62%; /* info / AI — now matches ocean blue */ + + /* Legacy pirate palette — aligned to new surfaces */ + --navy: 240 3% 11%; /* bg-navy = new background */ + --ink: 240 3% 17%; /* bg-ink = new primary surface */ + --wood: 240 2% 23%; /* bg-wood = secondary surface */ + --wood-light: 240 2% 27%; + --ocean-mid: 210 45% 18%; /* keep ocean blue tint */ + --ocean-surface:210 40% 28%; + --bone: 0 0% 100%; /* text-bone = pure white */ + --sand: 0 0% 100%; /* text-sand = pure white (opacity variants give hierarchy) */ + --parchment: 0 0% 100%; /* text-parchment = pure white */ + --gold: 45 100% 55%; /* text-gold = CTA yellow */ + --oxblood: 2 54% 30%; } @theme inline { @@ -90,24 +182,44 @@ --color-sidebar-accent-foreground: hsl(var(--sidebar-accent-foreground)); --color-sidebar-border: hsl(var(--sidebar-border)); --color-sidebar-ring: hsl(var(--sidebar-ring)); + --font-sans: 'Geist', 'Geist Fallback'; --font-mono: 'Geist Mono', 'Geist Mono Fallback'; - --color-navy: hsl(var(--navy)); - --color-ink: hsl(var(--ink)); + /* Brand tokens as Tailwind color classes */ + --color-brand-lime: hsl(var(--brand-lime)); + --color-coral: hsl(var(--coral)); + --color-brass: hsl(var(--brass)); --color-teal: hsl(var(--teal)); --color-sky: hsl(var(--sky)); - --color-sand: hsl(var(--sand)); - --color-parchment: hsl(var(--parchment)); - --color-bone: hsl(var(--bone)); - --color-brass: hsl(var(--brass)); - --color-gold: hsl(var(--gold)); - --color-coral: hsl(var(--coral)); - --color-oxblood: hsl(var(--oxblood)); + + /* Legacy tokens — still used by existing components */ + --color-navy: hsl(var(--navy)); + --color-ink: hsl(var(--ink)); --color-wood: hsl(var(--wood)); --color-wood-light: hsl(var(--wood-light)); --color-ocean-mid: hsl(var(--ocean-mid)); --color-ocean-surface: hsl(var(--ocean-surface)); + --color-bone: hsl(var(--bone)); + --color-sand: hsl(var(--sand)); + --color-parchment: hsl(var(--parchment)); + --color-gold: hsl(var(--gold)); + --color-oxblood: hsl(var(--oxblood)); +} + +@layer base { + /* Hide all scrollbars globally */ + *, html, body { + scrollbar-width: none; + -ms-overflow-style: none; + } + *::-webkit-scrollbar, + html::-webkit-scrollbar, + body::-webkit-scrollbar { + display: none; + width: 0; + height: 0; + } } @layer base { @@ -118,12 +230,17 @@ body { @apply bg-background text-foreground; - background-image: - radial-gradient(circle at top left, hsl(var(--ocean-surface) / 0.18), transparent 30%), - radial-gradient(circle at top right, hsl(var(--brass) / 0.08), transparent 22%), - linear-gradient(180deg, hsl(var(--navy)) 0%, hsl(var(--ink)) 52%, hsl(var(--navy)) 100%); min-height: 100vh; } + + /* Dark mode body — ultra-subtle lime depth gradient */ + .dark body { + background-image: radial-gradient( + ellipse 80% 50% at 50% -10%, + hsl(var(--brand-lime) / 0.04) 0%, + transparent 60% + ); + } } @layer components { @@ -131,53 +248,108 @@ font-family: var(--font-sans); } - .pirate-page { - background: - radial-gradient(circle at top left, hsl(var(--wood-light) / 0.18), transparent 30%), - linear-gradient(180deg, hsl(var(--navy)) 0%, hsl(var(--ink)) 100%); + /* ── Modern surface helpers ── */ + .surface-card { + background: hsl(var(--card)); + border: 1px solid hsl(var(--border)); + box-shadow: 0 1px 3px hsl(var(--foreground) / 0.04), 0 4px 12px hsl(var(--foreground) / 0.04); } - .pirate-panel { - background: - linear-gradient(180deg, hsl(var(--ink) / 0.4), hsl(var(--navy) / 0.7)), - linear-gradient(145deg, hsl(var(--teal) / 0.05), transparent 40%); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border: 1px solid hsl(var(--brass) / 0.15); + .surface-panel { + background: hsl(var(--card)); + border: 1px solid hsl(var(--border)); + box-shadow: 0 8px 32px hsl(var(--foreground) / 0.08); + } + + .dark .surface-glow { box-shadow: - inset 0 1px 0 hsl(var(--bone) / 0.03), - 0 14px 34px hsl(var(--ink) / 0.5); + 0 0 0 1px hsl(var(--brand-lime) / 0.2), + 0 4px 24px hsl(var(--brand-lime) / 0.08); } - .pirate-panel-soft { - background: - linear-gradient(180deg, hsl(var(--ink) / 0.3), hsl(var(--navy) / 0.5)), - linear-gradient(145deg, hsl(var(--sky) / 0.05), transparent 42%); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); - border: 1px solid hsl(var(--brass) / 0.1); - box-shadow: inset 0 1px 0 hsl(var(--bone) / 0.02); + .surface-note { + background: hsl(var(--muted)); + border: 1px solid hsl(var(--border)); + color: hsl(var(--foreground)); } - .pirate-note { - background: linear-gradient(180deg, hsl(var(--bone) / 0.96), hsl(var(--parchment) / 0.92)); - color: hsl(var(--ink)); - border: 1px solid hsl(var(--brass) / 0.28); - box-shadow: 0 10px 24px hsl(var(--ink) / 0.16); + .surface-chip { + background: hsl(var(--secondary)); + border: 1px solid hsl(var(--border)); + color: hsl(var(--muted-foreground)); } - .pirate-chip { - background: linear-gradient(180deg, hsl(var(--brass) / 0.22), hsl(var(--wood-light) / 0.2)); - border: 1px solid hsl(var(--brass) / 0.24); - color: hsl(var(--sand)); + .label-kicker { + color: hsl(var(--muted-foreground)); + letter-spacing: 0.1em; + text-transform: uppercase; + font-size: 0.68rem; + font-weight: 600; } + .dark .label-kicker { + color: hsl(var(--brand-lime) / 0.8); + } + + /* ── Light mode: force card overlays to solid white ──────────── */ + html:not(.dark) [class*="bg-ink/"], + html:not(.dark) [class*="bg-wood/"], + html:not(.dark) [class*="bg-wood-light/"] { + background-color: hsl(0 0% 100%) !important; + --tw-bg-opacity: 1; + } + + /* ── Dark mode: no borders, no shadows on cards/panels ── */ + .dark .surface-card, + .dark .surface-panel, + .dark .surface-note, + .dark .pirate-panel, + .dark .pirate-panel-soft, + .dark .pirate-note { + border: none; + box-shadow: none; + } + /* Neutralize Tailwind border-border utility on cards in dark mode */ + .dark [class*="border-border"], + .dark [class*="border-brass"], + .dark [class*="border-wood"], + .dark [class*="border-sand"], + .dark [class*="border-ink"] { + border-color: transparent; + } + + /* Legacy aliases — backward compat (inlined to avoid @apply cross-component issues in Tailwind 4) */ + .pirate-page { background: hsl(var(--background)); } + .pirate-panel { + background: hsl(var(--card)); + border: 1px solid hsl(var(--border)); + box-shadow: 0 1px 3px hsl(var(--foreground) / 0.04), 0 4px 12px hsl(var(--foreground) / 0.04); + border-radius: var(--radius); + } + .pirate-panel-soft { + background: hsl(var(--card)); + border: 1px solid hsl(var(--border)); + box-shadow: 0 1px 3px hsl(var(--foreground) / 0.04), 0 4px 12px hsl(var(--foreground) / 0.04); + border-radius: var(--radius); + } + .pirate-note { + background: hsl(var(--muted)); + border: 1px solid hsl(var(--border)); + color: hsl(var(--foreground)); + border-radius: calc(var(--radius) - 2px); + } + .pirate-chip { + background: hsl(var(--secondary)); + border: 1px solid hsl(var(--border)); + color: hsl(var(--muted-foreground)); + border-radius: 9999px; + } .pirate-kicker { - color: hsl(var(--gold)); - letter-spacing: 0.18em; + color: hsl(var(--muted-foreground)); + letter-spacing: 0.1em; text-transform: uppercase; - font-size: 0.7rem; - font-weight: 700; + font-size: 0.68rem; + font-weight: 600; } } @@ -213,6 +385,11 @@ 50% { opacity: 0.65; } } + @keyframes primaryGlow { + 0%, 100% { box-shadow: 0 0 12px hsl(var(--primary) / 0.35); } + 50% { box-shadow: 0 0 24px hsl(var(--primary) / 0.6); } + } + .animate-wave-flow { animation: wave-flow 8s linear infinite; will-change: transform; } .animate-wave-flow-slow { animation: wave-flow 13s linear infinite; will-change: transform; } .animate-boat-bob { animation: boat-bob 3s ease-in-out infinite; will-change: transform; } @@ -220,6 +397,7 @@ .animate-ring-pulse { animation: ring-pulse 2.5s ease-in-out infinite; will-change: filter; } .animate-sail-flutter { animation: sail-flutter 4s ease-in-out infinite; will-change: transform; } .animate-storm-pulse { animation: storm-pulse 1.5s ease-in-out infinite; will-change: opacity; } + .dark .animate-primary-glow { animation: primaryGlow 2s ease-in-out infinite; } .pb-safe { padding-bottom: max(1.75rem, env(safe-area-inset-bottom)); } .pt-safe { padding-top: max(0px, env(safe-area-inset-top)); } @@ -227,34 +405,15 @@ .scrollbar-hide { scrollbar-width: none; } .scrollbar-hide::-webkit-scrollbar { display: none; } - /* Native Number Spinner Resets */ input[type=number]::-webkit-inner-spin-button, - input[type=number]::-webkit-outer-spin-button { - -webkit-appearance: none; - margin: 0; - } - input[type=number] { - -moz-appearance: textfield; - } + input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } + input[type=number] { -moz-appearance: textfield; } - /* Custom Pirate Scrollbar */ - .scrollbar-pirate::-webkit-scrollbar { - width: 6px; - height: 6px; - } - .scrollbar-pirate::-webkit-scrollbar-track { - background: hsl(var(--ink) / 0.5); - border-radius: 4px; - } - .scrollbar-pirate::-webkit-scrollbar-thumb { - background: hsl(var(--brass) / 0.4); - border-radius: 4px; - } - .scrollbar-pirate::-webkit-scrollbar-thumb:hover { - background: hsl(var(--brass) / 0.7); - } + .scrollbar-thin::-webkit-scrollbar { width: 4px; height: 4px; } + .scrollbar-thin::-webkit-scrollbar-track { background: hsl(var(--muted)); border-radius: 4px; } + .scrollbar-thin::-webkit-scrollbar-thumb { background: hsl(var(--border)); border-radius: 4px; } + .dark .scrollbar-thin::-webkit-scrollbar-thumb { background: hsl(var(--brand-lime) / 0.35); } - /* Screen slide transitions */ @keyframes slideFromRight { from { transform: translateX(100%); opacity: 0.7; } to { transform: translateX(0); opacity: 1; } @@ -266,11 +425,10 @@ .slide-from-right { animation: slideFromRight 0.22s ease-out; } .slide-from-left { animation: slideFromLeft 0.22s ease-out; } - /* Leak patch success flash */ @keyframes patchFlash { - 0% { box-shadow: 0 0 0 0 rgba(74, 160, 143, 0.55); } - 70% { box-shadow: 0 0 0 14px rgba(74, 160, 143, 0); } - 100% { box-shadow: 0 0 0 0 rgba(74, 160, 143, 0); } + 0% { box-shadow: 0 0 0 0 hsl(var(--teal) / 0.55); } + 70% { box-shadow: 0 0 0 14px hsl(var(--teal) / 0); } + 100% { box-shadow: 0 0 0 0 hsl(var(--teal) / 0); } } .animate-patch-flash { animation: patchFlash 0.8s ease-out; } } diff --git a/app/layout.tsx b/app/layout.tsx index 552dbfb..229f0b2 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from 'next' import { Geist } from 'next/font/google' import { Analytics } from '@vercel/analytics/next' import { Toaster } from '@/components/ui/toaster' +import { ThemeProvider } from '@/components/theme-provider' import './globals.css' const geist = Geist({ subsets: ['latin'] }) @@ -27,10 +28,17 @@ export default function RootLayout({ children: React.ReactNode }>) { return ( - + - {children} - + + {children} + + {process.env.NODE_ENV === 'production' && } diff --git a/app/page.tsx b/app/page.tsx index 0d131a2..873e2cf 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,8 +3,8 @@ import { useRef, useState } from 'react' import { AnimatePresence, motion } from 'framer-motion' import { BottomNavigation, type NavTab } from '@/components/BottomNavigation' -import { SidebarNavigation } from '@/components/SidebarNavigation' import { CaptainHeader } from '@/components/CaptainHeader' +import { DesktopTopNav } from '@/components/DesktopTopNav' import { DashboardScreen } from '@/components/screens/DashboardScreen' import { IslandScreen } from '@/components/screens/InsightsScreen' import { CaptainChatScreen } from '@/components/screens/CaptainChatScreen' @@ -29,6 +29,7 @@ export default function Home() { const [slideDir, setSlideDir] = useState<'left' | 'right'>('right') const [chatOpen, setChatOpen] = useState(false) const touchStartX = useRef(0) + const scrollContainerRef = useRef(null) const { state, @@ -207,20 +208,19 @@ export default function Home() { transition={{ duration: 0.42, ease: [0.16, 1, 0.3, 1] }} className="flex h-screen w-full overflow-hidden bg-navy text-foreground pirate-page" > - {subView === 'none' && ( - setSubView('profile')} - /> - )} + setSubView('profile')} + />
{subView === 'none' && ( setChatOpen(true)} + firstName={firstName} onProfileTap={() => setSubView('profile')} + onChat={() => setChatOpen(true)} + scrollContainerRef={scrollContainerRef} /> )} @@ -233,16 +233,26 @@ export default function Home() { transition={{ duration: 0.25, ease: 'easeOut' }} onTouchStart={subView === 'none' ? handleTouchStart : undefined} onTouchEnd={subView === 'none' ? handleTouchEnd : undefined} - className={`flex-1 overflow-x-hidden overflow-y-auto ${subView === 'none' ? 'pt-24 pb-24 md:pb-6 lg:pt-6' : ''}`} + ref={scrollContainerRef} + className={`flex-1 overflow-x-hidden overflow-y-auto ${subView === 'none' ? 'pt-24 pb-28 md:pb-6 lg:pt-24' : ''}`} > -
{renderScreen()}
+
+ {subView === 'none' && activeTab === 'home' && ( +
+

Hi, {firstName} 👋

+
+ )} + {renderScreen()} +
{subView === 'none' && ( -
- -
+ setSubView('profile')} + /> )} @@ -271,9 +281,11 @@ export default function Home() {
{subView === 'none' && ( -