--- id: 24-1 title: 24-1 Public Design Foundation (Stylesheet, Script, Fonts, Layout) status: Done 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 ``` 6. **`lang/ar/site.php`** and **`lang/en/site.php`** — each `` becomes `
` (remove `bg-background` and the inline `style="font-family: …"`). - Immediately after the skip link add ``. - Remove the spacer `` (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). - `