Six existing customer-area shells under [slug]/* rebuilt against the
handoff design (sections §2/§4/§5/§6/§7/§8). Every screen reuses the
new Panel / Monogram / Sev primitives and the ledger-table token system
so the visual contract stays single-source-of-truth in globals.css.
* `[slug]/settings` (Organization, IT_ADMIN) — legal entity dl, primary
contact card, plan & seats meter, products subscribed kv-list
(ENTITLED green dot / TRIALING amber dot).
* `[slug]/settings/users` (Team, IT_ADMIN) — bracketed member ledger
with role chips, last-active mono dim, active/invited dot status.
Invite affordance present, modal wiring deferred.
* `[slug]/billing` (Billing, CXO + FINANCE + IT_ADMIN) — current plan
card with monthly net + 19% VAT, seats + evidence-storage meters,
payment method block that swaps to "Payment failed → Re-activate"
when tenant.status is frozen, full invoices ledger with paid/due dot.
* `[slug]/audit` (Audit log, LEGAL + IT_ADMIN) — filter bar (search +
event-type chip toggles + product select), ledger table with denied
red dot, footer count + retention note.
* `[slug]/settings/integrations` (SSO, IT_ADMIN) — read-only OIDC
summary pulling from KEYCLOAK_ISSUER / KEYCLOAK_CLIENT_ID, IdP-group→
role mapping table.
* `[slug]/products` (Products index, USER+) — 2x2 product grid with
live cards (entitled + trialing chips) and "Coming soon" dashed
placeholders, plus a cross-product findings table with filter chips.
Plus a new `NotAllowed` 403 surface in the same ledger language that
replaces the inline "NotAuthorized" message used by the old shells, so
forbidden routes still look like the rest of the portal.
Every page goes through `getPortalSession()` so `BP_DEV_FIXTURE` still
swaps between admin / user / trial / frozen / archived without
Keycloak. Every screen returns 200 against
`BP_DEV_FIXTURE=admin-acme pnpm dev`.
Still to come on this branch:
* Workflows editor (palette + canvas + inspector + drag-wiring)
* ⌘K command palette + toasts
* Product launch detail (per-product page)
* Login redesign (mock SSO picker + violet gradient panel)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Brings in the handoff design system from `Breakpilot Platform.zip`
(`breakpilot/design_handoff_customer_portal/`) as the base for restyling
every customer-area surface.
What's in:
* **Design tokens & layout primitives** — `src/app/globals.css` is the
handoff `styles.css` in full (OKLCH paper + ink + brand-violet,
--rule-* hairlines, --sev-* severity ramp, corner-tick bracket
treatment, ledger table, 32–36px row density, dark mode via
`[data-theme="dark"]`). Tailwind v4 layered on top via PostCSS for
utility helpers; the design system itself stays in plain CSS.
* **Geist + Geist Mono** wired through `next/font/google` so the
monospaced metadata/figures everywhere render at the intended weight.
* **Shell chrome** under `src/components/portal/`:
`Brand` (Breakpilot. wordmark with the violet trailing dot),
`Lifeline` (top full-width tenant rail — active / trial / frozen /
demo variants; archived swaps in `ArchivedLockout`),
`NavRail` (232px left rail with tenant switcher + workspace/admin/
settings groups + user chip; locked routes show a lock icon and a
"Requires X" tooltip rather than vanishing),
`Topbar` (breadcrumb + ⌘K button placeholder + theme toggle),
`ThemeToggle` (Sun/Moon, persists to `localStorage["bp.theme"]`,
no-flash via a head script in the root layout).
* **Dashboard** at `/[slug]/dashboard` rebuilt per handoff §1:
page-head with Export + Run scan (the latter wrapped in the frozen
write-guard hovercard surfacing `HTTP 402 · payment required`),
5-cell bracketed KPI rail (open findings + 14-day sparkbars + 7-day
delta, critical with severity stack, controls passing with violet
ring gauge + n/240, evidence area sparkline, last-scan cadence),
12-col grid: 30-day findings flow + severity stack legend +
top-5 open findings table on the left, product posture rows +
scan-activity heatmap (5x7) + recent-activity feed on the right.
Plain USER role drops the KPI rail and the org-wide panels per spec.
* **Charts** — minimal SVG primitives in `components/portal/charts/`:
Sparkbars, Sparkline (area + line), Ring, StackBar, Heatmap +
HeatLegend. All token-driven (`var(--sev-*)`, `var(--accent)`).
* **Fixtures** — `src/lib/fixtures.ts` is a TS port of the handoff's
`data.js`. Deterministic mulberry32 generators give the same
realistic DACH/EU compliance data every reload (~5 tenants × 30+ days
activity / 4–13 findings per product / 9 months invoices / hash-
chained audit). Source of truth for the design until tenant-registry
is enriched to carry these fields end-to-end. RBAC table (`canAccess`,
`landingFor`) ported alongside.
* **Dev session bypass** — `src/lib/get-session.ts` returns a synthetic
`SessionWithExtras` from one of the 6 fixtures when
`BP_DEV_FIXTURE=<id>` is set. Lets the portal render the design
without Keycloak + tenant-registry up. Real Auth.js wiring untouched.
What's NOT in yet (next commits):
* Products / Product launch / Org / Team / Billing / Audit / SSO pages
* Workflows editor (palette + canvas + inspector + drag-wiring)
* Command palette + toast system
* MSW handlers for the tenant data shapes (today the page reads the
fixture module directly server-side; MSW is for client-side calls)
Run locally:
pnpm install
BP_DEV_FIXTURE=admin-acme pnpm dev
open http://acme.localhost:3000/acme/dashboard
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Four real surfaces wired to tenant-registry (settings, settings/api-keys CRUD, audit pagination, products live entitlements), five forward-looking empty states with CTAs. 56 vitest tests + 10 Playwright canaries. lib/format.ts consolidates date helpers.
Refs: M10.1
Capture the redirect_uri gotcha from the live-stack smoke. .env.example pins AUTH_URL to acme.localhost:3000 with a long-form comment; README gets an 'AUTH_URL gotcha' callout.
Refs: M5.1 follow-up
playwright.config.ts + tests/e2e/{apex,tenant,health}.spec.ts. make e2e for local. CI e2e job opt-in via RUN_E2E repo variable. OIDC click-through deferred to when stage is up.
Refs: M5.3
10 route shells under /[slug]/, role-filtered Nav, backstage stub at /__backstage__, dashboard reads session.products to render tiles. src/lib/session.ts is the canonical role × surface matrix; canSee() is the only RBAC primitive in the portal (real enforcement remains at the API layer). 24 vitest tests; 100% src/lib coverage.
Refs: M5.2
Next.js 16 + Auth.js v5 skeleton: host→slug middleware, tenant-context layout, OIDC sign-in flow against breakpilot-dev realm. 100% coverage on src/lib. Bumps next to 16.2.6 to clear trivy CVEs in 15.0.3.