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:
Naser Mansour
2026-10-31 18:23:02 +02:00
parent caec396fa4
commit 3d1fe40be6
17 changed files with 1761 additions and 5 deletions
+147
View File
@@ -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.
+63
View File
@@ -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.
+146
View File
@@ -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.
+81
View File
@@ -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.
+196
View File
@@ -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.
+145
View File
@@ -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.
+90
View File
@@ -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.
+85
View File
@@ -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.
+91
View File
@@ -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.
+67
View File
@@ -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.
+35
View File
@@ -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
+143
View File
@@ -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 |
+12
View File
@@ -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 — 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-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 |
|----|-------|--------|
@@ -135,3 +136,14 @@
| 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-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 |