# S1-whole-surface — screen inventory

| | |
| --- | --- |
| Title (ar) | الواجهة الكاملة — المرحلة الأولى |
| Title (en) | Stage 1 whole surface |
| Owning epic | D0.3 |
| Reviewer | Raheem takes the decisions; Zayed records them and locks |
| Timebox | 3 days |
| Opened | 2026-09-16 |
| Status | draft |

## The question

Six prototypes are queued, one per area, and every one of them would answer the
same six questions again — and answer them differently. What the top-level
entries are, whether administration is a different world or a nav group, where
the club area lives, what shape an approach has, what a signed-in person opens
the product on, and what a refusal looks like: none of those belong to an area,
and all of them are cheap to change now and dear to change once six areas have
each assumed something. So this prototype goes one to two screens deep across
**every** journey instead of going deep in one, and every screen in it exists to
make one of those six questions answerable by clicking rather than arguing.

When it is locked, the shell is decided: a builder knows the navigation, the
landing, where club work lives, whether the admin screens share the shell, what
an approach looks like from the outside, and the single refusal pattern every
epic's refusals render into. The deep prototypes then start from a settled
surface and each one only has to answer its own area's questions.

Two things are deliberately *not* re-asked here. `P1-market-journey` is locked
and its four decisions hold, so the market search and the profile page are the
P1 screens **as they are**, linked from this prototype's navigation rather than
rebuilt: re-chroming them would mean copying a locked artefact and would reopen
D1–D4 by accident. And the gap ledger's verdicts hold, so nothing rejected there
— scores, grades, per-record access lists, in-app chat, a typed document
register, first-run tours, dashboard statistics — appears anywhere below.

## Decisions to settle

| Id | Decision | Options | Settled in |
| --- | --- | --- | --- |
| S1 | What are the top-level navigation entries, and do the scaffold's seven survive the arrival of needs, notifications and administration? | a) a fixed rail with grouped entries — `?nav=rail` (default) b) a top bar with the entries in one row and an overflow menu — `?nav=top` | findings.md |
| S2 | Is administration a different visual world, or the same shell with a different navigation group? | a) same shell, admin as a navigation group — `admin.html?admin=inline` (default) b) its own chrome, its own navigation, a way back — `admin.html?admin=separate` | findings.md |
| S3 | Where does the club area live? | a) one navigation entry that opens an area with its own sub-navigation — `?club=area` (default) b) each club screen as its own top-level entry — `?club=flat` | findings.md |
| S4 | What shape is an approach — a thread, a record, or a row on a board? | a) thread — `offer.html?deal=thread` (default) b) record with a history — `offer.html?deal=record` c) board by state — `requests.html?deal=board` | findings.md |
| S5 | What does a signed-in user land on? | a) the work waiting on them — `index.html?landing=work` (default) b) a launcher: one card per area with one real number each — `index.html?landing=launcher` | findings.md |
| S6 | What is the one denial pattern, used for all four refusals? | a) in place, inside the screen, the context intact — `?denial=inline` (default) b) a refusal screen that replaces the content region — `?denial=page` | findings.md |

### What the reviewer must answer

- **S1** — The scaffold guessed seven entries: home, market, watchlist,
  directory, requests, agent, club. Needs, notifications and administration have
  arrived since, and two of the seven are conditional on who is signed in. The
  rail groups them — *find someone*, *my work*, *my club*, *administration* —
  and the top bar cannot, so it pushes the overflow into a menu. Answer: which
  shape, what the final entry list is, and whether an entry that only some
  people ever see (agent, administration) is hidden or shown disabled.
- **S2** — Administration is a handful of screens for a handful of people, but
  it is where reference data, claims and club approvals live, and a mistake there
  is expensive. A separate world says *you are somewhere else now*; the same
  shell says *this is the same product, you simply have more of it*. Answer: one
  of them, and if it is the same shell, what tells a platform admin that the
  screen they are on writes reference data for everyone.
- **S3** — The club area is five screens deep on its own (profile and members,
  structure, roster and contracts, needs, the club's sent approaches) and every
  one of them is club-scoped work rather than market work. Flattening them puts
  four more entries in the main navigation; nesting them costs a click and makes
  the club a place. Answer: area or flat, and if area, whether the sub-navigation
  is a second rail, a tab strip, or a list on the area's own landing.
- **S4** — The offer state machine is server-enforced and every transition
  stores who, when and the terms at that moment. A thread shows the negotiation;
  a record shows the terms and demotes the history; a board shows the club's
  whole pipeline and hides the terms. They are not exclusive — the answer may be
  a board of records whose detail is a thread. Answer: the primary shape for the
  club side, the primary shape for the professional and agent side, and whether
  the two are allowed to differ.
- **S5** — Landing on the market privileges the push side of the marketplace on
  the day the pull side arrives, and the reference demo's landing invented
  statistics for an account minutes old. The work queue answers "what do I owe";
  the launcher answers "what is here". Answer: which one, whether it changes by
  role, and — the part that outlives the prototype — whether the landing is
  allowed to carry any number that is not a count of real, clickable work.
- **S6** — Four refusals, one pattern: a pending club refused a transaction, a
  basic-tier viewer meeting a private field, an eligibility refusal, and a route
  a role may not open. Each one names the rule and, where one exists, the date it
  changes. The question is the shape, never whether it explains. Answer: in place
  or a screen of its own, and whether a locked private field is the same pattern
  at a smaller size or a different thing altogether.

## Screens

Every file is in this folder; sub-screens are extra HTML files, never
subfolders. `s1.js` renders the chrome only — navigation, account menu,
notification badge, the variant bar — so that the navigation decision is made
once and every screen inherits it. Each screen's own content is hand-authored
HTML with the Arabic and English on the element.

| # | Screen | File | Purpose | Actor | Settles |
| --- | --- | --- | --- | --- | --- |
| 1 | Landing | `index.html` | What a signed-in club member opens the product on. Two candidate answers on one screen. Also the shell in its ordinary state. | Club member | S5, S1, S3 |
| 2 | Market search | `../P1-market-journey/index.html` | The market, reused verbatim from the locked P1. Reached from the navigation. | Club member | — |
| 3 | Profile detail | `../P1-market-journey/detail.html` | The full profile, basic and private tiers, reused verbatim from the locked P1. | Club member | — |
| 4 | Watchlist | `watchlist.html` | The club's one shared list, with the per-entry priority, club-private status, note and the four alarm toggles. | Club member | S1 |
| 5 | Needs board | `needs.html` | The club's needs by state — open, filled, withdrawn, expired — each with its application count and the window it must be filled in. | Club member | S3, S6 |
| 6 | One need | `need.html` | A need with its terms and its applications, each application reviewable; an applicant's private field is locked at the basic tier. | Club member | S6 |
| 7 | Requests inbox | `requests.html` | Approaches addressed to a professional or to an agent on a client's behalf, by state. The board variant of S4 lives here. | Professional, agent | S4 |
| 8 | Offer thread | `offer.html` | One approach in full: the terms, the transitions, a counter, the owning club's consent. | Professional or agent, and the club | S4, S6 |
| 9 | Club profile and members | `club.html` | The club's own record, its status, its members and the owner flag. Pending is a state of this screen and a refused transaction is what it shows. | Club member | S3, S6 |
| 10 | Club structure | `club-structure.html` | Sport sections, the teams inside them for a chosen season, and the staff on each team. | Club member | S3 |
| 11 | Roster and contracts | `club-roster.html` | The roster with contract dates, and the three work queues: ending within thirty days, within twelve months, and no contract recorded. | Club member | S3 |
| 12 | Agent dashboard | `agent.html` | Clients by mandate state, and the items addressed to the agent on a client's behalf. | Agent | S1, S4 |
| 13 | Notifications | `notifications.html` | The inbox, newest first, with the unread count that the chrome's badge reads. | Any signed-in user | S1 |
| 14 | Administration | `admin.html` | One reference-data list and one review queue whose decision requires a reason. Both chrome variants of S2 render here. | Platform admin | S2, S6 |
| 15 | Public club page | `public.html` | What a visitor with no account sees of a club: structure and a squad list, and nothing personal beyond it. | Visitor | S1 |
| 16 | Entitlements | `entitlements.html` | The module registry with each module's state and dependencies, a refused activation that names the missing dependency, and the free plan active for everyone. | Club owner | S6 |

### The market and the profile are P1's, not this prototype's

Screens 2 and 3 are `P1-market-journey/index.html` and
`P1-market-journey/detail.html` opened from this prototype's navigation. They
are not copied into this folder and not re-chromed. P1 is locked; a copy with
this prototype's shell around it would be an edit of a locked artefact in all
but name, and the four decisions P1 settled are exactly the ones a redesign
would reopen. The cost is visible and deliberate: the navigation disappears on
those two screens, and the reviewer returns with the browser's back button. That
is itself a finding to record — a locked prototype and a live shell cannot share
a chrome, and the Next.js app is where they finally do.

### Switches carried in the URL

Toggles, not states. They multiply the screenshots, not the markup.

| Parameter | Values | Screens | What it changes |
| --- | --- | --- | --- |
| `locale` | `ar` (default), `en` | all | handled by `shared/proto.js` |
| `theme` | `light` (default), `dark` | all | handled by `shared/proto.js` |
| `state` | see the table below | all | handled by `shared/proto.js` |
| `nav` | `rail` (default), `top` | all | **S1**: the shape of the navigation. The entries are the same in both; how many are visible without a menu is not |
| `club` | `area` (default), `flat` | all | **S3**: one club entry with a sub-navigation, or four club entries in the main navigation |
| `admin` | `inline` (default), `separate` | `admin.html` | **S2**: the same chrome with an administration group, or its own chrome with a way back |
| `landing` | `work` (default), `launcher` | `index.html` | **S5**: the work waiting on you, or a card per area |
| `deal` | `thread` (default), `record`, `board` | `requests.html`, `offer.html` | **S4**: the shape of an approach. `board` on the inbox, `thread` and `record` on the approach itself |
| `denial` | `inline` (default), `page` | `club.html`, `need.html`, `offer.html`, `admin.html` | **S6**: a refusal in place, or a refusal that replaces the content region |
| `tier` | `basic` (default), `private` | `need.html`, `public.html` | Which read tier the viewer holds. A private field is a locked placeholder naming the tier, never a gap |
| `role` | `club` (default), `pro`, `agent`, `admin` | all | Who is signed in, and therefore which navigation entries exist at all. Not a decision of its own; it is how S1's conditional entries are shown |

The variant bar that carries these sits under the prototype header on every
screen, is hidden below 64rem so a phone screenshot shows the product rather
than the paperwork, and never appears in the product chrome itself.

## States per screen

Only the states that teach something are built. The vocabulary is the repo's:
`default`, `loading`, `empty`, `error`, `partial`, `no-permission`.

| Screen | default | loading | empty | error | partial | no-permission | What the extra state is |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `index.html` | ✓ | | ✓ | | | | `empty`: a club member with nothing owed and nothing in flight — the landing's honest first week |
| `watchlist.html` | ✓ | | ✓ | | | | `empty`: a club that has watched nobody yet |
| `needs.html` | ✓ | | ✓ | | | ✓ | `empty`: a club with no needs; `no-permission`: publishing refused while the club is pending |
| `need.html` | ✓ | | ✓ | | | | `empty`: an open need nobody has applied to |
| `requests.html` | ✓ | | ✓ | | | | `empty`: an inbox with nothing in it |
| `offer.html` | ✓ | | | | ✓ | ✓ | `partial`: accepted by both parties, waiting on the owning club's consent; `no-permission`: the eligibility refusal, naming the rule and the date the window opens |
| `club.html` | ✓ | | | | | ✓ | `no-permission`: a pending club refused a transaction |
| `club-structure.html` | ✓ | | ✓ | | | | `empty`: an approved club that has created no section yet |
| `club-roster.html` | ✓ | | | | ✓ | | `partial`: roster rows with no contract recorded — the queue that exists so the eligibility engine is never deciding on a gap |
| `agent.html` | ✓ | | ✓ | | | | `empty`: an agent with no confirmed mandate yet |
| `notifications.html` | ✓ | | ✓ | | | | `empty`: nothing has happened to this account yet |
| `admin.html` | ✓ | | | | | ✓ | `no-permission`: a club member who opened an administration route |
| `public.html` | ✓ | | ✓ | | | | `empty`: an approved club with no published squad |
| `entitlements.html` | ✓ | | | ✓ | | | `error`: an activation refused for a missing dependency, naming it |

`error` on `entitlements.html` is a refusal, not a failure, and it renders in the
S6 pattern like every other refusal. It is filed as `error` rather than
`no-permission` because the person is entitled to the screen and to the action;
it is the dependency that is missing.

## The six decisions, screen by screen

### S1 — navigation and information architecture

The proposal the prototype renders, for the reviewer to accept, cut or extend:

| Group | Entry | Route | Shown to |
| --- | --- | --- | --- |
| — | الرئيسية | `index.html` | everyone |
| ابحث عن محترف | سوق الانتقالات | P1 market | everyone |
| ابحث عن محترف | قائمة المتابعة | `watchlist.html` | club members |
| ابحث عن محترف | دليل الأندية | `public.html` (signed-in twin reserved) | everyone |
| عملي | الطلبات | `requests.html` | everyone |
| عملي | الإشعارات | `notifications.html` | everyone, with the unread badge |
| ناديي | ملف النادي والأعضاء | `club.html` | club members |
| ناديي | الهيكل الرياضي | `club-structure.html` | club members |
| ناديي | القائمة والعقود | `club-roster.html` | club members |
| ناديي | الاحتياجات | `needs.html` | club members |
| ناديي | الوحدات والاشتراك | `entitlements.html` | the club owner only |
| وكالتي | عملائي | `agent.html` | agents only |
| الإدارة | البيانات المرجعية | `admin.html` | platform admins only |
| الإدارة | قوائم المراجعة | `admin.html?queue=claims` | platform admins only |

So: the scaffold's seven all survive, two of them change meaning. `home` becomes
the landing of S5; `club` becomes an area rather than a screen (S3); `agent`
becomes conditional; `directory` becomes the signed-in club directory with the
anonymous page as its public twin; `needs` and `notifications` are added; and
administration and entitlements arrive as their own groups. Under `?club=flat`
the four club entries and entitlements sit in the main list and the groups
collapse to one level.

### S2 — administration

`admin.html?admin=inline` renders the same header, the same rail and the same
type as every other screen, with an administration group in the navigation and a
band on the screen itself naming what the screen writes and for whom.
`?admin=separate` replaces the brand with an administration lozenge, drops every
market entry from the navigation, keeps only the administration entries and adds
a return link. The content — one reference-data list and one review queue — is
identical in both, so the reviewer is comparing chrome and nothing else.

### S3 — the club area

Under `?club=area` the rail carries one **ناديي** entry; opening it reveals a
sub-navigation across the four club screens plus entitlements, and the club's
name and status sit at the top of every one of them. Under `?club=flat` those
five become top-level entries, the sub-navigation disappears, and the club's
status has to be repeated on each screen or lost. The screens are the same; what
differs is how many entries the main navigation carries (nine against thirteen)
and whether the club is a place or a set of pages.

### S4 — the shape of an approach

One approach is modelled all the way through, in all three shapes:

- `requests.html?deal=thread` — the inbox as a list of conversations, each row
  the last transition and who owes the next move.
- `requests.html?deal=record` — the inbox as a list of records, each row the
  terms and the state.
- `requests.html?deal=board` — the same approaches as columns by state:
  مرسل، قيد المراجعة، عرض مقابل، مقبول، مرفوض.
- `offer.html?deal=thread` — the approach as a chronology: every transition with
  who, when and the terms at that moment, and the composer at the end.
- `offer.html?deal=record` — the approach as a record: the terms panel first, the
  state and the actions beside it, the history demoted to a list underneath.

The board is offered at the list level only. A board of one approach is a card,
and the reviewer should not be asked to judge that.

### S5 — the landing

`index.html?landing=work` opens on what is owed: items needing a reply, with a
count, a deadline where one exists and a link straight to the screen where the
action is taken, then the club's work in flight, then the two screens a club
member uses daily. `?landing=launcher` opens on the product: one card per area,
each carrying one real number, with the owed items folded into the first card.

A third candidate — the market as the landing — is named and not built. The
reason is on the record: needs arrived as the pull side of the marketplace, and
landing on the market decides in the shell that push wins. If the reviewer wants
it, it is a small change to the work variant and a separate finding.

Neither variant carries a number that is not a count of clickable work. No
profile views, no followers, no interactions, no trend. That is the one
non-negotiable in S5 and it is written here so the review can reject it
explicitly rather than by omission.

### S6 — the one denial pattern

Every refusal carries the same four parts, in the same order:

1. **What was refused**, in the words of the action that was attempted.
2. **The rule that refused it**, named — not a code, not an error number.
3. **The date it changes**, where a date exists, written with a month name.
4. **The one thing to do next**, or nothing at all when there is nothing to do.

Rendered in four places:

| Place | Refusal | Rule named | Date |
| --- | --- | --- | --- |
| `club.html?state=no-permission` | Sending an approach while the club's registration is under review | Pending clubs browse; approved clubs transact | none — it names the queue instead |
| `need.html?tier=basic` | An applicant's salary band, unread at the basic tier | Private-tier fields are salary band, contract dates, mandate details, documents | none |
| `offer.html?state=no-permission` | Approaching a contracted professional outside the window | The window opens six months before the contract ends | 1 يناير 2027 |
| `admin.html?state=no-permission` | Opening an administration route as a club member | Administration screens are for platform staff | none |

## Screens actually built and shot

One row per screen × state × variant × locale × viewport that exists in
`screenshots/`. Every URL is reproducible: paste it and you are looking at
exactly what the reviewer saw.

| Screen | State and variant | Locale | Viewport | Theme | File |
| --- | --- | --- | --- | --- | --- |
| index | default (`landing=work`) | ar | 1280×800 | light | `index--default--ar.png` |
| index | default (`landing=work`) | en | 1280×800 | light | `index--default--en.png` |
| index | default (`nav=top`) | ar | 1280×800 | light | `index--default--ar--topbar.png` |
| index | default (`club=flat`) | ar | 1280×800 | light | `index--default--ar--flat.png` |
| index | default | ar | 390×844 | light | `index--default--ar--mobile.png` |
| index | default | ar | 1280×800 | dark | `index--default--ar--dark.png` |
| index | default (`role=agent`) | ar | 1280×800 | light | `index--default--ar--agent.png` |
| index | default (`landing=launcher`) | ar | 1280×800 | light | `index--launcher--ar.png` |
| index | default (`landing=launcher`) | en | 1280×800 | light | `index--launcher--en.png` |
| index | empty | ar | 1280×800 | light | `index--empty--ar.png` |
| club | default | ar | 1280×800 | light | `club--default--ar.png` |
| club | no-permission (`denial=inline`) | ar | 1280×800 | light | `club--no-permission--ar.png` |
| club | no-permission (`denial=inline`) | en | 1280×800 | light | `club--no-permission--en.png` |
| club | no-permission (`denial=page`) | ar | 1280×800 | light | `club--no-permission--ar--page.png` |
| club-structure | default | ar | 1280×800 | light | `club-structure--default--ar.png` |
| club-roster | default | ar | 1280×800 | light | `club-roster--default--ar.png` |
| club-roster | partial | ar | 1280×800 | light | `club-roster--partial--ar.png` |
| needs | default | ar | 1280×800 | light | `needs--default--ar.png` |
| needs | empty | ar | 1280×800 | light | `needs--empty--ar.png` |
| needs | no-permission | ar | 1280×800 | light | `needs--no-permission--ar.png` |
| need | default (`tier=basic`) | ar | 1280×800 | light | `need--default--ar.png` |
| need | default (`tier=private`) | ar | 1280×800 | light | `need--private--ar.png` |
| requests | default (`deal=thread`) | ar | 1280×800 | light | `requests--default--ar.png` |
| requests | default (`deal=record`) | ar | 1280×800 | light | `requests--record--ar.png` |
| requests | default (`deal=board`) | ar | 1280×800 | light | `requests--board--ar.png` |
| requests | empty | ar | 1280×800 | light | `requests--empty--ar.png` |
| offer | default (`deal=thread`) | ar | 1280×800 | light | `offer--default--ar.png` |
| offer | default (`deal=thread`) | en | 1280×800 | light | `offer--default--en.png` |
| offer | default (`deal=record`) | ar | 1280×800 | light | `offer--record--ar.png` |
| offer | partial | ar | 1280×800 | light | `offer--partial--ar.png` |
| offer | no-permission (`denial=inline`) | ar | 1280×800 | light | `offer--no-permission--ar.png` |
| offer | no-permission (`denial=inline`) | en | 1280×800 | light | `offer--no-permission--en.png` |
| offer | no-permission (`denial=inline`) | ar | 1280×800 | dark | `offer--no-permission--ar--dark.png` |
| agent | default (`role=agent`) | ar | 1280×800 | light | `agent--default--ar.png` |
| watchlist | default | ar | 1280×800 | light | `watchlist--default--ar.png` |
| notifications | default | ar | 1280×800 | light | `notifications--default--ar.png` |
| admin | default (`admin=inline`) | ar | 1280×800 | light | `admin--default--ar.png` |
| admin | default (`admin=inline`) | en | 1280×800 | light | `admin--default--en.png` |
| admin | default (`admin=separate`) | ar | 1280×800 | light | `admin--default--ar--separate.png` |
| admin | no-permission | ar | 1280×800 | light | `admin--no-permission--ar.png` |
| public | default | ar | 1280×800 | light | `public--default--ar.png` |
| entitlements | default | ar | 1280×800 | light | `entitlements--default--ar.png` |
| entitlements | error | ar | 1280×800 | light | `entitlements--error--ar.png` |

## Fixture world

Every person, club and agency below is invented for testing and continues the
fixture set `P1-market-journey` already uses, so a name clicked in the market
and a name read here are the same person.

| | |
| --- | --- |
| Signed in as | ناصر العمري — club member and owner |
| His club | نادي الصحراء, football and basketball, approved |
| The pending club | نادي الصحراء again, with its status set to pending — the same screen in its other condition |
| The watched professional | فيصل الدوسري, striker, نادي النهضة, contract ends 30 يونيو 2027 |
| His agent | ريم المالكي, exclusive mandate until 31 يناير 2027 |
| Other names used | لوكاس بيريرا, مامادو ديوب, تركي الحربي, عبدالله القحطاني, سلطان الرشيد, ماجد الحربي |
| Other clubs | نادي النهضة, اتحاد الوسام, نادي محاربي نجد, نادي صقور الحجاز, نادي نسور تبوك |
| Seasons | 2025/26 current, 2026/27 next |
| Registration window | النافذة الصيفية 2026: 1 يونيو — 31 أغسطس 2026, closed; النافذة الشتوية 2027: 1 — 31 يناير 2027, upcoming |

## Out of scope

Deliberately not answered here, so the review does not drift into it:

- Every third screen. No journey gets one. No form gets every field. The needs
  form, the offer composer, the club registration form and the admin editors are
  each represented by the one screen that shows their shape, never by the form.
- The market and the profile page. They are P1's, locked, and reused as they are.
- Sign-in, sign-up, self-onboarding and claiming a profile. They are P2's, and
  nothing in the six decisions turns on them.
- The sport switch, the filter grammar, facet counts, card density and the table
  columns. All four are locked by P1 and reopening them is out of bounds.
- Tournament entries, squad list editing, friendly matches, fixtures and
  results, the ads marketplace and sponsor registration. Each is one screen in a
  later prototype; none of them changes the shell.
- Anything the gap ledger rejected on 16 September 2026.

## Open questions

Surfaced by building this and not settleable by it alone:

- The notification badge is in the chrome and the inbox is a navigation entry.
  If a club of fifteen people share one watchlist and one set of needs, are the
  notifications a club's or a person's? The alarms are club-scoped and the
  mandate events are personal, and the same badge counts both.
- The landing is role-aware, but a person can be a club member, an agent and a
  professional at once. The prototype renders one role at a time via `?role=`.
  Nothing on the screen yet says which hat you are wearing or offers to change it.
- The club directory has a signed-in twin and an anonymous one. The prototype
  navigates to the anonymous page from the signed-in shell, which is honest about
  the data but wrong about the chrome. Whether they are one route rendered twice
  or two routes is a question for the public-directory prototype.
- A refusal that names a date has to name it in the reader's calendar and locale.
  The Arabic strings here use month names, as the style guide requires, but the
  date itself is fixture text rather than a formatted value; the app will format
  it, and the format is not settled anywhere.
- The board variant of S4 shows a club's pipeline. If the answer to S4 is the
  board, nothing yet says whether the professional's inbox is also a board or
  stays a list — the two sides have different cardinality and the same states.

## Not built

Anything the timebox did not reach is listed here, so a half-built prototype
says which half is missing. Empty at the time of writing.
