Context. surface customer · domain onboarding-public · route /legal/terms · auth anon · source apps/oshun/web/src/app/legal/terms/page.tsx
Last walked. 2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data (2xx), console/page-errors, expected content, screenshot verified; manual screen-reader and real telemetry-delivery checks pending. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; 2026-06-28 public-legal-pages Playwright pass covered anonymous routing, exact section inventory, legal nav matrix/active states, TOC/back-to-top/print shell controls, 390 px no-overflow, 44 px touch targets, offline-after-load stability, no telemetry hooks, and axe. Manual AT pass and real telemetry delivery remain pending.
Purpose#
OSHUN's Terms of Service. Thirteen titled sections rendered through the shared
LegalPageShell (cream paper, terracotta accent, Cormorant Garamond serif) with
sticky cross-legal nav and in-page TOC.
Entry points#
- Footer link "Terms of Service" on
/welcomeand/landing - Sidebar nav inside any other
/legal/*page - Linked from sign-up "By creating an account, you agree to…" copy
- Direct URL / bookmark — yes (anon)
Layout regions#
page.tsx defines SECTIONS (13 entries) and renders the article inside
<LegalPageShell sections={SECTIONS}>. Section ids: acceptance,
description, account-registration, acceptable-use,
intellectual-property, user-content, subscription, disclaimer,
limitation, termination, governing-law, changes, contact.
- Header (shell): minimal brand + back link
- Skip link: jumps to main
- Sticky sidebar: cross-legal nav (7 entries) + in-page TOC
- Main article: h1 "Terms of Service",
data-legal-date"Last updated: February 21, 2026", then 13 numbered<section>blocks - Floating control: back-to-top button
States#
- Loading — N/A; static server component
- Default — all sections render
- TOC click / active section — clicking a TOC button scrolls to that
section and marks it
data-active="true"; passive scroll-spy remainsIntersectionObserver-driven - Back-to-top visible — the shared 44 px control appears after scrolling and returns the user near the document start
- Narrow viewport (≤ 768px) — sidebar collapses; TOC hidden
- Print — shell
@media printrules hide sidebar/header/back-to-top and append anchor hrefs after each link - Offline after load — once loaded, the URL, heading, section inventory, and active legal nav state remain stable when the browser context goes offline
- Gated — N/A; anonymous-readable
Interactions#
Cross-legal sidebar nav#
- Each
LEGAL_NAVlink (link) — navigates to sibling legal page; active whenpathname === href; hover applies paper background; mobile target is at least 44 px square
On-this-page TOC (13 buttons)#
- Each TOC button (button)
- Function:
scrollIntoViewfor that section id - Active styling: terracotta + 2px left border after click and via
IntersectionObserver - Hidden on ≤ 768px viewport
- Function:
Back-to-top#
- "↑" (button,
aria-label="Back to top")- Function: smooth-scroll to top
- Visible only when
scrollY > 300
Body content#
The Description-of-Service section enumerates the six domain pillars (Tara,
Veritas, Nyx, Arete, Nisaba, Metis) but as plain <strong> text, not links.
Verify whether these should deep-link into /welcome/domains#*.
Data & contracts#
- Reads: none — static text
- Writes: none
- Caching: SSR static; loaded-route offline stability is asserted. Cold
offline navigation / per-legal SW path caching is not asserted, and current
sw.jsdoes not path-cache/legal/*. - Auth: anon (in middleware
PUBLIC_PREFIXES) - Metadata:
title: 'Terms of Service'
Cross-references#
- Shell:
shell/01-app-shell.md - Sibling legal pages:
legal-privacy.md,legal-cookies.md,legal-accessibility.md,legal-ccpa.md,legal-dpa.md,legal-lilith.md,legal-lilith-privacy.md - Component sources:
apps/oshun/web/src/components/legal/LegalPageShell.tsx
E2E coverage#
- Backed by
apps/oshun/web/e2e/public-legal-pages.spec.ts— covers anonymous access, exact 13-section inventory, legal nav inventory/active state, shared TOC/back-to-top/print shell behavior, 390 px no-overflow, 44 px touch targets, offline-after-load stability, no telemetry hooks, and axe.
Open questions / known gaps#
- Cold offline navigation / per-legal SW cache is not asserted and current
sw.jsdoes not path-cache/legal/*; PWA shell cache coverage remains in the offline-first / PWA walkthroughs. - Domain names listed in
descriptionsection are not hyperlinked — confirm intent - No "I accept" tracking on this page; confirm acceptance is captured at sign-up rather than via inline checkbox
-
LAST_UPDATEDis hard-coded — document the change-control flow - Hard-coded en-US copy; no localisation hook