/* ==========================================================================
   mohamedgharkan.com

   Visual language adapted from the "Ober" WordPress theme by bslthemes,
   under Mohamed's ThemeForest licence. Rebuilt as plain CSS.

   One stylesheet serves both languages: every directional rule uses CSS
   logical properties (inline-start / inline-end), so the Arabic RTL layout
   is the same layout resolved the other way round, not a mirrored copy.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
	--bg:          #F7F5F2;
	--bg-alt:      #EBE7E0;
	--ink:         #1C2528;
	--body:        #3C4649;
	--accent:      #FF8059;
	/* The bright orange is Mo's colour, but on a cream page it only reaches
	   2.3:1 — below AA for text of any size. Fills, rules and shapes keep
	   it; anything that has to be *read* uses the deeper one, which clears
	   4.5:1 on all three light surfaces. In dark mode the bright orange
	   already clears 6:1, so there it is the same colour. */
	--accent-text: #B03C16;
	/* Dark ink on the orange fill: 6.3:1, where white would be 2.5:1. */
	--accent-ink:  #1C2528;
	--rule:        rgba(28, 37, 40, 0.18);
	--card:        #FFFFFF;
	--field:       #FFFFFF;
	--shadow:      0 18px 50px -24px rgba(28, 37, 40, 0.28);

	/* Hero shape palette — taken from the theme's own hero SVGs */
	--shape-orange: #FF8059;
	--shape-teal:   #3AAFC9;
	--shape-pale:   #B9D1E4;
	--shape-violet: #676CDB;
	--shape-yellow: #FFD041;

	--font-body:       "Rubik", system-ui, -apple-system, sans-serif;
	--font-display:    "Sorts Mill Goudy", Georgia, serif;
	/* Overridden per page in <head>; this is the fallback if that is missing. */
	--font-display-ar: "El Messiri", "IBM Plex Sans Arabic", system-ui, sans-serif;
	--font-body-ar:    "IBM Plex Sans Arabic", system-ui, sans-serif;

	/* Milestones are printed in two inks. Orange is the constant; the other
	   end swaps with the theme, so the photographs sit on their background
	   in either one. */
	--duo-shadow: #1C2528;
	--duo-light:  #FF8059;
	/* Project marks are shown without their colours until the pointer is on
	   them. Grey rather than a flat silhouette: more than one of these marks
	   carries pale lettering inside a solid shape, and painting the whole
	   thing one ink would swallow the words — Mufakiru's strapline sits in a
	   filled bar and would have become a black brick. */
	--logo-mono:  grayscale(1) contrast(1.2) brightness(0.34);

	--wrap:    1240px;
	--gutter:  clamp(20px, 5vw, 60px);
	--ease:    cubic-bezier(0.3, 0, 0.3, 1);
}

:root[data-theme="dark"] {
	--bg:         #1A1F26;
	--bg-alt:     #14181E;
	--ink:        #F4F2EF;
	--body:       #A8B0B8;
	--rule:       rgba(244, 242, 239, 0.18);
	--card:       #222830;
	--field:      #222830;
	--shadow:     0 18px 50px -24px rgba(0, 0, 0, 0.6);
	--accent-text: var(--accent);
	/* The same two-ink idea, warmed and lifted: on a near-black page the
	   light pair has to be the brighter of the two, or the photographs sink
	   into the background at their edges. Deep coffee to pale orange. */
	--duo-shadow: #4A2A1C;
	--duo-light:  #FFC9A8;
	--logo-mono:  grayscale(1) contrast(1.1) invert(1) brightness(1.6);
}

/* The Arabic page runs on its own two families — El Messiri for display,
   IBM Plex Sans Arabic for everything else. Rubik and the English serif are
   not loaded there at all. Arabic needs more leading and a slightly larger
   body size than Latin to stay comfortable. */
html[lang="ar"] {
	--font-display: var(--font-display-ar);
	--font-body:    var(--font-body-ar);
}
html[lang="ar"] body {
	font-size: clamp(17px, 1.2vw, 20px);
	line-height: 1.9;
}


/* --------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
}

/* Lenis drives scrolling when it is active; let it own the scroll position. */
html.lenis { scroll-behavior: auto; }
html.lenis body { height: auto; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }

body {
	margin: 0;
	background: var(--bg);
	color: var(--body);
	font-family: var(--font-body);
	font-weight: 300;
	font-size: clamp(16px, 1.15vw, 20px);
	line-height: 1.7;
	-webkit-font-smoothing: antialiased;
	transition: background 0.7s var(--ease), color 0.7s var(--ease);
	overflow-x: hidden;
}

body.is-locked { overflow: hidden; }

img { max-width: 100%; height: auto; display: block; }

/* Any rule that sets `display` beats the hidden attribute's own
   display:none, and several here do — .btn, for one. This says plainly
   that hidden means hidden, whatever else an element is. */
[hidden] { display: none !important; }

a {
	color: var(--accent-text);
	text-decoration: none;
	transition: color 0.4s var(--ease), opacity 0.4s var(--ease);
}
/* A link in running text underlines on hover, in the accent. Anything
   that is really a control — a button, a menu item, an icon — opts out. */
a:hover {
	text-decoration: underline;
	text-decoration-color: var(--accent);
	text-decoration-thickness: 2px;
	text-underline-offset: 0.22em;
}
.btn:hover,
.social-links a:hover,
.lang-switch:hover,
.icon-btn:hover,
.skip-link:hover,
.site-footer a:hover { text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3, h4 {
	color: var(--ink);
	font-weight: 400;
	line-height: 1.25;
	margin: 0 0 0.6em;
}

p { margin: 0 0 1.4em; }
p:last-child { margin-bottom: 0; }

/* Keeps a single word from being left alone on the last line.
   Ignored by browsers that don't support it. */
p, .about-points li, .cta-list li, .cv-list p { text-wrap: pretty; }

::selection { background: var(--accent); color: var(--accent-ink); }

:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 3px;
	border-radius: 2px;
}

.visually-hidden {
	position: absolute;
	width: 1px; height: 1px;
	margin: -1px; padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.skip-link {
	position: absolute;
	inset-inline-start: var(--gutter);
	top: -100px;
	z-index: 200;
	background: var(--accent);
	color: var(--accent-ink);
	padding: 12px 22px;
	transition: top 0.3s var(--ease);
}
.skip-link:focus { top: 16px; text-decoration: none; }

.wrap {
	max-width: var(--wrap);
	margin-inline: auto;
	padding-inline: var(--gutter);
}


/* --------------------------------------------------------------------------
   3. Header
   -------------------------------------------------------------------------- */

.site-header {
	position: fixed;
	inset-block-start: 0;
	inset-inline: 0;
	z-index: 90;
	padding-block: 28px;
	transition: background 0.5s var(--ease), padding 0.5s var(--ease),
	            box-shadow 0.5s var(--ease);
}

.site-header.is-stuck {
	padding-block: 16px;
	background: color-mix(in srgb, var(--bg) 88%, transparent);
	backdrop-filter: blur(14px);
	box-shadow: 0 1px 0 var(--rule);
}

.site-header .wrap {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 20px;
}

.wordmark {
	font-family: var(--font-display);
	font-size: clamp(20px, 2vw, 28px);
	color: var(--ink);
	line-height: 1;
	white-space: nowrap;
}
.wordmark:hover { text-decoration: none; opacity: 0.7; }

html[lang="ar"] .wordmark { font-size: clamp(22px, 2.3vw, 32px); }

.header-tools {
	display: flex;
	align-items: center;
	gap: clamp(10px, 1.6vw, 22px);
}

.icon-btn {
	appearance: none;
	background: none;
	border: 0;
	padding: 8px;
	margin: 0;
	cursor: pointer;
	color: var(--ink);
	line-height: 0;
	border-radius: 50%;
	transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}
.icon-btn:hover { opacity: 0.6; }
.icon-btn svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.6; }

/* Sun / moon crossfade */
.icon-btn .i-sun { display: none; }
:root[data-theme="dark"] .icon-btn .i-sun  { display: block; }
:root[data-theme="dark"] .icon-btn .i-moon { display: none; }

.lang-switch {
	font-size: 14px;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--ink);
	padding: 8px 12px;
	border: 1px solid var(--rule);
	border-radius: 100px;
	line-height: 1;
	font-weight: 400;
}
.lang-switch:hover {
	text-decoration: none;
	background: var(--ink);
	color: var(--bg);
	border-color: var(--ink);
}

/* Hamburger */
.menu-toggle { width: 38px; height: 38px; position: relative; }
.menu-toggle span {
	position: absolute;
	inset-inline: 9px;
	height: 1.5px;
	background: var(--ink);
	transition: transform 0.45s var(--ease), opacity 0.3s var(--ease);
}
.menu-toggle span:nth-child(1) { top: 13px; }
.menu-toggle span:nth-child(2) { top: 19px; }
.menu-toggle span:nth-child(3) { top: 25px; }
.menu-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.menu-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.menu-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }


/* --------------------------------------------------------------------------
   4. Fullscreen menu
   -------------------------------------------------------------------------- */

.fullscreen-menu {
	position: fixed;
	inset: 0;
	z-index: 80;
	background: var(--bg-alt);
	display: grid;
	place-items: center;
	opacity: 0;
	visibility: hidden;
	transition: opacity 0.6s var(--ease), visibility 0.6s var(--ease);
}
.fullscreen-menu.is-open { opacity: 1; visibility: visible; }

.fullscreen-menu ul {
	list-style: none;
	margin: 0;
	padding: 0;
	text-align: center;
}
.fullscreen-menu li {
	opacity: 0;
	transform: translateY(26px);
	transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
}
.fullscreen-menu.is-open li { opacity: 1; transform: none; }
.fullscreen-menu.is-open li:nth-child(1) { transition-delay: 0.10s; }
.fullscreen-menu.is-open li:nth-child(2) { transition-delay: 0.16s; }
.fullscreen-menu.is-open li:nth-child(3) { transition-delay: 0.22s; }
.fullscreen-menu.is-open li:nth-child(4) { transition-delay: 0.28s; }
.fullscreen-menu.is-open li:nth-child(5) { transition-delay: 0.34s; }

.fullscreen-menu a {
	font-family: var(--font-display);
	font-size: clamp(34px, 6vw, 68px);
	line-height: 1.5;
	color: var(--ink);
	display: block;
	padding: 2px 0;
}
.fullscreen-menu a:hover { text-decoration: none; color: var(--accent-text); }
.fullscreen-menu a[aria-current] {
	color: var(--accent-text);
	text-decoration: underline;
	text-decoration-color: var(--accent);
	text-decoration-thickness: 2px;
	text-underline-offset: 0.18em;
}

/* Arabic needs more leading at display sizes so marks and descenders
   on one line clear the line above. */
html[lang="ar"] .fullscreen-menu a { line-height: 1.85; }


/* --------------------------------------------------------------------------
   5. Sections & the big fading titles
   -------------------------------------------------------------------------- */

/* Each section carries the colour of its own shape, so anything inside
   that wants a tint — the expertise chips, for one — can just ask for it. */
#about       { --section-accent: var(--shape-teal); }
#resume      { --section-accent: var(--shape-yellow); }
#works       { --section-accent: var(--shape-violet); }
#collaborate { --section-accent: var(--shape-orange); }

.section { padding-block: clamp(70px, 10vw, 150px); position: relative; overflow: hidden; }
.section > .wrap { position: relative; z-index: 1; }

/* One quiet shape per section, at the outer edge and behind everything
   else. They sit in the gutter and hang off the page; the section clips
   what hangs over, so nothing here can widen the document. --sy is the
   scroll drift, written from main.js. */
.section-shape {
	position: absolute;
	z-index: 0;
	line-height: 0;
	pointer-events: none;
	--sy: 0px;
	translate: 0 var(--sy);
	will-change: translate;
	opacity: .9;
}
.section-shape svg { display: block; width: 100%; height: auto; }

.section-shape--about       { width: clamp(84px, 11vw, 190px); inset-block-start: 34%; inset-inline-end: -3%; }
.section-shape--resume      { width: clamp(96px, 13vw, 215px); inset-block-start: 30%; inset-inline-start: -4%; }
.section-shape--works       { width: clamp(90px, 12vw, 205px); inset-block-start: 26%; inset-inline-end: -2%; }
.section-shape--collaborate { width: clamp(130px, 18vw, 300px); inset-block-end: -12%; inset-inline-start: -8%; }

/* Below the two-column breakpoint the text runs the full width of the
   page, so there is no margin left to sit in. The shapes drop into the
   band of padding at the foot of each section instead, where they are
   under nothing at all. */
@media (max-width: 860px) {
	.section-shape {
		width: clamp(74px, 23vw, 120px);
		inset-block-start: auto;
		inset-block-end: -16px;
	}
	.section-shape--about       { inset-inline-end: -10%; }
	.section-shape--resume      { inset-inline-start: -12%; }
	.section-shape--works       { inset-inline-end: -9%; }
	.section-shape--collaborate { width: clamp(110px, 32vw, 170px); inset-inline-start: -18%; inset-block-end: -8%; }
}

.section--alt { background: var(--bg-alt); }

/* The oversized serif heading whose tail fades out.
   A mask on the whole word — never per-letter, which would break
   Arabic letter joining. */
.section-title {
	font-family: var(--font-display);
	font-size: clamp(54px, 11vw, 150px);
	line-height: 1;
	color: var(--ink);
	margin: 0 0 clamp(30px, 5vw, 60px);
	white-space: nowrap;
	overflow: hidden;
	/* Hug the text so the fade falls across the word itself rather than
	   across empty space beside it. */
	width: fit-content;
	max-width: 100%;
}

/* Only the last quarter or so of the word fades, so every word in a
   multi-word title stays readable — and the mask is on the word alone,
   never on the h2, so the full stop after it keeps its full colour. */
html:not([lang="ar"]) .section-title .title-text {
	display: inline-block;
	-webkit-mask-image: linear-gradient(to right, #000 74%, transparent 100%);
	        mask-image: linear-gradient(to right, #000 74%, transparent 100%);
}
html:not([lang="ar"])[dir="rtl"] .section-title .title-text {
	-webkit-mask-image: linear-gradient(to left, #000 74%, transparent 100%);
	        mask-image: linear-gradient(to left, #000 74%, transparent 100%);
}

/* Arabic gets no fade at all: thinning the tail of a word damages the
   letterforms. Instead the whole title starts faint and fills with ink from
   right to left as the section arrives, driven by --fill from main.js. The
   gradient is painted on the text itself, so the string is never split. */
html[lang="ar"] .section-title {
	line-height: 1.35;
	padding-block-end: 0.08em;
	font-weight: 600;
	overflow: visible;
	white-space: normal;
	-webkit-mask-image: none;
	        mask-image: none;
	position: relative;
	--fill: 0%;
}

html[lang="ar"] .title-text {
	background-image: linear-gradient(
		to left,
		var(--ink) var(--fill),
		color-mix(in srgb, var(--ink) 18%, transparent) var(--fill));
	-webkit-background-clip: text;
	        background-clip: text;
	color: transparent;
}
/* Registered so the value is a real percentage rather than a raw token;
   main.js rewrites it every frame, so it needs no transition of its own. */
@property --fill {
	syntax: "<percentage>";
	inherits: true;
	initial-value: 0%;
}
/* If background-clip:text is unavailable the title simply stays solid ink
   rather than vanishing. */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
	html[lang="ar"] .title-text { color: var(--ink); background-image: none; }
}

/* Decoration at display size, hidden from screen readers: Mo's own
   orange, not the reading one. */
.title-dot { color: var(--accent); }

/* The faint English word behind the Arabic one. Off unless site.json asks
   for it; it is decoration, so it is hidden from screen readers. */
.title-ghost {
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: 0;
	transform: translateY(-50%);
	font-family: "Sorts Mill Goudy", Georgia, serif;
	font-size: 1.5em;
	line-height: 1;
	color: var(--ink);
	opacity: 0.06;
	white-space: nowrap;
	pointer-events: none;
	z-index: -1;
}

/* Two-column frame: thin vertical rule with metadata outside it. */
.framed {
	display: grid;
	grid-template-columns: minmax(150px, 22%) 1fr;
	gap: clamp(24px, 4vw, 60px);
	align-items: start;
}
.framed > .framed-aside {
	text-align: end;
	padding-block: 10px;
	/* These labels carry a short sentence now, not two words, so they are set
	   small and quiet: a caption beside the column, not a second heading. */
	font-size: 0.8em;
	line-height: 1.6;
	color: var(--body);
}
/* English hangs the label off the rule it sits beside. Arabic cannot do the
   same: end-aligned there means left-aligned, so a label that wrapped left
   its second line hanging away from the column it belongs to, with ragged
   space on the reading edge. In Arabic it is aligned to the start instead —
   flush with the right-hand edge the eye begins from — and the two lines are
   evened out so the first is never left long and the second a single word. */
html[lang="ar"] .framed > .framed-aside {
	text-align: start;
	text-wrap: balance;
}
.framed > .framed-main {
	border-inline-start: 1px solid var(--rule);
	padding-inline-start: clamp(24px, 4vw, 60px);
	padding-block: 10px;
}

.lede { font-size: 1.05em; max-width: 56ch; }

/* How the work runs, start to finish: one line of stages. Set a little
   wider and looser than body copy so the arrows have room to breathe. */
.workflow {
	font-size: 0.85em;
	line-height: 2;
	max-width: 62ch;
	margin: 0;
	color: var(--body);
}


/* --------------------------------------------------------------------------
   6. Hero
   -------------------------------------------------------------------------- */

.hero {
	padding-block-start: clamp(118px, 14vh, 170px);
	padding-block-end: clamp(50px, 7vw, 90px);
	position: relative;
	overflow: hidden;
}

.hero-grid {
	display: grid;
	grid-template-columns: 1fr minmax(300px, 52%);
	gap: clamp(20px, 4vw, 50px);
	align-items: center;
}

/* Sized so three roles settle on two lines at desktop rather than three,
   without crowding the name below. */
.hero-role {
	font-size: clamp(14px, 1.3vw, 19px);
	text-transform: uppercase;
	/* Tighter than the rest of the small caps on the page: three roles of
	   spaced-out uppercase would otherwise run to three lines. */
	letter-spacing: 0.055em;
	line-height: 1.5;
	font-weight: 400;
	color: var(--body);
	margin: 0;
	/* Wide enough that three roles settle on two lines at desktop; the
	   grid column is the real constraint. */
	max-width: 34em;
}
html[lang="ar"] .hero-role {
	text-transform: none;
	letter-spacing: 0;
	font-size: clamp(15px, 1.4vw, 20px);
}

/* Each title stays in one piece; a line that has to wrap breaks at a
   bullet instead. The bullets are the accent colour. */
.role-part { white-space: nowrap; }
.role-sep  { padding-inline: 0.2em; color: var(--accent-text); }

/* Two ways of setting the three roles, chosen in data/site.json. Both
   share one rule: a bullet never ends a line.

   "stacked" — each role on its own line, no bullets at all. */
[data-role="stacked"] .hero-role {
	display: flex;
	flex-direction: column;
	align-items: start;
	gap: 0.12em;
}
[data-role="stacked"] .role-sep { display: none; }

/* "inline" — one line with bullets between the roles. It starts from the
   stacked setting and only goes to one line where the whole line actually
   fits the column, which on a desktop is Arabic but not English: the three
   English titles measure about 790px against a 512px column, and would
   need roughly 10px type to fit. Everything else keeps the stacked
   setting, so a bullet can never end up dangling at a line end. */
[data-role="inline"] .hero-role {
	display: flex;
	flex-direction: column;
	align-items: start;
	gap: 0.12em;
}
[data-role="inline"] .role-sep { display: none; }

@media (min-width: 1101px) {
	html[lang="ar"] [data-role="inline"] .hero-role {
		display: block;
		white-space: nowrap;
		font-size: clamp(15px, 1.35vw, 20px);
	}
	html[lang="ar"] [data-role="inline"] .role-sep { display: inline; }
}

.hero-name {
	font-family: var(--font-display);
	font-size: clamp(52px, 9.2vw, 118px);
	line-height: 0.95;
	color: var(--ink);
	margin: 0.08em 0 0.3em;
	font-weight: 400;
}
html[lang="ar"] .hero-name {
	line-height: 1.3;
	font-size: clamp(40px, 7.4vw, 96px);
}

.hero-intro {
	border-inline-start: 2px solid var(--accent);
	padding-inline-start: clamp(20px, 2.6vw, 40px);
	margin-inline-start: clamp(0px, 6vw, 100px);
	max-width: 460px;
	font-size: clamp(16px, 1.4vw, 21px);
	color: var(--body);
}
/* The tagline is the one line of prose in the hero: full body ink, not the
   quieter grey used for supporting text elsewhere. */
.hero-intro p { color: var(--ink); }

/* In Arabic the name above is a drawing that spans the whole column, so the
   tagline is given the same measure and starts at the same edge. It then
   settles into two balanced lines instead of three short ones. */
html[lang="ar"] .hero-intro {
	margin-inline-start: 0;
	max-width: min(100%, 620px);
}

/* Evens out the line lengths so the last line is never a single orphaned
   word. Browsers without support just wrap normally. */
.hero-intro p { text-wrap: balance; }

/* The second hero line: quieter than the tagline it follows. */
.hero-support {
	font-size: 0.82em;
	color: var(--body);
	margin-block-start: 0.7em;
}

/* Tighter than a section's button row — it sits inside the hero column. */
.btn-row--hero { margin-block-start: 26px; gap: 12px; }

.social-links {
	display: flex;
	gap: 18px;
	margin-block-start: 26px;
}
.social-links a {
	color: var(--ink);
	line-height: 0;
	padding: 4px;
	transition: color 0.4s var(--ease), transform 0.4s var(--ease);
}
.social-links a:hover { color: var(--accent-text); transform: translateY(-3px); }
.social-links svg { width: 20px; height: 20px; fill: currentColor; }

/* Facts row under the hero */
.hero-facts {
	list-style: none;
	margin: clamp(40px, 6vw, 72px) 0 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 20px;
}
.hero-facts li { font-size: clamp(14px, 1.1vw, 19px); }
.hero-facts li:nth-child(2) { text-align: center; }
.hero-facts li:nth-child(3) { text-align: end; }
/* The figure itself carries the colour; the words around it do not. */
.num { color: var(--accent-text); }

.hero-facts strong {
	display: block;
	color: var(--ink);
	font-weight: 500;
}

/* --- Portrait + geometric shapes ------------------------------------------ */

.hero-figure {
	position: relative;
	/* Roughly a quarter wider than it was, so the portrait holds its own
	   beside the name. It stays inside the content column: pushed into the
	   outer gutter it ran off the side of the screen. */
	width: 100%;
	max-width: 700px;
	margin: 0;
	margin-inline-start: auto;
}

/* The portraits and the shapes are one fixed picture, built with physical
   left/right on purpose: the composition should look identical in Arabic
   and English rather than mirroring with the text. */
.hero-stage {
	position: relative;
	aspect-ratio: 749 / 918;
	width: 100%;
	direction: ltr;
}

/* The cast: a box the size of the stage that clips at its floor, so the
   portrait can rise in from underneath at full opacity. */
.hero-cast {
	position: absolute;
	inset: 0;
	overflow: hidden;
	z-index: 5;
}

/* Two cut-outs stacked on one canvas. Both are fully opaque at every
   moment: the incoming one is wiped in on top of the outgoing one, which
   only drops away once it is completely hidden behind it. No opacity is
   ever in play between them, so the two poses are never both visible and
   neither is ever seen through. */
.hero-portrait {
	position: absolute;
	bottom: 0;
	left: 50%;
	/* --pdy carries him down with the shapes he stands in front of as the
	   hero scrolls away; if he stayed put while they sank, the cut edge of
	   the photo would come out from behind them. */
	--pdy: 0px;
	transform: translateX(-50%) translateY(var(--pdy));
	transition: transform 1s var(--ease);
	height: 100%;
	width: auto;
	max-width: none;
	z-index: 1;
	opacity: 0;
	/* The cut-out ends in a straight line at the waist, and the two front
	   domes leave a gap between them where that line would show. Softening
	   the last few per cent of the image means there is no edge to see,
	   whatever the width or the pose: he fades into the composition rather
	   than stopping in it. */
	-webkit-mask-image: linear-gradient(to bottom, #000 86%, rgba(0,0,0,0.5) 94%, transparent 99%);
	        mask-image: linear-gradient(to bottom, #000 86%, rgba(0,0,0,0.5) 94%, transparent 99%);
	/* ...and the image is cut off at the domes' baseline as well, so not
	   even a ghost of it can appear below the line they stand on. The depth
	   of that cut is a variable because each composition sets its own, and
	   the wipe has to finish on exactly the value in force. */
	--cut: 1%;
	clip-path: inset(0 0 var(--cut) 0);
}
.hero-portrait.is-active { opacity: 1; z-index: 2; }
/* the one being replaced: still solid, just underneath */
.hero-portrait.is-fading { z-index: 1; }
.hero-portrait.is-fading.is-wiping-out {
	animation: portrait-unwipe 0.7s cubic-bezier(.65, 0, .35, 1) forwards;
}

/* The change of pose: the new portrait is uncovered from the floor upwards
   over the old one, both of them opaque throughout. A cross-fade let the
   two poses show through each other half-way — a double exposure — which
   is what this replaces. The travelling edge is the only thing that moves;
   neither figure shifts, so the head stays exactly where it was. */
.hero-portrait.is-wiping { animation: portrait-wipe 0.7s cubic-bezier(.65, 0, .35, 1); }
/* The new pose starts at zero height sitting ON the cut line, not at the
   very bottom of the frame: the old pose is taken away from that same line
   upwards, so starting anywhere else leaves the two edges a percent or two
   apart and a sliver of background shows between them as they travel. */
@keyframes portrait-wipe {
	from { clip-path: inset(calc(100% - var(--cut)) 0 var(--cut) 0); }
	to   { clip-path: inset(0 0 var(--cut) 0); }
}
/* The old pose is taken away behind the same travelling edge, on the same
   curve. Without this it simply waited underneath, and wherever the new
   silhouette was the narrower of the two — a shoulder, the line of a
   jacket — the old one stood out around it for the length of the wipe. Now
   the edge divides the picture: below it the new pose, above it the old,
   and never any part of both. It holds its end state until the class comes
   off, so there is no frame in which it flashes back. */
@keyframes portrait-unwipe {
	from { clip-path: inset(0 0 var(--cut) 0); }
	to   { clip-path: inset(0 0 100% 0); }
}

/* He rises in from under the floor once the shapes have landed — position
   only, never opacity, so the orange disc is never seen through him. The
   cast is what hides him until he clears it. It animates `translate`
   rather than another transform, so the one-off entrance and the running
   drift stay out of each other's way, and it fills backwards rather than
   both ways: the moment it ends he goes back to taking his opacity from
   .is-active, which is what the crossfade needs. is-entering is a one-shot
   class, so a later change of portrait is a crossfade and not another
   rise. */
html.js .hero-stage:not(.is-in) .hero-portrait { translate: 0 101%; }
html.js .hero-stage.is-entering .hero-portrait.is-active {
	animation: portrait-rise 1.15s cubic-bezier(.22, 1, .36, 1) .45s backwards;
}
@keyframes portrait-rise {
	from { translate: 0 101%; }
	to   { translate: 0 0; }
}

.hero-stage .shape {
	position: absolute;
	bottom: 0;
	left: 0;
	line-height: 0;
	will-change: transform, opacity;
}

/* Layer 1 of 3 — the entrance. Each shape arrives small and low and
   springs very slightly past its resting size, one after the next. The
   delay is set per shape in the stagger block below. */
html.js .hero-stage .shape {
	opacity: 0;
	transform: translateY(22px) scale(0.84);
	transition: opacity .45s var(--ease), transform .85s cubic-bezier(.34, 1.42, .64, 1);
}
html.js .hero-stage.is-in .shape {
	opacity: 1;
	transform: none;
	transition-delay: var(--in-delay, 0ms);
}

/* Layer 2 — the mouse. A few pixels against the pointer, further for the
   shapes meant to read as nearer. Short transition so it follows the hand
   rather than trailing it. */
.hero-stage .shape-in {
	display: block;
	--mx: 0px; --my: 0px;
	transform: translate3d(var(--mx), var(--my), 0);
	transition: transform .4s cubic-bezier(.22, 1, .36, 1);
}

/* Layer 3 — the pose the shapes take when the portrait changes, and the
   drift as the hero scrolls away. Composed in one transform from four
   custom properties so neither effect can erase the other, and scaled
   from the foot of the shape so anything standing on the floor stays on
   it. */
.hero-stage .shape svg {
	--dx: 0px; --dy: 0px; --rot: 0deg; --sc: 1;
	transform: translate3d(var(--dx), var(--dy), 0) rotate(var(--rot)) scale(var(--sc));
	transform-origin: 50% 100%;
	transition: transform 1s var(--ease);
}

/* The stagger. The two that stand in front come first, because they are
   the ground he lands on — while they are still half transparent the cut
   edge of the photo shows straight through them. */
.hero-stage .shape-2, .hero-stage .shape-b4 { --in-delay:   0ms; }
.hero-stage .shape-3, .hero-stage .shape-b5 { --in-delay:  80ms; }
.hero-stage .shape-1, .hero-stage .shape-b1 { --in-delay: 160ms; }
.hero-stage .shape-4, .hero-stage .shape-b2 { --in-delay: 240ms; }
.hero-stage .shape-5, .hero-stage .shape-b3 { --in-delay: 320ms; }

/* display:block, not inline: an inline SVG sits on a text baseline and
   leaves a stray pixel or two of daylight under the shape, which is
   exactly where the bottom of the photo is supposed to be hidden. */
.hero-stage .shape svg { display: block; width: 100%; height: auto; }

/* Offsets are percentages of the stage, so the whole composition scales
   with the viewport. Shapes 1, 2 and 6 sit in FRONT of the portrait
   (z-index above 5) and cut it off around the waist — that is what makes
   Mo read as emerging from the shapes rather than standing beside them. */
/* The theme's own offsets, as percentages of the 749x918 stage so the
   whole composition scales with the viewport. The orange arc is the
   centrepiece behind the torso; the yellow and violet quarter-circles sit
   behind the shoulder; the teal and pale-blue domes come forward (z-index
   above the portrait's 5) and cut him off at the waist. */
.hero-stage .shape-1 { width: 100%;  bottom: 0;     left: 0;                 z-index: 2; }
.hero-stage .shape-4 { width: 16.2%; bottom: 29.4%; left: auto; right: 1.3%;  z-index: 3; }
.hero-stage .shape-5 { width: 31%;   bottom: 32.7%; left: auto; right: 6%;    z-index: 4; }
.hero-stage .shape-2 { width: 55.5%; bottom: 1%;    left: -10%;               z-index: 8; }
.hero-stage .shape-3 { width: 55.5%; bottom: 1%;    left: auto; right: -10%;  z-index: 9; }

/* Each portrait change nudges the composition, never enough to look like a
   slider changing slides.

   The two front domes are the exception: they never rotate, and they never
   move apart. Their flat bases are the line Mo stands on, so they only
   scale — and they scale from that base (transform-origin 50% 100%), which
   leaves both bases on exactly the same horizontal line. */
.hero-stage[data-pose="1"] .shape-1 svg { --rot:  2deg;  --sc: 1.02; }
.hero-stage[data-pose="1"] .shape-4 svg { --rot: -8deg;  --sc: 1.08; }
.hero-stage[data-pose="1"] .shape-5 svg { --rot:  6deg;  --sc: 1.05; }
.hero-stage[data-pose="1"] .shape-2 svg,
.hero-stage[data-pose="1"] .shape-3 svg { --rot: 0deg;   --sc: 1.03; }

/* A third pose, for a third portrait: it leans the other way from the
   second rather than returning to the first, so three changes in a row
   never repeat an arrangement. */
.hero-stage[data-pose="2"] .shape-1 svg { --rot: -3deg;  --sc: 1.05; }
.hero-stage[data-pose="2"] .shape-4 svg { --rot:  6deg;  --sc: 1.04; }
.hero-stage[data-pose="2"] .shape-5 svg { --rot: -9deg;  --sc: 1.10; }
.hero-stage[data-pose="2"] .shape-2 svg,
.hero-stage[data-pose="2"] .shape-3 svg { --rot: 0deg;   --sc: 1.05; }

/* --- The three hero compositions ------------------------------------------
   Set with "heroStyle" in data/site.json. The offsets above are shared by
   the first two; only the stacking order tells them apart. */

/* 1. Faithful Ober — the 2022 site: every shape sits behind the portrait,
      so he stands in front of the composition rather than inside it. */
/* With nothing in front of him, a softened edge turns the bottom of the
   photo into a ghost over the shapes. These two compositions put solid
   colour under the whole of the cut line instead, and end the photo on it. */
.hero-stage[data-hero="ober"] .hero-portrait,
.hero-stage[data-hero="bauhaus"] .hero-portrait {
	-webkit-mask-image: none;
	        mask-image: none;
	--cut: 2%;
}

.hero-stage[data-hero="ober"] .shape-2 { z-index: 3; }
.hero-stage[data-hero="ober"] .shape-3 { z-index: 4; }

/* 3. Bauhaus collage — its own set of shapes, so it needs its own offsets.

      The orange disc is the sun the head reads against: it is the largest
      thing on the stage and the only one that touches the top of it. The
      teal quarter and the pale arch come forward and overlap each other
      around the middle, which is what hides the bottom of the photo —
      between them they cover its whole width (it spans 3%-97% of the
      stage) with a few per cent to spare, and since both are anchored to
      the stage floor and only ever scale up from it, the pose can widen
      that overlap but never open a gap in it. The pair overshoot the floor
      by a pixel, because a stage height of 350.5px rounds their feet onto
      a different device row from the photo's and leaves a hairline. */
.hero-stage[data-hero="bauhaus"] .shape-b1 { width: 80%; bottom: 35%; left: 10%;              z-index: 1; }
.hero-stage[data-hero="bauhaus"] .shape-b2 { width: 24%; bottom: 72%; left: auto; right: 4%;  z-index: 3; }
.hero-stage[data-hero="bauhaus"] .shape-b3 { width: 6.5%; bottom: 62%; left: 1%;              z-index: 3; }
.hero-stage[data-hero="bauhaus"] .shape-b4 { width: 64%; bottom: -1px; left: auto; right: -3%; z-index: 7; }
.hero-stage[data-hero="bauhaus"] .shape-b5 { width: 50%; bottom: -1px; left: -5%;              z-index: 8; }

/* The two shapes that stand in front never rotate, for the same reason the
   domes in the other compositions do not: their feet are the line he
   stands on. */
.hero-stage[data-hero="bauhaus"][data-pose="1"] .shape-b1 svg { --rot: 0deg;  --sc: 1.03; }
.hero-stage[data-hero="bauhaus"][data-pose="1"] .shape-b2 svg { --rot: -7deg; --sc: 1.06; }
.hero-stage[data-hero="bauhaus"][data-pose="1"] .shape-b3 svg { --rot: 0deg;  --sc: 1.18; }
.hero-stage[data-hero="bauhaus"][data-pose="1"] .shape-b4 svg { --rot: 0deg;  --sc: 1.02; }
.hero-stage[data-hero="bauhaus"][data-pose="1"] .shape-b5 svg { --rot: 0deg;  --sc: 1.03; }

.hero-stage[data-hero="bauhaus"][data-pose="2"] .shape-b1 svg { --rot: 0deg;  --sc: 1.06; }
.hero-stage[data-hero="bauhaus"][data-pose="2"] .shape-b2 svg { --rot: 5deg;  --sc: 1.03; }
.hero-stage[data-hero="bauhaus"][data-pose="2"] .shape-b3 svg { --rot: 0deg;  --sc: 0.88; }
.hero-stage[data-hero="bauhaus"][data-pose="2"] .shape-b4 svg { --rot: 0deg;  --sc: 1.04; }
.hero-stage[data-hero="bauhaus"][data-pose="2"] .shape-b5 svg { --rot: 0deg;  --sc: 1.05; }


/* --------------------------------------------------------------------------
   7. Buttons
   -------------------------------------------------------------------------- */

.btn {
	display: inline-block;
	font-family: var(--font-body);
	font-size: 14px;
	font-weight: 500;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	padding: 16px 34px;
	border-radius: 100px;
	border: 1px solid var(--accent);
	color: var(--accent-text);
	background: none;
	cursor: pointer;
	transition: background 0.45s var(--ease), color 0.45s var(--ease);
}
.btn:hover { background: var(--accent); color: var(--accent-ink); text-decoration: none; }
.btn--solid { background: var(--accent); color: var(--accent-ink); }
.btn--solid:hover { background: var(--ink); border-color: var(--ink); }

html[lang="ar"] .btn { text-transform: none; letter-spacing: 0; font-size: 16px; }

.btn-row { display: flex; flex-wrap: wrap; gap: 14px; margin-block-start: 34px; }


/* --------------------------------------------------------------------------
   8. About
   -------------------------------------------------------------------------- */

.about-points { list-style: none; margin: 0; padding: 0; }
.about-points li {
	position: relative;
	padding-inline-start: 28px;
	margin-block-end: 1.1em;
}
.about-points li::before {
	content: "";
	position: absolute;
	inset-inline-start: 0;
	inset-block-start: 0.62em;
	width: 8px; height: 8px;
	background: var(--accent);
}


/* --------------------------------------------------------------------------
   9. Resume — skills, services, timeline
   -------------------------------------------------------------------------- */

.block-title {
	font-size: clamp(18px, 1.6vw, 24px);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--ink);
	font-weight: 500;
	margin: clamp(44px, 6vw, 70px) 0 28px;
}
html[lang="ar"] .block-title { letter-spacing: 0; text-transform: none; }

/* Areas of expertise: plain chips, no self-scoring */
.expertise {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
}
.expertise li {
	font-size: 0.8em;
	line-height: 1.5;
	color: var(--ink);
	border: 1px solid var(--rule);
	border-radius: 100px;
	padding: 8px 18px;
	transition: border-color 0.4s var(--ease), background 0.4s var(--ease), color 0.4s var(--ease);
}
.expertise li:hover {
	border-color: color-mix(in srgb, var(--section-accent, var(--accent)) 55%, transparent);
	background: color-mix(in srgb, var(--section-accent, var(--accent)) 18%, transparent);
	color: var(--ink);
}

/* Education / Experience columns */
.cv-columns {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: clamp(24px, 4vw, 56px);
}
.cv-list { list-style: none; margin: 0; padding: 0; }
.cv-list li { margin-block-end: 30px; }
.cv-date {
	font-size: 0.72em;
	letter-spacing: 0.08em;
	color: var(--accent-text);
	text-transform: uppercase;
	display: block;
	margin-block-end: 4px;
}
html[lang="ar"] .cv-date { letter-spacing: 0; text-transform: none; }
.cv-role { color: var(--ink); font-weight: 500; font-size: 1em; margin: 0 0 2px; }
.cv-org  { font-size: 0.85em; margin: 0 0 8px; }
/* A company's Latin name is not broken across lines. Split, the brackets
   around it come apart — and in an Arabic line the closing one is mirrored,
   so it reads as a second opening bracket at the start of the next line.
   Kept whole, the name simply moves down together. Measured at every width
   from 360 to 1920: the longest of them, "(Communication Legal
   Translation)", is at most 251px against a column no narrower than 253. */
.cv-org bdi { white-space: nowrap; }

/* A mark beside the entry it belongs to. ATHAR's is drawn for a dark
   ground, so it is given one: a small rounded badge that carries its own
   background with it instead of sitting on the page's. */
.cv-logo { margin: 0 0 10px; line-height: 0; }
.cv-logo img { height: 40px; width: auto; object-fit: contain; }
.cv-logo--badge img {
	height: 50px;
	width: 50px;
	/* The whole mark, not a square cut out of it: cropping to fill sliced
	   the company's name in half. The badge carries the artwork's own dark
	   ground, so the letters still read the way they were drawn. */
	object-fit: contain;
	background: #0E1527;
	padding: 4px;
	border-radius: 12px;
	box-shadow: 0 2px 12px -5px rgba(28, 37, 40, 0.55);
}
.cv-list p.cv-note { font-size: 0.8em; margin: 0; }

/* The run of titles held at one employer. A rule in the section colour
   marks it as a note about the entry above, not a new entry. */
.cv-prog {
	font-size: 0.78em;
	margin: 10px 0 0;
	padding-inline-start: 12px;
	border-inline-start: 2px solid color-mix(in srgb, var(--accent) 55%, transparent);
	color: var(--body);
}


/* --------------------------------------------------------------------------
   10. Works
   -------------------------------------------------------------------------- */

.filters {
	display: flex;
	flex-wrap: wrap;
	gap: clamp(14px, 2.4vw, 32px);
	margin-block-end: clamp(30px, 5vw, 56px);
	padding: 0;
	list-style: none;
}
.filter {
	appearance: none;
	background: none;
	border: 0;
	padding: 4px 0;
	font-family: inherit;
	font-size: 0.8em;
	font-weight: 500;
	color: var(--body);
	cursor: pointer;
	position: relative;
	transition: color 0.4s var(--ease);
}
.filter::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	inset-block-end: 0;
	height: 1.5px;
	background: var(--accent);
	transform: scaleX(0);
	transition: transform 0.4s var(--ease);
}
.filter[aria-pressed="true"] { color: var(--ink); }
.filter[aria-pressed="true"]::after { transform: scaleX(1); }
.filter:hover { color: var(--ink); }

.works { display: grid; gap: clamp(30px, 5vw, 70px); }

.work {
	display: grid;
	gap: clamp(20px, 4vw, 56px);
	align-items: center;
	background: none;
	border: 0;
	padding: 0;
	margin: 0;
	font: inherit;
	color: inherit;
	text-align: start;
	cursor: pointer;
}
.work[hidden] { display: none; }

.work--image { grid-template-columns: 1fr 1fr; }

/* A project with a cover but no description yet: it reads as a finished
   card, so nothing about it should invite a click. */
.work--static { cursor: default; }
.work--static:hover .work-media img { transform: none; }
.work--static:hover .work-title { color: var(--ink); }

/* A project without a cover image: a compact text row, title against a
   short description. Deliberate on its own, and a quiet companion to the
   image rows rather than a gap where a picture should be. */
.work--text {
	grid-template-columns: minmax(180px, 34%) 1fr;
	gap: clamp(14px, 3vw, 40px);
	align-items: start;
	border-block-start: 1px solid var(--rule);
	padding-block-start: clamp(18px, 2.4vw, 28px);
	transition: border-color 0.5s var(--ease);
}
.work--text:hover { border-block-start-color: var(--accent); }
.work--text .work-title { font-size: clamp(21px, 2vw, 29px); margin-block-end: 4px; }
.work--text .work-cats { color: var(--accent-text); margin-block-end: 0.5em; }
.work-line { font-size: 0.85em; margin: 0; max-width: 56ch; }
.works { gap: clamp(26px, 4vw, 56px); }

.work-media {
	overflow: hidden;
	background: var(--bg-alt);
	aspect-ratio: 4 / 3;
}
.work-media img {
	width: 100%; height: 100%;
	object-fit: cover;
	transition: transform 0.9s var(--ease), filter 0.6s var(--ease);
}
.work:hover .work-media img { transform: scale(1.05); }

.work-title {
	font-family: var(--font-display);
	font-size: clamp(24px, 2.6vw, 38px);
	color: var(--ink);
	margin: 0 0 10px;
	transition: color 0.4s var(--ease);
}
.work:hover .work-title { color: var(--accent-text); }
.work-sub {
	font-size: 0.8em;
	margin: -4px 0 10px;
	color: var(--body);
	letter-spacing: 0.02em;
}
.work-cats { font-size: 0.8em; margin: 0; }

/* Where the work was done. Deliberately plain: a fact about the setting,
   not a logo or a client credit. */
.work-context {
	font-size: 0.72em;
	letter-spacing: 0.02em;
	color: var(--body);
	margin: 8px 0 0;
}
.work-year {
	font-size: 0.75em;
	letter-spacing: 0.08em;
	color: var(--accent-text);
	margin: -2px 0 8px;
}
.work-context + .work-line { margin-block-start: 0.7em; }
.work-link {
	display: inline-block;
	font-size: 0.78em;
	color: var(--accent-text);
	margin-block-start: 12px;
}
.work-cta {
	font-size: 0.75em;
	color: var(--accent-text);
	margin-block-start: 14px;
	display: inline-block;
}

/* Alternate the image side down the list */
.work--image:nth-of-type(even) .work-media { order: 2; }

/* A project's own mark, under its title. Decorative — the name is set as
   text directly above it — so it is small, and it is allowed to keep its
   own colours, which is the whole reason it is there. */
.work-logo { margin: 10px 0 0; line-height: 0; }
.work-logo img { height: 44px; width: auto; max-width: 70%; object-fit: contain; }
.work-logo--icon img {
	height: 52px;
	width: 52px;
	border-radius: 12px;
	object-fit: cover;
	box-shadow: 0 2px 10px -4px rgba(28, 37, 40, 0.5);
}
.work-logo + .work-cats { margin-block-start: 12px; }

/* --------------------------------------------------------------------------
   10b. The projects strip
   -------------------------------------------------------------------------- */

/* Marks of the projects worked on, under the work itself. They are a record
   of where the work was done — never a client list — so the heading says so
   and nothing in here is a link. */
.logo-strip {
	margin-block-start: clamp(44px, 6vw, 80px);
	padding-block-start: clamp(26px, 3.5vw, 44px);
	border-block-start: 1px solid var(--rule);
}
.logo-strip-title {
	font-family: inherit;
	font-size: 0.75em;
	font-weight: 400;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--body);
	margin: 0 0 clamp(18px, 2.4vw, 30px);
}
html[lang="ar"] .logo-strip-title { letter-spacing: 0; text-transform: none; font-size: 0.8em; }
.logo-strip ul {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: clamp(22px, 4vw, 54px);
}
.logo-strip li {
	border-radius: 10px;
	padding: 6px 10px;
	transition: background 0.4s var(--ease);
}
.logo-strip img {
	display: block;
	height: 50px;
	width: auto;
	max-width: 196px;
	object-fit: contain;
	opacity: 0.88;
	filter: var(--logo-mono);
	transition: opacity 0.4s var(--ease), filter 0.4s var(--ease);
}
/* The Amir Al Shoara mark is the one upright drawing in a row of wide ones;
   left at the same height it would tower over them. */
.logo-strip img[src$=".svg"] { height: 66px; }

@media (hover: hover) {
	.logo-strip li:hover img { opacity: 1; filter: none; }
}
.logo-strip li:focus-within img { opacity: 1; filter: none; }
/* In dark mode every one of these marks is dark artwork, so showing its own
   colours needs something to show them against: the plate appears only for
   the mark being looked at, and only there. */
:root[data-theme="dark"] .logo-strip li:focus-within { background: #F7F5F2; }
@media (hover: hover) {
	:root[data-theme="dark"] .logo-strip li:hover { background: #F7F5F2; }
}

/* --------------------------------------------------------------------------
   10c. Milestones
   -------------------------------------------------------------------------- */

.milestones-intro { margin-block-start: clamp(10px, 1.6vw, 18px); }

/* The row runs the full width of the page, not of the column, because the
   point of it is that it carries on past the edge. */
.milestones {
	margin-block-start: clamp(34px, 5vw, 60px);
	overflow: hidden;
}
.milestones-row {
	list-style: none;
	margin: 0;
	padding-inline: var(--gutter);
	display: flex;
	gap: clamp(14px, 2vw, 28px);
	/* Set by js/main.js as the section crosses the screen; the row only
	   ever moves within the slack it has, so neither end runs dry. */
	transform: translate3d(var(--drift, 0px), 0, 0);
	will-change: transform;
}
.milestone { flex: 0 0 auto; width: clamp(190px, 21vw, 280px); }

.milestone-figure {
	margin: 0;
	position: relative;
	isolation: isolate;
}
/* Duotone, done over the photograph rather than baked into it: the picture
   is flattened to grey, then the blacks are lifted to one ink and the
   whites pulled down to the other. Lifting the pointer off it puts the
   original back. */
.milestone-figure > img {
	display: block;
	width: 100%;
	aspect-ratio: 4 / 5;
	object-fit: cover;
	object-position: 50% 34%;
	filter: grayscale(1) contrast(1.06) brightness(1.02);
	transition: filter 0.5s var(--ease);
}
.milestone-figure::before,
.milestone-figure::after {
	content: "";
	position: absolute;
	inset-block: 0;
	inset-inline: 0;
	bottom: auto;
	aspect-ratio: 4 / 5;
	pointer-events: none;
	transition: opacity 0.5s var(--ease);
	/* The photograph is filtered, and a filtered element makes a stacking
	   context of its own — which puts it in the same painting layer as
	   these two, ordered by the document. Without a z-index it would come
	   after ::before and cover the first of the two inks, leaving a
	   one-colour wash where the duotone should be. */
	z-index: 1;
}
.milestone-figure::before { background: var(--duo-shadow); mix-blend-mode: lighten; }
.milestone-figure::after  { background: var(--duo-light);  mix-blend-mode: darken; }

/* Lifting the duotone off is for a real pointer. A touch screen has none,
   and browsers that fake one leave the last thing tapped stuck in hover,
   which would leave a single photograph in colour in the middle of a row
   of two-ink ones. */
@media (hover: hover) {
	.milestone-figure:hover > img { filter: none; }
	.milestone-figure:hover::before,
	.milestone-figure:hover::after { opacity: 0; }
}
.milestone-figure:focus-within > img { filter: none; }
.milestone-figure:focus-within::before,
.milestone-figure:focus-within::after { opacity: 0; }

.milestone figcaption {
	margin-block-start: 12px;
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.milestone-name { font-size: 0.78em; line-height: 1.5; color: var(--ink); }
.milestone-year { font-size: 0.72em; letter-spacing: 0.06em; color: var(--accent-text); }

/* Lightbox */
.lightbox {
	border: 0;
	padding: 0;
	max-width: min(860px, 92vw);
	width: 100%;
	background: var(--card);
	color: var(--body);
	box-shadow: var(--shadow);
}
.lightbox::backdrop { background: rgba(10, 13, 16, 0.72); backdrop-filter: blur(4px); }
.lightbox-inner { padding: clamp(26px, 4vw, 48px); }
.lightbox img {
	width: 100%;
	aspect-ratio: 16 / 9;
	object-fit: cover;
	margin-block-end: clamp(20px, 3vw, 32px);
}
.lightbox h3 {
	font-family: var(--font-display);
	font-size: clamp(26px, 3.4vw, 40px);
	margin-block-start: 0;
}
.lightbox-close {
	position: absolute;
	inset-block-start: 14px;
	inset-inline-end: 14px;
	z-index: 2;
}
.lightbox-sub {
	font-size: 0.85em;
	margin: -0.6em 0 0.8em;
	color: var(--body);
}
.lightbox-meta {
	font-size: 0.8em;
	color: var(--accent-text);
	margin-block-end: 1em;
}


/* --------------------------------------------------------------------------
   11. Work with me + contact
   -------------------------------------------------------------------------- */

.cta-panel {
	background: var(--card);
	padding: clamp(30px, 5vw, 60px);
	box-shadow: var(--shadow);
}
.cta-panel h3 {
	font-family: var(--font-display);
	font-size: clamp(26px, 3.4vw, 44px);
	margin-block-start: 0;
}
.cta-list {
	list-style: none;
	margin: 26px 0 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
	gap: 14px 26px;
}
.cta-list li {
	position: relative;
	padding-inline-start: 24px;
	font-size: 0.85em;
}
.cta-list li::before {
	content: "";
	position: absolute;
	inset-inline-start: 0;
	inset-block-start: 0.6em;
	width: 7px; height: 7px;
	background: var(--accent);
}

/* The invitation panel and the ways to answer it are one section, so the
   form follows the panel directly rather than opening a second one. */
.contact-grid {
	display: grid;
	grid-template-columns: minmax(210px, 32%) 1fr;
	gap: clamp(28px, 5vw, 70px);
	align-items: start;
	margin-block-start: clamp(34px, 5vw, 64px);
}

/* In a narrow column the buttons stack and share a width, so their edges
   line up with the form beside them. */
.btn-row--stack { flex-direction: column; align-items: stretch; }
.btn-row--stack .btn { text-align: center; }

.contact-points { list-style: none; margin: 0; padding: 0; }
.contact-points li { margin-block-end: 30px; }
.contact-icon {
	width: 38px; height: 38px;
	display: grid;
	place-items: center;
	background: color-mix(in srgb, var(--accent) 16%, transparent);
	margin-block-end: 12px;
}
.contact-icon svg { width: 18px; height: 18px; stroke: var(--accent); fill: none; stroke-width: 1.6; }
.contact-label { font-size: 0.72em; letter-spacing: 0.08em; text-transform: uppercase; display: block; }
html[lang="ar"] .contact-label { letter-spacing: 0; text-transform: none; }
.contact-value { color: var(--ink); font-weight: 400; }

.field { margin-block-end: 22px; }
.field label {
	display: block;
	color: var(--ink);
	font-size: 0.8em;
	font-weight: 500;
	margin-block-end: 8px;
}
.field input,
.field textarea {
	width: 100%;
	font-family: inherit;
	font-size: 0.9em;
	font-weight: 300;
	color: var(--ink);
	background: var(--field);
	border: 1px solid var(--rule);
	padding: 14px 18px;
	transition: border-color 0.4s var(--ease);
}
.field input:focus,
.field textarea:focus { border-color: var(--accent); outline: none; }
.field textarea { min-height: 150px; resize: vertical; }
/* Honeypot. Clipped rather than pushed off-screen: a negative offset
   overflows the inline axis, which in RTL drags the whole document
   out of view. */
.field--hp {
	position: absolute;
	width: 1px; height: 1px;
	margin: -1px; padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.form-note { font-size: 0.75em; margin-block-start: 16px; opacity: 0.8; }

.form-success {
	background: color-mix(in srgb, var(--accent) 14%, transparent);
	border-inline-start: 3px solid var(--accent);
	color: var(--ink);
	padding: 14px 18px;
	font-size: 0.85em;
	margin-block-end: 22px;
}

/* The answer after a submission, in place of the form and in the language
   the visitor was reading. It takes focus and announces itself, and Escape
   or the close button brings the form back with what was typed still in
   it — nothing is cleared unless the message actually got through. */
.form-result {
	border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
	background: color-mix(in srgb, var(--accent) 10%, transparent);
	border-radius: 18px;
	padding: clamp(26px, 4vw, 40px);
}
.form-result:focus { outline: 2px solid var(--accent); outline-offset: 4px; }
.form-result:focus:not(:focus-visible) { outline: none; }
.form-result.is-error {
	border-color: color-mix(in srgb, var(--shape-violet) 45%, transparent);
	background: color-mix(in srgb, var(--shape-violet) 10%, transparent);
}
.form-result-text {
	margin: 0;
	color: var(--ink);
	font-size: clamp(16px, 1.3vw, 19px);
	line-height: 1.7;
}
.form-result-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	margin-block-start: 24px;
}

.btn[disabled] { opacity: 0.45; cursor: not-allowed; }
.btn[disabled]:hover { background: none; color: var(--accent-text); }


/* --------------------------------------------------------------------------
   12. Footer
   -------------------------------------------------------------------------- */

.site-footer {
	padding-block: clamp(34px, 4vw, 52px);
	border-block-start: 1px solid var(--rule);
	font-size: 0.8em;
}
/* The mark leads, the line of copyright follows it. On a phone they stack. */
.site-footer .wrap {
	display: flex;
	flex-wrap: wrap;
	gap: 18px 28px;
	justify-content: space-between;
	align-items: center;
}
.site-footer .mark--footer { flex: 0 1 auto; }


/* --------------------------------------------------------------------------
   13. Custom cursor (pointer devices only — see main.js)
   -------------------------------------------------------------------------- */

/* Physical left/top, not inset-inline-start: on the Arabic page the logical
   property resolves to `right`, and the translate then pushes the cursor
   off the side of the screen — which is why it was invisible in Arabic.
   direction:ltr keeps the label inside the ring laid out predictably. */
.cursor-dot,
.cursor-ring {
	position: fixed;
	left: 0;
	top: 0;
	direction: ltr;
	border-radius: 999px;
	pointer-events: none;
	z-index: 999;
	opacity: 0;
	transition: opacity 0.3s var(--ease);
}
.cursor-dot {
	width: 7px; height: 7px;
	background: var(--accent);
	margin: -3.5px 0 0 -3.5px;
}
.cursor-ring {
	width: 40px; height: 40px;
	margin: -20px 0 0 -20px;
	border: 1px solid var(--accent);
	display: grid;
	place-items: center;
	font-family: var(--font-body);
	font-size: 12px;
	font-weight: 500;
	color: var(--accent-ink);
	transition: opacity 0.3s var(--ease),
	            width 0.35s var(--ease), height 0.35s var(--ease),
	            margin 0.35s var(--ease), background 0.35s var(--ease),
	            border-radius 0.35s var(--ease), border-color 0.35s var(--ease);
}
.cursor-ring span {
	opacity: 0;
	transform: scale(0.8);
	transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
}

body.cursor-ready .cursor-dot,
body.cursor-ready .cursor-ring { opacity: 1; }

/* Over a link or button: the ring swells and fills, the dot gets out of
   the way. Multiply in light and screen in dark both keep whatever is
   underneath readable through the fill. */
body.cursor-hot .cursor-ring {
	width: 70px; height: 70px;
	margin: -35px 0 0 -35px;
	background: color-mix(in srgb, var(--accent) 45%, transparent);
	border-color: transparent;
	mix-blend-mode: multiply;
}
:root[data-theme="dark"] body.cursor-hot .cursor-ring { mix-blend-mode: screen; }
body.cursor-hot .cursor-dot { opacity: 0; }

/* Over a project card the ring becomes a labelled pill. */
body.cursor-view .cursor-ring {
	width: 84px; height: 40px;
	margin: -20px 0 0 -42px;
	border-radius: 999px;
	background: var(--accent);
	border-color: transparent;
	mix-blend-mode: normal;
}
body.cursor-view .cursor-ring span { opacity: 1; transform: none; }
body.cursor-view .cursor-dot { opacity: 0; }

/* Over a text field the native caret is the useful one. */
body.cursor-text .cursor-dot,
body.cursor-text .cursor-ring { opacity: 0; }

/* Hide the system cursor only once ours is actually running, so a failure
   never leaves the page with no pointer at all. */
body.cursor-ready,
body.cursor-ready a,
body.cursor-ready button,
body.cursor-ready .work { cursor: none; }
body.cursor-ready input,
body.cursor-ready textarea { cursor: auto; }

/* The magnetic pull on primary buttons. */
.btn--solid { transition: background 0.4s var(--ease), color 0.4s var(--ease),
                          border-color 0.4s var(--ease), transform 0.3s var(--ease); }


/* --------------------------------------------------------------------------
   14. Scroll reveals
   -------------------------------------------------------------------------- */

/* Gated on .js: without scripting these elements must stay visible,
   otherwise a failed script would leave a blank page. */
.js [data-reveal] {
	opacity: 0;
	transform: translateY(34px);
	transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.js [data-reveal].is-visible { opacity: 1; transform: none; }

/* Arabic animates by word or line, never by letter — splitting a word into
   letters breaks the joining forms. Both languages use the same unit here. */
.js .reveal-words > span {
	display: inline-block;
	opacity: 0;
	transform: translateY(26px);
	transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
.js .reveal-words.is-visible > span { opacity: 1; transform: none; }


/* --------------------------------------------------------------------------
   15. Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 1024px) {
	.hero-grid { grid-template-columns: 1fr; }
	.hero-figure { max-width: 430px; margin-inline: auto; order: -1; }
	.hero-intro { margin-inline-start: 0; }
	.contact-grid { grid-template-columns: 1fr; }
}

@media (max-width: 860px) {
	.framed { grid-template-columns: 1fr; }
	.framed > .framed-aside { text-align: start; }
	.framed > .framed-main {
		border-inline-start: 0;
		padding-inline-start: 0;
		border-block-start: 1px solid var(--rule);
		padding-block-start: 24px;
	}
	/* Each title has to stay in one piece at every width, so the line
	   breaks between them instead; the type steps down to make that fit.
	   Verified down to 320px in both languages. */
	.hero-role { font-size: clamp(13px, 3.4vw, 17px); }

	.cv-columns { grid-template-columns: 1fr; }
	.work--image, .work--text { grid-template-columns: 1fr; gap: 14px; }
	.work--image { gap: 18px; }
	.work--image:nth-of-type(even) .work-media { order: 0; }

	/* No pointer to drift under, so the row is swiped instead: it scrolls
	   on its own, one photograph at a time, and js/main.js leaves its
	   transform alone at this width. */
	.milestones {
		overflow-x: auto;
		overscroll-behavior-inline: contain;
		scroll-snap-type: x proximity;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
	}
	.milestones::-webkit-scrollbar { display: none; }
	.milestones-row { transform: none; will-change: auto; }
	.milestone {
		width: min(62vw, 260px);
		scroll-snap-align: center;
	}
}

@media (max-width: 560px) {
	.hero-facts { grid-template-columns: 1fr; gap: 14px; }
	.hero-facts li,
	.hero-facts li:nth-child(2),
	.hero-facts li:nth-child(3) { text-align: start; }
	.hero { padding-block-start: 104px; }
	/* The two front domes hang past the composition by design. On the
	   narrowest phones that runs them off the screen, so pull them in
	   rather than let the edges get clipped. */
	.hero-figure { max-width: 286px; }
	.hero-stage .shape-2 { left: -5%; }
	.hero-stage .shape-3 { right: -5%; }
	.lang-switch { padding: 7px 9px; font-size: 12px; }
	.section-title { white-space: normal; }
	html[lang="ar"] body { font-size: 16px; }
	.title-ghost { font-size: 1.2em; }
}


/* --------------------------------------------------------------------------
   15b. Arabic type corrections

   Latin small-caps tricks do not belong on Arabic: letter-spacing pulls
   joined letters apart and uppercase means nothing. This sits after every
   component rule so it wins on order, and it catches anything added later
   without each new rule needing its own Arabic exception.
   -------------------------------------------------------------------------- */

html[lang="ar"] *,
html[lang="ar"] *::before,
html[lang="ar"] *::after {
	letter-spacing: 0;
	text-transform: none;
}


/* --------------------------------------------------------------------------
   17. Mo's calligraphic marks

   Four drawings of the same name, each with one job: the taliq is the
   Arabic hero name and Arabic wordmark, the signature closes About, the
   banner frames the preloader and the plate signs the footer. Never two of
   them on one screen. They paint in currentColor, so light and dark need
   no separate files.
   -------------------------------------------------------------------------- */

.svg-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.mark { display: block; height: auto; fill: currentColor; }

/* Header wordmark (Arabic only; English keeps the typeset name). */
.wordmark--mark { display: flex; align-items: center; }
.mark--wordmark {
	height: clamp(22px, 2.2vw, var(--wordmark-size, 28px));
	width: auto;
	color: var(--ink);
	transition: color 0.4s var(--ease), opacity 0.4s var(--ease);
}

/* Hero name. One line, always: it scales with the column rather than
   wrapping, and the readable name sits beside it for screen readers. */
.hero-name--mark { margin: 0.1em 0 0.34em; line-height: 1; }
.mark--hero {
	width: 100%;
	max-width: 620px;
	height: auto;
	color: var(--ink);
}

/* The signature that closes About. Served as a file and painted through a
   mask, so it costs one cached request instead of ~22KB of markup on each
   page, and still takes the accent colour. */
.signature {
	width: min(330px, 78%);
	aspect-ratio: 3038 / 556;
	margin-block-start: clamp(26px, 3vw, 40px);
	background: var(--accent);
	/* Relative to this stylesheet, not to the page: the same rule then works
	   from "/", from "/en/", and from anywhere the site is served. */
	-webkit-mask: url("../img/logo/name-signature.svg") no-repeat center / contain;
	        mask: url("../img/logo/name-signature.svg") no-repeat center / contain;
}
/* Drawn in from the start edge as it arrives. Both .js and dir live on the
   <html> element, hence the combined selector. Gated on .js like the other
   reveals, so without scripting the signature is simply there. */
html.js .signature {
	clip-path: inset(0 0 0 100%);
	transition: clip-path 1.6s var(--ease);
}
html.js[dir="rtl"] .signature { clip-path: inset(0 100% 0 0); }
html.js .signature.is-visible { clip-path: inset(0 0 0 0); }

/* Footer mark. */
.mark--footer {
	height: 36px;
	width: auto;
	max-width: 100%;
	color: var(--ink);
	opacity: 0.85;
}

/* The preloader — one moment of loading, the first visit of a browser
   session only. The name writes itself in, a hairline under it fills as
   the page actually loads, and then the whole thing lifts away like a
   curtain and hands the hero its entrance.

   It is display:none until main.js decides to show it, so a page without
   scripting is never left behind a cover. */
.preloader {
	display: none;
	position: fixed;
	inset: 0;
	z-index: 1000;
	background: var(--bg);
	place-items: center;
	padding-inline: var(--gutter);
	transform: translateY(0);
}
.preloader.is-on { display: grid; }

/* The curtain. translate, not transform, so nothing here has to know what
   else might be transformed. */
.preloader.is-leaving {
	translate: 0 -100%;
	transition: translate .62s cubic-bezier(.76, 0, .24, 1);
}

.preloader-inner {
	display: grid;
	justify-items: center;
	gap: clamp(20px, 3vw, 34px);
	width: min(620px, 82vw);
}

/* The mark, written rather than faded in: Arabic from the right, English
   from the left, each in the direction the hand would take. */
.preloader-mark,
.preloader-name {
	width: 100%;
	/* Mo's own orange: WCAG 1.4.3 exempts a logotype from the contrast
	   rule, and that is what this is. The deeper orange is for running
	   text — links, labels, chip text — not for the mark. */
	color: var(--accent);
	clip-path: inset(0 100% 0 0);
}
.preloader-mark { height: auto; fill: currentColor; display: block; }
.preloader-name {
	margin: 0;
	font-family: "Cormorant Garamond", var(--font-display), serif;
	font-style: italic;
	font-weight: 600;
	font-size: clamp(42px, 8vw, 92px);
	line-height: 1.1;
	letter-spacing: 0;
	text-align: center;
	white-space: nowrap;
}
[dir="rtl"] .preloader-mark,
[dir="rtl"] .preloader-name { clip-path: inset(0 0 0 100%); }

.preloader.is-on .preloader-mark,
.preloader.is-on .preloader-name {
	animation: mark-draw 1.15s cubic-bezier(.33, 0, .2, 1) forwards;
}
@keyframes mark-draw { to { clip-path: inset(0 0 0 0); } }

/* The hairline. Its scale is written from main.js and really does follow
   the load, so it is a progress bar and not a stopwatch. */
.preloader-bar {
	width: min(260px, 60%);
	height: 2px;
	background: color-mix(in srgb, var(--accent) 22%, transparent);
	overflow: hidden;
}
.preloader-bar span {
	display: block;
	height: 100%;
	background: var(--accent);
	transform: scaleX(var(--p, 0));
	transform-origin: left center;
	transition: transform .35s var(--ease);
}
[dir="rtl"] .preloader-bar span { transform-origin: right center; }


/* --------------------------------------------------------------------------
   16. Reduced motion
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	*,
	*::before,
	*::after {
		animation-duration: 0.001ms !important;
		transition-duration: 0.001ms !important;
	}
	[data-reveal],
	.reveal-words > span { opacity: 1 !important; transform: none !important; }
	.cursor-dot, .cursor-ring { display: none; }
	/* One portrait, held still, and shapes that stay where they are. */
	.hero-portrait { opacity: 0 !important; }
	.hero-portrait.is-active { opacity: 1 !important; }
	.hero-stage .shape { opacity: 1 !important; transform: none !important; }
	.hero-stage .shape-in { transform: none !important; }
	.section-shape { translate: none !important; }
	.hero-stage .shape svg { --rot: 0deg !important; --sc: 1 !important; transform: none !important; }
	.hero-portrait { translate: none !important; animation: none !important; }
	/* The row stays where it is; it is still swipeable, it just does not
	   move by itself. */
	.milestones-row { transform: none !important; }
	/* No preloader at all, and the signature is simply present. */
	.preloader { display: none !important; }
	.signature { clip-path: none !important; }
}
