The soft reset to 77073ba inadvertently staged deletions of all phase 2
and 3 artifacts. This commit restores them from their source commits so
subsequent task commits build on the complete prior-phase foundation:
- components/mobile/{BottomNav,HeaderBar,KpiCardMobile,MoreDrawer,NeedsAttentionStrip,WorkerStatusRow}
- app/mobile/layout.tsx, dashboard/page.tsx, analyzer/page.tsx
- app/api/mobile/dashboard/route.ts
- All .planning/** files from phases 01-04
- CLAUDE.md, app/layout.tsx, app/styles/brand.css, public/manifest.json
11 KiB
| phase | plan | subsystem | gap_closure | tags | requirements_satisfied | roadmap_criteria_satisfied | dependency_graph | tech_stack | key_files | decisions | metrics | |||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-pwa-scaffolding | 02 | pwa-scaffolding | true |
|
|
|
|
|
|
|
|
Phase 01 Plan 02: PWA-04 Safe-Area Utility Gap Closure Summary
One-liner: Adds shared pt-safe / pb-safe Tailwind 4 @utility blocks to app/styles/brand.css, closing the orphaned PWA-04 requirement so Phase 2's sticky header and fixed bottom nav can opt into iOS notch / Android home-indicator padding via env(safe-area-inset-*).
Requirements Satisfied
- PWA-04 — Header and bottom tab bar respect
env(safe-area-inset-top/bottom)(Tailwind arbitrary values or shared utility class). Closed by shipping@utility pt-safeand@utility pb-safeinapp/styles/brand.css. This restores the orphaned-requirement state flagged by01-VERIFICATION.md(where 01-01 had declaredrequirements: [PWA-01, PWA-02, PWA-03]only and silently deferred PWA-04 to Phase 2). - ROADMAP Phase 1 Success Criterion #3 — "Shared safe-area utility class available" — satisfied by the same two
@utilityblocks.
What Changed
Files Modified
app/styles/brand.css— appended one section comment block + two@utilitydefinitions between the existing@utility tagline(ends line 140) and the/* === Wolf-mark watermark === */section header (now line 165). Net: +23 lines, 0 deletions.
Exact Diff (additive only)
/* === Safe-area insets =================================================
*
* Opt-in padding helpers for sticky top / fixed bottom bars on devices
* with notches, dynamic islands, or gesture home indicators. Pair with
* the viewport-fit=cover viewport meta (set in app/layout.tsx) — without
* that, env(safe-area-inset-*) resolves to 0 and these utilities are
* no-ops, which is the desired fallback on non-PWA / non-mobile contexts.
*
* Usage:
* <header class="sticky top-0 pt-safe ..."> // header clears notch
* <nav class="fixed bottom-0 pb-safe ..."> // bottom bar clears home bar
*
* Closes PWA-04 (REQUIREMENTS.md) and ROADMAP Phase 1 SC #3.
* ==================================================================== */
@utility pt-safe {
padding-top: env(safe-area-inset-top);
}
@utility pb-safe {
padding-bottom: env(safe-area-inset-bottom);
}
What Was NOT Changed
app/globals.css— untouched. The existing@import "./styles/brand.css";on line 125 already pulls the new utilities into the global stylesheet.- All pre-existing
@utilityblocks inbrand.css(num,num-lg,num-xl,metric-label,surface-brand,surface-brand-ink,rule-brand,text-chrome,border-chrome,tagline,has-mark-watermark) and the.mark-watermarkplain rule — preserved verbatim. - The
:root/.darkWulf brand token sections — preserved verbatim. - No
tailwind.config.*was created (Tailwind 4 + project convention forbids it). - No
next-pwa, no service worker, no new dependencies introduced.
Why These Choices
brand.css, not globals.css
All named project utilities (num, metric-label, surface-brand, tagline, etc.) already live in brand.css. Co-locating safe-area utilities there means Phase 2 has one file to scan when looking for project helpers. globals.css is reserved for Tailwind imports, @theme inline token mapping, and :root / .dark variable definitions — adding utility classes there would muddy that separation. Plus brand.css is already imported by globals.css (line 125), so no new wiring is required.
Named utilities, not arbitrary values
Phase 2 will use these classes in 2+ places (sticky header, bottom nav, drawer footer, possibly modals). A named utility is a single source of truth — if iOS rules ever change (e.g., max(env(safe-area-inset-top), 0.5rem) becomes desirable), it's a one-line edit to brand.css instead of multi-file find-and-replace. pt-safe / pb-safe also reads more clearly in JSX class lists than pt-[env(safe-area-inset-top)]. ROADMAP Phase 1 SC #3 explicitly accepts "shared utility class" as one valid form — picking that form removes ambiguity for Phase 2.
Top + bottom only
The manifest pins orientation to portrait (per 01-01-SUMMARY.md). Left/right safe-area insets (safe-area-inset-left, safe-area-inset-right) only matter in landscape on notched devices, which the app does not enter. Adding pl-safe / pr-safe now would be speculative; Phase 2 (or any future phase) can add them in 30 seconds if a real consumer appears.
Plain env(safe-area-inset-*), not max(env(...), 0px)
The CSS env() value already returns 0 when no inset is reported by the browser — wrapping it in max(..., 0) is a no-op and adds noise. Wrap it later if a real device misbehaves.
Verification Results
All checks from the plan's <verification> section ran successfully:
| Check | Command | Result |
|---|---|---|
pt-safe utility present |
grep -E '@utility pt-safe' app/styles/brand.css |
match (1 line) |
pb-safe utility present |
grep -E '@utility pb-safe' app/styles/brand.css |
match (1 line) |
| Top inset declaration correct | grep -E 'padding-top:\s*env\(safe-area-inset-top\)' app/styles/brand.css |
match (1 line) |
| Bottom inset declaration correct | grep -E 'padding-bottom:\s*env\(safe-area-inset-bottom\)' app/styles/brand.css |
match (1 line) |
tagline utility unchanged |
grep -E '@utility tagline' app/styles/brand.css |
match |
num utility unchanged |
grep -E '@utility num \{' app/styles/brand.css |
match |
| Wulf brand tokens intact | grep -E '\-\-wulf-blue:' app/styles/brand.css |
match |
.mark-watermark intact |
grep -E '\.mark-watermark \{' app/styles/brand.css |
match |
| globals.css import wiring intact | grep -E '@import "\./styles/brand\.css";' app/globals.css |
match |
No pl-safe / pr-safe (out of scope) |
grep -E '@utility (pl-safe|pr-safe)' app/styles/brand.css |
no match |
No tailwind.config.* created |
test ! -f tailwind.config.{ts,js,mjs} |
exit 0 |
| No service worker shipped | test ! -f public/{sw,service-worker}.js |
exit 0 |
next-pwa not added |
! grep '"next-pwa"' package.json |
not found |
| Type check baseline preserved | npx tsc --noEmit --pretty |
exit 0 (clean) |
Production build (load-bearing — Tailwind 4 fails on malformed @utility syntax) |
npm run build |
✓ Compiled successfully in 20.3s |
Compiled CSS Confirmation
Inspected the production CSS chunk emitted by the build:
$ grep -oE '\.pt-safe[^,{]*\{[^}]*\}|\.pb-safe[^,{]*\{[^}]*\}' .next/static/chunks/3c3ee60b60fe53db.css
.pt-safe{padding-top:env(safe-area-inset-top)}
.pb-safe{padding-bottom:env(safe-area-inset-bottom)}
Both utilities compiled cleanly into the production bundle and are ready for Phase 2 to consume.
Notes on Build-Time Runtime Errors (Pre-Existing, Out of Scope)
npm run build also surfaces unrelated runtime errors during static page generation: the auto-starting workers (sync-scheduler, analyzer worker, RMM worker) attempt to initialize against Postgres and Better Auth at import time, but no .env is present in this worktree, so they log SASL connection errors and a BETTER_AUTH_SECRET warning. These are pre-existing and unrelated to this plan — they appeared identically in the 01-01 build environment. The compile step (the load-bearing gate for this plan, which validates Tailwind 4 @utility syntax) reported ✓ Compiled successfully and emitted the new classes into the CSS bundle. Logged to deferred-items if needed; not in scope for PWA-04.
Pointer to Phase 2
The new utilities are ready for app/mobile/layout.tsx:
- Sticky header (SHELL-05):
<header class="sticky top-0 pt-safe ...">— clears the iPhone notch / Dynamic Island and Android status bar. - Fixed bottom nav (SHELL-06):
<nav class="fixed bottom-0 pb-safe ...">— clears the iOS home indicator and Android gesture bar.
No Phase 2 work is required to wire these in — they're already part of the global Tailwind class space the moment Phase 2's components mount.
Deviations from Plan
None — plan executed exactly as written. Single-task plan, single edit, single commit.
Threat Surface Scan
No new threat surface. CSS utilities are public client-side styles compiled into the (already-public) Tailwind CSS bundle. env(safe-area-inset-*) is a browser-native CSS environment variable resolved entirely client-side from the device viewport — no JavaScript, no user input, no data flow, no auth surface, no new endpoint. STRIDE assessment from the plan stands: only boundary is "browser ↔ static CSS bundle" (information-disclosure → accept; same risk profile as every other Tailwind class). ASVS-L1 baseline preserved.
Commits
| Task | Commit | Files |
|---|---|---|
1: Append pt-safe / pb-safe @utility blocks |
dff0264 |
app/styles/brand.css |
Self-Check: PASSED
- File modified exists and contains both new utilities — confirmed via grep.
- Commit
dff0264exists in current branch — confirmed viagit log. - Compiled CSS bundle in
.next/static/chunks/contains.pt-safeand.pb-saferules — confirmed via grep. npm run buildexited successfully (✓ Compiled successfully in 20.3s).npx tsc --noEmit --prettyexited 0.- PWA-04 explicitly claimed in this SUMMARY's
requirements_satisfiedfrontmatter and "Requirements Satisfied" section — orphaned-requirement trail closed.