/* =============================================================
   FAROUK — version AOS
   Layout / type system reconstructed from aosama.net,
   populated with Farouk's own content.
   Work section reworked into a fluid swipeable slider.
   ============================================================= */

:root {
  --bg:        #faf8f4;
  --bg-elev:   #f3f0e9;
  --fg:        #111111;
  --fg-muted:  #5e5a55;
  --fg-faint:  #8b857d;
  --border:    rgba(17,17,17,0.10);
  --border-2:  rgba(17,17,17,0.06);

  --font-serif: "Newsreader", "Tiempos Headline", Georgia, "Times New Roman", serif;
  --font-sans:  "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  --narrow: 768px;
  --page:   1120px;
  --pad:    40px;
  --ease:   cubic-bezier(0.22, 1, 0.36, 1);

  /* How tall any single uploaded image is allowed to get. Keeps a 4000px-tall
     export from turning into a page the reader has to scroll through — one
     image should fit comfortably in view. Raise for taller, lower for shorter. */
  --media-max-h: min(78vh, 860px);
}
@media (max-width: 640px) { :root { --pad: 22px; } }

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  background: var(--bg); color: var(--fg);
  font-family: var(--font-sans); font-size: 16px; line-height: 1.7;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
::selection { background: var(--fg); color: var(--bg); }

h1, h2, h3 { font-family: var(--font-serif); font-weight: 400; letter-spacing: -0.012em; color: var(--fg); }
h1 { font-size: clamp(2.35rem, 5.4vw, 4rem); line-height: 1.06; }
h2 { font-size: clamp(1.7rem, 3.4vw, 2.5rem); line-height: 1.12; }
h3 { font-size: 1.125rem; letter-spacing: -0.01em; }
p  { color: var(--fg-muted); }

.container-page   { max-width: var(--page);   margin-inline: auto; padding-inline: var(--pad); }
.container-narrow { max-width: var(--narrow); margin-inline: auto; padding-inline: var(--pad); }

/* a cleared CMS field hides its element; some of these live in flex rows,
   where the UA's [hidden] rule alone would lose */
[hidden] { display: none !important; }

.eyebrow {
  font-family: var(--font-sans); font-size: .72rem; font-weight: 500;
  letter-spacing: .14em; text-transform: uppercase; color: var(--fg-faint);
}
.sec { padding-block: clamp(3.5rem, 8vw, 6rem); }
.sec--tight { padding-block: clamp(2.5rem, 5vw, 4rem); }
.rule { height: 1px; background: var(--border-2); border: 0; }

/* ---------- reveal ---------- */
/* Reveals are progressive enhancement: without JS nothing is hidden, so a
   failed script can never leave the page blank. The .js class is set by a tiny
   inline script in <head> before first paint. */
.js [data-r] { opacity: 0; transform: translateY(18px); transition: opacity .8s var(--ease), transform .8s var(--ease); }
.js [data-r].in { opacity: 1; transform: none; }
[data-d="1"] { transition-delay: .07s } [data-d="2"] { transition-delay: .14s }
[data-d="5"] { transition-delay: .35s } [data-d="6"] { transition-delay: .42s }
[data-d="3"] { transition-delay: .21s } [data-d="4"] { transition-delay: .28s }

/* ---------- header ---------- */
.site-header { position: sticky; top: 0; z-index: 60; background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-bottom: 1px solid transparent;
  transition: border-color .4s, transform .5s var(--ease); }
.site-header.scrolled { border-bottom-color: var(--border-2); }
.site-header__inner { display: flex; align-items: center; justify-content: space-between; padding-block: 1.5rem; }
.brand { font-size: .95rem; font-weight: 500; letter-spacing: -0.01em; }
.site-nav ul { display: flex; align-items: center; gap: 1.5rem; list-style: none; padding: 0; font-size: .9rem; }
.site-nav a { color: var(--fg-muted); transition: color .25s; }
.site-nav a:hover { color: var(--fg); }

/* the one nav item that is an action, not a destination */
.site-nav .nav-cta { display: inline-flex; align-items: center;
  background: var(--fg); color: var(--bg); font-weight: 500;
  padding: .5em 1.1em; border-radius: 999px;
  transition: opacity .25s, transform .45s var(--ease); }
.site-nav .nav-cta:hover { color: var(--bg); opacity: .9; transform: translateY(-1px); }
@media (prefers-reduced-motion: reduce) { .site-nav .nav-cta:hover { transform: none; } }

/* on narrow screens only the CTA survives — it is the last item */
@media (max-width: 560px) { .site-nav li:not(:last-child) { display: none; } }


/* brand avatar */
.brand { display: inline-flex; align-items: center; gap: .6rem; }
.brand__avatar { width: 30px; height: 30px; border-radius: 50%; flex: none;
  background: var(--bg-elev) center/cover no-repeat;
  border: 1px solid var(--border-2);
  display: grid; place-items: center;
  font-size: .78rem; font-weight: 600; color: var(--fg-muted); }
.brand__avatar.has-photo { color: transparent; border-color: var(--border); }

/* ---------- hero ---------- */
.hero { padding-top: clamp(3.5rem, 7vw, 6rem); padding-bottom: clamp(3rem, 6vw, 5rem); }
/* No measure cap: the headline's line breaks come from content.json, so the
   container is the only limit. Capping it here re-broke the author's lines. */
.hero h1 { max-width: none; text-wrap: pretty; }
.hero__intro { margin-top: 1.8rem; max-width: 62ch; font-size: 1.0625rem; }
.hero__intro strong { color: var(--fg); font-weight: 500; }
.hero__meta { display: flex; flex-wrap: wrap; gap: 1rem 1.6rem; margin-top: 2.2rem; font-size: .875rem; color: var(--fg-faint); }
/* availability line, sitting above the headline */
.status { display: inline-flex; align-items: center; gap: .55rem;
  font-size: .82rem; color: var(--fg-muted);
  background: #fff; border: 1px solid var(--border-2); border-radius: 999px;
  padding: .5em 1.05em .5em .85em;
  margin-bottom: clamp(1.1rem, 2.4vw, 1.6rem); }
.status__dot { position: relative; width: 9px; height: 9px; border-radius: 50%;
  background: #3fae6a; flex: none; }
/* the ring is a box-shadow so it expands without affecting layout or the text baseline */
.status__dot::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  animation: statusPing 1.3s cubic-bezier(.22, 1, .36, 1) infinite;
}
@keyframes statusPing {
  0%   { box-shadow: 0 0 0 0 rgba(63,174,106,.55); }
  70%  { box-shadow: 0 0 0 7px rgba(63,174,106,0); }
  100% { box-shadow: 0 0 0 0 rgba(63,174,106,0); }
}


/* primary call to action */
.cta-btn { display: inline-flex; align-items: center; gap: .55rem;
  background: var(--fg); color: var(--bg);
  font-size: .9rem; font-weight: 500; letter-spacing: -.005em;
  padding: .85em 1.5em; border-radius: 999px;
  border: 1px solid transparent;
  transition: transform .45s var(--ease), opacity .3s, border-color .3s, background .3s; }
.cta-btn:hover { transform: translateY(-2px); opacity: .9; }
.cta-btn .ar { transition: transform .45s var(--ease); }
.cta-btn:hover .ar { transform: translateX(4px); }

/* the pair of hero buttons; owns the spacing the single button used to */
.hero__actions { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem;
  margin-top: clamp(1.6rem, 3vw, 2.2rem); }

/* secondary action — white pill sitting beside the primary one */
.cta-btn--alt { background: #fff; color: var(--fg); border-color: var(--border); }
.cta-btn--alt:hover { opacity: 1; background: #fff; border-color: rgba(17,17,17,.22); }

@media (prefers-reduced-motion: reduce) {
  .cta-btn:hover { transform: none; }
  .cta-btn:hover .ar { transform: none; }
}

/* ---------- section heads ---------- */
.sec-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 2rem; }
.sec-head h2 { font-size: clamp(1.5rem, 2.8vw, 2rem); }

/* =============================================================
   WORK — two-up grid
   ============================================================= */
.work__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
/* every other section heading is .sec-head h2; match it so Case studies
   and Selected work are not two different sizes */
.work__head h2 { font-size: clamp(1.5rem, 2.8vw, 2rem); }
.wgrid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.75rem, 4vw, 3rem) clamp(1.5rem, 3vw, 2.5rem); margin-top: clamp(2rem, 4vw, 3rem); }
.wcard { display: block; }
.wcard__frame {
  position: relative; overflow: hidden; border-radius: 8px;
  border: 1px solid var(--border); background: var(--bg-elev);
  transition: transform .6s var(--ease), box-shadow .6s var(--ease), border-color .4s;
}
.wcard__frame .ratio { aspect-ratio: 4/3; }
.wcard__frame video, .case-cover video, .case-fig video { display: block; background: var(--bg-elev); }
/* fill the card and sit in the middle of it: anchoring to the top left kept
   the frame on a corner of the artwork rather than the middle of it */
.wcard__frame img, .wcard__frame video { width: 100%; height: 100%; object-fit: cover; object-position: center;
  transition: transform 1s var(--ease); }
.wcard:hover .wcard__frame { transform: translateY(-4px); box-shadow: 0 22px 44px -26px rgba(40,32,20,.45); border-color: rgba(17,17,17,.17); }
.wcard:hover .wcard__frame img, .wcard:hover .wcard__frame video { transform: scale(1.035); }
.wcard__body { padding-top: 1.1rem; }
.wcard__title { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.wcard__title h3 { transition: opacity .3s; }
.wcard:hover .wcard__title h3 { opacity: .62; }
.wcard__cat { font-size: .8rem; color: var(--fg-faint); white-space: nowrap; }
.wcard__meta { margin-top: .5rem; font-size: .7rem; font-weight: 500; letter-spacing: .11em; text-transform: uppercase; color: var(--fg-faint); }
.wcard__desc { margin-top: .7rem; font-size: .95rem; line-height: 1.6; }
@media (max-width: 720px) { .wgrid { grid-template-columns: 1fr; } }

/* ---------- clients: borderless rotating marquee ---------- */
.marquee { overflow: hidden; white-space: nowrap; padding-block: clamp(1.5rem, 3vw, 2.5rem);
  /* sit inside the same column as everything else so the names fade at the
     container edge rather than running off the side of the page */
  width: min(var(--narrow), calc(100% - 2 * var(--pad))); margin-inline: auto;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); }
/* The track holds the list twice and animates to -50%, so the halves must be
   exactly equal in width. `gap` breaks that: 14 items have 13 gaps, so -50%
   travels 7 items + 6.5 gaps while one true period is 7 items + 7 gaps — the
   missing half-gap is the visible jump. Spacing lives on each item as *padding*
   rather than margin, because a trailing margin is excluded from scrollWidth —
   which would leave -50% short of a full period and still show a seam. */
.marquee__track { display: inline-flex; align-items: center;
  animation: marquee 14s linear infinite; will-change: transform; }
@keyframes marquee { to { transform: translateX(-50%); } }
.marquee__item { font-family: var(--font-serif); font-size: clamp(1.35rem, 3vw, 2.1rem);
  color: var(--fg-muted); white-space: nowrap; flex: none;
  padding-right: clamp(2.5rem, 6vw, 5rem); }
.marquee__slot { display: inline-flex; align-items: center; justify-content: center;
  width: clamp(130px, 16.2vw, 178px); }
.marquee__logo { max-height: clamp(30px, 4.6vw, 46px); max-width: 100%; width: auto; height: auto;
  object-fit: contain; opacity: .82; filter: grayscale(1) contrast(1.08); }

/* phones: the strip reads too small at the desktop clamp minimums */
@media (max-width: 720px) {
  .marquee__item { font-size: clamp(1.6rem, 6vw, 2.2rem); padding-right: clamp(1.5rem, 6vw, 2.5rem); }
  .marquee__slot { width: clamp(104px, 30vw, 140px); }
  .marquee__logo { max-height: clamp(34px, 10vw, 46px); }
}

/* ---------- scroll cue ---------- */
.scrollcue { display: flex; justify-content: center;
  padding-block: clamp(1.5rem, 4vw, 2.5rem); color: var(--fg-faint);
  transition: color .3s var(--ease); }
.scrollcue:hover { color: var(--fg); }
.scrollcue__txt { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.scrollcue__icon { display: grid; place-items: center; width: 34px; height: 34px;
  border: 1px solid var(--border); border-radius: 50%;
  animation: cueFloat 2.6s ease-in-out infinite; }
.scrollcue__icon svg { width: 15px; height: 15px; }
@keyframes cueFloat {
  0%, 100% { transform: translateY(-4px); opacity: .75; }
  50%      { transform: translateY(5px);  opacity: 1; }
}
/* on phones it sits with the content, not centred in the column */
@media (max-width: 720px) {
  .scrollcue { justify-content: flex-start; padding-inline: var(--pad); }
}
@media (prefers-reduced-motion: reduce) { .scrollcue__icon { animation: none; } }

/* ---------- statement: words ink in as you scroll ---------- */
.stmt__scroll { height: 240vh; position: relative; }
.stmt__sticky { position: sticky; top: 0; height: 100svh; display: grid; place-items: center; }
.stmt__inner { max-width: 880px; padding-inline: var(--pad); }
.stmt__text { font-family: var(--font-serif); font-weight: 400;
  font-size: clamp(1.5rem, 3.7vw, 2.9rem); line-height: 1.28; letter-spacing: -0.012em;
  color: var(--fg); margin-top: 1.6rem; }
.js .stmt__text { color: rgba(17,17,17,.15); }
.stmt__text .w { transition: color .4s var(--ease); }
.stmt__text .w.lit { color: var(--fg); }
.stmt__text em { font-style: italic; }


/* ---------- statement on small screens ----------
   The desktop sizing left the paragraph floating in a mostly empty viewport.
   Larger type and a shorter scroll span make it fill the screen with only a
   small margin above and below, without ever revealing the sections around it. */
@media (max-width: 720px) {
  .stmt__scroll { height: 185vh; }
  .stmt__sticky { align-content: center; padding-block: 4.5rem 3rem; }
  /* sized against height as well as width: a phone viewport is tall, so a
     width-only clamp leaves the paragraph stranded in the middle */
  .stmt__text { font-size: clamp(1.35rem, max(6.9vw, 4.15vh), 2.4rem); line-height: 1.3; margin-top: 1rem; }
}
@media (max-width: 400px) {
  .stmt__text { font-size: clamp(1.25rem, max(6.5vw, 3.9vh), 2.15rem); }
}

/* ---------- about ---------- */
.about p + p { margin-top: 1.1rem; }
.about p { font-size: 1.0625rem; }
.about strong { color: var(--fg); font-weight: 500; }


/* about: portrait beside the copy */
.about__grid { display: grid; grid-template-columns: .72fr 1.28fr; gap: clamp(1.4rem, 4vw, 2.8rem); align-items: start; }
.about__portrait { aspect-ratio: 4/5; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--border); background: var(--bg-elev); }
.about__portrait img, .about__portrait video { width: 100%; height: 100%; object-fit: cover; display: block;
  filter: grayscale(1) contrast(1.03); }
@media (max-width: 640px) {
  .about__grid { grid-template-columns: 1fr; gap: 1.4rem; }
  .about__portrait { max-width: 210px; }
}

/* ---------- experience ---------- */
.xp { margin-top: 1rem; }
.xp__row { display: grid; grid-template-columns: 150px 1fr; gap: 1.5rem;
  padding-block: 1.6rem; border-top: 1px solid var(--border-2); }
.xp__row:last-child { border-bottom: 1px solid var(--border-2); }
.xp__when { font-size: .82rem; color: var(--fg-faint); padding-top: .18rem; }
.xp__co { font-size: 1rem; font-weight: 500; color: var(--fg); letter-spacing: -0.01em; }
.xp__role { font-size: .95rem; color: var(--fg-muted); margin-top: .1rem; }
.xp__note { font-size: .9rem; margin-top: .5rem; max-width: 58ch; }
@media (max-width: 640px) { .xp__row { grid-template-columns: 1fr; gap: .35rem; } }

/* ---------- skills ---------- */
/* deliberately the same grammar as .xp above: a quiet label column, hairline
   rules, and no boxes — so it reads as a sibling of Experience, not a new idea */
.skills { margin-top: 1rem; }
.skills__row { display: grid; grid-template-columns: 150px 1fr; gap: 1.5rem;
  padding-block: 1.6rem; border-top: 1px solid var(--border-2); }
.skills__row:last-child { border-bottom: 1px solid var(--border-2); }
.skills__label { font-size: .82rem; color: var(--fg-faint); padding-top: .35rem; }
.skills__list { display: flex; flex-wrap: wrap; gap: .5rem; list-style: none; padding: 0; margin: 0; }
/* same pill treatment as the availability chip in the hero */
.skill { display: inline-flex; align-items: center; gap: .45em;
  font-size: .85rem; color: var(--fg-muted);
  background: #fff; border: 1px solid var(--border-2); border-radius: 999px;
  padding: .4em .95em; transition: border-color .3s, color .3s; }
.skill:hover { border-color: var(--border); color: var(--fg); }
/* tool marks: same grayscale register as the client logos, not brand colour */
.skill__icon { width: 13px; height: 13px; flex: none; fill: currentColor;
  color: var(--fg-faint); transition: color .3s; }
.skill:hover .skill__icon { color: var(--fg-muted); }
/* the sprite itself never renders */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
@media (max-width: 640px) { .skills__row { grid-template-columns: 1fr; gap: .7rem; } }
@media (prefers-reduced-motion: reduce) { .skill, .skill__icon { transition: none; } }

/* ---------- playground ---------- */
/* deliberately NOT a .wcard: work cards lead with a big image and open a case
   study, these lead with a mark and leave the site, so they carry card chrome
   the work grid doesn't have */
.pgrid { display: grid; gap: 1rem; margin-top: 1rem; }
.pcard { display: grid; grid-template-columns: minmax(0, 300px) 1fr; gap: 1.4rem; align-items: center;
  padding: 1.4rem 1.5rem; border: 1px solid var(--border-2); border-radius: 12px;
  background: #fff; transition: border-color .35s, transform .45s var(--ease); }
.pcard:hover { border-color: var(--border); transform: translateY(-2px); }
/* a live screenshot of the project — work cards crop 4:3 and sit flush, this
   one is inset in the card and keeps its own frame */
.pcard__shot { aspect-ratio: 16 / 10; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--border-2); background: var(--bg-elev); }
.pcard__shot img { width: 100%; height: 100%; display: block;
  object-fit: cover; object-position: top center;
  transition: transform .6s var(--ease); }
.pcard:hover .pcard__shot img { transform: scale(1.03); }
/* fallback for a project with no screenshot yet */
.pcard__mark { width: 56px; height: 56px; border-radius: 10px; display: grid; place-items: center;
  background: var(--bg-elev); border: 1px solid var(--border-2); overflow: hidden;
  font-size: 1.7rem; line-height: 1; color: var(--fg-muted); }
.pcard__title { font-size: 1.05rem; font-weight: 500; letter-spacing: -.01em; color: var(--fg); }
.pcard__desc { font-size: .92rem; color: var(--fg-muted); margin-top: .35rem; max-width: 58ch; }
.pcard__link { display: inline-flex; align-items: center; gap: .35em; margin-top: .8rem;
  font-size: .85rem; color: var(--fg-faint); transition: color .3s; }
.pcard:hover .pcard__link { color: var(--fg-muted); }
.pcard__link .ar { transition: transform .45s var(--ease); }
.pcard:hover .pcard__link .ar { transform: translate(2px, -2px); }
@media (max-width: 620px) {
  .pcard { grid-template-columns: 1fr; gap: 1.1rem; padding: 1.15rem; align-items: start; }
  .pcard__mark { width: 44px; height: 44px; font-size: 1.35rem; }
}
@media (prefers-reduced-motion: reduce) {
  .pcard:hover { transform: none; }
  .pcard:hover .pcard__link .ar { transform: none; }
  .pcard:hover .pcard__shot img { transform: none; }
}

/* ---------- snippets: project galleries ---------- */
/* A card here is a doorway into a pile of images, not a story: it shows one
   cover, the project name, and how many images are behind it. Cover thumbs are
   cropped to a shared ratio so the grid reads as a grid — the images *inside*
   a gallery are never cropped. */
/* same geometry as .wgrid/.wcard above — two up, same gaps, same frame and
   hover, same 720px breakpoint — so the two card rows read as one system */
.ggrid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.75rem, 4vw, 3rem) clamp(1.5rem, 3vw, 2.5rem); margin-top: clamp(2rem, 4vw, 3rem); }
.gcard { display: block; }
.gcard__frame { aspect-ratio: 4/3; border-radius: 8px; overflow: hidden; position: relative;
  border: 1px solid var(--border); background: var(--bg-elev);
  transition: transform .6s var(--ease), box-shadow .6s var(--ease), border-color .4s; }
.gcard__frame img, .gcard__frame video { width: 100%; height: 100%; display: block;
  object-fit: cover; object-position: center; transition: transform 1s var(--ease); }
.gcard:hover .gcard__frame { transform: translateY(-4px); box-shadow: 0 22px 44px -26px rgba(40,32,20,.45); border-color: rgba(17,17,17,.17); }
.gcard:hover .gcard__frame img, .gcard:hover .gcard__frame video { transform: scale(1.035); }
/* matches .wcard__body padding-top and the .wcard__title h3 face */
.gcard__title { font-family: var(--font-serif); font-size: 1.125rem; font-weight: 400;
  letter-spacing: -.01em; color: var(--fg); margin-top: 1.1rem; transition: opacity .3s; }
.gcard:hover .gcard__title { opacity: .62; }
/* the small note under the card */
.gcard__count { font-size: .7rem; font-weight: 500; letter-spacing: .11em; text-transform: uppercase;
  color: var(--fg-faint); margin-top: .5rem; }
.gcard__empty { display: grid; place-items: center; height: 100%;
  font-size: .82rem; color: var(--fg-faint); }
@media (max-width: 720px) { .ggrid { grid-template-columns: 1fr; } }

/* ---------- gallery page ---------- */
.gal-head { padding-top: clamp(2rem, 5vw, 3rem); }
.gal-head h1 { font-size: clamp(1.9rem, 4.4vw, 2.8rem); letter-spacing: -.02em; margin-top: 1.4rem; }
.gal-head .gal-sub { font-size: .9rem; color: var(--fg-faint); margin-top: .6rem; }
.gal-note { font-size: 1.0625rem; color: var(--fg-muted); margin-top: 1rem; max-width: 62ch; }
/* Every image at its own natural height, full width, never cropped. */
.gal-stack { padding-block: clamp(2.5rem, 6vw, 4rem); display: grid; gap: clamp(1.6rem, 3.5vw, 2.6rem); }
.gal-item { border-radius: 8px; overflow: hidden; }
/* Full width, natural height, no cap. width:100% + object-fit:contain used to
   pillarbox tall screenshots into a sliver with dead ground either side, and
   capping the height instead shrank a full-page shot to a thumbnail. A long
   screenshot should simply be long — the page scrolls. */
.gal-item img, .gal-item video { display: block;
  width: 100%; height: auto;
  border-radius: 8px; border: 1px solid var(--border-2); background: var(--bg-elev); }
.gal-item figcaption { font-size: .85rem; color: var(--fg-faint); margin-top: .7rem; }
.gal-missing { padding-block: clamp(3rem, 8vw, 5rem); color: var(--fg-muted); }

/* ---------- media loading ---------- */
/* Media used to arrive at unknown size: a gallery collapsed to nothing, then
   shoved everything down as each file landed. Now every image and clip holds
   its box before it loads — the exact one when content.json knows the file's
   dimensions, a 4:3 stand-in otherwise — and shimmers while it is on the way.
   Where a box is already fixed (card frames), aspect-ratio is ignored and only
   the shimmer shows, which is the intent. */
@keyframes media-shimmer {
  from { background-position: 150% 0; }
  to   { background-position: -150% 0; }
}
img.is-loading, video.is-loading {
  aspect-ratio: var(--ar, 4 / 3);
  background-color: var(--bg-elev);
  background-image: linear-gradient(100deg,
    rgba(17, 17, 17, 0) 30%,
    rgba(17, 17, 17, .06) 47%,
    rgba(17, 17, 17, .06) 53%,
    rgba(17, 17, 17, 0) 70%);
  background-size: 250% 100%;
  background-repeat: no-repeat;
  animation: media-shimmer 1.5s linear infinite;
}
/* a shimmer is decoration; the reserved box is the part that matters */
@media (prefers-reduced-motion: reduce) {
  img.is-loading, video.is-loading { animation: none; }
}

/* ---------- snippet slideshow ---------- */
/* One fixed stage rather than a stack: every slide is letterboxed into the same
   box, so stepping through a collection of mixed portrait and landscape shots
   never moves anything on the page. */
.show { margin-top: clamp(1.1rem, 3vw, 1.8rem); }
.show__stage { position: relative; display: flex; align-items: center; justify-content: center;
  height: clamp(260px, 56vh, 620px);
  background: var(--bg-elev); border: 1px solid var(--border-2);
  border-radius: 10px; overflow: hidden;
  touch-action: pan-y;              /* vertical belongs to the sheet, sideways to us */
  cursor: grab; user-select: none; -webkit-user-select: none; }
.show__stage:active { cursor: grabbing; }
/* a three-slide track that follows the finger; the middle cell is the one
   showing, so the track rests one cell to the left */
.show__view { position: relative; width: 100%; height: 100%; overflow: hidden; }
.show__track { position: absolute; inset: 0; display: flex; width: 300%;
  will-change: transform; }
.show__slide { flex: 0 0 33.3333%; height: 100%;
  display: flex; align-items: center; justify-content: center; }
/* JS sets fit and position per slide: whole on a phone, filled on a wide stage */
.show__slide img, .show__slide video { width: 100%; height: 100%;
  object-fit: contain; display: block; }
/* a loading slide should letterbox like a loaded one, not stretch the stage */
.show__slide img.is-loading, .show__slide video.is-loading { aspect-ratio: auto; }

.show__nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  border: 1px solid var(--border); color: var(--fg);
  box-shadow: 0 2px 12px -6px rgba(40, 32, 20, .5);
  transition: opacity .2s var(--ease), background .2s; }
.show__nav:hover { background: var(--bg); }
.show__nav:focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }
.show__nav--prev { left: 10px; }
.show__nav--next { right: 10px; }
.show__nav[disabled] { opacity: 0; pointer-events: none; }
.show__nav svg { width: 14px; height: 14px; }

.show__bar { margin-top: .6rem; min-height: 1.25em; }
.show__cap { font-size: .85rem; color: var(--fg-faint); }
/* on the stage rather than below it, so an uncaptioned collection spends no
   row on a counter */
.show__count { position: absolute; right: 10px; bottom: 10px; z-index: 2;
  padding: .3em .65em; border-radius: 999px;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  border: 1px solid var(--border-2);
  font-size: .75rem; color: var(--fg-muted);
  font-variant-numeric: tabular-nums; pointer-events: none; }

.show__thumbs { display: flex; gap: .5rem; margin-top: .75rem;
  overflow-x: auto; overscroll-behavior-x: contain; padding-bottom: .35rem;
  scrollbar-width: thin; justify-content: safe center; }
.show__thumb { flex: none; width: 70px; height: 52px; padding: 0;
  border-radius: 6px; overflow: hidden; background: var(--bg-elev);
  border: 1px solid var(--border-2); opacity: .5; cursor: pointer;
  transition: opacity .2s var(--ease), border-color .2s var(--ease); }
.show__thumb img, .show__thumb video { width: 100%; height: 100%; object-fit: cover; display: block; }
.show__thumb:hover { opacity: .85; }
.show__thumb.is-current { opacity: 1; border-color: var(--fg); }
.show__thumb:focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }

@media (max-width: 620px) {
  .show__stage { height: clamp(220px, 46vh, 420px); }
  .show__nav { width: 34px; height: 34px; }
  .show__thumb { width: 58px; height: 44px; }
}

/* A collection is not a document: it fills the panel between the title bar and
   the thumbnail strip, so the slideshow is there on open with nothing to scroll
   past. A case study keeps the ordinary scrolling layout. */
.sheet--media .sheet__scroll { overflow: hidden; display: flex; padding-bottom: 0; }
.sheet--media .sheet__inner { flex: 1; min-height: 0; display: flex; flex-direction: column;
  padding-bottom: clamp(.9rem, 2.2vw, 1.3rem); }
.sheet--media .show { flex: 1; min-height: 0; display: flex; flex-direction: column; margin-top: 0; }
.sheet--media .show__stage { flex: 1; min-height: 0; height: auto; }

/* ---------- bottom sheet ---------- */
/* the dialog fills the viewport and stays transparent; the panel inside it is
   the sheet, so a click landing on the dialog is a click on the backdrop */
/* inset:0 rather than height:100% — Safari resolves a percentage height against
   the viewport mid address-bar transition, which collapsed the dialog and left
   the panel (bottom:0) stranded near the top of the screen, clipped to a sliver */
.sheet { position: fixed; inset: 0; width: auto; height: auto;
  max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0; background: transparent; overflow: hidden; }
/* aligning the panel with flex beats absolute positioning: wherever the dialog
   box ends up, the panel sits on its bottom edge */
.sheet[open] { display: flex; align-items: flex-end; justify-content: center; }
.sheet::backdrop { background: rgba(24, 20, 14, .44); opacity: 0; transition: opacity .38s var(--ease); }
.sheet.is-in::backdrop { opacity: 1; }

.sheet__panel { position: relative; width: min(940px, 100%); height: 92%;
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--bg); border-radius: 20px 20px 0 0;
  box-shadow: 0 -30px 80px -40px rgba(40, 32, 20, .6);
  transform: translateY(100%); transition: transform .42s var(--ease); }
.sheet.is-in .sheet__panel { transform: translateY(0); }

/* grabber and close, both live in a bar you can drag downward */
.sheet__head { position: relative; flex: none; padding: 10px 0 0;
  background: var(--bg); touch-action: none; cursor: grab;
  border-bottom: 1px solid var(--border-2); }
.sheet__head:active { cursor: grabbing; }
.sheet__grab { display: block; width: 42px; height: 5px; margin: 0 auto;
  border-radius: 999px; background: rgba(17, 17, 17, .15); }
/* title and close share a row and a centre line */
.sheet__bar { display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  height: 46px; padding-inline: clamp(1rem, 3vw, 1.6rem); }
.sheet--media .sheet__title { opacity: 1; }
.sheet__title { font-family: var(--font-sans); font-size: 1rem; font-weight: 500;
  letter-spacing: -.01em; color: var(--fg); margin: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: 0; transition: opacity .25s var(--ease); }
.sheet__title.is-shown { opacity: 1; }
.sheet__x { flex: none;
  width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center;
  color: var(--fg-muted); background: var(--bg-elev);
  transition: color .25s, background .25s; }
.sheet__x:hover { color: var(--fg); background: rgba(17, 17, 17, .08); }
.sheet__x:focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }
.sheet__scroll:focus { outline: none; }
.sheet__x svg { width: 14px; height: 14px; }

.sheet__scroll { flex: 1; overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch; padding-bottom: clamp(2rem, 6vw, 3.5rem); }
.sheet__inner { max-width: 900px; margin-inline: auto;
  padding: clamp(1rem, 3vw, 1.5rem) clamp(1rem, 3vw, 1.6rem) 0; }
.sheet__scroll .gal-stack { padding-block: 0; gap: clamp(1rem, 2.5vw, 1.6rem); }
.sheet__empty, .sheet__loading { padding-block: clamp(2rem, 6vw, 3rem); color: var(--fg-faint); text-align: center; }
/* the case study arrives with its own hero spacing, which is too much here */
.sheet__scroll .case-hero { padding-top: .5rem; }

/* the page behind must not scroll while the sheet is up. Reserving the
   scrollbar gutter up front stops the whole layout jumping sideways by the
   scrollbar's width the moment the sheet opens. */
html { scrollbar-gutter: stable; }
/* overflow:hidden alone does not hold on iOS — the page scrolls behind the
   sheet and lands somewhere else on close. Pinning the body and putting the
   scroll back afterwards is the only thing Safari respects. */
body.sheet-open { position: fixed; left: 0; right: 0; width: 100%; overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .sheet__panel { transition: none; transform: none; }
  .sheet::backdrop { transition: none; opacity: 1; }
}

/* ---------- contact ---------- */
.contact h2 { max-width: 16ch; }
.contact__mail { display: inline-block; margin-top: 1.4rem; font-family: var(--font-serif);
  font-size: clamp(1.3rem, 3vw, 1.9rem); border-bottom: 1px solid var(--border); padding-bottom: .1em;
  transition: border-color .3s; }
.contact__mail:hover { border-color: var(--fg); }
.contact__links { display: flex; gap: 1.4rem; margin-top: 2rem; font-size: .9rem; }
.contact__links a { color: var(--fg-muted); transition: color .25s; }
.contact__links a:hover { color: var(--fg); }

/* ---------- footer ---------- */
.site-footer { margin-top: clamp(4rem, 8vw, 7rem); border-top: 1px solid var(--border-2); }
.site-footer__inner { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  padding-block: 1.9rem; font-size: .82rem; color: var(--fg-faint); }


/* =============================================================
   CASE STUDY PAGES
   ============================================================= */
.case-hero { padding-top: clamp(2.5rem, 6vw, 4.5rem); padding-bottom: clamp(1.5rem, 3vw, 2.5rem); }
.case-back { font-size: .875rem; color: var(--fg-muted); display: inline-flex; align-items: center; gap: .45rem; transition: color .25s; }
.case-back:hover { color: var(--fg); }
.case-hero h1 { margin-top: .8rem; max-width: 18ch; }
.case-lead { margin-top: 1.5rem; max-width: 62ch; font-size: 1.0625rem; }
.case-meta { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem;
  margin-top: 2.4rem; padding-block: 1.5rem; border-top: 1px solid var(--border-2); border-bottom: 1px solid var(--border-2); }
.case-meta dt { font-size: .72rem; color: var(--fg-faint); }
.case-meta dd { font-weight: 500; margin-top: .3rem; font-size: .95rem; }
@media (max-width: 620px) { .case-meta { grid-template-columns: 1fr 1fr; gap: 1.2rem; } }

/* Uploaded media decides its own height: the frame has no fixed ratio and the
   image is never cropped — whatever is uploaded is shown whole, edge to edge. */
.case-cover { margin-top: 2rem; border-radius: 8px; overflow: hidden; border: 1px solid var(--border);
  background: var(--bg-elev); }
.case-cover img, .case-cover video { display: block; width: 100%; height: auto;
  max-height: var(--media-max-h); object-fit: contain; }

.case-body { padding-block: clamp(2.5rem, 6vw, 4rem); }
.case-body section + section, .case-body section + figure { margin-top: clamp(2rem, 4vw, 3rem); }
.case-body figure + section { margin-top: clamp(2rem, 4vw, 3rem); }
.case-body h2 { font-size: clamp(1.4rem, 2.8vw, 1.9rem); margin-bottom: .9rem; }
.case-body p { font-size: 1.0625rem; }
.case-body p + p { margin-top: 1rem; }
.case-body ul { margin: 1rem 0 0 1.1rem; color: var(--fg-muted); }
.case-body li { margin-bottom: .45rem; }

.case-fig { border-radius: 8px; overflow: hidden; }
.case-fig img, .case-fig video { display: block; width: 100%; height: auto;
  max-height: var(--media-max-h); object-fit: contain;
  border-radius: 8px; border: 1px solid var(--border-2); background: var(--bg-elev); }
.case-fig figcaption { font-size: .85rem; color: var(--fg-faint); margin-top: .7rem; }

.case-metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-top: 1.4rem; }
.cmet { border-top: 1px solid var(--border); padding-top: 1rem; }
.cmet .n { font-family: var(--font-serif); font-size: clamp(1.6rem, 3.4vw, 2.2rem); letter-spacing: -.012em; }
.cmet .l { font-size: .85rem; color: var(--fg-muted); margin-top: .25rem; }
@media (max-width: 620px) { .case-metrics { grid-template-columns: 1fr; gap: 1.2rem; } }

.case-blocks > section, .case-blocks > figure { margin-top: clamp(2rem, 4vw, 3rem); }
.case-blocks h2 { font-size: clamp(1.4rem, 2.8vw, 1.9rem); margin-bottom: .9rem; }
.case-blocks p { font-size: 1.0625rem; }
.case-blocks p + p { margin-top: 1rem; }

.case-next { display: block; border-top: 1px solid var(--border-2); padding-block: clamp(2.5rem, 6vw, 4.5rem); }
.case-next__t { font-family: var(--font-serif); font-size: clamp(1.6rem, 4vw, 2.6rem);
  letter-spacing: -.012em; margin-top: .5rem; transition: opacity .3s; }
.case-next:hover .case-next__t { opacity: .6; }

/* ---------- placeholder marker ---------- */
.ph { font-style: italic; color: var(--fg-faint); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .marquee__track { animation: none; }
  .stmt__scroll { height: auto; }
  .stmt__sticky { position: static; height: auto; padding-block: 4rem; }
  .stmt__text { color: var(--fg); }
  [data-r] { opacity: 1 !important; transform: none !important; }
  .status__dot::after { animation: none; box-shadow: 0 0 0 3px rgba(63,174,106,.22); }
  .wcard:hover .wcard__frame, .wcard:hover .wcard__frame img { transform: none; }
  .gcard:hover .gcard__frame, .gcard:hover .gcard__frame img, .gcard:hover .gcard__frame video { transform: none; }
}
