/* ==========================================================================
   Ahmed Diab — product design portfolio
   Work first, at full size. Writing where it earns its place.
   ========================================================================== */

@font-face {
  font-family: 'Instrument Sans Variable';
  font-style: normal; font-display: swap; font-weight: 400 700;
  src: url('fonts/instrument-sans.woff2') format('woff2-variations');
}
@font-face {
  font-family: 'Instrument Sans Variable';
  font-style: italic; font-display: swap; font-weight: 400 700;
  src: url('fonts/instrument-sans-italic.woff2') format('woff2-variations');
}
@font-face {
  font-family: 'IBM Plex Sans Variable';
  font-style: normal; font-display: swap; font-weight: 100 700;
  src: url('fonts/ibm-plex-sans.woff2') format('woff2-variations');
}

/* ---------- tokens ----------
   Dark only. There is no light theme and no toggle. */
:root {
  --bg: #0b0b0c;
  --bg-2: #121214;
  --surface: #141416;
  --ink: #f4f4f5;
  --ink-2: #adaeb4;
  --ink-3: #8b8c92;
  --line: #232326;
  --line-2: #313135;
  --accent: #ff7a58;
  --dot: #22c55e;
  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 20px 50px -20px rgba(0,0,0,.75);

  --font-display: 'Instrument Sans Variable', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-sans: 'IBM Plex Sans Variable', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  --t-xs: 0.75rem;
  --t-sm: 0.875rem;
  --t-base: clamp(1rem, 0.97rem + 0.14vw, 1.0625rem);
  --t-lg: clamp(1.1rem, 1.04rem + 0.3vw, 1.28rem);
  --t-xl: clamp(1.35rem, 1.2rem + 0.7vw, 1.75rem);
  --t-2xl: clamp(1.9rem, 1.5rem + 1.9vw, 3.1rem);

  --gutter: clamp(1.15rem, 4vw, 3rem);
  --maxw: 1280px;
  --sec-y: clamp(3.5rem, 8vw, 7rem);

  --r-sm: 8px;
  --r: 14px;
  --r-lg: 20px;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --dur: 220ms;
  /* motion scale: things arrive on --ease-out and leave on --ease-in.
     hover states move in at --dur-fast and relax back at --dur, so a pointer
     sweeping across the page feels responsive without twitching */
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-in: cubic-bezier(.5, 0, .75, 0);
  --dur-fast: 140ms;
  --dur-slow: 640ms;
  color-scheme: dark;
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html {
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  scroll-behavior: smooth;
  /* the horizontal scrollers (phone strip, full-page frames) leak a few px past
     body's clip on very narrow screens — clip at the root so a phone can never
     scroll the page sideways.
     clip, not hidden: hidden turns this into a scroll container, which kills
     position:sticky on the header */
  overflow-x: clip;
}
body {
  min-height: 100dvh; background: var(--bg); color: var(--ink);
  font-family: var(--font-sans); font-size: var(--t-base);
  letter-spacing: -0.011em; line-height: 1.6;
  text-rendering: optimizeLegibility; overflow-x: clip;
}
img, svg { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; cursor: pointer; background: none; border: none; }
ul { list-style: none; padding: 0; }
:where(h1,h2,h3,h4) { text-wrap: balance; font-weight: 650; letter-spacing: -0.022em; font-family: var(--font-display); line-height: 1.12; }
:where(p, li) { text-wrap: pretty; }
::selection { background: #7c6cff; color: #fff; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; border-radius: 3px; }

.skip-link {
  position: absolute; left: .75rem; top: -4rem; z-index: 200;
  padding: .6rem 1rem; border-radius: var(--r-sm);
  background: var(--ink); color: var(--bg); font-size: var(--t-sm);
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: .75rem; }

.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
.rule { height: 1px; background: var(--line); border: 0; }

/* ---------- header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 60; padding-top: .7rem; pointer-events: none;
  /* coming back: long and soft, so it settles rather than snaps */
  transition: transform 560ms cubic-bezier(.16, 1, .3, 1);
}
/* hides on the way down, comes back the moment you scroll up.
   transform only - fading it too makes the nav's backdrop-filter drop out mid-transition */
.site-header.is-hidden {
  transform: translateY(calc(-100% - .6rem));
  /* leaving: eases in and picks up speed, so it gets out of the way without a jolt */
  transition: transform 340ms cubic-bezier(.5, 0, .75, 0);
}
.site-header.is-hidden .nav { pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .site-header, .site-header.is-hidden { transition: none; }
}
.nav {
  display: flex; align-items: center; gap: 1rem;
  min-height: 3.15rem; padding: .35rem .45rem .35rem 1rem;
  border-radius: 999px; pointer-events: auto;
  transition: background-color 320ms var(--ease), backdrop-filter 320ms var(--ease), box-shadow 320ms var(--ease);
}
.site-header.is-scrolled .nav {
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  -webkit-backdrop-filter: saturate(170%) blur(20px);
  backdrop-filter: saturate(170%) blur(20px);
  box-shadow: 0 1px 0 var(--line), 0 8px 24px -18px rgba(0,0,0,.4);
}
.brand { display: inline-flex; align-items: center; gap: .5rem; font-family: var(--font-display); font-size: 1rem; font-weight: 680; line-height: 1; letter-spacing: -.035em; }
.brand .diab-mark { width: auto; height: 1.3rem; flex: none; }
.nav-spacer { flex: 1 1 auto; }
/* the only action in the header */
.nav-cta {
  display: inline-flex; align-items: center; gap: .45rem; flex: none;
  font-size: var(--t-sm); font-weight: 500;
  padding: .55rem 1.05rem; border-radius: 999px;
  background: var(--ink); color: var(--bg); border: 1px solid var(--ink);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.nav-cta:hover { opacity: .86; }
.nav-cta:active { transform: translateY(1px); }
.nav-cta .arrow { transition: transform var(--dur) var(--ease); }
.nav-cta:hover .arrow { transform: translateX(2px); }

/* ---------- intro ---------- */
.intro { padding-block: clamp(2.5rem, 7vw, 5rem) clamp(2rem, 5vw, 3.5rem); }
.eyebrow {
  display: inline-flex; align-items: center; gap: .55rem;
  font-size: var(--t-xs); font-weight: 550; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-2);
}
.dot { width: 7px; height: 7px; border-radius: 999px; flex: none; background: var(--dot); box-shadow: 0 0 0 4px color-mix(in srgb, var(--dot) 16%, transparent); }
.intro h1 {
  margin-top: clamp(1.25rem, 3vw, 1.75rem);
  font-size: clamp(2.1rem, 5.2vw, 3.6rem);
  line-height: 1.06; letter-spacing: -0.03em;
  max-width: 24ch;
}
.intro .lede { margin-top: 1.1rem; max-width: 62ch; font-size: var(--t-lg); line-height: 1.55; color: var(--ink-2); }
.facts {
  display: grid; gap: .6rem 2.5rem; margin-top: clamp(1.75rem, 4vw, 2.5rem);
  padding-top: 1.25rem; border-top: 1px solid var(--line);
  grid-template-columns: 1fr;
}
@media (min-width: 700px) { .facts { grid-template-columns: repeat(3, minmax(0,1fr)); } }
.facts div { display: grid; gap: .2rem; }
.facts dt, .facts b { font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); font-weight: 550; }
.facts span { font-size: var(--t-sm); color: var(--ink); }

/* ---------- home: one card per project ---------- */
.work { padding-block: clamp(1rem, 3vw, 2rem) var(--sec-y); }
.piece {
  position: relative;
  display: grid; gap: clamp(1.1rem, 2.6vw, 1.6rem);
  padding-block: clamp(2.5rem, 6vw, 4.5rem);
  border-top: 1px solid var(--line);
}
.piece:first-child { border-top: 0; padding-top: clamp(1rem, 3vw, 2rem); }
.piece-link { position: absolute; inset: 0; z-index: 1; border-radius: var(--r); }
.piece-link:focus-visible { outline: 2px solid var(--ink); outline-offset: 6px; }

.piece-media { display: grid; gap: clamp(.5rem, 1.1vw, .8rem); }
.piece-thumbs { display: grid; gap: clamp(.5rem, 1.1vw, .8rem); grid-template-columns: 1fr 1fr; }
.piece-thumbs.single { grid-template-columns: 1fr; }
.piece-cover { aspect-ratio: 16 / 10; }
.piece-thumbs .shot { aspect-ratio: 16 / 10; }
.piece-thumbs.single .shot { aspect-ratio: 32 / 10; }
.piece-media .shot img { width: 100%; height: 100%; object-fit: cover; object-position: top center; }
/* box-shadow is not compositable - a long transition on it repaints the whole card
   every single frame, and that repaint invalidates the large image scaling on top
   of it. keep the shadow brief and let transform carry the motion. */
.piece .shot { transition: box-shadow var(--dur-fast) var(--ease-out), transform 420ms var(--ease-out); }
.piece:hover .piece-cover { box-shadow: inset 0 0 0 1px var(--line-2), var(--shadow); }

/* hover:hover only - on a touchscreen these stick after a tap and read as broken.
   the work is the point, so the picture is what responds: a slow push in, and the
   card lifts a hair off the page. */
@media (hover: hover) {
  .piece-media .shot img { transition: transform 700ms var(--ease-out); }
  /* promote the hovered card's images so the zoom is a GPU transform instead of a
     per-frame re-raster of a 2880px bitmap. scoped to :hover on purpose - promoting
     all 39 images up front would hold compositor memory for the whole page. */
  .piece:hover .piece-cover img,
  .piece:hover .piece-thumbs img { will-change: transform; }
  .piece:hover .piece-cover img { transform: scale(1.035); }
  .piece:hover .piece-thumbs img { transform: scale(1.02); }
  /* the lift rides on the cover, not .piece-media - the reveal rule owns that
     element's transform and would win the specificity fight */
  .piece:hover .piece-cover { transform: translateY(-3px); }
  .piece-head h2, .piece-lede { transition: color var(--dur) var(--ease-out); }
  .piece:hover .piece-lede { color: var(--ink); }
}

.piece-head { display: grid; gap: .55rem; max-width: 70ch; align-content: center; }
.piece-head h2 { font-size: var(--t-xl); letter-spacing: -0.025em; }
.piece-lede { color: var(--ink-2); max-width: 62ch; }
.piece-role { font-size: var(--t-sm); color: var(--ink-3); }
.piece-role b { color: var(--ink-2); font-weight: 550; margin-inline-end: .4rem; }
.piece-cta {
  display: inline-flex; align-items: center; gap: .45rem;
  margin-top: .35rem; font-size: var(--t-sm); font-weight: 500; color: var(--ink);
  width: fit-content; border-bottom: 1px solid var(--line-2); padding-bottom: .15rem;
  position: relative;
}
/* the rule underneath draws itself in from the left rather than just changing colour */
.piece-cta::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--ink); transform: scaleX(0); transform-origin: left center;
  transition: transform var(--dur-slow) var(--ease-out);
}
.piece:hover .piece-cta::after { transform: scaleX(1); }
.piece-cta .arrow { transition: transform var(--dur-fast) var(--ease-out); }
.piece:hover .piece-cta .arrow { transform: translateX(5px); }

@media (min-width: 940px) {
  .piece { grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); align-items: start; gap: clamp(1.5rem, 3vw, 2.75rem); }
}

/* a quieter treatment for the archive pieces, so the four cases still lead */
.piece.is-archive .piece-head h2 { font-size: var(--t-lg); }
.piece.is-archive .piece-lede { font-size: var(--t-sm); }

/* ---------- project page shell ---------- */
.backlink { padding-top: clamp(1.5rem, 4vw, 2.5rem); }
.backlink .wrap { display: block; }
.backlink a {
  display: inline-flex; align-items: center; gap: .45rem;
  font-size: var(--t-sm); color: var(--ink-3);
  transition: color var(--dur) var(--ease);
}
.backlink a:hover { color: var(--ink); }
.project.page-lead { border-top: 0; padding-top: clamp(1.5rem, 4vw, 2.5rem); }

.nextup { border-top: 1px solid var(--line); }
.nextup a {
  display: grid; gap: .3rem; position: relative;
  padding-block: clamp(2.5rem, 6vw, 4rem);
  transition: opacity var(--dur) var(--ease);
}
@media (hover: hover) {
  .nextup a { transition: opacity var(--dur) var(--ease-out), transform var(--dur-slow) var(--ease-out); }
  .nextup a:hover { opacity: .8; transform: translateX(4px); }
}
.nextup .meta { font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); }
.nextup strong { font-family: var(--font-display); font-size: var(--t-2xl); font-weight: 650; letter-spacing: -0.03em; line-height: 1.05; }
.nextup em { font-style: normal; color: var(--ink-2); max-width: 48ch; }
.nextup .arrow { position: absolute; right: 0; bottom: clamp(2.5rem, 6vw, 4rem); color: var(--ink-2); transition: transform var(--dur) var(--ease); }
.nextup a:hover .arrow { transform: translateX(4px); }
@media (max-width: 620px) { .nextup .arrow { display: none; } }

/* ---------- project ---------- */
.project { padding-block: var(--sec-y); border-top: 1px solid var(--line); }
.p-head { display: grid; gap: .9rem; }
.p-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem .7rem; font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); }
.p-meta b { color: var(--ink); font-weight: 600; }
/* the company's own mark, lifted from its own page */
.brand-chip {
  display: inline-flex; align-items: center; flex: none;
  --chip-h: 24px; margin-inline-end: .1rem; translate: 0 -1px;
}
/* Every mark is now transparent vector artwork, so none of them needs a plate. */
/* A fixed height makes a wide wordmark read large and a squarish mark read tiny -
   masader lands at 84px across, Msh Ady at 25. Squarer marks get proportionally
   more height so every mark carries roughly the same visual weight. */
.brand-chip img { height: calc(var(--chip-h, 24px) * var(--chip-k, 1)); }
.brand-chip img[src$="mshady.svg"]   { --chip-k: 1.26; }
.brand-chip img[src$="geekay.svg"]   { --chip-k: 1.26; }
.brand-chip img[src$="ecc.svg"]      { --chip-k: 1.24; }
.brand-chip img[src$="betna.svg"]    { --chip-k: 1.20; }
.brand-chip img[src$="elites.svg"]   { --chip-k: 1.20; }
.brand-chip img[src$="sawaship.svg"] { --chip-k: 1.12; }
.brand-chip img[src$="intlakaa.svg"] { --chip-k: 1.07; }
.brand-chip img { width: auto; display: block; }
/* the same mark used inline in a caption, where it sits in running text */
.page-cap .brand-chip { vertical-align: -.35em; margin-inline-end: .15rem; }
.p-meta span + span::before { content: '·'; margin-inline-end: .6rem; opacity: .6; }
/* the mark and the name are one unit - no separator between them */
.p-meta .brand-chip + span::before { content: none; }
/* on a phone the attribution line has to survive in ~350px: tighten the tracking
   and the mark so it stays on one line instead of orphaning a separator. */
@media (max-width: 620px) {
  .p-meta { letter-spacing: .05em; gap: .25rem .45rem; }
  .p-meta span + span::before { margin-inline-end: .4rem; }
  .brand-chip { --chip-h: 20px; }
}
.project h2 { font-size: var(--t-2xl); line-height: 1; letter-spacing: -0.03em; }
.p-what { max-width: 74ch; font-size: var(--t-lg); line-height: 1.5; color: var(--ink-2); }

.p-body { display: grid; gap: clamp(1.75rem, 4vw, 3rem); margin-top: clamp(1.75rem, 4vw, 2.5rem); }
/* single column — the attribution strip moved up under the subtitle */
.p-body { grid-template-columns: minmax(0, 1fr); }

.notes { display: grid; gap: clamp(1.25rem, 3vw, 1.75rem); max-width: 68ch; }
.note h3 { font-size: var(--t-xs); letter-spacing: .11em; text-transform: uppercase; color: var(--ink-3); font-weight: 600; font-family: var(--font-sans); }
.note p { margin-top: .5rem; }
.note p + p { margin-top: .7rem; }
.note strong { font-weight: 600; color: var(--ink); }
.note ul { display: grid; gap: .7rem; margin-top: .6rem; }
/* absolute marker, not a grid child — otherwise inline <em>/<code> inside the
   item become extra grid items and break onto their own line */
.note li { position: relative; padding-inline-start: 1.15rem; }
.note li::before {
  content: ''; position: absolute; inset-inline-start: 0; top: .62em;
  width: 5px; height: 5px; border-radius: 999px; background: var(--accent);
}
.note code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .88em; padding: .12em .38em; border-radius: 4px;
  background: var(--bg-2); border: 1px solid var(--line);
}

/* attribution, read before the work rather than beside it */
.p-facts {
  display: grid; gap: .7rem 2.5rem;
  margin-top: clamp(1.25rem, 3vw, 1.75rem); padding-top: 1.15rem;
  border-top: 1px solid var(--line);
  grid-template-columns: 1fr;
}
@media (min-width: 640px) { .p-facts { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (min-width: 1000px) { .p-facts { grid-template-columns: repeat(4, minmax(0,1fr)); } }
.p-facts div { display: grid; gap: .22rem; align-content: start; }
.p-facts b { font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); font-weight: 550; }
.p-facts span { font-size: var(--t-sm); color: var(--ink); }
.p-live { margin-top: clamp(1.1rem, 2.5vw, 1.5rem); }
.btn-live {
  display: inline-flex; align-items: center; gap: .5rem; white-space: nowrap;
  padding: .7rem 1.2rem; border-radius: 999px;
  border: 1px solid var(--line-2); color: var(--ink);
  font-size: var(--t-sm); font-weight: 500;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.btn-live:hover { border-color: var(--ink); background: var(--bg-2); }
.btn-live .arrow { flex: none; transition: transform var(--dur-fast) var(--ease-out); }
/* .btn.btn-live, not .btn-live: this arrow leaves the site, so it travels up and
   out. the generic .btn arrow rule is later in the file and would flatten it. */
.btn.btn-live:hover .arrow { transform: translate(3px, -3px); }

.aside { display: grid; gap: 1rem; align-content: start; }
@media (min-width: 900px) { .aside { position: sticky; top: 5.5rem; } }
.aside dl { display: grid; gap: .85rem; padding: 1.1rem 1.15rem; border: 1px solid var(--line); border-radius: var(--r); background: var(--bg-2); }
.aside dt { font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); font-weight: 550; }
.aside dd { font-size: var(--t-sm); color: var(--ink); margin-top: .12rem; }
.aside .link {
  display: inline-flex; align-items: center; gap: .45rem;
  font-size: var(--t-sm); color: var(--ink);
  border-bottom: 1px solid var(--line-2); padding-bottom: .1rem; width: fit-content;
  transition: border-color var(--dur) var(--ease);
}
.aside .link:hover { border-color: var(--ink); }

/* ---------- imagery ---------- */
.shots { display: grid; gap: clamp(.5rem, 1.1vw, .8rem); margin-top: clamp(1.5rem, 3.5vw, 2.25rem); }
.pair, .trio { display: grid; gap: clamp(.5rem, 1.1vw, .8rem); }
@media (min-width: 760px) {
  .pair { grid-template-columns: 1fr 1fr; align-items: start; }
  .trio { grid-template-columns: repeat(3, 1fr); align-items: start; }
}
.shot { overflow: hidden; border-radius: var(--r); background: var(--bg-2); box-shadow: inset 0 0 0 1px var(--line); }
.shot img { width: 100%; }
/* a silent, controls-free loop */
.shot video.loop { width: 100%; height: auto; display: block; }

/* full-length designs, scrollable in place */
.page-frame {
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--bg-2); overflow: hidden; box-shadow: var(--shadow);
}
.page-bar {
  display: flex; align-items: center; gap: .45rem;
  padding: .6rem .9rem; border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.page-bar i { width: 9px; height: 9px; border-radius: 999px; background: var(--line-2); flex: none; }
.page-bar em {
  margin-inline-start: .5rem; font-style: normal;
  font-size: var(--t-xs); letter-spacing: .04em; color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.page-bar .hint { margin-inline-start: auto; font-size: var(--t-xs); color: var(--ink-3); white-space: nowrap; }
.page-view {
  max-height: min(76vh, 720px);
  overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin;
}
.page-view img { width: 100%; }

.pages { display: grid; gap: clamp(1rem, 2.5vw, 1.5rem); margin-top: clamp(1.5rem, 3.5vw, 2.25rem); }
@media (min-width: 1000px) { .pages.two { grid-template-columns: 1fr 1fr; } }
.page-cap { margin-top: .7rem; font-size: var(--t-sm); color: var(--ink-2); }
.page-cap b { color: var(--ink); font-weight: 600; }

/* a labelled flow */
.flow-label {
  margin-top: clamp(1.75rem, 4vw, 2.5rem);
  font-size: var(--t-xs); letter-spacing: .11em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 600;
}

/* phones */
.strip {
  display: grid; grid-auto-flow: column; grid-auto-columns: 58vw;
  gap: clamp(.5rem, 1.1vw, .8rem);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scroll-padding-inline: var(--gutter);
  margin-inline: calc(var(--gutter) * -1); padding-inline: var(--gutter);
  padding-bottom: .4rem; scrollbar-width: thin;
  margin-top: clamp(1.5rem, 3.5vw, 2.25rem);
}
.strip > * { scroll-snap-align: start; }
.strip figure { display: grid; gap: .5rem; align-content: start; }
/* a section label that has to carry weight - names what a thing IS, not what to
   notice about it, so it sits at display scale with its own mark */
.big-label {
  display: flex; align-items: center; gap: .6rem;
  /* it introduces the thing below it, so it needs room above and to sit close beneath */
  margin-top: clamp(2.4rem, 5vw, 3.6rem);
  margin-bottom: clamp(.9rem, 1.8vw, 1.3rem);
  font-family: var(--font-display); font-size: var(--t-xl);
  font-weight: 640; letter-spacing: -.024em; line-height: 1.2; color: var(--ink);
}
.big-label-ico { width: 1.15em; height: 1.15em; flex: none; color: var(--accent); }

/* per-screen labels under a pair/trio row - these name a destination, so they
   carry that product's mark and read at body scale rather than as fine print */
.pair figure, .trio figure { display: grid; gap: .7rem; }
.pair figcaption, .trio figcaption {
  display: flex; align-items: center; gap: .6rem;
  font-family: var(--font-display); font-size: var(--t-lg);
  font-weight: 620; letter-spacing: -.022em; line-height: 1.25;
  text-transform: none; color: var(--ink);
}
.dest-ico { width: 1.3em; height: 1.3em; flex: none; }
.trio figcaption .soon {
  font-size: var(--t-xs); font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3); border: 1px solid var(--line-2); border-radius: 999px;
  padding: .15rem .5rem; margin-inline-start: .1rem;
}
.strip figcaption { order: -1; font-size: var(--t-xs); letter-spacing: .07em; text-transform: uppercase; color: var(--ink-3); }
@media (min-width: 840px) { .strip { grid-auto-columns: minmax(0,1fr); overflow: visible; margin-inline: 0; padding-inline: 0; } }

/* ---------- contact ---------- */
.closing { padding-block: var(--sec-y); border-top: 1px solid var(--line); }
.closing h2 { font-size: clamp(1.9rem, 5vw, 3.1rem); letter-spacing: -0.03em; max-width: 18ch; }
.closing .lede { margin-top: 1rem; max-width: 54ch; color: var(--ink-2); font-size: var(--t-lg); }
/* social links */
.socials {
  display: flex; flex-wrap: wrap; gap: .6rem;
  margin-top: clamp(1.5rem, 3.5vw, 2.25rem);
}
.socials a {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .6rem 1.05rem; border-radius: 999px;
  border: 1px solid var(--line-2); color: var(--ink); font-size: var(--t-sm);
  transition: border-color var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out),
              transform var(--dur) var(--ease-out);
}
.socials a:hover { border-color: var(--ink); background: var(--bg-2); transform: translateY(-2px); }
.socials a:active { transform: translateY(0); transition-duration: 60ms; }
.socials svg { width: 17px; height: 17px; flex: none; opacity: .82; transition: opacity var(--dur) var(--ease-out); }
/* the Tribbel mark is wider than tall — match its optical weight to the others */
.socials svg.tribbel { width: 16px; height: 14px; }
.socials a:hover svg { opacity: 1; }
@media (max-width: 480px) { .socials { display: grid; grid-template-columns: 1fr; } }

.actions { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: clamp(1.5rem, 3.5vw, 2.25rem); }
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5em;
  font-size: var(--t-sm); font-weight: 500; padding: .8rem 1.3rem;
  border-radius: var(--r-sm); border: 1px solid transparent;
  transition: transform var(--dur) var(--ease-out), opacity var(--dur-fast) var(--ease-out),
              border-color var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out),
              box-shadow var(--dur) var(--ease-out);
}
/* press wins over hover - the button must always answer the finger going down */
.btn:active { transform: translateY(1px) scale(.99); transition-duration: 60ms; }
.btn-primary { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.btn-secondary { color: var(--ink); border-color: var(--line-2); }
@media (hover: hover) {
  .btn:hover { transform: translateY(-1px); }
  .btn-primary:hover { opacity: .87; box-shadow: 0 6px 18px -10px rgba(244,244,245,.55); }
  .btn-secondary:hover { border-color: var(--ink); background: var(--bg-2); }
}
.btn .arrow { transition: transform var(--dur-fast) var(--ease-out); }
.btn:hover .arrow { transform: translateX(4px); }
@media (max-width: 560px) { .actions .btn { flex: 1 1 100%; } }

.site-footer { border-top: 1px solid var(--line); padding-block: 1.6rem 2.4rem; }
.site-footer .wrap { display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; justify-content: space-between; }
.site-footer span, .site-footer a { font-size: var(--t-xs); letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); }
.site-footer a:hover { color: var(--ink); }

/* ---------- lightbox ----------
   A dark stage: the image blurred behind, the real thing framed in a device. */
/* opens the viewer - it does not zoom, so it must not promise a magnifier */
.zoomable { cursor: pointer; }
.lightbox {
  position: fixed; inset: 0; z-index: 9000;
  display: none; flex-direction: column;
  background: #070708;
}
.lightbox.is-open { display: flex; }
.lb-blur {
  position: absolute; inset: -8%;
  background-position: center top; background-size: cover;
  filter: blur(64px) saturate(150%) brightness(.55);
  transform: scale(1.12); opacity: .8; pointer-events: none;
}
.lb-bar {
  position: relative; z-index: 2; flex: none;
  display: flex; align-items: center; gap: .75rem;
  padding: .7rem clamp(.8rem, 3vw, 1.4rem);
  color: var(--ink);
}
.lb-title { font-size: var(--t-xs); letter-spacing: .01em; opacity: .6; max-width: 54ch;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lb-actions { margin-inline-start: auto; display: flex; align-items: center; gap: .4rem; }
.lb-btn {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .42rem .8rem; border-radius: 999px;
  border: 1px solid rgba(244,244,245,.22);
  background: rgba(10,10,11,.5);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  color: var(--ink); font-size: var(--t-xs);
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.lb-btn:hover { background: rgba(244,244,245,.14); border-color: rgba(244,244,245,.45); }
.lb-hint { font-size: var(--t-xs); opacity: .5; color: var(--ink); }
@media (max-width: 720px) { .lb-hint { display: none; } }

.lb-stage {
  position: relative; z-index: 1;
  flex: 1 1 auto; overflow: auto; overscroll-behavior: contain;
  display: grid; place-items: center;
  padding: clamp(.5rem, 2vw, 1.5rem) clamp(.5rem, 3vw, 2.5rem) clamp(1.5rem, 4vw, 3rem);
}

/* the device */
.lb-device { display: grid; justify-items: center; margin: auto; }
.lb-screen {
  overflow: auto; overscroll-behavior: contain;
  background: #0b0b0c; box-shadow: 0 40px 120px -30px rgba(0,0,0,.9);
}
.lb-screen img { width: 100%; display: block; }
.lb-base { display: none; }

/* laptop — bezel, rounded lid, foot */
.lb-device.is-laptop .lb-screen {
  width: min(1180px, 88vw); max-height: min(74vh, 780px);
  border: 12px solid #17171a; border-bottom-width: 14px;
  border-radius: 16px;
  outline: 1px solid rgba(255,255,255,.07); outline-offset: -13px;
}
.lb-device.is-laptop .lb-base {
  display: block; width: min(1320px, 96vw); height: 13px;
  margin-top: -1px; border-radius: 0 0 14px 14px;
  background: linear-gradient(#202024, #141417);
  box-shadow: 0 22px 40px -22px rgba(0,0,0,.9);
}
.lb-device.is-laptop .lb-base::after {
  content: ''; display: block; width: 88px; height: 4px; margin: 4px auto 0;
  border-radius: 0 0 5px 5px; background: rgba(255,255,255,.09);
}

/* phone — tall bezel, island */
.lb-device.is-phone { position: relative; }
.lb-device.is-phone .lb-screen {
  width: min(340px, 74vw); max-height: min(76vh, 800px);
  border: 11px solid #17171a; border-radius: 42px;
  outline: 1px solid rgba(255,255,255,.08); outline-offset: -12px;
}
/* absolutely placed, or the grid treats it as a row above the phone */
.lb-device.is-phone::before {
  content: ''; position: absolute; z-index: 3;
  top: 18px; left: 50%; transform: translateX(-50%);
  width: 92px; height: 22px; border-radius: 999px; background: #101012;
  pointer-events: none;
}

/* already carries its own chrome — show it flat and big, no second frame */
.lb-device.is-flat .lb-screen {
  width: min(1280px, 94vw); max-height: min(82vh, 900px);
  border: 1px solid rgba(255,255,255,.10); border-radius: 14px;
}
/* a bare panel, if ever needed */
.lb-device.is-panel .lb-screen {
  width: min(560px, 84vw); max-height: min(80vh, 900px);
  border: 1px solid rgba(255,255,255,.12); border-radius: 14px;
}

html.lb-lock, html.lb-lock body { overflow: hidden; }

/* ---------- reveal ----------
   Entrances are ANIMATIONS, never transitions. `transition` is a shorthand, so a
   reveal rule that sets it wins on specificity and silently deletes whatever
   transition the element needed for :hover - which is how the card zoom ended up
   snapping on any image that happened to load late. Hovers own `transition`,
   entrances own `animation`, and the two can no longer collide.
   Each rule keeps its own start offset because a `to`-only keyframe takes its
   implicit start from the element's own computed style. */
@keyframes reveal-rise { to { opacity: 1; transform: none; } }
@keyframes media-fade-in { to { opacity: 1; } }

.js .rv { opacity: 0; transform: translateY(14px); }
.js .rv.is-in { animation: reveal-rise var(--dur-slow) var(--ease-out) forwards; }
/* inside a project card the picture arrives first and the writing follows a beat
   later - a card that lands all at once reads as a page jump, not an entrance */
.js .piece.rv .piece-media, .js .piece.rv .piece-head > * { opacity: 0; transform: translateY(12px); }
.js .piece.rv.is-in .piece-media { animation: reveal-rise 560ms var(--ease-out) 60ms forwards; }
.js .piece.rv.is-in .piece-head > *:nth-child(1) { animation: reveal-rise 560ms var(--ease-out) 150ms forwards; }
.js .piece.rv.is-in .piece-head > *:nth-child(2) { animation: reveal-rise 560ms var(--ease-out) 210ms forwards; }
.js .piece.rv.is-in .piece-head > *:nth-child(3) { animation: reveal-rise 560ms var(--ease-out) 270ms forwards; }
.js .piece.rv.is-in .piece-head > *:nth-child(n+4) { animation: reveal-rise 560ms var(--ease-out) 320ms forwards; }

/* a case study is a long read - the argument assembles as you come down it
   rather than sitting there fully formed before you arrive */
.js .project .p-facts,
.js .project .shots,
.js .project > .wrap > .page-cap,
.js .project .note,
.js .project .pages > div { opacity: 0; transform: translateY(16px); }
.js .project .p-facts.is-in,
.js .project .shots.is-in,
.js .project > .wrap > .page-cap.is-in,
.js .project .note.is-in,
.js .project .pages > div.is-in { animation: reveal-rise var(--dur-slow) var(--ease-out) forwards; }

/* screens fade up as they decode instead of snapping in half-drawn.
   only applied to images that had not already loaded, so nothing flashes */
.js img.is-loading { opacity: 0; }
.js img.is-loading.is-loaded { animation: media-fade-in 700ms var(--ease-out) forwards; }

@media (hover: hover) {
  /* these open the lightbox, so they answer the pointer like the home cards do */
  .project .shot { transition: box-shadow var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out); }
  .project .shot:hover { box-shadow: inset 0 0 0 1px var(--line-2), var(--shadow); transform: translateY(-2px); }
  .project .shot img.zoomable { transition: transform 900ms var(--ease-out); }
  .project .shot:hover img.zoomable { transform: scale(1.02); }
}

/* the frame's "scroll inside" hint has done its job once you have */
.page-bar .hint { transition: opacity var(--dur) var(--ease-out); }
.page-frame.is-explored .hint { opacity: 0; }

/* the lightbox arrives instead of blinking on - display:flex cannot transition,
   so the entrance is an animation, which does run on a display change */
.lightbox.is-open { animation: lb-in 240ms var(--ease-out); }
.lightbox.is-open .lb-device { animation: lb-rise 420ms var(--ease-out); }
@keyframes lb-in { from { opacity: 0; } }
@keyframes lb-rise { from { opacity: 0; transform: translateY(14px) scale(.985); } }

/* ---------- loader ---------- */
html.loader-pending, html.loader-pending body { overflow: hidden; }
.site-loader {
  --loader-black: #09090a; --loader-white: #f6f6f3;
  position: fixed; inset: 0; z-index: 10000;
  display: grid; place-items: center; min-height: 100dvh; overflow: hidden;
  color: var(--loader-white); background: var(--loader-black); isolation: isolate;
}
.site-loader[hidden] { display: none; }
.site-loader.is-done { background: transparent; }
.loader-curtain {
  position: absolute; z-index: -1; left: 0; width: 100%; height: calc(50% + 1px);
  background-color: var(--loader-black);
  background-image:
    linear-gradient(rgba(255,255,255,.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.035) 1px, transparent 1px);
  background-size: clamp(3.5rem, 8vw, 7rem) clamp(3.5rem, 8vw, 7rem);
  transition: transform 1000ms cubic-bezier(.62, .02, .2, 1); will-change: transform;
}
.loader-curtain::after { content:''; position:absolute; left:0; width:100%; height:1px; background: linear-gradient(90deg, transparent, rgba(255,255,255,.32), transparent); }
.loader-curtain-top { top: 0; } .loader-curtain-top::after { bottom: 0; }
.loader-curtain-bottom { bottom: 0; } .loader-curtain-bottom::after { top: 0; }
.loader-center { display: grid; place-items: center; gap: 1.3rem; transform: translateY(-.75rem); }
.loader-mark { position: relative; width: clamp(4.2rem,6.5vw,5.6rem); height: clamp(5.7rem,8.8vw,7.6rem); filter: drop-shadow(0 0 1.5rem rgba(255,255,255,.1)); }
.loader-mark .diab-mark { width: 100%; height: 100%; }
.loader-mark-main, .loader-mark-echo { position: absolute; inset: 0; display: block; clip-path: inset(100% 0 0 0); animation: loader-build 820ms steps(9,end) 180ms forwards; }
.loader-mark-main { z-index: 3; color: var(--loader-white); }
.loader-mark-echo { opacity: 0; mix-blend-mode: screen; }
.loader-mark-echo-a { z-index:1; color:#ff5c35; animation: loader-build 820ms steps(9,end) 120ms forwards, loader-reg-a 1.15s steps(8,end) 120ms forwards; }
.loader-mark-echo-b { z-index:2; color:#7c6cff; animation: loader-build 820ms steps(9,end) 210ms forwards, loader-reg-b 1.15s steps(8,end) 210ms forwards; }
.loader-scan { position:absolute; z-index:4; inset-inline:-1.75rem; top:-8%; height:1px; background: var(--loader-white); box-shadow: 0 0 .85rem rgba(255,255,255,.85); opacity:0; animation: loader-scan 920ms cubic-bezier(.65,0,.35,1) 180ms forwards; }
.loader-name { display:flex; align-items:center; gap:.75rem; font-size:.68rem; font-weight:500; line-height:1; letter-spacing:.14em; text-transform:uppercase; opacity:0; transform:translateY(.7rem); animation: loader-copy 600ms var(--ease) 850ms forwards; }
.loader-name i { width:2.2rem; height:1px; background: rgba(255,255,255,.32); }
.loader-name span:last-child { color: rgba(255,255,255,.5); }
.loader-corner { position:absolute; bottom:max(1.4rem,4vh); font-size:.66rem; letter-spacing:.16em; text-transform:uppercase; color:rgba(255,255,255,.66); opacity:0; animation: loader-copy 600ms var(--ease) 950ms forwards; }
.loader-corner-left { left: max(1.25rem,4vw); }
.loader-corner-right { right: max(1.25rem,4vw); }
@media (max-width: 560px) { .loader-corner-right { display:none; } }
/* the mark leaves with the curtains rather than blinking out ahead of them */
.site-loader.is-done .loader-center { opacity:0; transform: translateY(-1.75rem); transition: opacity 420ms var(--ease-in), transform 760ms var(--ease-out); }
.site-loader.is-done .loader-center, .site-loader.is-done .loader-corner { animation: none; }
.site-loader.is-done .loader-corner { opacity:0; transition: opacity 340ms var(--ease-in); }
.site-loader.is-done .loader-curtain-top { transform: translateY(-101%); }
.site-loader.is-done .loader-curtain-bottom { transform: translateY(101%); }
@keyframes loader-build { to { clip-path: inset(0 0 0 0); } }
@keyframes loader-scan { 0%{opacity:0;top:-8%} 12%{opacity:1} 88%{opacity:1} 100%{opacity:0;top:104%} }
@keyframes loader-reg-a { 0%{opacity:.85;transform:translate3d(-6px,3px,0)} 55%{opacity:.5;transform:translate3d(3px,-2px,0)} 100%{opacity:0;transform:none} }
@keyframes loader-reg-b { 0%{opacity:.85;transform:translate3d(6px,-3px,0)} 55%{opacity:.5;transform:translate3d(-3px,2px,0)} 100%{opacity:0;transform:none} }
@keyframes loader-copy { to { opacity:1; transform:none; } }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; transition-delay: 0ms !important;
  }
  .js .rv { opacity: 1; transform: none; }
  /* the staggered children must not be left holding a delay or an opacity of 0 */
  .js .piece.rv .piece-media, .js .piece.rv .piece-head > * { opacity: 1; transform: none; }
  .js .project .p-facts, .js .project .shots, .js .project > .wrap > .page-cap,
  .js .project .note, .js .project .pages > div,
  .js img.is-loading { opacity: 1; transform: none; }
  .piece:hover .piece-cover img, .piece:hover .piece-thumbs img,
  .piece:hover .piece-cover, .btn:hover, .socials a:hover { transform: none; }
}
@media print {
  .site-loader, .site-header, .actions { display: none !important; }
  .project { break-inside: avoid; }
  .page-view { max-height: none; overflow: visible; }
}
