/* ==========================================================================
   Ruba Aloufi — AI Engineer portfolio
   Dark-first, blue accent on near-black, bilingual EN/AR.
   Hand-written, no build step. Design tokens live in the :root block below.
   ========================================================================== */

:root {
  /* Display face for the hero only. Arial Black ships with Windows and macOS;
     Archivo Black is the webfont stand-in everywhere else. */
  --font-display: "Arial Black", "Archivo Black", "Inter", system-ui, sans-serif;

  /* RTL stack. Inter leads on purpose: it carries NO Arabic glyphs, so Arabic
     falls through to the Arabic face while Latin terms inside Arabic copy
     (RAG, P99, LangGraph) keep Inter's shapes rather than the Arabic face's
     weaker Latin. Thmanyah Sans is used when its files are present in
     assets/fonts/ (see that folder's README); Readex Pro is the fallback and
     is what renders until they are. */
  --font-ar: "Inter", "Thmanyah Sans", "Readex Pro", "IBM Plex Sans Arabic", system-ui, sans-serif;

  --bg: #0a0a0a;
  --bg-2: #0f0f10;
  --surface: #111112;
  --surface-2: #161617;
  --surface-3: #1d1d1f;
  --line: #26262a;
  --line-strong: #35353a;
  --text: #fafafa;
  --muted: #9a9a9e;
  --dim: #6d6d72;

  --accent: #3b82f6;           /* text & marks — 5.13:1 on surface */
  --accent-fill: #2563eb;      /* button/chip fills — white ink reads 5.17:1 */
  --accent-ink: #ffffff;       /* text sitting on an accent fill */

  --accent-soft: rgba(59, 130, 246, .13);
  --accent-line: rgba(59, 130, 246, .36);

  --radius: 14px;
  --max: 1180px;
  --shadow: 0 30px 90px rgba(0, 0, 0, .5);
}

html[data-theme="light"] {
  --bg: #f7f7f8;
  --bg-2: #efeff1;
  --surface: #ffffff;
  --surface-2: #f4f4f6;
  --surface-3: #eaeaed;
  --line: #e3e3e7;
  --line-strong: #cbcbd1;
  --text: #0d0d0e;
  --muted: #5a5a60;
  --dim: #85858c;

  --accent: #1d4ed8;           /* 6.70:1 on white */
  --accent-fill: #1d4ed8;      /* white ink reads 6.70:1 */
  --accent-ink: #ffffff;

  --accent-soft: rgba(29, 78, 216, .1);
  --accent-line: rgba(29, 78, 216, .3);

  --shadow: 0 30px 90px rgba(20, 20, 25, .1);
}

/* ──────────────────────────────────────────────────────────────────────────
   Thmanyah Sans 1.2 — self-hosted, Arabic only.

   Files live in assets/fonts/ and are served from this site, not hotlinked.
   If they are ever removed the faces simply fail to load and --font-ar falls
   through to Readex Pro, so the page stays correct either way.

   unicode-range pins these faces to Arabic. Thmanyah's Latin is weaker than
   Inter's, and this guarantees Latin never reaches it even if someone later
   reorders --font-ar.

   Only 400/500/700 exist here — the 1.2 release has no Black, and nothing on
   this site asks for a heavier Arabic weight than 700.

   LICENCE NOTE: Thmanyah is free to download and free for commercial use, but
   its licence forbids web embedding ("...بما في ذلك عبر التضمين على الويب").
   Self-hosting it here is a deliberate decision by the site owner, taken with
   that term in view. Files came from font.thmanyah.com's own faces.
   ────────────────────────────────────────────────────────────────────────── */
@font-face {
  font-family: "Thmanyah Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("./assets/fonts/thmanyah-sans-400.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200F, U+2010-2011, U+204F, U+2E41;
}
@font-face {
  font-family: "Thmanyah Sans"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("./assets/fonts/thmanyah-sans-500.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200F, U+2010-2011, U+204F, U+2E41;
}
@font-face {
  font-family: "Thmanyah Sans"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("./assets/fonts/thmanyah-sans-700.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200F, U+2010-2011, U+204F, U+2E41;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; background: var(--bg); color-scheme: dark; overflow-x: clip; }
html[data-theme="light"] { color-scheme: light; }
html, body { width: 100%; max-width: 100%; min-height: 100%; background-color: var(--bg); }

body {
  margin: 0; position: relative; overflow-x: clip; color: var(--text);
  font-family: "Inter", system-ui, -apple-system, sans-serif;
  font-size: 16px; line-height: 1.6;
  transition: background .3s ease, color .3s ease;
}
/* Readex Pro sits slightly smaller in the em box than Inter — a light root bump
   evens the two languages out without patching individual rules. */
html[dir="rtl"] { font-size: 106%; }
html[dir="rtl"] body { font-family: var(--font-ar); line-height: 1.8; }
/* Micro-labels that are uppercase mono in English need a touch more presence in
   Arabic, which has no caps to carry that job. */
html[dir="rtl"] .brand-copy small,
html[dir="rtl"] .hero-meta span,
html[dir="rtl"] .contact-links a > span:first-child { font-size: .74rem; font-weight: 500; }

a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; }
button { color: inherit; }
img { max-width: 100%; display: block; }
main { width: 100%; overflow: clip; }
::selection { background: var(--accent-fill); color: var(--accent-ink); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

.mono { font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace; }
html[dir="rtl"] .mono { font-family: "JetBrains Mono", var(--font-ar), ui-monospace, monospace; }

.grain {
  position: fixed; inset: 0; z-index: 99; pointer-events: none; opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ───────────────────────── header ───────────────────────── */
.site-header {
  position: fixed; top: 14px; left: 50%; transform: translateX(-50%); z-index: 60;
  width: min(calc(100% - 28px), 1240px); min-height: 62px;
  padding: 9px 11px 9px 14px; display: flex; align-items: center; gap: 24px;
  border: 1px solid var(--line); border-radius: 13px;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
}
.brand { display: inline-flex; align-items: center; gap: 11px; min-width: max-content; }
.monogram {
  position: relative; width: 40px; height: 40px; flex: 0 0 40px; border-radius: 10px;
  background: var(--accent-fill); color: var(--accent-ink);
  display: grid; place-items: center;
  font-family: "JetBrains Mono", monospace; font-weight: 700; font-size: .8rem; letter-spacing: -.04em;
  transition: transform .25s ease;
}
.brand:hover .monogram { transform: rotate(-4deg) scale(1.04); }
.brand-copy { display: flex; flex-direction: column; line-height: 1.15; }
.brand-copy strong { font-size: .84rem; font-weight: 700; letter-spacing: -.01em; }
.brand-copy small { color: var(--dim); font-size: .62rem; letter-spacing: .1em; text-transform: uppercase; font-family: "JetBrains Mono", monospace; }
html[dir="rtl"] .brand-copy { line-height: 1.45; }
html[dir="rtl"] .brand-copy small { letter-spacing: 0; text-transform: none; font-size: .62rem; font-family: inherit; }

.nav { margin: 0 auto; display: flex; gap: 22px; align-items: center; }
.nav a {
  color: var(--muted); font-size: .78rem; font-weight: 500; letter-spacing: .02em;
  font-family: "JetBrains Mono", monospace; transition: color .18s;
}
html[dir="rtl"] .nav a { font-family: inherit; font-size: .84rem; }
.nav a:hover { color: var(--accent); }

.header-actions { display: flex; align-items: center; gap: 7px; }
.icon-btn, .lang-btn, .menu-btn {
  border: 1px solid var(--line); background: transparent; border-radius: 9px; height: 38px;
  cursor: pointer; transition: border-color .2s, color .2s, background .2s;
}
.icon-btn:hover, .lang-btn:hover, .menu-btn:hover { border-color: var(--accent); color: var(--accent); }
.icon-btn { width: 38px; display: grid; place-items: center; }
.icon-btn svg { width: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
html[data-theme="dark"] .sun, html[data-theme="light"] .moon { display: none; }
.lang-btn { padding: 0 13px; font-size: .72rem; font-weight: 700; font-family: "JetBrains Mono", monospace; }
.menu-btn { display: none; width: 38px; padding: 0 9px; }
.menu-btn span { display: block; height: 1.5px; margin: 5px 0; background: currentColor; }

/* ───────────────────────── layout ───────────────────────── */
.section { width: min(calc(100% - 40px), var(--max)); margin: 0 auto; padding: 100px 0; position: relative; }

.kicker {
  display: inline-flex; align-items: center; gap: 9px;
  color: var(--accent); font-family: "JetBrains Mono", monospace;
  font-size: .68rem; font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
}
html[dir="rtl"] .kicker { letter-spacing: .03em; font-family: inherit; font-size: .76rem; }
.kicker::before { content: ""; width: 22px; height: 1px; background: var(--accent-line); }

.section-heading { margin-bottom: 46px; max-width: 820px; }
.section-heading.center { max-width: none; text-align: center; }
.section-heading.center p { margin-inline: auto; }
.section-heading h2 {
  margin: 16px 0 0; font-family: "Playfair Display", Georgia, serif;
  font-size: clamp(2rem, 4.2vw, 3.5rem); line-height: 1.08; letter-spacing: -.025em; font-weight: 600;
}
html[dir="rtl"] .section-heading h2 { font-family: inherit; font-weight: 700; line-height: 1.3; letter-spacing: 0; font-size: clamp(1.9rem, 4vw, 3.2rem); }
.section-heading p { color: var(--muted); font-size: 1rem; line-height: 1.75; margin: 16px 0 0; max-width: 680px; }

/* ───────────────────────── hero ───────────────────────── */
.hero {
  width: min(calc(100% - 40px), var(--max)); margin: 0 auto;
  min-height: 100svh; padding: 150px 0 80px; position: relative;
  display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, .65fr);
  gap: 32px; align-items: center;
}
.hero-copy { display: flex; flex-direction: column; justify-content: center; min-width: 0; }
.hero::after {
  content: ""; position: absolute; z-index: -1; width: 540px; height: 540px; border-radius: 50%;
  inset-inline-end: -180px; top: 60px; pointer-events: none;
  background: radial-gradient(circle, var(--accent-soft), transparent 68%);
}
.hero h1 {
  /* Max size is capped so the longest line still fits --max on one line;
     line-height stays >= .95 so an accidental wrap never collides.
     Arial Black is wider than Inter, so the cap is lower here than it looks. */
  /* Sized for the hero's LEFT column, not the full width — the graph takes the
     other 38%, so this cap is lower than a full-bleed headline would allow. */
  margin: 14px 0 0; font-family: var(--font-display);
  font-weight: 900; text-transform: uppercase;
  /* Capped lower than the Arabic: Arial Black is a wide face and the English
     sentence is longer, so the reference size would push it past four lines. */
  font-size: clamp(2.3rem, 5.2vw, 4.7rem); line-height: 1.12; letter-spacing: -.035em;
  text-wrap: balance;
}
/* Arabic has no Arial Black equivalent — Readex Pro 700 carries the display weight. */
/* Arabic needs more leading than Latin at display size — deep descenders and
   dots make stacked lines collide well before Latin's 0.98. */
html[dir="rtl"] .hero h1 {
  /* Arabic runs looser than the Latin 1.12: ج ح خ ع غ م ي all drop below the
     baseline, so a 1.1 leading collides at display size. */
  font-family: var(--font-ar); font-weight: 700; text-transform: none;
  line-height: 1.34; letter-spacing: -.01em; font-size: clamp(2rem, 5.2vw, 4.6rem);
}
.hero h1 span { display: block; }
.hero h1 .tint { color: var(--accent); }

/* ── .text-grad — gradient-filled display type ──
   Teal → accent blue → periwinkle, clipped to the glyphs. Reusable: override
   the three stops to retint. Every stop clears 3:1 on its surface, which is
   the bar for large text — this is NOT for body copy.
   padding-bottom is load-bearing for Arabic: ج ح خ ع غ م ي all drop below the
   baseline and the clip box would shear them off without it. */
.text-grad {
  --grad-1: #2dd4bf;
  --grad-2: var(--accent);
  --grad-3: #a5b4fc;
  color: transparent;
  background-image: linear-gradient(95deg, var(--grad-1) 0%, var(--grad-2) 52%, var(--grad-3) 100%);
  -webkit-background-clip: text;
          background-clip: text;
  padding-bottom: .14em;
}
html[data-theme="light"] .text-grad { --grad-1: #0f766e; --grad-2: #1d4ed8; --grad-3: #4f46e5; }
/* without clip support the glyphs would be fully transparent — i.e. invisible */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .text-grad { color: var(--grad-2); background-image: none; }
}

/* The h1's spans are display:block, so a gradient box would span the whole
   line while the text sits at one end — the glyphs would only ever see one
   end of the ramp. inline-block shrinks the box to the text so all three
   stops land on the letters. */
.hero h1 .text-grad { display: inline-block; }

.hero-sub {
  margin: 18px 0 0; max-width: 620px; color: var(--muted);
  font-size: clamp(1.05rem, 1.7vw, 1.6rem); font-weight: 500; line-height: 1.6;
}

.hero-intro {
  margin: 26px 0 0; color: var(--muted); font-size: .92rem; font-weight: 500;
  font-family: "JetBrains Mono", ui-monospace, monospace; letter-spacing: .01em;
}
html[dir="rtl"] .hero-intro { font-family: inherit; font-size: 1rem; letter-spacing: 0; }
/* One colour across the intro line — the name stands out by weight, not hue. */
.hero-intro b { color: inherit; font-weight: 700; }

/* Count-up figures. tabular-nums is load-bearing: without it the text reflows
   on every frame as the digits change width. The bidi isolate is load-bearing
   too: inside Arabic copy, "12+" would otherwise render as "+12" — the plus is
   a neutral character, so it drifts to whichever side the paragraph runs. */
.tally {
  color: var(--accent); font-weight: 700;
  font-variant-numeric: tabular-nums;
  direction: ltr; unicode-bidi: isolate;
}
.hero-lead { margin: 26px 0 0; max-width: 620px; color: var(--muted); font-size: 1.05rem; line-height: 1.8; }
.hero-actions { display: flex; gap: 11px; margin-top: 34px; flex-wrap: wrap; }

.btn {
  min-height: 46px; display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  border-radius: 10px; padding: 0 20px; font-weight: 600; font-size: .84rem;
  border: 1px solid var(--line); cursor: pointer;
  transition: transform .18s, background .18s, border-color .18s, color .18s;
}
/* `hidden` is only display:none in the UA sheet, so .btn's display wins
   without this — the CV button would show even with the attribute set. */
.btn[hidden] { display: none; }
.btn:hover { transform: translateY(-2px); }
/* Primary CTA: a slowly drifting blue gradient with a glow halo.
   Every stop is picked so WHITE stays at or above 5:1 on it — the usual vivid
   version of this effect reaches light blues like #93c5fd, where white drops to
   1.8:1 and the label goes unreadable mid-cycle. */
.btn.primary {
  --g1: #1e40af; --g2: #2563eb; --g3: #1d4ed8; --g4: #0e62cc;
  position: relative; isolation: isolate;
  border-color: transparent; color: #fff; font-weight: 700;
  background: linear-gradient(100deg, var(--g1), var(--g2), var(--g3), var(--g4), var(--g1));
  background-size: 300% 100%;
  animation: btnDrift 7s linear infinite;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22),
              inset 0 0 0 1px rgba(147, 197, 253, .18),
              0 6px 22px rgba(37, 99, 235, .4);
}
/* a blurred copy of the same gradient sits behind as the halo */
.btn.primary::before {
  content: ""; position: absolute; inset: -3px; z-index: -1; border-radius: inherit;
  background: inherit; background-size: inherit;
  animation: btnDrift 7s linear infinite; filter: blur(11px); opacity: .55;
}
.btn.primary:hover { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .28), 0 10px 30px rgba(37, 99, 235, .55); }
.btn.primary:hover::before { opacity: .8; }
@keyframes btnDrift { to { background-position: 300% 0; } }
@media (prefers-reduced-motion: reduce) {
  .btn.primary, .btn.primary::before { animation: none; }
}
.btn.secondary:hover { border-color: var(--accent); color: var(--accent); }
.btn svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex: 0 0 auto; }

.hero-meta {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  margin-top: 56px; border-top: 1px solid var(--line); max-width: 660px;
}
.hero-meta div { padding: 18px 18px 0 0; display: flex; flex-direction: column; gap: 5px; min-width: 0; }
html[dir="rtl"] .hero-meta div { padding-right: 0; padding-left: 18px; }
.hero-meta strong { font-size: .9rem; font-weight: 700; }
/* margin-top:auto bottom-aligns the three labels. Grid stretches the cells to
   equal height, so a value that wraps to two lines no longer drags its own
   label out of line with the other two. */
.hero-meta span { margin-top: auto; color: var(--dim); font-size: .68rem; font-family: "JetBrains Mono", monospace; letter-spacing: .05em; text-transform: uppercase; }
html[dir="rtl"] .hero-meta span { font-family: inherit; text-transform: none; letter-spacing: 0; font-size: .76rem; }

/* ═══════════════════ HERO AGENT GRAPH ═══════════════════
   Tweak everything from here. Colours inherit the site tokens by default, so
   changing --accent re-themes the graph with it.
   Timing lives in GRAPH at the top of app.js (cycle length, node count, edges).
   ──────────────────────────────────────────────────────── */
.hero-viz {
  --g-edge: color-mix(in srgb, var(--muted) 26%, transparent);  /* resting edge */
  --g-node: color-mix(in srgb, var(--muted) 34%, transparent);  /* resting node ring */
  --g-node-fill: var(--bg-2);
  --g-live: var(--accent);        /* pulses + the active node */
  --g-label: var(--dim);          /* node captions */
  --g-grid: color-mix(in srgb, var(--muted) 11%, transparent);  /* blueprint dots */
  --g-glow: .55;                  /* brain halo strength, 0–1 */
  --g-fade: 22%;                  /* where the mask reaches full opacity */

  position: relative; min-width: 0; align-self: stretch;
  display: flex; align-items: center; justify-content: center;
  /* Fade toward the text column so the headline always wins. */
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 var(--g-fade), #000 100%);
          mask-image: linear-gradient(to right, transparent 0%, #000 var(--g-fade), #000 100%);
}
html[dir="rtl"] .hero-viz {
  -webkit-mask-image: linear-gradient(to left, transparent 0%, #000 var(--g-fade), #000 100%);
          mask-image: linear-gradient(to left, transparent 0%, #000 var(--g-fade), #000 100%);
}

/* direction:ltr keeps text-anchor GEOMETRIC (start = left, end = right). Under
   dir="rtl" the browser swaps those two and every caption lands on the wrong
   side — the builder already mirrors the x coordinates itself. Arabic glyphs
   inside each caption still shape and order correctly via bidi. */
.hero-viz svg { width: 100%; height: auto; max-height: 72vh; overflow: visible; display: block; direction: ltr; }

/* mouse parallax — the whole scene shifts a few px, nothing more */
.graph-scene { transition: transform .5s cubic-bezier(.22,.8,.3,1); will-change: transform; }

.g-grid-dot { fill: var(--g-grid); }
.g-edge { fill: none; stroke: var(--g-edge); stroke-width: 1; transition: stroke .3s ease; }
.g-edge.lit { stroke: color-mix(in srgb, var(--g-live) 55%, transparent); }

/* The pulse is a short dash running the edge. pathLength="100" normalises every
   path to the same scale, so one keyframe set drives edges of any length. */
.g-pulse {
  fill: none; stroke: var(--g-live); stroke-width: 1.8; stroke-linecap: round;
  stroke-dasharray: var(--pulse-len, 16) 100; stroke-dashoffset: 100; opacity: 0;
  filter: drop-shadow(0 0 4px color-mix(in srgb, var(--g-live) 70%, transparent));
}

.g-node-ring { fill: var(--g-node-fill); stroke: var(--g-node); stroke-width: 1.1; transition: stroke .3s ease, fill .3s ease; }
.g-node.is-hot .g-node-ring, .g-node.lit .g-node-ring { stroke: var(--g-live); }
.g-node-icon { fill: none; stroke: var(--g-label); stroke-width: 1.3; stroke-linecap: round; stroke-linejoin: round; transition: stroke .3s ease; }
.g-node.is-hot .g-node-icon, .g-node.lit .g-node-icon { stroke: var(--g-live); }
.g-label { fill: var(--g-label); font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 9.5px; letter-spacing: .04em; transition: fill .3s ease; }
html[dir="rtl"] .g-label { font-family: var(--font-ar); font-size: 10.5px; letter-spacing: 0; }
.g-node.is-hot .g-label, .g-node.lit .g-label { fill: var(--text); }

/* the agent node breathes continuously — it is the one always-on motion */
.g-halo { fill: var(--g-live); opacity: 0; }
.g-hit { fill: transparent; cursor: default; }

.g-wave rect { fill: var(--g-live); opacity: .25; }

.graph-tip {
  position: absolute; z-index: 5; pointer-events: none; opacity: 0;
  transform: translate(-50%, -100%);
  background: var(--surface-3); border: 1px solid var(--line-strong);
  border-radius: 8px; padding: 6px 10px; white-space: nowrap;
  box-shadow: var(--shadow); transition: opacity .15s ease;
}
.graph-tip.on { opacity: 1; }
.graph-tip b { display: block; font-size: .72rem; font-weight: 700; }
.graph-tip span { color: var(--muted); font-size: .64rem; }

@media (prefers-reduced-motion: reduce) {
  /* static diagram: no travelling pulses, no breathing, no parallax */
  .hero-viz * { animation: none !important; }
  .graph-scene { transition: none !important; }
  .g-pulse { opacity: 0 !important; }
  .g-halo { opacity: calc(var(--g-glow) * .18) !important; }
}

/* ═══════════════════ LIFT — what the work changes ═══════════════════
   Text on one side, an illustrative productivity card on the other.
   The chart carries NO data: no axis values, no point labels. The only real
   numbers in this section are the three KPI tiles. */
.lift { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 54px; align-items: center; }
.lift-copy p { color: var(--muted); font-size: 1.04rem; line-height: 1.95; margin: 0; max-width: 620px; }
html[dir="rtl"] .lift-copy p { line-height: 2.05; }

.lift-card {
  border: 1px solid var(--line); border-radius: 20px; padding: 22px;
  background: linear-gradient(160deg, var(--surface-2), var(--surface));
  box-shadow: var(--shadow), 0 0 60px -28px var(--accent);
  min-width: 0;
}
.lift-card-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.lift-card-top h3 { margin: 0; font-size: .88rem; font-weight: 700; letter-spacing: -.01em; }

/* charts read left-to-right in every locale, so the figure never mirrors */
.lift-chart { margin: 14px 0 18px; direction: ltr; }
.lift-chart svg { width: 100%; height: auto; display: block; }
.lift-base { stroke: var(--line); stroke-width: 1; }
.lift-line {
  fill: none; stroke: url(#liftStroke); stroke-width: 2.6;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 100; stroke-dashoffset: 100;
}
.lift-area { fill: url(#liftFill); opacity: 0; }
.lift-dot { fill: var(--grad-3, #a5b4fc); opacity: 0; }

/* the reveal observer adds .visible once, so these run a single time */
.lift-card.visible .lift-line { animation: liftDraw 1.2s cubic-bezier(.22, .8, .25, 1) forwards; }
.lift-card.visible .lift-area { animation: liftFade .6s ease .95s forwards; }
.lift-card.visible .lift-dot  { animation: liftFade .4s ease 1.15s forwards; }
@keyframes liftDraw { to { stroke-dashoffset: 0; } }
@keyframes liftFade { to { opacity: 1; } }

.kpi-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.kpi-tile {
  border: 1px solid var(--line); border-radius: 12px; padding: 13px 14px;
  background: var(--bg-2); display: flex; flex-direction: column; gap: 5px; min-width: 0;
}
.kpi-tile b { font-size: 1.4rem; font-weight: 800; letter-spacing: -.03em; color: var(--accent); font-variant-numeric: tabular-nums; }
.kpi-tile span { color: var(--muted); font-size: .7rem; line-height: 1.45; }
html[dir="rtl"] .kpi-tile span { font-size: .76rem; }

@media (prefers-reduced-motion: reduce) {
  /* show the finished chart, never draw it */
  .lift-line { stroke-dashoffset: 0 !important; animation: none !important; }
  .lift-area, .lift-dot { opacity: 1 !important; animation: none !important; }
}

/* ───────────────────────── ticker ───────────────────────── */
.strip { width: 100%; max-width: none; padding: 0; border-block: 1px solid var(--line); overflow: hidden; }
.marquee { overflow: hidden; padding: 15px 0; }
.marquee-track {
  width: max-content; display: flex; gap: 22px; align-items: center;
  color: var(--dim); font-family: "JetBrains Mono", monospace;
  font-size: .72rem; text-transform: uppercase; letter-spacing: .1em;
  animation: marquee 34s linear infinite; will-change: transform;
}
.marquee-track i { color: var(--accent); font-style: normal; }
@keyframes marquee { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-50%, 0, 0); } }

/* ───────────────────────── experience ───────────────────────── */
.role-list { display: flex; flex-direction: column; border-top: 1px solid var(--line); }
.role {
  display: grid; grid-template-columns: minmax(0, .3fr) minmax(0, 1.7fr); gap: 30px;
  border-bottom: 1px solid var(--line); padding: 30px 0;
}
.role-when { padding-top: 4px; display: flex; flex-direction: column; gap: 6px; }
.role-when time {
  color: var(--text); font-size: .78rem; font-weight: 600;
  font-family: "JetBrains Mono", ui-monospace, monospace;
}
html[dir="rtl"] .role-when time { font-family: inherit; font-size: .86rem; }
.role-when span { color: var(--dim); font-size: .72rem; }

.role-head { display: flex; align-items: baseline; justify-content: space-between; gap: 18px; flex-wrap: wrap; margin-bottom: 6px; }
.role h3 { margin: 0; font-size: 1.22rem; font-weight: 700; letter-spacing: -.015em; }
.role-org { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin: 0 0 16px; font-size: .86rem; color: var(--muted); }
.role-org a {
  color: var(--accent); font-weight: 600;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  transition: border-color .2s;
}
.role-org a:hover { border-bottom-color: var(--accent); }
.role-org a svg { width: 11px; height: 11px; display: inline; vertical-align: -1px; margin-inline-start: 3px; fill: none; stroke: currentColor; stroke-width: 2; }
.role-body > p { color: var(--muted); font-size: .92rem; line-height: 1.85; margin: 0 0 14px; max-width: 760px; }
/* the one figure in the role copy — lifted out of the muted body colour */
.role-body > p b { color: var(--text); font-weight: 700; }

.role-points { margin: 0 0 4px; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 10px; max-width: 760px; }
.role-points li { position: relative; color: var(--muted); font-size: .88rem; line-height: 1.75; padding-inline-start: 18px; }
.role-points li::before {
  content: ""; position: absolute; inset-inline-start: 0; top: .62em;
  width: 5px; height: 5px; border-radius: 1px; background: var(--accent); opacity: .7;
}
.role-points li b { color: var(--text); font-weight: 700; }

/* ───────────────────────── projects ───────────────────────── */
.work-list { display: flex; flex-direction: column; gap: 14px; }
.work-card {
  border: 1px solid var(--line); border-radius: 18px; background: var(--surface);
  padding: 30px; display: grid; grid-template-columns: 1fr; gap: 36px;
  transition: border-color .25s, transform .25s;
}
.work-card:hover { border-color: var(--accent-line); transform: translateY(-3px); }

/* Marks the shift from internal systems to client work, inside one section. */
.work-group {
  margin: 44px 0 16px; padding-top: 30px; border-top: 1px solid var(--line);
  font-size: .9rem; font-weight: 600; color: var(--muted); letter-spacing: -.01em;
}

.work-card h3 { margin: 0 0 12px; font-size: clamp(1.3rem, 2.4vw, 1.8rem); letter-spacing: -.025em; line-height: 1.22; font-weight: 700; }
.work-card > div > p { color: var(--muted); font-size: .9rem; line-height: 1.8; margin: 0 0 14px; }
.stack-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 16px; }
.stack-tags span {
  border: 1px solid var(--line); border-radius: 6px; padding: 5px 9px;
  font-family: "JetBrains Mono", monospace; font-size: .64rem; color: var(--muted);
}

/* ───────────────────────── stack ───────────────────────── */
/* ── tool marquee ──────────────────────────────────────────────────────────
   Two rows sliding opposite ways. app.js clones each track's children once and
   marks the clone aria-hidden, so the loop is seamless without the names being
   read out twice; the whole block is one role="img" with a full label. */
.tool-rows { display: flex; flex-direction: column; gap: 12px; --tool-dim: #cdcdd2; }
html[data-theme="light"] .tool-rows { --tool-dim: #3a3a42; }

/* The mask fades both ends so chips enter and leave instead of being cut.
   direction:ltr belongs on the ROW, not on the track. A track wider than its
   row is laid out from the row's inline-start edge — under dir="rtl" that is
   the RIGHT edge, so the track hangs off to the left and the -50% translate
   drags its content clean out of view. Pinning the row LTR anchors the track
   at the left in both languages, which is also how the strip should read. */
.tool-row {
  overflow: hidden; width: 100%; direction: ltr;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.tool-track {
  display: flex; width: max-content; gap: 12px; margin: 0; padding: 0; list-style: none;
  animation: toolScroll 46s linear infinite;
  will-change: transform;
}
.tool-track.reverse { animation-direction: reverse; }
.tool-row:hover .tool-track { animation-play-state: paused; }

.tool-track li {
  display: inline-flex; align-items: center; gap: 10px; flex: 0 0 auto;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); padding: 11px 20px 11px 14px;
  font-size: .92rem; font-weight: 600; color: var(--text); white-space: nowrap;
  transition: border-color .2s ease, background .2s ease;
}
.tool-track li:hover { border-color: var(--accent-line); background: var(--surface-2); }
.tool-track svg { width: 22px; height: 22px; flex: 0 0 22px; }
.tool-mono {
  width: 22px; height: 22px; flex: 0 0 22px; border-radius: 6px;
  display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent);
  font-family: "JetBrains Mono", monospace; font-size: .5rem; font-weight: 700;
}

/* -50% because app.js duplicates the children exactly once. */
@keyframes toolScroll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* ───────────────────────── credentials ───────────────────────── */
.cred-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 13px; }
.cred-main {
  border: 1px solid var(--line); border-radius: 18px; padding: 30px; background: var(--surface);
  display: flex; flex-direction: column; justify-content: space-between; gap: 28px; min-height: 230px;
}
.cred-main h3 { margin: 12px 0 6px; font-size: clamp(1.5rem, 2.8vw, 2rem); letter-spacing: -.03em; font-weight: 700; }
.cred-main p { margin: 0; color: var(--muted); font-size: .88rem; }
.cred-main .gpa { margin: 14px 0 0; font-size: .82rem; color: var(--muted); }
.cred-main .gpa strong { color: var(--accent); font-weight: 700; font-variant-numeric: tabular-nums; }
.langs { display: flex; flex-wrap: wrap; gap: 10px 22px; margin-top: 4px; }
.langs span { display: flex; flex-direction: column; gap: 2px; }
.langs b { font-size: .8rem; font-weight: 600; }
.langs i { font-style: normal; color: var(--dim); font-size: .7rem; }
.degree-mark { align-self: flex-end; font-family: "JetBrains Mono", monospace; font-size: 2.6rem; font-weight: 700; color: var(--accent); opacity: .5; letter-spacing: -.05em; line-height: 1; }
html[dir="rtl"] .degree-mark { align-self: flex-start; }

.award-list { border: 1px solid var(--line); border-radius: 18px; background: var(--surface); overflow: hidden; }
.award-item { padding: 16px 20px; border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 13px; }
.award-item:last-child { border-bottom: 0; }
.award-item svg { width: 17px; height: 17px; flex: 0 0 auto; fill: none; stroke: var(--accent); stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.award-item div { min-width: 0; }
.award-item h4 { margin: 0 0 3px; font-size: .84rem; font-weight: 700; line-height: 1.4; }
.award-item p { margin: 0; color: var(--dim); font-size: .72rem; }

/* Certificates — one wrapping strip of text chips, no logos.
   Content is Latin in both languages, so unicode-bidi:isolate keeps each chip
   reading left-to-right even when the page runs RTL. The chips themselves
   still flow with the page direction, so the list stays right-aligned in Arabic. */
.cert-strip {
  grid-column: 1 / -1; margin: 0; padding: 0; list-style: none;
  display: flex; flex-wrap: wrap; gap: 9px;
}
.cert-strip li {
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
  border: 1px solid var(--line); border-radius: 11px;
  background: var(--surface); padding: 11px 15px;
  direction: ltr; unicode-bidi: isolate; text-align: start;
  transition: border-color .2s ease, background .2s ease;
}
.cert-strip li:hover { border-color: var(--accent-line); background: var(--surface-2); }
.cert-strip b { font-size: .8rem; font-weight: 600; line-height: 1.35; }
.cert-strip span {
  color: var(--dim); font-size: .66rem; line-height: 1.4;
  font-family: "JetBrains Mono", ui-monospace, monospace;
}

/* ───────────────────────── contact ───────────────────────── */
.contact-shell {
  border: 1px solid var(--line); border-radius: 22px; padding: 44px;
  /* single column since the form was removed — centred so it doesn't hug one
     edge of the wider section */
  display: grid; grid-template-columns: 1fr; gap: 0; max-width: 760px; margin-inline: auto;
  background: radial-gradient(circle at 12% 8%, var(--accent-soft), transparent 42%), var(--surface);
}
.contact-copy h2 { font-family: "Playfair Display", Georgia, serif; font-size: clamp(2rem, 4vw, 3.2rem); line-height: 1.08; letter-spacing: -.03em; margin: 16px 0 18px; font-weight: 600; }
html[dir="rtl"] .contact-copy h2 { font-family: inherit; font-weight: 700; line-height: 1.3; font-size: clamp(1.9rem, 3.8vw, 3rem); }
.contact-copy > p { color: var(--muted); line-height: 1.8; margin: 0; font-size: .95rem; }
/* ── rotating word in the contact headline ──
   Every option sits in the SAME grid cell, so the cell sizes itself to the
   longest word and the text around it never shifts as they swap. Works for
   either language with no measuring. */
.build-line { display: block; }
/* keep the closing words on one line — "مع بعض" / "together" must not split */
.build-line .nb { white-space: nowrap; }
/* Centred inside the cell: the cell is as wide as the longest option, so a
   short word would otherwise leave all its slack on one side. */
.rotator { display: inline-grid; vertical-align: baseline; text-align: center; }
.rot-word {
  grid-area: 1 / 1; color: var(--accent); white-space: nowrap;
  opacity: 0; transform: translateY(.2em);
  transition: opacity .5s ease, transform .5s cubic-bezier(.2, .8, .2, 1);
}
.rot-word.is-on { opacity: 1; transform: none; }
.rot-word.is-out { opacity: 0; transform: translateY(-.2em); }

@media (prefers-reduced-motion: reduce) {
  /* rotating type is motion — hold the first word and never cycle */
  .rot-word { transition: none; }
}

.contact-cta { display: flex; gap: 11px; flex-wrap: wrap; margin-top: 28px; }

/* ── LinkedIn row: an iMessage bubble that pops out of the handle ──
   Absolutely positioned so revealing it never changes the row's height, and
   the rows below never shift. */
.link-val { position: relative; display: flex; flex-direction: column; align-items: flex-end; min-width: 0; }
.chat-bubble {
  position: absolute; top: calc(100% + 9px); inset-inline-end: 0; z-index: 3;
  width: max-content; max-width: min(30ch, 60vw);
  /* #0a74e0 rather than iOS's #0b84ff: same blue read, but white on it clears
     4.58:1 instead of 3.65:1, so the text is legible at this size. */
  background: #0a74e0; color: #fff;
  border-radius: 19px; padding: 9px 15px;
  font-size: .74rem; font-weight: 500; line-height: 1.55; text-align: start;
  box-shadow: 0 8px 22px rgba(10, 116, 224, .3);
}
html[dir="rtl"] .chat-bubble { font-size: .8rem; }

/* iOS tail: a rounded nub on the bubble's trailing corner */
.chat-bubble::after {
  content: ""; position: absolute; top: -4px; inset-inline-end: 17px;
  width: 13px; height: 13px; background: #0a74e0;
  border-radius: 4px 0 0 0; transform: rotate(45deg);
}

/* Reveal on hover/focus — only where a pointer can actually hover. On touch
   there is nothing to hover with, so the bubble just stays visible. */
@media (hover: hover) and (pointer: fine) {
  .chat-bubble {
    opacity: 0; pointer-events: none;
    transform: scale(.72) translateY(-8px);
    transform-origin: top var(--bubble-origin, right);
    /* the overshoot in the curve is what gives it the iOS "pop" */
    transition: opacity .16s ease, transform .36s cubic-bezier(.34, 1.56, .64, 1);
  }
  html[dir="rtl"] .chat-bubble { --bubble-origin: left; }
  .has-bubble:hover .chat-bubble,
  .has-bubble:focus-visible .chat-bubble,
  .has-bubble:active .chat-bubble { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .chat-bubble { transition: opacity .16s ease !important; transform: none !important; }
}

.contact-links { margin-top: 32px; }
.contact-links a { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 14px 0; border-top: 1px solid var(--line); transition: color .2s; }
.contact-links a:hover { color: var(--accent); }
/* Only the field label — scoping to the first child keeps the mono/uppercase
   treatment off the value wrapper and the speech bubble inside it. */
.contact-links a > span:first-child { color: var(--dim); font-family: "JetBrains Mono", monospace; font-size: .66rem; letter-spacing: .1em; text-transform: uppercase; }
html[dir="rtl"] .contact-links a > span:first-child { font-family: inherit; letter-spacing: 0; text-transform: none; font-size: .76rem; }
.contact-links strong { font-size: .84rem; font-weight: 600; overflow-wrap: anywhere; }

/* ───────────────────────── footer ───────────────────────── */
footer {
  width: min(calc(100% - 40px), var(--max)); margin: 0 auto; padding: 26px 0 32px;
  border-top: 1px solid var(--line); display: flex; align-items: center; justify-content: space-between;
  gap: 18px; color: var(--dim); font-size: .74rem; flex-wrap: wrap;
}
footer a:hover { color: var(--accent); }

/* ───────────────────────── reveal ───────────────────────── */
.reveal { opacity: 0; transform: translateY(18px); transition: opacity .6s ease, transform .6s cubic-bezier(.2, .8, .2, 1); }
.reveal.visible { opacity: 1; transform: none; }
.delay-1 { transition-delay: .08s; }
.delay-2 { transition-delay: .16s; }

/* ───────────────────────── responsive ───────────────────────── */
@media (max-width: 1040px) {
  .work-card { gap: 24px; }
}

/* Below the two-column breakpoint there is no empty half left, so the graph
   drops behind the hero as faint texture rather than competing with the text. */
@media (max-width: 1040px) {
  .hero { grid-template-columns: 1fr; }
  /* no room beside the copy — the productivity card stacks under it */
  .lift { grid-template-columns: 1fr; gap: 34px; }
  /* the headline gets the full width back once the graph leaves the row */
  .hero h1 { font-size: clamp(2.2rem, 7vw, 4.6rem); }
  html[dir="rtl"] .hero h1 { font-size: clamp(2rem, 5.6vw, 3.7rem); }
  .hero-viz {
    position: absolute; inset: 0; z-index: -1; opacity: .2; pointer-events: none;
    --g-fade: 0%;
    -webkit-mask-image: radial-gradient(70% 60% at 50% 45%, #000 0%, transparent 78%);
            mask-image: radial-gradient(70% 60% at 50% 45%, #000 0%, transparent 78%);
  }
  html[dir="rtl"] .hero-viz {
    -webkit-mask-image: radial-gradient(70% 60% at 50% 45%, #000 0%, transparent 78%);
            mask-image: radial-gradient(70% 60% at 50% 45%, #000 0%, transparent 78%);
  }
  .hero-viz svg { max-height: none; height: 100%; }
  .graph-tip { display: none; }
}

@media (max-width: 680px) {
  /* small screens: the hero is already tall and the text runs edge to edge */
  .hero-viz { display: none; }
}

@media (max-width: 920px) {
  .nav {
    position: absolute; inset-inline: 0; top: calc(100% + 8px); display: none;
    flex-direction: column; align-items: stretch; gap: 0; padding: 9px;
    border: 1px solid var(--line); border-radius: 14px; background: var(--surface-2); box-shadow: var(--shadow);
  }
  .nav.open { display: flex; }
  .nav a { padding: 12px; }
  .menu-btn { display: block; }
  .brand { margin-inline-end: auto; }
  .cred-grid { grid-template-columns: 1fr; }

  .contact-shell { grid-template-columns: 1fr; gap: 32px; }
}

@media (max-width: 680px) {
  .site-header { top: max(8px, env(safe-area-inset-top)); width: calc(100% - 18px); padding: 7px 8px; gap: 8px; }
  .brand-copy { display: none; }
  .section { width: calc(100% - 28px); padding: 70px 0; }
  .hero { width: calc(100% - 28px); min-height: auto; padding: 118px 0 56px; }
  .hero h1 { font-size: clamp(2.2rem, 11.5vw, 3.4rem); }
  html[dir="rtl"] .hero h1 { font-size: clamp(1.8rem, 8.6vw, 2.9rem); }
  .hero-lead { font-size: .96rem; }
  .hero-actions { display: grid; grid-template-columns: 1fr; }
  .hero-actions .btn { width: 100%; min-height: 50px; }
  .hero-meta { grid-template-columns: 1fr 1fr; }
  .hero-meta div:last-child { display: none; }
  .work-card { padding: 22px 18px; border-radius: 15px; }
  .contact-shell { padding: 24px 18px; border-radius: 16px; }
  .cred-main { padding: 24px; }
  .contact-links a { flex-direction: column; align-items: flex-start; gap: 5px; }
  footer { padding-bottom: max(26px, calc(env(safe-area-inset-bottom) + 16px)); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; }
  .reveal { opacity: 1; transform: none; }

  .marquee-track { animation: marquee 110s linear infinite !important; }
  .tool-track { animation: none !important; }
}

@media print {
  .site-header, .marquee { display: none !important; }
  .reveal { opacity: 1 !important; transform: none !important; }
}
