Merge approved Anwarpy logo kit and site icon update
Deploy GitHub Pages / deploy (push) Canceled after 0s

This commit was merged in pull request #8.
This commit is contained in:
2026-10-20 15:41:45 +03:00
43 changed files with 178 additions and 13 deletions
+52
View File
@@ -0,0 +1,52 @@
# أنور پآي — Logo Guidelines (compact)
## 1. The logo
- **Idea:** an A built from a triangle lattice, where the gold piece is the next one being placed. It shows structure being built one step at a time, the way a model generates.
- **Versions:** horizontal lockup (primary) · stacked · symbol · wordmark only · small-size symbol
- **Light backgrounds:** `*-light.svg`. **Dark backgrounds:** `*-dark.svg`. **One colour:** `*-black.svg` / `*-white.svg`.
- Arabic reads right to left, so in the horizontal lockup the symbol sits on the **right**, at the start of the line.
## 2. Clear space
Keep **one lattice-triangle height** (⅓ of the symbol's height) clear on every side. The zone scales with the logo.
## 3. Minimum size and which symbol to use
| Version | Screen | Print |
|---|---|---|
| Horizontal lockup | 120 px wide | 30 mm wide |
| Stacked lockup | 72 px wide | 18 mm wide |
| Symbol, full lattice (`anwarpy-symbol-*`) | **40 px and up** | 10 mm |
| Symbol, small-size cut (`anwarpy-symbol-small*`, favicons) | **16–32 px** | 4–9 mm |
Below about 40 px the gaps between triangles close up, so switch to the small-size cut there.
## 4. Colour
| Name | Use | HEX | RGB | CMYK (approx.) |
|---|---|---|---|---|
| Basalt ink | Pieces and wordmark on light | `#131519` | 19 21 25 | 24 16 0 90 |
| Gold (light) | The gold piece on light | `#b08a45` | 176 138 69 | 0 22 61 31 |
| Gold (dark) | The gold piece on dark | `#d4b47c` | 212 180 124 | 0 15 42 17 |
| Paper | Pieces and wordmark on dark | `#f2f1ee` | 242 241 238 | 0 0 2 5 |
- Contrast: ink on white 18.3:1 · gold on white 3.2:1 (passes the 3:1 minimum for graphics) · dark gold on `#0c0d0f` 9.8:1.
- **Gold is used for exactly one piece.** Never colour the wordmark, a second triangle, or a background in gold behind the mark.
- The CMYK values are naive conversions. Ask the printer for a proof or a Pantone match before printing.
- The reversed (dark) files use slightly wider gaps (4.6 vs 4.0 units) so the light pieces don't look swollen. Always use the dark files on dark backgrounds rather than recolouring the light ones.
**Approved pairs:** full colour on white or paper · dark version on basalt `#0c0d0f`/`#131519` · black on white · white on photos (calm area only).
## 5. Typography
- Wordmark: **IBM Plex Sans Arabic Bold**, outlined to vector paths with HarfBuzz shaping. Don't retype it.
- Licence: SIL Open Font License 1.1 (allows use in a logo).
## 6. Don'ts
Don't stretch, rotate or mirror · don't move, add or recolour pieces · don't bring back the grey tone ramp · don't add glows, gradients, shadows or outlines · don't put the full-colour mark on gold or busy photos · don't use the full lattice below 40 px · don't rearrange the lockup or retype the name.
## 7. Files
- `svg/`: masters (symbol, small cut, horizontal, stacked, wordmark; light/dark/black/white; app icon)
- `png/`: transparent PNG exports
- `web/`: `favicon.ico`, `favicon.svg` (adapts to light/dark tabs), 16/32/48 px PNGs, `apple-touch-icon.png`, `icon-192/512.png`
- `presentation.html` + `slides/`: final board with mockups
## 8. Open items
- No Latin "Anwarpy" lockup yet. Add one if the brand needs to appear in English.
- No trademark search has been done. Run one (trademark databases plus a reverse image search) before wide rollout.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256.00 256.00" role="img" aria-label="أنور پآي"><title>أنور پآي</title><rect width="256" height="256" rx="57.6" fill="#131519"/><path d="M128.00 70.34 L109.50 102.39 L146.50 102.39Z M104.24 111.50 L85.74 143.54 L122.74 143.54Z M109.50 108.46 L146.50 108.46 L128.00 140.50Z M151.76 111.50 L133.26 143.54 L170.26 143.54Z M80.48 152.65 L61.98 184.69 L98.98 184.69Z M85.74 149.61 L122.74 149.61 L104.24 181.66Z M133.26 149.61 L170.26 149.61 L151.76 181.66Z M175.52 152.65 L157.02 184.69 L194.02 184.69Z" fill="#f2f1ee"/><path d="M157.02 108.46 L194.02 108.46 L175.52 140.50Z" fill="#d4b47c"/></svg>

After

Width:  |  Height:  |  Size: 662 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256.00 256.00" role="img" aria-label="أنور پآي"><title>أنور پآي</title><rect width="256" height="256" rx="44" fill="#131519"/><path d="M128.00 52.18 L214.40 201.82 L41.60 201.82Z M156.80 201.82 L99.20 201.82 L128.00 151.94Z" fill="#f2f1ee" fill-rule="evenodd"/><path d="M169.27 109.26 L201.93 109.26 L185.60 137.54Z" fill="#d4b47c"/></svg>

After

Width:  |  Height:  |  Size: 407 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256.00 256.00" role="img" aria-label="أنور پآي"><title>أنور پآي</title><path d="M128.00 40.97 L98.93 91.32 L157.07 91.32Z M92.00 103.32 L62.93 153.68 L121.07 153.68Z M98.93 99.32 L157.07 99.32 L128.00 149.68Z M164.00 103.32 L134.93 153.68 L193.07 153.68Z M56.00 165.68 L26.93 216.03 L85.07 216.03Z M62.93 161.68 L121.07 161.68 L92.00 212.03Z M134.93 161.68 L193.07 161.68 L164.00 212.03Z M200.00 165.68 L170.93 216.03 L229.07 216.03Z" fill="#000000"/><path d="M170.93 99.32 L229.07 99.32 L200.00 149.68Z" fill="#000000"/></svg>

After

Width:  |  Height:  |  Size: 595 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256.00 256.00" role="img" aria-label="أنور پآي"><title>أنور پآي</title><path d="M128.00 42.17 L99.97 90.72 L156.03 90.72Z M92.00 104.52 L63.97 153.08 L120.03 153.08Z M99.97 99.92 L156.03 99.92 L128.00 148.48Z M164.00 104.52 L135.97 153.08 L192.03 153.08Z M56.00 166.88 L27.97 215.43 L84.03 215.43Z M63.97 162.28 L120.03 162.28 L92.00 210.83Z M135.97 162.28 L192.03 162.28 L164.00 210.83Z M200.00 166.88 L171.97 215.43 L228.03 215.43Z" fill="#f2f1ee"/><path d="M171.97 99.92 L228.03 99.92 L200.00 148.48Z" fill="#d4b47c"/></svg>

After

Width:  |  Height:  |  Size: 595 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256.00 256.00" role="img" aria-label="أنور پآي"><title>أنور پآي</title><path d="M128.00 40.97 L98.93 91.32 L157.07 91.32Z M92.00 103.32 L62.93 153.68 L121.07 153.68Z M98.93 99.32 L157.07 99.32 L128.00 149.68Z M164.00 103.32 L134.93 153.68 L193.07 153.68Z M56.00 165.68 L26.93 216.03 L85.07 216.03Z M62.93 161.68 L121.07 161.68 L92.00 212.03Z M134.93 161.68 L193.07 161.68 L164.00 212.03Z M200.00 165.68 L170.93 216.03 L229.07 216.03Z" fill="#131519"/><path d="M170.93 99.32 L229.07 99.32 L200.00 149.68Z" fill="#b08a45"/></svg>

After

Width:  |  Height:  |  Size: 595 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256.00 256.00" role="img" aria-label="أنور پآي"><title>أنور پآي</title><path d="M128.00 27.86 L242.48 226.14 L13.52 226.14Z M166.16 226.14 L89.84 226.14 L128.00 160.05Z" fill="#f2f1ee" fill-rule="evenodd"/><path d="M182.68 103.49 L225.96 103.49 L204.32 140.97Z" fill="#d4b47c"/></svg>

After

Width:  |  Height:  |  Size: 352 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256.00 256.00" role="img" aria-label="أنور پآي"><title>أنور پآي</title><path d="M128.00 27.86 L242.48 226.14 L13.52 226.14Z M166.16 226.14 L89.84 226.14 L128.00 160.05Z" fill="#131519" fill-rule="evenodd"/><path d="M182.68 103.49 L225.96 103.49 L204.32 140.97Z" fill="#b08a45"/></svg>

After

Width:  |  Height:  |  Size: 352 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256.00 256.00" role="img" aria-label="أنور پآي"><title>أنور پآي</title><path d="M128.00 42.17 L99.97 90.72 L156.03 90.72Z M92.00 104.52 L63.97 153.08 L120.03 153.08Z M99.97 99.92 L156.03 99.92 L128.00 148.48Z M164.00 104.52 L135.97 153.08 L192.03 153.08Z M56.00 166.88 L27.97 215.43 L84.03 215.43Z M63.97 162.28 L120.03 162.28 L92.00 210.83Z M135.97 162.28 L192.03 162.28 L164.00 210.83Z M200.00 166.88 L171.97 215.43 L228.03 215.43Z" fill="#ffffff"/><path d="M171.97 99.92 L228.03 99.92 L200.00 148.48Z" fill="#ffffff"/></svg>

After

Width:  |  Height:  |  Size: 595 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.8 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256.00 256.00" role="img" aria-label="أنور پآي"><title>أنور پآي</title><style>.i{fill:#131519}.g{fill:#b08a45}@media (prefers-color-scheme:dark){.i{fill:#f2f1ee}.g{fill:#d4b47c}}</style><path d="M128.00 27.86 L242.48 226.14 L13.52 226.14Z M166.16 226.14 L89.84 226.14 L128.00 160.05Z" class="i" fill-rule="evenodd"/><path d="M182.68 103.49 L225.96 103.49 L204.32 140.97Z" class="g"/></svg>

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 595 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256.00 256.00" role="img" aria-label="أنور پآي"><title>أنور پآي</title><style>.i{fill:#131519}.g{fill:#b08a45}@media (prefers-color-scheme:dark){.i{fill:#f2f1ee}.g{fill:#d4b47c}}</style><path d="M128.00 27.86 L242.48 226.14 L13.52 226.14Z M166.16 226.14 L89.84 226.14 L128.00 160.05Z" class="i" fill-rule="evenodd"/><path d="M182.68 103.49 L225.96 103.49 L204.32 140.97Z" class="g"/></svg>

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 37 KiB

+1
View File
@@ -0,0 +1 @@
{"name":"أنور پآي","short_name":"أنور پآي","lang":"ar","dir":"rtl","icons":[{"src":"/icon-192.png","sizes":"192x192","type":"image/png"},{"src":"/icon-512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#131519","background_color":"#0c0d0f","display":"standalone"}
+21
View File
@@ -0,0 +1,21 @@
/* Official light lockup for the site's light-only surfaces. Its SVG contains
the approved RTL composition and outlined IBM Plex Sans Arabic wordmark. */
#site img.logo-lockup {
display: block;
width: 152px;
height: 60px;
max-width: none;
object-fit: contain;
flex: none;
transform: none;
filter: none;
background: none;
border-radius: 0;
}
@media (max-width: 640px) {
#site .site-header img.logo-lockup,
#site .menu-head img.logo-lockup {
width: 132px;
height: 52px;
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

+12 -1
View File
@@ -4,6 +4,7 @@ import './globals.css'
import './polish.css' import './polish.css'
import './design.css' import './design.css'
import './color-trial.css' import './color-trial.css'
import './brand.css'
/* Bundle Readex Pro's Arabic/Latin variable font so builds never fetch Google Fonts. */ /* Bundle Readex Pro's Arabic/Latin variable font so builds never fetch Google Fonts. */
const sans = localFont({ const sans = localFont({
@@ -13,13 +14,23 @@ const sans = localFont({
}) })
/* The design is light-only: stop browsers' forced dark modes from repainting it. */ /* The design is light-only: stop browsers' forced dark modes from repainting it. */
export const viewport: Viewport = { colorScheme: 'only light', themeColor: '#ffffff' } export const viewport: Viewport = { colorScheme: 'only light', themeColor: '#131519' }
export const metadata: Metadata = { export const metadata: Metadata = {
metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? 'https://anwarpi.ai'), metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? 'https://anwarpi.ai'),
title: { default: 'أنور پآي — محتوى الذكاء الاصطناعي العملي بالعربية', template: '%s — أنور پآي' }, title: { default: 'أنور پآي — محتوى الذكاء الاصطناعي العملي بالعربية', template: '%s — أنور پآي' },
description: 'محتوى عربي عملي عن الذكاء الاصطناعي: تشغيل النماذج محليًا، وبناء الوكلاء، والأتمتة وRAG. استكشف تجارب أنور پآي وشروحات الأدوات بخطوات قابلة للتطبيق.', description: 'محتوى عربي عملي عن الذكاء الاصطناعي: تشغيل النماذج محليًا، وبناء الوكلاء، والأتمتة وRAG. استكشف تجارب أنور پآي وشروحات الأدوات بخطوات قابلة للتطبيق.',
alternates: { canonical: './' }, alternates: { canonical: './' },
manifest: '/site.webmanifest',
icons: {
icon: [
{ url: '/favicon.ico', sizes: '48x48' },
{ url: '/favicon.svg', type: 'image/svg+xml' },
{ url: '/favicon-32.png', sizes: '32x32', type: 'image/png' },
{ url: '/favicon-16.png', sizes: '16x16', type: 'image/png' },
],
apple: [{ url: '/apple-touch-icon.png', sizes: '180x180' }],
},
openGraph: { openGraph: {
type: 'website', type: 'website',
siteName: 'أنور پآي', siteName: 'أنور پآي',
+13
View File
@@ -0,0 +1,13 @@
/* The approved wordmark is outlined in the supplied SVG; never retype it. */
export default function BrandLogo() {
return (
// eslint-disable-next-line @next/next/no-img-element
<img
className="logo-lockup"
src="/brand/anwarpy-horizontal-light.svg"
alt="أنور پآي"
width={152}
height={60}
/>
)
}
+2 -3
View File
@@ -1,4 +1,5 @@
import Link from 'next/link' import Link from 'next/link'
import BrandLogo from './brand-logo'
/* Footer — brand column + three link columns. */ /* Footer — brand column + three link columns. */
export default function Footer() { export default function Footer() {
@@ -8,9 +9,7 @@ export default function Footer() {
<div className="footer-grid"> <div className="footer-grid">
<div> <div>
<Link className="logo" href="/"> <Link className="logo" href="/">
{/* eslint-disable-next-line @next/next/no-img-element */} <BrandLogo />
<img className="logo-mark" src="/brand/mark-light.svg" alt="" width={36} height={32} />
أنور پآي
</Link> </Link>
<p>محتوى عربي عملي لتعلّم الذكاء الاصطناعي والوكلاء.</p> <p>محتوى عربي عملي لتعلّم الذكاء الاصطناعي والوكلاء.</p>
</div> </div>
+2 -3
View File
@@ -2,6 +2,7 @@
import { useEffect } from 'react' import { useEffect } from 'react'
import Link from 'next/link' import Link from 'next/link'
import BrandLogo from './brand-logo'
import { usePathname } from 'next/navigation' import { usePathname } from 'next/navigation'
import { SITE_LINKS } from './site-links' import { SITE_LINKS } from './site-links'
import AccountLink from './account-link' import AccountLink from './account-link'
@@ -71,9 +72,7 @@ export default function SiteMenu({
<div className="menu-drawer" role="dialog" aria-modal="true" aria-label="قائمة أنور پآي"> <div className="menu-drawer" role="dialog" aria-modal="true" aria-label="قائمة أنور پآي">
<div className="menu-head"> <div className="menu-head">
<Link className="logo" href="/"> <Link className="logo" href="/">
{/* eslint-disable-next-line @next/next/no-img-element */} <BrandLogo />
<img className="logo-mark" src="/brand/mark-light.svg" alt="" width={36} height={32} />
أنور پآي
</Link> </Link>
<button className="icon-btn" type="button" aria-label="إغلاق القائمة" data-menu-close>×</button> <button className="icon-btn" type="button" aria-label="إغلاق القائمة" data-menu-close>×</button>
</div> </div>
+2 -3
View File
@@ -1,4 +1,5 @@
import Link from 'next/link' import Link from 'next/link'
import BrandLogo from './brand-logo'
import NavLinks from './nav-links' import NavLinks from './nav-links'
import SiteMenu from './site-menu' import SiteMenu from './site-menu'
import AccountLink from './account-link' import AccountLink from './account-link'
@@ -12,9 +13,7 @@ export default function Topnav({ viewer }: { viewer?: { email: string } | null }
<header className="topnav site-header" data-od-id="topnav"> <header className="topnav site-header" data-od-id="topnav">
<div className="container nav-inner"> <div className="container nav-inner">
<Link className="logo" href="/" data-od-id="brand"> <Link className="logo" href="/" data-od-id="brand">
{/* eslint-disable-next-line @next/next/no-img-element */} <BrandLogo />
<img className="logo-mark" src="/brand/mark-light.svg" alt="" width={36} height={32} />
أنور پآي
</Link> </Link>
<NavLinks links={SITE_LINKS} /> <NavLinks links={SITE_LINKS} />
<div className="nav-tools"> <div className="nav-tools">
+52
View File
@@ -0,0 +1,52 @@
const test = require('node:test')
const assert = require('node:assert/strict')
const fs = require('node:fs')
const path = require('node:path')
const crypto = require('node:crypto')
const root = path.resolve(__dirname, '..')
const read = p => fs.readFileSync(path.join(root, p), 'utf8')
const hash = p => crypto.createHash('sha256').update(fs.readFileSync(path.join(root, p))).digest('hex')
test('all navigation lockups use the approved outlined wordmark component', () => {
for (const file of ['topnav.tsx', 'site-menu.tsx', 'footer.tsx']) {
const source = read(`src/components/${file}`)
assert.match(source, /import BrandLogo from '\.\/brand-logo'/)
assert.match(source, /<BrandLogo \/>/)
assert.doesNotMatch(source, /className="logo-mark"/)
}
const logo = read('src/components/brand-logo.tsx')
assert.match(logo, /alt="أنور پآي"/)
assert.match(logo, /src="\/brand\/anwarpy-horizontal-light\.svg"/)
assert.match(logo, /width=\{152\}/)
})
test('SVG logo masters contain outlined artwork, not scripts or external fonts', () => {
for (const name of ['anwarpy-horizontal-light.svg', 'anwarpy-horizontal-dark.svg']) {
const svg = read(`public/brand/${name}`)
assert.match(svg, /<path/)
assert.doesNotMatch(svg, /<script|<foreignObject|<text\b|https?:\/\/[^" ]+\.(?:js|css|woff)/i)
}
assert.match(read('public/brand/anwarpy-horizontal-light.svg'), /#b08a45/)
assert.match(read('public/brand/anwarpy-horizontal-dark.svg'), /#d4b47c/)
})
test('manifest and all raster icon sizes are internally consistent', () => {
const manifest = JSON.parse(read('public/site.webmanifest'))
assert.equal(manifest.name, 'أنور پآي')
assert.equal(manifest.dir, 'rtl')
for (const [name, size] of [['favicon-16.png',16],['favicon-32.png',32],['favicon-48.png',48],['apple-touch-icon.png',180],['icon-192.png',192],['icon-512.png',512]]) {
const bytes = fs.readFileSync(path.join(root,'public',name))
assert.equal(bytes.readUInt32BE(16), size, name)
assert.equal(bytes.readUInt32BE(20), size, name)
}
for (const icon of manifest.icons) assert.ok(fs.existsSync(path.join(root,'public',icon.src)))
assert.equal(hash('src/app/icon.png'), hash('public/icon-512.png'))
})
test('browser metadata includes the supplied favicon, touch icon and manifest', () => {
const source = read('src/app/layout.tsx')
for (const url of ['/favicon.ico','/favicon.svg','/favicon-32.png','/favicon-16.png','/apple-touch-icon.png','/site.webmanifest']) assert.ok(source.includes(url), url)
assert.match(source, /themeColor: '#131519'/)
assert.match(read('public/favicon.svg'), /prefers-color-scheme:dark/)
assert.match(read('src/app/brand.css'), /width: 132px/)
})