docs: client-approved public-site redesign — PRD 1.4, UX spec v2.0, architecture §6.2, tasks 24-x Ready for Dev
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -116,7 +116,7 @@ php artisan make:model ModelName -mf
|
|||||||
- `app/Enums/` - UserType, UserStatus, ConsultationType, ConsultationStatus, PaymentStatus, TimelineStatus, PostStatus
|
- `app/Enums/` - UserType, UserStatus, ConsultationType, ConsultationStatus, PaymentStatus, TimelineStatus, PostStatus
|
||||||
- `docs/prd.md` - Full product requirements document
|
- `docs/prd.md` - Full product requirements document
|
||||||
- `docs/architecture.md` - Complete architecture document
|
- `docs/architecture.md` - Complete architecture document
|
||||||
- `docs/ux-design.md` - Design system reference (colors, typography, layout, components — retrospective, written 2026-09-27)
|
- `docs/ux-design.md` - Design spec. **Part A** = the public-site redesign (approved by the client 2026-10-31, mockup `docs/ux-mockup.html`); **Part B** = the pre-redesign system, which still governs the dashboards, emails and the Nablus form
|
||||||
- `docs/tasks/` - **Current task/status tracker** (`statuses.md` is the status source of truth). One row per epic (parent) and per story (subtask), e.g. `3.md` / `3-4.md`.
|
- `docs/tasks/` - **Current task/status tracker** (`statuses.md` is the status source of truth). One row per epic (parent) and per story (subtask), e.g. `3.md` / `3-4.md`.
|
||||||
- `docs/stories/`, `docs/epics/`, `docs/qa/gates/` - **Legacy.** Original BMAD-agent build records (98→102 stories across 16 epics). Kept for implementation history and traceability, no longer the status source — `docs/tasks/statuses.md` superseded them 2026-09-27. `.bmad-core/` and `.claude/commands/BMad/` are the retired BMAD toolchain, also kept but unused.
|
- `docs/stories/`, `docs/epics/`, `docs/qa/gates/` - **Legacy.** Original BMAD-agent build records (98→102 stories across 16 epics). Kept for implementation history and traceability, no longer the status source — `docs/tasks/statuses.md` superseded them 2026-09-27. `.bmad-core/` and `.claude/commands/BMad/` are the retired BMAD toolchain, also kept but unused.
|
||||||
|
|
||||||
@@ -246,7 +246,25 @@ Located in `app/Notifications/`:
|
|||||||
- **Cascade deletes:** User deletion cascades to consultations, timelines, notifications
|
- **Cascade deletes:** User deletion cascades to consultations, timelines, notifications
|
||||||
- **Bilingual required:** All user-facing text needs Arabic and English
|
- **Bilingual required:** All user-facing text needs Arabic and English
|
||||||
|
|
||||||
|
## Public-site redesign (PRD 1.4, tasks 24-x)
|
||||||
|
|
||||||
|
The public pages (home, about, posts, booking, legal) and the auth pages' type/controls follow
|
||||||
|
`docs/ux-design.md` Part A; the map is `docs/architecture.md` §6.2. Boundaries that keep the
|
||||||
|
dashboards safe:
|
||||||
|
- Redesign styles live in `resources/css/public.css`, every selector under `body.site` (auth:
|
||||||
|
`body.site-auth`); its script is `resources/js/public.js` + `resources/js/site/*`, initialised on
|
||||||
|
`livewire:navigated`. Both load only from the public (and, CSS only, auth) layouts.
|
||||||
|
- Never edit `resources/css/app.css` `@theme`/`.btn-*`, `components/language-toggle.blade.php`,
|
||||||
|
or `lang/*/navigation.php` for the redesign — the dashboards share them. New public copy goes in
|
||||||
|
`lang/{ar,en}/site.php`.
|
||||||
|
- `availability-calendar` defaults to `variant="month"` (the client dashboard uses it); the home
|
||||||
|
page uses `variant="strip"`. `pages.booking` takes `compact` for the home card; booking rules are
|
||||||
|
shared and unchanged.
|
||||||
|
|
||||||
## Design Requirements (from PRD)
|
## Design Requirements (from PRD)
|
||||||
|
|
||||||
|
Below: the dashboards' palette and rules. Public pages: Part A of `docs/ux-design.md`.
|
||||||
|
|
||||||
- **Color scheme:**
|
- **Color scheme:**
|
||||||
- Dark Forest Green (#2D3624) - Header/Footer backgrounds
|
- Dark Forest Green (#2D3624) - Header/Footer backgrounds
|
||||||
- Warm Gold (#A68966) - CTA buttons, links, accents
|
- Warm Gold (#A68966) - CTA buttons, links, accents
|
||||||
|
|||||||
+64
-2
@@ -1,7 +1,7 @@
|
|||||||
# Libra Law Firm - Architecture Document
|
# Libra Law Firm - Architecture Document
|
||||||
|
|
||||||
**Version:** 1.1
|
**Version:** 1.2
|
||||||
**Date:** December 21, 2025
|
**Date:** October 31, 2026
|
||||||
**Status:** Approved
|
**Status:** Approved
|
||||||
**Domain:** libra.ps
|
**Domain:** libra.ps
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
|------|---------|-------------|--------|
|
|------|---------|-------------|--------|
|
||||||
| Dec 21, 2025 | 1.0 | Initial architecture document | Winston (Architect) |
|
| Dec 21, 2025 | 1.0 | Initial architecture document | Winston (Architect) |
|
||||||
| Dec 21, 2025 | 1.1 | Added CI/CD, rollback procedures, monitoring, timezone handling, alternatives analysis | Winston (Architect) |
|
| Dec 21, 2025 | 1.1 | Added CI/CD, rollback procedures, monitoring, timezone handling, alternatives analysis | Winston (Architect) |
|
||||||
|
| Oct 31, 2026 | 1.2 | Added §6.2 Public-site presentation layer (PRD 1.4 redesign, ux-design.md v2.0 Part A) | Jarvis |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1008,6 +1009,67 @@ libra/
|
|||||||
└── build/ # Compiled assets
|
└── build/ # Compiled assets
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> §6.1 is the original (Dec 2025) plan. As built, the public layout is
|
||||||
|
> `resources/views/components/layouts/public.blade.php` (not `guest.blade.php`) and public pages
|
||||||
|
> are Volt components under `resources/views/livewire/pages/`. §6.2 is current.
|
||||||
|
|
||||||
|
### 6.2 Public-Site Presentation Layer (redesign, PRD 1.4)
|
||||||
|
|
||||||
|
**Overview.** The October 2026 redesign (PRD Update 1.4; `docs/ux-design.md` Part A) is a
|
||||||
|
presentation-only change to the public pages. No route, model, migration, service, mail or job
|
||||||
|
changes. The redesign is isolated from the admin and client dashboards by loading its stylesheet
|
||||||
|
and script only from the public and auth layouts, and by scoping every rule under a body class.
|
||||||
|
|
||||||
|
**Components**
|
||||||
|
|
||||||
|
| Component | Path | Owns |
|
||||||
|
|---|---|---|
|
||||||
|
| Public stylesheet | `resources/css/public.css` (new Vite entry) | Part A tokens (`--forest`, `--gold`, …), linen ground, type, buttons, sections, cards, docket rows, booking card, footer, reveal and reduced-motion rules. Every selector is scoped under `body.site`; auth-page rules under `body.site-auth`. |
|
||||||
|
| Public script | `resources/js/public.js` (new Vite entry) imports `resources/js/site/balance.js`, `reveal.js`, `progress.js` | The balance SVG builder and damped spring, hero pointer response, finale scroll mapping, IntersectionObserver reveal, reading-progress rule, nav scrolled state. Initialises on `livewire:navigated` (fires on first load and after every `wire:navigate`) and tears down listeners on `livewire:navigating`. |
|
||||||
|
| Font partial | `resources/views/partials/site-fonts.blade.php` (new) | Google Fonts link for Markazi Text (400–700) and IBM Plex Sans Arabic (300–600), `display=swap`. |
|
||||||
|
| Public layout | `resources/views/components/layouts/public.blade.php` (changed) | Body class `site`; includes the font partial and `@vite(['resources/css/public.css','resources/js/public.js'])` after `partials.head`; drops the 1200px content wrapper and the 64px fixed-nav spacer (nav becomes sticky); adds the progress rule element. |
|
||||||
|
| Auth layouts | `resources/views/components/layouts/auth/split.blade.php`, `card.blade.php`, `simple.blade.php` (changed) | Body class `site-auth`; font partial; `public.css` only (no public script). Composition unchanged. |
|
||||||
|
| Navigation / footer | `resources/views/components/navigation.blade.php`, `footer.blade.php` (restyled in place; used only by the public layout) | Part A nav and footer. Keeps the existing Alpine mobile menu, auth-aware links and language toggle. |
|
||||||
|
| Site Blade components | `resources/views/components/site/` (new): `balance.blade.php` (SVG mount point; props `id`, `size`, `mode` = `hero` / `finale` / `static`), `section-head.blade.php` (article label + h2 + lede), `post-card.blade.php` (text-only post card) | Shared markup for the public pages. |
|
||||||
|
| Redesign copy | `lang/ar/site.php`, `lang/en/site.php` (new) | Strings introduced by the redesign only (hero line, trust strip, pillars, article labels, card titles, booking lede, nav and footer labels). Existing keys in `home.php`, `about.php`, `booking.php`, `footer.php`, `navigation.php` are reused, never duplicated or edited. `navigation.php` keys used by the dashboards are untouched. |
|
||||||
|
| Photography | `public/images/site/olive-tree.jpg`, `embroidery-hoop.jpg`, `olives.jpg`, `hills.jpg` (new; extracted from the data URIs in `docs/ux-mockup.html`) | Production images (Unsplash licence). Portrait stays `public/images/huda-armouche.jpg`; seal uses `public/images/logo.png`. |
|
||||||
|
| Guest booking | `resources/views/livewire/pages/booking.blade.php` (changed) | Gains `public bool $compact = false` (mount parameter). `compact` renders the one-screen home layout; default renders the `/booking` page layout. Validation, captcha, one-per-day rule, confirm step and redirect to `booking.success` are shared and unchanged. |
|
||||||
|
| Availability calendar | `resources/views/livewire/availability-calendar.blade.php` (changed) | Gains `public string $variant = 'month'`. `strip` lists the next 14 dates whose `AvailabilityService::getDateStatus()` is available (scanning at most 60 days ahead) and their `getAvailableSlots()`; `month` is the existing grid. Default stays `month`, so the client dashboard's booking page (`resources/views/livewire/client/consultations/book.blade.php`) is unaffected; its public restyle applies only under `body.site`. |
|
||||||
|
|
||||||
|
**Data flows**
|
||||||
|
|
||||||
|
1. Request to a public route → Volt page with `#[Layout('components.layouts.public')]` → layout
|
||||||
|
loads `app.css` + `app.js` (unchanged, via `partials.head`), then `public.css` + `public.js`.
|
||||||
|
2. Home `/` → `Post::published()->latest('published_at')->take(3)` (existing) → `x-site.post-card`;
|
||||||
|
embeds `<livewire:pages.booking :compact="true" />`, which embeds
|
||||||
|
`<livewire:availability-calendar variant="strip" />`. Slot choice → `$parent.selectSlot(date,
|
||||||
|
time)` (existing contract) → submit → existing confirm step → existing `submit()` → redirect
|
||||||
|
`/booking/success`.
|
||||||
|
3. `public.js` on `livewire:navigated` → finds `[data-balance]` mounts → builds the SVG → binds
|
||||||
|
pointer (hero, fine pointers only) and scroll (finale) → spring per `requestAnimationFrame`.
|
||||||
|
`prefers-reduced-motion: reduce` → every balance drawn level, no listeners bound.
|
||||||
|
4. Language: unchanged — `/language/{locale}` sets the session locale; layouts set `lang` and
|
||||||
|
`dir`.
|
||||||
|
|
||||||
|
**Module boundaries**
|
||||||
|
|
||||||
|
- The redesign owns `public.css`, `public.js`, `resources/js/site/*`, `components/site/*`,
|
||||||
|
`lang/*/site.php`, `public/images/site/*`, and the restyle of the public views listed above.
|
||||||
|
- It does not own and must not change: `resources/css/app.css` `@theme` tokens and `.btn-*`
|
||||||
|
classes, Flux configuration, anything under `resources/views/livewire/admin/` or
|
||||||
|
`livewire/client/`, `components/layouts/app*`, emails, PDFs,
|
||||||
|
`resources/views/livewire/pages/forms/nablus-workshop.blade.php`, `layouts/standalone.blade.php`,
|
||||||
|
routes, models, services (read-only use of `AvailabilityService`), migrations.
|
||||||
|
- Pages restyled: `livewire/pages/home.blade.php`, `about.blade.php`, `booking.blade.php`,
|
||||||
|
`booking-success.blade.php`, `posts/index.blade.php`, `posts/show.blade.php`,
|
||||||
|
`resources/views/pages/legal.blade.php` (and the `terms`/`privacy` wrappers if they render
|
||||||
|
markup).
|
||||||
|
- `partials/head.blade.php`: `theme-color` corrected to `#2D3624` (was the superseded
|
||||||
|
`#4A4A42`); nothing else changes there, because the dashboards share it.
|
||||||
|
|
||||||
|
**Open gaps** — none blocking. The four rulings (photography, 14-day strip, unmocked pages, auth
|
||||||
|
scope) are recorded in `docs/ux-design.md` Part A *Open Questions* and the project ledger.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Authentication & Authorization
|
## 7. Authentication & Authorization
|
||||||
|
|||||||
+57
-1
@@ -1,7 +1,7 @@
|
|||||||
# Product Requirements Document (PRD)
|
# Product Requirements Document (PRD)
|
||||||
## Libra Law Firm Website
|
## Libra Law Firm Website
|
||||||
|
|
||||||
**Version:** 1.2
|
**Version:** 1.4
|
||||||
**Date:** January 3, 2026
|
**Date:** January 3, 2026
|
||||||
**Domain:** libra.ps
|
**Domain:** libra.ps
|
||||||
**Prepared for:** Development Team & Client Review
|
**Prepared for:** Development Team & Client Review
|
||||||
@@ -1106,6 +1106,7 @@ All emails in both Arabic and English based on user preference:
|
|||||||
| 1.1 | Dec 20, 2025 | Added brand identity section, clarified booking rules (1/day, 45min), multiple timelines, account management (deactivate/delete), exports, analytics, Terms/Privacy pages | Development Team |
|
| 1.1 | Dec 20, 2025 | Added brand identity section, clarified booking rules (1/day, 45min), multiple timelines, account management (deactivate/delete), exports, analytics, Terms/Privacy pages | Development Team |
|
||||||
| 1.2 | Jan 3, 2026 | Added guest booking functionality: public booking form at /booking, custom captcha, 1-per-day limit, guest email notifications, admin guest booking management | Development Team |
|
| 1.2 | Jan 3, 2026 | Added guest booking functionality: public booking form at /booking, custom captcha, 1-per-day limit, guest email notifications, admin guest booking management | Development Team |
|
||||||
| 1.3 | Sep 27, 2026 | Added standalone Nablus workshop registration form (unlinked page at /forms/nablus-workshop) — see Update below | Development Team |
|
| 1.3 | Sep 27, 2026 | Added standalone Nablus workshop registration form (unlinked page at /forms/nablus-workshop) — see Update below | Development Team |
|
||||||
|
| 1.4 | Oct 31, 2026 | Public-site redesign approved by the client (mockup v3): new visual system for the public pages, compact booking card on the home page — see Update below | Development Team |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1145,4 +1146,59 @@ None outstanding — all decisions confirmed directly with Naser in the same ses
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Update — Version 1.4
|
||||||
|
**Date:** 2026-10-31
|
||||||
|
|
||||||
|
### New Features Added
|
||||||
|
|
||||||
|
**Public-site redesign.** ElenosLabs proposed a redesign of Libra's public website (mockup v3,
|
||||||
|
`docs/ux-mockup.html`); the client approved it on 2026-10-31. Scope answers given by Naser on
|
||||||
|
2026-10-31: scope "Public site" (home, about, posts, booking, auth); brand "Keep logo, evolve
|
||||||
|
rest" (the logo and the forest/gold/cream palette stay; type, layout and motion change); driver
|
||||||
|
"Our pitch". Naser's constraint on content: "we dont need to promice smth we wont deliver" — every
|
||||||
|
claim on the new pages is backed by existing behaviour. The full visual specification is
|
||||||
|
`docs/ux-design.md` Part A.
|
||||||
|
|
||||||
|
- **New visual system on every public page** — `/`, `/about`, `/posts`, `/posts/{post}`,
|
||||||
|
`/booking`, `/booking/success`, `/page/{slug}`: Markazi Text (display) and IBM Plex Sans Arabic
|
||||||
|
(body); the brand palette used in full-bleed forest bands, gold rules and a linen cream ground;
|
||||||
|
sections numbered as articles; a sticky translucent navigation with a reading-progress rule; a
|
||||||
|
four-column forest footer.
|
||||||
|
- **Signature balance** — an engraved-gold scales-of-justice graphic over olive-tree photography in
|
||||||
|
the home hero, settling into balance on load and responding to the pointer; a second balance in
|
||||||
|
the booking card comes level as the reader arrives. All motion respects
|
||||||
|
`prefers-reduced-motion`.
|
||||||
|
- **Redesigned home page** — hero, pillars (Justice, Dignity, Advocacy), founder, services docket,
|
||||||
|
vision with photography, values, latest three articles (text-only cards), and an embedded
|
||||||
|
booking card.
|
||||||
|
- **Compact booking on the home page** — the home page embeds the existing guest-booking form as
|
||||||
|
one screen (name, phone, email, a 14-day strip of days with free slots, slot buttons, case
|
||||||
|
summary, captcha), with the same rules as `/booking` (required fields, 20-character minimum
|
||||||
|
summary, captcha, one booking per email per day, slots from the availability settings, the
|
||||||
|
confirm step, the success page). This realises §5.1's "Embedded simplified booking form".
|
||||||
|
- **Auth pages** — the new type and form controls; the split layout is unchanged.
|
||||||
|
- **Production photography** — four licensed Unsplash photographs (olive tree, embroidery hoop,
|
||||||
|
olives, hills) and the founder's existing portrait.
|
||||||
|
|
||||||
|
### Scope Changes
|
||||||
|
|
||||||
|
- §5.1 and §7 (Design Requirements) are superseded for the public pages by `docs/ux-design.md`
|
||||||
|
Part A. §7's typography (Cairo/Montserrat), button radius and card specs continue to apply to
|
||||||
|
the admin and client dashboards, which are unchanged.
|
||||||
|
- §5.2 navigation gains a "Services" link (to the home page's services section); the menu labels
|
||||||
|
on the public site read Home, About, Services, Articles, Booking.
|
||||||
|
- Out of scope: admin and client dashboards, emails, PDF exports, `/forms/nablus-workshop`, any
|
||||||
|
backend or database change, post images or categories, new content beyond the redesign's
|
||||||
|
section labels and short lines approved in the mockup.
|
||||||
|
|
||||||
|
### Open Questions
|
||||||
|
|
||||||
|
None blocking. Rulings recorded in the project ledger (2026-10-31): the mockup's Unsplash photos
|
||||||
|
ship to production (replaceable by the client's own later); the home day strip covers the next 14
|
||||||
|
days with free slots; pages not shown in the mockup are composed from its components with their
|
||||||
|
existing content; auth pages change type and controls only. The build price is not yet set (a
|
||||||
|
commercial matter, not a product requirement).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
**END OF DOCUMENT**
|
**END OF DOCUMENT**
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
---
|
||||||
|
id: 24-1
|
||||||
|
title: 24-1 Public Design Foundation (Stylesheet, Script, Fonts, Layout)
|
||||||
|
status: Ready for Dev
|
||||||
|
date: 2026-10-31
|
||||||
|
parent: 24
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Create the shared foundation every later redesign subtask builds on. It must be loaded only by
|
||||||
|
the public layout (and, for the stylesheet only, by the auth layouts in task 24-9), and every rule
|
||||||
|
must be scoped under `body.site` so the admin and client dashboards are unaffected.
|
||||||
|
|
||||||
|
The approved visual reference is the mockup file `docs/ux-mockup.html` (committed in `caec396`).
|
||||||
|
Where this task says "port lines X–Y", copy those CSS rules from that file and prefix every
|
||||||
|
selector with `body.site ` (for example `.btn-gold` → `body.site .btn-gold`; `:root{…}` →
|
||||||
|
`body.site{…}`; `body{…}` → `body.site{…}`; `html[dir=ltr] .x` → `html[dir=ltr] body.site .x`;
|
||||||
|
`html[lang=en] .x` → `html[lang=en] body.site .x`). The mockup's lines contain no images, so line
|
||||||
|
numbers are exact.
|
||||||
|
|
||||||
|
### Files to create
|
||||||
|
|
||||||
|
1. **`resources/css/public.css`** — contains, in this order:
|
||||||
|
- Tokens on `body.site` (from mockup lines 12–29):
|
||||||
|
`--forest:#2D3624; --forest-2:#232A1C; --gold:#A68966; --gold-ink:#8A7555; --gold-lt:#C4A882;
|
||||||
|
--cream:#F4F1EA; --olive:#4A5D23; --ink:#1E2219; --muted:#5B6152; --line:rgba(45,54,36,.14);
|
||||||
|
--display:"Markazi Text","Times New Roman",serif; --body:"IBM Plex Sans Arabic",system-ui,sans-serif;
|
||||||
|
--r:10px; --ease:cubic-bezier(.2,.7,.2,1);
|
||||||
|
--shadow-1:0 1px 2px rgba(30,34,25,.06),0 4px 14px rgba(30,34,25,.06);
|
||||||
|
--shadow-2:0 2px 4px rgba(30,34,25,.08),0 18px 40px -12px rgba(30,34,25,.28);`
|
||||||
|
- Page ground and type on `body.site` (port lines 32–39): `font-family:var(--body);
|
||||||
|
color:var(--ink); font-size:17px; line-height:1.75; background-color:var(--cream)` plus the
|
||||||
|
linen weave `background-image: repeating-linear-gradient(0deg, rgba(45,54,36,.025) 0 1px,
|
||||||
|
transparent 1px 3px), repeating-linear-gradient(90deg, rgba(45,54,36,.022) 0 1px, transparent
|
||||||
|
1px 4px)`. This overrides the inline `style="font-family: var(--font-…)"` on the body, so the
|
||||||
|
layout's inline style is removed (see layout changes below).
|
||||||
|
- Port lines 40–50 (img, links, `.wrap`, skip link, focus-visible, h1–h3, `.eyebrow`,
|
||||||
|
`.art-no`). `.wrap` = `max-width:1200px; margin-inline:auto; padding-inline:28px`.
|
||||||
|
Focus = `outline:2px solid var(--gold); outline-offset:3px; border-radius:4px`.
|
||||||
|
- Buttons: port lines 52–68 (`.btn`, `.btn-gold` with shimmer `::after`, `.btn-ghost`,
|
||||||
|
`.on-dark .btn-ghost`, `.btn .arr` and LTR mirroring). Class names `.btn`, `.btn-gold`,
|
||||||
|
`.btn-ghost` do not exist in `resources/css/app.css` (which uses `.btn-primary`,
|
||||||
|
`.btn-secondary`), and they are scoped under `body.site`, so there is no collision.
|
||||||
|
- Progress rule: port lines 70–73 (`#hem` → rename to `.site-progress`; fixed, top 0, z 60,
|
||||||
|
height 2px, gradient `linear-gradient(90deg,#8A7555,#E2CDA4,#A68966)`, `transform:scaleX(0)`,
|
||||||
|
origin right; `html[dir=ltr]` origin left).
|
||||||
|
- Sections: port lines 139–144 (`section{position:relative}`, `.sec{padding:120px 0}`,
|
||||||
|
`.sec-head`, `.sec-head h2`, `.sec-head p`).
|
||||||
|
- Reveal: port lines 286–290, changed so content is hidden only when JavaScript runs:
|
||||||
|
`body.site.js [data-reveal]{opacity:0;transform:translateY(28px);transition:opacity .9s
|
||||||
|
var(--ease),transform .9s var(--ease)}`, `body.site.js [data-reveal].in{opacity:1;transform:none}`,
|
||||||
|
and the `data-d` 1–5 delays (.08s, .16s, .24s, .32s, .4s).
|
||||||
|
- Responsive base: at `max-width:760px`: `body.site{font-size:16px}`,
|
||||||
|
`body.site .wrap{padding-inline:18px}`, `body.site .sec{padding:80px 0}`.
|
||||||
|
- Reduced motion (port lines 324–327): under `@media (prefers-reduced-motion:reduce)`,
|
||||||
|
`body.site *, body.site *::before, body.site *::after{animation:none!important;
|
||||||
|
transition:none!important; scroll-behavior:auto!important}` and
|
||||||
|
`body.site [data-reveal]{opacity:1;transform:none}`.
|
||||||
|
- `html:has(body.site){scroll-behavior:smooth}`.
|
||||||
|
2. **`resources/js/public.js`** — imports `./site/reveal.js` and `./site/progress.js`, and calls
|
||||||
|
each module's `init()` on the `livewire:navigated` document event (Livewire 3 dispatches it on
|
||||||
|
the first page load and after every `wire:navigate`), and each module's `destroy()` on
|
||||||
|
`livewire:navigating`. On init it adds the class `js` to `document.body`.
|
||||||
|
3. **`resources/js/site/reveal.js`** — exports `init()`/`destroy()`. If
|
||||||
|
`matchMedia('(prefers-reduced-motion: reduce)').matches`, add class `in` to every
|
||||||
|
`[data-reveal]` and return. Otherwise create one `IntersectionObserver` with
|
||||||
|
`rootMargin: '0px 0px -8% 0px'`; when an observed element intersects, add class `in` and
|
||||||
|
unobserve it. `destroy()` disconnects the observer.
|
||||||
|
4. **`resources/js/site/progress.js`** — exports `init()`/`destroy()`. Adds a passive `scroll`
|
||||||
|
listener on `window` that sets `.site-progress` `style.transform = scaleX(p)` where
|
||||||
|
`p = max > 0 ? scrollY / max : 1` and `max = document.documentElement.scrollHeight - innerHeight`,
|
||||||
|
and toggles class `scrolled` on `#site-nav` when `scrollY > 8` (if that element exists; it is
|
||||||
|
added in task 24-2). Runs once on init. `destroy()` removes the listener.
|
||||||
|
5. **`resources/views/partials/site-fonts.blade.php`** — exactly:
|
||||||
|
```html
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Markazi+Text:wght@400;500;600;700&family=IBM+Plex+Sans+Arabic:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||||
|
```
|
||||||
|
6. **`lang/ar/site.php`** and **`lang/en/site.php`** — each `<?php return [ ... ];` with the
|
||||||
|
keys `progress_label` (ar `'تقدّم القراءة'`, en `'Reading progress'`). Later subtasks add keys
|
||||||
|
to these files.
|
||||||
|
|
||||||
|
### Files to modify
|
||||||
|
|
||||||
|
7. **`vite.config.js`** — `input` becomes
|
||||||
|
`['resources/css/app.css', 'resources/js/app.js', 'resources/css/public.css', 'resources/js/public.js']`.
|
||||||
|
8. **`resources/views/components/layouts/public.blade.php`**:
|
||||||
|
- After `@include('partials.head')` add `@include('partials.site-fonts')` and
|
||||||
|
`@vite(['resources/css/public.css', 'resources/js/public.js'])`.
|
||||||
|
- `<body>` becomes `<body class="site min-h-screen flex flex-col">` (remove `bg-background`
|
||||||
|
and the inline `style="font-family: …"`).
|
||||||
|
- Immediately after the skip link add
|
||||||
|
`<div class="site-progress" aria-hidden="true"></div>`.
|
||||||
|
- Remove the spacer `<div class="h-16"></div>` (the navigation becomes sticky in 24-2; until
|
||||||
|
then the existing fixed navigation may overlap the first 64px — acceptable only between
|
||||||
|
24-1 and 24-2).
|
||||||
|
- `<main>` keeps `id="main-content" role="main" class="flex-1" tabindex="-1"` but its child
|
||||||
|
`<div class="max-w-[1200px] mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-8">` is removed, so
|
||||||
|
`{{ $slot }}` is rendered directly inside `<main>`.
|
||||||
|
9. Because the wrapper is gone, wrap the root content of each existing public page that is not
|
||||||
|
yet redesigned in `<div class="wrap py-8">…</div>` so these pages keep a contained width until
|
||||||
|
their own subtask restyles them:
|
||||||
|
- `resources/views/livewire/pages/home.blade.php`
|
||||||
|
- `resources/views/livewire/pages/about.blade.php` (also remove the negative margins
|
||||||
|
`-mx-4 sm:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8` from its first `<section>`)
|
||||||
|
- `resources/views/livewire/pages/booking.blade.php`
|
||||||
|
- `resources/views/livewire/pages/booking-success.blade.php`
|
||||||
|
- `resources/views/livewire/pages/posts/index.blade.php`
|
||||||
|
- `resources/views/livewire/pages/posts/show.blade.php`
|
||||||
|
- `resources/views/pages/legal.blade.php`
|
||||||
|
- `resources/views/pages/terms.blade.php`
|
||||||
|
- `resources/views/pages/privacy.blade.php`
|
||||||
|
(`resources/views/pages/booking.blade.php` is a dead placeholder — `/booking` is served by the
|
||||||
|
Volt page — and is left untouched.)
|
||||||
|
For a Volt page whose root is a single element, put the wrapper inside the root element so the
|
||||||
|
component keeps one root.
|
||||||
|
10. **`resources/views/partials/head.blade.php`** — change
|
||||||
|
`<meta name="theme-color" content="#4A4A42">` to `<meta name="theme-color" content="#2D3624">`.
|
||||||
|
Change nothing else in this file (the dashboards share it).
|
||||||
|
|
||||||
|
Do not modify `resources/css/app.css` or any file under `resources/views/livewire/admin/`,
|
||||||
|
`resources/views/livewire/client/`, `resources/views/components/layouts/app*`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] `npm run build` succeeds and `public/build/manifest.json` contains entries for
|
||||||
|
`resources/css/public.css` and `resources/js/public.js`.
|
||||||
|
- [ ] Every selector in `resources/css/public.css` is scoped under `body.site` (or
|
||||||
|
`html[...] body.site`, or `html:has(body.site)`), verify:
|
||||||
|
`grep -nE '^[^@/ }].*\{' resources/css/public.css | grep -v 'body.site'` returns nothing.
|
||||||
|
- [ ] The HTML of `/` contains `<body class="site`, the `Markazi+Text` font link, a
|
||||||
|
`public.css` and a `public.js` asset, and `<div class="site-progress"`.
|
||||||
|
- [ ] The HTML of `/admin/dashboard` (logged in as admin) and `/client/dashboard` (logged in as a
|
||||||
|
client) contains neither `public.css`, `public.js`, `Markazi` nor `class="site`.
|
||||||
|
- [ ] `resources/css/app.css` is unchanged: `git diff --quiet BASE -- resources/css/app.css`.
|
||||||
|
- [ ] On `/` with the browser at 390px wide, the document has no horizontal scroll, and every
|
||||||
|
public page listed in item 9 still renders its content inside a 1200px-max container.
|
||||||
|
- [ ] Scrolling `/` changes the `.site-progress` element's transform from `scaleX(0)` toward
|
||||||
|
`scaleX(1)`; with `prefers-reduced-motion: reduce` emulated, every `[data-reveal]` element
|
||||||
|
is visible without scrolling.
|
||||||
|
- [ ] `partials/head.blade.php` contains `content="#2D3624"` and no `#4A4A42`.
|
||||||
|
|
||||||
|
## Sub-tasks
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
id: 24-10
|
||||||
|
title: 24-10 Redesign Integration Verification
|
||||||
|
status: Ready for Dev
|
||||||
|
date: 2026-10-31
|
||||||
|
parent: 24
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Verify the finished redesign as a whole before it is offered for deployment, and fix any defect
|
||||||
|
found within the files touched by tasks 24-1 to 24-9. This task adds no feature. Build the assets
|
||||||
|
(`npm run build`) first so the pages are not rendered from stale CSS.
|
||||||
|
|
||||||
|
Check, on the local site (`https://libra.test` via Valet, or `php artisan serve`), with at least
|
||||||
|
three published posts in the database and working hours configured so slots exist:
|
||||||
|
|
||||||
|
1. **Pages** — `/`, `/about`, `/posts`, one `/posts/{post}`, `/booking`, `/booking/success`,
|
||||||
|
`/page/terms`, `/page/privacy`, `/login`, `/forgot-password`: each in Arabic (RTL) and English
|
||||||
|
(LTR), each at 1440, 768 and 390px wide.
|
||||||
|
2. **Booking end-to-end** — one guest booking from the home card and one from `/booking`, each
|
||||||
|
reaching `/booking/success` and each appearing in the admin's pending bookings
|
||||||
|
(`/admin/bookings/pending`).
|
||||||
|
3. **Unchanged areas** — `/admin/dashboard`, `/admin/bookings/pending`, `/admin/posts`,
|
||||||
|
`/client/dashboard`, `/client/consultations/book`, `/forms/nablus-workshop`.
|
||||||
|
4. **Motion** — load `/` with and without `prefers-reduced-motion: reduce` emulated.
|
||||||
|
5. **Navigation** — move between public pages with the navigation links (`wire:navigate`) and back
|
||||||
|
to `/`.
|
||||||
|
|
||||||
|
Fix defects in the redesign's own files only: `resources/css/public.css`, `resources/js/public.js`,
|
||||||
|
`resources/js/site/*.js`, `resources/views/components/site/*.blade.php`,
|
||||||
|
`resources/views/components/navigation.blade.php`, `resources/views/components/footer.blade.php`,
|
||||||
|
`resources/views/components/layouts/public.blade.php`,
|
||||||
|
`resources/views/components/layouts/auth/*.blade.php`, `resources/views/partials/site-fonts.blade.php`,
|
||||||
|
`resources/views/livewire/pages/home.blade.php`, `about.blade.php`, `booking.blade.php`,
|
||||||
|
`booking-success.blade.php`, `posts/index.blade.php`, `posts/show.blade.php`,
|
||||||
|
`resources/views/livewire/availability-calendar.blade.php`, `resources/views/pages/legal.blade.php`,
|
||||||
|
`terms.blade.php`, `privacy.blade.php`, `lang/ar/site.php`, `lang/en/site.php`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Every page in item 1, in both languages and at all three widths, has no horizontal scroll
|
||||||
|
(`document.documentElement.scrollWidth <= innerWidth`), no console error, and no missing
|
||||||
|
translation key (no raw `site.` or `home.` key text visible).
|
||||||
|
- [ ] Each page in item 1 has exactly one `<h1>`, every `<img>` has a non-empty `alt`, and every
|
||||||
|
interactive element in the navigation, footer and booking card is reachable by Tab with a
|
||||||
|
visible gold focus ring.
|
||||||
|
- [ ] Both bookings in item 2 succeed and appear in `/admin/bookings/pending`.
|
||||||
|
- [ ] The pages in item 3 contain no `public.css`, `public.js`, `class="site` or `site-auth`, and
|
||||||
|
`git diff BASE -- resources/css/app.css resources/views/livewire/admin resources/views/livewire/client resources/views/components/layouts/app.blade.php resources/views/components/layouts/app resources/views/livewire/pages/forms`
|
||||||
|
(BASE = the commit recorded before task 24-1) is empty.
|
||||||
|
- [ ] With reduced motion emulated, all `[data-reveal]` content is visible on load and every
|
||||||
|
balance is level; without it, the hero balance settles and the finale levels on scroll.
|
||||||
|
- [ ] After five `wire:navigate` hops ending on `/`, the hero balance and reveal still work and the
|
||||||
|
finale still levels.
|
||||||
|
- [ ] Contrast: body text on cream, `--gold-lt` text on forest, and the gold button text meet
|
||||||
|
WCAG AA (4.5:1 for body text, 3:1 for large text) — measured, values noted in the task's QA
|
||||||
|
record.
|
||||||
|
- [ ] No page contains the text "Unsplash" or "mockup".
|
||||||
|
|
||||||
|
## Sub-tasks
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
---
|
||||||
|
id: 24-2
|
||||||
|
title: 24-2 Navigation and Footer
|
||||||
|
status: Ready for Dev
|
||||||
|
date: 2026-10-31
|
||||||
|
parent: 24
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Restyle the public navigation and footer to the approved redesign. Both components are used only
|
||||||
|
by `resources/views/components/layouts/public.blade.php`, so they are restyled in place. Keep all
|
||||||
|
existing behaviour: the Alpine `x-data="{ mobileMenuOpen: false }"` menu with its
|
||||||
|
`x-trap.inert.noscroll`, `@click.away` and `@keydown.escape.window` handling, the `@auth` branch
|
||||||
|
(Dashboard link to `route('admin.dashboard')` or `route('client.dashboard')` by
|
||||||
|
`auth()->user()->isAdmin()`, and the logout `<form method="POST" action="{{ route('logout') }}">`),
|
||||||
|
the language toggle's `/language/{locale}` links, and `wire:navigate` on internal links.
|
||||||
|
|
||||||
|
All CSS goes in `resources/css/public.css` (created in 24-1), every selector prefixed with
|
||||||
|
`body.site `. Port the CSS from `docs/ux-mockup.html`: navigation lines 75–98, footer lines
|
||||||
|
273–284, and the responsive nav/footer rules inside lines 293–323 (`.links` hidden and
|
||||||
|
`.menu-btn` shown at ≤1100px; `.nav .btn` and `.login` hidden at ≤760px; footer grid 2 columns at
|
||||||
|
≤1100px and 1 column at ≤760px).
|
||||||
|
|
||||||
|
### Navigation — `resources/views/components/navigation.blade.php`
|
||||||
|
|
||||||
|
- Root: `<header class="nav" id="site-nav">` — sticky (`position:sticky; top:0; z-index:50`),
|
||||||
|
height 72px, background `rgba(244,241,234,.82)`, `backdrop-filter: blur(14px) saturate(1.2)`
|
||||||
|
(with `-webkit-` prefix), bottom border transparent; with class `scrolled` (toggled by
|
||||||
|
`resources/js/site/progress.js` from 24-1 when `scrollY > 8`) the border becomes
|
||||||
|
`1px solid var(--line)` and the shadow `0 8px 24px -18px rgba(30,34,25,.5)`. Remove the old
|
||||||
|
fixed positioning and forest background.
|
||||||
|
- Inner `<div class="wrap">` laid out as flex, `align-items:center; gap:28px; height:72px`:
|
||||||
|
1. Brand link to `route('home')`: `<span class="seal">` (46×46px, background
|
||||||
|
`#fff url('/images/logo.png') center/cover no-repeat`, `border:2px solid #1A1A1A`,
|
||||||
|
`box-shadow:3px 3px 5px rgba(26,26,26,.35)`) then a two-line wordmark: `<b>` =
|
||||||
|
`__('site.brand_name')` (display font, 1.35rem, line-height 1) and `<small>` =
|
||||||
|
`__('site.brand_sub')` (.72rem, `--muted`; letter-spacing .12em in English).
|
||||||
|
`aria-label="{{ __('site.brand_label') }}"`.
|
||||||
|
2. `<nav class="links" aria-label="{{ __('site.nav_main') }}">` with links: Home →
|
||||||
|
`route('home')`, About → `route('about')`, Services → `route('home').'#services'`,
|
||||||
|
Articles → `route('posts.index')`, Booking → `route('booking')`. Labels from `site.nav_*`.
|
||||||
|
The current route's link gets `aria-current="page"` (home for `home`, about for `about`,
|
||||||
|
articles for `posts.*`, booking for `booking*`). Link style: padding 10px 14px, weight 500,
|
||||||
|
.95rem, radius 8px; hover colour `--olive`; `::after` underline — `inset-inline:14px;
|
||||||
|
bottom:4px; height:2px; background:repeating-linear-gradient(90deg,var(--gold) 0 4px,
|
||||||
|
transparent 4px 7px); transform:scaleX(0)`, origin right (left in LTR), transition
|
||||||
|
`.35s var(--ease)`; `scaleX(1)` on hover and on `[aria-current]`.
|
||||||
|
3. `.nav-tools` (flex, gap 10px): replace both `<x-language-toggle />` usages in this file
|
||||||
|
(desktop and mobile) with a plain link
|
||||||
|
`<a class="lang" href="{{ url('/language/'.(app()->getLocale() === 'ar' ? 'en' : 'ar')) }}" aria-label="{{ __('site.lang_switch') }}">`
|
||||||
|
— a pill (min 48×44px, inline-grid centred, radius 999px, `1.5px solid var(--line)`, white,
|
||||||
|
weight 600 .85rem, no underline; hover gold border and lift 1px) whose text is `EN` when the
|
||||||
|
locale is `ar` and `ع` when it is `en`. Do not edit
|
||||||
|
`resources/views/components/language-toggle.blade.php` (the auth layouts and the dashboard
|
||||||
|
sidebar use it); for guests a `.login` link (`__('site.nav_login')` →
|
||||||
|
`route('login')`, .92rem, min-height 44px, hover olive); for authenticated users the
|
||||||
|
existing Dashboard link and logout button styled like `.login`; then
|
||||||
|
`<a class="btn btn-gold" href="{{ route('home') }}#book">{{ __('site.nav_book') }}</a>`
|
||||||
|
(in the nav: min-height 44px, padding 10px 20px, .92rem); then the mobile menu button
|
||||||
|
`.menu-btn` (44×44px, radius 10px, `1.5px solid var(--line)`, white, hidden above 1100px)
|
||||||
|
carrying the existing Alpine toggle, `:aria-expanded`, and
|
||||||
|
`aria-label="{{ __('site.nav_menu') }}"`.
|
||||||
|
- Mobile menu sheet (the existing `x-show="mobileMenuOpen"` panel): cream background
|
||||||
|
(`var(--cream)`), directly under the nav, full width, each link a 48px-high row with a
|
||||||
|
`1px solid var(--line)` bottom border; contains the five links, the login (or Dashboard and
|
||||||
|
logout) and the gold Book button full width.
|
||||||
|
|
||||||
|
### Footer — `resources/views/components/footer.blade.php`
|
||||||
|
|
||||||
|
- `<footer class="site-footer">`: background `var(--forest-2)`, colour `var(--gold-lt)`,
|
||||||
|
margin-top 120px, padding 72px 0 28px, .92rem, position relative. First child
|
||||||
|
`<span class="foot-band" aria-hidden="true">` — absolute, top 0, full width, height 1px,
|
||||||
|
`linear-gradient(90deg,transparent,rgba(196,168,130,.7),transparent)`.
|
||||||
|
- `<div class="wrap"><div class="foot">` grid `1.4fr 1fr 1fr 1fr`, gap 40px:
|
||||||
|
1. The brand mark (same seal + wordmark as the nav; wordmark `<b>` cream, `<small>` gold-lt)
|
||||||
|
and `<p>` = `__('home.tagline')` (margin-top 16px, max-width 26em).
|
||||||
|
2. `<h4>{{ __('site.footer_explore') }}</h4>` then links About → `route('about')`, Services →
|
||||||
|
`route('home').'#services'`, Articles → `route('posts.index')`, Booking → `route('booking')`.
|
||||||
|
3. `<h4>{{ __('site.footer_clients') }}</h4>` then Client login → `route('login')`, Terms →
|
||||||
|
`route('terms')` with `__('site.footer_terms')`, Privacy → `route('privacy')` with
|
||||||
|
`__('site.footer_privacy')`.
|
||||||
|
4. `<h4>{{ __('site.footer_contact') }}</h4>` then `<a href="tel:+970599353502" dir="ltr">`
|
||||||
|
`{{ __('footer.phone') }}`, `<a href="mailto:{{ __('footer.email') }}">`, and the address
|
||||||
|
`{{ __('footer.address') }}` as plain text.
|
||||||
|
- `h4`: display font, 1.15rem, cream, margin 0 0 14px. Links: block, padding 10px 0, hover
|
||||||
|
colour cream and `translateX(-3px)` (RTL) / `translateX(3px)` (LTR).
|
||||||
|
- `.legal` row: flex, space-between, wrap, `border-top:1px solid rgba(244,241,234,.1)`,
|
||||||
|
margin-top 48px, padding-top 22px, .8rem, colour `rgba(196,168,130,.75)`; content
|
||||||
|
`© {{ date('Y') }} {{ __('footer.copyright') }}`. No photography credit.
|
||||||
|
|
||||||
|
### Language keys — add to `lang/ar/site.php` and `lang/en/site.php`
|
||||||
|
|
||||||
|
| Key | ar | en |
|
||||||
|
|---|---|---|
|
||||||
|
| `brand_name` | ليبرا | Libra |
|
||||||
|
| `brand_sub` | للحقوق | FOR RIGHTS |
|
||||||
|
| `brand_label` | ليبرا للحقوق | Libra for Rights |
|
||||||
|
| `nav_main` | القائمة الرئيسية | Main |
|
||||||
|
| `nav_home` | الرئيسية | Home |
|
||||||
|
| `nav_about` | من نحن | About |
|
||||||
|
| `nav_services` | الخدمات | Services |
|
||||||
|
| `nav_articles` | المقالات | Articles |
|
||||||
|
| `nav_booking` | الحجز | Booking |
|
||||||
|
| `nav_login` | دخول العملاء | Client login |
|
||||||
|
| `nav_book` | احجز استشارة | Book a consultation |
|
||||||
|
| `nav_menu` | القائمة | Menu |
|
||||||
|
| `lang_switch` | English | العربية |
|
||||||
|
| `footer_explore` | تصفّح | Explore |
|
||||||
|
| `footer_clients` | العملاء | Clients |
|
||||||
|
| `footer_contact` | تواصل | Contact |
|
||||||
|
| `footer_terms` | شروط الاستخدام | Terms of use |
|
||||||
|
| `footer_privacy` | سياسة الخصوصية | Privacy policy |
|
||||||
|
|
||||||
|
Do not edit `lang/*/navigation.php` or `lang/*/footer.php` (the dashboards use `navigation.php`).
|
||||||
|
|
||||||
|
### Files
|
||||||
|
|
||||||
|
- Modify: `resources/views/components/navigation.blade.php`,
|
||||||
|
`resources/views/components/footer.blade.php`, `resources/css/public.css`,
|
||||||
|
`lang/ar/site.php`, `lang/en/site.php`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] On `/` at 1440px the navigation is 72px high, stays at the top of the viewport while
|
||||||
|
scrolling, shows brand, five links, language pill, Client login and the gold Book button,
|
||||||
|
and gains a bottom border once the page is scrolled past 8px.
|
||||||
|
- [ ] On `/about` the About link has `aria-current="page"`; on `/posts` the Articles link does.
|
||||||
|
- [ ] At ≤1100px the five links are hidden and the menu button opens a sheet containing all five
|
||||||
|
links, login and Book; Escape closes it; at 390px there is no horizontal scroll.
|
||||||
|
- [ ] Logged in as a client, the navigation shows Dashboard (to `/client/dashboard`) and a working
|
||||||
|
Log out instead of Client login; logged in as admin, Dashboard goes to `/admin/dashboard`.
|
||||||
|
- [ ] The language pill reads `EN` on Arabic pages and `ع` on English pages and switches locale;
|
||||||
|
`resources/views/components/language-toggle.blade.php` is unchanged against BASE.
|
||||||
|
- [ ] The footer shows four columns at 1440px with the phone, email and address from
|
||||||
|
`lang/*/footer.php`, links to `/terms` and `/privacy`, and `© <current year>`; it contains no
|
||||||
|
"Unsplash" text.
|
||||||
|
- [ ] Every key in the table exists in both `lang/ar/site.php` and `lang/en/site.php`, and
|
||||||
|
`lang/*/navigation.php` and `lang/*/footer.php` are unchanged against BASE.
|
||||||
|
- [ ] The admin and client dashboards' navigation is unchanged (they do not render
|
||||||
|
`components/navigation.blade.php`): verify `grep -rn "x-navigation" resources/views` lists
|
||||||
|
only `components/layouts/public.blade.php`.
|
||||||
|
|
||||||
|
## Sub-tasks
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
id: 24-3
|
||||||
|
title: 24-3 Signature Balance Component
|
||||||
|
status: Ready for Dev
|
||||||
|
date: 2026-10-31
|
||||||
|
parent: 24
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Build the redesign's signature: an engraved-gold balance (scales of justice) rendered as an
|
||||||
|
inline SVG, with three behaviours — `hero` (settles on load, weighed by the pointer), `finale`
|
||||||
|
(comes level as the reader scrolls to the booking card) and `static` (always level). Pages place
|
||||||
|
it in later subtasks (24-4 hero and vision card, 24-5 finale, 24-6 success page).
|
||||||
|
|
||||||
|
### Files to create
|
||||||
|
|
||||||
|
1. **`resources/views/components/site/balance.blade.php`** — props: `mode` (`hero` | `finale` |
|
||||||
|
`static`, default `static`), `label` (default null), `size` (integer px, default null; when set,
|
||||||
|
the SVG gets `width` and `height` of that many px). Renders
|
||||||
|
`<svg class="site-balance" data-balance="{{ $mode }}" viewBox="0 0 400 400" …>` with
|
||||||
|
`overflow:visible`; when `label` is set: `role="img" aria-label="{{ $label }}"`, otherwise
|
||||||
|
`aria-hidden="true"`. Pass through `$attributes` (class, id, style).
|
||||||
|
2. **`resources/js/site/balance.js`** — exports `init()` and `destroy()`; imported and wired in
|
||||||
|
`resources/js/public.js` exactly like `reveal.js` (init on `livewire:navigated`, destroy on
|
||||||
|
`livewire:navigating`). It contains:
|
||||||
|
- `build(svg)` — fills the SVG with the markup from `docs/ux-mockup.html` lines 566–595
|
||||||
|
(the `scales()` function's template: four gradients `gd`, `gc`, `gb`, `gh`, two filters
|
||||||
|
`sh` (drop shadow dy 8, stdDeviation 7, flood `#0d1009` opacity .45) and `bl` (blur 6), the
|
||||||
|
ground shadow ellipse, base, pillar, olive-leaf finial, the `.beam` group, the pivot, and two
|
||||||
|
`.pan` groups). Gradient and filter ids must be unique per instance (suffix a running
|
||||||
|
counter) so several balances on one page do not share ids. Returns `tilt(deg)` (mockup lines
|
||||||
|
597–602): `beam.setAttribute('transform', rotate(deg 200 110))`; with
|
||||||
|
`r = deg·π/180, c = cos(r)·142, s = sin(r)·142`, pan 0 at `translate(200−c, 110−s)` and pan 1
|
||||||
|
at `translate(200+c, 110+s)`.
|
||||||
|
- `spring(tilt, start)` (mockup lines 605–610): state `a = start, v = 0, target = 0`; each
|
||||||
|
animation frame `v += (target − a)·0.028; v ·= 0.935; a += v; tilt(a)`; keep requesting
|
||||||
|
frames while `|v| > 0.002` or `|target − a| > 0.01`. Exposes `set(target)`.
|
||||||
|
- Behaviour per `data-balance`:
|
||||||
|
- `static`: `tilt(0)`.
|
||||||
|
- `hero`: `tilt(14)`, create a spring from 14, after 450ms `set(0)`. If
|
||||||
|
`matchMedia('(pointer:fine)').matches`, find the closest ancestor `.frame` (the hero frame,
|
||||||
|
built in 24-4); on `pointermove` over it: `x = (clientX − rect.left) / rect.width`,
|
||||||
|
`set(clamp((x − 0.5)·2.4, −1, 1)·9)`; move the frame's `.glow` element's `left`/`top` to
|
||||||
|
the pointer position within the frame; set the frame's `img` transform to
|
||||||
|
`scale(1.08) translate(${(clientX − rect.left − rect.width/2)/−40}px, ${(clientY − rect.top − rect.height/2)/−40}px)`.
|
||||||
|
On `pointerleave`: `set(0)` and clear the image transform.
|
||||||
|
- `finale`: `tilt(−12)`, spring from −12; on a passive window `scroll` listener (and once on
|
||||||
|
init): `p = scrollY / (scrollHeight − innerHeight)` (1 if the denominator is ≤ 0),
|
||||||
|
`q = clamp((p − 0.7) / 0.25, 0, 1)`, `set(−12·(1 − q))`.
|
||||||
|
- If `matchMedia('(prefers-reduced-motion: reduce)').matches`: every balance is built and
|
||||||
|
`tilt(0)`; no spring, no listeners.
|
||||||
|
- `destroy()` removes every listener added and cancels pending animation frames.
|
||||||
|
3. CSS in **`resources/css/public.css`**: `body.site .site-balance{display:block;overflow:visible}`.
|
||||||
|
|
||||||
|
### Files to modify
|
||||||
|
|
||||||
|
- `resources/js/public.js` (import and wire `./site/balance.js`), `resources/css/public.css`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] `<x-site.balance mode="hero" label="ميزان العدالة" />` renders an `<svg>` with
|
||||||
|
`data-balance="hero"`, `viewBox="0 0 400 400"`, `role="img"` and the given `aria-label`;
|
||||||
|
without `label` it renders `aria-hidden="true"`.
|
||||||
|
- [ ] With two balances on one page, every `id` inside the SVGs is unique
|
||||||
|
(`document.querySelectorAll('[id]')` has no duplicate values).
|
||||||
|
- [ ] A `hero` balance is drawn tilted on load and its beam's `transform` reaches `rotate(0…)`
|
||||||
|
(|angle| < 0.05) within 4 seconds without pointer input.
|
||||||
|
- [ ] Moving a fine pointer to the right edge of the hero frame tips the beam toward +9° (in the
|
||||||
|
browser, read the beam's transform); leaving the frame returns it toward 0°.
|
||||||
|
- [ ] A `finale` balance's beam is at −12° at the top of the page and within 0.5° of 0° when the
|
||||||
|
page is scrolled to the bottom.
|
||||||
|
- [ ] With `prefers-reduced-motion: reduce` emulated, every balance's beam transform is
|
||||||
|
`rotate(0 200 110)` and no animation frame is requested after load.
|
||||||
|
- [ ] Navigating between public pages with `wire:navigate` and back to `/` re-initialises the
|
||||||
|
hero balance (it renders and settles) and leaves no duplicate scroll listeners (the finale
|
||||||
|
still levels correctly after three navigations).
|
||||||
|
|
||||||
|
## Sub-tasks
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
---
|
||||||
|
id: 24-4
|
||||||
|
title: 24-4 Home Page Sections (Hero to Latest Articles)
|
||||||
|
status: Ready for Dev
|
||||||
|
date: 2026-10-31
|
||||||
|
parent: 24
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Rebuild `resources/views/livewire/pages/home.blade.php` as the approved redesign, sections 1–7
|
||||||
|
(hero, pillars, founder, services, vision, values, latest articles). Section 8, the booking card,
|
||||||
|
is task 24-5; leave a placeholder comment `{{-- booking card: task 24-5 --}}` where it goes. Keep
|
||||||
|
the component's PHP class as it is (`#[Layout('components.layouts.public')]`, `with()` returning
|
||||||
|
`latestPosts` = `Post::published()->latest('published_at')->take(3)->get()`). Remove the
|
||||||
|
temporary `<div class="wrap py-8">` wrapper added in 24-1: each section is full-bleed and holds a
|
||||||
|
`<div class="wrap">`.
|
||||||
|
|
||||||
|
The visual reference is `docs/ux-mockup.html` (commit `caec396`). Port the CSS for each section
|
||||||
|
from the line ranges below into `resources/css/public.css`, every selector prefixed with
|
||||||
|
`body.site `, and port the matching responsive rules from lines 293–323. Put `data-reveal` and
|
||||||
|
`data-d` attributes on the same elements as the mockup (lines 358–506).
|
||||||
|
|
||||||
|
### Photography — create `public/images/site/`
|
||||||
|
|
||||||
|
Extract these JPEG data URIs from `docs/ux-mockup.html` and save them, decoded, as files (a short
|
||||||
|
script reading the file and base64-decoding the `src="data:image/jpeg;base64,…"` value on each
|
||||||
|
line is enough; do not commit the script):
|
||||||
|
|
||||||
|
| Mockup line | `<img>` | Save as |
|
||||||
|
|---|---|---|
|
||||||
|
| 377 | hero olive tree | `public/images/site/olive-tree.jpg` |
|
||||||
|
| 447 | embroidery hoop | `public/images/site/embroidery-hoop.jpg` |
|
||||||
|
| 455 | olives on a branch | `public/images/site/olives.jpg` |
|
||||||
|
| 459 | hills and road | `public/images/site/hills.jpg` |
|
||||||
|
|
||||||
|
The founder portrait uses the existing `public/images/huda-armouche.jpg`. The images are from
|
||||||
|
Unsplash (licence permits commercial use without attribution); show no credit line.
|
||||||
|
|
||||||
|
### Sections (in order, inside the root `<div>`)
|
||||||
|
|
||||||
|
1. **Hero** — `<section class="hero" id="top">`; CSS lines 100–125. Two-column grid
|
||||||
|
(`1fr 1.08fr`, gap 56px; one column ≤1100px). Start column: `.eyebrow` = `site.hero_eyebrow`;
|
||||||
|
`<h1>` = `{{ __('site.hero_title_a') }}<em>{{ __('site.hero_title_em') }}</em>{{ __('site.hero_title_b') }}`
|
||||||
|
(the `em` is upright, `--olive`, with `text-decoration: underline dashed var(--gold) .07em;
|
||||||
|
text-underline-offset:.28em`); `<p class="lede">` = `home.intro`; CTA row: gold button
|
||||||
|
`home.cta_book` → `#book` with the arrow SVG (mockup line 366), ghost button
|
||||||
|
`home.cta_services` → `#services`; `.trust` strip with three items, each
|
||||||
|
`<strong>` + `<span>`: (`site.trust_1_title`, `site.trust_1_text`), (`site.trust_2_title`,
|
||||||
|
`site.trust_2_text`), (`site.trust_3_title`, `site.trust_3_text`). End column `.hero-art`:
|
||||||
|
`<figure class="frame">` (aspect 4/4.6, radius `999px 999px 18px 18px`, forest ground,
|
||||||
|
gradient veil `::before` from `rgba(35,42,28,.35)` to `.9`), containing
|
||||||
|
`<img src="/images/site/olive-tree.jpg" alt="{{ __('site.alt_olive_tree') }}">` (filter
|
||||||
|
`saturate(.75) contrast(1.05)`, `scale(1.08)`), `<span class="glow">` (420px radial gold
|
||||||
|
glow), `<x-site.balance mode="hero" :label="__('site.balance_label')" id="scales" />`
|
||||||
|
positioned `inset:10% 8% 12%`, and `<figcaption>` = `site.frame_caption` (display italic,
|
||||||
|
cream .78). Then `<div class="tag">` with `<span class="dot">` (olive, pulsing) and
|
||||||
|
`site.frame_tag`.
|
||||||
|
2. **Pillars band** — `<section class="band on-dark" aria-label="{{ __('site.pillars_label') }}">`;
|
||||||
|
CSS lines 127–137. Three `.pillar` columns: (`site.pillar_justice`, `site.pillar_justice_text`),
|
||||||
|
(`site.pillar_dignity`, `site.pillar_dignity_text`), (`site.pillar_advocacy`,
|
||||||
|
`site.pillar_advocacy_text`), each title preceded by `<span class="gem">` (CSS line 210).
|
||||||
|
3. **Founder** — `<section class="sec" id="founder">`; CSS lines 146–157. Portrait
|
||||||
|
`/images/huda-armouche.jpg` (alt `home.lawyer_name`) with `.namecard`
|
||||||
|
(`<b>` `home.lawyer_name`, `<span>` `site.founder_role`). Text column: `.art-no`
|
||||||
|
`site.art_founder`; `<h2>` `home.about_title`; `<blockquote>` `site.founder_quote`;
|
||||||
|
paragraphs `home.lawyer_bio` and `home.lawyer_bio_2`; ghost button `site.founder_cta` →
|
||||||
|
`route('about')`.
|
||||||
|
4. **Services docket** — `<section class="sec" id="services" style="padding-top:40px">`;
|
||||||
|
CSS lines 159–174. `x-site.section-head` (see below) with label `site.art_services`, title
|
||||||
|
`home.services_subtitle`, lede `site.services_lede`. Four `<a class="row" href="#book">` rows:
|
||||||
|
number `01`–`04` (`.n`), `<h3>` title, `.chips` of the items, `.go` arrow circle. Rows in order:
|
||||||
|
(`home.service_consultation_title`, `home.service_consultation_items`),
|
||||||
|
(`home.service_family_title`, `home.service_family_items`),
|
||||||
|
(`home.service_civil_title`, `home.service_civil_items`),
|
||||||
|
(`home.service_business_title`, `home.service_business_items`). Items are arrays — render each
|
||||||
|
as `<span class="chip">`.
|
||||||
|
5. **Vision** — `<section class="sec fabric" id="mission">`; CSS lines 176–197. Text column:
|
||||||
|
`.art-no` `site.art_vision`, `<h2>` `site.vision_title`, `<p class="big">` `site.vision_text`;
|
||||||
|
beside it `.hoop` with `/images/site/embroidery-hoop.jpg` (alt `site.alt_hoop`; filter
|
||||||
|
`hue-rotate(-140deg) saturate(.55) sepia(.25)`). Below, `.three` grid of `<article class="card">`:
|
||||||
|
(a) `.ph.motif-ph` containing `<x-site.balance mode="static" />` (150×150px) and label
|
||||||
|
`site.card_institution_label`, title `site.card_institution_title`, text
|
||||||
|
`site.card_institution_text`; (b) `.ph` with `/images/site/olives.jpg` (alt `site.alt_olives`),
|
||||||
|
label `site.card_society_label`, title `site.card_society_title`, text `site.card_society_text`;
|
||||||
|
(c) `.ph` with `/images/site/hills.jpg` (alt `site.alt_hills`), label
|
||||||
|
`site.card_diaspora_label`, title `site.card_diaspora_title`, text `site.card_diaspora_text`.
|
||||||
|
6. **Values** — `<section class="sec" id="values">`; CSS lines 199–212. Section head: label
|
||||||
|
`site.art_values`, title `home.values_title`, lede `home.values_subtitle`. `.sampler` grid of six
|
||||||
|
`.val` cards, each `<span class="medal">` with the line icon and `<b>` =
|
||||||
|
`home.values.<key>.title`, keys in order `integrity`, `justice`, `knowledge`, `empowerment`,
|
||||||
|
`professionalism`, `innovation`. Icons: the inline SVGs on mockup lines 474–479 in the same
|
||||||
|
order (24×24 viewBox, `aria-hidden="true"`, stroke `--gold-ink` 1.5, no fill). Replace the
|
||||||
|
current `flux:icon` usage.
|
||||||
|
7. **Latest articles** — `<section class="sec" id="posts" style="padding-top:20px">`; CSS lines
|
||||||
|
214–234. Head: label `site.art_articles`, title `home.posts_title`, and in place of the lede a
|
||||||
|
ghost button `home.view_all_posts` → `route('posts.index')`. If `$latestPosts` is not empty:
|
||||||
|
`.posts` grid of `<x-site.post-card :post="$post" />`; otherwise a `<p>` with
|
||||||
|
`home.posts_empty` (muted, centred).
|
||||||
|
|
||||||
|
### Components to create
|
||||||
|
|
||||||
|
- **`resources/views/components/site/section-head.blade.php`** — props `label`, `title`,
|
||||||
|
`lede` (nullable); renders
|
||||||
|
`<div class="sec-head"><div><span class="art-no" data-reveal>{{ $label }}</span><h2 data-reveal data-d="1">{{ $title }}</h2></div>`
|
||||||
|
then either `<p data-reveal data-d="2">{{ $lede }}</p>` or the `$slot` (for the button), then
|
||||||
|
`</div>`.
|
||||||
|
- **`resources/views/components/site/post-card.blade.php`** — prop `post` (App\Models\Post);
|
||||||
|
`<a class="post" href="{{ route('posts.show', $post) }}" wire:navigate data-reveal>` →
|
||||||
|
`<div class="bd">` with `<span class="meta">` = the published date
|
||||||
|
(`$post->published_at?->translatedFormat('j F Y')`), `<h3>` = `$post->getTitle()`, `<p>` = an
|
||||||
|
excerpt of `strip_tags($post->getBody())` limited to 140 characters (`Str::limit`), and
|
||||||
|
`<span class="more">` = `home.read_more`. Text-only: no image element.
|
||||||
|
|
||||||
|
### Language keys — add to `lang/ar/site.php` and `lang/en/site.php`
|
||||||
|
|
||||||
|
| Key | ar | en |
|
||||||
|
|---|---|---|
|
||||||
|
| `hero_eyebrow` | ليبرا للحقوق · فلسطين | Libra for Rights · Palestine |
|
||||||
|
| `hero_title_a` | العدالة␠ | Justice is a␠ |
|
||||||
|
| `hero_title_em` | ممارسةٌ يومية | daily practice |
|
||||||
|
| `hero_title_b` | ، لا مبدأٌ نظري. | , not a theory. |
|
||||||
|
| `trust_1_title` | أفراد وشركات | Individuals & companies |
|
||||||
|
| `trust_1_text` | استشارات وتمثيل | Consultations and representation |
|
||||||
|
| `trust_2_title` | المرأة والأسرة | Women & family |
|
||||||
|
| `trust_2_text` | دعم قانوني متخصص | Specialised legal support |
|
||||||
|
| `trust_3_title` | عربي · English | Arabic · English |
|
||||||
|
| `trust_3_text` | خدمة بلغتين | Bilingual service |
|
||||||
|
| `alt_olive_tree` | شجرة زيتون معمّرة بين صخور كلسية | An ancient olive tree among limestone rocks |
|
||||||
|
| `balance_label` | ميزان العدالة | The scales of justice |
|
||||||
|
| `frame_caption` | لكلّ حقٍّ وزنُه | Every right carries its weight |
|
||||||
|
| `frame_tag` | استشارات حضورية أو عن بُعد | Consultations in person or remotely |
|
||||||
|
| `pillars_label` | ركائزنا | Our pillars |
|
||||||
|
| `pillar_justice` | العدالة | Justice |
|
||||||
|
| `pillar_justice_text` | الإنصاف، والمساءلة، وسيادة القانون. | Fairness, accountability and the rule of law. |
|
||||||
|
| `pillar_dignity` | الكرامة | Dignity |
|
||||||
|
| `pillar_dignity_text` | القيمة الأصيلة لكل إنسان. | The inherent worth of every person. |
|
||||||
|
| `pillar_advocacy` | الدفاع | Advocacy |
|
||||||
|
| `pillar_advocacy_text` | انخراطٌ فاعل، وقولُ الحق في وجه السلطة. | Active engagement, and speaking truth to power. |
|
||||||
|
| `art_founder` | المادة ٠١ — المؤسِّسة | Article 01 — The founder |
|
||||||
|
| `founder_role` | المؤسِّسة · محامية | Founder · Attorney at Law |
|
||||||
|
| `founder_quote` | «العدالة ممارسة يومية، وليست مجرد مبدأ نظري.» | “Justice is a daily practice, not merely a theoretical principle.” |
|
||||||
|
| `founder_cta` | اقرأ قصتنا | Read our story |
|
||||||
|
| `art_services` | المادة ٠٢ — الخدمات | Article 02 — Services |
|
||||||
|
| `services_lede` | ممارسة قانونية أصيلة، تقوم على البحث والمعرفة الميدانية. | Traditional legal practice, combined with research-driven insight. |
|
||||||
|
| `art_vision` | المادة ٠٣ — الرؤية | Article 03 — Vision |
|
||||||
|
| `vision_title` | منسوجةٌ من نسيج المجتمع | Woven from the fabric of society |
|
||||||
|
| `vision_text` | ليبرا نسيجٌ أصيل من المجتمع، تتقصّى مشكلاته وتوثّقها وتكشف جذورها، وتقدّم حلولاً عملية لرأب أوجاعه مهما صغرت، عبر العمل التشاركي والبحث الميداني الحقيقي. | Libra investigates society's problems, documents them, uncovers their roots, and offers practical solutions to mend social pains — however small — through collaborative work and genuine field research. |
|
||||||
|
| `alt_hoop` | تطريز بغرزة الصليب مشدود على طارة خشبية | Cross-stitch embroidery stretched on a wooden hoop |
|
||||||
|
| `card_institution_label` | لمؤسستنا | For our institution |
|
||||||
|
| `card_institution_title` | القانون والبحث والميدان | Law, research and fieldwork |
|
||||||
|
| `card_institution_text` | نلتقط نبض المجتمع ونحوّل تحدياته إلى معرفة وحلول قانونية مبتكرة. | Capturing society's pulse and turning its challenges into knowledge and innovative legal solutions. |
|
||||||
|
| `alt_olives` | ثمار زيتون على غصن | Olives ripening on a branch |
|
||||||
|
| `card_society_label` | للمجتمع | For society |
|
||||||
|
| `card_society_title` | شريكٌ موثوق للقضاء | A trusted partner to the judiciary |
|
||||||
|
| `card_society_text` | نعيد وصل المجتمع بتاريخه وإرثه القانوني. | Reconnecting society with its history and legal heritage. |
|
||||||
|
| `alt_hills` | تلال وطريق متعرّج عند الغروب | Hills and a winding road at sunset |
|
||||||
|
| `card_diaspora_label` | لفلسطينيي الشتات | For Palestinians in the diaspora |
|
||||||
|
| `card_diaspora_title` | عودةٌ إلى الأرض والعائلة | Back to the land and the family |
|
||||||
|
| `card_diaspora_text` | نساعد الفلسطينيين في الخارج على البحث في عائلاتهم وتاريخهم وأراضيهم. | Helping Palestinians abroad research their families, their history and their land. |
|
||||||
|
| `art_values` | المادة ٠٤ — القيم | Article 04 — Values |
|
||||||
|
| `art_articles` | المادة ٠٥ — المقالات | Article 05 — Articles |
|
||||||
|
|
||||||
|
`␠` marks a trailing space that must be kept in the value.
|
||||||
|
|
||||||
|
### Files
|
||||||
|
|
||||||
|
- Create: `public/images/site/olive-tree.jpg`, `public/images/site/embroidery-hoop.jpg`,
|
||||||
|
`public/images/site/olives.jpg`, `public/images/site/hills.jpg`,
|
||||||
|
`resources/views/components/site/section-head.blade.php`,
|
||||||
|
`resources/views/components/site/post-card.blade.php`.
|
||||||
|
- Modify: `resources/views/livewire/pages/home.blade.php`, `resources/css/public.css`,
|
||||||
|
`lang/ar/site.php`, `lang/en/site.php`.
|
||||||
|
- Do not edit `lang/*/home.php`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] `/` renders, in order, sections with ids `top`, (the pillars band), `founder`, `services`,
|
||||||
|
`mission`, `values`, `posts`, each with its content from the keys above in both Arabic and
|
||||||
|
English (switch with `/language/en`).
|
||||||
|
- [ ] `/` has exactly one `<h1>`, and every `<img>` has a non-empty `alt` in both languages.
|
||||||
|
- [ ] The four files in `public/images/site/` exist, are valid JPEGs (`file` reports JPEG image
|
||||||
|
data) and load with HTTP 200 on `/`.
|
||||||
|
- [ ] The hero balance renders inside the hero frame and settles level (task 24-3 behaviour).
|
||||||
|
- [ ] Each services row links to `#book`; each row shows the item chips from `home.php`.
|
||||||
|
- [ ] With three or more published posts, three post cards show date, title, excerpt and "Read
|
||||||
|
more", link to `/posts/{post}`, and contain no `<img>`; with no published posts, the
|
||||||
|
`home.posts_empty` text shows instead of the grid.
|
||||||
|
- [ ] At 1440, 768 and 390px wide, `/` has no horizontal scroll (`document.documentElement.scrollWidth <= innerWidth`).
|
||||||
|
- [ ] Hovering a service row fills it forest with cream text; hovering a value card lifts it and
|
||||||
|
rotates its medallion; hovering a post card lifts it.
|
||||||
|
- [ ] The page contains no "Unsplash" text and `lang/*/home.php` is unchanged against BASE.
|
||||||
|
|
||||||
|
## Sub-tasks
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
---
|
||||||
|
id: 24-5
|
||||||
|
title: 24-5 Home Booking Card (Compact Booking and Day Strip)
|
||||||
|
status: Ready for Dev
|
||||||
|
date: 2026-10-31
|
||||||
|
parent: 24
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Add section 8 of the home page: a forest booking card that embeds the existing guest-booking
|
||||||
|
component in a one-screen "compact" mode, with a 14-day strip of bookable days. The booking rules
|
||||||
|
must not change: name, email and phone required; case summary required, minimum 20 characters;
|
||||||
|
the math captcha (`App\Services\CaptchaService`); one booking per email per day; slots from
|
||||||
|
`App\Services\AvailabilityService`; the existing confirm step (`showConfirmation`); and the
|
||||||
|
existing `submit()` that redirects to `route('booking.success')`. Logged-in visitors see the
|
||||||
|
existing logged-in message instead of the form.
|
||||||
|
|
||||||
|
### 1. Availability calendar — `resources/views/livewire/availability-calendar.blade.php`
|
||||||
|
|
||||||
|
- Add `public string $variant = 'month';` (settable from the tag: `variant="strip"`). The default
|
||||||
|
`month` must render exactly as today, because
|
||||||
|
`resources/views/livewire/client/consultations/book.blade.php` also uses this component.
|
||||||
|
- When `$variant === 'strip'`:
|
||||||
|
- Compute `$stripDays`: starting today, step one day at a time up to 60 days ahead and collect
|
||||||
|
the first 14 dates for which `AvailabilityService::getDateStatus($date)` returns
|
||||||
|
`'available'` or `'partial'` (the same two values `selectDate()` accepts today), each with
|
||||||
|
its `getAvailableSlots($date)`.
|
||||||
|
- On mount, select the first strip day (`$selectedDate`) and load its slots with the existing
|
||||||
|
`loadAvailableSlots()`.
|
||||||
|
- Render a day strip: `<div class="days" role="group" aria-label="{{ __('site.booking_day') }}">`
|
||||||
|
of `<button type="button" class="day" aria-pressed="true|false" wire:click="selectDate('Y-m-d')">`
|
||||||
|
with the weekday name (`translatedFormat('l')`) and `<b>` the day number. Then a slot row:
|
||||||
|
`<div class="slots" role="group" aria-label="{{ __('site.booking_time') }}">` of
|
||||||
|
`<button type="button" class="slot" dir="ltr" aria-pressed=…>` per slot, clicking calls the
|
||||||
|
existing `$parent.selectSlot('<date>', '<slot>')`, and the button for the parent's current
|
||||||
|
`selectedTime` on the selected date is pressed. Slot labels use the same time format as the
|
||||||
|
month view.
|
||||||
|
- No strip day available within 60 days → show `site.booking_none` (ar «لا توجد مواعيد
|
||||||
|
متاحة حالياً»; en "No times are available at the moment") in place of the strip and slots.
|
||||||
|
- CSS (in `resources/css/public.css`, scoped `body.site`; mockup `docs/ux-mockup.html` lines
|
||||||
|
256–265): `.days` flex, gap 8px, `overflow-x:auto`, `scrollbar-width:none`; `.day` 66×72px min,
|
||||||
|
radius 14px, `1.5px solid rgba(45,54,36,.15)`, white, .78rem muted, `<b>` display font 1.35rem
|
||||||
|
ink; hover gold border and lift 2px; `[aria-pressed=true]` forest background, `--gold-lt` text,
|
||||||
|
cream number, shadow `0 10px 22px -10px rgba(45,54,36,.8)`. `.slots` flex-wrap gap 8px;
|
||||||
|
`.slot` min-height 44px, padding 8px 16px, pill, same border, white; hover gold border;
|
||||||
|
`[aria-pressed=true]` gold fill, gold border, ink text.
|
||||||
|
|
||||||
|
### 2. Booking component — `resources/views/livewire/pages/booking.blade.php`
|
||||||
|
|
||||||
|
- Add `public bool $compact = false;` and accept it in `mount(bool $compact = false)` (keep the
|
||||||
|
existing mount logic). The `#[Layout]` attribute stays; it is ignored when the component is
|
||||||
|
embedded.
|
||||||
|
- When `$compact` is true, render (instead of the page markup) a
|
||||||
|
`<form class="book-r" wire:submit="showConfirm">` (mockup CSS lines 247–267: cream, padding
|
||||||
|
44px 44px 40px, margin 12px, radius 20px) containing, in order:
|
||||||
|
1. `.f2` two-column row: full name (`guestName`, `autocomplete="name"`) and phone
|
||||||
|
(`guestPhone`, `type="tel" dir="ltr" autocomplete="tel"`), labels from the existing
|
||||||
|
`booking.*` keys with ` *`;
|
||||||
|
2. email (`guestEmail`, `type="email" dir="ltr" autocomplete="email"`);
|
||||||
|
3. `<livewire:availability-calendar variant="strip" />` — the strip variant itself renders a
|
||||||
|
visible label `site.booking_day` (.85rem, weight 600, margin-bottom 6px) above `.days` and
|
||||||
|
`site.booking_time` above `.slots`;
|
||||||
|
4. case summary textarea (`problemSummary`, rows 3, label `booking.problem_summary` + ` *`,
|
||||||
|
help text `booking.problem_summary_help`);
|
||||||
|
5. the captcha question and input (`captchaAnswer`, `inputmode="numeric" dir="ltr"`,
|
||||||
|
max-width 140px) exactly as the page version renders it;
|
||||||
|
6. `<button type="submit" class="btn btn-gold">` `site.booking_submit`, full width;
|
||||||
|
7. `<p class="note">` `booking.limit_message`.
|
||||||
|
Validation errors render under each field as in the page version. Submitting without a
|
||||||
|
selected slot shows the existing `selectedTime` error.
|
||||||
|
- When `$compact` and `$showConfirmation` are true, the same `.book-r` panel shows the existing
|
||||||
|
confirmation summary (date, time, name, email, phone, summary) with the existing Confirm
|
||||||
|
(`submit`) and Back (`$set('showConfirmation', false)`) actions, styled `btn-gold` and
|
||||||
|
`btn-ghost`.
|
||||||
|
- When `$compact` and the visitor is logged in, the panel shows the existing
|
||||||
|
`booking.logged_in_title`, `booking.logged_in_message` and the `booking.go_to_dashboard` link
|
||||||
|
(`route('client.consultations.book')`) styled as `btn-gold`.
|
||||||
|
- Inputs and textarea (scoped `body.site .book-r`): min-height 48px, padding 12px 16px, radius
|
||||||
|
12px, `1.5px solid rgba(45,54,36,.18)`, white, `font:400 1rem var(--body)`; focus border
|
||||||
|
`--gold` and `box-shadow:0 0 0 4px rgba(166,137,102,.2)`; labels .85rem weight 600. Flux inputs
|
||||||
|
may be replaced by plain inputs in compact mode, keeping the same `wire:model` names.
|
||||||
|
- The non-compact (page) rendering is unchanged by this task.
|
||||||
|
|
||||||
|
### 3. Home page — `resources/views/livewire/pages/home.blade.php`
|
||||||
|
|
||||||
|
Replace `{{-- booking card: task 24-5 --}}` with
|
||||||
|
`<section id="book"><div class="wrap"><div class="book" data-reveal>` (mockup CSS lines 236–246:
|
||||||
|
forest, radius 28px, two columns `1fr 1.1fr`, one column ≤1100px, shadow
|
||||||
|
`0 50px 90px -40px rgba(30,34,25,.8)`), containing:
|
||||||
|
- `<div class="book-l on-dark">` (padding 56px 52px; 40px 24px ≤760px): `.eyebrow`
|
||||||
|
`site.art_booking` (colour `--gold-lt`), `<h2>` `site.booking_title` (cream), `<p>`
|
||||||
|
`site.booking_lede` (`--gold-lt`), `.contact` list — phone link `tel:+970599353502` with
|
||||||
|
`<span dir="ltr">{{ __('footer.phone') }}</span>`, email link `mailto:` `footer.email`, and the
|
||||||
|
address `footer.address`, each with the 20px gold line icon from mockup lines 517–519 — and
|
||||||
|
`<x-site.balance mode="finale" style="width:190px;height:190px;margin-top:22px" />`.
|
||||||
|
- `<livewire:pages.booking :compact="true" />`.
|
||||||
|
|
||||||
|
### Language keys — add to `lang/ar/site.php` and `lang/en/site.php`
|
||||||
|
|
||||||
|
| Key | ar | en |
|
||||||
|
|---|---|---|
|
||||||
|
| `art_booking` | المادة ٠٦ — الاستشارة | Article 06 — Consultation |
|
||||||
|
| `booking_title` | احجز استشارة | Book a consultation |
|
||||||
|
| `booking_lede` | اختر يوماً ووقتاً متاحَين، وأخبرنا باختصار عن قضيتك. نراجع كل طلب ونؤكّد الموعد بالبريد الإلكتروني — دون الحاجة إلى حساب. | Choose an available day and time and tell us briefly about your case. We review every request and confirm by email — no account needed. |
|
||||||
|
| `booking_day` | اليوم | Day |
|
||||||
|
| `booking_time` | الوقت | Time |
|
||||||
|
| `booking_submit` | اطلب هذا الموعد | Request this appointment |
|
||||||
|
| `booking_none` | لا توجد مواعيد متاحة حالياً | No times are available at the moment |
|
||||||
|
|
||||||
|
### Files
|
||||||
|
|
||||||
|
- Modify: `resources/views/livewire/availability-calendar.blade.php`,
|
||||||
|
`resources/views/livewire/pages/booking.blade.php`,
|
||||||
|
`resources/views/livewire/pages/home.blade.php`, `resources/css/public.css`,
|
||||||
|
`lang/ar/site.php`, `lang/en/site.php`.
|
||||||
|
- Do not modify `app/Services/AvailabilityService.php`, `app/Services/CaptchaService.php`,
|
||||||
|
`resources/views/livewire/client/consultations/book.blade.php`, or any migration.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] `/` shows the booking card with id `book`: the forest panel (title, lede, phone, email,
|
||||||
|
address, finale balance) and the cream form with fields in the order name, phone, email,
|
||||||
|
day strip, slots, summary, captcha, submit, one-per-day note.
|
||||||
|
- [ ] The day strip lists up to 14 dates, all of which have at least one slot in
|
||||||
|
`AvailabilityService::getAvailableSlots()`; the first is pressed on load and its slots show;
|
||||||
|
clicking another day loads that day's slots.
|
||||||
|
- [ ] A guest who fills valid data, picks a slot, answers the captcha and confirms creates exactly
|
||||||
|
one guest consultation (same attributes as a booking made on `/booking`) and lands on
|
||||||
|
`/booking/success`.
|
||||||
|
- [ ] A summary of fewer than 20 characters, a wrong captcha answer, a missing slot, or a second
|
||||||
|
booking with the same email on the same day each produce the same error as on `/booking`,
|
||||||
|
and no consultation is created.
|
||||||
|
- [ ] Logged in as a client, `/` shows the logged-in message with a link to
|
||||||
|
`/client/consultations/book` and no form.
|
||||||
|
- [ ] `/booking` and `/client/consultations/book` render the month calendar as before this task
|
||||||
|
(`variant` defaults to `month`).
|
||||||
|
- [ ] The finale balance is tilted at the top of `/` and level when scrolled to the booking card at
|
||||||
|
the end of the page.
|
||||||
|
- [ ] At 390px the card is one column, the day strip scrolls horizontally inside the card, and the
|
||||||
|
page has no horizontal scroll.
|
||||||
|
|
||||||
|
## Sub-tasks
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
id: 24-6
|
||||||
|
title: 24-6 Booking Page and Booking Success
|
||||||
|
status: Ready for Dev
|
||||||
|
date: 2026-10-31
|
||||||
|
parent: 24
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Restyle `/booking` (the non-compact rendering of `resources/views/livewire/pages/booking.blade.php`)
|
||||||
|
and `/booking/success` (`resources/views/livewire/pages/booking-success.blade.php`) to the
|
||||||
|
redesign. Behaviour does not change: the month calendar, the guest fields, the captcha, the
|
||||||
|
confirm step, the one-per-day rule, the logged-in message, and the redirect to `/booking/success`.
|
||||||
|
Task 24-5 already added `$compact`, the strip variant and the `.book`, `.book-l`, `.book-r`,
|
||||||
|
input and label styles in `resources/css/public.css`; reuse them.
|
||||||
|
|
||||||
|
### `/booking` (non-compact branch of `booking.blade.php`)
|
||||||
|
|
||||||
|
Replace the page markup (keeping every `wire:` binding, error output, and the
|
||||||
|
`@if($isLoggedIn)` / step logic) with:
|
||||||
|
|
||||||
|
`<section class="sec" style="padding-top:56px"><div class="wrap">` →
|
||||||
|
`<x-site.section-head :label="__('site.art_booking')" :title="__('booking.request_consultation')" :lede="__('site.booking_lede')" />` →
|
||||||
|
`<div class="book">`:
|
||||||
|
- `.book-l.on-dark`: `.contact` list (phone `tel:+970599353502` with
|
||||||
|
`<span dir="ltr">{{ __('footer.phone') }}</span>`, `mailto:` `footer.email`, address
|
||||||
|
`footer.address`, same 20px gold icons as the home card) and
|
||||||
|
`<x-site.balance mode="static" style="width:190px;height:190px;margin-top:22px" />`.
|
||||||
|
- `.book-r` (cream inset): for a logged-in visitor, the existing logged-in title, message and
|
||||||
|
`booking.go_to_dashboard` link (`route('client.consultations.book')`) as `btn btn-gold`.
|
||||||
|
For a guest: step 1 — `booking.select_date_time` then `<livewire:availability-calendar />`
|
||||||
|
(month variant); step 2 — the selected date/time summary with the existing "change" action,
|
||||||
|
the fields name, email, phone, case summary, captcha, the gold submit; step 3 — the existing
|
||||||
|
confirmation summary with Confirm (`btn-gold`) and Back (`btn-ghost`). The existing
|
||||||
|
`session('success')` callout stays at the top of `.book-r`.
|
||||||
|
|
||||||
|
Below the card, inside the same `.wrap`, keep `<x-booking-expectations class="mt-10" />`
|
||||||
|
unchanged.
|
||||||
|
|
||||||
|
### Month calendar restyle (CSS only, scoped to the public site)
|
||||||
|
|
||||||
|
In `resources/css/public.css`, under `body.site`, style the month grid rendered by
|
||||||
|
`resources/views/livewire/availability-calendar.blade.php` (month variant) without editing its
|
||||||
|
markup: day cells min 44×44px, radius 10px; available and partial days white with a 6px gold dot
|
||||||
|
below the number (`::after`, `background:var(--gold)`); the selected day `background:var(--forest)`
|
||||||
|
with cream text; unavailable, closed, blocked and past days `--muted` text at 50% opacity, no
|
||||||
|
hover; month navigation buttons as 44px pills with `1.5px solid var(--line)`. Read the template
|
||||||
|
to find the exact classes or `wire:click` attributes the selectors must target; if the template
|
||||||
|
has no class to hook onto, add a class attribute only (e.g. `site-cal-day`, plus
|
||||||
|
`is-available`, `is-selected`) without changing structure or behaviour, so the client dashboard
|
||||||
|
(which has no `body.site`) renders as before.
|
||||||
|
|
||||||
|
### `/booking/success` — `booking-success.blade.php`
|
||||||
|
|
||||||
|
`<section class="sec"><div class="wrap">` → a centred cream card (max-width 560px, margin-inline auto,
|
||||||
|
`background:var(--cream)`, `1px solid var(--line)` border, radius 20px, `--shadow-2`, padding
|
||||||
|
48px 40px, text-align center) containing
|
||||||
|
`<x-site.balance mode="static" style="width:120px;height:120px;margin:0 auto 18px" />`, `<h1>`
|
||||||
|
`booking.success_title` (display font, `clamp(2rem,3.4vw,2.6rem)`), `<p>`
|
||||||
|
`booking.success_message` (muted), and `<a class="btn btn-ghost" href="{{ route('home') }}" wire:navigate>`
|
||||||
|
`site.nav_home`. Remove the `flux:icon` check-circle, `flux:heading` and `flux:button`.
|
||||||
|
|
||||||
|
### Files
|
||||||
|
|
||||||
|
- Modify: `resources/views/livewire/pages/booking.blade.php`,
|
||||||
|
`resources/views/livewire/pages/booking-success.blade.php`, `resources/css/public.css`, and
|
||||||
|
(class attributes only, if needed) `resources/views/livewire/availability-calendar.blade.php`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] `/booking` as a guest shows the section head, the forest card with contact details and a
|
||||||
|
level balance, and the month calendar inside the cream panel; selecting an available day and
|
||||||
|
a slot reveals the fields; completing the form and confirming creates one guest consultation
|
||||||
|
and redirects to `/booking/success`.
|
||||||
|
- [ ] Every validation rule behaves as before this task (required fields, 20-character summary,
|
||||||
|
captcha, one booking per email per day), each error rendered under its field.
|
||||||
|
- [ ] `/booking` logged in as a client shows the logged-in message and the link to
|
||||||
|
`/client/consultations/book`, and no form.
|
||||||
|
- [ ] In the month calendar on `/booking`, available days show a gold dot, the selected day is
|
||||||
|
forest with cream text, and unavailable days are dimmed; day cells are at least 44×44px.
|
||||||
|
- [ ] `/client/consultations/book` (logged in as a client) renders the calendar as before
|
||||||
|
(no `body.site` styles apply there).
|
||||||
|
- [ ] `/booking/success` shows the balance, title, message and a Home button, and contains no
|
||||||
|
`flux:` markup output (`data-flux-` attributes) in its main content.
|
||||||
|
- [ ] `/booking` and `/booking/success` have no horizontal scroll at 1440, 768 and 390px.
|
||||||
|
|
||||||
|
## Sub-tasks
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
---
|
||||||
|
id: 24-7
|
||||||
|
title: 24-7 About Page
|
||||||
|
status: Ready for Dev
|
||||||
|
date: 2026-10-31
|
||||||
|
parent: 24
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Restyle `/about` (`resources/views/livewire/pages/about.blade.php`) with the redesign's components,
|
||||||
|
using only the page's existing content from `lang/*/about.php` (no new copy beyond the article
|
||||||
|
labels below). Keep the Volt class and layout attribute. Remove the temporary
|
||||||
|
`<div class="wrap py-8">` from 24-1; each section is full-bleed with its own `.wrap`. The
|
||||||
|
Scholarship section stays removed (it was taken out on 2026-01-11 at Naser's request).
|
||||||
|
|
||||||
|
Sections, in order (all CSS classes already exist in `resources/css/public.css` from 24-1 to 24-5;
|
||||||
|
add only what is listed as new):
|
||||||
|
|
||||||
|
1. **Hero** — `<section class="sec" style="padding-bottom:64px">`, `.wrap`, max-width 52em:
|
||||||
|
`.eyebrow` = `about.tagline`; `<h1>` = `about.title` (display font,
|
||||||
|
`clamp(2.6rem,4.6vw,4.1rem)`, line-height 1.12); `<p class="lede">` = `about.identity`.
|
||||||
|
2. **Vision and mission** — `<section class="sec fabric">`: `.fabric-grid` with the text column
|
||||||
|
(`.art-no` `site.about_art_vision`, `<h2>` `about.vision_title`, `<p class="big">`
|
||||||
|
`about.vision_text`, then `<h3>` `about.mission_title` and `<p>` `about.mission_intro`) and the
|
||||||
|
`.hoop` photo `/images/site/embroidery-hoop.jpg` (alt `site.alt_hoop`); below, `.three` cards
|
||||||
|
exactly as on the home page but with the about texts: (a) static balance panel, label
|
||||||
|
`site.card_institution_label`, title `about.mission_institution_title`, text
|
||||||
|
`about.mission_institution_text`; (b) `/images/site/olives.jpg`, label
|
||||||
|
`site.card_society_label`, title `about.mission_society_title`, text
|
||||||
|
`about.mission_society_text`; (c) `/images/site/hills.jpg`, label `site.card_diaspora_label`,
|
||||||
|
title `about.mission_diaspora_title`, text `about.mission_diaspora_text`.
|
||||||
|
3. **Values** — `<section class="sec">`: `<x-site.section-head :label="__('site.about_art_values')" :title="__('about.values_title')" :lede="__('about.values_intro')" />`,
|
||||||
|
then the `.sampler` of six `.val` medallion cards with the same six icons and order as the home
|
||||||
|
page (inline SVGs from `docs/ux-mockup.html` lines 474–479), names `about.value_integrity`,
|
||||||
|
`about.value_justice`, `about.value_knowledge`, `about.value_women`,
|
||||||
|
`about.value_professionalism`, `about.value_innovation`.
|
||||||
|
4. **Goals** — `<section class="sec" style="padding-top:20px">`: section head with label
|
||||||
|
`site.about_art_goals`, title `about.goals_title`; then a `.docket` list (`<ol>`, no bullets)
|
||||||
|
with one `.row.row-static` per item of `about.goals` (an array of 7 strings): `.n` = the
|
||||||
|
two-digit number (`01`–`07`) and a `<p>` with the goal text (1.1rem, ink). New CSS:
|
||||||
|
`body.site .row-static{grid-template-columns:90px 1fr}` (52px 1fr at ≤760px) and
|
||||||
|
`body.site .row-static:hover{background:none;color:inherit;transform:none;box-shadow:none}`,
|
||||||
|
`body.site .row-static::before{display:none}`.
|
||||||
|
5. **Legal services** — `<section class="sec" id="services" style="padding-top:20px">`: section
|
||||||
|
head with label `site.about_art_services`, title `about.services_title`, lede
|
||||||
|
`about.services_intro`; four `<a class="row">` rows linking to `route('booking')`, as on the
|
||||||
|
home page, with titles and chips from `about.service_consultation_title/items`,
|
||||||
|
`about.service_family_title/items`, `about.service_civil_title/items`,
|
||||||
|
`about.service_business_title/items`.
|
||||||
|
6. **Closing CTA** — `<section class="band on-dark">` with a `.wrap` centred column, padding
|
||||||
|
72px 0: `<h2>` `site.about_cta_title` (cream, display font) and
|
||||||
|
`<a class="btn btn-gold" href="{{ route('booking') }}" wire:navigate>` `about.services_cta`.
|
||||||
|
|
||||||
|
All elements that reveal on the home page carry `data-reveal` here too.
|
||||||
|
|
||||||
|
### Language keys — add to `lang/ar/site.php` and `lang/en/site.php`
|
||||||
|
|
||||||
|
| Key | ar | en |
|
||||||
|
|---|---|---|
|
||||||
|
| `about_art_vision` | المادة ٠١ — الرؤية والرسالة | Article 01 — Vision and mission |
|
||||||
|
| `about_art_values` | المادة ٠٢ — القيم | Article 02 — Values |
|
||||||
|
| `about_art_goals` | المادة ٠٣ — الأهداف | Article 03 — Goals |
|
||||||
|
| `about_art_services` | المادة ٠٤ — الخدمات | Article 04 — Services |
|
||||||
|
| `about_cta_title` | لكلّ حقٍّ وزنُه | Every right carries its weight |
|
||||||
|
|
||||||
|
### Files
|
||||||
|
|
||||||
|
- Modify: `resources/views/livewire/pages/about.blade.php`, `resources/css/public.css`,
|
||||||
|
`lang/ar/site.php`, `lang/en/site.php`. Do not edit `lang/*/about.php`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] `/about` renders the six sections in order with all existing `about.*` content in Arabic and
|
||||||
|
English: identity, vision, mission intro and three mission cards, six values, seven goals,
|
||||||
|
four service groups with their items.
|
||||||
|
- [ ] `/about` has exactly one `<h1>`; every `<img>` has a non-empty `alt`.
|
||||||
|
- [ ] Goal rows have no hover fill; service rows have the forest hover fill and link to `/booking`.
|
||||||
|
- [ ] No Scholarship content appears (`grep -i scholarship resources/views/livewire/pages/about.blade.php` returns nothing).
|
||||||
|
- [ ] `/about` has no horizontal scroll at 1440, 768 and 390px.
|
||||||
|
- [ ] `lang/*/about.php` is unchanged against BASE.
|
||||||
|
|
||||||
|
## Sub-tasks
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
id: 24-8
|
||||||
|
title: 24-8 Articles and Legal Pages
|
||||||
|
status: Ready for Dev
|
||||||
|
date: 2026-10-31
|
||||||
|
parent: 24
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Restyle the articles index, the article page and the legal pages. Behaviour does not change:
|
||||||
|
search (`wire:model.live.debounce.300ms="search"`), pagination (`paginate(10)`), result counts,
|
||||||
|
empty states, the 404 for unpublished posts, and the CMS content of legal pages. Remove the
|
||||||
|
temporary `<div class="wrap py-8">` wrappers added in 24-1.
|
||||||
|
|
||||||
|
### `/posts` — `resources/views/livewire/pages/posts/index.blade.php`
|
||||||
|
|
||||||
|
`<section class="sec" style="padding-top:56px"><div class="wrap">`:
|
||||||
|
- `<x-site.section-head :label="__('site.hero_eyebrow')" :title="__('site.nav_articles')">`
|
||||||
|
(the h2 reads «المقالات» / "Articles"; the existing `posts.posts` key reads "Posts" in English, so
|
||||||
|
it is not used here) with,
|
||||||
|
in the slot, the search field: a pill input (`.search`: min-height 48px, radius 999px, padding
|
||||||
|
12px 20px, `1.5px solid rgba(45,54,36,.18)`, white, width min(360px, 100%); focus gold border
|
||||||
|
plus `0 0 0 4px rgba(166,137,102,.2)`), same `wire:model` and placeholder
|
||||||
|
`posts.search_placeholder`, with an accessible label (`sr-only` text
|
||||||
|
`posts.search_placeholder`).
|
||||||
|
- The existing result-count / no-result lines (`posts.search_results`, `posts.no_results`) as a
|
||||||
|
muted paragraph, and the existing clear-search action as `btn btn-ghost`.
|
||||||
|
- The posts as `<x-site.post-card :post="$post" />` in a `.posts` grid (3 columns; 2 at ≤1100px;
|
||||||
|
1 at ≤760px — the grid CSS exists from 24-4; at this page the last card does not span two
|
||||||
|
columns: add `body.site .posts-index .post:last-child{grid-column:auto}` and give the grid the
|
||||||
|
extra class `posts-index`).
|
||||||
|
- Empty states `posts.no_posts` as before.
|
||||||
|
- Pagination: `{{ $posts->links() }}` kept; style its links under `body.site .pagination-wrap`
|
||||||
|
(wrap the call in `<div class="pagination-wrap">`): 44px pill buttons, `1.5px solid var(--line)`,
|
||||||
|
current page `background:var(--forest)` with cream text, disabled at 50% opacity.
|
||||||
|
|
||||||
|
### `/posts/{post}` — `resources/views/livewire/pages/posts/show.blade.php`
|
||||||
|
|
||||||
|
`<section class="sec" style="padding-top:56px"><article class="wrap" style="max-width:768px">`
|
||||||
|
(720px content + padding):
|
||||||
|
- `<span class="meta">` the published date (existing `translatedFormat('l, d F Y')` expression),
|
||||||
|
colour `--gold-ink`, .85rem, weight 600.
|
||||||
|
- `<h1>` = `$post->getTitle()` — display font, `clamp(2rem,3.4vw,3rem)`, margin 10px 0 28px.
|
||||||
|
- Body `<div class="prose prose-lg prose-brand max-w-none site-prose">{!! $post->getBody() !!}</div>`
|
||||||
|
(keep the existing output exactly). New CSS: `body.site .site-prose :is(h2,h3,h4){font-family:var(--display)}`,
|
||||||
|
`body.site .site-prose a{color:var(--olive)}`, `body.site .site-prose{font-size:1.06rem;line-height:1.85}`.
|
||||||
|
- Footer: `<a class="btn btn-ghost" href="{{ route('posts.index') }}" wire:navigate>`
|
||||||
|
`posts.back_to_posts`, preceded by a hairline `border-top:1px solid var(--line)` with 48px
|
||||||
|
margin-top and 24px padding-top. Remove `flux:heading`.
|
||||||
|
|
||||||
|
### Legal pages
|
||||||
|
|
||||||
|
- `resources/views/pages/legal.blade.php` — inside `<x-layouts.public>`:
|
||||||
|
`<section class="sec" style="padding-top:56px"><div class="wrap" style="max-width:768px">`,
|
||||||
|
`<h1>` `$page->getTitle()` (same h1 style as the article page), the existing
|
||||||
|
`prose prose-brand` block with the class `site-prose` added and its card styling replaced by
|
||||||
|
white background, radius 16px, `--shadow-1`, padding 40px (24px ≤760px); the existing
|
||||||
|
"last updated" line as muted .85rem text. Keep the `clean()` call and the
|
||||||
|
`pages.content_coming_soon` fallback unchanged.
|
||||||
|
- `resources/views/pages/terms.blade.php` and `resources/views/pages/privacy.blade.php` — the same
|
||||||
|
structure with their existing title and placeholder text. (`/terms` and `/privacy` redirect to
|
||||||
|
`/page/terms` and `/page/privacy`; these two views are kept in case they are rendered
|
||||||
|
directly.)
|
||||||
|
|
||||||
|
### Language keys
|
||||||
|
|
||||||
|
None new; `site.hero_eyebrow` (24-4) and `site.nav_articles` (24-2) are reused.
|
||||||
|
|
||||||
|
### Files
|
||||||
|
|
||||||
|
- Modify: `resources/views/livewire/pages/posts/index.blade.php`,
|
||||||
|
`resources/views/livewire/pages/posts/show.blade.php`, `resources/views/pages/legal.blade.php`,
|
||||||
|
`resources/views/pages/terms.blade.php`, `resources/views/pages/privacy.blade.php`,
|
||||||
|
`resources/css/public.css`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] `/posts` shows the section head, a working search (typing filters results after the
|
||||||
|
debounce and shows the count line), text-only post cards and styled pagination when there
|
||||||
|
are more than 10 published posts.
|
||||||
|
- [ ] `/posts/{post}` for a published post shows date, a display-font `<h1>`, the body and a Back
|
||||||
|
button to `/posts`; an unpublished post still returns 404.
|
||||||
|
- [ ] `/page/terms` and `/page/privacy` show the title, the sanitised CMS content (or the
|
||||||
|
coming-soon text) and the last-updated line.
|
||||||
|
- [ ] Each of these pages has exactly one `<h1>` and no horizontal scroll at 1440, 768 and 390px.
|
||||||
|
- [ ] The admin posts screens (`/admin/posts`) are unchanged (they do not use the public layout).
|
||||||
|
|
||||||
|
## Sub-tasks
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
id: 24-9
|
||||||
|
title: 24-9 Auth Pages Type and Controls
|
||||||
|
status: Ready for Dev
|
||||||
|
date: 2026-10-31
|
||||||
|
parent: 24
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Apply the redesign's type and form controls to the auth pages (login, forgot password, reset
|
||||||
|
password, confirm password, verify email, two-factor challenge) without changing their
|
||||||
|
composition. The split layout (forest panel with the logo on the start side at ≥1024px, cream form
|
||||||
|
panel with the existing `auth-pattern` background, the language toggle at the top end, and the
|
||||||
|
mobile logo above the form) stays exactly as it is. No public script is loaded on auth pages.
|
||||||
|
|
||||||
|
### Layouts
|
||||||
|
|
||||||
|
In each of `resources/views/components/layouts/auth/split.blade.php`,
|
||||||
|
`resources/views/components/layouts/auth/card.blade.php` and
|
||||||
|
`resources/views/components/layouts/auth/simple.blade.php`:
|
||||||
|
- After `@include('partials.head')` add `@include('partials.site-fonts')` and
|
||||||
|
`@vite(['resources/css/public.css'])`.
|
||||||
|
- Add the class `site-auth` to `<body>` and remove the inline
|
||||||
|
`style="font-family: var(--font-…)"` from `<body>`.
|
||||||
|
|
||||||
|
### CSS — `resources/css/public.css`, scoped under `body.site-auth`
|
||||||
|
|
||||||
|
- `body.site-auth{font-family:"IBM Plex Sans Arabic",system-ui,sans-serif;color:#1E2219}`.
|
||||||
|
- Headings (`h1, h2, [data-flux-heading]`): `font-family:"Markazi Text","Times New Roman",serif;
|
||||||
|
font-weight:700`.
|
||||||
|
- Text inputs (`input[type=email], input[type=password], input[type=text]`) — including those
|
||||||
|
rendered by `flux:input`: min-height 48px, radius 12px, border `1.5px solid rgba(45,54,36,.18)`,
|
||||||
|
white background; focus border `#A68966` and `box-shadow:0 0 0 4px rgba(166,137,102,.2)`, no
|
||||||
|
default outline.
|
||||||
|
- Primary submit buttons (`[data-flux-button][type=submit]` and `button[type=submit]`): radius
|
||||||
|
999px, min-height 48px, background `#A68966`, text `#1E2219`, weight 600, shadow
|
||||||
|
`0 1px 0 rgba(255,255,255,.35) inset, 0 8px 22px -8px rgba(166,137,102,.8)`; hover lift 2px.
|
||||||
|
- Links inside the form panel: colour `#4A5D23`, underline on hover.
|
||||||
|
- Focus ring for every control: `outline:2px solid #A68966; outline-offset:3px`.
|
||||||
|
- Reduced motion: no transitions under `prefers-reduced-motion: reduce`.
|
||||||
|
Inspect the rendered login page to confirm which attributes Flux outputs before writing the
|
||||||
|
selectors; target only elements inside `body.site-auth`.
|
||||||
|
|
||||||
|
### Files
|
||||||
|
|
||||||
|
- Modify: `resources/views/components/layouts/auth/split.blade.php`,
|
||||||
|
`resources/views/components/layouts/auth/card.blade.php`,
|
||||||
|
`resources/views/components/layouts/auth/simple.blade.php`, `resources/css/public.css`.
|
||||||
|
- Do not modify any file under `resources/views/livewire/auth/` or `app/Actions/Fortify/`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] `/login` renders in IBM Plex Sans Arabic with the heading in Markazi Text, 48px inputs with a
|
||||||
|
gold focus ring, and a gold pill submit button; the forest logo panel and the cream pattern
|
||||||
|
panel are unchanged in layout.
|
||||||
|
- [ ] Logging in with valid credentials still works for an admin and a client (redirect to their
|
||||||
|
dashboard), and invalid credentials still show the existing error.
|
||||||
|
- [ ] `/forgot-password` shows the same styles and still sends the reset link for a known email.
|
||||||
|
- [ ] The auth pages' HTML contains `public.css` and `class="site-auth`, and does not contain
|
||||||
|
`public.js`.
|
||||||
|
- [ ] The admin and client dashboards contain neither `public.css` nor `site-auth`.
|
||||||
|
- [ ] `/login` has no horizontal scroll at 1440, 768 and 390px.
|
||||||
|
|
||||||
|
## Sub-tasks
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
id: 24
|
||||||
|
title: 24 Public-Site Redesign (PRD 1.4)
|
||||||
|
status: Ready for Dev
|
||||||
|
date: 2026-10-31
|
||||||
|
parent: null
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Container for the public-site redesign the client approved on 2026-10-31 (mockup v3,
|
||||||
|
`docs/ux-mockup.html`, commit `caec396`). The redesign is presentation-only: no route, model,
|
||||||
|
migration, service, mail or job changes. It restyles the public pages (`/`, `/about`, `/posts`,
|
||||||
|
`/posts/{post}`, `/booking`, `/booking/success`, `/page/{slug}`, `/terms`, `/privacy`) and the
|
||||||
|
auth pages' type and controls. The admin and client dashboards, emails, PDFs and
|
||||||
|
`/forms/nablus-workshop` must not change.
|
||||||
|
|
||||||
|
This parent carries no implementation of its own. Execute the subtasks in ID order.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Subtasks 24-1, 24-2, 24-3, 24-4, 24-5, 24-6, 24-7, 24-8, 24-9 and 24-10 are all Done.
|
||||||
|
|
||||||
|
## Sub-tasks
|
||||||
|
|
||||||
|
- 24-1 Public design foundation (stylesheet, script, fonts, layout)
|
||||||
|
- 24-2 Navigation and footer
|
||||||
|
- 24-3 Signature balance component
|
||||||
|
- 24-4 Home page sections (hero to latest articles)
|
||||||
|
- 24-5 Home booking card (compact booking and day strip)
|
||||||
|
- 24-6 Booking page and booking success
|
||||||
|
- 24-7 About page
|
||||||
|
- 24-8 Articles and legal pages
|
||||||
|
- 24-9 Auth pages type and controls
|
||||||
|
- 24-10 Redesign integration verification
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
# Task Index: Libra
|
||||||
|
**Generated:** 2026-10-31
|
||||||
|
|
||||||
|
| ID | Title | Status | Parent |
|
||||||
|
|----|-------|--------|--------|
|
||||||
|
| 1 | 1 Core Foundation & Infrastructure | Done | — |
|
||||||
|
| 1-1 | 1-1 Project Setup & Database Schema | Done | 1 |
|
||||||
|
| 1-2 | 1-2 Authentication & Role System | Done | 1 |
|
||||||
|
| 1-3 | 1-3 Bilingual Infrastructure (Arabic/English) | Done | 1 |
|
||||||
|
| 1-4 | 1-4 Base UI & Navigation | Done | 1 |
|
||||||
|
| 2 | 2 User Management System | Done | — |
|
||||||
|
| 2-1 | 2-1 Individual Client Account Management | Done | 2 |
|
||||||
|
| 2-2 | 2-2 Company/Corporate Client Account Management | Done | 2 |
|
||||||
|
| 2-3 | 2-3 Account Type Conversion | Done | 2 |
|
||||||
|
| 2-4 | 2-4 Account Lifecycle Management | Done | 2 |
|
||||||
|
| 2-5 | 2-5 Account Creation Email Notification | Done | 2 |
|
||||||
|
| 3 | 3 Booking & Consultation System | Done | — |
|
||||||
|
| 3-1 | 3-1 Working Hours Configuration | Done | 3 |
|
||||||
|
| 3-2 | 3-2 Time Slot Blocking | Done | 3 |
|
||||||
|
| 3-3 | 3-3 Availability Calendar Display | Done | 3 |
|
||||||
|
| 3-4 | 3-4 Booking Request Submission | Done | 3 |
|
||||||
|
| 3-5 | 3-5 Admin Booking Review & Approval | Done | 3 |
|
||||||
|
| 3-6 | 3-6 Calendar File Generation (.ics) | Done | 3 |
|
||||||
|
| 3-7 | 3-7 Consultation Management | Done | 3 |
|
||||||
|
| 3-8 | 3-8 Consultation Reminders | Done | 3 |
|
||||||
|
| 3-9 | 3-9 Guest Booking Request | Done | 3 |
|
||||||
|
| 4 | 4 Case Timeline System | Done | — |
|
||||||
|
| 4-1 | 4-1 Timeline Creation | Done | 4 |
|
||||||
|
| 4-2 | 4-2 Timeline Updates Management | Done | 4 |
|
||||||
|
| 4-3 | 4-3 Timeline Archiving | Done | 4 |
|
||||||
|
| 4-4 | 4-4 Admin Timeline Dashboard | Done | 4 |
|
||||||
|
| 4-5 | 4-5 Client Timeline View | Done | 4 |
|
||||||
|
| 4-6 | 4-6 Timeline Update Notifications | Done | 4 |
|
||||||
|
| 5 | 5 Posts/Blog System | Done | — |
|
||||||
|
| 5-1 | 5-1 Post Creation & Editing | Done | 5 |
|
||||||
|
| 5-2 | 5-2 Post Management Dashboard | Done | 5 |
|
||||||
|
| 5-3 | 5-3 Post Deletion | Done | 5 |
|
||||||
|
| 5-4 | 5-4 Public Posts Display | Done | 5 |
|
||||||
|
| 5-5 | 5-5 Post Search | Done | 5 |
|
||||||
|
| 6 | 6 Admin Dashboard | Done | — |
|
||||||
|
| 6-1 | 6-1 Dashboard Overview & Statistics | Done | 6 |
|
||||||
|
| 6-2 | 6-2 Analytics Charts | Done | 6 |
|
||||||
|
| 6-3 | 6-3 Quick Actions Panel | Done | 6 |
|
||||||
|
| 6-4 | 6-4 Data Export - User Lists | Done | 6 |
|
||||||
|
| 6-5 | 6-5 Data Export - Consultation Records | Done | 6 |
|
||||||
|
| 6-6 | 6-6 Data Export - Timeline Reports | Done | 6 |
|
||||||
|
| 6-7 | 6-7 Monthly Statistics Report | Done | 6 |
|
||||||
|
| 6-8 | 6-8 System Settings | Done | 6 |
|
||||||
|
| 6-9 | 6-9 Legal Pages Editor | Done | 6 |
|
||||||
|
| 6-10 | 6-10 Audit Log Viewer | Done | 6 |
|
||||||
|
| 7 | 7 Client Dashboard | Done | — |
|
||||||
|
| 7-1 | 7-1 Client Dashboard Overview | Done | 7 |
|
||||||
|
| 7-2 | 7-2 My Consultations View | Done | 7 |
|
||||||
|
| 7-3 | 7-3 My Cases/Timelines View (Dashboard Integration) | Done | 7 |
|
||||||
|
| 7-4 | 7-4 My Profile View | Done | 7 |
|
||||||
|
| 7-5 | 7-5 New Booking Interface | Done | 7 |
|
||||||
|
| 7-6 | 7-6 Booking Limit Indicator | Done | 7 |
|
||||||
|
| 8 | 8 Email Notification System | Done | — |
|
||||||
|
| 8-1 | 8-1 Email Infrastructure Setup | Done | 8 |
|
||||||
|
| 8-2 | 8-2 Welcome Email (Account Created) | Done | 8 |
|
||||||
|
| 8-3 | 8-3 Booking Submitted Confirmation | Done | 8 |
|
||||||
|
| 8-4 | 8-4 Booking Approved Email | Done | 8 |
|
||||||
|
| 8-5 | 8-5 Booking Rejected Email | Done | 8 |
|
||||||
|
| 8-6 | 8-6 Consultation Reminder (24 Hours) | Done | 8 |
|
||||||
|
| 8-7 | 8-7 Consultation Reminder (2 Hours) | Done | 8 |
|
||||||
|
| 8-8 | 8-8 Timeline Update Notification | Done | 8 |
|
||||||
|
| 8-9 | 8-9 Admin Notification - New Booking | Done | 8 |
|
||||||
|
| 8-10 | 8-10 Admin Notification - System Events | Done | 8 |
|
||||||
|
| 9 | 9 Design & Branding Implementation | Done | — |
|
||||||
|
| 9-1 | 9-1 Color System Implementation | Done | 9 |
|
||||||
|
| 9-2 | 9-2 Typography System | Done | 9 |
|
||||||
|
| 9-3 | 9-3 Logo Integration | Done | 9 |
|
||||||
|
| 9-4 | 9-4 Component Styling - Buttons | Done | 9 |
|
||||||
|
| 9-5 | 9-5 Component Styling - Forms | Done | 9 |
|
||||||
|
| 9-6 | 9-6 Component Styling - Cards & Containers | Done | 9 |
|
||||||
|
| 9-7 | 9-7 Navigation & Footer Styling | Done | 9 |
|
||||||
|
| 9-8 | 9-8 RTL/LTR Layout Perfection | Done | 9 |
|
||||||
|
| 9-9 | 9-9 Responsive Design Implementation | Done | 9 |
|
||||||
|
| 9-10 | 9-10 Accessibility Compliance | Done | 9 |
|
||||||
|
| 9-11 | 9-11 Animations & Micro-interactions | Done | 9 |
|
||||||
|
| 10 | 10 Brand Color Refresh | Done | — |
|
||||||
|
| 10-1 | 10-1 Core CSS Theme Update | Done | 10 |
|
||||||
|
| 10-2 | 10-2 Blade Component Color Migration | Done | 10 |
|
||||||
|
| 10-3 | 10-3 Email Template Color Update | Done | 10 |
|
||||||
|
| 10-4 | 10-4 PDF Template Color Update | Done | 10 |
|
||||||
|
| 10-5 | 10-5 Logo and SVG Assets Update | Done | 10 |
|
||||||
|
| 10-6 | 10-6 Documentation Update | Done | 10 |
|
||||||
|
| 11 | 11 Guest Booking | Done | — |
|
||||||
|
| 11-1 | 11-1 Database Schema & Model Updates | Done | 11 |
|
||||||
|
| 11-2 | 11-2 Public Booking Form with Custom Captcha | Done | 11 |
|
||||||
|
| 11-3 | 11-3 Guest Email Notifications & Admin Integration | Done | 11 |
|
||||||
|
| 11-4 | 11-4 Documentation Updates | Done | 11 |
|
||||||
|
| 12 | 12 Branding Refresh (Logo + Color Scheme) | Done | — |
|
||||||
|
| 12-1 | 12-1 Logo Asset Deployment and CSS Layout Updates | Done | 12 |
|
||||||
|
| 12-2 | 12-2 Core CSS Theme Update - Dark Charcoal & Warm Gold Palette | Done | 12 |
|
||||||
|
| 12-3 | 12-3 Blade Component Color Migration - Dark Charcoal & Warm Gold | Done | 12 |
|
||||||
|
| 12-4 | 12-4 Email Template Color Update - Dark Charcoal & Warm Gold | Done | 12 |
|
||||||
|
| 12-5 | 12-5 PDF Template Color Update - Dark Charcoal & Warm Gold | Done | 12 |
|
||||||
|
| 12-6 | 12-6 Brand Documentation Update - Dark Charcoal & Warm Gold | Done | 12 |
|
||||||
|
| 13 | 13 Authentication Pages Design Enhancement | Done | — |
|
||||||
|
| 13-1 | 13-1 Language Toggle Visibility Enhancement | Done | 13 |
|
||||||
|
| 13-2 | 13-2 Auth Layout Split Design Update | Done | 13 |
|
||||||
|
| 13-3 | 13-3 Subtle Background Pattern Implementation | Done | 13 |
|
||||||
|
| 13-4 | 13-4 Auth Pages Migration to Split Layout | Done | 13 |
|
||||||
|
| 13-5 | 13-5 Visual QA and Refinement | Done | 13 |
|
||||||
|
| 14 | 14 Home Page Redesign | Done | — |
|
||||||
|
| 14-1 | 14-1 Hero Section Implementation | Done | 14 |
|
||||||
|
| 14-2 | 14-2 About Section with Lawyer Profile | Done | 14 |
|
||||||
|
| 14-3 | 14-3 Services Section | Done | 14 |
|
||||||
|
| 14-4 | 14-4 Values Section | Done | 14 |
|
||||||
|
| 14-5 | 14-5 Latest Posts Section | Done | 14 |
|
||||||
|
| 14-6 | 14-6 Footer Contact Information Update | Done | 14 |
|
||||||
|
| 15 | 15 Potential Clients Management | Done | — |
|
||||||
|
| 15-1 | 15-1 Database Schema & Model Setup | Done | 15 |
|
||||||
|
| 15-2 | 15-2 Potential Clients List with Type Filter | Done | 15 |
|
||||||
|
| 15-3 | 15-3 CRUD Operations for Potential Clients | Done | 15 |
|
||||||
|
| 16 | 16 About Us Page | Done | — |
|
||||||
|
| 16-1 | 16-1 About Page Layout & Hero Section | Done | 16 |
|
||||||
|
| 16-2 | 16-2 Vision & Mission Section | Done | 16 |
|
||||||
|
| 16-3 | 16-3 Values Section | Done | 16 |
|
||||||
|
| 16-4 | 16-4 Goals Section | Done | 16 |
|
||||||
|
| 16-5 | 16-5 Legal Services Section | Done | 16 |
|
||||||
|
| 16-6 | 16-6 Scholarship Aspiration Section (REMOVED from production 2026-01-11 — do not re-implement) | Done | 16 |
|
||||||
|
| 17 | 17 Nablus Workshop Registration Form | Done | — |
|
||||||
|
| 17-1 | 17-1 Registration mailable and email template | Done | 17 |
|
||||||
|
| 17-2 | 17-2 Standalone registration page, form, and submit wiring | Done | 17 |
|
||||||
|
| 18 | 18 Nablus Workshop Form - Image Sizing Fix | Done | — |
|
||||||
|
| 19 | 19 Nablus Workshop Form - Remove Logo, Shrink Flyer Further | Done | — |
|
||||||
|
| 20 | 20 Nablus Workshop Form - Match Site Background and Card Styling | Done | — |
|
||||||
|
| 21 | 21 Nablus Workshop Form - Flyer Size Adjustment | Done | — |
|
||||||
|
| 22 | 22 Fix Footer Address - Add Missing Street Name | Done | — |
|
||||||
|
| 23 | 23 Fix AdminNotificationService Carbon TypeError Masking Exceptions | Done | — |
|
||||||
|
| 24 | 24 Public-Site Redesign (PRD 1.4) | Ready for Dev | — |
|
||||||
|
| 24-1 | 24-1 Public Design Foundation (Stylesheet, Script, Fonts, Layout) | Ready for Dev | 24 |
|
||||||
|
| 24-2 | 24-2 Navigation and Footer | Ready for Dev | 24 |
|
||||||
|
| 24-3 | 24-3 Signature Balance Component | Ready for Dev | 24 |
|
||||||
|
| 24-4 | 24-4 Home Page Sections (Hero to Latest Articles) | Ready for Dev | 24 |
|
||||||
|
| 24-5 | 24-5 Home Booking Card (Compact Booking and Day Strip) | Ready for Dev | 24 |
|
||||||
|
| 24-6 | 24-6 Booking Page and Booking Success | Ready for Dev | 24 |
|
||||||
|
| 24-7 | 24-7 About Page | Ready for Dev | 24 |
|
||||||
|
| 24-8 | 24-8 Articles and Legal Pages | Ready for Dev | 24 |
|
||||||
|
| 24-9 | 24-9 Auth Pages Type and Controls | Ready for Dev | 24 |
|
||||||
|
| 24-10 | 24-10 Redesign Integration Verification | Ready for Dev | 24 |
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
**Last synced:** 2026-09-28 — 19 QA passed (logo removed, flyer max-h-40 confirmed, 7/7 tests), Done
|
**Last synced:** 2026-09-28 — 19 QA passed (logo removed, flyer max-h-40 confirmed, 7/7 tests), Done
|
||||||
**Last synced:** 2026-09-28 — 20 QA passed (auth-pattern/bg-background/card/text-body confirmed in compiled CSS and Blade, 7/7 tests), Done
|
**Last synced:** 2026-09-28 — 20 QA passed (auth-pattern/bg-background/card/text-body confirmed in compiled CSS and Blade, 7/7 tests), Done
|
||||||
**Last synced:** 2026-09-28 — 21 QA passed (flyer max-h-56 confirmed in Blade and compiled CSS, HTTP 200, 7/7 tests), Done
|
**Last synced:** 2026-09-28 — 21 QA passed (flyer max-h-56 confirmed in Blade and compiled CSS, HTTP 200, 7/7 tests), Done
|
||||||
|
**Last synced:** 2026-10-31 — added tasks 24, 24-1…24-10 (public-site redesign, PRD 1.4); readiness review same day: 6 task-level fixes, all 11 promoted to Ready for Dev
|
||||||
|
|
||||||
| ID | Title | Status |
|
| ID | Title | Status |
|
||||||
|----|-------|--------|
|
|----|-------|--------|
|
||||||
@@ -135,3 +136,14 @@
|
|||||||
| 21 | 21 Nablus Workshop Form - Flyer Size Adjustment | Done |
|
| 21 | 21 Nablus Workshop Form - Flyer Size Adjustment | Done |
|
||||||
| 22 | 22 Fix Footer Address - Add Missing Street Name | Done |
|
| 22 | 22 Fix Footer Address - Add Missing Street Name | Done |
|
||||||
| 23 | 23 Fix AdminNotificationService Carbon TypeError Masking Exceptions | Done |
|
| 23 | 23 Fix AdminNotificationService Carbon TypeError Masking Exceptions | Done |
|
||||||
|
| 24 | 24 Public-Site Redesign (PRD 1.4) | Ready for Dev |
|
||||||
|
| 24-1 | 24-1 Public Design Foundation (Stylesheet, Script, Fonts, Layout) | Ready for Dev |
|
||||||
|
| 24-2 | 24-2 Navigation and Footer | Ready for Dev |
|
||||||
|
| 24-3 | 24-3 Signature Balance Component | Ready for Dev |
|
||||||
|
| 24-4 | 24-4 Home Page Sections (Hero to Latest Articles) | Ready for Dev |
|
||||||
|
| 24-5 | 24-5 Home Booking Card (Compact Booking and Day Strip) | Ready for Dev |
|
||||||
|
| 24-6 | 24-6 Booking Page and Booking Success | Ready for Dev |
|
||||||
|
| 24-7 | 24-7 About Page | Ready for Dev |
|
||||||
|
| 24-8 | 24-8 Articles and Legal Pages | Ready for Dev |
|
||||||
|
| 24-9 | 24-9 Auth Pages Type and Controls | Ready for Dev |
|
||||||
|
| 24-10 | 24-10 Redesign Integration Verification | Ready for Dev |
|
||||||
|
|||||||
+320
-1
@@ -1,4 +1,323 @@
|
|||||||
# UX / Design Reference — Libra Law Firm
|
# UX Design Spec: Libra for Rights — Public-Site Redesign
|
||||||
|
**Version:** 2.0
|
||||||
|
**Date:** 2026-10-31
|
||||||
|
**Status:** Approved (client approved mockup v3 on 2026-10-31)
|
||||||
|
**Visual mockup:** `docs/ux-mockup.html` (open in a browser to view; v3, commit `caec396`)
|
||||||
|
|
||||||
|
This file has two parts:
|
||||||
|
|
||||||
|
- **Part A — Public site (redesign, approved 2026-10-31).** Governs every page rendered through
|
||||||
|
`resources/views/components/layouts/public.blade.php` and the auth layouts' type and controls.
|
||||||
|
- **Part B — Dashboards, emails and the Nablus form (unchanged).** The retrospective record of the
|
||||||
|
design system as built before the redesign. It still governs the admin and client dashboards,
|
||||||
|
the emails and `/forms/nablus-workshop`, none of which the redesign touches.
|
||||||
|
|
||||||
|
Where Part A and Part B disagree about a public page, Part A wins.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Part A — Public site
|
||||||
|
|
||||||
|
## Design Direction
|
||||||
|
|
||||||
|
Arabic-first, editorial and quiet: the firm reads like a well-bound legal document, not a tech
|
||||||
|
product. The brand palette (forest, gold, cream, olive) is kept and used with more confidence —
|
||||||
|
large forest bands, gold as thread and rule, cream as linen. Headlines are set in an editorial
|
||||||
|
naskh serif (Markazi Text); body in IBM Plex Sans Arabic. Sections are numbered like articles of
|
||||||
|
a statute ("المادة ٠١ — المؤسِّسة"). The signature is an engraved-gold balance over olive-tree
|
||||||
|
photography that settles into level on load and is weighed by the visitor's pointer. Tatreez
|
||||||
|
appears only inside real photography — Naser ruled out drawn cross-stitch ("drawing using xxx and
|
||||||
|
other symbols is more of a tech stuff not lawyers", 2026-10-31). Every claim on the page is backed
|
||||||
|
by the code (code-fit check, 2026-10-31): no availability promises, no response-time promises, no
|
||||||
|
article images.
|
||||||
|
|
||||||
|
**Layout default:** desktop-first; every layout works at 768px and 390px with no horizontal scroll.
|
||||||
|
|
||||||
|
**Scope:** public pages only — home `/`, about `/about`, posts `/posts` and `/posts/{post}`,
|
||||||
|
booking `/booking` and `/booking/success`, legal `/page/{slug}` (terms, privacy), and the auth
|
||||||
|
pages (type and controls only, see *Auth pages*). Out of scope: admin and client dashboards,
|
||||||
|
emails, PDFs, `/forms/nablus-workshop`.
|
||||||
|
|
||||||
|
## Layout & Navigation
|
||||||
|
|
||||||
|
- **Containment.** The public layout no longer wraps pages in one padded 1200px container; each
|
||||||
|
page section is full-bleed and places its own content in a `.wrap` container:
|
||||||
|
`max-width: 1200px; margin-inline: auto; padding-inline: 28px` (18px at ≤760px).
|
||||||
|
- **Section rhythm.** Standard section padding 120px top and bottom (80px at ≤760px). Section
|
||||||
|
header (`.sec-head`): flex row, items aligned to the end, space-between, gap 32px, wrap,
|
||||||
|
margin-bottom 54px; left side holds the article label and h2, right side a muted lede
|
||||||
|
(max-width 30em).
|
||||||
|
- **Article label** (`.art-no`): display font, 1.15rem, weight 600, `--gold-ink`. Format
|
||||||
|
`المادة ٠N — <name>` / `Article 0N — <Name>` (Arabic-Indic digits in Arabic).
|
||||||
|
- **Eyebrow** (`.eyebrow`): 0.82rem, weight 600, `--gold-ink`, preceded by a 22×2px rule in
|
||||||
|
currentColor with 10px gap; in English 0.74rem, uppercase, letter-spacing .16em. Arabic text
|
||||||
|
never gets letter-spacing (it breaks joining).
|
||||||
|
- **Navigation** (sticky, top 0, z 50, height 72px): background `rgba(244,241,234,.82)` with
|
||||||
|
`backdrop-filter: blur(14px) saturate(1.2)`; bottom border transparent until the page has
|
||||||
|
scrolled more than 8px, then `1px solid var(--line)` plus shadow `0 8px 24px -18px
|
||||||
|
rgba(30,34,25,.5)`. Order (start → end): brand (seal + wordmark), links, then tools (language
|
||||||
|
toggle, client login, gold "Book a consultation" button). Links: Home, About, Services (→
|
||||||
|
`/#services`), Articles (→ `/posts`), Booking (→ `/booking`). Link style: 10px 14px padding,
|
||||||
|
weight 500, .95rem; hover colour `--olive`; a 2px dashed gold underline (4px dash, 3px gap)
|
||||||
|
scales in from the inline-start edge on hover and stays on the current page's link
|
||||||
|
(`aria-current="page"`). Logged-in visitors see "Dashboard" (to their role's dashboard) and
|
||||||
|
"Log out" in place of "Client login", as today.
|
||||||
|
- **Brand mark:** a 46×46px "seal" — `public/images/logo.png` as a cover background on white,
|
||||||
|
2px solid `#1A1A1A` border, shadow `3px 3px 5px rgba(26,26,26,.35)` — followed by the wordmark:
|
||||||
|
«ليبرا» (display font, 1.35rem) over «للحقوق» (.72rem, muted). English: "Libra" over
|
||||||
|
"FOR RIGHTS" (letter-spacing .12em).
|
||||||
|
- **Mobile (≤1100px):** links hide; a 44×44px menu button (1.5px `--line` border, radius 10px,
|
||||||
|
white) opens the existing Alpine mobile menu, restyled as a cream sheet below the nav with
|
||||||
|
full-width 48px rows. At ≤760px the nav's gold button and the client-login link also hide (both
|
||||||
|
remain inside the sheet).
|
||||||
|
- **Reading progress rule:** a fixed 2px bar at the very top of the viewport (z 60), gradient
|
||||||
|
`#8A7555 → #E2CDA4 → #A68966`, `transform: scaleX(p)` where p = scrollY / (scrollHeight −
|
||||||
|
innerHeight); origin is the inline-start edge (right in RTL, left in LTR). Decorative
|
||||||
|
(`aria-hidden`).
|
||||||
|
- **Footer:** background `--forest-2` (#232A1C), text `--gold-lt`, margin-top 120px, padding 72px 0
|
||||||
|
28px, .92rem. A 1px top hairline: horizontal gradient transparent → `rgba(196,168,130,.7)` →
|
||||||
|
transparent. Four columns (1.4fr 1fr 1fr 1fr, gap 40px; 2 columns ≤1100px; 1 column ≤760px):
|
||||||
|
(1) brand mark with cream wordmark and the tagline «ملتزمون بالعدالة – متجذرون بالكرامة – مدفوعون
|
||||||
|
للدفاع»; (2) "Explore": About, Services, Articles, Booking; (3) "Clients": Client login, Terms of
|
||||||
|
use, Privacy policy; (4) "Contact": phone (LTR), email, address. Column headings: display font,
|
||||||
|
1.15rem, cream. Links: 10px vertical padding, hover → cream and a 3px nudge toward the
|
||||||
|
inline-start. Bottom row: copyright, top border `rgba(244,241,234,.1)`, .8rem. No photography
|
||||||
|
credit line.
|
||||||
|
|
||||||
|
## Visual Style
|
||||||
|
|
||||||
|
### Color Palette
|
||||||
|
|
||||||
|
| Token | Hex | Use |
|
||||||
|
|---|---|---|
|
||||||
|
| `--forest` | `#2D3624` | hero frame ground, pillar band, booking card, hover fill of service rows |
|
||||||
|
| `--forest-2` | `#232A1C` | footer only (depth) |
|
||||||
|
| `--gold` | `#A68966` | gold buttons, rules, row numbers, focus ring |
|
||||||
|
| `--gold-ink` | `#8A7555` | gold text on cream (labels, dates) |
|
||||||
|
| `--gold-lt` | `#C4A882` | small gold text on forest |
|
||||||
|
| `--cream` | `#F4F1EA` | page ground |
|
||||||
|
| `--olive` | `#4A5D23` | link hover, emphasis word in the hero, active accents |
|
||||||
|
| `--ink` | `#1E2219` | body and heading text |
|
||||||
|
| `--muted` | `#5B6152` | secondary text |
|
||||||
|
| `--line` | `rgba(45,54,36,.14)` | hairlines and borders |
|
||||||
|
| fabric ground | `#ECE7DC` | background of the Vision section |
|
||||||
|
|
||||||
|
The palette is the brand's own (`docs/brand.md`); `--ink`, `--muted`, `--forest-2` and the fabric
|
||||||
|
ground are darker/lighter steps of it, added for contrast and depth. Status colours (success,
|
||||||
|
danger, warning) are unchanged from Part B.
|
||||||
|
|
||||||
|
**Page ground:** `--cream` with a linen weave —
|
||||||
|
`repeating-linear-gradient(0deg, rgba(45,54,36,.025) 0 1px, transparent 1px 3px),
|
||||||
|
repeating-linear-gradient(90deg, rgba(45,54,36,.022) 0 1px, transparent 1px 4px)`.
|
||||||
|
|
||||||
|
**Shadows:** `--shadow-1: 0 1px 2px rgba(30,34,25,.06), 0 4px 14px rgba(30,34,25,.06)`;
|
||||||
|
`--shadow-2: 0 2px 4px rgba(30,34,25,.08), 0 18px 40px -12px rgba(30,34,25,.28)`.
|
||||||
|
**Easing:** `--ease: cubic-bezier(.2,.7,.2,1)`. **Radius:** cards 16px, buttons and chips 999px,
|
||||||
|
inputs 12px.
|
||||||
|
|
||||||
|
### Typography
|
||||||
|
|
||||||
|
- **Display** (h1–h3, article labels, blockquotes, numbers): **Markazi Text**, weights 400–700,
|
||||||
|
fallback `"Times New Roman", serif`. Headings weight 700, line-height 1.25.
|
||||||
|
- **Body and UI:** **IBM Plex Sans Arabic**, weights 300–600, fallback `system-ui, sans-serif`.
|
||||||
|
17px, line-height 1.75 (16px at ≤760px). Both families carry Latin glyphs, so English uses the
|
||||||
|
same two families.
|
||||||
|
- Loaded from Google Fonts with `display=swap`, on the public and auth layouts only. The dashboards
|
||||||
|
keep Cairo/Montserrat.
|
||||||
|
- Sizes: hero h1 `clamp(2.6rem, 4.6vw, 4.1rem)`, line-height 1.12, word-spacing .08em (English:
|
||||||
|
letter-spacing −.02em); section h2 `clamp(2rem, 3.4vw, 3rem)`; row h3 1.65rem; card h3 1.4rem;
|
||||||
|
post h3 1.35rem; lede 1.15rem.
|
||||||
|
|
||||||
|
### Tone & Style
|
||||||
|
|
||||||
|
Editorial, warm, unhurried. Real photography only (olive tree, embroidery on a hoop, olives,
|
||||||
|
hills, the founder's portrait). No drawn cross-stitch, no gradient blobs, no carousels. Depth comes
|
||||||
|
from layered shadows, gold hairlines and the engraved balance.
|
||||||
|
|
||||||
|
### Buttons
|
||||||
|
|
||||||
|
- **Gold** (`.btn-gold`): pill (radius 999px), min-height 48px, padding 12px 26px, weight 600,
|
||||||
|
background `--gold`, text `--ink`, shadow `0 1px 0 rgba(255,255,255,.35) inset, 0 8px 22px -8px
|
||||||
|
rgba(166,137,102,.8)`. Hover: lift 2px, deeper glow plus a 6px `rgba(166,137,102,.14)` halo, and
|
||||||
|
a white shimmer (105° gradient, 30–70%) sweeps across in .7s.
|
||||||
|
- **Ghost** (`.btn-ghost`): transparent, 1.5px inset `--forest` ring, text `--ink`. Hover: fills
|
||||||
|
`--forest`, text cream, lift 2px. On dark grounds: cream text and a `rgba(244,241,234,.6)` ring;
|
||||||
|
hover fills cream with forest text.
|
||||||
|
- An arrow icon inside a button nudges 4px toward the reading direction on hover; it is mirrored
|
||||||
|
in LTR.
|
||||||
|
- Focus (all controls): `outline: 2px solid var(--gold); outline-offset: 3px`.
|
||||||
|
|
||||||
|
## Key Screens / Flows
|
||||||
|
|
||||||
|
### Home `/`
|
||||||
|
|
||||||
|
Top to bottom (content is real; copy keys are listed in the tasks):
|
||||||
|
|
||||||
|
1. **Hero** — two columns (1fr : 1.08fr, gap 56px; one column ≤1100px). Start column: eyebrow
|
||||||
|
«ليبرا للحقوق · فلسطين»; h1 «العدالة *ممارسةٌ يومية*، لا مبدأٌ نظري.» with the emphasised phrase
|
||||||
|
in `--olive` and a dashed gold underline; lede (the existing `home.intro`); gold "Book a
|
||||||
|
consultation" (→ `#book`) and ghost "Our services" (→ `#services`); a trust strip of three
|
||||||
|
items separated from the CTAs by a hairline: «أفراد وشركات / استشارات وتمثيل», «المرأة والأسرة /
|
||||||
|
دعم قانوني متخصص», «عربي · English / خدمة بلغتين». End column: the **balance frame** (see
|
||||||
|
Signature Moment): an arched frame (aspect 4:4.6, radius 999px 999px 18px 18px) holding the
|
||||||
|
olive-tree photo under a forest gradient veil, a soft gold glow, the gold balance, and the
|
||||||
|
italic caption «لكلّ حقٍّ وزنُه». A cream tag with a pulsing olive dot overlaps the frame edge:
|
||||||
|
«استشارات حضورية أو عن بُعد».
|
||||||
|
2. **Pillars band** — full-bleed forest band, three columns divided by cream hairlines: Justice,
|
||||||
|
Dignity, Advocacy, each with a gold gem (12px rotated square) and a one-line gloss in
|
||||||
|
`--gold-lt`. Hover lightens the column background.
|
||||||
|
3. **Founder** (Article 01) — portrait (aspect 4:5, radius 18px, greyscale, an inset dashed gold
|
||||||
|
frame, a cream name card «هدى عرموش / المؤسِّسة · محامية»), h2 «تعرّف على المؤسِّسة», a pull
|
||||||
|
quote «العدالة ممارسة يومية، وليست مجرد مبدأ نظري.» (display font, gold inline-start rule), the
|
||||||
|
two bio paragraphs, and a ghost "Read our story" → `/about`. Hover on the portrait restores some
|
||||||
|
colour and scales it 3%.
|
||||||
|
4. **Services docket** (Article 02) — four rows, each a link to `#book`: big gold number, title,
|
||||||
|
chips of the service's items, a round arrow button. Hover: the row fills forest, text turns
|
||||||
|
cream, chips go gold-light, the arrow fills gold, a dashed gold inner border appears, lift 2px.
|
||||||
|
5. **Vision** (Article 03, fabric ground `#ECE7DC`) — text column (h2 «منسوجةٌ من نسيج المجتمع»
|
||||||
|
and a display-font paragraph) beside the embroidery-hoop photo (circular, two gold ring
|
||||||
|
shadows). Below it, three cards: "For our institution" (forest panel with a static balance),
|
||||||
|
"For society" (olives photo), "For Palestinians in the diaspora" (hills photo); each with an
|
||||||
|
italic label on the image, a title and one line.
|
||||||
|
6. **Values** (Article 04) — six cards in a row (3 ≤1100px, 2 ≤760px): a gold-ringed medallion
|
||||||
|
with a fine-line icon, and the value name. Hover: card lifts and tilts −0.6°, the medallion
|
||||||
|
rotates −8°, gains a gold glow, the icon turns olive.
|
||||||
|
7. **Latest articles** (Article 05) — the three most recent published posts as text-only cards
|
||||||
|
(date, title, excerpt, "Read more"), gold top border turning olive on hover, a large faint
|
||||||
|
quotation mark in the corner; "View all articles" ghost button → `/posts`. No post → the
|
||||||
|
existing "Articles coming soon" line in place of the grid.
|
||||||
|
8. **Booking card** (Article 06, `id="book"`) — one rounded (28px) forest card in two columns:
|
||||||
|
start panel with h2 "Book a consultation", the lede «اختر يوماً ووقتاً متاحَين، وأخبرنا باختصار
|
||||||
|
عن قضيتك. نراجع كل طلب ونؤكّد الموعد بالبريد الإلكتروني — دون الحاجة إلى حساب.», phone, email,
|
||||||
|
address, and the finale balance (190px); end panel is a cream inset form (see *Booking flow*).
|
||||||
|
|
||||||
|
### Booking flow (home card and `/booking`)
|
||||||
|
|
||||||
|
Both surfaces run the existing guest-booking component and its rules unchanged: name, email,
|
||||||
|
phone (all required), a case summary (required, minimum 20 characters), the math captcha, one
|
||||||
|
booking per email per day, slots from `AvailabilityService`, the existing confirm step, and the
|
||||||
|
redirect to `/booking/success`. Logged-in visitors see the existing "you are logged in" message,
|
||||||
|
restyled, in place of the form.
|
||||||
|
|
||||||
|
- **Home card (compact):** fields in this order — name and phone side by side, email, a **day
|
||||||
|
strip**, a **slot row**, the case summary, the captcha, the gold submit "Request this
|
||||||
|
appointment" (full width), and the note «ملاحظة: يمكنك حجز استشارة واحدة كحدّ أقصى في اليوم.».
|
||||||
|
The day strip shows the next 14 calendar days that have at least one available slot, as
|
||||||
|
66×72px buttons (weekday name over the day number), horizontally scrollable; the first is
|
||||||
|
pre-selected. Selected day: forest fill, cream number. The slot row shows that day's available
|
||||||
|
slots as pill buttons (min-height 44px); selected slot: gold fill. Both use `aria-pressed`.
|
||||||
|
Submit opens the existing confirm step inside the same card (summary of date, time and contact,
|
||||||
|
with Confirm and Back).
|
||||||
|
- **`/booking` page:** the same two-panel forest card, full page; the end panel keeps the month
|
||||||
|
calendar (the existing component, restyled: cream ground, 44px day cells, selected day forest,
|
||||||
|
available days with a gold dot, unavailable days muted), then the fields and confirm step.
|
||||||
|
- **Inputs:** min-height 48px, padding 12px 16px, radius 12px, 1.5px border `rgba(45,54,36,.18)`,
|
||||||
|
white; focus: border `--gold` plus a 4px `rgba(166,137,102,.2)` ring. Labels .85rem, weight 600.
|
||||||
|
Errors: the existing message under the field, in the danger colour.
|
||||||
|
- **`/booking/success`:** a centred cream card on the linen ground with a small level balance, the
|
||||||
|
existing success copy and a ghost button home.
|
||||||
|
|
||||||
|
### About `/about`
|
||||||
|
|
||||||
|
The same system, existing content only (no new copy beyond section labels): hero (eyebrow, h1
|
||||||
|
from `about.title`, the tagline, the identity statement), Vision and Mission (the fabric section
|
||||||
|
pattern; the three mission cards with the olives, hills and static-balance panels), Values (the
|
||||||
|
medallion row), Goals (a numbered docket list: gold display numbers, no chips, no links), Legal
|
||||||
|
services (the docket rows, linking to `/booking`), closing gold CTA to `/booking`.
|
||||||
|
|
||||||
|
### Articles `/posts` and `/posts/{post}`
|
||||||
|
|
||||||
|
- **Index:** page head (article label, h2 "Articles"), the existing search field restyled as a
|
||||||
|
pill input, the text-only post cards in a three-column grid (2 ≤1100px, 1 ≤760px), the existing
|
||||||
|
pagination restyled (pill buttons, current page forest).
|
||||||
|
- **Show:** a reading column max-width 720px; date in `--gold-ink`; title in the display font
|
||||||
|
`clamp(2rem, 3.4vw, 3rem)`; body in the existing prose styles with headings in the display font
|
||||||
|
and links in `--olive`; a "Back to articles" ghost button.
|
||||||
|
|
||||||
|
### Legal pages `/page/{slug}`
|
||||||
|
|
||||||
|
Reading column max-width 720px, display-font title, existing prose body.
|
||||||
|
|
||||||
|
### Auth pages (login, forgot/reset password, confirm password, verify email)
|
||||||
|
|
||||||
|
Type and controls only: the auth layouts load the two new families and apply the input, focus and
|
||||||
|
gold-button styles above. The split composition (forest panel with the logo, cream form panel) and
|
||||||
|
the existing auth pattern stay as they are.
|
||||||
|
|
||||||
|
## Signature Moment
|
||||||
|
|
||||||
|
**Element:** an engraved-gold balance (scales of justice) drawn as layered SVG — gradient-filled
|
||||||
|
beam, pillar, olive-leaf finial, base and two pans with a drop shadow — standing over the
|
||||||
|
olive-tree photograph in the hero frame. A second instance (the "finale", 190px) sits in the
|
||||||
|
booking card; a static instance fills the "For our institution" card.
|
||||||
|
**Built from:** the firm's own name — Libra, the balance — and the olive tree of the region; the
|
||||||
|
photograph is real.
|
||||||
|
**Trigger:** page load; pointer movement over the hero frame (fine pointers only); scroll position
|
||||||
|
for the finale.
|
||||||
|
**Mapping:**
|
||||||
|
- Load: the hero beam starts tilted 14° and, after 450ms, a damped spring settles it to 0°
|
||||||
|
(per frame: `v += (target − a) × 0.028; v ×= 0.935; a += v`; stop when |v| < 0.002 and
|
||||||
|
|target − a| < 0.01).
|
||||||
|
- Pointer: x = (clientX − frame.left) / frame.width; target = clamp((x − 0.5) × 2.4, −1, 1) × 9°.
|
||||||
|
The gold glow follows the pointer; the photo pans opposite the pointer by
|
||||||
|
`(pointer − centre) / −40` px at scale 1.08. Pointer leave → target 0°, photo resets.
|
||||||
|
- Pans hang from the beam ends: for beam angle θ, pan centres are at (200 ∓ 142·cos θ,
|
||||||
|
110 ∓ 142·sin θ) in the 400×400 viewBox, so they stay vertical while the beam tips.
|
||||||
|
- Finale: with p the page progress, q = clamp((p − 0.7) / 0.25, 0, 1); target = −12° × (1 − q), so
|
||||||
|
the balance comes level as the reader reaches the booking card.
|
||||||
|
**Tech suggestion:** an inline SVG Blade component and a small vanilla JS module
|
||||||
|
(`requestAnimationFrame`); no library.
|
||||||
|
**Reduced-motion fallback:** all balances render level (0°), no spring, no pointer response, no
|
||||||
|
glow movement; the photo stays still.
|
||||||
|
|
||||||
|
## Interaction Patterns (secondary)
|
||||||
|
|
||||||
|
- **Entrance choreography:** elements marked `data-reveal` start at opacity 0 and translateY 28px
|
||||||
|
and transition (.9s, `--ease`) to rest when 8% inside the viewport (IntersectionObserver,
|
||||||
|
`rootMargin: 0px 0px -8% 0px`, observe once). Delays by `data-d` 1–5: .08s steps. Content stays
|
||||||
|
visible if JavaScript fails (the hidden state applies only once the script adds a `js` class).
|
||||||
|
- **Hover depth:** cards lift 6px with `--shadow-2` and a gold border `rgba(166,137,102,.55)`;
|
||||||
|
card images scale 1.05–1.06 over .8s.
|
||||||
|
- **Language toggle:** the existing `/language/{locale}` route; a 48×44px pill showing "EN" in
|
||||||
|
Arabic and "ع" in English.
|
||||||
|
- **Reduced motion:** `@media (prefers-reduced-motion: reduce)` disables every animation and
|
||||||
|
transition on the public pages and shows all `data-reveal` content at rest.
|
||||||
|
- **Touch targets:** every interactive element at least 44×44px.
|
||||||
|
- **RTL/LTR:** logical properties throughout; arrows and the progress rule's origin mirror.
|
||||||
|
|
||||||
|
## Design Decisions & Rationale
|
||||||
|
|
||||||
|
- **Markazi Text + IBM Plex Sans Arabic** — Amiri's calligraphic ligatures broke in large
|
||||||
|
headlines; Markazi is an editorial naskh with a matching Latin serif (brand.md asks for a serif
|
||||||
|
headline); Plex Arabic is highly legible at body size. Two families total.
|
||||||
|
- **Balance over olive photography, no drawn tatreez** — Naser rejected the cross-stitch drawings
|
||||||
|
as "tech stuff"; the balance is the firm's name and reads as law at a glance.
|
||||||
|
- **Article numbering** — frames the firm's content in the language of a statute; costs nothing.
|
||||||
|
- **Text-only article cards** — posts have no image field (code-fit check); no images are promised.
|
||||||
|
- **Copy limited to what the code backs** — no availability or response-time claims; booking copy
|
||||||
|
is drawn from the existing lang strings.
|
||||||
|
- **Public-only scope** — the dashboards share global tokens and `.btn` classes; the redesign is
|
||||||
|
scoped to the public layout so no dashboard screen changes.
|
||||||
|
- **Compact booking on the home page** — the client approved a one-screen form; it reuses the same
|
||||||
|
component and rules, so the backend does not change.
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
None blocking. Rulings taken for the build (ledger, 2026-10-31):
|
||||||
|
|
||||||
|
1. **Photography** — the mockup's four Unsplash photos (olive tree, embroidery hoop, olives, hills)
|
||||||
|
ship as the production images; the Unsplash licence allows commercial use without attribution.
|
||||||
|
The client may replace them later with their own.
|
||||||
|
2. **Home day strip** — the next 14 calendar days with an available slot; `/booking` keeps the
|
||||||
|
month calendar.
|
||||||
|
3. **Pages the mockup does not show** (about, articles, booking page, success, legal) — composed
|
||||||
|
from the homepage's components with their existing content; no new sections.
|
||||||
|
4. **Auth pages** — type and controls only; composition unchanged.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Part B — Dashboards, emails and the Nablus form (pre-redesign record)
|
||||||
|
|
||||||
|
|
||||||
Retrospective documentation of the design system as it is currently **built and live**. This
|
Retrospective documentation of the design system as it is currently **built and live**. This
|
||||||
is not a proposal — it describes what shipped, so a dev/QA agent picking up a new task can match
|
is not a proposal — it describes what shipped, so a dev/QA agent picking up a new task can match
|
||||||
|
|||||||
Reference in New Issue
Block a user