The mobile bottom bar rendered all 15 routes into a single row. On a
390px iPhone that works out at ~17px per tab: the icons overlap, every
label truncates to a single letter and the tap targets sit far below
the 44px minimum, so the tabs could not be read or hit reliably.
Mobile now uses a hamburger in the header that opens a full-screen menu
listing every section, grouped (Main / CIPC & BizPortal / SARS /
Practice). The panel locks background scroll, closes on Escape or on
navigation, and carries the theme toggle so the mobile chrome keeps
parity with the desktop cluster.
Desktop had the same problem in a different shape. The full 15-link row
needed 1752px inside a 1280px container, so the page scrolled sideways
even at 1920px (measured scrollWidth 2065 vs a 1920 viewport) and the
right-hand cluster was pushed off screen. The inline nav now carries
six primary links plus a "More" dropdown, which fits exactly.
- NavBar: mobile tab bar replaced by the header menu panel; desktop nav
reduced to primary links + More dropdown; grouped list shared by both
so the two surfaces cannot drift apart.
- RouteChrome: dropped the pb-24 md:pb-0 padding that only existed to
clear the old bottom bar.
- .gitignore / Workflow/.dockerignore: ignore .next-stale* so a stale
build directory cannot bloat the Docker build context.
next build runs 'Linting and checking validity of types' and fails on
the first type error, so the Coolify image build died at
app/api/cipc/search/route.ts:640. These errors were pre-existing but
only surface on next build (npm run dev does not type check).
- cipc/search/route.ts: coerce spawnSync stdout/stderr with toString();
result is typed as the un-overloaded return, so they are string|Buffer
- group-report pdf+email: use fetchCollection<YsterbaardGroup> instead of
an invalid cast; normalise description null -> undefined for ReportData
- CipcExplorer (cipc + cipc-2): split the state ternary into two returns;
the RowState union correlates state with differs, so 'diff' | 'match'
matched neither variant
- ComplianceTreeGrid: ar_non_compliance_date is optional; the API omits it
and this component never reads it
Verified: tsc --noEmit 0 errors, next build exit 0, standalone output ok.