# Marketing pages

Pilot inventory of Gradeful’s public marketing, catalog, legal, and contact routes.

Live student-facing copy is the source of truth in the app. Marketing copy was restored from Git `a7de9a9` (the commit immediately before the tutoring-platform rewrite), with factual/legal exceptions documented in `07-release-status.md` and `08-known-limitations.md`.

This file covers unauthenticated marketing surfaces only.

Related inventories:

- [00-how-everything-connects.md](./00-how-everything-connects.md) — registration, checkout, dashboard, settings, study
- [02-auth-onboarding.md](./02-auth-onboarding.md)
- [03-checkout.md](./03-checkout.md)
- [04-dashboard-and-workspace.md](./04-dashboard-and-workspace.md)
- [05-settings.md](./05-settings.md)
- [06-study-views.md](./06-study-views.md)

Admin (`/admin/*`) is still not in this set.

## Scope

Included:

- `/` homepage
- `/pricing`
- `/courses` listing
- `/courses/[slug]` public course preview
- `/about`
- `/contact`
- `/support`
- `/tutoring`
- `/policies`
- `/privacy`
- `/terms`
- `/refunds`
- `/academic-integrity`
- `/site-map`
- Redirects: `/faq`, `/access`, `/content-standards`
- `/markitup` (public cinematic site; not Gradeful marketing chrome)

Excluded from this file: `/login`, `/signup`, `/forgot-password`, `/reset-password`, `/welcome`, `/checkout`, `/dashboard`, `/my-courses`, `/admin`, `/maintenance`, `/dev`.

There is no App Router `(marketing)` group. These pages live at the app root and share `SiteHeader` + `SiteFooter` except `/markitup`.

Normal access for every Gradeful page below is **public** (no login required) except `/`: logged-in users with **active** `user_access` are redirected to `/dashboard` before the marketing homepage HTML is rendered (`proxy.ts` + `app/page.tsx`). Logged-in users without active access still see `/`. Active users may still open `/pricing`, `/courses`, public course pages, and legal/help on purpose. If `platform_settings` key `maintenance_mode` is `"true"`, non-admins are sent to `/maintenance` instead.

---

## Shared chrome

Used on every Gradeful marketing page in this file except `/markitup`. Documented once. Each PAGE still lists header then footer in render order and points here, plus any page-specific exception.

### Site header (`components/site/SiteHeader.tsx` + `NavAuthButton.tsx`)

SECTION: Site header
  Copy: Logo image alt `Gradeful`. Desktop and mobile nav labels `Courses`, `Pricing`, `About`. Logged-in extras documented on elements. Logged-out CTA `Get access` and `Sign in`. Mobile control `Open menu` / `Close menu`.
  Data shown: Current path (underline active nav). Auth session from Supabase Auth. Profile fields `profiles.avatar_url`, `profiles.full_name`, `profiles.university`, `profiles.campus`, `profiles.major`, `profiles.second_major`, `profiles.role`. Active paid access from `user_access.plan_type`, `user_access.payment_status`, `user_access.expires_at` (rows with `payment_status` in `paid` or `confirmed` and unexpired). Header is sticky except homepage (`sticky={false}`).

  ELEMENT: Gradeful (logo)
    Action: navigate
    Destination: `/`
    Writes to: none
    Reads from: none

  ELEMENT: Courses
    Action: navigate
    Destination: `/courses`
    Writes to: none
    Reads from: none

  ELEMENT: Pricing
    Action: navigate
    Destination: `/pricing`
    Writes to: none
    Reads from: none

  ELEMENT: About
    Action: navigate
    Destination: `/about`
    Writes to: none
    Reads from: none

  ELEMENT: My courses
    Action: navigate
    Destination: `/my-courses`
    Writes to: none
    Reads from: Visible only when logged in and at least one active `user_access` row (`payment_status` in `paid`/`confirmed`, `expires_at` null or in the future)

  ELEMENT: Admin
    Action: navigate
    Destination: `/admin`
    Writes to: none
    Reads from: Visible only when `profiles.role` is `admin`

  ELEMENT: Open menu / Close menu
    Action: toggle
    Destination: mobile nav panel `#site-mobile-menu`
    Writes to: none
    Reads from: none (mobile only)

  ELEMENT: Get access
    Action: navigate
    Destination: `/checkout`
    Writes to: none
    Reads from: Shown when logged out, except on `/pricing` and `/checkout`. When logged in with no paid plan (`getNavAccessCtaLabel` tier `none`), same label. Hidden while auth is still loading.

  ELEMENT: Add more courses
    Action: navigate
    Destination: `/checkout`
    Writes to: none
    Reads from: Shown when logged in with an active paid `user_access.plan_type` (tier `partial`). Desktop header uses avatar-only layout, so this CTA appears in the **mobile** menu, not the desktop bar.

  ELEMENT: Sign in
    Action: navigate
    Destination: `/login`
    Writes to: none
    Reads from: Shown when logged out

  ELEMENT: Dashboard
    Action: navigate
    Destination: `/dashboard`
    Writes to: none
    Reads from: Shown when logged in, in the **mobile** `NavAuthButton` full layout only (desktop logged-in header is avatar-only)

  ELEMENT: Open settings (gear)
    Action: navigate
    Destination: `/dashboard?settings=profile`
    Writes to: none
    Reads from: Shown when logged in (`AccountChrome`)

  ELEMENT: Open account menu (avatar / initials)
    Action: open modal
    Destination: account flyout menu
    Writes to: none
    Reads from: `profiles.avatar_url`, `profiles.full_name`, auth email; initials derived from name/email

Account flyout (opens from avatar; also used from header on marketing pages):

  ELEMENT: Settings
    Action: navigate
    Destination: `/dashboard?settings=profile`
    Writes to: none
    Reads from: none

  ELEMENT: Get help
    Action: navigate
    Destination: `/dashboard?settings=help`
    Writes to: none
    Reads from: none

  ELEMENT: Upgrade plan
    Action: navigate
    Destination: `/pricing`
    Writes to: none
    Reads from: none

  ELEMENT: Dashboard
    Action: navigate
    Destination: `/dashboard`
    Writes to: none
    Reads from: none

  ELEMENT: Learn more
    Action: toggle
    Destination: Learn more submenu
    Writes to: none
    Reads from: none

  ELEMENT: About Gradeful
    Action: navigate
    Destination: `/about`
    Writes to: none
    Reads from: none

  ELEMENT: Privacy policy
    Action: navigate
    Destination: `/privacy`
    Writes to: none
    Reads from: none

  ELEMENT: Terms of service
    Action: navigate
    Destination: `/terms`
    Writes to: none
    Reads from: none

  ELEMENT: Refund policy
    Action: navigate
    Destination: `/refunds`
    Writes to: none
    Reads from: none

  ELEMENT: Academic integrity
    Action: navigate
    Destination: `/academic-integrity`
    Writes to: none
    Reads from: none

  ELEMENT: All policies
    Action: navigate
    Destination: `/policies`
    Writes to: none
    Reads from: none

  ELEMENT: Log out / Logging out…
    Action: submit
    Destination: `/` after `supabase.auth.signOut()`
    Writes to: clears Auth session cookies (no Gradeful table write)
    Reads from: none

### Site footer (`SiteFooter` in `components/site/PublicPageShell.tsx`)

SECTION: Site footer
  Copy: Logo alt `Gradeful`. Body: `Course-specific study packs for AUB and LAU students. Built for focused self-study.` Copyright: `© 2026 Gradeful.` Column headings `Platform` and `Policies`.
  Data shown: static

  ELEMENT: Home
    Action: navigate
    Destination: `/`
    Writes to: none
    Reads from: none

  ELEMENT: Courses
    Action: navigate
    Destination: `/courses`
    Writes to: none
    Reads from: none

  ELEMENT: Pricing
    Action: navigate
    Destination: `/pricing`
    Writes to: none
    Reads from: none

  ELEMENT: Privacy Policy
    Action: navigate
    Destination: `/privacy`
    Writes to: none
    Reads from: none

  ELEMENT: Terms of Service
    Action: navigate
    Destination: `/terms`
    Writes to: none
    Reads from: none

  ELEMENT: Refund Policy
    Action: navigate
    Destination: `/refunds`
    Writes to: none
    Reads from: none

  ELEMENT: Academic Integrity
    Action: navigate
    Destination: `/academic-integrity`
    Writes to: none
    Reads from: none

---

PAGE: /
Purpose: Search-first marketing homepage for course-specific study packs (notes, formula sheets, flashcards, exam review) and plan CTAs.
Access: public for anonymous and logged-in users with no active plan. Logged-in users with active `user_access` are redirected to `/dashboard` (no homepage flash).
Entry points: Direct URL; footer Home; header logo; site map; post-logout redirect (`AccountFlyout` signs out to `/`); external links to gradeful.app.

SECTION: Site header
  Copy: Shared chrome. Homepage header is not sticky.
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Hero
  Copy: `Course-specific study packs`
  `Stop studying from scattered sources that weren't built for your course.`
  `Notes, formula sheets, flashcards, and exam review, all organized around your exact syllabus. Currently serving AUB and LAU students.`
  Data shown: static

SECTION: Course search
  Copy: Screen-reader label `Search courses`. Input `aria-label` `Search courses`. Placeholder: `Search by course code or name, e.g. CHEM201, Calculus III`
  Empty suggestions: `No matching course yet.`
  Suggestion row secondary label (sm+): `Open`
  Data shown: Suggestion rows are dynamic from `getCourses()`: `courses.code`, `courses.title`, `courses.slug`, `universities.short_name`, pack count from related `course_packs` rows. Catalog limited to `courses.is_active = true`, non-sample, and `course_packs.length > 0`. Query uses first 50 courses.

  ELEMENT: Search form (no visible submit button; submitting the field)
    Action: submit
    Destination: If a highlighted suggestion is active → `/courses/[courses.slug]`. Else if query non-empty → `/courses?q={typed query}`. Else → `/courses`. Note: `/courses` does not currently read `?q=`; the listing search state is client-only.
    Writes to: none
    Reads from: `courses.*` / `universities.short_name` / `course_packs` as above

  ELEMENT: [university short name] [course code] [course title] (each suggestion)
    Action: navigate
    Destination: `/courses/[courses.slug]`
    Writes to: none
    Reads from: `universities.short_name`, `courses.code`, `courses.title`, `courses.slug`

  ELEMENT: Search all courses
    Action: navigate
    Destination: `/courses?q={typed query}`
    Writes to: none
    Reads from: Visible when the dropdown is open and no suggestion matches

  ELEMENT: Request this course
    Action: navigate
    Destination: `/courses#course-request`
    Writes to: none
    Reads from: Visible when the dropdown is open and no suggestion matches

SECTION: Hero course marquee
  Copy: Each tile shows a compatibility university short name (when display mode is `compatibility`) and a display course code. Pack counts are not shown.
  Data shown: Rendered only when `heroCourses.length > 0` and `platform_settings` key `show_course_catalog` is true (default true). Courses from `getCourses()` as above.

  ELEMENT: [university] [code]
    Action: navigate
    Destination: `/courses/[courses.slug]`
    Writes to: none
    Reads from: `universities.short_name` via `formatUniversityShortName`, `courses.code`, `courses.slug`. Visibility also reads `platform_settings` key `show_course_catalog`.

SECTION: Request prompt under search
  Copy: `Don't see your course?` then link `Request it →`
  Data shown: static

  ELEMENT: Request it →
    Action: navigate
    Destination: `/courses#course-request`
    Writes to: none
    Reads from: none

SECTION: Available now
  Copy: `Available now`
  `Admin-selected study packs built for your exact course at AUB and LAU.`
  Slider helper (mobile): `Swipe sideways or use the arrows to see more courses.`
  End card: badge `Full catalog`; `Check more`; `Find your next course pack.`; `Browse the full Gradeful catalog and open every course available for AUB and LAU.`; `Browse all courses`
  Data shown: Section omitted when no featured courses. Cards from `getHomepageCourses()`: `courses` where `is_active = true` and `is_homepage_featured = true`, ordered by `courses.homepage_sort_order` then `courses.code`, max 10, then `isListedCatalogCourse`. Card fields: `universities.short_name` (compatibility display), `courses.code`, `courses.title`, `courses.description`, `courses.semester_label`, `courses.slug`; pack count from `course_packs`. Instructor names stay off unless `NEXT_PUBLIC_GRADEFUL_SHOW_INSTRUCTOR_NAMES=true`. Badge: `1 pack` / `{n} packs`; `Access required` if any related pack is locked, else `Preview open`. Label `Semester:` is a static prefix.

  ELEMENT: Scroll course previews left
    Action: toggle
    Destination: none (scrolls the slider)
    Writes to: none
    Reads from: none

  ELEMENT: Scroll course previews right
    Action: toggle
    Destination: none (scrolls the slider)
    Writes to: none
    Reads from: none

  ELEMENT: [course title] (card heading link)
    Action: navigate
    Destination: `/courses/[courses.slug]`
    Writes to: none
    Reads from: `courses.title`, `courses.slug`

  ELEMENT: Preview pack
    Action: navigate
    Destination: `/courses/[courses.slug]`
    Writes to: none
    Reads from: `courses.slug`; `aria-label` is dynamically `View pack for {courses.code} {courses.title}`

  ELEMENT: Check more courses / Browse all courses (end card)
    Action: navigate
    Destination: `/courses`
    Writes to: none
    Reads from: none

  ELEMENT: Browse all courses →
    Action: navigate
    Destination: `/courses`
    Writes to: none
    Reads from: none

SECTION: Testimonials
  Copy: Testimonials section removed. Hardcoded quotes are not shipped. `lib/reviews.ts` is an empty list until consented testimonials exist.
  Data shown: none

SECTION: Comparison
  Copy: `The usual way`
  `WhatsApp PDF from last semester. No solutions.`
  `YouTube videos that skip the course you are actually taking.`
  `Classmate notes from a different section.`
  `Practice with no answer key.`
  `Googling at 2am the night before.`
  `With Gradeful`
  `Notes built for your exact course.`
  `Formula sheet organized by chapter. Printable.`
  `Practice quiz written for this course.`
  `Exam review for the week before finals.`
  `One place. Everything ready. Nothing missing.`
  Data shown: static

SECTION: Pack tools
  Copy: `Everything you need. Nothing you don't.`
  `Everything you'd normally chase across different sources, organized once, for your exact course.`
  Cards:
  Notes — `Built for your exact chapter. Not a generic overview.`
  Formula sheets — `One page. Everything organized. Print-ready.`
  Flashcards — `Drill the ideas until they stick.`
  Practice quizzes — `Original questions to check your understanding.`
  Mock exams — `Timed Gradeful practice under exam-like pressure.`
  Exam review — `Condensed version for finals week.`
  Mistake fixes — `What students get wrong in this course, fixed.`
  AI explanations — `Ask anything. Grounded in your pack.` Badge: `Coming soon`
  Data shown: static

  ELEMENT: Preview a sample pack
    Action: navigate
    Destination: `/courses`
    Writes to: none
    Reads from: none

SECTION: Pricing
  Copy: `Straightforward access.`
  `Starting at {formatStartingPrice(plans)}. Less than one textbook chapter.` from `getPaymentPlans()` (catalog `lib/payments/catalog.ts` plus optional `plan_overrides`).
  Per card: plan display name, price, unit label, duration, benefits, and CTA from the same live plan record. Default names: Focus, Balance, Full Load, Crunch. Price via `formatPlanPrice`. Unit/duration from catalog helpers (`/1 course`, `/2 courses`, `/up to 6 courses`, `/up to 4 courses`; semester window or `{n}-day sprint`). Badge `Recommended` on Full Load. Badge `{n} days` on Crunch.
  Data shown: `getPaymentPlans()` → `lib/payments/catalog.ts` merged with `platform_settings.plan_overrides`.

  ELEMENT: Get Focus
    Action: navigate
    Destination: `/checkout?plan=focus`
    Writes to: none
    Reads from: none

  ELEMENT: Get Balance
    Action: navigate
    Destination: `/checkout?plan=balance`
    Writes to: none
    Reads from: none

  ELEMENT: Cover my semester
    Action: navigate
    Destination: `/checkout?plan=fullload`
    Writes to: none
    Reads from: none

  ELEMENT: Prep for Finals
    Action: navigate
    Destination: `/checkout?plan=crunch`
    Writes to: none
    Reads from: none

  ELEMENT: Compare full features →
    Action: navigate
    Destination: `/pricing#compare`
    Writes to: none
    Reads from: none

SECTION: Closing CTA
  Copy: `Finals are coming. Find your course before the panic starts.`
  `Search available courses or request the one you need next.`
  Data shown: static. WhatsApp URL is hardcoded `https://wa.me/96179057533` (not `platform_settings.whatsapp_contact`).

  ELEMENT: Find your course pack →
    Action: navigate
    Destination: `/courses`
    Writes to: none
    Reads from: none

  ELEMENT: WhatsApp →
    Action: external link
    Destination: `https://wa.me/96179057533`
    Writes to: none
    Reads from: none

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /pricing
Purpose: Compare Focus / Balance / Full Load / Crunch and send the student into checkout.
Access: public
Entry points: Header/footer Pricing; homepage Compare full features; about is not a source; contact secondary “How access works” goes to `/access` which redirects here; `/faq` redirect; `/access` redirect; account flyout Upgrade plan; site map.

SECTION: Site header
  Copy: Shared chrome. Exception: logged-out `Get access` is hidden on this path.
  Data shown: Shared chrome
  ELEMENT: Shared chrome except `Get access`

SECTION: Hero
  Copy: `Pricing for AUB and LAU course packs`
  `Pick the access that matches your semester.`
  `Choose a plan and get instant access to course-specific study packs.`
  Data shown: static

  ELEMENT: View plans
    Action: navigate
    Destination: in-page `#plans` (smooth scroll)
    Writes to: none
    Reads from: none

SECTION: Plans
  Copy: Eyebrow `Plans`
  `Simple pricing. Clear coverage.`
  `Focus on one course, cover two difficult classes, unlock your semester load, or use Crunch for finals pressure.`
  Cards use `getPaymentPlans()` so admin overlays apply. Names, prices, unit labels, duration, benefits, and CTAs come from `lib/payments/catalog.ts`.
  Data shown: live plan records, not hardcoded dollar amounts.

  ELEMENT: Get Focus
    Action: navigate
    Destination: `/checkout?plan=focus`
    Writes to: none
    Reads from: none

  ELEMENT: Get Balance
    Action: navigate
    Destination: `/checkout?plan=balance`
    Writes to: none
    Reads from: none

  ELEMENT: Cover my semester
    Action: navigate
    Destination: `/checkout?plan=fullload`
    Writes to: none
    Reads from: none

  ELEMENT: Prep for Finals
    Action: navigate
    Destination: `/checkout?plan=crunch`
    Writes to: none
    Reads from: none

SECTION: Compare plans
  Copy: Eyebrow `Compare plans`
  `The full picture before you choose.`
  `Every plan includes the same course tutoring tools. The difference is how many courses you cover and how long access lasts.`
  Toggle closed label `Compare all features`; open label `Hide comparison`.
  Table headers: `Outcome` plus live plan names
  Rows from live plans + `tutoringMessagesLabels()`:
  Courses covered — catalog `coursesCoveredLabel` (1 / 2 / up to 6 / up to 4)
  Access duration — catalog `durationShortLabel`
  Lessons + reference sheets — checkmark (sr-only `Included`) all plans
  Flashcards + quizzes — Included all plans
  Exam prep + common traps — Included all plans
  AI tutor — fair-use tutoring message counts from `lib/payments/economics.ts` (not a credit wallet)
  Best for — catalog `comparisonBestFor`
  Footnote: tutoring messages are a fair-use allowance, not credits; core lessons/practice/review do not consume it.
  Data shown: `getPaymentPlans()` plus economics labels. Hash `#compare` auto-opens the table.

  ELEMENT: Compare all features / Hide comparison
    Action: toggle
    Destination: `#pricing-feature-table` (show/hide)
    Writes to: none
    Reads from: none

SECTION: Which plan
  Copy: `Which plan should I choose?`
  `Match the plan to the pressure.`
  `The right plan depends on how many courses need structure, not on a generic subscription tier.`
  Choose Focus — `Not sure if Gradeful is worth it? Try one course first. If it helps, you'll know before spending more.`
  Choose Balance — `Tried one course and it worked. Now cover the other one. Two courses for $7 more than Focus.`
  Choose Full Load — live copy from `choiceGuidance` (up to 6 courses at checkout).
  Choose Crunch — live copy from `choiceGuidance` (up to 4 courses for `{durationDays}` days).
  Data shown: `choiceGuidance(plans)` from `lib/payments/catalog.ts`

SECTION: FAQ
  Copy: Eyebrow `FAQ`
  `Answers before you choose a plan.`
  `What is inside, how access works, and what to do if your course is not listed yet.`
  Accordion from `PricingFaq` + live plans:
  `What is inside each pack?` / worked notes, formula sheet, flashcards, practice quizzes, exam review, mistake fixes for the specific AUB or LAU course
  `How long does access last?` / Focus, Balance, Full Load `{durationDays}` days; Crunch `{durationDays}` days
  `Can I share my account with friends?` / No, one student per account
  `Is there a free preview?` / Yes — open any course to see the pack structure before buying
  `What if my course is not listed?` / Request it on the courses page
  `Can I switch or upgrade my plan?` / Focus can move to Balance or Full Load by paying the difference via WhatsApp; self-serve upgrades are not live
  `Do I pick my courses when I buy?` / Focus 1, Balance 2, Full Load up to 6, Crunch up to 4. No schedule upload to buy.
  `Can I access on my phone?` / Yes
  `What is the refund policy?` / `REFUND_POLICY.studentCopy` from `lib/payments/transitions.ts`
  Footer: `Didn't find your answer?` `Ask directly and get a straight answer.`
  Data shown: live plans + refund policy module

  ELEMENT: [each FAQ question]
    Action: toggle
    Destination: that item’s answer region
    Writes to: none
    Reads from: none

  ELEMENT: Ask on WhatsApp
    Action: external link
    Destination: `https://wa.me/96179057533`
    Writes to: none
    Reads from: none

SECTION: Refund footnote
  Copy: `Refunds are reviewed for documented course withdrawals or drops with university confirmation. Contact us before requesting access if you are unsure about your enrollment.` then `Read our refund policy →`
  Data shown: static

  ELEMENT: Read our refund policy →
    Action: navigate
    Destination: `/refunds`
    Writes to: none
    Reads from: none

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /courses
Purpose: Browse AUB/LAU course packs and request a missing course.
Access: public
Entry points: Header/footer Courses; homepage search, marquee, Browse all courses, Preview a sample pack, Find your course pack; contact Browse courses; support Request a course; about Request a course; site map.

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Hero
  Copy: `AUB + LAU course packs`
  `Study smarter with packs made for your course.`
  `Access organized notes, formula sheets, flashcards, and exam review for the AUB and LAU courses you take.` plus the course compatibility statement.
  Image alt: `Gradeful study pack preview showing lecture notes, formula sheets, flashcards, and exam review`
  Data shown: static (`/assets/courses-hero-graphic.png`)

SECTION: Catalog error (only if `getCourses` fails)
  Copy: `Course catalog is being built. Check back soon.`
  `The public catalog is not available yet. The page is ready and will show courses once Supabase is connected and seeded.`
  Data shown: static strings from `catalogUnavailable()` in `lib/catalog.ts` (not database content)

SECTION: Filters (when catalog loads without issue)
  Copy: Screen-reader `Search courses by code, title, or university`. Placeholder: `Search by course code or name, e.g. MATH 201`. Group `aria-label` `Filter by university compatibility`. Tab labels `All` plus each `universities.short_name` present in offerings.
  Data shown: URL state `q` and `university` (stable short_name slug). Listing dataset is `getCourses({ limit: 200 })` then client ranker. Sample slug/title/code stay hidden. Active in-development courses are listed with status, not hidden for empty packs.

  ELEMENT: Search courses (input)
    Action: toggle
    Destination: `/courses?q=` via replaceState (shareable; does not break back/forward)
    Writes to: none
    Reads from: compact course codes, titles, offering aliases on `course_universities`, university names, topic keywords. Instructor names are secondary only.

  ELEMENT: All
    Action: toggle
    Destination: clears `university` query param via pushState
    Writes to: none
    Reads from: none

  ELEMENT: University compatibility tabs
    Action: toggle
    Destination: `/courses?university={short_name}` via pushState
    Writes to: none
    Reads from: offerings on `course_universities` plus primary `courses.university_id` fallback

  ELEMENT: Clear filters
    Action: toggle
    Destination: `/courses` via pushState
    Writes to: none
    Reads from: Visible when a university filter or search is set

SECTION: Empty catalog (loaded, zero listed courses)
  Copy: `Course catalog is being built. Check back soon.`
  `Published courses will appear here once the catalog is seeded.`
  Data shown: static (this empty state is separate from the fetch-issue banner)

SECTION: Filtered empty
  Copy: `No courses found for "{search}"` (search preserved) then `Keep your search. Request the course so Gradeful can prioritize it.`
  Data shown: `{search}` is the typed filter string.

  ELEMENT: Request this course →
    Action: navigate
    Destination: in-page `#course-request` (opens request collapsible and prefills from search)
    Writes to: none
    Reads from: current search string used as prefill

SECTION: Course table
  Copy: Column headers `Course` `Compatibility` `Coverage` `Last reviewed` `Action`
  Row: course title and `courses.code`; compatibility badges from `course_universities` (neutral text, no logos); availability (`Available` / `Preview available` / `In development`) and coverage (`Full support` / `Core topics` / `Early access`) derived from published module items; last reviewed from max `module_pack_items.updated_at`; one action (`Open course` / `Preview` / `Notify me`).
  Data shown: `courses.id`, `courses.slug`, `courses.code`, `courses.title`, offering codes, computed availability/coverage.

  ELEMENT: Open course / Preview (row link)
    Action: navigate
    Destination: `/courses/[courses.slug]`
    Writes to: none
    Reads from: `courses.slug`

  ELEMENT: Notify me
    Action: toggle
    Destination: in-page `#course-request`
    Writes to: none
    Reads from: course code prefill

SECTION: Pagination
  Copy: `Show` `{10|20|50}` `per page` then `{start}–{end} of {totalItems}`. Ellipsis `…`. Controls `aria-label` `Previous page` / `Next page` / `Courses per page` / `Course list pagination`.
  Data shown: Counts from the filtered in-memory list, not a SQL offset.

  ELEMENT: 10 / 20 / 50
    Action: toggle
    Destination: none (changes page size)
    Writes to: none
    Reads from: none

  ELEMENT: Previous page
    Action: toggle
    Destination: none (previous page)
    Writes to: none
    Reads from: disabled on page 1

  ELEMENT: [page number]
    Action: toggle
    Destination: none (that page)
    Writes to: none
    Reads from: none

  ELEMENT: Next page
    Action: toggle
    Destination: none (next page)
    Writes to: none
    Reads from: disabled on last page

SECTION: Course request collapsible
  Copy: Closed header: `Don't see your course?` `Request it and we'll prioritize by demand.` trailing `Request a course`
  Open eyebrow: `Course request`
  Fields: `Course code` placeholder `MATH201`; `Course name` placeholder `Calculus III`; `University` with `Select university` / `AUB` / `LAU` / `Other`; `Your email` placeholder `your@university.edu`; `Notes` placeholder `Instructor name, section, anything specific about what you need`
  Submit: `Send request` or `Sending...`
  Success: `Request sent.` `We prioritize by demand.`
  Error body uses `formError` when insert fails: `Request could not be sent yet. The public request table may still be setting up.` Field errors include `Enter a valid email address.` and `Add a university, course code, or course title.` and `Keep this under {n} characters.`
  Data shown: Success is client state. Insert writes `course_requests`. Prefill may copy the catalog search string into code or title.

  ELEMENT: Request a course (header button)
    Action: toggle
    Destination: expands the form (`aria-expanded`)
    Writes to: none
    Reads from: none

  ELEMENT: Send request / Sending...
    Action: submit
    Destination: none (stays on `/courses`; success state in place)
    Writes to: `course_requests.id`, `course_requests.email`, `course_requests.university_name`, `course_requests.course_code`, `course_requests.course_title`, `course_requests.instructor_name` (hidden empty), `course_requests.notes`
    Reads from: none

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /courses/[slug]
Purpose: Public preview of one course pack (what’s included, locked module outline) and checkout CTA.
Access: public (page itself). `View formula sheet` may send a logged-out user toward a protected study URL.
Entry points: Homepage search/marquee/cards; courses table; direct/shared URL; internal course links.

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Load error
  Copy: `Back to courses`
  `Couldn't load this course right now. Try refreshing.`
  Data shown: static. Shown when `getCourseBySlug` throws/returns an issue.

  ELEMENT: Back to courses
    Action: navigate
    Destination: `/courses`
    Writes to: none
    Reads from: none

(Unknown slug uses Next `notFound()` — custom 404, not this page.)

SECTION: Course header
  Copy: Breadcrumb `Courses` / `{subject}` / `{courses.title}` where subject is mapped from course-code prefix (`BIOL`→`Biology`, `CHEM`→`Chemistry`, `ECON`→`Economics`, `MATH`/`MTH`→`Mathematics`, `MECH`→`Mechanical Engineering`, `PHYS`→`Physics`, else the letter prefix or `Course`).
  Badges: `{universities.short_name} · {courseCode}` from `getUniversityCourses`.
  Heading: `courses.title`
  Optional body: `courses.description`
  Data shown: `courses.title`, `courses.description`, `courses.code`, `courses.slug`, `universities.short_name`, related university course codes. JSON-LD BreadcrumbList uses those same fields.

  ELEMENT: Courses
    Action: navigate
    Destination: `/courses`
    Writes to: none
    Reads from: none

  ELEMENT: Get access →
    Action: navigate
    Destination: `/checkout?plan=focus&course={courses.id}`
    Writes to: none
    Reads from: `courses.id`

  ELEMENT: View formula sheet
    Action: navigate
    Destination: `/my-courses/{courses.slug}?module={course_modules.id}&tab=formula_sheet` when a module has `course_modules.is_free_preview` and a `formula_sheet` pack item
    Writes to: none
    Reads from: `course_modules.is_free_preview`, `module_pack_items.item_type`, `courses.slug`

  ELEMENT: Unlock formula sheet
    Action: navigate
    Destination: `/pricing`
    Writes to: none
    Reads from: Shown when no free-preview formula sheet exists

SECTION: What's included
  Copy: `What's included`
  Cards: `Notes` `Formula sheet` `Flashcards` `Practice quiz` `Exam review` `Mistake fixes`
  Data shown: static labels (not read from `module_pack_items` on this grid)

SECTION: Course modules
  Copy: `Course modules`
  Shared-track rows: `course_modules.title`; status chips `Not published yet` (no visible pack items) or `Up next` when `course_modules.status === "up_next"`; expanded chips `{module_pack_items.label}` optionally ` · {module_pack_items.count}` (label omitted when stored as empty/`default`).
  Divider: `Tracks split by school from here`
  Columns: `AUB {aubCourse.courseCode ?? courses.code} track` and `LAU {lauCourse.courseCode ?? courses.code} track` listing locked `course_modules.title` for `track` `aub` / `lau`.
  Data shown: `course_modules.title`, `course_modules.sort_order`, `course_modules.status`, `course_modules.track`, `course_modules.is_free_preview`, `module_pack_items.label`, `module_pack_items.count`, `module_pack_items.item_type` (flagged items hidden). Modules with zero student-visible items treated as unpublished.

  ELEMENT: [module title] (shared-track row)
    Action: toggle
    Destination: expands that module’s pack-item chips
    Writes to: none
    Reads from: `course_modules.title`; first shared module defaults open

  ELEMENT: Get access to unlock all modules →
    Action: navigate
    Destination: `/checkout?plan=focus&course={courses.id}`
    Writes to: none
    Reads from: `courses.id`

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /about
Purpose: Founder story, product beliefs, independence disclaimer, and contact.
Access: public
Entry points: Header About; account Learn more → About Gradeful; site map.

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Why Gradeful exists
  Copy: `Why Gradeful exists`
  `Built by an engineering student who couldn't make sense of Dynamics.`
  `The rules weren't hard. The textbook made them hard.`
  `So I built a pack for Dynamics. Worked notes. A formula sheet that organized the rules by type. Practice problems that escalated. It made the course click — not because it leaked an exam, but because the ideas finally had a path.`
  `That was the idea. If one pack could do that for Dynamics, the same approach could work for every course at AUB and LAU.`
  Data shown: static

SECTION: What we believe
  Copy: `What we believe`
  `01` `Generic material fails specific courses.` `MATH201 is not the same as a random Calculus III playlist. Your course has a scope. Your workspace should follow that scope — without pretending to be the university.`
  `02` `Organized beats comprehensive.` `A 200-page textbook is not the problem. Not knowing which ideas to practice first is. Gradeful sequences lessons, drills, and review.`
  `03` `Practice should feel like pressure.` `Original quizzes and practice exams train the method. They are not copies of anyone's live assessment.`
  Data shown: static

SECTION: Independence
  Copy: `Independence`
  Canonical independence statement from `lib/brand/copy.ts`.
  Data shown: static

SECTION: Get in touch
  Copy: `Get in touch`
  `Questions about a specific course, a workspace you think is missing, or anything else. Reach out directly.`
  Data shown: static

  ELEMENT: WhatsApp
    Action: external link
    Destination: `https://wa.me/96179057533`
    Writes to: none
    Reads from: none

  ELEMENT: Request a course
    Action: navigate
    Destination: `/courses#course-request`
    Writes to: none
    Reads from: none

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /contact
Purpose: Public intake for course requests and support topics.
Access: public
Entry points: Policies Contact Gradeful; checkout/support copy linking `/contact`; direct URL.

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Hero
  Copy: Eyebrow `Contact Gradeful`
  `Requests, corrections, and launch questions in one place.`
  `The most useful message is specific: university, course code, course title, instructor if relevant, and the kind of material that would help.`
  Badges: `Course requests` `Corrections` `Access questions`
  Decorative visual label `contact`
  Data shown: static

  ELEMENT: Browse courses
    Action: navigate
    Destination: `/courses`
    Writes to: none
    Reads from: none

  ELEMENT: How access works
    Action: navigate
    Destination: `/access` (immediately redirects to `/pricing`)
    Writes to: none
    Reads from: none

SECTION: What to send
  Copy: Eyebrow `What to send`
  `Make the request specific enough to act on.`
  `Short, precise course details are more useful than broad feedback. If you are reporting an error, include the course and section where you saw it.`
  Course requests — `Tell Gradeful which AUB or LAU course, instructor, or topic should be prioritized next.`
  Access questions — `Ask about pricing, payment confirmation, or course coverage.`
  Corrections — `Flag unclear explanations, possible mistakes, missing formulas, or outdated course details.`
  Campus partners — `Students, ambassadors, tutors, and reviewers can reach out when campus programs open.`
  Support band: `Support` `Send the course details Gradeful needs to help.` `For fastest support, include your university, course code, instructor if relevant, and the specific material or access question.`
  Data shown: static

SECTION: Request a course form
  Copy: `Request a course`
  `Tell Gradeful what you need next.`
  `Share the course code, university, and anything specific your section needs. We use requests to prioritize what gets built next.`
  Labels: `Email` placeholder `you@example.com`; `University` placeholder `AUB or LAU`; `Course code` placeholder `MATH201`; `Course title` placeholder `Calculus III`; `Instructor` placeholder `Instructor name, if relevant`; `Notes` placeholder `Tell us what materials would help.`
  Submit: `Submit request` / `Sending...`
  Success: `Request sent` `Gradeful has your course request.` `Reference number: {course_requests.id}` `Gradeful can use this to prioritize courses.`
  Fail banner: `Request could not be sent yet. The public request table may still be setting up.`
  Data shown: Success reference is the generated `course_requests.id`.

  ELEMENT: Submit request / Sending...
    Action: submit
    Destination: none (success state on `/contact`)
    Writes to: `course_requests.id`, `course_requests.email`, `course_requests.university_name`, `course_requests.course_code`, `course_requests.course_title`, `course_requests.instructor_name`, `course_requests.notes`
    Reads from: none

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /support
Purpose: Public help landing (same Help & contact content used in settings).
Access: public
Entry points: Direct URL; account scheme `Get help` href is `/support` but the header flyout currently opens `/dashboard?settings=help` instead; site map does not list it.

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Help & contact
  Copy: `Help & contact`
  `Reach out or browse common answers`
  Chat with us — `Usually responds within a day`
  Request a missing course — `We prioritize by demand`
  Policy links: `Privacy Policy` `Terms of Service` `Refund Policy` `Academic Integrity`
  Data shown: static

  ELEMENT: Open WhatsApp
    Action: external link
    Destination: `https://wa.me/96179057533`
    Writes to: none
    Reads from: none

  ELEMENT: Request a course
    Action: navigate
    Destination: `/courses#course-request`
    Writes to: none
    Reads from: none

  ELEMENT: Privacy Policy
    Action: navigate
    Destination: `/privacy`
    Writes to: none
    Reads from: none

  ELEMENT: Terms of Service
    Action: navigate
    Destination: `/terms`
    Writes to: none
    Reads from: none

  ELEMENT: Refund Policy
    Action: navigate
    Destination: `/refunds`
    Writes to: none
    Reads from: none

  ELEMENT: Academic Integrity
    Action: navigate
    Destination: `/academic-integrity`
    Writes to: none
    Reads from: none

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /tutoring
Purpose: Public tutoring/help request intake (stored as a course request).
Access: public
Entry points: Direct URL (not in main nav or footer).

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Hero
  Copy: `Need help beyond a study pack?`
  `Need a tutor for your course?`
  `Request help for an AUB or LAU course when you need more than a pack: a difficult chapter, a topic explanation, exam prep, or a course that is not covered yet.`
  Side card: `How it works` `This is a request path, not a full tutoring marketplace.` `Gradeful collects the course and topic details first. If help is available, the team can follow up with next steps. If not, the request still helps prioritize future packs and support.`
  Data shown: static

  ELEMENT: Request help
    Action: navigate
    Destination: in-page `#request`
    Writes to: none
    Reads from: none

  ELEMENT: View study pack plans
    Action: navigate
    Destination: `/pricing`
    Writes to: none
    Reads from: none

SECTION: Use cases
  Copy: Eyebrow `Use cases`
  `Ask for specific help, not vague tutoring.`
  `The better the course and topic details, the easier it is to tell whether Gradeful can help.`
  You are stuck before an exam — `Send the course, topic, and deadline so Gradeful can understand what kind of help would actually matter.`
  One chapter is blocking everything — `Ask for help with a specific chapter instead of searching through generic videos and old group chats.`
  You need a topic explained — `Use the request form for a focused explanation path connected to the course you are taking.`
  Your course is not covered yet — `Requests help Gradeful see which AUB and LAU courses need packs or support next.`
  Data shown: static

SECTION: Request form
  Copy: Eyebrow `Request`
  `Tell us the course, topic, and kind of help you want.`
  `This uses Gradeful's existing safe public request system. No payment is collected here, and no tutor match is promised before Gradeful reviews the request.`
  Form: `Request help` `Tell us what you are stuck on.` `This is an intake request. It does not promise a tutor match, but it gives Gradeful the details needed to respond clearly.`
  Labels: `Email` placeholder `you@example.com`; `University` `Select university` / `AUB` / `LAU` / `Other`; `Course code` placeholder `MATH201`; `Topic or chapter` placeholder `Sequences, organic mechanisms, circuits...`; `Preferred help type` options `Exam prep` / `Specific chapter` / `Topic explanation` / `Course not covered yet`; `Details` placeholder `Tell us what you need help understanding and when your exam or quiz is.`
  Submit: `Send help request` / `Sending...`
  Success: `Request sent` `Gradeful has your help request.` `Reference number: {id}. The team can use this to understand what course support students need next.`
  Data shown: Success `{id}` is `course_requests.id`. Topic is stored in `course_requests.course_title` (fallback `Tutoring request`). Notes are composed as `Tutoring/help request` plus preferred help type, topic, and details (max 500 chars).

  ELEMENT: Send help request / Sending...
    Action: submit
    Destination: none (success state on `/tutoring`)
    Writes to: `course_requests.id`, `course_requests.email`, `course_requests.university_name`, `course_requests.course_code`, `course_requests.course_title`, `course_requests.instructor_name` (empty), `course_requests.notes`
    Reads from: none

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /policies
Purpose: Index of legal/trust pages.
Access: public
Entry points: Account flyout All policies; direct URL.

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Hero
  Copy: Eyebrow `Policies`
  `The trust layer behind the study packs.`
  `These pages set public expectations for privacy, access, refunds, academic integrity, and original study material.`
  Badges: `Privacy` `Terms` `Academic integrity`
  Data shown: static

  ELEMENT: Contact Gradeful
    Action: navigate
    Destination: `/contact`
    Writes to: none
    Reads from: none

  ELEMENT: Integrity rules
    Action: navigate
    Destination: `/academic-integrity`
    Writes to: none
    Reads from: none

SECTION: Policy library
  Copy: Eyebrow `Policy library`
  `Legal and trust pages students can actually scan.`
  `These policies are written for the public site and paid course-pack access. They should be reviewed by counsel before a major launch.`
  Rows from `policyLinks`: `Privacy Policy` note `Student, schedule, usage, and support data.`; `Terms of Service` note `Rules for course packs and platform use.`; `Refund Policy` note `Course drops, withdrawals, and access issues.`; `Academic Integrity` note `Honest studying, practice, AI expectations, and original content.` Each row CTA `Read policy`.
  Data shown: static

  ELEMENT: Privacy Policy / Read policy
    Action: navigate
    Destination: `/privacy`
    Writes to: none
    Reads from: none

  ELEMENT: Terms of Service / Read policy
    Action: navigate
    Destination: `/terms`
    Writes to: none
    Reads from: none

  ELEMENT: Refund Policy / Read policy
    Action: navigate
    Destination: `/refunds`
    Writes to: none
    Reads from: none

  ELEMENT: Academic Integrity / Read policy
    Action: navigate
    Destination: `/academic-integrity`
    Writes to: none
    Reads from: none

SECTION: Operating principles
  Copy: Eyebrow `Operating principles` `The short version.` `Every policy should reinforce the same product posture: useful study help, honest access, original content, and no university-affiliation implication.`
  Independent — `Gradeful is not affiliated with AUB, LAU, or any instructor.`
  Original — `Published study material should be independently created and reviewed.`
  Student-safe — `Policies explain data, access, refunds, and fair-use expectations in plain language.`
  Data shown: static

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /privacy
Purpose: Privacy policy.
Access: public
Entry points: Footer; policy nav; policies index; account Learn more; support list; site map.

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Policy nav
  Copy: `Privacy Policy` `Terms of Service` `Refund Policy` `Academic Integrity`
  Data shown: Active path styles the current pill.

  ELEMENT: Privacy Policy
    Action: navigate
    Destination: `/privacy`
    Writes to: none
    Reads from: `usePathname()` for active state

  ELEMENT: Terms of Service
    Action: navigate
    Destination: `/terms`
    Writes to: none
    Reads from: pathname

  ELEMENT: Refund Policy
    Action: navigate
    Destination: `/refunds`
    Writes to: none
    Reads from: pathname

  ELEMENT: Academic Integrity
    Action: navigate
    Destination: `/academic-integrity`
    Writes to: none
    Reads from: pathname

SECTION: Title
  Copy: `Privacy Policy`
  `Last updated: June 2026`
  Data shown: static

SECTION: Overview
  Copy: `Overview`
  `Gradeful is an independent study platform for AUB and LAU students. We collect only the information needed to run course previews, course requests, support, safety, and paid course-pack features.`
  `Gradeful is not affiliated with AUB, LAU, or any instructor. Do not upload university documents, professor materials, exam files, or private third-party content unless you have the right to share them.`
  Data shown: static

SECTION: Information we may collect
  Copy: `Information we may collect`
  `Contact details, such as email address or support messages.`
  `Course request details, such as university, course code, course title, instructor name, and notes.`
  `Account details when accounts are active, such as login method, course access, and subscription status.`
  `Schedule or enrollment proof if required to verify course access.`
  `Payment and access records when paid access is active.`
  `Usage, device, and security data, such as pages visited, feature usage, IP address, browser, and device identifiers.`
  `Content feedback, correction reports, and academic-integrity reports.`
  Data shown: static

SECTION: How we use information
  Copy: `How we use information`
  `To provide course previews, course packs, and student support.`
  `To prioritize new AUB and LAU course content.`
  `To verify access, payment status, and course eligibility.`
  `To prevent abuse, account sharing abuse, scraping, or cheating.`
  `To improve explanations, quizzes, and platform reliability.`
  `To comply with legal, safety, and academic-integrity obligations.`
  Data shown: static

SECTION: AI and study assistance
  Copy: `AI and study assistance`
  `If Gradeful offers AI study assistance, prompts and interactions may be processed to provide answers, improve safety, prevent abuse, and debug the product. AI features should be grounded in Gradeful course material and are not a substitute for university rules or instructor guidance.`
  Data shown: static

SECTION: Sharing information
  Copy: `Sharing information`
  `Gradeful may share information with service providers that help run the platform, including hosting, database, analytics, email, payment, support, security, and AI providers. We may also disclose information when required by law, to protect rights and safety, or to investigate misuse.`
  `Gradeful does not sell student personal information as a standalone data product.`
  Data shown: static

SECTION: Retention and deletion
  Copy: `Retention and deletion`
  `We keep information for as long as needed to provide the platform, keep records, prevent abuse, resolve disputes, and comply with legal obligations. Students may request deletion of personal data, subject to records Gradeful must keep for security, payment, academic-integrity, or legal reasons.`
  Data shown: static

SECTION: Student responsibilities
  Copy: `Student responsibilities`
  `Students should not submit confidential, copyrighted, or restricted course materials. If you believe content on Gradeful includes protected material or personal information, contact Gradeful with the course, page, and details so it can be reviewed.`
  Data shown: static

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /terms
Purpose: Terms of service.
Access: public
Entry points: Footer; policy nav; policies index; account Learn more; support; site map.

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Policy nav
  Copy: Same four pills as `/privacy`
  Data shown: pathname for active pill
  ELEMENT: same four policy nav links as `/privacy`

SECTION: Title
  Copy: `Terms of Service`
  `Last updated: June 2026`
  Data shown: static

SECTION: Independent platform
  Copy: `Independent platform`
  `Gradeful is an independent academic support platform. It is not affiliated with, sponsored by, or endorsed by AUB, LAU, any university, or any instructor.`
  Data shown: static

SECTION: Educational use
  Copy: `Educational use`
  `Gradeful provides study materials, previews, practice tools, and course-pack features to support self-study. Gradeful does not guarantee grades, exam results, course outcomes, admission outcomes, or academic standing.`
  `Students remain responsible for attending class, reading official course material, following instructor requirements, and obeying university policies.`
  Data shown: static

SECTION: Accounts and access
  Copy: `Accounts and access`
  `Each account is intended for the approved student or approved access group only. Gradeful may limit access, revoke access, or request verification if it detects misuse, suspicious activity, or account sharing outside the allowed plan.`
  Data shown: static

SECTION: Payments and refunds
  Copy: `Payments and refunds`
  `Paid access and payment confirmation are subject to the plan shown at purchase. Refunds are handled under the Refund Policy.`
  Data shown: static

SECTION: Academic integrity
  Copy: `Academic integrity`
  `Students may not use Gradeful to cheat, plagiarize, submit generated or copied work as their own, bypass exam rules, or violate university policy. Gradeful may remove users or reports connected to misuse.`
  Data shown: static

SECTION: Content ownership and restrictions
  Copy: `Content ownership and restrictions`
  `Gradeful content may not be copied, resold, scraped, uploaded to other platforms, or redistributed without permission. Students may not upload professor slides, exams, copyrighted books, paid materials, or private university files unless they have permission.`
  Data shown: static

SECTION: Availability and changes
  Copy: `Availability and changes`
  `Gradeful may change course availability, pricing, features, policies, and access rules as the platform develops. Public catalog pages may show setup states when course data is unavailable.`
  Data shown: static

SECTION: Limitation of responsibility
  Copy: `Limitation of responsibility`
  `Gradeful is provided as a study support service. To the maximum extent allowed by law, Gradeful is not responsible for indirect losses, academic decisions, missed deadlines, exam performance, or university disciplinary outcomes.`
  Data shown: static

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /refunds
Purpose: Refund policy.
Access: public
Entry points: Footer; policy nav; pricing footnote; account Learn more; support; site map.

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Policy nav
  Copy: Same four pills as `/privacy`
  Data shown: pathname
  ELEMENT: same four policy nav links as `/privacy`

SECTION: Title
  Copy: `Refund Policy`
  `Last updated: June 2026`
  Data shown: static

SECTION: General rule
  Copy: `General rule`
  `Gradeful generally issues refunds only for official course withdrawal or course drop cases that are verified with university documentation.`
  `Refund requests are reviewed under this policy. Submitting a request does not guarantee approval.`
  Data shown: static

SECTION: Usually eligible for review
  Copy: `Usually eligible for review`
  `Official withdrawal from the course connected to the paid pack.`
  `Official course drop with documentation showing the relevant course and date.`
  `Duplicate payment for the same student and same access period.`
  `A Gradeful-side access issue that prevents use and cannot be resolved within a reasonable support window.`
  Data shown: static

SECTION: Usually not refundable
  Copy: `Usually not refundable`
  `Change of mind after access is approved.`
  `Partial semester use or low usage.`
  `Exam failure, grade dissatisfaction, or course difficulty.`
  `Missing university deadlines or ignoring official course rules.`
  `Account removal for cheating, redistribution, abuse, or other terms violations.`
  Data shown: static

SECTION: How to request a refund
  Copy: `How to request a refund`
  `Send the student name or account email, course code, plan purchased, payment reference if available, and official university drop or withdrawal documentation. Gradeful may ask for additional verification before approving a refund.`
  Data shown: static

SECTION: Processing
  Copy: `Processing`
  `Approved refunds are normally returned through the original payment path when possible. Processing time may depend on the payment method, bank, wallet, or gateway.`
  Data shown: static

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /academic-integrity
Purpose: Academic integrity and original-content rules.
Access: public
Entry points: Footer; policy nav; policies Integrity rules; account Learn more; support; `/content-standards` permanent redirect; site map.

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Policy nav
  Copy: Same four pills as `/privacy`
  Data shown: pathname
  ELEMENT: same four policy nav links as `/privacy`

SECTION: Title
  Copy: `Academic Integrity`
  `Last updated: June 2026`
  Data shown: static

SECTION: What Gradeful supports
  Copy: `What Gradeful supports`
  `Reviewing concepts before class, quizzes, exams, or finals.`
  `Practicing with original questions and worked examples.`
  `Learning from mistakes and improving problem-solving habits.`
  `Using flashcards, formula sheets, and summaries for recall.`
  `Asking for explanations that help you understand the method.`
  Data shown: static

SECTION: What Gradeful does not allow
  Copy: `What Gradeful does not allow`
  `Using Gradeful during an exam, quiz, or assessment if prohibited.`
  `Submitting Gradeful explanations or AI output as your own work.`
  `Uploading professor slides, private exams, answer keys, or restricted files.`
  `Requesting solutions to active graded assignments in a dishonest way.`
  `Redistributing paid Gradeful materials outside the approved access plan.`
  Data shown: static

SECTION: AI study help
  Copy: `AI study help`
  `If AI features are available, they should explain concepts, diagnose mistakes, and guide practice. They should not be used to bypass learning, complete prohibited work, or violate course rules.`
  Data shown: static

SECTION: Student contributions
  Copy: `Student contributions`
  `Students, tutors, or reviewers who contribute material must only provide content they have the right to share. Contributions should be original, consented, and free of protected institutional material.`
  Data shown: static

SECTION: Original study material
  Copy: `Original study material`
  `Gradeful materials should be independently created from student knowledge, tutor expertise, public concepts, and original synthesis. Content should explain ideas in Gradeful's own words and examples.`
  `Gradeful should not publish professor slides, private course files, copyrighted textbooks, restricted exams, paid answer keys, or institutional materials without permission.`
  Data shown: static

SECTION: Review and corrections
  Copy: `Review and corrections`
  `Collect student pain points and common confusion areas.`
  `Create notes, examples, and practice from original synthesis.`
  `Review for accuracy with tutors or subject-matter reviewers.`
  `Use AI-assisted checks for consistency where helpful.`
  `Publish with a correction pathway for students.`
  `Students should be able to report unclear explanations, errors, outdated course details, or suspected protected material. Reports should include the course code, pack, section, and a clear description of the issue.`
  Data shown: static

SECTION: Removal requests
  Copy: `Removal requests`
  `If someone believes Gradeful content infringes rights or includes restricted material, they should send enough detail for review. Gradeful may remove or revise content while the report is investigated.`
  Data shown: static

SECTION: Reports and enforcement
  Copy: `Reports and enforcement`
  `Gradeful may remove content, restrict access, investigate reports, or suspend accounts connected to cheating, copyright misuse, harassment, scraping, redistribution, or academic-integrity violations.`
  Data shown: static

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /site-map
Purpose: Internal public route tracker (not linked from header/footer).
Access: public
Entry points: Direct URL only.

SECTION: Site header
  Copy: Shared chrome
  Data shown: Shared chrome
  ELEMENT: all Shared chrome elements apply

SECTION: Hero
  Copy: Eyebrow `Route dashboard`
  `Every public Gradeful route in one polished tracker.`
  `Use this page to check the live homepage sections, public pages, policies, and account routes.`
  Badges: `Public pages` `Account` `Policies`
  Data shown: static

  ELEMENT: Back home
    Action: navigate
    Destination: `/`
    Writes to: none
    Reads from: none

SECTION: Counts
  Copy: Number `{publicRoutes.length}` then `Public pages`. Number `{accountRoutes.length}` then `Account routes`. Number `{policyLinks.length}` then `Policy routes`.
  Data shown: Counts are `publicRoutes.length` (Homepage, Sign in, Sign up, Forgot password, Reset password, plus `Courses`/`Pricing`/`About` from `siteNavLinks`), `accountRoutes.length` (1), `policyLinks.length` (4). Not database.

SECTION: Public pages
  Copy: Heading `Public pages` and `{n} routes`. Each card: label, path, note, badge `Open`.
  Homepage — `/` — `Main public search homepage.`
  Sign in — `/login` — `Email, social, and phone authentication entry point.`
  Sign up — `/signup` — `Student account creation and email verification entry point.`
  Forgot password — `/forgot-password` — `Password recovery email request form.`
  Reset password — `/reset-password` — `Secure new-password form for recovery links.`
  Courses — `/courses` (no note on `siteNavLinks`)
  Pricing — `/pricing`
  About — `/about`
  Data shown: static lists in `app/site-map/page.tsx` + `siteNavLinks`

  ELEMENT: Open (each row)
    Action: navigate
    Destination: that row’s `href`
    Writes to: none
    Reads from: none

SECTION: Account
  Copy: `Account` `{n} routes`
  My courses — `/my-courses` — `Protected dashboard for active student course access.`
  Data shown: static

  ELEMENT: Open
    Action: navigate
    Destination: `/my-courses` (middleware sends logged-out users to login)
    Writes to: none
    Reads from: none

SECTION: Policies
  Copy: `Policies` `{n} routes` plus the four `policyLinks` labels/notes from Shared chrome / `PublicPageShell`
  Data shown: static `policyLinks`

  ELEMENT: Open
    Action: navigate
    Destination: `/privacy` `/terms` `/refunds` `/academic-integrity`
    Writes to: none
    Reads from: none

SECTION: Homepage sections
  Copy: `Homepage sections` `{n} routes`
  Homepage hero — `/` — `Search-first landing hero.`
  What's inside — `/` — `Homepage tutoring tools overview.`
  Data shown: static

  ELEMENT: Open
    Action: navigate
    Destination: `/`
    Writes to: none
    Reads from: none

SECTION: Site footer
  Copy: Shared chrome
  Data shown: static
  ELEMENT: all Shared chrome footer elements apply

---

PAGE: /faq
Purpose: Legacy FAQ URL; not a rendered page.
Access: public
Entry points: Old bookmarks/external links.

SECTION: Redirect
  Copy: none (no page body)
  Data shown: none
  ELEMENT: none — `next.config.ts` redirects `/faq` → `/pricing` (temporary)

---

PAGE: /access
Purpose: Legacy “how access works” URL; not a rendered page.
Access: public
Entry points: Contact hero `How access works`; leftover `/access` links.

SECTION: Redirect
  Copy: none (no page body)
  Data shown: none
  ELEMENT: none — `redirect("/pricing")`

---

PAGE: /content-standards
Purpose: Legacy content-standards URL; not a rendered page.
Access: public
Entry points: Old bookmarks/external links.

SECTION: Redirect
  Copy: none (no page body)
  Data shown: none
  ELEMENT: none — `permanentRedirect("/academic-integrity")`

---

PAGE: /markitup
Purpose: Separate public cinematic site for MarkItUp (Beirut marketing studio). Does not use Gradeful header/footer or brand tokens.
Access: public
Entry points: Direct URL only (not in Gradeful nav/footer).

SECTION: Preloader (skipped when `prefers-reduced-motion: reduce`)
  Copy: `Loading`
  Skip control uses `Skip the sequence`
  Giant numeric progress `00`–`100` (dynamic)
  `Lighting the street`
  `markitup.lb`
  Data shown: Progress is client animation, not a database.

  ELEMENT: Skip the sequence
    Action: toggle
    Destination: none (dismisses preloader)
    Writes to: none
    Reads from: none

SECTION: Fixed header
  Copy: Wordmark `MARKIT` + `U` (rocket glyph). Instagram chip `@{markitup.handle}` → `@markitup.lb`
  Data shown: static `lib/markitup/content.ts`

  ELEMENT: @markitup.lb
    Action: external link
    Destination: `https://www.instagram.com/markitup.lb/`
    Writes to: none
    Reads from: none

SECTION: Scroll film
  Copy: Screen-reader h1: `We don’t just market. We move brands.`
  Hint: `Scroll`
  Chapter copy is dynamic from scroll progress (`lib/markitup/film.ts` `filmChapters`), not a CMS:
  `01 — Beirut, at night` / `A quiet brand is already falling.` / `Lights off. Nobody looking. The rocket waits in the window like a thing that never launched.`
  `02 — The mark` / `We don’t just market.` / `A gold line is drawn. The object becomes a mark. The night starts to move.`
  `03 — Consulting` / `We sit with the business first.` / `Through the glass, onto the table. Maps, the gap, the rocket in the centre.`
  `04 — Strategy` / `Every post is a move.` / `The rocket lifts. Gold lines connect. Noise becomes a system.`
  `05 — Full-service` / `We move brands.` / `The street is the same. The window is not. The rocket leaves the sill.`
  `06 — MarkItUp` / `DM us to get started.` / `Consulting. Strategy. Full-service packages. The work begins in a message.`
  Data shown: static chapter table; visible chapter depends on scroll position.

  ELEMENT: @markitup.lb (shown on last chapter)
    Action: external link
    Destination: `https://www.instagram.com/markitup.lb/`
    Writes to: none
    Reads from: Visible when `chapter.showCta` is true (signal chapter)

---

## Notes

- `/courses?q=` and `?university=` hydrate catalog search. University filters use `universities.short_name` slugs.
- Course request forms use `#course-request`. Tutoring help on `/tutoring` keeps its own `#request` form.
- Pricing cards use code `PLANS`; homepage prices can follow `platform_settings.plan_overrides`.
- `platform_settings.show_pricing_publicly` is stored in admin but does not gate `/pricing`.
- Course request + tutoring request both insert `course_requests` (no tutoring table).
- Marketing **Get access** / plan CTAs enter [03-checkout.md](./03-checkout.md); **Sign in** enters [02-auth-onboarding.md](./02-auth-onboarding.md).
