Commit 3fc863bf authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat: chess piece theming + tournament module + promo assets

- Add chess piece upload UI to admin/branding.php (12 slots: white + black)
- Fix loadPieceImages race condition in board renderer
- Add tournament system module (Swiss, knockout, arena)
- Add promo banner (1m×3m display) and video assets
- Add 2-min display video script
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 40356211
......@@ -259,6 +259,81 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
</div>
<!-- CHESS PIECES -->
<h2>♟ قطع الشطرنج</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">ارفع صور PNG أو SVG لكل قطعة — ستظهر على الرقعة مباشرة بدل الرسم الافتراضي</p>
<div class="section">
<h3 style="color:#f8fafc;margin-bottom:12px;">⬜ القطع البيضاء</h3>
<div class="grid">
<?php
$whitePieces = [
['slot' => 'chess_piece_wK', 'label' => 'ملك أبيض ♔', 'w' => 128, 'h' => 128, 'hint' => 'White King'],
['slot' => 'chess_piece_wQ', 'label' => 'وزير أبيض ♕', 'w' => 128, 'h' => 128, 'hint' => 'White Queen'],
['slot' => 'chess_piece_wR', 'label' => 'قلعة بيضاء ♖', 'w' => 128, 'h' => 128, 'hint' => 'White Rook'],
['slot' => 'chess_piece_wB', 'label' => 'فيل أبيض ♗', 'w' => 128, 'h' => 128, 'hint' => 'White Bishop'],
['slot' => 'chess_piece_wN', 'label' => 'حصان أبيض ♘', 'w' => 128, 'h' => 128, 'hint' => 'White Knight'],
['slot' => 'chess_piece_wP', 'label' => 'بيدق أبيض ♙', 'w' => 128, 'h' => 128, 'hint' => 'White Pawn'],
];
foreach ($whitePieces as $a):
$current = $theme['assets'][$a['slot']] ?? null;
?>
<div class="field">
<form method="POST" enctype="multipart/form-data">
<input type="hidden" name="slot" value="<?= $a['slot'] ?>">
<input type="hidden" name="expected_w" value="<?= $a['w'] ?>">
<input type="hidden" name="expected_h" value="<?= $a['h'] ?>">
<label><?= $a['label'] ?></label>
<div class="upload-box" onclick="this.querySelector('input[type=file]').click()">
<input type="file" name="asset" accept=".svg,.png,.webp" style="display:none" onchange="this.form.submit()">
<?php if ($current): ?>
<div class="current"><img src="<?= $current ?>" style="width:64px;height:64px;object-fit:contain;background:#F0D9B5;border-radius:8px;padding:4px;"></div>
<div style="font-size:10px;color:#34D399;margin-top:4px;">✓ مرفوع</div>
<?php else: ?>
📤 اضغط للرفع
<?php endif; ?>
<div class="size-hint"><?= $a['w'] ?>×<?= $a['h'] ?>px — <?= $a['hint'] ?></div>
</div>
</form>
</div>
<?php endforeach; ?>
</div>
<h3 style="color:#f8fafc;margin:20px 0 12px;">⬛ القطع السوداء</h3>
<div class="grid">
<?php
$blackPieces = [
['slot' => 'chess_piece_bK', 'label' => 'ملك أسود ♚', 'w' => 128, 'h' => 128, 'hint' => 'Black King'],
['slot' => 'chess_piece_bQ', 'label' => 'وزير أسود ♛', 'w' => 128, 'h' => 128, 'hint' => 'Black Queen'],
['slot' => 'chess_piece_bR', 'label' => 'قلعة سوداء ♜', 'w' => 128, 'h' => 128, 'hint' => 'Black Rook'],
['slot' => 'chess_piece_bB', 'label' => 'فيل أسود ♝', 'w' => 128, 'h' => 128, 'hint' => 'Black Bishop'],
['slot' => 'chess_piece_bN', 'label' => 'حصان أسود ♞', 'w' => 128, 'h' => 128, 'hint' => 'Black Knight'],
['slot' => 'chess_piece_bP', 'label' => 'بيدق أسود ♟', 'w' => 128, 'h' => 128, 'hint' => 'Black Pawn'],
];
foreach ($blackPieces as $a):
$current = $theme['assets'][$a['slot']] ?? null;
?>
<div class="field">
<form method="POST" enctype="multipart/form-data">
<input type="hidden" name="slot" value="<?= $a['slot'] ?>">
<input type="hidden" name="expected_w" value="<?= $a['w'] ?>">
<input type="hidden" name="expected_h" value="<?= $a['h'] ?>">
<label><?= $a['label'] ?></label>
<div class="upload-box" onclick="this.querySelector('input[type=file]').click()">
<input type="file" name="asset" accept=".svg,.png,.webp" style="display:none" onchange="this.form.submit()">
<?php if ($current): ?>
<div class="current"><img src="<?= $current ?>" style="width:64px;height:64px;object-fit:contain;background:#B58863;border-radius:8px;padding:4px;"></div>
<div style="font-size:10px;color:#34D399;margin-top:4px;">✓ مرفوع</div>
<?php else: ?>
📤 اضغط للرفع
<?php endif; ?>
<div class="size-hint"><?= $a['w'] ?>×<?= $a['h'] ?>px — <?= $a['hint'] ?></div>
</div>
</form>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- LUDO BOARD -->
<h2>🎲 Ludo Board Colors</h2>
<div class="section">
......
# سكريبت عرض — العب | دقيقتين | بدون صوت | 6m x 4m (3:2 landscape)
> **Format:** Text + visuals only. No voiceover. Designed for large wall display.
> **Aspect Ratio:** 3:2 landscape (6 meters wide x 4 meters tall)
> **Duration:** 120 seconds
> **Style:** Game trailer — kinetic Arabic typography, hard cuts, bass thuds, glitch flashes
---
## BEAT 1 — BLACK (0:00 – 0:05)
**[VISUAL]:** أسود تام. فلاش أبيض سريع (0.1s). تظهر 3 خطوط رفيعة بالتتابع:
```
🎲 ━━━━━━━━━━━━━━━━━━
♟ ━━━━━━━━━━━━━━━━━━
🁣 ━━━━━━━━━━━━━━━━━━
```
الخطوط تخفت بعد ثانيتين. Bass thud خفيف.
---
## BEAT 2 — HOOK (0:05 – 0:15)
**[VISUAL]:** فلاش أبيض →
**TEXT SLAM (ملء الشاشة):**
> في ناس شايفة إن الألعاب الذهنية
> ~~حاجة قديمة.~~
*يفضل 3 ثواني. فلاش →*
**TEXT SLAM:**
> الجديد؟
*ثانية واحدة. فلاش →*
**TEXT SLAM:**
> إنك تحطها في إيد
> **كل طفل في مصر.** ← (ذهبي)
---
## BEAT 3 — FIRST LOOK (0:15 – 0:25)
**[VISUAL]:** فلاش → Screenshot لوحة الشطرنج (phone frame) يظهر من blur+scale كبير → يثبت في النص. يفضل 3 ثواني.
فلاش ذهبي →
**TEXT SLAM (ضخم جداً):**
> **ده العب.** ← (ذهبي، 14vh)
---
## BEAT 4 — THE THREE GAMES (0:25 – 0:40)
**[VISUAL]:** فلاش → 3 phones تطلع من تحت بالتتابع (0.1s delay بينهم):
| يسار | وسط | يمين |
|------|------|------|
| شطرنج (midgame) | لودو (board) | دومينو (menu) |
تحتهم بعد ثانية:
> شطرنج • لودو • دومينو
*تفضل 4 ثواني. فلاش →*
**TEXT SLAM:**
> من أول ما تفتح — لحد ما تلعب
> **ثواني.** ← (سيان)
*فلاش →*
**TEXT SLAM (3 أسطر):**
> مفيش تسجيل معقد.
> مفيش إعلان بيقطعك.
> **60 فريم. سلس.** ← (ذهبي)
---
## BEAT 5 — NOT JUST A GAME (0:40 – 0:55)
**[VISUAL]:** فلاش →
**TEXT SLAM (كبير):**
> مش لعبة ~~وخلاص.~~
*فلاش → Screenshot شطرنج midgame (phone frame) →*
**TEXT SLAM:**
> تعليم شطرنج مدمج.
> ألغاز يومية. تصنيف.
> **الطفل بيلعب وهو بيتعلم.** ← (ذهبي)
---
## BEAT 6 — TOURNAMENTS (0:55 – 1:20)
**[VISUAL]:** فلاش ذهبي →
**TEXT SLAM (ضخم):**
> **بطولات.** ← (ذهبي، 14vh)
*فلاش → Bracket animation: نقاط ذهبية تظهر واحدة واحدة، خطوط تتمد بينهم (tournament bracket visual). فوقه:*
**TEXT SLAM:**
> مدارس • جامعات • أندية
> شركات • مراكز شباب • أحياء
*5 ثواني. فلاش →*
**TEXT SLAM:**
> Swiss • Knockout • Arena
> **كل الأنظمة جاهزة.** ← (ذهبي)
*فلاش →*
**TEXT SLAM (4 أسطر):**
> القرعة أوتوماتيك.
> الجدول أوتوماتيك.
> النتايج أوتوماتيك.
> **البنية التحتية شغالة.** ← (سيان)
---
## BEAT 7 — SAFETY (1:20 – 1:30)
**[VISUAL]:** فلاش → 🛡️ (ضخم، يظهر بـspin+scale). تحته:
**TEXT SLAM:**
> **مفيش chat مفتوح.** ← (أخضر)
> عبارات جاهزة. نظام إبلاغ.
> **بيئة نضيفة. الأهل يطمنوا.** ← (أخضر)
---
## BEAT 8 — THE NUMBERS (1:30 – 1:45)
**[VISUAL]:** فلاش → 3 أرقام تظهر بالتتابع (stat counters):
| 4 | 38 | 100% |
|---|---|---|
| ألعاب ذهنية | module إدارة | مصري |
*3 ثواني. فلاش ذهبي →*
**NUMBER SLAM (ضخم جداً، 20vh):**
> **400M**
تحته:
> المنطقة العربية.
> **مفيهاش منافس محلي.** ← (ذهبي)
---
## BEAT 9 — THE PUNCH (1:45 – 1:52)
**[VISUAL]:** فلاش →
**TEXT SLAM (كبير):**
> السوق مفتوح.
> **والمنتج جاهز.** ← (سيان)
---
## BEAT 10 — CLOSER (1:52 – 2:00)
**[VISUAL]:** فلاش ذهبي → أسود. اللوجو (logof.png) يظهر من scale صغير → يكبر ببطء. Drop shadow ذهبي ضخم. بعد ثانيتين:
**TEXT (هادي، مش slam):**
> الأطفال بتوعنا يستاهلوا أحسن من كده.
**TEXT (ذهبي):**
> وده — أحسن من كده.
*الجزيئات الذهبية بتطلع ببطء. اللوجو يفضل 5 ثواني. Fade to black.*
---
## ملاحظات إنتاج
| | |
|---|---|
| **Aspect Ratio** | 3:2 landscape (6000 x 4000px render, scaled to display) |
| **Tempo** | Cut كل 3-6 ثواني. مفيش مشهد يفضل أكتر من 10 ثواني |
| **Typography** | IBM Plex Sans Arabic — weights 700-900 للـslams |
| **Colors** | Black bg, white text, gold (#E4AC38) accent, cyan (#00FFFF) secondary, green (#34D399) safety |
| **Transitions** | White flash (0.12s) on every cut. Gold flash for emphasis moments |
| **Audio** | Sub-bass drone (45Hz) + bass thud on every flash/cut. No VO. No music bed needed — the thuds ARE the rhythm |
| **Rhythm** | ~20 cuts total. كل cut عليه thud. ده بيخلي الفيديو عنده heartbeat |
| **Text Rules** | Max 3 lines per slam. One idea per screen. Gold = emphasis. Dim/strikethrough = dismissal |
| **Screenshots** | Phone frames (rounded corners, subtle glow, dark border) — never flat rectangles |
| **Particles** | Gold + cyan particles floating up slowly throughout. Subtle. Depth. |
{
"name": "el3ab-player",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"devDependencies": {
"puppeteer": "^24.43.1"
}
},
"node_modules/@babel/code-frame": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.29.7",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@puppeteer/browsers": {
"version": "2.13.2",
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.2.tgz",
"integrity": "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"debug": "^4.4.3",
"extract-zip": "^2.0.1",
"progress": "^2.0.3",
"proxy-agent": "^6.5.0",
"semver": "^7.7.4",
"tar-fs": "^3.1.1",
"yargs": "^17.7.2"
},
"bin": {
"browsers": "lib/cjs/main-cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@tootallnate/quickjs-emscripten": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"undici-types": ">=7.24.0 <7.24.7"
}
},
"node_modules/@types/yauzl": {
"version": "2.10.3",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
"integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/ast-types": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
"integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
"dev": true,
"license": "MIT",
"dependencies": {
"tslib": "^2.0.1"
},
"engines": {
"node": ">=4"
}
},
"node_modules/b4a": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
"integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==",
"dev": true,
"license": "Apache-2.0",
"peerDependencies": {
"react-native-b4a": "*"
},
"peerDependenciesMeta": {
"react-native-b4a": {
"optional": true
}
}
},
"node_modules/bare-events": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz",
"integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==",
"dev": true,
"license": "Apache-2.0",
"peerDependencies": {
"bare-abort-controller": "*"
},
"peerDependenciesMeta": {
"bare-abort-controller": {
"optional": true
}
}
},
"node_modules/bare-fs": {
"version": "4.7.2",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.2.tgz",
"integrity": "sha512-aTvMFUWkBmjzKtEQMDGGDNF8bkfpD5N1b/FCwt7A3wrU4t1o/e/85Wzkluh6JlODCjqVESYCkQCdTXqZ9G7VFg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"bare-events": "^2.5.4",
"bare-path": "^3.0.0",
"bare-stream": "^2.6.4",
"bare-url": "^2.2.2",
"fast-fifo": "^1.3.2"
},
"engines": {
"bare": ">=1.16.0"
},
"peerDependencies": {
"bare-buffer": "*"
},
"peerDependenciesMeta": {
"bare-buffer": {
"optional": true
}
}
},
"node_modules/bare-os": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.1.tgz",
"integrity": "sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"bare": ">=1.14.0"
}
},
"node_modules/bare-path": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.1.tgz",
"integrity": "sha512-ghj2DSK/2e99a1anTVPCV4m4YIYtrbXhfM7V3D7XZLOTsybnYyaJloymGqssQc8l/or0UoDyRtNQkmkEF/ysgQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"bare-os": "^3.0.1"
}
},
"node_modules/bare-stream": {
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.1.tgz",
"integrity": "sha512-Vp0cnjYyrEC4whYTymQ+YZi6pBpfiICZO3cfRG8sy67ZNWe951urv1x4eW1BKNngw3U+3fPYb5JQvHbCtxH7Ow==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"streamx": "^2.25.0",
"teex": "^1.0.1"
},
"peerDependencies": {
"bare-abort-controller": "*",
"bare-buffer": "*",
"bare-events": "*"
},
"peerDependenciesMeta": {
"bare-abort-controller": {
"optional": true
},
"bare-buffer": {
"optional": true
},
"bare-events": {
"optional": true
}
}
},
"node_modules/bare-url": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz",
"integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"bare-path": "^3.0.0"
}
},
"node_modules/basic-ftp": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz",
"integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/chromium-bidi": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz",
"integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"mitt": "^3.0.1",
"zod": "^3.24.1"
},
"peerDependencies": {
"devtools-protocol": "*"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/cosmiconfig": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz",
"integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"env-paths": "^2.2.1",
"import-fresh": "^3.3.0",
"js-yaml": "^4.1.0",
"parse-json": "^5.2.0"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
},
"peerDependencies": {
"typescript": ">=4.9.5"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/data-uri-to-buffer": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
"integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/degenerator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
"integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ast-types": "^0.13.4",
"escodegen": "^2.1.0",
"esprima": "^4.0.1"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/devtools-protocol": {
"version": "0.0.1608973",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1608973.tgz",
"integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==",
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"dev": true,
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/env-paths": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/error-ex": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
"integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-arrayish": "^0.2.1"
}
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/escodegen": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
"integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"esprima": "^4.0.1",
"estraverse": "^5.2.0",
"esutils": "^2.0.2"
},
"bin": {
"escodegen": "bin/escodegen.js",
"esgenerate": "bin/esgenerate.js"
},
"engines": {
"node": ">=6.0"
},
"optionalDependencies": {
"source-map": "~0.6.1"
}
},
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true,
"license": "BSD-2-Clause",
"bin": {
"esparse": "bin/esparse.js",
"esvalidate": "bin/esvalidate.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
}
},
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/events-universal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
"integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"bare-events": "^2.7.0"
}
},
"node_modules/extract-zip": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"debug": "^4.1.1",
"get-stream": "^5.1.0",
"yauzl": "^2.10.0"
},
"bin": {
"extract-zip": "cli.js"
},
"engines": {
"node": ">= 10.17.0"
},
"optionalDependencies": {
"@types/yauzl": "^2.9.1"
}
},
"node_modules/fast-fifo": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
"dev": true,
"license": "MIT"
},
"node_modules/fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"pend": "~1.2.0"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dev": true,
"license": "MIT",
"dependencies": {
"pump": "^3.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-uri": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
"integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
"dev": true,
"license": "MIT",
"dependencies": {
"basic-ftp": "^5.0.2",
"data-uri-to-buffer": "^6.0.2",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ip-address": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
"integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
"dev": true,
"license": "MIT"
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"dev": true,
"license": "MIT"
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true,
"license": "MIT"
},
"node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"dev": true,
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
"node_modules/netmask": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz",
"integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/pac-proxy-agent": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz",
"integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@tootallnate/quickjs-emscripten": "^0.23.0",
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"get-uri": "^6.0.1",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.6",
"pac-resolver": "^7.0.1",
"socks-proxy-agent": "^8.0.5"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/pac-resolver": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
"integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
"dev": true,
"license": "MIT",
"dependencies": {
"degenerator": "^5.0.0",
"netmask": "^2.0.2"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
"json-parse-even-better-errors": "^2.3.0",
"lines-and-columns": "^1.1.6"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
"dev": true,
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/proxy-agent": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz",
"integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"http-proxy-agent": "^7.0.1",
"https-proxy-agent": "^7.0.6",
"lru-cache": "^7.14.1",
"pac-proxy-agent": "^7.1.0",
"proxy-from-env": "^1.1.0",
"socks-proxy-agent": "^8.0.5"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"dev": true,
"license": "MIT"
},
"node_modules/pump": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
"dev": true,
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/puppeteer": {
"version": "24.43.1",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.43.1.tgz",
"integrity": "sha512-/FSOViCrqRdb1HDocpsM9Z1giA71gTQPUt3SpHGVRALKAy/rJr1fLFYZW9F23qPxqVxTHQnbh/5B5opJST3kAw==",
"dev": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@puppeteer/browsers": "2.13.2",
"chromium-bidi": "14.0.0",
"cosmiconfig": "^9.0.0",
"devtools-protocol": "0.0.1608973",
"puppeteer-core": "24.43.1",
"typed-query-selector": "^2.12.2"
},
"bin": {
"puppeteer": "lib/cjs/puppeteer/node/cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/puppeteer-core": {
"version": "24.43.1",
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.43.1.tgz",
"integrity": "sha512-T5ScUMAsmhdNbgDR41AGESYeS6V9MSgetkSnVhhW+gXvzC42VesKCn5ld87gAZDJ6vLHL9GkRvY9WtQWSnwFbw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@puppeteer/browsers": "2.13.2",
"chromium-bidi": "14.0.0",
"debug": "^4.4.3",
"devtools-protocol": "0.0.1608973",
"typed-query-selector": "^2.12.2",
"webdriver-bidi-protocol": "0.4.1",
"ws": "^8.20.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/semver": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
"integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz",
"integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==",
"dev": true,
"license": "MIT",
"dependencies": {
"ip-address": "^10.1.1",
"smart-buffer": "^4.2.0"
},
"engines": {
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks-proxy-agent": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
"integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"socks": "^2.8.3"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/streamx": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.27.0.tgz",
"integrity": "sha512-WZ189TKnHoAokYHvwzaAQMpd55cgUmFIcJFzBSgGcb886jau5DL+XdDhTWV4ps3FLvk+OORp0dLRTPsLZ21CSA==",
"dev": true,
"license": "MIT",
"dependencies": {
"events-universal": "^1.0.0",
"fast-fifo": "^1.3.2",
"text-decoder": "^1.1.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/tar-fs": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz",
"integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==",
"dev": true,
"license": "MIT",
"dependencies": {
"pump": "^3.0.0",
"tar-stream": "^3.1.5"
},
"optionalDependencies": {
"bare-fs": "^4.0.1",
"bare-path": "^3.0.0"
}
},
"node_modules/tar-stream": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz",
"integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==",
"dev": true,
"license": "MIT",
"dependencies": {
"b4a": "^1.6.4",
"bare-fs": "^4.5.5",
"fast-fifo": "^1.2.0",
"streamx": "^2.15.0"
}
},
"node_modules/teex": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
"integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"streamx": "^2.12.5"
}
},
"node_modules/text-decoder": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
"integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"b4a": "^1.6.4"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/typed-query-selector": {
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz",
"integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==",
"dev": true,
"license": "MIT"
},
"node_modules/undici-types": {
"version": "7.24.6",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/webdriver-bidi-protocol": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz",
"integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true,
"license": "ISC"
},
"node_modules/ws": {
"version": "8.21.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/yauzl": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
}
},
"node_modules/zod": {
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}
{
"devDependencies": {
"puppeteer": "^24.43.1"
}
}
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EL3AB — Promo Display A</title>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 100vw; height: 100vh;
overflow: hidden;
font-family: 'IBM Plex Sans Arabic', sans-serif;
background: #000; color: #F8FAFC;
display: flex; align-items: center; justify-content: center;
}
.stage {
width: min(100vw, calc(100vh / 3));
height: min(100vh, calc(100vw * 3));
position: relative; overflow: hidden;
background: #030508;
}
/* BG */
.bg {
position: absolute; inset: 0;
background:
radial-gradient(ellipse 120% 25% at 50% 0%, rgba(228,172,56,0.05) 0%, transparent 50%),
radial-gradient(ellipse 100% 20% at 50% 100%, rgba(32,130,240,0.03) 0%, transparent 50%);
}
.grid-bg {
position: absolute; inset: 0; opacity: 0.015;
background-image:
linear-gradient(rgba(255,255,255,0.4) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.4) 1px, transparent 1px);
background-size: 10% 3.33%;
animation: gridm 25s linear infinite;
}
@keyframes gridm { 0%{transform:translateY(0);} 100%{transform:translateY(3.33%);} }
/* Particles */
.ptcls { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.pt {
position: absolute; border-radius: 50%;
animation: ptup linear infinite;
}
@keyframes ptup {
0%{transform:translateY(0) scale(1);opacity:0;}
10%{opacity:0.7;} 90%{opacity:0.2;}
100%{transform:translateY(-100vh) scale(0.2);opacity:0;}
}
/* Content */
.content {
position: relative; z-index: 10;
width: 100%; height: 100%;
display: flex; flex-direction: column;
align-items: center; justify-content: space-between;
padding: 4% 7%;
}
/* TOP */
.top {
display: flex; flex-direction: column;
align-items: center; gap: 2cqi;
padding-top: 2%;
}
.logo {
width: 28cqi; height: 28cqi;
object-fit: contain;
filter: drop-shadow(0 0 25px rgba(228,172,56,0.4));
animation: lbr 3.5s ease-in-out infinite;
}
@keyframes lbr { 0%,100%{transform:scale(1);} 50%{transform:scale(1.03);} }
.brand {
font-size: 12cqi; font-weight: 900;
background: linear-gradient(180deg, #FFF 20%, #E4AC38 100%);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text;
}
.tagline {
font-size: 3.5cqi; color: #94A3B8;
font-weight: 300; letter-spacing: 0.15em;
}
/* MIDDLE */
.mid {
display: flex; flex-direction: column;
align-items: center; gap: 4%;
flex: 1; justify-content: center;
width: 100%;
}
.phone-hero {
position: relative;
width: 60%;
aspect-ratio: 9/19.5;
border-radius: 5%;
overflow: hidden;
border: 2px solid rgba(255,255,255,0.06);
box-shadow: 0 30px 80px rgba(0,0,0,0.7), 0 0 50px rgba(32,130,240,0.08);
animation: phf 5s ease-in-out infinite;
}
@keyframes phf { 0%,100%{transform:translateY(0);} 50%{transform:translateY(-1%);} }
.phone-hero img { width: 100%; height: 100%; object-fit: cover; }
/* Phone glow ring */
.phone-glow {
position: absolute; inset: -3px;
border-radius: 5%; z-index: -1;
background: conic-gradient(from 0deg, rgba(228,172,56,0.3), rgba(32,130,240,0.2), rgba(0,255,255,0.2), rgba(228,172,56,0.3));
filter: blur(8px);
animation: glow-spin 8s linear infinite;
}
@keyframes glow-spin { 0%{transform:rotate(0);} 100%{transform:rotate(360deg);} }
.features {
display: flex; flex-direction: column;
align-items: center; gap: 2cqi;
}
.feat {
display: flex; align-items: center; gap: 2cqi;
font-size: 3cqi; color: #94A3B8;
}
.feat-dot {
width: 6px; height: 6px; border-radius: 50%;
background: #E4AC38; flex-shrink: 0;
}
/* BOTTOM */
.bot {
display: flex; flex-direction: column;
align-items: center; gap: 3%;
padding-bottom: 3%;
width: 100%;
}
.cta-line {
font-size: 5cqi; font-weight: 800;
text-align: center;
}
.cta-line .gold { color: #E4AC38; }
.qr-box {
position: relative;
padding: 3%;
background: #fff;
border-radius: 6%;
box-shadow: 0 0 40px rgba(228,172,56,0.2), 0 15px 40px rgba(0,0,0,0.5);
animation: qrp 2.5s ease-in-out infinite;
}
@keyframes qrp {
0%,100%{box-shadow: 0 0 40px rgba(228,172,56,0.2), 0 15px 40px rgba(0,0,0,0.5);}
50%{box-shadow: 0 0 60px rgba(228,172,56,0.4), 0 15px 40px rgba(0,0,0,0.5);}
}
.qr-box img { width: 22cqi; height: 22cqi; display: block; }
.url {
font-size: 2.5cqi; color: #2082F0;
direction: ltr; font-family: monospace;
}
.egypt {
display: flex; align-items: center; gap: 1.5cqi;
font-size: 2.5cqi; color: #64748B;
padding: 1.5% 4%;
border-radius: 999px;
background: rgba(255,255,255,0.02);
border: 1px solid rgba(255,255,255,0.05);
}
.eg-flag {
width: 3cqi; height: 2cqi;
border-radius: 2px;
display: flex; flex-direction: column; overflow: hidden;
}
.eg-flag span { flex: 1; }
.eg-flag span:nth-child(1) { background: #CE1126; }
.eg-flag span:nth-child(2) { background: #FFF; }
.eg-flag span:nth-child(3) { background: #000; }
</style>
</head>
<body>
<div class="stage">
<div class="bg"></div>
<div class="grid-bg"></div>
<div class="ptcls" id="ptcls"></div>
<div class="content">
<div class="top">
<img class="logo" src="video/assets/logof.png" alt="EL3AB">
<div class="brand">العب</div>
<div class="tagline">منصة الألعاب الذهنية المصرية</div>
</div>
<div class="mid">
<div class="phone-hero">
<div class="phone-glow"></div>
<img src="screenshots/02-home.png" alt="EL3AB">
</div>
<div class="features">
<div class="feat"><span class="feat-dot"></span>بطولات مدارس وجامعات وأندية</div>
<div class="feat"><span class="feat-dot"></span>تعليم شطرنج تفاعلي</div>
<div class="feat"><span class="feat-dot"></span>بيئة آمنة 100% للأطفال</div>
<div class="feat"><span class="feat-dot"></span>مجاني بالكامل</div>
</div>
</div>
<div class="bot">
<div class="cta-line">امسح <span class="gold">وابدأ العب</span></div>
<div class="qr-box">
<img src="video/assets/qr-code.png" alt="QR">
</div>
<div class="url">el3ab-player.caprover.al-arcade.com</div>
<div class="egypt">
<div class="eg-flag"><span></span><span></span><span></span></div>
صنع في مصر
</div>
</div>
</div>
</div>
<script>
const c = document.getElementById('ptcls');
const cols = ['rgba(228,172,56,0.5)','rgba(32,130,240,0.4)','rgba(0,255,255,0.3)'];
for (let i = 0; i < 20; i++) {
const p = document.createElement('div');
p.className = 'pt';
const s = 2 + Math.random() * 4;
Object.assign(p.style, {
width:s+'px', height:s+'px',
left:Math.random()*100+'%', bottom:'-3%',
background:cols[i%3],
animationDuration:(10+Math.random()*14)+'s',
animationDelay:(Math.random()*12)+'s'
});
c.appendChild(p);
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EL3AB — Promo Display B (Stats)</title>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 100vw; height: 100vh;
overflow: hidden;
font-family: 'IBM Plex Sans Arabic', sans-serif;
background: #000; color: #F8FAFC;
display: flex; align-items: center; justify-content: center;
}
.stage {
width: min(100vw, calc(100vh / 3));
height: min(100vh, calc(100vw * 3));
position: relative; overflow: hidden;
background: #030508;
}
/* BG */
.bg {
position: absolute; inset: 0;
background:
radial-gradient(circle at 50% 30%, rgba(104,52,190,0.04) 0%, transparent 40%),
radial-gradient(circle at 50% 80%, rgba(228,172,56,0.03) 0%, transparent 40%);
}
/* Diagonal streaks */
.streaks {
position: absolute; inset: -50%;
background: repeating-linear-gradient(
-35deg,
transparent, transparent 80px,
rgba(228,172,56,0.012) 80px, rgba(228,172,56,0.012) 81px
);
animation: streak-drift 20s linear infinite;
}
@keyframes streak-drift { 0%{transform:translateX(0) translateY(0);} 100%{transform:translateX(113px) translateY(113px);} }
/* Orbs */
.orb {
position: absolute; border-radius: 50%;
filter: blur(60px);
animation: orb-float 10s ease-in-out infinite alternate;
}
.orb-1 { width: 40%; aspect-ratio: 1; top: -8%; right: -10%; background: rgba(32,130,240,0.06); }
.orb-2 { width: 30%; aspect-ratio: 1; bottom: 15%; left: -8%; background: rgba(228,172,56,0.05); animation-delay: -4s; }
@keyframes orb-float { 0%{transform:translate(0,0) scale(1);} 100%{transform:translate(2%, 3%) scale(1.1);} }
/* Particles */
.ptcls { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.pt {
position: absolute; border-radius: 50%;
animation: ptup linear infinite;
}
@keyframes ptup {
0%{transform:translateY(0) scale(1);opacity:0;}
10%{opacity:0.6;} 90%{opacity:0.2;}
100%{transform:translateY(-100vh) scale(0.2);opacity:0;}
}
/* Content */
.content {
position: relative; z-index: 10;
width: 100%; height: 100%;
display: grid;
grid-template-rows: 15% 50% 35%;
padding: 4% 6%;
}
/* === TOP === */
.top {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
gap: 1.5cqi;
}
.logo-sm {
width: 14cqi; height: 14cqi;
object-fit: contain;
filter: drop-shadow(0 0 15px rgba(228,172,56,0.4));
}
.title {
font-size: 10cqi; font-weight: 900;
background: linear-gradient(180deg, #FFF 30%, #E4AC38 100%);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle {
font-size: 2.8cqi; color: #64748B;
letter-spacing: 0.2em; font-weight: 300;
}
/* === MIDDLE: Stats + rotating messages === */
.mid {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
gap: 6%;
}
/* Rotating messages */
.msg-box {
position: relative;
width: 85%;
height: 18%;
display: flex; align-items: center; justify-content: center;
}
.msg {
position: absolute;
font-size: 4cqi; font-weight: 700;
text-align: center; line-height: 1.6;
opacity: 0; transform: translateY(30%);
animation: msg-in 20s ease-in-out infinite;
}
.msg:nth-child(1) { animation-delay: 0s; }
.msg:nth-child(2) { animation-delay: 5s; }
.msg:nth-child(3) { animation-delay: 10s; }
.msg:nth-child(4) { animation-delay: 15s; }
@keyframes msg-in {
0%{opacity:0;transform:translateY(30%);}
5%{opacity:1;transform:translateY(0);}
20%{opacity:1;transform:translateY(0);}
25%{opacity:0;transform:translateY(-30%);}
100%{opacity:0;}
}
.msg .gold { color: #E4AC38; }
.msg .cyan { color: #00FFFF; }
/* Game cards */
.cards {
display: flex; gap: 3%; justify-content: center;
width: 100%;
}
.card {
width: 28%;
aspect-ratio: 0.75;
border-radius: 8%;
background: rgba(255,255,255,0.02);
border: 1px solid rgba(255,255,255,0.05);
display: flex; flex-direction: column;
align-items: center; justify-content: center;
gap: 2cqi;
backdrop-filter: blur(5px);
animation: card-bob 4s ease-in-out infinite;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
.card:nth-child(1) { animation-delay: 0s; }
.card:nth-child(2) { animation-delay: 0.6s; }
.card:nth-child(3) { animation-delay: 1.2s; }
@keyframes card-bob { 0%,100%{transform:translateY(0);} 50%{transform:translateY(-2%);} }
.card img { width: 8cqi; height: 8cqi; object-fit: contain; }
.card-name { font-size: 2.8cqi; font-weight: 700; }
.card-live {
font-size: 2cqi; color: #34D399;
display: flex; align-items: center; gap: 1cqi;
}
.card-live::before {
content: ''; width: 5px; height: 5px;
border-radius: 50%; background: #34D399;
animation: blink 1.5s ease-in-out infinite;
}
@keyframes blink { 0%,100%{opacity:1;} 50%{opacity:0.3;} }
/* Stats row */
.stats {
display: flex; gap: 6%; align-items: center;
}
.stat { text-align: center; }
.stat-num {
font-size: 7cqi; font-weight: 900; color: #E4AC38; line-height: 1;
}
.stat-lbl { font-size: 2.2cqi; color: #64748B; margin-top: 0.5cqi; }
/* Tournament badge */
.tourn-badge {
display: flex; align-items: center; gap: 2cqi;
padding: 2% 5%;
border-radius: 999px;
background: linear-gradient(135deg, rgba(32,130,240,0.08), rgba(104,52,190,0.06));
border: 1px solid rgba(32,130,240,0.12);
}
.tourn-badge img { width: 4cqi; height: 4cqi; }
.tourn-badge span { font-size: 2.5cqi; font-weight: 600; }
/* === BOTTOM: CTA === */
.bot {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
gap: 4%;
}
.cta-card {
display: flex; align-items: center; gap: 5%;
width: 90%; padding: 5%;
border-radius: 6%;
background: linear-gradient(135deg, rgba(228,172,56,0.06), rgba(228,172,56,0.02));
border: 1px solid rgba(228,172,56,0.15);
position: relative; overflow: hidden;
}
.cta-card::after {
content: ''; position: absolute; inset: 0;
background: linear-gradient(105deg, transparent 40%, rgba(228,172,56,0.08) 50%, transparent 60%);
animation: shm 4s ease-in-out infinite;
}
@keyframes shm { 0%{transform:translateX(-150%);} 100%{transform:translateX(150%);} }
.qr-area {
position: relative; z-index: 2;
background: #fff;
padding: 2.5%;
border-radius: 8%;
flex-shrink: 0;
}
.qr-area img { width: 16cqi; height: 16cqi; display: block; }
/* Scan line */
.scan-line {
position: absolute;
left: 2.5%; right: 2.5%;
height: 2px;
background: linear-gradient(90deg, transparent, #E4AC38, transparent);
animation: scanm 2.5s ease-in-out infinite;
border-radius: 1px;
}
@keyframes scanm {
0%,100%{top:2.5%;opacity:0;} 10%{opacity:1;} 90%{opacity:1;} 50%{top:calc(100% - 2.5%);}
}
/* Ring pulse */
.qr-ring {
position: absolute; inset: -4px;
border-radius: 10%;
border: 1.5px solid rgba(228,172,56,0.3);
animation: ring-exp 2s ease-out infinite;
}
@keyframes ring-exp { 0%{transform:scale(1);opacity:1;} 100%{transform:scale(1.12);opacity:0;} }
.cta-text {
position: relative; z-index: 2;
display: flex; flex-direction: column; gap: 1.5cqi;
}
.cta-main { font-size: 4cqi; font-weight: 800; }
.cta-sub { font-size: 2.5cqi; color: #94A3B8; }
.cta-url {
font-size: 2cqi; color: #2082F0; direction: ltr;
font-family: monospace;
}
/* Bottom bar */
.bot-bar {
display: flex; align-items: center; gap: 3%;
flex-wrap: wrap; justify-content: center;
}
.bb {
display: flex; align-items: center; gap: 1cqi;
font-size: 2cqi; color: #64748B;
padding: 1% 2.5%;
border-radius: 999px;
background: rgba(255,255,255,0.02);
border: 1px solid rgba(255,255,255,0.04);
}
.bb-dot {
width: 5px; height: 5px; border-radius: 50%;
background: #34D399;
animation: blink 1.5s ease-in-out infinite;
}
.bb-flag {
width: 2.5cqi; height: 1.7cqi;
border-radius: 2px;
display: flex; flex-direction: column; overflow: hidden;
}
.bb-flag span { flex: 1; }
.bb-flag span:nth-child(1) { background: #CE1126; }
.bb-flag span:nth-child(2) { background: #fff; }
.bb-flag span:nth-child(3) { background: #111; }
</style>
</head>
<body>
<div class="stage">
<div class="bg"></div>
<div class="streaks"></div>
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="ptcls" id="ptcls"></div>
<div class="content">
<!-- TOP -->
<div class="top">
<img class="logo-sm" src="video/assets/logof.png" alt="EL3AB">
<div class="title">العب</div>
<div class="subtitle">MIND GAMES ARENA</div>
</div>
<!-- MIDDLE -->
<div class="mid">
<div class="msg-box">
<div class="msg">نمّي <span class="gold">ذكاء طفلك</span><br>من خلال ألعاب بيحبها</div>
<div class="msg">بطولات <span class="cyan">مدارس وجامعات</span><br>وأندية ومراكز شباب</div>
<div class="msg">تعليم شطرنج <span class="gold">تفاعلي</span><br>من الصفر للاحتراف</div>
<div class="msg">بيئة <span class="cyan">آمنة تماماً</span><br>لكل العيلة</div>
</div>
<div class="cards">
<div class="card">
<img src="../app icons/chess.png" alt="Chess">
<div class="card-name">شطرنج</div>
<div class="card-live">LIVE</div>
</div>
<div class="card">
<img src="../app icons/domino.png" alt="Domino">
<div class="card-name">دومينو</div>
<div class="card-live">LIVE</div>
</div>
<div class="card">
<img src="../app icons/dice.png" alt="Ludo">
<div class="card-name">لودو</div>
<div class="card-live">LIVE</div>
</div>
</div>
<div class="stats">
<div class="stat"><div class="stat-num">4</div><div class="stat-lbl">ألعاب ذهنية</div></div>
<div class="stat"><div class="stat-num"></div><div class="stat-lbl">بطولات</div></div>
<div class="stat"><div class="stat-num">0</div><div class="stat-lbl">إعلانات</div></div>
</div>
<div class="tourn-badge">
<img src="../app icons/trophy.png" alt="">
<span>جاهز لاستضافة كل البطولات الرسمية</span>
</div>
</div>
<!-- BOTTOM -->
<div class="bot">
<div class="cta-card">
<div class="qr-area">
<div class="qr-ring"></div>
<div class="scan-line"></div>
<img src="video/assets/qr-code.png" alt="QR">
</div>
<div class="cta-text">
<div class="cta-main">جرّب دلوقتي مجاناً</div>
<div class="cta-sub">امسح الكود وابدأ أول ماتش</div>
<div class="cta-url">el3ab-player.caprover.al-arcade.com</div>
</div>
</div>
<div class="bot-bar">
<div class="bb"><span class="bb-dot"></span> Online Now</div>
<div class="bb"><div class="bb-flag"><span></span><span></span><span></span></div> تطوير مصري 100%</div>
<div class="bb">مجاني بالكامل</div>
</div>
</div>
</div>
</div>
<script>
const c = document.getElementById('ptcls');
const cols = ['rgba(228,172,56,0.4)','rgba(32,130,240,0.3)','rgba(0,255,255,0.25)'];
for (let i = 0; i < 18; i++) {
const p = document.createElement('div');
p.className = 'pt';
const s = 2 + Math.random() * 3;
Object.assign(p.style, {
width:s+'px', height:s+'px',
left:Math.random()*100+'%', bottom:'-3%',
background:cols[i%3],
animationDuration:(12+Math.random()*16)+'s',
animationDelay:(Math.random()*14)+'s'
});
c.appendChild(p);
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EL3AB — 1m×3m Display</title>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 100vw; height: 100vh;
overflow: hidden;
font-family: 'IBM Plex Sans Arabic', sans-serif;
background: #000; color: #F8FAFC;
display: flex; align-items: center; justify-content: center;
}
/* 1:3 portrait — 1m wide × 3m tall */
.stage {
width: min(100vw, calc(100vh / 3));
height: min(100vh, calc(100vw * 3));
position: relative; overflow: hidden;
background: #020406;
container-type: size;
}
/* ═══ BG ═══ */
.bg {
position: absolute; inset: 0;
background:
radial-gradient(ellipse 100% 15% at 50% 5%, rgba(228,172,56,0.05) 0%, transparent 60%),
radial-gradient(ellipse 80% 10% at 50% 63%, rgba(32,130,240,0.03) 0%, transparent 40%),
radial-gradient(ellipse 90% 12% at 50% 95%, rgba(228,172,56,0.03) 0%, transparent 50%);
}
.bg-grid {
position: absolute; inset: 0; opacity: 0.01;
background-image:
linear-gradient(rgba(255,255,255,0.3) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.3) 1px, transparent 1px);
background-size: 12.5% 4.16%;
animation: drift 40s linear infinite;
}
@keyframes drift { to { transform: translateY(4.16%); } }
.vein {
position: absolute; width: 1px; top: 0; bottom: 0;
background: linear-gradient(180deg, transparent 10%, rgba(228,172,56,0.05) 50%, transparent 90%);
animation: vein-glow 6s ease-in-out infinite alternate;
}
.vein:nth-child(1) { left: 6%; }
.vein:nth-child(2) { right: 6%; animation-delay: -3s; }
@keyframes vein-glow { 0%{opacity:0.2;} 100%{opacity:0.6;} }
.particles { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.pt {
position: absolute; border-radius: 50%;
animation: rise linear infinite;
}
@keyframes rise {
0% { transform: translateY(0) scale(1); opacity: 0; }
8% { opacity: 0.5; }
85% { opacity: 0.08; }
100% { transform: translateY(calc(-100cqh)) scale(0.3); opacity: 0; }
}
/* ═══ LAYOUT ═══
Safe areas: 4% top, 4% bottom, 8% sides
Zones (of 100% height):
4-18% : Logo + brand (14%)
20-52% : Phone screenshot (32%)
54-74% : QR section (20%) — center at 64% = 1.08m from ground ✓
76-96% : Games + features (20%)
*/
.layout {
position: absolute;
top: 4cqh; bottom: 4cqh;
left: 8cqw; right: 8cqw;
z-index: 10;
}
/* ═══ TOP: Logo ═══ */
.top {
position: absolute;
top: 0; left: 0; right: 0;
height: 14cqh;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
gap: 0.6cqh;
}
.logo-wrap {
position: relative;
width: 8cqh; height: 8cqh;
}
.logo-wrap img {
width: 100%; height: 100%; object-fit: contain;
filter: drop-shadow(0 0 15px rgba(228,172,56,0.4));
animation: breathe 4s ease-in-out infinite;
}
@keyframes breathe { 0%,100%{transform:scale(1);} 50%{transform:scale(1.03);} }
.logo-ring {
position: absolute; inset: -20%;
border-radius: 50%;
border: 1px solid rgba(228,172,56,0.1);
animation: spin 18s linear infinite;
}
.logo-ring::after {
content: ''; position: absolute;
top: -2px; left: 50%; width: 4px; height: 4px;
margin-left: -2px; border-radius: 50%;
background: #E4AC38; box-shadow: 0 0 6px #E4AC38;
}
@keyframes spin { to { transform: rotate(360deg); } }
.brand { font-size: 4cqh; font-weight: 900; line-height: 1; background: linear-gradient(180deg, #FFF 20%, #E4AC38 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.brand-sub { font-size: 1cqh; font-weight: 300; color: #64748B; letter-spacing: 0.4em; }
/* ═══ PHONE CAROUSEL ═══ */
.phone-area {
position: absolute;
top: 16cqh;
left: 50%; transform: translateX(-50%);
width: 38cqw;
height: 30cqh;
}
.phone-frame {
position: relative;
width: 100%; height: 100%;
border-radius: 2cqh;
overflow: hidden;
border: 2px solid rgba(255,255,255,0.07);
box-shadow:
0 15px 50px rgba(0,0,0,0.5),
0 0 30px rgba(32,130,240,0.05);
animation: phone-float 6s ease-in-out infinite;
}
@keyframes phone-float { 0%,100%{transform:translateY(0);} 50%{transform:translateY(-0.4cqh);} }
.phone-glow {
position: absolute; inset: -2px;
border-radius: 2.2cqh;
z-index: -1;
background: conic-gradient(from 0deg, rgba(228,172,56,0.2), rgba(32,130,240,0.12), rgba(0,255,255,0.1), rgba(228,172,56,0.2));
filter: blur(5px);
animation: glow-spin 12s linear infinite;
}
@keyframes glow-spin { to { transform: rotate(360deg); } }
.phone-screen {
position: relative;
width: 100%; height: 100%;
}
.phone-screen img {
position: absolute; inset: 0;
width: 100%; height: 100%;
object-fit: cover;
object-position: top center;
opacity: 0;
animation: crossfade 16s ease-in-out infinite;
}
.phone-screen img:nth-child(1) { animation-delay: 0s; }
.phone-screen img:nth-child(2) { animation-delay: 4s; }
.phone-screen img:nth-child(3) { animation-delay: 8s; }
.phone-screen img:nth-child(4) { animation-delay: 12s; }
@keyframes crossfade {
0% { opacity: 0; transform: scale(1.02); }
5% { opacity: 1; transform: scale(1); }
25% { opacity: 1; transform: scale(1); }
30% { opacity: 0; transform: scale(0.98); }
100% { opacity: 0; }
}
.phone-notch {
position: absolute; top: 0; left: 50%;
transform: translateX(-50%);
width: 30%; height: 1.8cqh;
background: #000;
border-radius: 0 0 0.8cqh 0.8cqh;
z-index: 5;
}
/* ═══ QR SECTION — at ~64% from top ═══ */
.qr-section {
position: absolute;
top: 50cqh;
left: 0; right: 0;
height: 16cqh;
border-radius: 2cqh;
background: linear-gradient(155deg, rgba(228,172,56,0.04) 0%, rgba(8,12,24,0.85) 100%);
border: 1px solid rgba(228,172,56,0.1);
display: flex;
flex-direction: row;
align-items: center;
padding: 2cqh 3cqw;
gap: 3cqw;
overflow: hidden;
}
.qr-section::before {
content: ''; position: absolute; inset: 0;
background: linear-gradient(110deg, transparent 30%, rgba(228,172,56,0.04) 42%, rgba(228,172,56,0.07) 50%, rgba(228,172,56,0.04) 58%, transparent 70%);
animation: shimmer 7s ease-in-out infinite;
pointer-events: none;
}
@keyframes shimmer { 0%{transform:translateX(-200%);} 100%{transform:translateX(200%);} }
.qr-box {
position: relative; z-index: 2;
flex-shrink: 0;
width: 11cqh; height: 11cqh;
background: #fff;
border-radius: 1.2cqh;
padding: 0.6cqh;
display: flex; align-items: center; justify-content: center;
box-shadow: 0 6px 25px rgba(0,0,0,0.4);
}
.qr-box img { width: 100%; height: 100%; display: block; }
.qr-scanline {
position: absolute;
left: 0.6cqh; right: 0.6cqh; height: 2px;
background: linear-gradient(90deg, transparent, #E4AC38, transparent);
border-radius: 1px;
animation: scan 3s ease-in-out infinite;
}
@keyframes scan { 0%,100%{top:0.6cqh;opacity:0;} 10%{opacity:1;} 90%{opacity:1;} 50%{top:calc(100% - 0.6cqh);} }
.qr-corner {
position: absolute; width: 15%; height: 15%;
border: 2px solid #E4AC38; z-index: 3;
}
.qr-corner.tl { top: -1px; left: -1px; border-right: none; border-bottom: none; border-radius: 5px 0 0 0; }
.qr-corner.tr { top: -1px; right: -1px; border-left: none; border-bottom: none; border-radius: 0 5px 0 0; }
.qr-corner.bl { bottom: -1px; left: -1px; border-right: none; border-top: none; border-radius: 0 0 0 5px; }
.qr-corner.br { bottom: -1px; right: -1px; border-left: none; border-top: none; border-radius: 0 0 5px 0; }
.qr-pulse {
position: absolute; inset: -4px;
border-radius: 1.5cqh;
border: 1.5px solid rgba(228,172,56,0.2);
animation: qpulse 2.5s ease-out infinite;
z-index: 1;
}
@keyframes qpulse { 0%{transform:scale(1);opacity:1;} 100%{transform:scale(1.1);opacity:0;} }
.qr-info {
position: relative; z-index: 2;
display: flex; flex-direction: column;
gap: 0.8cqh;
flex: 1;
min-width: 0;
}
.qr-headline {
font-size: 2.2cqh; font-weight: 800; line-height: 1.4;
}
.qr-headline .gold { color: #E4AC38; }
.qr-desc {
font-size: 1.2cqh; color: #94A3B8; line-height: 1.6;
}
.qr-url {
font-size: 0.9cqh; color: #2082F0; direction: ltr;
padding: 0.3cqh 1cqw;
background: rgba(32,130,240,0.05);
border: 1px solid rgba(32,130,240,0.08);
border-radius: 999px; width: fit-content;
font-family: monospace;
}
/* ═══ BOTTOM ZONE ═══ */
.bottom {
position: absolute;
top: 69cqh;
left: 0; right: 0;
bottom: 0;
display: flex; flex-direction: column;
align-items: center;
justify-content: space-evenly;
}
.games-row {
display: flex; gap: 7cqw; align-items: center;
}
.game-item {
display: flex; flex-direction: column;
align-items: center; gap: 0.3cqh;
animation: bob 4s ease-in-out infinite;
}
.game-item:nth-child(2) { animation-delay: -1.3s; }
.game-item:nth-child(3) { animation-delay: -2.6s; }
@keyframes bob { 0%,100%{transform:translateY(0);} 50%{transform:translateY(-3px);} }
.game-item img { width: 3.5cqh; height: 3.5cqh; object-fit: contain; }
.game-item .name { font-size: 1cqh; font-weight: 700; color: #CBD5E1; }
.game-item .live { font-size: 0.8cqh; color: #34D399; display: flex; align-items: center; gap: 0.2cqh; }
.game-item .live::before { content:''; width:3px; height:3px; border-radius:50%; background:#34D399; animation:blink 1.5s ease-in-out infinite; }
@keyframes blink { 0%,100%{opacity:1;} 50%{opacity:0.3;} }
.feat-area {
position: relative; width: 100%; height: 2.5cqh;
overflow: hidden;
display: flex; align-items: center; justify-content: center;
}
.feat-line {
position: absolute;
font-size: 1.2cqh; font-weight: 600; color: #94A3B8;
white-space: nowrap; opacity: 0;
animation: feat-rot 20s ease-in-out infinite;
text-align: center;
}
.feat-line:nth-child(1) { animation-delay: 0s; }
.feat-line:nth-child(2) { animation-delay: 5s; }
.feat-line:nth-child(3) { animation-delay: 10s; }
.feat-line:nth-child(4) { animation-delay: 15s; }
@keyframes feat-rot {
0%{opacity:0;transform:translateY(100%);}
5%{opacity:1;transform:translateY(0);}
22%{opacity:1;transform:translateY(0);}
27%{opacity:0;transform:translateY(-100%);}
100%{opacity:0;}
}
.feat-line .gold { color: #E4AC38; }
.feat-line .cyan { color: #00FFFF; }
.badges {
display: flex; gap: 1.5cqw; flex-wrap: wrap; justify-content: center;
}
.badge {
display: flex; align-items: center; gap: 0.5cqw;
font-size: 0.85cqh; color: #64748B;
padding: 0.25cqh 1cqw;
border-radius: 999px;
background: rgba(255,255,255,0.02);
border: 1px solid rgba(255,255,255,0.04);
}
.b-dot { width:3px; height:3px; border-radius:50%; background:#34D399; animation:blink 1.5s ease-in-out infinite; }
.b-flag {
width: 1cqh; height: 0.7cqh; border-radius: 1px;
display: flex; flex-direction: column; overflow: hidden;
}
.b-flag span { flex: 1; }
.b-flag span:nth-child(1) { background: #CE1126; }
.b-flag span:nth-child(2) { background: #fff; }
.b-flag span:nth-child(3) { background: #111; }
/* Separator line between zones */
.sep {
position: absolute;
left: 15%; right: 15%;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(228,172,56,0.08), transparent);
}
.sep-1 { top: 15.5cqh; }
.sep-2 { top: 48.5cqh; }
.sep-3 { top: 67.5cqh; }
</style>
</head>
<body>
<div class="stage">
<div class="bg"></div>
<div class="bg-grid"></div>
<div class="vein"></div>
<div class="vein"></div>
<div class="particles" id="ptcls"></div>
<div class="layout">
<!-- Subtle separators -->
<div class="sep sep-1"></div>
<div class="sep sep-2"></div>
<div class="sep sep-3"></div>
<!-- TOP: Logo + Brand -->
<div class="top">
<div class="logo-wrap">
<div class="logo-ring"></div>
<img src="video/assets/logof.png" alt="EL3AB">
</div>
<div class="brand">العب</div>
<div class="brand-sub">MIND GAMES ARENA</div>
</div>
<!-- PHONE: Crossfade carousel -->
<div class="phone-area">
<div class="phone-frame">
<div class="phone-glow"></div>
<div class="phone-notch"></div>
<div class="phone-screen">
<img src="screenshots/02-home.png" alt="">
<img src="screenshots/06-chess-midgame.png" alt="">
<img src="screenshots/08-ludo-game.png" alt="">
<img src="screenshots/09-domino-menu.png" alt="">
</div>
</div>
</div>
<!-- QR: Center at ~58% from top = 1.26m from ground on 3m -->
<div class="qr-section">
<div class="qr-box">
<div class="qr-pulse"></div>
<div class="qr-corner tl"></div>
<div class="qr-corner tr"></div>
<div class="qr-corner bl"></div>
<div class="qr-corner br"></div>
<div class="qr-scanline"></div>
<img src="video/assets/qr-code.png" alt="QR">
</div>
<div class="qr-info">
<div class="qr-headline">امسح. سجّل.<br><span class="gold">العب.</span></div>
<div class="qr-desc">ابدأ أول ماتش مجاناً<br>ضد صحابك أو الكمبيوتر</div>
<div class="qr-url">el3ab-player.caprover.al-arcade.com</div>
</div>
</div>
<!-- BOTTOM: Games + Features -->
<div class="bottom">
<div class="games-row">
<div class="game-item">
<img src="icons-chess.png" alt="">
<span class="name">شطرنج</span>
<span class="live">LIVE</span>
</div>
<div class="game-item">
<img src="icons-dice.png" alt="">
<span class="name">لودو</span>
<span class="live">LIVE</span>
</div>
<div class="game-item">
<img src="icons-domino.png" alt="">
<span class="name">دومينو</span>
<span class="live">LIVE</span>
</div>
</div>
<div class="feat-area">
<div class="feat-line">بطولات <span class="gold">مدارس وجامعات وأندية ومراكز شباب</span></div>
<div class="feat-line">تعليم شطرنج <span class="cyan">تفاعلي</span> — من الصفر للاحتراف</div>
<div class="feat-line">بيئة <span class="gold">آمنة 100%</span> — مفيش chat مفتوح</div>
<div class="feat-line">تطوير <span class="cyan">مصري بالكامل</span> — مجاني — بدون إعلانات</div>
</div>
<div class="badges">
<div class="badge"><span class="b-dot"></span> شغّال دلوقتي</div>
<div class="badge"><div class="b-flag"><span></span><span></span><span></span></div> مصري 100%</div>
<div class="badge">مجاني بالكامل</div>
<div class="badge">آمن للأطفال</div>
</div>
</div>
</div>
</div>
<script>
const pc = document.getElementById('ptcls');
const cols = ['rgba(228,172,56,0.2)','rgba(32,130,240,0.14)','rgba(0,255,255,0.1)'];
for (let i = 0; i < 14; i++) {
const p = document.createElement('div');
p.className = 'pt';
const s = 1.5 + Math.random() * 2.5;
Object.assign(p.style, {
width:s+'px', height:s+'px',
left:Math.random()*100+'%', bottom:'0',
background:cols[i%3],
animationDuration:(20+Math.random()*24)+'s',
animationDelay:(Math.random()*18)+'s'
});
pc.appendChild(p);
}
</script>
</body>
</html>
const puppeteer = require('puppeteer');
const path = require('path');
const BASE = 'https://el3ab-player.caprover.al-arcade.com';
const OUT = path.join(__dirname, 'screenshots');
const PHONE = { width: 390, height: 844, deviceScaleFactor: 2 };
const wait = ms => new Promise(r => setTimeout(r, ms));
const EMAIL = 'promo-test@el3ab.com';
const PASS = 'PromoTest123';
async function run() {
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.setViewport(PHONE);
// ─── 1. LOGIN ───
console.log('1. Loading app...');
await page.goto(BASE, { waitUntil: 'networkidle2', timeout: 30000 });
await wait(3000);
await page.screenshot({ path: path.join(OUT, '01-login.png') });
console.log(' ✓ 01-login.png');
console.log('2. Logging in...');
const inputs = await page.$$('input');
if (inputs.length >= 2) {
await inputs[0].click({ clickCount: 3 });
await inputs[0].type(EMAIL);
await inputs[1].click({ clickCount: 3 });
await inputs[1].type(PASS);
await wait(300);
const btn = await page.$('button');
if (btn) await btn.click();
await wait(6000);
}
// ─── 2. HOME SCREEN ───
await page.screenshot({ path: path.join(OUT, '02-home.png') });
console.log(' ✓ 02-home.png');
// ─── 3. CHESS: Navigate to chess ───
console.log('3. Navigating to Chess...');
try {
const chessEl = await page.evaluateHandle(() => {
const all = document.querySelectorAll('*');
for (const el of all) {
const txt = el.textContent?.trim();
if (txt === 'شطرنج' || txt === 'Chess') return el;
}
for (const el of all) {
if (el.textContent?.includes('شطرنج') && el.tagName !== 'BODY' && el.tagName !== 'HTML' && el.children.length < 5) return el;
}
return null;
});
if (chessEl) {
await chessEl.click();
await wait(3000);
await page.screenshot({ path: path.join(OUT, '03-chess-menu.png') });
console.log(' ✓ 03-chess-menu.png');
}
} catch(e) { console.log(' ✗ Chess nav failed:', e.message); }
// ─── 4. CHESS: Click "لاعب واحد" (single player) ───
console.log('4. Selecting single player mode...');
try {
const singleEl = await page.evaluateHandle(() => {
const all = document.querySelectorAll('*');
for (const el of all) {
const txt = el.textContent?.trim();
if (txt === 'لاعب واحد' || txt === 'Single Player') return el;
}
return null;
});
if (singleEl) {
await singleEl.click();
await wait(3000);
}
} catch(e) { console.log(' ✗ Single player:', e.message); }
// ─── 5. CHESS: Bot selection screen ───
await page.screenshot({ path: path.join(OUT, '04-bot-select.png') });
console.log(' ✓ 04-bot-select.png');
// ─── 6. CHESS: Click first bot card ───
console.log('5. Selecting a bot...');
try {
const botCard = await page.evaluateHandle(() => {
const card = document.querySelector('.bot-card');
return card || null;
});
if (botCard) {
await botCard.click();
await wait(2000);
console.log(' → Bot selected, now on time select');
// ─── 7. CHESS: Click a time control (5+0 blitz) ───
const timeBtn = await page.evaluateHandle(() => {
const btns = document.querySelectorAll('.time-btn, button');
for (const b of btns) {
if (b.textContent?.includes('5+0') || b.textContent?.includes('3+0')) return b;
}
// Fallback: first time button
const firstTime = document.querySelector('.time-btn');
return firstTime || null;
});
if (timeBtn) {
await timeBtn.click();
console.log(' → Time control selected, game starting...');
await wait(6000);
await page.screenshot({ path: path.join(OUT, '05-chess-start.png') });
console.log(' ✓ 05-chess-start.png');
// ─── 8. CHESS: Play moves by clicking canvas ───
console.log('6. Playing chess moves...');
const canvas = await page.$('canvas');
if (canvas) {
const box = await canvas.boundingBox();
if (box) {
const sq = box.width / 8;
// Click e2 then e4 (column 4, rows from bottom: row 6 = e2, row 4 = e4)
await page.mouse.click(box.x + sq * 4.5, box.y + sq * 6.5);
await wait(500);
await page.mouse.click(box.x + sq * 4.5, box.y + sq * 4.5);
await wait(3000); // Wait for bot response
// Click d2 then d4
await page.mouse.click(box.x + sq * 3.5, box.y + sq * 6.5);
await wait(500);
await page.mouse.click(box.x + sq * 3.5, box.y + sq * 4.5);
await wait(3000); // Wait for bot response
// Click Nf3 (g1 to f3)
await page.mouse.click(box.x + sq * 6.5, box.y + sq * 7.5);
await wait(500);
await page.mouse.click(box.x + sq * 5.5, box.y + sq * 5.5);
await wait(3000);
}
}
await page.screenshot({ path: path.join(OUT, '06-chess-midgame.png') });
console.log(' ✓ 06-chess-midgame.png');
}
}
} catch(e) { console.log(' ✗ Bot/game flow:', e.message); }
// ─── 9. LUDO ───
console.log('7. Navigating to Ludo...');
await page.goto(BASE, { waitUntil: 'networkidle2', timeout: 30000 });
await wait(4000);
try {
const ludoEl = await page.evaluateHandle(() => {
const all = document.querySelectorAll('*');
for (const el of all) {
const txt = el.textContent?.trim();
if (txt === 'لودو' || txt === 'Ludo') return el;
}
for (const el of all) {
if (el.textContent?.includes('لودو') && el.tagName !== 'BODY' && el.tagName !== 'HTML' && el.children.length < 5) return el;
}
return null;
});
if (ludoEl) {
await ludoEl.click();
await wait(3000);
await page.screenshot({ path: path.join(OUT, '07-ludo-menu.png') });
console.log(' ✓ 07-ludo-menu.png');
// Start ludo game vs bot
const ludoPlayBtn = await page.evaluateHandle(() => {
const all = document.querySelectorAll('button, .btn, [class*="play"], [class*="start"]');
for (const el of all) {
const txt = el.textContent?.trim();
if (txt && (txt.includes('العب') || txt.includes('ابدأ') || txt.includes('بوت') || txt.includes('لاعب واحد'))) return el;
}
return null;
});
if (ludoPlayBtn) {
await ludoPlayBtn.click();
await wait(6000);
await page.screenshot({ path: path.join(OUT, '08-ludo-game.png') });
console.log(' ✓ 08-ludo-game.png');
}
}
} catch(e) { console.log(' ✗ Ludo:', e.message); }
// ─── 10. DOMINO ───
console.log('8. Navigating to Domino...');
await page.goto(BASE, { waitUntil: 'networkidle2', timeout: 30000 });
await wait(4000);
try {
const domEl = await page.evaluateHandle(() => {
const all = document.querySelectorAll('*');
for (const el of all) {
const txt = el.textContent?.trim();
if (txt === 'دومينو' || txt === 'Domino') return el;
}
return null;
});
if (domEl) {
await domEl.click();
await wait(3000);
await page.screenshot({ path: path.join(OUT, '09-domino-menu.png') });
console.log(' ✓ 09-domino-menu.png');
}
} catch(e) { console.log(' ✗ Domino:', e.message); }
// ─── 11. FINAL HOME ───
console.log('9. Final home capture...');
await page.goto(BASE, { waitUntil: 'networkidle2', timeout: 30000 });
await wait(5000);
await page.screenshot({ path: path.join(OUT, '10-home-final.png') });
console.log(' ✓ 10-home-final.png');
await browser.close();
console.log('\n✅ Done! Screenshots saved to:', OUT);
}
run().catch(e => { console.error(e); process.exit(1); });
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EL3AB — Game Trailer</title>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 100vw;
height: 100vh;
overflow: hidden;
font-family: 'IBM Plex Sans Arabic', sans-serif;
background: #000;
color: #F8FAFC;
}
.stage {
width: 100vw;
height: 100vh;
position: relative;
overflow: hidden;
background: #030508;
}
/* ═══ OVERLAYS ═══ */
.vignette {
position: fixed; inset: 0;
background: radial-gradient(ellipse at center, transparent 40%, rgba(0,0,0,0.55) 100%);
z-index: 900; pointer-events: none;
}
.scanlines {
position: fixed; inset: 0;
z-index: 901; pointer-events: none; opacity: 0.025;
background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,0,0,0.4) 2px, rgba(0,0,0,0.4) 4px);
}
.glitch-flash {
position: fixed; inset: 0;
z-index: 950; pointer-events: none;
opacity: 0; background: #fff;
}
.glitch-flash.fire { animation: gf 0.12s ease-out forwards; }
.glitch-flash.gold { animation: gfg 0.12s ease-out forwards; }
@keyframes gf { 0%{opacity:0.8;} 100%{opacity:0;} }
@keyframes gfg { 0%{opacity:0.6; background:#E4AC38;} 100%{opacity:0;} }
.progress {
position: fixed; top: 0; left: 0;
height: 3px; z-index: 2000;
background: linear-gradient(90deg, #E4AC38, #00FFFF);
}
.particles { position: fixed; inset: 0; z-index: 800; pointer-events: none; }
.ptcl {
position: absolute; border-radius: 50%;
animation: ptcl-r linear infinite;
}
@keyframes ptcl-r {
0% { transform: translateY(100vh) scale(0); opacity: 0; }
10% { opacity: 0.7; }
90% { opacity: 0.3; }
100% { transform: translateY(-10vh) scale(0.3); opacity: 0; }
}
/* ═══ SCENES ═══ */
.scene {
position: absolute; inset: 0;
display: flex; align-items: center; justify-content: center;
flex-direction: column; gap: 3vh;
opacity: 0; pointer-events: none;
}
.scene.active { opacity: 1; pointer-events: auto; transition: opacity 0.05s; }
/* ═══ TEXT ═══ */
.slam {
font-weight: 900; text-align: center; line-height: 1.2;
opacity: 0; transform: scale(1.6) translateY(5vh);
filter: blur(15px);
}
.slam.hit {
opacity: 1; transform: scale(1) translateY(0); filter: blur(0);
transition: all 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}
.sz-huge { font-size: min(14vh, 18vw); }
.sz-big { font-size: min(10vh, 13vw); }
.sz-med { font-size: min(6.5vh, 8vw); }
.sz-sm { font-size: min(4.5vh, 5.5vw); }
.gold { color: #E4AC38; }
.cyan { color: #00FFFF; }
.green { color: #34D399; }
.dim { color: #333; }
.strike { text-decoration: line-through; color: #475569; }
/* ═══ COLD OPEN ═══ */
#s0 { background: #000; }
.cold-ln {
font-size: 2.5vh; color: #1a1a1a; letter-spacing: 0.5em;
opacity: 0;
}
.cold-ln.show { opacity: 1; transition: opacity 0.3s; }
.cold-ln.out { opacity: 0.08; transition: opacity 1.5s; }
/* ═══ PHONE FRAMES ═══ */
.phone {
width: min(26vh, 30vw); height: min(48vh, 56vw);
border-radius: 2.5vh; overflow: hidden;
border: 2px solid rgba(255,255,255,0.07);
box-shadow: 0 30px 80px rgba(0,0,0,0.8), 0 0 60px rgba(32,130,240,0.12);
opacity: 0; transform: scale(0.6);
}
.phone.in {
opacity: 1; transform: scale(1);
transition: all 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}
.phone img { width: 100%; height: 100%; object-fit: cover; }
/* ═══ TRIPLE PHONES ═══ */
#s-triple { flex-direction: row; gap: 3vw; flex-wrap: wrap; }
.tri-phone {
width: min(18vh, 20vw); height: min(34vh, 38vw);
border-radius: 2vh; overflow: hidden;
border: 2px solid rgba(255,255,255,0.06);
box-shadow: 0 20px 60px rgba(0,0,0,0.7);
opacity: 0; transform: translateY(12vh) scale(0.85);
}
.tri-phone.in {
opacity: 1; transform: translateY(0) scale(1);
transition: all 0.55s cubic-bezier(0.16, 1, 0.3, 1);
}
.tri-phone:nth-child(2).in { transition-delay: 0.08s; }
.tri-phone:nth-child(3).in { transition-delay: 0.16s; }
.tri-phone img { width: 100%; height: 100%; object-fit: cover; }
.tri-label {
position: absolute; bottom: 7vh;
font-size: min(4.5vh, 5vw); font-weight: 700;
opacity: 0; width: 100%; text-align: center;
}
.tri-label.in { opacity: 1; transition: opacity 0.5s ease 0.3s; }
/* ═══ STATS ═══ */
#s-stats { flex-direction: row; gap: 8vw; }
.st-item {
text-align: center; opacity: 0; transform: translateY(4vh);
}
.st-item.in { opacity: 1; transform: translateY(0); transition: all 0.5s cubic-bezier(0.16,1,0.3,1); }
.st-num {
font-size: min(11vh, 14vw); font-weight: 900; line-height: 1;
background: linear-gradient(180deg, #FFF 20%, #E4AC38 100%);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text;
}
.st-lbl { font-size: min(2.5vh, 3vw); color: #64748B; margin-top: 1vh; }
/* ═══ 400M ═══ */
.map-num {
font-size: min(22vh, 28vw); font-weight: 900; line-height: 1;
background: linear-gradient(135deg, #E4AC38, #FF6B35);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text;
opacity: 0; transform: scale(2.5); filter: blur(20px);
}
.map-num.in {
opacity: 1; transform: scale(1); filter: blur(0);
transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
/* ═══ BRACKET ═══ */
.bracket-box { position: relative; width: 70vw; height: 50vh; }
.bn {
position: absolute; width: min(3.5vh, 4vw); height: min(3.5vh, 4vw);
border-radius: 50%; background: #E4AC38;
box-shadow: 0 0 25px rgba(228,172,56,0.5);
opacity: 0; transform: scale(0);
}
.bn.in { opacity: 1; transform: scale(1); transition: all 0.3s cubic-bezier(0.34,1.56,0.64,1); }
.bl {
position: absolute; background: rgba(228,172,56,0.25); border-radius: 1px;
opacity: 0; transform: scaleX(0); transform-origin: right;
}
.bl.in { opacity: 1; transform: scaleX(1); transition: all 0.35s ease; }
/* ═══ SHIELD ═══ */
.shield-huge {
font-size: min(14vh, 18vw);
opacity: 0; transform: scale(0.3) rotate(-20deg);
}
.shield-huge.in {
opacity: 1; transform: scale(1) rotate(0);
transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* ═══ CLOSER ═══ */
.c-logo {
width: min(22vh, 28vw); height: min(22vh, 28vw);
object-fit: contain; opacity: 0; transform: scale(0.4);
filter: drop-shadow(0 0 80px rgba(228,172,56,0.5));
}
.c-logo.in {
opacity: 1; transform: scale(1);
transition: all 1.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.c-text {
font-size: min(4vh, 5vw); font-weight: 400; color: #94A3B8;
opacity: 0; text-align: center; line-height: 1.8;
}
.c-text.in { opacity: 1; transition: opacity 0.8s ease 0.4s; }
.c-gold {
font-size: min(4vh, 5vw); font-weight: 700; color: #E4AC38;
opacity: 0; text-align: center;
}
.c-gold.in { opacity: 1; transition: opacity 0.8s ease 0.8s; }
/* ═══ PLAY OVERLAY ═══ */
.play-ov {
position: fixed; inset: 0; z-index: 9999; background: #000;
display: flex; flex-direction: column; align-items: center; justify-content: center;
gap: 3vh; cursor: pointer;
}
.play-ov.gone { display: none; }
.play-circle {
width: min(12vh, 15vw); height: min(12vh, 15vw); border-radius: 50%;
background: linear-gradient(135deg, #E4AC38, #FFD700);
display: flex; align-items: center; justify-content: center;
box-shadow: 0 0 60px rgba(228,172,56,0.4);
animation: ppulse 2s ease-in-out infinite;
}
@keyframes ppulse { 0%,100%{transform:scale(1);} 50%{transform:scale(1.05);} }
.play-circle::after {
content: ''; border-style: solid;
border-width: min(2vh, 2.5vw) 0 min(2vh, 2.5vw) min(3.5vh, 4vw);
border-color: transparent transparent transparent #000;
margin-left: min(0.5vh, 0.6vw);
}
.play-txt { font-size: min(2.5vh, 3vw); color: #475569; letter-spacing: 0.3em; }
</style>
</head>
<body>
<div class="stage">
<div class="vignette"></div>
<div class="scanlines"></div>
<div class="glitch-flash" id="flash"></div>
<div class="particles" id="particles"></div>
<div class="progress" id="progress"></div>
<div class="play-ov" id="play-ov">
<div class="play-circle"></div>
<div class="play-txt">تشغيل</div>
</div>
<!-- S0: COLD OPEN -->
<div class="scene" id="s0">
<div class="cold-ln" id="c0">🎲 ━━━━━━━━━━━━━━━━━━━━━━━━</div>
<div class="cold-ln" id="c1">♟ ━━━━━━━━━━━━━━━━━━━━━━━━</div>
<div class="cold-ln" id="c2">🁣 ━━━━━━━━━━━━━━━━━━━━━━━━</div>
</div>
<!-- S1: HOOK LINE 1 -->
<div class="scene" id="s1">
<div class="slam sz-med" id="t1">في ناس شايفة إن الألعاب الذهنية<br><span class="strike">حاجة قديمة.</span></div>
</div>
<!-- S2: HOOK LINE 2 -->
<div class="scene" id="s2">
<div class="slam sz-big" id="t2">الجديد؟</div>
</div>
<!-- S3: HOOK LINE 3 -->
<div class="scene" id="s3">
<div class="slam sz-med" id="t3">إنك تحطها في إيد<br><span class="gold">كل طفل في مصر.</span></div>
</div>
<!-- S4: PHONE REVEAL -->
<div class="scene" id="s4">
<div class="phone" id="ph1"><img src="assets/05-chess-start.png"></div>
</div>
<!-- S5: ده العب -->
<div class="scene" id="s5">
<div class="slam sz-huge" id="t5"><span class="gold">ده العب.</span></div>
</div>
<!-- S6: TRIPLE -->
<div class="scene" id="s-triple">
<div class="tri-phone" id="tr1"><img src="assets/06-chess-midgame.png"></div>
<div class="tri-phone" id="tr2"><img src="assets/08-ludo-game.png"></div>
<div class="tri-phone" id="tr3"><img src="assets/09-domino-menu.png"></div>
<div class="tri-label" id="tr-lbl">شطرنج &bull; لودو &bull; دومينو</div>
</div>
<!-- S7: ثواني -->
<div class="scene" id="s7">
<div class="slam sz-med" id="t7">من أول ما تفتح — لحد ما تلعب<br><span class="cyan">ثواني.</span></div>
</div>
<!-- S8: no ads -->
<div class="scene" id="s8">
<div class="slam sz-sm" id="t8">مفيش تسجيل معقد.<br>مفيش إعلان بيقطعك.<br><span class="gold">60 فريم. سلس.</span></div>
</div>
<!-- S9: مش لعبة -->
<div class="scene" id="s9">
<div class="slam sz-big" id="t9">مش لعبة <span class="strike">وخلاص.</span></div>
</div>
<!-- S10: PHONE chess midgame -->
<div class="scene" id="s10">
<div class="phone" id="ph2"><img src="assets/06-chess-midgame.png"></div>
</div>
<!-- S11: education -->
<div class="scene" id="s11">
<div class="slam sz-sm" id="t11">تعليم شطرنج مدمج.<br>ألغاز يومية. تصنيف.<br><span class="gold">الطفل بيلعب وهو بيتعلم.</span></div>
</div>
<!-- S12: بطولات -->
<div class="scene" id="s12">
<div class="slam sz-huge" id="t12"><span class="gold">بطولات.</span></div>
</div>
<!-- S13: BRACKET -->
<div class="scene" id="s13">
<div class="bracket-box" id="brk"></div>
<div class="slam sz-sm" id="t13" style="position:absolute;">مدارس &bull; جامعات &bull; أندية<br>شركات &bull; مراكز شباب &bull; أحياء</div>
</div>
<!-- S14: systems -->
<div class="scene" id="s14">
<div class="slam sz-med" id="t14">Swiss &bull; Knockout &bull; Arena<br><span class="gold">كل الأنظمة جاهزة.</span></div>
</div>
<!-- S15: auto -->
<div class="scene" id="s15">
<div class="slam sz-sm" id="t15">القرعة أوتوماتيك.<br>الجدول أوتوماتيك.<br>النتايج أوتوماتيك.<br><span class="cyan">البنية التحتية شغالة.</span></div>
</div>
<!-- S16: SHIELD -->
<div class="scene" id="s16">
<div class="shield-huge" id="shield">🛡️</div>
<div class="slam sz-sm" id="t16"><span class="green">مفيش chat مفتوح.</span><br>عبارات جاهزة. نظام إبلاغ.<br><span class="green">بيئة نضيفة. الأهل يطمنوا.</span></div>
</div>
<!-- S17: STATS -->
<div class="scene" id="s17" style="flex-direction:row; gap:8vw;">
<div class="st-item" id="st1"><div class="st-num">4</div><div class="st-lbl">ألعاب ذهنية</div></div>
<div class="st-item" id="st2"><div class="st-num">38</div><div class="st-lbl">module إدارة</div></div>
<div class="st-item" id="st3"><div class="st-num">100%</div><div class="st-lbl">مصري</div></div>
</div>
<!-- S18: 400M -->
<div class="scene" id="s18">
<div class="map-num" id="mapn">400M</div>
<div class="slam sz-med" id="t18">المنطقة العربية.<br><span class="gold">مفيهاش منافس محلي.</span></div>
</div>
<!-- S19: PUNCH -->
<div class="scene" id="s19">
<div class="slam sz-big" id="t19">السوق مفتوح.<br><span class="cyan">والمنتج جاهز.</span></div>
</div>
<!-- S20: CLOSER -->
<div class="scene" id="s20">
<img class="c-logo" id="clogo" src="assets/logof.png">
<div class="c-text" id="ctxt">الأطفال بتوعنا يستاهلوا أحسن من كده.</div>
<div class="c-gold" id="cgold">وده — أحسن من كده.</div>
</div>
</div>
<script>
const DUR = 120;
let t0, on = false, did = new Set(), actx;
// [time, actions...]
// show:id = activate scene, slam:id, cls:id:class, flash, flash-gold, bracket
const Q = [
// BEAT 1: COLD (0-5)
[0, 'show:s0', 'cls:c0:show'],
[1, 'cls:c1:show'],
[2, 'cls:c2:show'],
[3.5, 'cls:c0:out', 'cls:c1:out', 'cls:c2:out'],
// BEAT 2: HOOK (5-15)
[5, 'show:s1', 'flash', 'slam:t1'],
[9, 'show:s2', 'flash', 'slam:t2'],
[11, 'show:s3', 'flash', 'slam:t3'],
// BEAT 3: FIRST LOOK (15-25)
[15, 'show:s4', 'flash', 'cls:ph1:in'],
[19, 'show:s5', 'flash-gold', 'slam:t5'],
// BEAT 4: THREE GAMES (25-40)
[24, 'show:s-triple', 'flash', 'cls:tr1:in', 'cls:tr2:in', 'cls:tr3:in'],
[26, 'cls:tr-lbl:in'],
[30, 'show:s7', 'flash', 'slam:t7'],
[35, 'show:s8', 'flash', 'slam:t8'],
// BEAT 5: NOT JUST A GAME (40-55)
[40, 'show:s9', 'flash', 'slam:t9'],
[44, 'show:s10', 'flash', 'cls:ph2:in'],
[48, 'show:s11', 'flash', 'slam:t11'],
// BEAT 6: TOURNAMENTS (55-80)
[54, 'show:s12', 'flash-gold', 'slam:t12'],
[57, 'show:s13', 'flash', 'bracket'],
[60, 'slam:t13'],
[67, 'show:s14', 'flash', 'slam:t14'],
[73, 'show:s15', 'flash', 'slam:t15'],
// BEAT 7: SAFETY (80-90)
[80, 'show:s16', 'flash', 'cls:shield:in'],
[82, 'slam:t16'],
// BEAT 8: NUMBERS (90-105)
[90, 'show:s17', 'flash', 'cls:st1:in'],
[91.5, 'cls:st2:in'],
[93, 'cls:st3:in'],
[97, 'show:s18', 'flash-gold', 'cls:mapn:in'],
[99, 'slam:t18'],
// BEAT 9: PUNCH (105-112)
[105, 'show:s19', 'flash', 'slam:t19'],
// BEAT 10: CLOSER (112-120)
[112, 'show:s20', 'flash-gold', 'cls:clogo:in'],
[114, 'cls:ctxt:in'],
[116, 'cls:cgold:in'],
];
function tick() {
if (!on) return;
const e = (Date.now() - t0) / 1000;
document.getElementById('progress').style.width = (e / DUR * 100) + '%';
Q.forEach((q, i) => {
if (did.has(i) || e < q[0]) return;
did.add(i);
for (let k = 1; k < q.length; k++) {
const a = q[k];
if (a === 'flash') doFlash();
else if (a === 'flash-gold') doFlash(true);
else if (a === 'bracket') buildBracket();
else if (a.startsWith('show:')) {
document.querySelectorAll('.scene.active').forEach(s => s.classList.remove('active'));
document.getElementById(a.slice(5)).classList.add('active');
}
else if (a.startsWith('slam:')) {
const el = document.getElementById(a.slice(5));
el.classList.add('hit');
}
else if (a.startsWith('cls:')) {
const p = a.split(':');
document.getElementById(p[1]).classList.add(p[2]);
}
}
});
if (e < DUR) requestAnimationFrame(tick);
}
function doFlash(gold) {
const f = document.getElementById('flash');
f.classList.remove('fire', 'gold');
void f.offsetHeight;
if (gold) f.classList.add('gold');
f.classList.add('fire');
if (actx) hit();
}
function buildBracket() {
const c = document.getElementById('brk');
c.innerHTML = '';
const nodes = [[8,10],[8,30],[8,50],[8,70],[8,90],[30,20],[30,60],[30,80],[55,40],[55,70],[78,55],[95,55]];
const lines = [[8,12,22,2],[8,32,22,2],[8,52,22,2],[8,72,22,2],[8,92,22,2],[30,22,25,2],[30,62,25,2],[30,82,25,2],[55,42,23,2],[55,72,23,2],[78,57,17,2]];
lines.forEach((l,i) => {
const d = document.createElement('div');
d.className = 'bl';
d.style.cssText = `left:${l[0]}%;top:${l[1]}%;width:${l[2]}%;height:${l[3]}%;min-height:2px;`;
c.appendChild(d);
setTimeout(() => d.classList.add('in'), 200+i*120);
});
nodes.forEach((n,i) => {
const d = document.createElement('div');
d.className = 'bn';
d.style.cssText = `left:${n[0]}%;top:${n[1]}%;`;
c.appendChild(d);
setTimeout(() => d.classList.add('in'), i*100);
});
}
// ═══ AUDIO ═══
function initAudio() {
actx = new (window.AudioContext || window.webkitAudioContext)();
// Sub drone
const o = actx.createOscillator();
const g = actx.createGain();
o.type = 'sine'; o.frequency.value = 42;
g.gain.value = 0;
g.gain.linearRampToValueAtTime(0.07, actx.currentTime + 5);
g.gain.setValueAtTime(0.07, actx.currentTime + 110);
g.gain.linearRampToValueAtTime(0, actx.currentTime + 120);
o.connect(g); g.connect(actx.destination);
o.start(); o.stop(actx.currentTime + 120);
// Warm pad
const p = actx.createOscillator();
const pg = actx.createGain();
const pf = actx.createBiquadFilter();
p.type = 'triangle'; p.frequency.value = 110;
pf.type = 'lowpass'; pf.frequency.value = 250;
pg.gain.value = 0;
pg.gain.linearRampToValueAtTime(0.035, actx.currentTime + 8);
pg.gain.linearRampToValueAtTime(0.06, actx.currentTime + 55);
pg.gain.linearRampToValueAtTime(0.02, actx.currentTime + 112);
pg.gain.linearRampToValueAtTime(0, actx.currentTime + 120);
p.connect(pf); pf.connect(pg); pg.connect(actx.destination);
p.start(); p.stop(actx.currentTime + 120);
// LFO
const l = actx.createOscillator();
const lg = actx.createGain();
l.frequency.value = 0.07; lg.gain.value = 80;
l.connect(lg); lg.connect(pf.frequency);
l.start(); l.stop(actx.currentTime + 120);
}
function hit() {
// Bass thud
const o = actx.createOscillator();
const g = actx.createGain();
o.type = 'sine'; o.frequency.value = 75;
o.frequency.exponentialRampToValueAtTime(28, actx.currentTime + 0.12);
g.gain.value = 0.25;
g.gain.exponentialRampToValueAtTime(0.001, actx.currentTime + 0.25);
o.connect(g); g.connect(actx.destination);
o.start(); o.stop(actx.currentTime + 0.25);
// Click transient
const b = actx.createBuffer(1, actx.sampleRate*0.03, actx.sampleRate);
const d = b.getChannelData(0);
for(let i=0;i<d.length;i++) d[i]=(Math.random()*2-1)*(1-i/d.length);
const s = actx.createBufferSource(); s.buffer = b;
const sg = actx.createGain(); sg.gain.value = 0.12;
s.connect(sg); sg.connect(actx.destination); s.start();
}
// ═══ PARTICLES ═══
function initPtcl() {
const c = document.getElementById('particles');
const cols = ['rgba(228,172,56,0.35)','rgba(32,130,240,0.25)','rgba(0,255,255,0.2)'];
for(let i=0;i<20;i++){
const p = document.createElement('div');
p.className = 'ptcl';
const sz = 2+Math.random()*4;
Object.assign(p.style, {
width:sz+'px', height:sz+'px',
left:Math.random()*100+'%',
background:cols[i%3],
animationDuration:(14+Math.random()*16)+'s',
animationDelay:(Math.random()*20)+'s'
});
c.appendChild(p);
}
}
// ═══ GO ═══
document.getElementById('play-ov').addEventListener('click', () => {
document.getElementById('play-ov').classList.add('gone');
initAudio(); initPtcl();
t0 = Date.now(); on = true;
requestAnimationFrame(tick);
});
</script>
</body>
</html>
......@@ -74,17 +74,17 @@ function renderTabBar() {
rank: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3 7h7l-5.5 4.5 2 7L12 16l-6.5 4.5 2-7L2 9h7z"/></svg>',
social: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M16 11c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3zm-8 0c1.66 0 3-1.34 3-3S9.66 5 8 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>',
play: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>',
shop: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M7 18c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></svg>',
tournaments: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19 5h-2V3H7v2H5c-1.1 0-2 .9-2 2v1c0 2.55 1.92 4.63 4.39 4.94.63 1.5 1.98 2.63 3.61 2.96V19H7v2h10v-2h-4v-3.1c1.63-.33 2.98-1.46 3.61-2.96C19.08 12.63 21 10.55 21 8V7c0-1.1-.9-2-2-2zM5 8V7h2v3.82C5.84 10.4 5 9.3 5 8zm14 0c0 1.3-.84 2.4-2 2.82V7h2v1z"/></svg>',
profile: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>'
};
const icons = {
rank: assetImg('tab_rank', svgFallback.rank, 22, 22),
social: assetImg('tab_social', svgFallback.social, 22, 22),
play: assetImg('tab_play', svgFallback.play, 22, 22),
shop: assetImg('tab_shop', svgFallback.shop, 22, 22),
tournaments: assetImg('tab_tournaments', svgFallback.tournaments, 22, 22),
profile: assetImg('tab_profile', svgFallback.profile, 22, 22),
};
const labels = { rank: 'nav.rank', social: 'nav.social', play: 'nav.play', shop: 'nav.shop', profile: 'nav.profile' };
const labels = { rank: 'nav.rank', social: 'nav.social', play: 'nav.play', tournaments: 'nav.tournaments', profile: 'nav.profile' };
const current = scene.getCurrentWorld();
tabBar.innerHTML = worlds.map(w => `
......
......@@ -6,7 +6,7 @@ const strings = {
'nav.rank': 'الترتيب',
'nav.social': 'الأصدقاء',
'nav.play': 'العب',
'nav.shop': 'المتجر',
'nav.tournaments': 'البطولات',
'nav.profile': 'حسابي',
'auth.login': 'تسجيل الدخول',
'auth.register': 'حساب جديد',
......@@ -75,7 +75,7 @@ const strings = {
'nav.rank': 'Rank',
'nav.social': 'Social',
'nav.play': 'Play',
'nav.shop': 'Shop',
'nav.tournaments': 'Tournaments',
'nav.profile': 'Profile',
'auth.login': 'Login',
'auth.register': 'Register',
......
import * as bus from './bus.js';
import * as store from './store.js';
const worlds = ['rank', 'social', 'play', 'shop', 'profile'];
const sceneStacks = { rank: [], social: [], play: [], shop: [], profile: [] };
const worlds = ['rank', 'social', 'play', 'tournaments', 'profile'];
const sceneStacks = { rank: [], social: [], play: [], tournaments: [], profile: [] };
const sceneRegistry = {};
let currentWorld = 'play';
let container = null;
......
......@@ -24,7 +24,7 @@ async function boot() {
scene.setRoot('play', 'play-table');
scene.setRoot('rank', 'leaderboard');
scene.setRoot('social', 'friends');
scene.setRoot('shop', 'shop-browse');
scene.setRoot('tournaments', 'tournaments-hub');
scene.setRoot('profile', 'profile-view');
// Check for active match to resume (tab refresh / re-entry recovery)
......@@ -73,7 +73,7 @@ function onAuthSuccess() {
scene.setRoot('play', 'play-table');
scene.setRoot('rank', 'leaderboard');
scene.setRoot('social', 'friends');
scene.setRoot('shop', 'shop-browse');
scene.setRoot('tournaments', 'tournaments-hub');
scene.setRoot('profile', 'profile-view');
scene.switchWorld('play');
tournamentSession.init();
......@@ -101,7 +101,7 @@ async function loadModules() {
await import('./modules/ludo/mod.js');
await import('./modules/rank/mod.js');
await import('./modules/social/mod.js');
await import('./modules/shop/mod.js');
await import('./modules/tournaments/mod.js');
await import('./modules/profile/mod.js');
await import('./modules/rewards/mod.js');
await import('./modules/puzzles/mod.js');
......
......@@ -22,18 +22,22 @@ let boardInstance = null;
function loadPieceImages() {
if (pieceImagesLoaded) return;
pieceImagesLoaded = true;
let anyFound = false;
let pending = 0;
for (const [piece, slot] of Object.entries(PIECE_ASSET_MAP)) {
const url = getAsset(slot);
if (url) {
anyFound = true;
if (pieceImages[piece]?.src === url) continue;
const img = new Image();
pending++;
img.onload = () => { if (--pending === 0 && boardInstance) boardInstance.render(); };
img.onerror = () => { --pending; };
img.src = url;
pieceImages[piece] = img;
}
}
if (anyFound) pieceImagesLoaded = true;
}
const PIECE_PATHS = {
......
import * as scene from '../../core/scene.js';
import { mountTournamentsHub } from './scenes/hub.js';
import { mountTournamentDetail } from './scenes/detail.js';
import { mountTournamentBracket } from './scenes/bracket.js';
import { mountTournamentArena } from './scenes/arena.js';
import { mountTournamentLobby } from './scenes/lobby.js';
import { mountTournamentLive } from './scenes/live.js';
scene.register('tournaments-hub', mountTournamentsHub);
scene.register('tournament-detail', mountTournamentDetail);
scene.register('tournament-bracket', mountTournamentBracket);
scene.register('tournament-arena', mountTournamentArena);
scene.register('tournament-lobby', mountTournamentLobby);
scene.register('tournament-live', mountTournamentLive);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment