diff --git a/app/expenses/page.tsx b/app/expenses/page.tsx index 31af6b7..ac1cc4a 100644 --- a/app/expenses/page.tsx +++ b/app/expenses/page.tsx @@ -117,7 +117,7 @@ export default async function ExpensesHub() {
@@ -133,12 +133,12 @@ export default async function ExpensesHub() {
{s.description}
-
Monthly
+
Monthly
{ZAR(s.total)}
-
{s.count} items
+
{s.count} items
diff --git a/app/globals.css b/app/globals.css index 9892522..1d5e34a 100644 --- a/app/globals.css +++ b/app/globals.css @@ -36,19 +36,35 @@ --color-text-muted: #6b7280; --color-text-soft: #9ca3af; --color-row-hover: rgba(0, 0, 0, 0.02); + + /* "Ink" steps — the light-mode colour for text that borrows a brand hue. + The base brand palette above is never used as text on a light surface unless + it clears 4.5:1; these names exist so `text-accent-ink` works in BOTH modes + and only its value changes. See the .dark block for the dark counterparts. */ + --color-accent-ink: #30638E; + --color-brick-ink: #D1495B; + --color-indigo-ink: #003D5B; } /* Dark theme overrides */ .dark { - --color-bg: #0b1014; - --color-surface: #131a21; - --color-surface-muted: #0f1419; - --color-border: #1f2937; - --color-border-soft: #1a222b; - --color-text: #e6edf3; - --color-text-muted: #9ca3af; - --color-text-soft: #6b7280; - --color-row-hover: rgba(255, 255, 255, 0.03); + --color-bg: #0f151b; + --color-surface: #1e2732; + --color-surface-muted: #182029; + --color-border: #303c48; + --color-border-soft: #26313c; + --color-text: #e8eef4; + --color-text-muted: #adb8c2; + --color-text-soft: #87919a; + --color-row-hover: rgba(255, 255, 255, 0.04); + + /* Dark "ink" steps. The base brand hues are correct as *fills* but fail WCAG AA + as text on a dark surface (seaweed 3.6:1, casual 2.9:1, brick 3.7:1, indigo 1.6:1). + These are lighter steps of the same hues, reaching 4.8–7.3:1. Text-only — + never a fill — so the @theme brand palette stays untouched. */ + --color-accent-ink: #2BB0C4; + --color-brick-ink: #E86E7E; + --color-indigo-ink: #7FA8C9; /* Part A.4: dark-mode chart hues */ --color-chart-income: #00A6BE; @@ -81,9 +97,9 @@ body, .dark body { transition: background-color 0.2s ease, color 0.2s ease; } .recharts-legend-item-text { color: #6b7280 !important; font-size: 12px !important; } .dark .recharts-default-tooltip { - background: rgba(15, 20, 25, 0.98) !important; - border: 1px solid #1f2937 !important; - color: #e6edf3 !important; + background: rgba(30, 39, 50, 0.98) !important; + border: 1px solid #303c48 !important; + color: #e8eef4 !important; } -.dark .recharts-tooltip-label { color: #e6edf3 !important; } -.dark .recharts-legend-item-text { color: #9ca3af !important; } +.dark .recharts-tooltip-label { color: #e8eef4 !important; } +.dark .recharts-legend-item-text { color: #adb8c2 !important; } diff --git a/app/layout.tsx b/app/layout.tsx index 8bb8e15..ff8bbe7 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -99,13 +99,13 @@ export default async function RootLayout({ children }: { children: React.ReactNo -
+
Mega Plant
Mega Plant
diff --git a/app/overheads/page.tsx b/app/overheads/page.tsx index 0bdb567..8b1cb89 100644 --- a/app/overheads/page.tsx +++ b/app/overheads/page.tsx @@ -42,7 +42,7 @@ export default async function OverheadsPage() { const items = overheads.filter((o) => o.section === sec); const secTotal = items.reduce((s, o) => s + (o.amount_incl || 0), 0); return ( -
+

{sec}

@@ -53,7 +53,7 @@ export default async function OverheadsPage() {
- + @@ -64,7 +64,7 @@ export default async function OverheadsPage() { {items.map((o) => ( - + diff --git a/app/page.tsx b/app/page.tsx index db4572c..a3efe08 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -100,7 +100,7 @@ export default async function DashboardPage() { {/* Main charts */}
-
+

Income vs Fixed vs Running

@@ -115,7 +115,7 @@ export default async function DashboardPage() {
-
+

Overhead mix

By section · {ZAR(totalMonthlyIncl)} total

@@ -137,7 +137,7 @@ export default async function DashboardPage() { {/* Bottom row */}
-
+

Monthly Income trend

Project income per month

diff --git a/components/ChartsGrid.tsx b/components/ChartsGrid.tsx index 43e30a7..18c72a6 100644 --- a/components/ChartsGrid.tsx +++ b/components/ChartsGrid.tsx @@ -89,14 +89,14 @@ export function ChartsGrid({ items }: { items: Item[] }) {
{/* Toolbar — wraps to multiple rows on mobile. Round 4 Part B.1/B.3/B.5. */} -
+
setQuery(e.target.value)} placeholder="Search by code or name…" - className="w-full min-h-[44px] pl-9 pr-3 bg-gray-50 dark:bg-white/5 border border-transparent rounded-lg text-sm placeholder:text-gray-400 focus:bg-white dark:focus:bg-[#0f1419] focus:border-[#30638E] focus:outline-none" + className="w-full min-h-[44px] pl-9 pr-3 bg-gray-50 dark:bg-white/5 border border-transparent rounded-lg text-sm placeholder:text-gray-400 focus:bg-white dark:focus:bg-white/10 focus:border-[#30638E] focus:outline-none" />
@@ -119,7 +119,7 @@ export function ChartsGrid({ items }: { items: Item[] }) { ))}
- View + View {[ { id: "lines", label: "Lines", icon: LineChartIcon }, { id: "stack", label: "Cost stack", icon: BarChart3 }, @@ -142,10 +142,10 @@ export function ChartsGrid({ items }: { items: Item[] }) {
{filtered.length === 0 ? ( -
- +
+
No machines match "{query}"
-
@@ -157,7 +157,7 @@ export function ChartsGrid({ items }: { items: Item[] }) { const profit = computeProfit({ income: item.totalIncome, fixed: 0, running: 0 }); const positiveProfit = profit >= 0; return ( -
+
diff --git a/components/ExpenseSectionPage.tsx b/components/ExpenseSectionPage.tsx index 13e5696..f309408 100644 --- a/components/ExpenseSectionPage.tsx +++ b/components/ExpenseSectionPage.tsx @@ -65,7 +65,7 @@ export async function ExpenseSectionPage({
{glBreakdown.length > 1 && ( -
+

By GL account

{glBreakdown.map((g) => { @@ -75,7 +75,7 @@ export async function ExpenseSectionPage({
{g.gl} - {ZAR(g.total)} · {g.count} item{g.count !== 1 && "s"} · {pct.toFixed(0)}% + {ZAR(g.total)} · {g.count} item{g.count !== 1 && "s"} · {pct.toFixed(0)}%
@@ -93,16 +93,16 @@ export async function ExpenseSectionPage({ {/* Part B.1: empty state must say so plainly, not render a table of zeros */} {items.length === 0 ? ( -
+
No {section} entries in PocketBase.
-
+
This category either doesn't apply yet or lives elsewhere — see round-3 brief.
) : ( -
+

Line items

{items.length} entries @@ -110,7 +110,7 @@ export async function ExpenseSectionPage({
Description GL Account Supplier
{o.description} {o.gl_account || "—"} {o.supplier || "—"}
- + @@ -121,11 +121,11 @@ export async function ExpenseSectionPage({ {items.map((o) => ( - + ))} - + @@ -159,7 +159,7 @@ export async function ExpenseSectionPage({ function StatCard({ label, value, color }: { label: string; value: string; color: string }) { return ( -
+
{label}
{value}
diff --git a/components/GlobalSearch.tsx b/components/GlobalSearch.tsx index 5be3898..a2dd875 100644 --- a/components/GlobalSearch.tsx +++ b/components/GlobalSearch.tsx @@ -35,7 +35,7 @@ export function GlobalSearch({ assets }: { assets: Asset[] }) { return (
- + { @@ -52,12 +52,12 @@ export function GlobalSearch({ assets }: { assets: Asset[] }) { } }} placeholder="Search plant, supplier, code…" - className="w-full h-9 pl-9 pr-3 bg-gray-100 dark:bg-white/5 border border-transparent rounded-lg text-sm placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:bg-white dark:focus:bg-[#0f1419] focus:border-[#30638E] focus:outline-none transition" + className="w-full h-9 pl-9 pr-3 bg-gray-100 dark:bg-white/5 border border-transparent rounded-lg text-sm placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:bg-white dark:focus:bg-white/10 focus:border-[#30638E] focus:outline-none transition" />
{open && matches.length > 0 && ( -
+
{matches.map((a) => ( {a.code ? ( @@ -86,7 +86,7 @@ export function GlobalSearch({ assets }: { assets: Asset[] }) { )}
{a.category && ( - + {a.category} )} diff --git a/components/IncomeView.tsx b/components/IncomeView.tsx index 3737a92..44e2aa8 100644 --- a/components/IncomeView.tsx +++ b/components/IncomeView.tsx @@ -50,7 +50,7 @@ export function IncomeView({ rows, palette }: { rows: IncomeRow[]; palette: { in
{/* Round 4 Part B.3: Sort collapses to a select on mobile. View toggle stays as buttons. */}
-
+
- Sort: + Sort: {[ { id: "profit", label: "Profit" }, { id: "income", label: "Income" }, @@ -137,7 +137,7 @@ function CardsView({ return (
-
YTD income
+
YTD income
{ZAR2(r.income)}
@@ -185,7 +185,7 @@ function CardsView({
-
YTD income
+
YTD income
{ZAR2(r.income)}
{isOpen && ( -
+
{/* 12-col on md+, 3-col on mobile — Part 3: unreadable at 375px otherwise */}
{r.months.map((v, i) => (
-
{MONTH_NAMES[i]}
+
{MONTH_NAMES[i]}
0 ? "bg-[#00798C]/5 text-[#00798C] dark:bg-[#00798C]/15" - : "text-gray-300 dark:text-gray-600" + : "text-gray-300 dark:text-text-soft" }`} > {v == null ? "—" : (v > 0 ? ZAR2(v) : "R 0")} @@ -219,7 +219,7 @@ function CardsView({
))}
-
+
YTD income
{ZAR(r.income)}
@@ -248,12 +248,12 @@ function CardsView({ function GridView({ rows }: { rows: IncomeRow[] }) { return ( -
+
Description GL Account Supplier
{o.description} {o.gl_account ? ( - + {o.gl_account} ) : ( @@ -140,7 +140,7 @@ export async function ExpenseSectionPage({
Total {ZAR(totalExcl)} {ZAR(totalVat)}
- - + + {MONTH_NAMES.map((m) => ( @@ -265,8 +265,8 @@ function GridView({ rows }: { rows: IncomeRow[] }) { {rows.map((r) => { const positive = r.ytdProfit >= 0; return ( - - + {r.months.map((v, i) => ( - ))} @@ -293,8 +293,8 @@ function GridView({ rows }: { rows: IncomeRow[] }) { {/* Totals row: the sum of every machine for each month. */} - - +
Code
Code Machine{m}
+
{r.code} @@ -278,7 +278,7 @@ function GridView({ rows }: { rows: IncomeRow[] }) { 0 ? "text-gray-700 dark:text-gray-300" : "text-gray-300 dark:text-gray-600"}`}> + 0 ? "text-gray-700 dark:text-gray-300" : "text-gray-300 dark:text-text-soft"}`}> {v == null ? "—" : (v > 0 ? ZAR2(v) : "R 0")}
+
Total diff --git a/components/InvoiceIntake.tsx b/components/InvoiceIntake.tsx index 48325bc..8295a8a 100644 --- a/components/InvoiceIntake.tsx +++ b/components/InvoiceIntake.tsx @@ -166,9 +166,9 @@ export function AddInvoiceButton() { role="dialog" aria-modal="true" aria-label="Add invoice" - className="relative w-full sm:max-w-lg bg-white dark:bg-[#131a21] rounded-t-2xl sm:rounded-2xl shadow-2xl border border-gray-100 dark:border-white/10 max-h-[90vh] overflow-y-auto" + className="relative w-full sm:max-w-lg bg-white dark:bg-surface rounded-t-2xl sm:rounded-2xl shadow-2xl border border-gray-100 dark:border-white/10 max-h-[90vh] overflow-y-auto" > -
+

Add invoice

-
+