This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,194 @@
|
||||
/* ============================================================
|
||||
AnwarPi — shared motion & brand layer
|
||||
Loaded after each page's inline styles; tokens stay in-page.
|
||||
============================================================ */
|
||||
|
||||
/* ---- Shared palette: cool light on deep blue-black ---- */
|
||||
:root {
|
||||
--bg: oklch(16% 0.03 225);
|
||||
--surface: oklch(22% 0.04 220);
|
||||
--fg: oklch(96% 0.015 210);
|
||||
--muted: oklch(74% 0.03 215);
|
||||
--border: oklch(36% 0.05 215);
|
||||
--accent: oklch(78% 0.17 190);
|
||||
}
|
||||
|
||||
/* ---- Shared responsive safeguards ---- */
|
||||
html, body { max-width: 100%; overflow-x: clip; }
|
||||
img, svg, video { height: auto; }
|
||||
main, section, .container, .grid, [class*="grid"], [class*="layout"] { min-width: 0; }
|
||||
code, pre, bdi { overflow-wrap: anywhere; }
|
||||
pre, table { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
|
||||
:focus-visible { outline-color: var(--accent); }
|
||||
|
||||
@media (pointer: coarse) {
|
||||
a, button, input, select, textarea, summary, .btn, .icon-btn { min-height: 44px; }
|
||||
}
|
||||
|
||||
@media (max-width: 920px) {
|
||||
:root { --gutter: 22px; }
|
||||
.container { width: 100%; }
|
||||
|
||||
/* Keep primary navigation visible on phones and tablets. */
|
||||
.topnav .nav-inner,
|
||||
.topnav .topnav-inner {
|
||||
flex-wrap: wrap;
|
||||
gap: 0 16px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.topnav .logo,
|
||||
.topnav .nav-tools { margin-bottom: 10px; }
|
||||
.topnav .logo { min-height: 50px; }
|
||||
input, select, textarea { min-height: 48px; font-size: 16px; }
|
||||
.footer-col a { min-height: 44px; display: flex; align-items: center; }
|
||||
.section-head { gap: 20px; }
|
||||
.demo-strip { padding-inline: var(--gutter); }
|
||||
.table-wrap, .billing-table { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
|
||||
.topnav .nav-links,
|
||||
.topnav .topnav-inner > nav {
|
||||
display: flex;
|
||||
order: 3;
|
||||
flex: 0 0 calc(100% + (var(--gutter) * 2));
|
||||
gap: 8px;
|
||||
margin-inline: calc(var(--gutter) * -1);
|
||||
padding-inline: var(--gutter);
|
||||
overflow-x: auto;
|
||||
overscroll-behavior-inline: contain;
|
||||
scrollbar-width: none;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
.topnav .nav-links::-webkit-scrollbar,
|
||||
.topnav .topnav-inner > nav::-webkit-scrollbar { display: none; }
|
||||
.topnav .nav-links a,
|
||||
.topnav .topnav-inner > nav a {
|
||||
flex: none;
|
||||
min-height: 52px;
|
||||
padding-inline: 12px;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
.topnav a[aria-current="page"] {
|
||||
position: relative;
|
||||
color: var(--fg);
|
||||
font-weight: 700;
|
||||
}
|
||||
.topnav a[aria-current="page"]::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset-inline: 12px;
|
||||
bottom: 5px;
|
||||
height: 3px;
|
||||
border-radius: 999px;
|
||||
background: var(--accent);
|
||||
}
|
||||
.topnav .nav-links + .nav-tools .menu-trigger,
|
||||
.topnav .topnav-inner > nav + .nav-tools .menu-trigger { display: none; }
|
||||
|
||||
.menu-drawer { width: min(520px, 92vw); }
|
||||
.dialog-card, .modal-card { max-width: calc(100vw - 36px); max-height: calc(100dvh - 36px); overflow-y: auto; }
|
||||
}
|
||||
|
||||
@media (max-width: 620px) {
|
||||
:root { --gutter: 18px; }
|
||||
body { line-height: 1.7; }
|
||||
h1 { font-size: clamp(38px, 11vw, 50px) !important; overflow-wrap: normal; word-break: normal; }
|
||||
h2 { overflow-wrap: normal; word-break: normal; }
|
||||
.topnav .logo { font-size: 20px; }
|
||||
.topnav .nav-return { display: inline-flex !important; min-height: 44px; align-items: center; font-size: 13px; padding-inline: 4px; }
|
||||
.topnav .nav-inner,
|
||||
.topnav .topnav-inner { gap: 0 10px; }
|
||||
.topnav .nav-links,
|
||||
.topnav .topnav-inner > nav { gap: 4px; }
|
||||
.topnav .nav-links a,
|
||||
.topnav .topnav-inner > nav a { padding-inline: 10px; }
|
||||
.hero-cta, .dialog-actions, .form-actions { align-items: stretch; gap: 12px; }
|
||||
.hero-cta .btn, .dialog-actions .btn, .form-actions .btn { width: 100%; }
|
||||
.pagefoot { padding-block: 40px; }
|
||||
.footer-bottom { gap: 12px; }
|
||||
.card, .membership-card, .paywall, .plan, .team-contact { padding-inline: 20px; }
|
||||
.dialog-card, .modal-card, .dialog-panel, .modal-panel { max-width: calc(100vw - 28px); }
|
||||
}
|
||||
|
||||
/* ---- Brand mark: real logo image replaces the geometric span ---- */
|
||||
img.logo-mark {
|
||||
width: 30px;
|
||||
height: 34px;
|
||||
object-fit: contain;
|
||||
transform: none;
|
||||
background: none;
|
||||
border-radius: 4px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
/* ---- Text selection on dark canvas ---- */
|
||||
::selection { background: color-mix(in oklch, var(--accent) 28%, transparent); }
|
||||
|
||||
/* ---- Button hover: light-sweep + lift (AnwarPi "light" motif) ---- */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.btn, .btn-primary, .btn-secondary, .btn-ghost, .icon-btn {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: transform .18s ease, box-shadow .18s ease, background-color .18s ease, border-color .18s ease, color .18s ease;
|
||||
}
|
||||
.btn::after, .btn-primary::after, .btn-secondary::after, .btn-ghost::after, .icon-btn::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset-block: 0;
|
||||
inset-inline-start: -45%;
|
||||
width: 34%;
|
||||
background: linear-gradient(100deg, transparent 0%, rgba(255, 255, 255, 0.45) 50%, transparent 100%);
|
||||
transform: skewX(-18deg);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.btn:hover::after, .btn-primary:hover::after, .btn-secondary:hover::after,
|
||||
.btn-ghost:hover::after, .icon-btn:hover::after {
|
||||
animation: anw-sweep 0.85s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.btn:hover, .btn-primary:hover, .btn-secondary:hover, .btn-ghost:hover, .icon-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.btn-primary:hover, .btn:hover {
|
||||
box-shadow: 0 8px 28px color-mix(in oklch, var(--accent) 30%, transparent);
|
||||
}
|
||||
.btn:active, .btn-primary:active, .btn-secondary:active, .btn-ghost:active, .icon-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes anw-sweep {
|
||||
0% { opacity: 1; inset-inline-start: -45%; }
|
||||
100% { opacity: 1; inset-inline-start: 118%; }
|
||||
}
|
||||
|
||||
/* ---- Scroll reveal (driven by anwarpi.js) ---- */
|
||||
[data-anw] {
|
||||
transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.anw-anim [data-anw]:not(.anw-in) {
|
||||
opacity: 0;
|
||||
transform: translateY(18px);
|
||||
}
|
||||
|
||||
/* ---- WebGL canvas sits behind everything, above the root background ---- */
|
||||
#anwarpi-bg {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.9s ease;
|
||||
}
|
||||
|
||||
/* ---- Hero brand mark: the beam logo, black field dissolved into the canvas ---- */
|
||||
.hero-logo {
|
||||
display: block;
|
||||
width: 68px;
|
||||
height: auto;
|
||||
border-radius: 6px;
|
||||
mix-blend-mode: screen; /* drops the jpg's black field — only the beams glow */
|
||||
margin-block-end: 26px;
|
||||
}
|
||||
|
||||
/* ---- Dark-theme correction: scrims must dim with black, not --fg (now white) ---- */
|
||||
.dialog-scrim, .menu-scrim, .modal { background: rgba(5, 5, 5, 0.68); }
|
||||
@@ -0,0 +1,215 @@
|
||||
/* AnwarPi — WebGL light-beam background + scroll reveal.
|
||||
Loads deferred on every page; no-ops gracefully if WebGL is unavailable. */
|
||||
(function () {
|
||||
"use strict";
|
||||
var reduced = false;
|
||||
try { reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches; } catch (e) {}
|
||||
var staticMode = false;
|
||||
try { staticMode = new URLSearchParams(location.search).has("static"); } catch (e) {}
|
||||
|
||||
/* ================= WebGL beam field ================= */
|
||||
function initWebGL() {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.id = "anwarpi-bg";
|
||||
canvas.setAttribute("aria-hidden", "true");
|
||||
var first = document.body.firstChild;
|
||||
if (first) document.body.insertBefore(canvas, first);
|
||||
else document.body.appendChild(canvas);
|
||||
|
||||
var gl = canvas.getContext("webgl", { antialias: false, alpha: false, depth: false, stencil: false })
|
||||
|| canvas.getContext("experimental-webgl");
|
||||
if (!gl) { canvas.parentNode.removeChild(canvas); return; }
|
||||
|
||||
var vertSrc = "attribute vec2 p;void main(){gl_Position=vec4(p,0.0,1.0);}";
|
||||
var fragSrc = [
|
||||
"precision highp float;",
|
||||
"uniform float uTime;",
|
||||
"uniform vec2 uRes;",
|
||||
"uniform vec2 uMouse;",
|
||||
"float hash(float n){return fract(sin(n*127.1)*43758.5453);}",
|
||||
"void main(){",
|
||||
" vec2 uv = gl_FragCoord.xy / uRes;", /* y: 0 bottom -> 1 top */
|
||||
" float t = uTime * 0.06;",
|
||||
" float v = 0.0;",
|
||||
/* vertical light beams — the AnwarPi "A" ray motif */
|
||||
" for(int i = 0; i < 7; i++){",
|
||||
" float fi = float(i);",
|
||||
" float x = 0.08 + 0.84 * hash(fi + 1.0) + 0.05 * sin(t * 0.7 + fi * 2.4);",
|
||||
" x += (uMouse.x - x) * 0.16;",
|
||||
" float w = 0.016 + 0.05 * hash(fi + 7.0);",
|
||||
" float beam = exp(-pow((uv.x - x) / w, 2.0));",
|
||||
" float flick = 0.55 + 0.45 * sin(t * 1.1 + fi * 1.7);",
|
||||
" flick *= 1.0 + 0.55 * exp(-pow((uv.x - uMouse.x) / 0.24, 2.0));",
|
||||
" float fade = smoothstep(1.0, 0.12, uv.y);", /* brighter toward the bottom, like the mark */
|
||||
" v += beam * flick * fade * (0.10 + 0.17 * hash(fi + 13.0));",
|
||||
" }",
|
||||
/* cursor halo — the light follows the visitor */
|
||||
" v += 0.11 * exp(-length((uv - uMouse) * vec2(1.0, 0.9)) * 4.5);",
|
||||
/* broad floor glow + soft vignette */
|
||||
" v += 0.15 * exp(-uv.y * 2.4);",
|
||||
" v *= smoothstep(1.3, 0.55, uv.y);",
|
||||
" v = clamp(v, 0.0, 1.0);",
|
||||
/* deep blue-black base, aligned with the shared --bg token */
|
||||
" vec3 base = vec3(0.0005, 0.0618, 0.0896);",
|
||||
" gl_FragColor = vec4(base + vec3(v), 1.0);",
|
||||
"}"
|
||||
].join("\n");
|
||||
|
||||
function compile(type, src) {
|
||||
var s = gl.createShader(type);
|
||||
gl.shaderSource(s, src);
|
||||
gl.compileShader(s);
|
||||
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) { gl.deleteShader(s); return null; }
|
||||
return s;
|
||||
}
|
||||
var vs = compile(gl.VERTEX_SHADER, vertSrc);
|
||||
var fs = compile(gl.FRAGMENT_SHADER, fragSrc);
|
||||
if (!vs || !fs) { canvas.parentNode.removeChild(canvas); return; }
|
||||
var prog = gl.createProgram();
|
||||
gl.attachShader(prog, vs); gl.attachShader(prog, fs); gl.linkProgram(prog);
|
||||
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) { canvas.parentNode.removeChild(canvas); return; }
|
||||
gl.useProgram(prog);
|
||||
|
||||
var buf = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
|
||||
var loc = gl.getAttribLocation(prog, "p");
|
||||
gl.enableVertexAttribArray(loc);
|
||||
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
|
||||
var uTime = gl.getUniformLocation(prog, "uTime");
|
||||
var uRes = gl.getUniformLocation(prog, "uRes");
|
||||
var uMouse = gl.getUniformLocation(prog, "uMouse");
|
||||
|
||||
/* ---- pointer reaction: beams lean toward the cursor, halo follows ---- */
|
||||
var mouseTX = 0.5, mouseTY = 0.55, mouseX = 0.5, mouseY = 0.55;
|
||||
if (!reduced && !staticMode) {
|
||||
window.addEventListener("pointermove", function (e) {
|
||||
if (e.pointerType === "touch") return;
|
||||
mouseTX = e.clientX / window.innerWidth;
|
||||
mouseTY = 1 - e.clientY / window.innerHeight;
|
||||
}, { passive: true });
|
||||
document.addEventListener("pointerleave", function () {
|
||||
mouseTX = 0.5; mouseTY = 0.55;
|
||||
});
|
||||
}
|
||||
gl.uniform2f(uMouse, mouseX, mouseY);
|
||||
|
||||
var DPR_CAP = 1.5;
|
||||
function resize() {
|
||||
var dpr = Math.min(window.devicePixelRatio || 1, DPR_CAP);
|
||||
var w = Math.max(1, Math.floor(window.innerWidth * dpr));
|
||||
var h = Math.max(1, Math.floor(window.innerHeight * dpr));
|
||||
if (canvas.width !== w || canvas.height !== h) {
|
||||
canvas.width = w; canvas.height = h;
|
||||
gl.viewport(0, 0, w, h);
|
||||
}
|
||||
gl.uniform2f(uRes, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
var start = (window.performance && performance.now()) || 0;
|
||||
function draw(now) {
|
||||
mouseX += (mouseTX - mouseX) * 0.07;
|
||||
mouseY += (mouseTY - mouseY) * 0.07;
|
||||
gl.uniform1f(uTime, (now - start) / 1000);
|
||||
gl.uniform2f(uMouse, mouseX, mouseY);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
}
|
||||
|
||||
resize();
|
||||
draw(start); /* paint one frame synchronously so screenshots capture the field */
|
||||
|
||||
/* ---- article reading mode: fade the field down once past the header ---- */
|
||||
if (document.body.hasAttribute("data-anw-dim")) {
|
||||
var first = document.querySelector("main") && document.querySelector("main").firstElementChild;
|
||||
var dimFrom = first ? first.offsetHeight : 420;
|
||||
var onScroll = function () {
|
||||
canvas.style.opacity = (window.scrollY || 0) > dimFrom ? "0.22" : "1";
|
||||
};
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
onScroll();
|
||||
}
|
||||
|
||||
if (reduced || staticMode) {
|
||||
window.addEventListener("resize", function () { resize(); draw(start); });
|
||||
return;
|
||||
}
|
||||
var running = true;
|
||||
function loop(now) {
|
||||
if (running) { draw(now); }
|
||||
window.requestAnimationFrame(loop);
|
||||
}
|
||||
window.requestAnimationFrame(loop);
|
||||
document.addEventListener("visibilitychange", function () {
|
||||
running = !document.hidden;
|
||||
if (running) start = (window.performance && performance.now()) - 0; /* keep phase simple */
|
||||
});
|
||||
}
|
||||
|
||||
/* ================= Scroll reveal ================= */
|
||||
function initReveal() {
|
||||
if (reduced || staticMode) return;
|
||||
var vh = window.innerHeight || 800;
|
||||
var candidates = document.querySelectorAll(
|
||||
"main section > .container > *, main > .container > *, main .container > .grid > *, main .grid > *"
|
||||
);
|
||||
var seen = {};
|
||||
var targets = [];
|
||||
for (var i = 0; i < candidates.length; i++) {
|
||||
var el = candidates[i];
|
||||
if (seen[el.anwId]) continue;
|
||||
if (el.closest(".menu-panel, .modal, dialog")) continue;
|
||||
var r = el.getBoundingClientRect();
|
||||
if (r.top < vh * 0.92) continue; /* above the fold: show instantly, never hide */
|
||||
el.anwId = "anw" + i;
|
||||
seen[el.anwId] = 1;
|
||||
el.setAttribute("data-anw", "");
|
||||
el.style.transitionDelay = ((i % 3) * 70) + "ms";
|
||||
targets.push(el);
|
||||
}
|
||||
if (!targets.length) return;
|
||||
document.documentElement.classList.add("anw-anim");
|
||||
/* Force reveal after a safety window so nothing can stay hidden */
|
||||
window.setTimeout(function () {
|
||||
targets.forEach(function (el) { el.classList.add("anw-in"); });
|
||||
}, 3000);
|
||||
if (!("IntersectionObserver" in window)) return;
|
||||
var io = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add("anw-in");
|
||||
io.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.1, rootMargin: "0px 0px -8% 0px" });
|
||||
targets.forEach(function (el) { io.observe(el); });
|
||||
}
|
||||
|
||||
/* ================= Current page in mobile navigation ================= */
|
||||
function initActiveNav() {
|
||||
var topnav = document.querySelector(".topnav");
|
||||
if (!topnav) return;
|
||||
|
||||
var page = (location.pathname.split("/").pop() || "index.html").toLowerCase();
|
||||
var selector = {
|
||||
"index.html": ".logo",
|
||||
"arabic-ai-learning-catalog.html": "nav a[href^='#catalog']",
|
||||
"arabic-ai-learning-catalog-v2.html": "nav a[href^='#catalog']",
|
||||
"article-agent-guide.html": "nav a[href='article-agent-guide.html']",
|
||||
"post-locked.html": "nav a[href='article-agent-guide.html']",
|
||||
"post-unlocked.html": "nav a[href='article-agent-guide.html']",
|
||||
"subscription.html": "nav a[href='#exclusive-guides']",
|
||||
"vip-onboarding.html": "nav a[href='subscription.html']",
|
||||
"account-settings.html": ".nav-account",
|
||||
"login.html": ".logo",
|
||||
"forgot-password.html": ".logo",
|
||||
"privacy.html": "nav a[href='privacy.html']"
|
||||
}[page];
|
||||
|
||||
var active = selector && topnav.querySelector(selector);
|
||||
if (active) active.setAttribute("aria-current", "page");
|
||||
}
|
||||
|
||||
function boot() { initActiveNav(); initWebGL(); initReveal(); }
|
||||
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", boot);
|
||||
else boot();
|
||||
})();
|
||||
Reference in New Issue
Block a user