wulf-pulse/.planning/phases/01-pwa-scaffolding/01-02-SUMMARY.md
lorentz 9658640c04 fix(04-01): restore phase 2/3 work lost by worktree soft-reset
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
2026-05-03 18:01:14 -04:00

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
css
tailwind4
mobile
pwa
safe-area
PWA-04
Phase 1 SC #3 — safe-area utility available
requires provides affects
@utility pt-safe (padding-top: env(safe-area-inset-top))
@utility pb-safe (padding-bottom: env(safe-area-inset-bottom))
Phase 2 (mobile shell) — sticky header (SHELL-05) and fixed bottom nav (SHELL-06) consume these utilities
added patterns
Tailwind 4 @utility blocks (already in use across brand.css)
CSS env(safe-area-inset-*) — browser-native, falls back to 0
created modified
app/styles/brand.css
Named utilities (pt-safe / pb-safe) over arbitrary values (pt-[env(safe-area-inset-top)]) — single source of truth, clearer JSX, easy future tweak if iOS rules change
brand.css over globals.css — co-located with all other named project utilities (num, metric-label, surface-brand, tagline, etc.); already imported by globals.css line 125
Top + bottom only (no pl-safe / pr-safe) — manifest pins orientation to portrait; left/right insets only matter in landscape on notched devices; speculative until a consumer asks
Plain env() (not max(env(), 0px)) — env() already returns 0 on devices without insets; max() wrapper is a no-op
duration completed tasks_completed files_modified commits
~5 min 2026-05-03 1 1 1

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-safe and @utility pb-safe in app/styles/brand.css. This restores the orphaned-requirement state flagged by 01-VERIFICATION.md (where 01-01 had declared requirements: [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 @utility blocks.

What Changed

Files Modified

  • app/styles/brand.css — appended one section comment block + two @utility definitions 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 @utility blocks in brand.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-watermark plain rule — preserved verbatim.
  • The :root / .dark Wulf 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 dff0264 exists in current branch — confirmed via git log.
  • Compiled CSS bundle in .next/static/chunks/ contains .pt-safe and .pb-safe rules — confirmed via grep.
  • npm run build exited successfully (✓ Compiled successfully in 20.3s).
  • npx tsc --noEmit --pretty exited 0.
  • PWA-04 explicitly claimed in this SUMMARY's requirements_satisfied frontmatter and "Requirements Satisfied" section — orphaned-requirement trail closed.