From 45ca8fbbc3de854e05d24370800d65d59495ffd9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 14 Aug 2026 18:37:27 +0000 Subject: [PATCH] Rebuild landing previews from the real app screens. The marketing shots were a separate miniature UI. They now compose the same shell, panels, metrics, charts, and ledger as the signed-in app, so the landing page shows those screens as they actually look. Co-authored-by: danielxu0307 --- src/components/landing/landing.css | 719 +----------- src/components/landing/mocks.tsx | 1672 ++++++++++++++++++++-------- 2 files changed, 1260 insertions(+), 1131 deletions(-) diff --git a/src/components/landing/landing.css b/src/components/landing/landing.css index 151e69d..92183d8 100644 --- a/src/components/landing/landing.css +++ b/src/components/landing/landing.css @@ -378,13 +378,8 @@ pointer-events: none; } -.lp-app { - --accent: var(--lagoon-deep); - --accent-soft: color-mix(in oklab, var(--lagoon) 18%, var(--foam)); - --line: rgba(28, 25, 23, 0.1); - --line-soft: rgba(28, 25, 23, 0.06); - display: grid; - grid-template-columns: 148px minmax(0, 1fr); +.lp-app, +.lp-m { border: 1px solid var(--border); border-radius: 12px 12px 0 0; background: var(--background); @@ -392,298 +387,93 @@ 0 1px 0 var(--inset-glint) inset, 0 30px 60px rgba(28, 25, 23, 0.13); overflow: hidden; - font-size: 12px; text-align: left; - animation: lp-rise 700ms var(--e) both; + color: var(--foreground); + pointer-events: none; } -/* On a phone the mock's sidebar would eat half the frame, so the replica drops - to its main column. */ -@media (width < 48rem) { - .lp-app { - grid-template-columns: minmax(0, 1fr); - } - - .lp-app-side { - display: none; - } +.lp-app { + animation: lp-rise 700ms var(--e) both; +} - .lp-app-figure { - font-size: 24px; - } +.lp-m { + border-radius: 11px 11px 0 0; + box-shadow: + 0 1px 0 var(--inset-glint) inset, + 0 22px 44px rgba(28, 25, 23, 0.14); +} +@media (width < 48rem) { .lp-stage { padding: 20px 20px 0; } -} - -.lp-app-side { - padding: 14px 10px; - border-right: 1px solid var(--line-soft); - background: var(--foam); -} - -.lp-app-brand { - padding: 0 8px 14px; - font-family: 'Instrument Sans', sans-serif; - font-size: 15px; - font-weight: 600; - letter-spacing: -0.02em; - color: var(--sea-ink); -} - -.lp-app-nav { - display: flex; - flex-direction: column; - gap: 1px; -} - -.lp-app-nav span { - display: flex; - align-items: center; - gap: 8px; - padding: 6px 8px; - border-radius: 6px; - color: var(--muted-foreground); - font-size: 11.5px; - font-weight: 500; -} - -.lp-app-nav span[data-active='true'] { - background: rgba(28, 25, 23, 0.07); - color: var(--sea-ink); -} - -.lp-app-nav i { - width: 6px; - height: 6px; - border-radius: 999px; - background: currentColor; - opacity: 0.4; -} - -.lp-app-main { - padding: 16px 18px 22px; - min-width: 0; -} - -.lp-app-hero { - display: flex; - flex-wrap: wrap; - align-items: flex-end; - justify-content: space-between; - gap: 14px; - padding-bottom: 16px; -} - -.lp-app-kicker { - font-size: 9.5px; - font-weight: 600; - letter-spacing: 0.14em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.lp-app-figure { - margin-top: 4px; - font-size: 30px; - font-weight: 600; - line-height: 1; - letter-spacing: -0.035em; - font-variant-numeric: tabular-nums; - color: var(--sea-ink); -} -.lp-app-delta { - margin-top: 6px; - font-size: 11px; - color: var(--palm); -} - -.lp-spark { - width: 190px; - height: 52px; -} - -.lp-spark path { - fill: none; - stroke: var(--accent); - stroke-width: 1.6; - stroke-linecap: round; - stroke-dasharray: 320; - stroke-dashoffset: 320; - animation: lp-draw 1.5s var(--e) 250ms both; -} - -.lp-app-panels { - display: grid; - gap: 10px; -} - -@media (width >= 40rem) { - .lp-app-panels { - grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); + .lp-app .lp-preview-side { + display: none; } } -.lp-app-col { - display: flex; - flex-direction: column; - gap: 10px; - min-width: 0; -} - -.lp-app-panel { - border: 1px solid var(--border); - border-radius: 9px; - background: var(--card); - overflow: hidden; -} - -.lp-app-panel-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - padding: 8px 10px; - border-bottom: 1px solid rgba(28, 25, 23, 0.07); - font-size: 11.5px; - font-weight: 600; - color: var(--sea-ink); -} - -.lp-app-panel-head em { - font-style: normal; - font-weight: 500; - color: var(--muted-foreground); -} - -.lp-app-panel-body { - padding: 10px; -} - -.lp-pace { - display: flex; - align-items: baseline; - justify-content: space-between; - font-size: 11px; - color: var(--muted-foreground); +/* The shot is the real app chrome, isolated so landing type and gutters + cannot leak in. Container queries size the dashboard split to the frame, + not the window. */ +.lp-preview { + --page-gutter: 1rem; + --tabbar-height: 0px; + --app-header-height: 3rem; + container-type: inline-size; + container-name: lp-preview; + background: var(--background); + color: var(--foreground); + font-size: 16px; + line-height: 1.5; } -.lp-pace b { - font-size: 17px; - font-weight: 600; - letter-spacing: -0.02em; - color: var(--sea-ink); - font-variant-numeric: tabular-nums; +.lp-m .lp-preview { + --page-gutter: 0.75rem; } -.lp-pace-track { +.lp-preview .app-shell-header { position: relative; - margin-top: 8px; - height: 7px; - border-radius: 999px; - background: rgba(28, 25, 23, 0.08); - overflow: hidden; -} - -.lp-pace-track i { - display: block; - width: 0; - height: 100%; - border-radius: 999px; - background: var(--accent); - animation: lp-fill 1.1s var(--e) 420ms both; -} - -.lp-donut-row { - display: flex; - align-items: center; - gap: 12px; + top: auto; } -.lp-donut { - flex: 0 0 auto; - width: 66px; - height: 66px; - border-radius: 999px; - background: conic-gradient( - var(--chart-1) 0 34%, - var(--chart-2) 34% 58%, - var(--chart-3) 58% 76%, - var(--chart-5) 76% 89%, - color-mix(in oklab, var(--sea-ink) 14%, transparent) 89% 100% - ); - mask: radial-gradient(circle, transparent 56%, #000 57%); +.lp-preview .app-shell-main { + padding-bottom: 1.5rem; } -.lp-legend { +.lp-preview-shell { display: flex; - flex-direction: column; - gap: 5px; - min-width: 0; - flex: 1 1 auto; -} - -.lp-legend div { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - font-size: 11px; - color: var(--sea-ink-soft); -} - -.lp-legend b { - font-weight: 500; - color: var(--sea-ink); - font-variant-numeric: tabular-nums; -} - -.lp-legend i { - width: 7px; - height: 7px; - border-radius: 999px; - background: var(--c); - margin-right: 6px; + min-height: 560px; + background: var(--background); } -.lp-legend span { +.lp-preview-side { + position: relative; display: flex; - align-items: center; + width: 212px; min-width: 0; -} - -.lp-txns { - display: flex; + flex-shrink: 0; flex-direction: column; + overflow: hidden; + background: var(--background); } -.lp-txn { - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - padding: 7px 10px; - border-bottom: 1px solid rgba(28, 25, 23, 0.06); - font-size: 11.5px; -} - -.lp-txn:last-child { - border-bottom: 0; +.lp-preview-split { + display: grid; + gap: 0.75rem; } -.lp-txn-cat { - padding: 2px 7px; - border-radius: 999px; - background: rgba(28, 25, 23, 0.06); - font-size: 10px; - color: var(--muted-foreground); +@container lp-preview (min-width: 40rem) { + .lp-preview-split { + grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); + gap: 1rem; + } } -.lp-txn-amt { - font-variant-numeric: tabular-nums; - color: var(--sea-ink); +@container lp-preview (max-width: 28rem) { + .lp-preview-debt-share { + display: none; + } } /* A few degrees of tilt reads as a product shot rather than a screenshot pasted @@ -700,21 +490,6 @@ } } -@keyframes lp-draw { - to { - stroke-dashoffset: 0; - } -} - -@keyframes lp-fill { - from { - width: 0; - } - to { - width: var(--w, 62%); - } -} - /* Section furniture ── */ .lp-section { padding-block: 88px 96px; @@ -884,34 +659,10 @@ pointer-events: none; } -/* A phone has about 290px inside the frame. Rather than squeeze the desktop - replica into it — which turned merchant names into two-letter stubs — the - replicas shed their middle column and show the shape the app actually takes - on a phone: the name leads, the amount closes. */ @media (width < 48rem) { .lp-feature-visual { padding: 20px 20px 0; } - - .lp-m-next, - .lp-m-note, - .lp-m-mini { - display: none; - } - - .lp-m-rec .lp-m-name { - flex-wrap: wrap; - gap: 2px 7px; - } - - .lp-m-learn-row { - flex-wrap: wrap; - gap: 6px; - } - - .lp-m-learn-row em { - flex-basis: 100%; - } } .lp-feature-visual[data-tone='mint'] { @@ -994,360 +745,6 @@ linear-gradient(160deg, #d8e8e2, var(--sand)); } -/* Screen replicas inside the frames ── */ -.lp-m { - --accent: var(--lagoon-deep); - --accent-soft: color-mix(in oklab, var(--lagoon) 18%, var(--foam)); - --line: rgba(28, 25, 23, 0.1); - --line-soft: rgba(28, 25, 23, 0.06); - position: relative; - border: 1px solid var(--border); - border-radius: 11px 11px 0 0; - background: var(--card); - box-shadow: - 0 1px 0 var(--inset-glint) inset, - 0 22px 44px rgba(28, 25, 23, 0.14); - overflow: hidden; - font-size: 12px; - text-align: left; - color: var(--sea-ink); -} - -.lp-m-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - padding: 9px 12px; - border-bottom: 1px solid rgba(28, 25, 23, 0.08); - font-size: 11.5px; - font-weight: 600; -} - -.lp-m-head em { - font-style: normal; - font-weight: 500; - color: var(--muted-foreground); -} - -.lp-m-body { - padding: 12px; -} - -.lp-m-groups, -.lp-m-rows { - display: flex; - flex-direction: column; -} - -.lp-m-group { - display: flex; - align-items: center; - justify-content: space-between; - padding: 9px 12px 5px; - font-family: 'JetBrains Mono', ui-monospace, monospace; - font-size: 9.5px; - letter-spacing: 0.1em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.lp-m-group b { - font-weight: 500; - color: var(--sea-ink-soft); - font-variant-numeric: tabular-nums; -} - -.lp-m-row, -.lp-m-rec, -.lp-m-cat, -.lp-m-fix { - display: flex; - align-items: center; - gap: 9px; - padding: 8px 12px; - border-top: 1px solid rgba(28, 25, 23, 0.055); - font-size: 11.5px; -} - -.lp-m-inst { - display: grid; - place-items: center; - flex: 0 0 auto; - width: 18px; - height: 18px; - border-radius: 5px; - background: color-mix(in oklab, var(--lagoon) 14%, transparent); - color: var(--lagoon-deep); - font-size: 9.5px; - font-style: normal; - font-weight: 600; -} - -.lp-m-name { - flex: 1 1 auto; - min-width: 0; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -.lp-m-name em { - font-style: normal; - color: var(--muted-foreground); -} - -.lp-m-note, -.lp-m-next { - flex: 0 0 auto; - font-size: 10px; - color: var(--muted-foreground); -} - -.lp-m-amt { - flex: 0 0 auto; - font-weight: 500; - font-variant-numeric: tabular-nums; -} - -.lp-m-amt[data-up='true'] { - color: var(--palm); -} - -.lp-m-amt[data-up='false'] { - color: var(--chart-5); -} - -.lp-m-figure { - margin: 0; - font-size: 27px; - font-weight: 600; - line-height: 1; - letter-spacing: -0.035em; - font-variant-numeric: tabular-nums; -} - -.lp-m-delta { - margin: 6px 0 0; - font-size: 11px; - color: var(--palm); -} - -.lp-m-chart { - display: block; - width: 100%; - height: 92px; - margin-top: 10px; -} - -.lp-m-line { - fill: none; - stroke: var(--accent); - stroke-width: 1.6; - stroke-linecap: round; -} - -.lp-m-splits { - display: flex; - gap: 18px; - margin-top: 10px; - padding-top: 10px; - border-top: 1px solid rgba(28, 25, 23, 0.07); -} - -.lp-m-splits div { - display: flex; - flex-direction: column; - gap: 2px; -} - -.lp-m-splits span { - font-size: 10px; - color: var(--muted-foreground); -} - -.lp-m-splits b { - font-size: 13px; - font-weight: 600; - letter-spacing: -0.02em; - font-variant-numeric: tabular-nums; -} - -.lp-m-rec .lp-m-name { - display: flex; - align-items: center; - gap: 7px; -} - -.lp-m-rec em { - font-style: normal; - font-size: 10px; - color: var(--muted-foreground); -} - -.lp-m-flag { - display: inline-flex; - align-items: center; - gap: 3px; - padding: 1px 6px; - border-radius: 999px; - background: color-mix(in oklab, var(--chart-3) 12%, transparent); - font-size: 9.5px; - color: var(--chart-5); -} - -.lp-m-chip { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 2px 8px; - border-radius: 999px; - background: rgba(28, 25, 23, 0.06); - font-size: 10px; - color: var(--muted-foreground); -} - -.lp-m-chip[data-changed='true'] { - background: var(--accent-soft); - color: var(--accent); - box-shadow: 0 0 0 1px color-mix(in oklab, var(--lagoon-deep) 24%, transparent); -} - -.lp-m-cat[data-editing='true'] { - background: color-mix(in oklab, var(--lagoon) 5%, transparent); -} - -.lp-m-learn { - margin: 10px 12px 14px; - padding: 11px 12px; - border: 1px solid color-mix(in oklab, var(--lagoon-deep) 22%, transparent); - border-radius: 9px; - background: var(--card); - box-shadow: 0 6px 18px rgba(28, 25, 23, 0.06); -} - -.lp-m-learn p { - margin: 0 0 9px; - font-size: 11.5px; -} - -.lp-m-learn-row { - display: flex; - align-items: center; - gap: 7px; - font-size: 10.5px; -} - -.lp-m-learn-row em { - font-style: normal; - color: var(--muted-foreground); -} - -.lp-m-ghost, -.lp-m-solid { - padding: 4px 10px; - border-radius: 999px; - font-size: 10.5px; - font-weight: 500; -} - -.lp-m-ghost { - border: 1px solid rgba(28, 25, 23, 0.14); - color: var(--sea-ink-soft); -} - -.lp-m-solid { - background: var(--accent); - color: var(--primary-foreground); -} - -/* Budget replica ── the flex pool is the focal point of the screen, so it gets - the tint and the pace bar while the fixed lines stay a plain list. */ -.lp-m-kicker { - margin: 0 0 5px; - font-family: 'JetBrains Mono', ui-monospace, monospace; - font-size: 9.5px; - letter-spacing: 0.1em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.lp-m-pool { - padding: 11px 12px 12px; - border-block: 1px solid rgba(28, 25, 23, 0.07); - background: color-mix(in oklab, var(--lagoon) 6%, transparent); -} - -.lp-m-pool-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 10px; - margin-bottom: 9px; - font-size: 11.5px; - font-weight: 600; -} - -.lp-m-pool-head em { - display: block; - margin-top: 2px; - font-size: 10px; - font-style: normal; - font-weight: 400; - color: var(--muted-foreground); -} - -/* Where the month actually is, against how much of the pool is gone. */ -.lp-m-today { - position: absolute; - inset-block: 0; - width: 2px; - border-radius: 999px; - background: rgba(28, 25, 23, 0.55); -} - -.lp-m-pace-note { - display: flex; - justify-content: space-between; - gap: 8px; - margin-top: 7px; - font-size: 10px; - color: var(--muted-foreground); -} - -.lp-m-fix .lp-m-name em { - font-size: 10px; -} - -.lp-m-mini { - flex: 0 0 auto; - width: 52px; - height: 5px; - border-radius: 999px; - background: rgba(28, 25, 23, 0.09); - overflow: hidden; -} - -.lp-m-mini i { - display: block; - height: 100%; - border-radius: 999px; - background: color-mix(in oklab, var(--lagoon-deep) 65%, transparent); -} - -/* Reads as the field it is on the real screen: a number you typed once. */ -.lp-m-field { - flex: 0 0 auto; - padding: 3px 9px; - border: 1px solid rgba(28, 25, 23, 0.16); - border-radius: 7px; - background: var(--card); - font-size: 11px; - font-weight: 500; - font-variant-numeric: tabular-nums; - color: var(--sea-ink); -} - /* Budget section ── the positions Bud takes on budgeting, beside the screen where they play out. */ .lp-budget { @@ -1563,9 +960,7 @@ } @media (prefers-reduced-motion: reduce) { - .lp-app, - .lp-spark path, - .lp-pace-track i { + .lp-app { animation-duration: 1ms; animation-delay: 0ms; } diff --git a/src/components/landing/mocks.tsx b/src/components/landing/mocks.tsx index e6bcbec..d5cf53e 100644 --- a/src/components/landing/mocks.tsx +++ b/src/components/landing/mocks.tsx @@ -1,474 +1,1163 @@ -/* DOM replicas of Bud's own screens. Real numbers from a plausible month, drawn - with markup rather than screenshots so they stay sharp and legible when the - cards scale down. */ - -import type { CSSProperties } from 'react' - -const appNav = [ - 'Dashboard', - 'Transactions', - 'Budget', - 'Cash flow', - 'Net worth', - 'Accounts', +/* Product shots built from the same pieces as the signed-in app — Page, + Panel, HeroMetric, PaceBar, the ledger — so the landing page shows the + screens as they actually are, not a restyled sketch of them. */ + +import { Fragment, type ComponentProps, type ReactNode } from 'react' +import { + ArrowLeftRight, + Landmark, + LayoutDashboard, + LineChart, + List, + PanelLeftClose, + PieChart, + Repeat, + Settings2, + Tags, + Wallet, +} from 'lucide-react' +import type { LucideIcon } from 'lucide-react' + +import { CategoryDonut } from '#/components/category-donut' +import { + CategoryDot, + DataList, + EmptyState, + HeroMetric, + Kicker, + RowGroupHeader, + RowMeta, + RowTitle, + ShareBar, + Stat, +} from '#/components/dense' +import { Money } from '#/components/money' +import { PaceBar } from '#/components/pace-bar' +import { Page, PageSummary, Panel } from '#/components/panel' +import { SpendingChart } from '#/components/spending-chart' +import { SproutMark } from '#/components/sprout' +import { Badge } from '#/components/ui/badge' +import { Button } from '#/components/ui/button' +import { Input } from '#/components/ui/input' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '#/components/ui/select' +import { formatDayLabel, formatUsdPlain } from '#/lib/money' +import { cn } from '#/lib/utils' + +const MONTH = 'August' +const MONTH_KEY = '2026-08' +const THROUGH_DAY = 14 +const DAYS_IN_MONTH = 31 +const PACE = THROUGH_DAY / DAYS_IN_MONTH + +const NAV = [ + { label: 'Dashboard', icon: LayoutDashboard }, + { label: 'Transactions', icon: List }, + { label: 'Budget', icon: Wallet }, + { label: 'Cash flow', icon: ArrowLeftRight }, + { label: 'Net worth', icon: LineChart }, + { label: 'Investments', icon: PieChart }, + { label: 'Accounts', icon: Landmark }, + { label: 'Recurring', icon: Repeat }, +] as const + +const SETTINGS = [ + { label: 'Categories', icon: Tags }, + { label: 'Rules', icon: Settings2 }, +] as const + +const COLORS = { + housing: 'var(--chart-1)', + groceries: 'var(--chart-2)', + restaurants: 'var(--chart-3)', + shopping: 'var(--chart-4)', + coffee: 'var(--chart-5)', + other: 'color-mix(in oklab, var(--sea-ink) 16%, transparent)', +} + +function running(daily: Array) { + let sum = 0 + return daily.map((n, i) => { + sum += n + return { day: i + 1, cumulative: sum } + }) +} + +const thisMonthSpend = running([ + 2486, 42, 118, 0, 86, 0, 0, 312, 64, 156, 28, 94, 71, 390, +]) +const lastMonthSpend = running([ + 2400, 88, 156, 42, 210, 18, 0, 280, 94, 122, 66, 188, 54, 340, 90, 0, 0, 210, + 76, 144, 38, 0, 188, 92, 56, 210, 44, 0, 0, 168, 72, +]) + +const spentThisMonth = thisMonthSpend.at(-1)?.cumulative ?? 0 +const incomeThisMonth = 8412 +const kept = incomeThisMonth - spentThisMonth +const flexSpent = 1186 +const flexPlanned = 2600 +const fixedSpent = 2489 +const fixedPlanned = 3814 +const planned = flexPlanned + fixedPlanned +const left = planned - spentThisMonth + +const spendMix = [ + { name: 'Housing', color: COLORS.housing, amount: 2400 }, + { name: 'Groceries', color: COLORS.groceries, amount: 412 }, + { name: 'Restaurants', color: COLORS.restaurants, amount: 218 }, + { name: 'Shopping', color: COLORS.shopping, amount: 280 }, + { name: 'Coffee', color: COLORS.coffee, amount: 96 }, + { name: 'Everything else', color: COLORS.other, amount: 441, muted: true }, ] -const mix = [ - { name: 'Fixed', amount: '$3,980', color: 'var(--chart-1)' }, - { name: 'Groceries', amount: '$612', color: 'var(--chart-2)' }, - { name: 'Restaurants', amount: '$438', color: 'var(--chart-3)' }, - { name: 'Travel', amount: '$305', color: 'var(--chart-5)' }, +const recentDays = [ + { + date: '2026-08-14', + spent: 90.95, + rows: [ + { + merchant: 'Whole Foods', + category: 'Groceries', + color: COLORS.groceries, + amount: 84.2, + }, + { + merchant: 'Blue Bottle', + category: 'Coffee', + color: COLORS.coffee, + amount: 6.75, + }, + ], + }, + { + date: '2026-08-13', + spent: 62.1, + rows: [ + { + merchant: 'Trader Joe’s', + category: 'Groceries', + color: COLORS.groceries, + amount: 62.1, + }, + ], + }, + { + date: '2026-08-12', + spent: 0, + rows: [ + { + merchant: 'Payroll', + category: 'Income', + color: COLORS.housing, + amount: -4206, + }, + ], + }, + { + date: '2026-08-11', + spent: 390, + rows: [ + { + merchant: 'Amex payment', + category: 'Transfer', + color: COLORS.other, + amount: 1240, + transfer: true, + }, + { + merchant: 'REI', + category: 'Shopping', + color: COLORS.shopping, + amount: 86.4, + }, + ], + }, ] -const txns = [ - { merchant: 'Whole Foods', cat: 'Groceries', amount: '−$84.20' }, - { merchant: 'Amex payment', cat: 'Transfer', amount: '−$1,240.00' }, - { merchant: 'Blue Bottle', cat: 'Coffee', amount: '−$6.75' }, - { merchant: 'Payroll', cat: 'Income', amount: '+$4,206.00' }, +const cashAccounts = [ + { + name: 'Checking', + meta: 'checking · Chase', + mask: '4021', + amount: 8240.19, + available: 7912.4, + }, + { + name: 'Savings', + meta: 'savings · Ally', + mask: '7788', + amount: 4240, + }, ] -/** The hero's product shot: the dashboard, sidebar and all. */ -export function DashboardMock() { - return ( -
-
-

Bud

-
- {appNav.map((item, i) => ( - -