/* GENERATED by scripts/extract-blog-css.mjs — do not edit by hand.
 *
 * The blog's design, lifted out of the inline <style> blocks of the static
 * pages. Identical rule sets appearing in more than one source are kept once.
 */

/* --- from blog.html --- */
:root{
  --bg:var(--page); --bg2:var(--surface); --card:var(--surface); --card2:var(--page);
  --line:var(--rule); --txt:var(--ink); --mut:var(--ink-soft);
  --accent:var(--teal); --accent2:var(--teal-bright); --glow:rgba(var(--teal-rgb),.18);
  --accent-ink:#fff;
  --grn:var(--teal); --mono:"JetBrains Mono",monospace;
  --r:16px; --r-sm:12px;
  --focus:var(--teal);
  --ease:cubic-bezier(.2,.7,.2,1);
  --acc:var(--teal); --acc2:var(--coral);
  --s1:4px;--s2:8px;--s3:12px;--s4:16px;--s5:24px;--s6:32px;--s7:48px;--s8:64px;--s9:96px;
}
/* --accent2 carries small text (kickers, chips, typing line); #7fe8bd was 2.63:1 on the cream
   paper — below the 4.5 AA floor. The decorative uses keep the brighter tone. */


/* Qomraa Arabic — the brand face, self-hosted so it never depends on a CDN */
@font-face{font-family:"Qomraa";src:url("/fonts/itfQomraArabic-Light.woff2") format("woff2");
  font-weight:300;font-style:normal;font-display:swap}
@font-face{font-family:"Qomraa";src:url("/fonts/itfQomraArabic-Regular.woff2") format("woff2");
  font-weight:400 500;font-style:normal;font-display:swap}
@font-face{font-family:"Qomraa";src:url("/fonts/itfQomraArabic-Bold.woff2") format("woff2");
  font-weight:600 800;font-style:normal;font-display:swap}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;overflow-x:clip}
body{background:var(--bg);color:var(--txt);font-family:'Instrument Sans',system-ui,sans-serif;line-height:1.7;overflow-x:clip}
:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:6px;transition:none}
:focus:not(:focus-visible){outline:none}
.skip{position:fixed;inset-inline-start:16px;top:12px;z-index:100;background:var(--accent);color:var(--accent-ink);
  padding:10px 18px;border-radius:10px;font-weight:700;font-size:14px;
  transform:translateY(-200%);transition:transform .2s var(--ease)}
.skip:focus{transform:none}
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
html[lang="ar"] body{font-family:Qomraa,Tajawal,'Instrument Sans',system-ui,sans-serif}
h1,h2{font-family:var(--display),Georgia,serif}
h3{font-family:'Instrument Sans',sans-serif}
html[lang="ar"] h1,html[lang="ar"] h2,html[lang="ar"] h3{font-family:Qomraa,Tajawal,sans-serif}
a{color:inherit;text-decoration:none}
.wrap{max-width:1180px;margin:0 auto;padding:0 24px}


section{padding:100px 0;position:relative}
h2{font-size:clamp(30px,4vw,46px);font-weight:800;line-height:1.2}

body::before{content:"";position:fixed;inset:0;z-index:-2;background:
  linear-gradient(rgba(var(--ink-rgb),.035) 1px,transparent 1px),
  linear-gradient(90deg,rgba(var(--ink-rgb),.035) 1px,transparent 1px);
  background-size:44px 44px}




/* NAV CTA — shared with home */
.nav-cta{display:inline-flex;align-items:center;background:var(--accent);color:var(--accent-ink);font-weight:700;
  font-size:14px;padding:11px 20px;border-radius:var(--r);transition:.25s;white-space:nowrap}
.nav-cta:hover{background:var(--accent2);transform:translateY(-2px);box-shadow:0 12px 34px var(--glow)}
.nav-cta .nc-short{display:none}
@media(max-width:760px){.nav-cta .nc-full{display:none}.nav-cta .nc-short{display:inline}}

/* NAV */
nav{position:fixed;top:16px;left:50%;transform:translateX(-50%);z-index:50;width:min(1180px,94%);
  background:rgba(252,248,239,.88);backdrop-filter:blur(16px);border:1px solid var(--line);border-radius:14px;
  display:flex;align-items:center;justify-content:space-between;padding:12px 18px;transition:.3s}
nav.scrolled{border-color:rgba(var(--teal-rgb),.3);box-shadow:0 12px 32px rgba(var(--shadow-rgb),.14)}



@keyframes blink{50%{opacity:0}}
.brand{display:flex;align-items:center;gap:11px}
.brand-pic{width:38px;height:38px;border-radius:50%;overflow:hidden;border:2px solid var(--accent);flex:none;
  box-shadow:0 1px 2px rgba(0,0,0,.28);transition:.3s}
.brand:hover .brand-pic{transform:scale(1.08) rotate(-4deg)}
.brand-pic img{width:100%;height:100%;object-fit:cover}
.brand-name{font-family:'Instrument Sans',Qomraa,Tajawal;font-weight:600;font-size:16px;white-space:nowrap}
.brand-name b{color:var(--accent)}
/* floating pill nav — the active section gets its own filled sub-pill */
nav ul{display:flex;align-items:center;gap:2px;list-style:none;
  background:var(--card2);border:1px solid var(--line);border-radius:999px;padding:4px}
/* "~/about" is a path token — force the whole link LTR so RTL can't flip "~/" into "/~" */
nav ul a{color:var(--mut);font-size:13.5px;font-family:var(--mono);transition:color .2s,background .25s;
  direction:ltr;unicode-bidi:isolate;display:inline-block;padding:7px 13px;border-radius:999px}
nav ul a::before{content:"~/";color:var(--accent);opacity:.7;transition:color .25s}
nav ul a:hover{color:var(--txt);background:rgba(150,150,160,.13)}
nav ul a[aria-current="true"]{background:var(--accent);color:var(--accent-ink)}
nav ul a[aria-current="true"]::before{color:var(--accent-ink);opacity:.75}
.menu-btn{display:none;width:38px;height:32px;border-radius:10px;border:1px solid var(--line);
  background:var(--card2);color:var(--txt);cursor:pointer;align-items:center;justify-content:center;flex:none;transition:.25s}
.menu-btn svg{width:17px;height:17px}
.menu-btn:hover{border-color:var(--accent);color:var(--accent)}
.menu-btn .x{display:none}
.menu-btn[aria-expanded="true"] .x{display:block}
.menu-btn[aria-expanded="true"] .bars{display:none}
.btn{display:inline-flex;align-items:center;gap:8px;background:var(--accent);color:var(--accent-ink);font-weight:700;font-size:14px;
  padding:11px 20px;border-radius:12px;border:0;cursor:pointer;transition:.25s;position:relative;overflow:hidden}
.btn::after{content:"";position:absolute;inset:0;background:linear-gradient(110deg,transparent 30%,rgba(255,255,255,.25) 50%,transparent 70%);transform:translateX(-100%);transition:.5s}
.btn:hover{background:var(--accent2);transform:translateY(-2px);box-shadow:0 12px 34px var(--glow)}
.btn:hover::after{transform:translateX(100%)}
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--txt)}
.btn.ghost:hover{border-color:var(--accent);color:var(--accent);box-shadow:none}

/* switch (theme) + lang toggle */
.switch{position:relative;width:66px;height:32px;border-radius:999px;cursor:pointer;flex:none;
  background:var(--card2);border:1px solid var(--line);transition:.35s;padding:0}
.switch .knob{position:absolute;top:3px;left:3px;width:24px;height:24px;border-radius:50%;
  background:var(--accent);display:flex;align-items:center;justify-content:center;color:var(--accent-ink);
  transition:transform .4s var(--ease),background .35s;box-shadow:0 3px 10px var(--glow)}
.switch .knob svg{width:13px;height:13px}
.switch .tk{position:absolute;top:50%;transform:translateY(-50%);color:var(--mut);transition:opacity .35s}
.switch .tk svg{width:12px;height:12px;display:block}
.switch .tk.l{left:9px}.switch .tk.r{right:9px}
/* the knob parks over the ACTIVE icon; the uncovered one is what you'd switch to */
.switch .tk.l{opacity:0}
.switch.on .tk.l{opacity:1}
.switch.on .tk.r{opacity:0}
.switch.on .knob{transform:translateX(34px)}
.switch:hover{border-color:var(--accent)}
.lang-btn{height:32px;border-radius:999px;border:1px solid var(--line);background:var(--card2);color:var(--mut);
  font-family:var(--mono);font-size:12.5px;font-weight:600;letter-spacing:.04em;padding:0 13px;cursor:pointer;
  transition:.25s;flex:none;direction:ltr}
.lang-btn:hover{color:var(--accent);border-color:var(--accent)}
/* below 1180px the pill is the first thing to give — never let the bar wrap */
@media(max-width:1180px){
  nav ul a{padding:7px 10px;font-size:12.5px}
  nav ul{gap:0}
}

/* HERO */





/* keep ">" from bidi-mirroring into "<" and keep the caret glued to the text end */




.hero .tag{color:var(--mut);margin:18px 0 8px;max-width:560px;font-size:16.5px;line-height:1.75}




@keyframes pulse{50%{opacity:.4}}






/* --orb must clear the code card (half-width 200px, half-height ~206px) or the icons get bisected */








@keyframes spin{to{transform:rotate(360deg)}}

@keyframes float{50%{transform:translateY(-12px)}}
/* HERO PORTRAIT — cutout on a target halo, flanked by code brackets.
   Only renders once shots/hero-portrait.png loads; until then the code card stands in.
   Needs a TRANSPARENT PNG cutout — a photo with its own background defeats the halo. */



/* Full colour. The cutout has no box to put a border on, so stacked zero-offset
   drop-shadows trace the alpha silhouette — that IS the border — over a deep cast shadow. */


@media(hover:hover) and (pointer:fine){
  .avatar-wrap:hover .portrait img{filter:
    drop-shadow(0 0 1.5px rgba(var(--teal-rgb),.85))
    drop-shadow(0 0 14px rgba(var(--teal-rgb),.22))
    drop-shadow(0 26px 42px rgba(0,0,0,.62))}
}
/* soft bloom instead of a hard target ring — the portrait dissolves into the page */


/* PHOTO MODE — a real photo that carries its own background: framed, not haloed.
   (Cutout mode above still wins if a transparent shots/hero-portrait.png is added later.) */




/* accent bloom behind the frame, standing in for the halo */

/* physical left/right — the brackets must stay < … > and not mirror to > … < in RTL */


.chev.l{left:-26px}
.chev.r{right:-26px}
/* framed mode: the frame is opaque, so the brackets must clear it entirely */

html[data-portrait="photo"] .chev.l{left:-58px}
html[data-portrait="photo"] .chev.r{right:-58px}
@media(max-width:1100px){
  html[data-portrait="photo"] .chev.l{left:-44px}
  html[data-portrait="photo"] .chev.r{right:-44px}
  .chev svg{width:52px;height:52px}
}
/* below 600px there isn't room for the brackets to sit outside the portrait without
   being half-clipped by the viewport edge — drop them rather than show them cut */
@media(max-width:600px){.chev{display:none}}
.avatar-wrap:hover .chev.l{transform:translateY(-50%) translateX(-8px);opacity:.85}
.avatar-wrap:hover .chev.r{transform:translateY(-50%) translateX(8px);opacity:.85}











/* STATS */



/* direction:ltr keeps "8+" / "5.0/5" from flipping to "+8" / "5.0" in RTL */



/* CARDS */





/* accent hairline sweeps in along the card's top edge on hover */












/* scoped to .tagrow — unscoped, this pill rule also swallowed the hero's <p class="tag"> pitch */
.tagrow .tag{font-size:11px;color:var(--accent2);background:rgba(var(--teal-rgb),.1);padding:4px 10px;border-radius:999px;font-family:var(--mono);direction:ltr}



/* PROJECTS — case-study cards. The shot is the point; the copy supports it. */











/* Fallback when the screenshot file isn't there yet. Visitors just see the icon tile;
   the "which file do I drop in" hint renders only on localhost (html[data-dev]). */



















/* all-projects drawer — live from the GitHub API, so it never goes stale */








@media(max-width:900px){.repos{grid-template-columns:repeat(2,1fr)}}
@media(max-width:600px){.repos{grid-template-columns:1fr}}


/* TESTIMONIALS */
/* a static wall — 9 verified 5.0/5 reviews read at the visitor's pace, not on a conveyor belt */










/* CTA */








/* 360px: controls needed 323px inside a 312px content box. Tighten the gap and the
   theme switch rather than dropping a control — nothing becomes unreachable. */
@media(max-width:400px){
  nav > div:last-child{gap:6px!important}
  .switch{width:54px}
  .switch.on .knob{transform:translateX(22px)}
  .switch .tk.l{left:7px}.switch .tk.r{right:7px}
  nav .btn{padding:9px 11px;font-size:12px}
}
/* PROCESS — a spine that fills as you scroll, so the sequence reads as a journey
   rather than a list. The rail is one element; each node lights when it arrives. */













@media(max-width:600px){
  .steps::before,.steps::after{inset-inline-start:18px}
  .step{grid-template-columns:38px 1fr;gap:16px;padding-bottom:26px}
  .step .no{width:38px;height:38px;font-size:12.5px}
}
@media(prefers-reduced-motion:reduce){
  .steps::after{display:none}
  .step .body{opacity:1;transform:none}
  .step .no{transition:none}
}

/* CONTACT FORM */











@media(max-width:600px){.cform .row{grid-template-columns:1fr}}

/* FOOTER — identity block + link columns, social icons grouped in one capsule */
footer{border-top:1px solid var(--line);background:var(--bg2)}
.foot{display:grid;grid-template-columns:1.25fr 1fr 1fr;gap:40px;padding:56px 0 44px;align-items:start}
.foot-id .brand{margin-bottom:16px}
.foot-bio{color:var(--mut);font-size:14px;max-width:330px;line-height:1.75}
/* isolate (not direction:ltr) so the Latin string stays intact but still hugs the column's start edge in RTL */
.foot-copy{display:block;color:var(--mut);font-size:12.5px;margin-top:18px;font-family:var(--mono);unicode-bidi:isolate}
.foot-col h3{font-family:'Instrument Sans',Qomraa,Tajawal;font-size:14px;font-weight:700;margin-bottom:14px;color:var(--txt)}
.foot-col ul{list-style:none;display:flex;flex-direction:column;gap:9px}
.foot-col a{color:var(--mut);font-size:14px;transition:color .2s}
.foot-col a:hover{color:var(--accent)}




.foot-bar{border-top:1px solid var(--line);padding:16px 0;color:var(--mut);font-size:12.5px;
  text-align:center;font-family:var(--mono);direction:ltr}
@media(max-width:760px){
  .foot{grid-template-columns:1fr 1fr;gap:32px;padding:44px 0 32px}
  .foot-id{grid-column:1 / -1}
  .foot-bio{max-width:none}
}

/* ============ AI ASSISTANT ============ */




/* --- the bot: blinking eyes, wobbling antenna, hop on hover --- */





.botface .eye.l{left:6px}.botface .eye.r{right:6px}

@keyframes antwob{0%,100%{transform:translateX(-50%) rotate(-13deg)}50%{transform:translateX(-50%) rotate(13deg)}}
@keyframes blinkeye{0%,92%,100%{transform:scaleY(1)}95%{transform:scaleY(.12)}}
@keyframes bothop{0%,100%{transform:translateY(0) rotate(0)}30%{transform:translateY(-5px) rotate(-7deg)}60%{transform:translateY(0) rotate(5deg)}}


/* peeking wave when the panel first opens */
@keyframes botwave{0%{transform:rotate(0)}25%{transform:rotate(-18deg)}50%{transform:rotate(12deg)}75%{transform:rotate(-8deg)}100%{transform:rotate(0)}}


.ai-panel.open{display:flex}





.ai-head .x{margin-inline-start:auto;background:none;border:0;color:var(--mut);cursor:pointer;padding:6px;border-radius:8px}
.ai-head .x:hover{color:var(--txt);background:rgba(150,150,160,.14)}
.ai-head .x svg{width:16px;height:16px}
/* min-height:0 is required — without it a scrollable flex child refuses to shrink and its
   content spills under the chips/composer instead of scrolling */

/* full-width row + bubble inside — a bare flex item with align-self shrinks to its
   longest word here, which collapsed every message to one word per line */
/* flex:none is load-bearing — as shrinkable items in a column flex container the rows
   get compressed once the log overflows, which squashed bubbles and spilled their text */


/* fit-content is load-bearing: without it the bubble computes a zero-width content box
   and the text overflows as one word per line (verified empirically, 30px vs 315px) */






@keyframes blip{0%,60%,100%{opacity:.3;transform:translateY(0)}30%{opacity:1;transform:translateY(-4px)}}










@media(max-width:600px){
  .ai-fab{bottom:16px;inset-inline-end:16px;padding:12px 16px;font-size:13px}
  .ai-panel{bottom:12px;inset-inline-end:12px;inset-inline-start:12px;width:auto;height:min(72vh,540px)}
}
/* Arabic in mono-styled chrome: JetBrains Mono has no Arabic glyphs, so these were
   silently falling back to a system face and breaking the type system. Latin-only
   elements (kickers, tech tags, slugs, terminal lines) stay mono on purpose. */
html[lang="ar"] nav ul a{font-family:Qomraa,Tajawal,sans-serif}
html[lang="ar"] nav ul a::before{font-family:var(--mono)}


svg{flex:none}

/* 3D */










/* grids stagger their children instead of fading as one 0.8s slab */











/* the orbit ring needs ~530px of column to clear the code card — below that it overflows the viewport */
@media(max-width:1100px){.orbit{display:none}.ring{width:min(400px,86vw)}}
@media(max-width:900px){
  .hero-grid,.cta{grid-template-columns:1fr}
  .stats{grid-template-columns:repeat(2,1fr)}
  .grid3{grid-template-columns:1fr 1fr}
  .avatar-wrap{margin-top:24px}
  .wall{grid-template-columns:repeat(2,1fr)}
  .projwall{grid-template-columns:1fr}
  .proj.feat{flex-direction:column}
  .proj.feat .thumb{min-height:0}
  .proj.feat .body{padding:24px}
  .proj.feat .body h3{font-size:21px}
  nav{flex-wrap:wrap;padding:10px 12px;gap:8px}
  .menu-btn{display:inline-flex}
  nav .btn{padding:9px 14px;font-size:13px;white-space:nowrap}
  .brand-name{font-size:14.5px}
  nav ul{display:none;order:9;flex-basis:100%;flex-direction:column;gap:0;
    padding-top:8px;margin-top:8px;border-top:1px solid var(--line)}
  nav ul.open{display:flex}
  nav ul a{display:block;padding:11px 4px;font-size:15px}
  section{padding:76px 0}
  .cta{padding:40px 28px}
}
@media(max-width:600px){
  .grid3,.grid2,.wall{grid-template-columns:1fr}
  .hero{padding:130px 0 56px}
  section{padding:60px 0}
  .wrap{padding:0 18px}
  .stats{margin-top:52px}
  .cta{padding:32px 22px;border-radius:20px}
}
@media(max-width:430px){
  .brand-name{display:none}
  .stats{gap:12px}
  .stat{padding:20px 10px}
  .stat b{font-size:29px}
  .stat span{font-size:12px}
}

@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;transition-delay:0!important;scroll-behavior:auto!important}
  .reveal,.reveal>*{opacity:1!important;transform:none!important}
  .orbit,.ring,.codecard{animation:none!important}
  .proj:hover .thumb img{transform:none}
  .card::after{display:none}
  .proj:hover .badge{transform:none}
  .botface .ant,.botface .eye{animation:none!important}
  .ai-fab:hover .botface,.ai-head .botface.wave{animation:none!important}
  .caret,.cursor,.dot{animation:none!important}
}

/* ---- polish: selection + scrollbar ---- */
::selection{background:rgba(var(--teal-rgb),.4);color:#fff}
*{scrollbar-width:thin;scrollbar-color:rgba(var(--teal-rgb),.45) var(--bg2)}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:rgba(255,255,255,.04)}
::-webkit-scrollbar-thumb{background:linear-gradient(180deg,rgba(var(--teal-rgb),.55),rgba(var(--teal-rgb),.25));border-radius:99px;border:2px solid var(--bg)}
::-webkit-scrollbar-thumb:hover{background:linear-gradient(180deg,var(--accent2),var(--accent))}
section[id]{scroll-margin-top:96px}

/* ---- scroll progress (top edge) ---- */
#scrollProgress{position:fixed;top:0;inset-inline:0;height:3px;z-index:120;pointer-events:none;background:linear-gradient(90deg,var(--accent),var(--accent2));transform-origin:0 50%;transform:scaleX(0);border-radius:0 99px 99px 0}
html[dir="rtl"] #scrollProgress{transform-origin:100% 50%;border-radius:99px 0 0 99px}

/* ---- back to top ---- */



@media(max-width:600px){.totop{width:40px;height:40px;bottom:18px;inset-inline-start:18px}}

/* ---- mobile ring fix: keep the spinning dashed ring fully on-screen ---- */
@media(max-width:600px){.ring{width:min(280px,72vw)}}
@media(max-width:400px){.ring{width:min(240px,66vw)}}

/* ---- card v2: lift + ring + ambient glow + light sheen ---- */






/* ---- stats: gradient ring + gradient number ---- */

/* --- from blog.html --- */
.article { max-width: 760px; margin: 0 auto; }
.article .page-header { padding: 150px 0 20px; }
.article h1 { font-size: clamp(30px, 4.6vw, 46px); }
.article h2 { margin-top: 2.2rem; font-size: clamp(22px, 2.6vw, 28px); }
.article h3 { margin-top: 1.4rem; }
.article section { margin: 2rem 0; }
.article p, .article li { color: var(--mut); line-height: 1.9; font-size: 16.5px; }
.article .lead { color: var(--txt); font-size: 18px; }
.article ul, .article ol { padding-inline-start: 1.4rem; margin: .8rem 0; }
.article li { margin-bottom: .5rem; }
.article code { background: var(--card2); border: 1px solid var(--line); padding: .1rem .45rem; border-radius: 6px; font-family: var(--mono); font-size: .88em; direction: ltr; unicode-bidi: embed; color: var(--accent2); }
.article a { color: var(--accent); }
.article .post-meta { color: var(--mut); font-size: .9rem; font-family: var(--mono); margin-top: 12px; }
.video-frame { position: relative; aspect-ratio: 16 / 9; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; margin: 1.5rem 0; box-shadow: 0 20px 50px rgba(0,0,0,.35); }
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; }
.watch-link { margin-top: .5rem; }
.watch-link a { display: inline-flex; align-items: center; gap: 8px; }
.posts { display: grid; gap: 1.4rem; margin: 2rem 0 3rem; }
.post-card a { display: block; padding: 1.6rem; border: 1px solid var(--line); border-radius: var(--r); background: var(--card); color: inherit; text-decoration: none; transition: border-color .3s, transform .3s var(--ease); }
.post-card a:hover { border-color: rgba(var(--teal-rgb),.5); transform: translateY(-3px); }
.post-card h2 { margin: 0 0 .5rem; font-size: 1.3rem; color: var(--txt); }
.post-card p { margin: 0 0 .8rem; color: var(--mut); line-height: 1.6; }
.post-meta { display: flex; gap: .7rem; align-items: center; flex-wrap: wrap; font-size: .85rem; color: var(--mut); }
.tag { background: rgba(var(--teal-rgb),.15); color: var(--accent); padding: .15rem .55rem; border-radius: 6px; font-size: .75rem; }
.foot-socials { display: flex; gap: 10px; margin: 14px 0; }
.foot-socials a { display: grid; place-items: center; width: 36px; height: 36px; border-radius: 10px; border: 1px solid var(--line); color: var(--mut); transition: .2s; }
.foot-socials a:hover { color: var(--accent); border-color: var(--accent); }
.foot-socials svg { width: 16px; height: 16px; }

h1,h2{font-weight:700}
html[lang="ar"] h1,html[lang="ar"] h2{font-weight:800}

/* --- from ar/blog/how-to-build-landing-page-from-scratch.html --- */
:root{
  --bg:var(--page); --bg2:var(--surface); --card:var(--surface); --card2:var(--page);
  --line:var(--rule); --txt:var(--ink); --mut:var(--ink-soft);
  --accent:var(--teal); --accent2:var(--teal-bright); --glow:rgba(var(--teal-rgb),.18);
  --accent-ink:#fff;
  --grn:var(--teal); --mono:"JetBrains Mono",monospace;
  --r:16px;
  --focus:var(--teal);
  --ease:cubic-bezier(.2,.7,.2,1);
}
/* --accent2 carries small text (kickers, chips, typing line); #7fe8bd was 2.63:1 on the cream
   paper — below the 4.5 AA floor. The decorative uses keep the brighter tone. */


/* Qomraa Arabic — the brand face, self-hosted so it never depends on a CDN */
@font-face{font-family:"Qomraa";src:url("/fonts/itfQomraArabic-Light.woff2") format("woff2");
  font-weight:300;font-style:normal;font-display:swap}
@font-face{font-family:"Qomraa";src:url("/fonts/itfQomraArabic-Regular.woff2") format("woff2");
  font-weight:400 500;font-style:normal;font-display:swap}
@font-face{font-family:"Qomraa";src:url("/fonts/itfQomraArabic-Bold.woff2") format("woff2");
  font-weight:600 800;font-style:normal;font-display:swap}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;overflow-x:clip}
body{background:var(--bg);color:var(--txt);font-family:'Instrument Sans',system-ui,sans-serif;line-height:1.7;overflow-x:clip}
:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:6px;transition:none}
:focus:not(:focus-visible){outline:none}
.skip{position:fixed;inset-inline-start:16px;top:12px;z-index:100;background:var(--accent);color:var(--accent-ink);
  padding:10px 18px;border-radius:10px;font-weight:700;font-size:14px;
  transform:translateY(-200%);transition:transform .2s var(--ease)}
.skip:focus{transform:none}
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
html[lang="ar"] body{font-family:Qomraa,Tajawal,'Instrument Sans',system-ui,sans-serif}
h1,h2{font-family:var(--display),Georgia,serif}
h3{font-family:'Instrument Sans',sans-serif}
html[lang="ar"] h1,html[lang="ar"] h2,html[lang="ar"] h3{font-family:Qomraa,Tajawal,sans-serif}
a{color:inherit;text-decoration:none}
.wrap{max-width:1180px;margin:0 auto;padding:0 24px}


section{padding:100px 0;position:relative}
h2{font-size:clamp(30px,4vw,46px);font-weight:800;line-height:1.2}

body::before{content:"";position:fixed;inset:0;z-index:-2;background:
  linear-gradient(rgba(var(--ink-rgb),.035) 1px,transparent 1px),
  linear-gradient(90deg,rgba(var(--ink-rgb),.035) 1px,transparent 1px);
  background-size:44px 44px}
.kicker{display:inline-flex;align-items:center;gap:8px;color:var(--accent2);font-family:var(--mono);font-size:13px;margin-bottom:14px}
.kicker::before{content:"//";color:var(--mut)}


/* NAV CTA — shared with home */
.nav-cta{display:inline-flex;align-items:center;background:var(--accent);color:var(--accent-ink);font-weight:700;
  font-size:14px;padding:11px 20px;border-radius:var(--r);transition:.25s;white-space:nowrap}
.nav-cta:hover{background:var(--accent2);transform:translateY(-2px);box-shadow:0 12px 34px var(--glow)}
.nav-cta .nc-short{display:none}
@media(max-width:760px){.nav-cta .nc-full{display:none}.nav-cta .nc-short{display:inline}}

/* NAV */
nav{position:fixed;top:16px;left:50%;transform:translateX(-50%);z-index:50;width:min(1180px,94%);
  background:rgba(252,248,239,.88);backdrop-filter:blur(16px);border:1px solid var(--line);border-radius:14px;
  display:flex;align-items:center;justify-content:space-between;padding:12px 18px;transition:.3s}
nav.scrolled{border-color:rgba(var(--teal-rgb),.3);box-shadow:0 12px 32px rgba(var(--shadow-rgb),.14)}



@keyframes blink{50%{opacity:0}}
.brand{display:flex;align-items:center;gap:11px}
.brand-pic{width:38px;height:38px;border-radius:50%;overflow:hidden;border:2px solid var(--accent);flex:none;
  box-shadow:0 1px 2px rgba(0,0,0,.28);transition:.3s}
.brand:hover .brand-pic{transform:scale(1.08) rotate(-4deg)}
.brand-pic img{width:100%;height:100%;object-fit:cover}
.brand-name{font-family:'Instrument Sans',Qomraa,Tajawal;font-weight:600;font-size:16px;white-space:nowrap}
.brand-name b{color:var(--accent)}
/* floating pill nav — the active section gets its own filled sub-pill */
nav ul{display:flex;align-items:center;gap:2px;list-style:none;
  background:var(--card2);border:1px solid var(--line);border-radius:999px;padding:4px}
/* "~/about" is a path token — force the whole link LTR so RTL can't flip "~/" into "/~" */
nav ul a{color:var(--mut);font-size:13.5px;font-family:var(--mono);transition:color .2s,background .25s;
  direction:ltr;unicode-bidi:isolate;display:inline-block;padding:7px 13px;border-radius:999px}
nav ul a::before{content:"~/";color:var(--accent);opacity:.7;transition:color .25s}
nav ul a:hover{color:var(--txt);background:rgba(150,150,160,.13)}
nav ul a[aria-current="true"]{background:var(--accent);color:var(--accent-ink)}
nav ul a[aria-current="true"]::before{color:var(--accent-ink);opacity:.75}
.menu-btn{display:none;width:38px;height:32px;border-radius:10px;border:1px solid var(--line);
  background:var(--card2);color:var(--txt);cursor:pointer;align-items:center;justify-content:center;flex:none;transition:.25s}
.menu-btn svg{width:17px;height:17px}
.menu-btn:hover{border-color:var(--accent);color:var(--accent)}
.menu-btn .x{display:none}
.menu-btn[aria-expanded="true"] .x{display:block}
.menu-btn[aria-expanded="true"] .bars{display:none}
.btn{display:inline-flex;align-items:center;gap:8px;background:var(--accent);color:var(--accent-ink);font-weight:700;font-size:14px;
  padding:11px 20px;border-radius:12px;border:0;cursor:pointer;transition:.25s;position:relative;overflow:hidden}
.btn::after{content:"";position:absolute;inset:0;background:linear-gradient(110deg,transparent 30%,rgba(255,255,255,.25) 50%,transparent 70%);transform:translateX(-100%);transition:.5s}
.btn:hover{background:var(--accent2);transform:translateY(-2px);box-shadow:0 12px 34px var(--glow)}
.btn:hover::after{transform:translateX(100%)}
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--txt)}
.btn.ghost:hover{border-color:var(--accent);color:var(--accent);box-shadow:none}

/* switch (theme) + lang toggle */
.switch{position:relative;width:66px;height:32px;border-radius:999px;cursor:pointer;flex:none;
  background:var(--card2);border:1px solid var(--line);transition:.35s;padding:0}
.switch .knob{position:absolute;top:3px;left:3px;width:24px;height:24px;border-radius:50%;
  background:var(--accent);display:flex;align-items:center;justify-content:center;color:var(--accent-ink);
  transition:transform .4s var(--ease),background .35s;box-shadow:0 3px 10px var(--glow)}
.switch .knob svg{width:13px;height:13px}
.switch .tk{position:absolute;top:50%;transform:translateY(-50%);color:var(--mut);transition:opacity .35s}
.switch .tk svg{width:12px;height:12px;display:block}
.switch .tk.l{left:9px}.switch .tk.r{right:9px}
/* the knob parks over the ACTIVE icon; the uncovered one is what you'd switch to */
.switch .tk.l{opacity:0}
.switch.on .tk.l{opacity:1}
.switch.on .tk.r{opacity:0}
.switch.on .knob{transform:translateX(34px)}
.switch:hover{border-color:var(--accent)}
.lang-btn{height:32px;border-radius:999px;border:1px solid var(--line);background:var(--card2);color:var(--mut);
  font-family:var(--mono);font-size:12.5px;font-weight:600;letter-spacing:.04em;padding:0 13px;cursor:pointer;
  transition:.25s;flex:none;direction:ltr}
.lang-btn:hover{color:var(--accent);border-color:var(--accent)}
/* below 1180px the pill is the first thing to give — never let the bar wrap */
@media(max-width:1180px){
  nav ul a{padding:7px 10px;font-size:12.5px}
  nav ul{gap:0}
}

/* HERO */





/* keep ">" from bidi-mirroring into "<" and keep the caret glued to the text end */









@keyframes pulse{50%{opacity:.4}}






/* --orb must clear the code card (half-width 200px, half-height ~206px) or the icons get bisected */








@keyframes spin{to{transform:rotate(360deg)}}

@keyframes float{50%{transform:translateY(-12px)}}
/* HERO PORTRAIT — cutout on a target halo, flanked by code brackets.
   Only renders once shots/hero-portrait.png loads; until then the code card stands in.
   Needs a TRANSPARENT PNG cutout — a photo with its own background defeats the halo. */



/* Full colour. The cutout has no box to put a border on, so stacked zero-offset
   drop-shadows trace the alpha silhouette — that IS the border — over a deep cast shadow. */


@media(hover:hover) and (pointer:fine){
  .avatar-wrap:hover .portrait img{filter:
    drop-shadow(0 0 1.5px rgba(var(--teal-rgb),.85))
    drop-shadow(0 0 14px rgba(var(--teal-rgb),.22))
    drop-shadow(0 26px 42px rgba(0,0,0,.62))}
}
/* soft bloom instead of a hard target ring — the portrait dissolves into the page */


/* PHOTO MODE — a real photo that carries its own background: framed, not haloed.
   (Cutout mode above still wins if a transparent shots/hero-portrait.png is added later.) */




/* accent bloom behind the frame, standing in for the halo */

/* physical left/right — the brackets must stay < … > and not mirror to > … < in RTL */


.chev.l{left:-26px}
.chev.r{right:-26px}
/* framed mode: the frame is opaque, so the brackets must clear it entirely */

html[data-portrait="photo"] .chev.l{left:-58px}
html[data-portrait="photo"] .chev.r{right:-58px}
@media(max-width:1100px){
  html[data-portrait="photo"] .chev.l{left:-44px}
  html[data-portrait="photo"] .chev.r{right:-44px}
  .chev svg{width:52px;height:52px}
}
/* below 600px there isn't room for the brackets to sit outside the portrait without
   being half-clipped by the viewport edge — drop them rather than show them cut */
@media(max-width:600px){.chev{display:none}}
.avatar-wrap:hover .chev.l{transform:translateY(-50%) translateX(-8px);opacity:.85}
.avatar-wrap:hover .chev.r{transform:translateY(-50%) translateX(8px);opacity:.85}











/* STATS */



/* direction:ltr keeps "8+" / "5.0/5" from flipping to "+8" / "5.0" in RTL */



/* CARDS */





/* accent hairline sweeps in along the card's top edge on hover */












/* scoped to .tagrow — unscoped, this pill rule also swallowed the hero's <p class="tag"> pitch */




/* PROJECTS — case-study cards. The shot is the point; the copy supports it. */











/* Fallback when the screenshot file isn't there yet. Visitors just see the icon tile;
   the "which file do I drop in" hint renders only on localhost (html[data-dev]). */



















/* all-projects drawer — live from the GitHub API, so it never goes stale */








@media(max-width:900px){.repos{grid-template-columns:repeat(2,1fr)}}
@media(max-width:600px){.repos{grid-template-columns:1fr}}


/* TESTIMONIALS */
/* a static wall — 9 verified 5.0/5 reviews read at the visitor's pace, not on a conveyor belt */










/* CTA */








/* 360px: controls needed 323px inside a 312px content box. Tighten the gap and the
   theme switch rather than dropping a control — nothing becomes unreachable. */
@media(max-width:400px){
  nav > div:last-child{gap:6px!important}
  .switch{width:54px}
  .switch.on .knob{transform:translateX(22px)}
  .switch .tk.l{left:7px}.switch .tk.r{right:7px}
  nav .btn{padding:9px 11px;font-size:12px}
}
/* PROCESS — a spine that fills as you scroll, so the sequence reads as a journey
   rather than a list. The rail is one element; each node lights when it arrives. */













@media(max-width:600px){
  .steps::before,.steps::after{inset-inline-start:18px}
  .step{grid-template-columns:38px 1fr;gap:16px;padding-bottom:26px}
  .step .no{width:38px;height:38px;font-size:12.5px}
}
@media(prefers-reduced-motion:reduce){
  .steps::after{display:none}
  .step .body{opacity:1;transform:none}
  .step .no{transition:none}
}

/* CONTACT FORM */











@media(max-width:600px){.cform .row{grid-template-columns:1fr}}

/* FOOTER — identity block + link columns, social icons grouped in one capsule */
footer{border-top:1px solid var(--line);background:var(--bg2)}
.foot{display:grid;grid-template-columns:1.25fr 1fr 1fr;gap:40px;padding:56px 0 44px;align-items:start}
.foot-id .brand{margin-bottom:16px}
.foot-bio{color:var(--mut);font-size:14px;max-width:330px;line-height:1.75}
/* isolate (not direction:ltr) so the Latin string stays intact but still hugs the column's start edge in RTL */
.foot-copy{display:block;color:var(--mut);font-size:12.5px;margin-top:18px;font-family:var(--mono);unicode-bidi:isolate}
.foot-col h3{font-family:'Instrument Sans',Qomraa,Tajawal;font-size:14px;font-weight:700;margin-bottom:14px;color:var(--txt)}
.foot-col ul{list-style:none;display:flex;flex-direction:column;gap:9px}
.foot-col a{color:var(--mut);font-size:14px;transition:color .2s}
.foot-col a:hover{color:var(--accent)}




.foot-bar{border-top:1px solid var(--line);padding:16px 0;color:var(--mut);font-size:12.5px;
  text-align:center;font-family:var(--mono);direction:ltr}
@media(max-width:760px){
  .foot{grid-template-columns:1fr 1fr;gap:32px;padding:44px 0 32px}
  .foot-id{grid-column:1 / -1}
  .foot-bio{max-width:none}
}

/* ============ AI ASSISTANT ============ */




/* --- the bot: blinking eyes, wobbling antenna, hop on hover --- */





.botface .eye.l{left:6px}.botface .eye.r{right:6px}

@keyframes antwob{0%,100%{transform:translateX(-50%) rotate(-13deg)}50%{transform:translateX(-50%) rotate(13deg)}}
@keyframes blinkeye{0%,92%,100%{transform:scaleY(1)}95%{transform:scaleY(.12)}}
@keyframes bothop{0%,100%{transform:translateY(0) rotate(0)}30%{transform:translateY(-5px) rotate(-7deg)}60%{transform:translateY(0) rotate(5deg)}}


/* peeking wave when the panel first opens */
@keyframes botwave{0%{transform:rotate(0)}25%{transform:rotate(-18deg)}50%{transform:rotate(12deg)}75%{transform:rotate(-8deg)}100%{transform:rotate(0)}}


.ai-panel.open{display:flex}





.ai-head .x{margin-inline-start:auto;background:none;border:0;color:var(--mut);cursor:pointer;padding:6px;border-radius:8px}
.ai-head .x:hover{color:var(--txt);background:rgba(150,150,160,.14)}
.ai-head .x svg{width:16px;height:16px}
/* min-height:0 is required — without it a scrollable flex child refuses to shrink and its
   content spills under the chips/composer instead of scrolling */

/* full-width row + bubble inside — a bare flex item with align-self shrinks to its
   longest word here, which collapsed every message to one word per line */
/* flex:none is load-bearing — as shrinkable items in a column flex container the rows
   get compressed once the log overflows, which squashed bubbles and spilled their text */


/* fit-content is load-bearing: without it the bubble computes a zero-width content box
   and the text overflows as one word per line (verified empirically, 30px vs 315px) */






@keyframes blip{0%,60%,100%{opacity:.3;transform:translateY(0)}30%{opacity:1;transform:translateY(-4px)}}










@media(max-width:600px){
  .ai-fab{bottom:16px;inset-inline-end:16px;padding:12px 16px;font-size:13px}
  .ai-panel{bottom:12px;inset-inline-end:12px;inset-inline-start:12px;width:auto;height:min(72vh,540px)}
}
/* Arabic in mono-styled chrome: JetBrains Mono has no Arabic glyphs, so these were
   silently falling back to a system face and breaking the type system. Latin-only
   elements (kickers, tech tags, slugs, terminal lines) stay mono on purpose. */
html[lang="ar"] nav ul a{font-family:Qomraa,Tajawal,sans-serif}
html[lang="ar"] nav ul a::before{font-family:var(--mono)}


svg{flex:none}

/* 3D */










/* grids stagger their children instead of fading as one 0.8s slab */











/* the orbit ring needs ~530px of column to clear the code card — below that it overflows the viewport */
@media(max-width:1100px){.orbit{display:none}.ring{width:min(400px,86vw)}}
@media(max-width:900px){
  .hero-grid,.cta{grid-template-columns:1fr}
  .stats{grid-template-columns:repeat(2,1fr)}
  .grid3{grid-template-columns:1fr 1fr}
  .avatar-wrap{margin-top:24px}
  .wall{grid-template-columns:repeat(2,1fr)}
  .projwall{grid-template-columns:1fr}
  .proj.feat{flex-direction:column}
  .proj.feat .thumb{min-height:0}
  .proj.feat .body{padding:24px}
  .proj.feat .body h3{font-size:21px}
  nav{flex-wrap:wrap;padding:10px 12px;gap:8px}
  .menu-btn{display:inline-flex}
  nav .btn{padding:9px 14px;font-size:13px;white-space:nowrap}
  .brand-name{font-size:14.5px}
  nav ul{display:none;order:9;flex-basis:100%;flex-direction:column;gap:0;
    padding-top:8px;margin-top:8px;border-top:1px solid var(--line)}
  nav ul.open{display:flex}
  nav ul a{display:block;padding:11px 4px;font-size:15px}
  section{padding:76px 0}
  .cta{padding:40px 28px}
}
@media(max-width:600px){
  .grid3,.grid2,.wall{grid-template-columns:1fr}
  .hero{padding:130px 0 56px}
  section{padding:60px 0}
  .wrap{padding:0 18px}
  .stats{margin-top:52px}
  .cta{padding:32px 22px;border-radius:20px}
}
@media(max-width:430px){
  .brand-name{display:none}
  .stats{gap:12px}
  .stat{padding:20px 10px}
  .stat b{font-size:29px}
  .stat span{font-size:12px}
}

@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;transition-delay:0!important;scroll-behavior:auto!important}
  .reveal,.reveal>*{opacity:1!important;transform:none!important}
  .orbit,.ring,.codecard{animation:none!important}
  .proj:hover .thumb img{transform:none}
  .card::after{display:none}
  .proj:hover .badge{transform:none}
  .botface .ant,.botface .eye{animation:none!important}
  .ai-fab:hover .botface,.ai-head .botface.wave{animation:none!important}
  .caret,.cursor,.dot{animation:none!important}
}

/* ---- polish: selection + scrollbar ---- */
::selection{background:rgba(var(--teal-rgb),.4);color:#fff}
*{scrollbar-width:thin;scrollbar-color:rgba(var(--teal-rgb),.45) var(--bg2)}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:rgba(255,255,255,.04)}
::-webkit-scrollbar-thumb{background:linear-gradient(180deg,rgba(var(--teal-rgb),.55),rgba(var(--teal-rgb),.25));border-radius:99px;border:2px solid var(--bg)}
::-webkit-scrollbar-thumb:hover{background:linear-gradient(180deg,var(--accent2),var(--accent))}
section[id]{scroll-margin-top:96px}

/* ---- scroll progress (top edge) ---- */
#scrollProgress{position:fixed;top:0;inset-inline:0;height:3px;z-index:120;pointer-events:none;background:linear-gradient(90deg,var(--accent),var(--accent2));transform-origin:0 50%;transform:scaleX(0);border-radius:0 99px 99px 0}
html[dir="rtl"] #scrollProgress{transform-origin:100% 50%;border-radius:99px 0 0 99px}

/* ---- back to top ---- */



@media(max-width:600px){.totop{width:40px;height:40px;bottom:18px;inset-inline-start:18px}}

/* ---- mobile ring fix: keep the spinning dashed ring fully on-screen ---- */
@media(max-width:600px){.ring{width:min(280px,72vw)}}
@media(max-width:400px){.ring{width:min(240px,66vw)}}

/* ---- card v2: lift + ring + ambient glow + light sheen ---- */






/* ---- stats: gradient ring + gradient number ---- */


/* ==========================================================================
   The index reads as ruled rows, like the writing section on the homepage.
   Card boxes were the one place left on the site using the lazy container, and
   three identical bordered rectangles say nothing the rule between them does
   not already say. Appended last so it wins over both halves of this generated
   file.
   ========================================================================== */
.posts{margin-top:clamp(32px,4vw,48px);display:block}
.post-card{border:0;border-radius:0;background:none;
  border-block-start:1px solid var(--line)}
.post-card:last-child{border-block-end:1px solid var(--line)}
.post-card a{display:grid;grid-template-columns:minmax(120px,18%) minmax(0,1fr);
  column-gap:clamp(20px,3.6vw,48px);align-items:start;
  padding:clamp(22px,2.8vw,32px) 0;
  border:0;border-radius:0;background:none;transform:none;
  color:inherit;text-decoration:none;transition:none}
.post-card a:hover{border:0;transform:none;background:none}
.post-card h2{grid-column:2;grid-row:1;margin:0 0 10px;
  font-size:clamp(18px,2vw,24px);line-height:1.4;transition:color .2s}
.post-card p{grid-column:2;grid-row:2;margin:0;font-size:15px;line-height:1.8}
.post-card .post-meta{grid-column:1;grid-row:1;margin:0;font-size:13px}
.post-card a:hover h2{color:var(--accent)}
@media(max-width:640px){
  .post-card a{grid-template-columns:minmax(0,1fr)}
  .post-card h2,.post-card p,.post-card .post-meta{grid-column:1}
  .post-card .post-meta{grid-row:3;margin-top:12px}
}

/* The graph-paper wash belongs to the old dark identity and appears on no
   other route. */
body::before{display:none}

/* The header is sticky and already in flow; 150px on top of it was ~230px of
   nothing before the title, the same fault the homepage hero had. */
.article .page-header{padding:clamp(36px,5vw,64px) 0 20px}
/* An eyebrow above a heading was removed everywhere else on the site. */
.article .kicker{display:none}
