/* ============================================================
   Dhananjay Chopra — Portfolio
   Design tokens, paper-frame layout, time-aware sky
   ============================================================ */

/* Recoleta, declared here rather than pulled in via the cdnfonts stylesheet.
   That stylesheet was a render-blocking request to a third-party origin whose
   @font-face carries no font-display, so the browser hid every heading until
   the font arrived (up to 3s on a slow connection) — the hero headline is the
   largest thing on the page, so that blank wait was the whole first paint.
   Same URL, one less blocking round trip, and swap paints the fallback
   immediately. local() first, so anyone with the font installed skips the
   download entirely. If the URL ever 404s, self-host the .woff next to this
   file and point src at it. */
@font-face {
  font-family: "Recoleta";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: local("Recoleta"),
       url("https://fonts.cdnfonts.com/s/31694/Recoleta-RegularDEMO.woff") format("woff");
}

/* The free CDN build of Recoleta replaces "%" (U+0025) with a foundry
   watermark. Desktop override (works where these system serifs exist).
   Must stay AFTER the face above: for U+0025 both match, and the later
   declaration wins. */
@font-face {
  font-family: "Recoleta";
  src: local("Georgia"), local("Times New Roman"), local("serif");
  unicode-range: U+0025;
}
/* Robust cross-device fix: script.js wraps every "%" inside a Recoleta heading
   in <span class="pct">, which renders with the generic serif (always present,
   including on phones where Georgia/Times aren't installed). */
.pct { font-family: Georgia, "Times New Roman", serif; }

/* Smooth cross-fade when navigating between pages / back-forward
   (View Transitions API; ignored by browsers that don't support it). */
@view-transition { navigation: auto; }
::view-transition-old(root),
::view-transition-new(root) { animation-duration: 0.32s; }

/* ---------- Tokens ---------- */
:root {
  --bg:            #ffffff;
  --bg-elev:       #ffffff;
  --ink:           #16150f;
  --ink-soft:      #4a4740;
  --ink-faint:     #86827a;
  --line:          #e4e1d8;
  --line-strong:   #d6d2c6;
  --accent:        #1d4ed8;
  --accent-soft:   #6a8cf0;
  --cta-bg:        #33302a;  /* filled-CTA ground: a step lighter than --ink so the buttons read warm, not black */
  --device-bg:     #f7f7f7;
  --chip-bg:       rgba(255,255,255,0.72);
  --dot:           #dcd8cd;
  --dot-hot:       #1d4ed8;
  --frame-max:     1120px;
  --pad-x:         clamp(20px, 5vw, 72px);
  --sect-pad:      clamp(84px, 13vh, 132px);  /* shared top/bottom rhythm for hero + sections */

  --serif: "Recoleta", "Fraunces", Georgia, "Times New Roman", serif;
  --sans:  "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --dur:  0.6s;
}
@media (max-width: 640px) {
  :root { --sect-pad: clamp(44px, 7vh, 68px); }  /* tighter vertical rhythm on mobile */
}

:root[data-theme="dark"] {
  --bg:            #0c0d10;
  --bg-elev:       #15171c;
  --ink:           #f3f1ea;
  --ink-soft:      #b8b5ad;
  --ink-faint:     #7d7a72;
  --line:          #23252b;
  --line-strong:   #2c2f36;
  --accent:        #7aa2ff;
  --accent-soft:   #9db6ff;
  --cta-bg:        var(--ink);  /* dark theme's CTA is already a light pill — nothing to lighten */
  --device-bg:     #1a1c22;
  --chip-bg:       rgba(20,22,27,0.66);
  --dot:           #2a2d34;
  --dot-hot:       #7aa2ff;
}

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

a { color: inherit; text-decoration: none; }
::selection { background: var(--accent); color: #fff; }

/* ---------- Paper frame (paper.design blueprint) ---------- */
.frame {
  position: relative;
  z-index: 1;
  max-width: var(--frame-max);
  margin: 0 auto;
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  min-height: 100vh;
  background: var(--bg);   /* opaque, so the canvas dots only show in the side gutters */
}

/* canvas-style interactive dot grid filling the area outside the frame */
.canvas-dots {
  position: fixed; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}

/* section divider intersection marks, centered on the outer frame lines */
.section-head::before,
.section-head::after {
  content: "";
  position: absolute;
  width: 9px; height: 9px;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: var(--bg);
}

/* ---------- Nav ---------- */
.nav {
  position: sticky;
  top: 0;
  z-index: 30;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: saturate(160%) blur(14px);
  -webkit-backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid var(--line);
}
.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px var(--pad-x);
}
.nav__group { display: flex; align-items: center; gap: 22px; }
.nav__link {
  font-size: 15px;
  font-weight: 450;
  color: var(--ink-soft);
  position: relative;
  transition: color 0.25s var(--ease);
}
.nav__link::after {
  content: ""; position: absolute; left: 0; bottom: -4px;
  width: 100%; height: 1px; background: var(--ink);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.3s var(--ease);
}
.nav__link:hover { color: var(--ink); }
.nav__link:hover::after { transform: scaleX(1); }

.nav__resume {
  font-size: 14px; font-weight: 500;
  color: var(--bg);
  background: var(--ink);
  padding: 8px 16px;
  border-radius: 999px;
  transition: transform 0.25s var(--ease), opacity 0.25s;
}
.nav__resume:hover { transform: translateY(-1px); opacity: 0.9; }

/* theme toggle */
.theme-toggle {
  border: none; background: none; cursor: pointer; padding: 0;
  color: var(--ink);
}
.theme-toggle__track {
  display: grid; place-items: center;
  width: 34px; height: 34px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  position: relative;
  transition: border-color 0.25s, background 0.25s;
}
.theme-toggle:hover .theme-toggle__track { background: var(--bg-elev); }
.theme-toggle__icon {
  width: 17px; height: 17px;
  grid-area: 1 / 1;
  transition: opacity 0.4s var(--ease), transform 0.5s var(--ease);
}
.theme-toggle__icon--sun  { opacity: 1; transform: rotate(0) scale(1); }
.theme-toggle__icon--moon { opacity: 0; transform: rotate(-90deg) scale(0.5); }
:root[data-theme="dark"] .theme-toggle__icon--sun  { opacity: 0; transform: rotate(90deg) scale(0.5); }
:root[data-theme="dark"] .theme-toggle__icon--moon { opacity: 1; transform: rotate(0) scale(1); }

/* ---------- Hero ---------- */
.hero {
  position: relative;
  padding: var(--sect-pad) var(--pad-x) var(--sect-pad);
  overflow: hidden;
}

.hero__content { position: relative; z-index: 3; max-width: none; }

/* Hero entrance: the headline resolves word by word out of a soft blur, with
   the avatar, greeting and CTAs easing up around it — replacing the old single
   all-at-once fade. The sky is a sibling of .hero__content, so it stays put. */
.hero__content > * { animation: heroFadeIn 0.7s var(--ease) both; }
.hero .avatar   { animation-delay: 0.04s; }
.hero__greeting { animation-delay: 0.12s; }
.hero__headline { animation: none; }        /* animates per word instead */
.hero__meta     { animation-delay: 0.62s; }
@keyframes heroFadeIn {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* Word-by-word blur-in. Opacity + blur only, no movement, so the line never
   reflows mid-animation. Delays are set per word in script.js. */
.hero__word {
  display: inline-block;
  opacity: 0;
  filter: blur(10px);
  animation: heroWordIn 0.6s var(--ease) forwards;
}
@keyframes heroWordIn {
  to { opacity: 1; filter: blur(0); }
}
/* Finished state, pinned by script.js once the sequence completes. Also drops
   the animations so nothing keeps a compositing layer alive afterwards. */
.hero__content.hero-in > *,
.hero__content.hero-in .hero__word {
  animation: none; opacity: 1; filter: none; transform: none;
}
/* Reduced motion / low-end: no blur work, text simply present. Opacity is reset
   explicitly — the animation is what reveals it, so without these the headline
   would stay invisible. */
@media (prefers-reduced-motion: reduce) {
  .hero__content > *,
  .hero__word { animation: none; opacity: 1; filter: none; transform: none; }
}
html.lite .hero__content > *,
html.lite .hero__word { animation: none; opacity: 1; filter: none; transform: none; }

.avatar { margin-bottom: 22px; }
.avatar__img {
  width: 100px; height: 100px;
  border-radius: 12px;
  overflow: hidden;
  background: transparent;
  display: grid; place-items: center;
  color: #fff; font-family: var(--serif); font-weight: 600; font-size: 32px;
  border: 1px solid var(--line-strong);
}
.avatar__img img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center 22%;
  display: block; border-radius: inherit;
}
.hero__greeting {
  font-size: 17px; color: var(--ink-soft); margin: 0 0 12px;
}
.hero__headline {
  font-family: var(--serif);
  font-weight: 400;
  -webkit-text-stroke: 0.3px currentColor; /* subtle weight bump (CDN only ships Regular) */
  font-size: clamp(25px, 3.4vw, 40px);
  line-height: 1.14;
  letter-spacing: -0.012em;
  margin: 0;
  max-width: none;
  text-wrap: balance;
}
@media (max-width: 640px) { .hero__headline { line-height: 1.34; } }

/* Mobile: keep the sticky nav rock-steady — an opaque nav avoids the
   backdrop-filter repaint that makes it jitter/shrink while scrolling. */
@media (max-width: 640px) {
  .nav {
    background: var(--bg);
    backdrop-filter: none; -webkit-backdrop-filter: none;
    transform: translateZ(0);
  }
  /* The gutter dot-canvas isn't visible on mobile (the frame is full-width) and
     it renders a few px wider than the viewport, causing a slight horizontal
     scroll — hide it. */
  .canvas-dots { display: none; }
  /* Belt-and-braces: clip any stray horizontal overflow (clip keeps the sticky
     nav working, unlike overflow:hidden). */
  html, body { overflow-x: clip; }
}

/* No hero background animation on mobile */
@media (max-width: 640px) { .sky { display: none; } }
.hero__headline .grad { -webkit-text-stroke: 0; } /* keep the gradient numerals clean */
/* vertically centre the arrow between the 0 and 1 (own gradient so the nudge
   doesn't fall outside the parent's clipped fill) */
.hero__headline .grad__arrow {
  display: inline-block; position: relative; top: -0.06em;
  background: linear-gradient(100deg, var(--accent), var(--accent-soft));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.hero__headline .grad {
  background: linear-gradient(100deg, var(--accent), var(--accent-soft));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  white-space: nowrap;
}
.hero__meta { margin-top: 30px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.chip {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 450; color: var(--ink-soft);
  background: var(--chip-bg);
  padding: 7px 13px; border-radius: 999px;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.cta {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 14px; font-weight: 500;
  color: var(--bg); background: var(--cta-bg);
  padding: 8px 17px; border-radius: 999px;
  transition: transform 0.25s var(--ease), opacity 0.25s;
}
.cta:hover { transform: translateY(-1px); opacity: 0.9; }
.cta--secondary {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: 999px; /* secondary stays a pill */
}
.cta--secondary:hover { background: var(--bg-elev); opacity: 1; }
.chip__dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: #46c26a;
  box-shadow: 0 0 0 0 rgba(70,194,106,0.5);
  animation: pulse 2.4s infinite;
}
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(70,194,106,0.45); }
  70% { box-shadow: 0 0 0 7px rgba(70,194,106,0); }
  100% { box-shadow: 0 0 0 0 rgba(70,194,106,0); }
}

/* ---------- Sky ---------- */
.sky {
  position: absolute; inset: 0; z-index: 1;
  overflow: hidden;
  /* Sky is anchored as an ambient glow in the top-right, fading toward the content. */
  -webkit-mask-image: radial-gradient(115% 78% at 100% 0%, #000 30%, rgba(0,0,0,0.4) 56%, transparent 80%);
          mask-image: radial-gradient(115% 78% at 100% 0%, #000 30%, rgba(0,0,0,0.4) 56%, transparent 80%);
}
.sky__gradient {
  position: absolute; inset: 0;
  transition: background 1.6s var(--ease);
}
.sky__celestial {
  position: absolute; inset: 0; width: 100%; height: 100%;
  z-index: 1;
}
.sky__haze {
  position: absolute; inset: 0;
  background: radial-gradient(112% 74% at 100% 0%,
    transparent 0%,
    transparent 24%,
    color-mix(in srgb, var(--bg) 52%, transparent) 46%,
    var(--bg) 72%);
  pointer-events: none;
}
.sky__stars { position: absolute; inset: 0; opacity: 0; transition: opacity 1.6s var(--ease); }
.star {
  position: absolute; width: 2px; height: 2px; border-radius: 50%;
  background: #fff;
  animation: twinkle var(--tw, 4s) ease-in-out infinite;
}
@keyframes twinkle { 0%,100% { opacity: 0.25; } 50% { opacity: 1; } }

/* sun / moon */
.celestial-body { transition: transform 1.6s var(--ease); filter: blur(0.5px); }

/* clouds — soft, wispy, built from blurred translucent puffs */
.sky__clouds { position: absolute; inset: 0; z-index: 2; }
.cloud {
  position: absolute;
  opacity: 0;
  filter: blur(4px);
  transition: opacity 1.6s var(--ease);
  will-change: transform;
}
.cloud .puff {
  position: absolute;
  bottom: 0;
  border-radius: 50%;
  background: var(--tint, #fff);
}
@keyframes drift {
  from { transform: translateX(-40%); }
  to   { transform: translateX(130vw); }
}

/* ---------- Section heads ---------- */
.section-head {
  position: relative;
  display: flex; align-items: baseline; gap: 16px;
  padding: var(--sect-pad) var(--pad-x) 30px;
  border-top: 1px solid var(--line);
}
.section-head::before { left: -5.5px; top: -5px; }
.section-head::after  { right: -5.5px; top: -5px; }
.section-head__index {
  font-family: var(--sans); font-size: 12px; font-weight: 600;
  letter-spacing: 0.14em; color: var(--ink-faint);
}
.section-head__title {
  font-family: var(--serif); font-weight: 400; -webkit-text-stroke: 0.3px currentColor;
  font-size: clamp(22px, 3vw, 30px); margin: 0; letter-spacing: -0.01em;
}

/* ---------- Work ---------- */
.work { position: relative; }
.project {
  padding: clamp(30px, 4vw, 48px) var(--pad-x) clamp(44px, 6vw, 76px);
}
.work .project:first-of-type { padding-top: clamp(8px, 1.4vw, 16px); }
.project .sol-plate { margin-top: 0; }
.project:last-of-type { padding-bottom: var(--sect-pad); }
.project__frame {
  background: var(--device-bg);
  border-radius: 20px;
  padding: clamp(36px, 6vw, 72px) 0;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  transition: background var(--dur) var(--ease);
}
.project__caption {
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; padding-top: 20px; flex-wrap: wrap;
}
@media (max-width: 640px) {
  .project__caption { flex-direction: column; align-items: flex-start; gap: 14px; }
}
.project__desc {
  margin: 0; max-width: 60ch; font-size: 16px; color: var(--ink-soft);
  line-height: 1.55;
}
.project__desc-link {
  color: inherit; text-decoration: none;
  transition: color 0.2s var(--ease);
}
.project__desc-link:hover { color: var(--ink); }
@media (max-width: 640px) {
  .project__desc { font-size: 15px; }
}
.project__tag {
  font-size: 12px; font-weight: 500; letter-spacing: 0.02em;
  color: var(--ink-faint); white-space: nowrap;
  border: 1px solid var(--line-strong); border-radius: 999px;
  padding: 5px 12px;
}

/* Phone mockup — a clean frame with a placeholder screen.
   Drop your own screenshot in by adding an <img class="device__shot"> inside .device
   (or set --shot: url("...") on the .device element). */
.device {
  position: relative;
  width: 196px; height: 404px;
  background: #0c0d10;
  border-radius: 34px;
  padding: 7px;
  box-shadow: 0 30px 60px -25px rgba(0,0,0,0.55), inset 0 0 0 1px rgba(255,255,255,0.08);
  --scr-a: #eaf1ff; --scr-b: #f6f8fc;
}
.device--alt  { --scr-a: #fff4e6; --scr-b: #fff9f2; }
.device--care { --scr-a: #e9fbf1; --scr-b: #f4fdf8; }
.device::before { /* dynamic island */
  content: ""; position: absolute; top: 15px; left: 50%; transform: translateX(-50%);
  width: 56px; height: 16px; background: #000; border-radius: 10px; z-index: 4;
}
.device::after { /* placeholder screen */
  content: ""; position: absolute; inset: 7px; border-radius: 27px;
  background:
    var(--shot, radial-gradient(120% 90% at 50% 0%, var(--scr-a) 0%, var(--scr-b) 45%, #ffffff 100%));
  background-size: cover; background-position: center;
}
.device__shot, /* real screenshot slot */
.device__video { /* case-study screen recording */
  position: absolute; inset: 7px; border-radius: 27px;
  width: calc(100% - 14px); height: calc(100% - 14px);
  object-fit: cover; z-index: 2;
}
/* the video stays hidden until it actually loads, so the placeholder
   screen shows gracefully whenever a file is missing */
.device__video { opacity: 0; transition: opacity 0.5s var(--ease); }
.device__video.is-ready { opacity: 1; }

/* ---------- About ---------- */
.about__grid {
  display: grid;
  grid-template-columns: 1.45fr 1fr;
  gap: clamp(32px, 6vw, 76px);
  align-items: start;
  padding: 18px var(--pad-x) var(--sect-pad);
}
.about__headline {
  font-family: var(--serif); font-weight: 400; -webkit-text-stroke: 0.3px currentColor;
  font-size: clamp(26px, 3.4vw, 40px); line-height: 1.12;
  letter-spacing: -0.015em; margin: 0 0 30px;
}
.about__body p {
  margin: 0 0 20px; max-width: 48ch;
  font-size: 16px; line-height: 1.6; color: var(--ink-soft);
}
.about__body p:last-child { margin-bottom: 0; }
.about__body--em { font-weight: 600; color: var(--ink); }
.about__link {
  color: var(--ink); font-weight: 500;
  text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: var(--line-strong);
  transition: text-decoration-color 0.2s var(--ease);
}
.about__link:hover { text-decoration-color: var(--ink); }
.about__cta { margin-top: 34px; }

/* Vertical auto-scroll reel. Each image carries its own bottom margin (not a
   flex gap) so the two stacked copies tile perfectly and translateY(-50%)
   lands exactly on the start of the second copy — a seamless, jump-free loop. */
.about__reel {
  position: relative;
  height: clamp(440px, 56vh, 580px);
  overflow: hidden;
  border-radius: 20px;
}
.about__reel-track {
  display: block;
  animation: aboutReel 28s linear infinite;
  will-change: transform;
}
.about__reel:hover .about__reel-track { animation-play-state: paused; }
/* Only scroll while the About section is on screen (set by script.js). */
.about__reel.is-paused .about__reel-track { animation-play-state: paused; }
.about__reel-track img {
  display: block; width: 100%;
  /* height:auto is required, not redundant: the HTML width/height attributes
     are presentational hints for the width/height properties, so without this
     the attribute height wins and aspect-ratio below is ignored. */
  height: auto;
  aspect-ratio: 3 / 4; object-fit: cover;
  border-radius: 16px; margin-bottom: 16px;
  background: var(--device-bg);
}
@keyframes aboutReel {
  from { transform: translateY(0); }
  to   { transform: translateY(-50%); }
}
/* Respect reduced-motion / low-end devices: hold the reel still. */
@media (prefers-reduced-motion: reduce) { .about__reel-track { animation: none; } }
html.lite .about__reel-track { animation: none; }

@media (max-width: 720px) {
  .about__grid { grid-template-columns: 1fr; gap: 32px; }
  .about__reel {
    height: clamp(320px, 78vw, 400px); order: -1; /* reel above the text on mobile */
    pointer-events: none; /* a tap can't pause or grab the reel */
  }
  .about__reel:hover .about__reel-track { animation-play-state: running; }
  .about__headline { margin-bottom: 16px; }
  .about__body p { max-width: none; font-size: 15px; }
}

/* ---------- Footer ---------- */
.footer__inner { padding: 22px var(--pad-x) var(--sect-pad); }
.footer__mail {
  font-family: var(--serif); font-weight: 400; -webkit-text-stroke: 0.3px currentColor;
  font-size: clamp(24px, 4.5vw, 44px); letter-spacing: -0.015em;
  display: inline-block; position: relative;
  transition: opacity 0.3s;
  word-break: break-word;
}
.footer__mail:hover { opacity: 0.7; }
.footer__links {
  display: flex; gap: 22px; flex-wrap: wrap;
  margin: 28px 0 40px; font-size: 15px; color: var(--ink-soft);
}
.footer__links a { transition: color 0.25s; }
.footer__links a:hover { color: var(--ink); }
.footer__top { margin-left: auto; }
.footer__fine { font-size: 13px; color: var(--ink-faint); margin: 0; }

/* ---------- Reveal on scroll ---------- */
[data-reveal] {
  opacity: 0; transform: translateY(22px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
[data-reveal].is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
  html { scroll-behavior: auto; }
  [data-reveal] { opacity: 1; transform: none; }
}

/* ============================================================
   Case study page
   ============================================================ */
.case-header {
  position: sticky; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: saturate(160%) blur(14px);
  -webkit-backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid var(--line);
}
.case-header .nav__inner { border-bottom: 1px solid var(--line); transition: border-color 0.24s var(--ease); }

/* Collapse the sub-nav (animated) once scrolled past the last section (toggled in JS) */
.case-header .casenav {
  max-height: 60px; overflow-y: hidden;
  transition: max-height 0.34s var(--ease), opacity 0.24s var(--ease), padding 0.34s var(--ease);
}
.case-header.casenav-hidden .casenav {
  max-height: 0; opacity: 0; padding-top: 0; padding-bottom: 0; pointer-events: none;
}
/* when collapsed, drop the nav row's own border so it doesn't double up with the header's */
.case-header.casenav-hidden .nav__inner { border-bottom-color: transparent; }

.casenav {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  padding: 12px var(--pad-x);
  font-size: 14px; overflow-x: auto; scrollbar-width: none;
}
.casenav::-webkit-scrollbar { display: none; }
.casenav__links { position: relative; display: flex; align-items: center; gap: 2px; }
/* sliding gray pill behind the active section */
.casenav__pill {
  position: absolute; top: 0; left: 0; height: 100%;
  width: 0; border-radius: 999px; background: #ececec;
  z-index: 0; pointer-events: none; opacity: 0;
  transition: transform 0.36s var(--ease), width 0.36s var(--ease), opacity 0.2s var(--ease);
}
.casenav__pill.is-ready { opacity: 1; }

/* Segmented scroll progress along the bottom edge of the case-study header —
   one segment per nav section, each sized to that section's share of the page
   (YouTube chapter-bar style). Deliberately a sibling of .casenav, not a child:
   .casenav scrolls horizontally on mobile and would drag the bar with it. */
.casenav__progress {
  position: relative; z-index: 2;
  display: flex; gap: 3px;
  height: 2px;
  margin-bottom: -1px;          /* cover the header's hairline border */
  cursor: pointer;
  /* pan-y, not none: a horizontal swipe scrubs, while a vertical swipe still
     scrolls the page normally instead of being swallowed by the bar. */
  touch-action: pan-y;
  transition: height 0.34s var(--ease), opacity 0.24s var(--ease);
}
/* Collapse with the sub-nav once scrolled past the last section */
.case-header.casenav-hidden .casenav__progress {
  height: 0; opacity: 0; pointer-events: none;
}
/* Invisible grab area: the visual bar is only 3px, far too thin to hit. Reaches
   up into the sub-nav's bottom padding (so it never covers a link) and a few px
   below, without affecting layout. */
.casenav__progress::before {
  content: ""; position: absolute;
  left: 0; right: 0; top: -6px; bottom: -5px;
}
.casenav__seg {
  position: relative;
  flex: 0 1 0%;                 /* flex-grow set per-section from JS */
  min-width: 10px;              /* keep very short sections visible */
  background: var(--line);
  overflow: hidden;
  transform-origin: bottom;     /* thickens upward, so no layout shift */
  transition: transform 0.16s var(--ease);
}
.casenav__progress.is-scrubbing .casenav__seg { transform: scaleY(2); }
.casenav__seg-fill {
  position: absolute; inset: 0;
  background: var(--ink-faint);
  transform: scaleX(0);
  transform-origin: left center;
}
/* Section name, following the pointer while hovering or scrubbing */
.casenav__tip {
  position: absolute; top: 11px; left: 0;
  transform: translateX(-50%);
  padding: 4px 9px; border-radius: 7px;
  background: var(--ink); color: var(--bg);
  font-size: 11px; font-weight: 500;
  white-space: nowrap; pointer-events: none;
  opacity: 0; transition: opacity 0.15s var(--ease);
}
.casenav__progress.is-scrubbing .casenav__tip { opacity: 1; }
/* Hover states only where a real pointer exists. On touch, :hover sticks after
   release and would leave the bar thickened with the label still showing. */
@media (hover: hover) and (pointer: fine) {
  .casenav__progress:hover .casenav__seg { transform: scaleY(2); }
  .casenav__progress:hover .casenav__tip { opacity: 1; }
}
.casenav a {
  color: var(--ink-faint); white-space: nowrap; position: relative;
  transition: color 0.25s var(--ease);
}
.casenav__links a { position: relative; z-index: 1; padding: 6px 14px; }
.casenav a:hover { color: var(--ink); }
.casenav a.is-active { color: var(--ink); }
.casenav a.casenav__back {
  position: absolute; left: var(--pad-x); top: 50%; transform: translateY(-50%);
  color: var(--ink-soft); font-weight: 450;
}
@media (max-width: 760px) {
  .casenav { justify-content: flex-start; }
  .casenav a.casenav__back { position: static; transform: none; margin-right: 24px; }
}

/* ---- Hero ---- */
.case-hero { padding: clamp(56px, 8vh, 96px) var(--pad-x) clamp(28px, 4vw, 44px); }
.case-eyebrow {
  font-size: 13px; font-weight: 500; letter-spacing: 0.06em;
  color: var(--ink-faint); text-transform: uppercase; margin: 0 0 18px;
}
.case-title {
  font-family: var(--serif); font-weight: 400;
  -webkit-text-stroke: 0.3px currentColor;
  font-size: clamp(40px, 7vw, 78px); line-height: 1.02;
  letter-spacing: -0.02em; margin: 0 0 20px;
}
.case-subtitle {
  font-size: clamp(18px, 2.2vw, 23px); line-height: 1.45;
  color: var(--ink-soft); max-width: 40ch; margin: 0;
}

/* ---- Sections ---- */
.case-section {
  padding: var(--sect-pad) var(--pad-x);
  border-top: 1px solid var(--line);
  scroll-margin-top: 116px;
}
.case-section--first { border-top: none; }
.case-section__title {
  font-family: var(--serif); font-weight: 400;
  -webkit-text-stroke: 0.3px currentColor;
  font-size: clamp(30px, 5vw, 40px); letter-spacing: -0.015em; line-height: 1.12;
  margin: 0 0 clamp(24px, 3vw, 38px);
}
.case-overview__headline { max-width: none; margin-bottom: 80px; line-height: 1.1; }
#impact { scroll-margin-top: 116px; }
.case-lead {
  font-size: 18px; font-weight: 400; line-height: 1.6;
  letter-spacing: -0.005em; color: var(--ink); max-width: 75%; margin: 0 0 44px;
}
.case-body {
  font-size: 18px; line-height: 1.65;
  color: var(--ink-soft); max-width: none; margin: 0 0 20px;
}
.case-body strong { color: var(--ink); font-weight: 600; }
.case-body em { font-style: italic; color: var(--ink); }

.case-list { list-style: none; padding: 0; margin: 28px 0 0; max-width: 66ch; }
.case-list li {
  position: relative; padding: 16px 0 16px 26px;
  border-top: 1px solid var(--line);
  font-size: 18px; line-height: 1.6; color: var(--ink-soft);
}
.case-list li::before {
  content: ""; position: absolute; left: 2px; top: 25px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
}
.case-list strong { color: var(--ink); font-weight: 600; }

/* ---- Meta grid (Overview) ---- */
.metagrid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
  margin: 44px 0 0;
}
.metagrid > div {
  display: flex; flex-direction: column; gap: 10px;
  padding: clamp(40px, 5vw, 60px) clamp(20px, 2vw, 28px) clamp(28px, 3vw, 40px);
  border-left: 1px solid var(--line);
}
.metagrid > div:first-child { border-left: none; }
.metagrid__label { font-size: 15px; color: var(--ink-faint); }
.metagrid__value { font-size: clamp(17px, 1.6vw, 19px); font-weight: 600; color: var(--ink); }
@media (max-width: 720px) {
  .metagrid { grid-template-columns: 1fr 1fr; }
  .metagrid > div:nth-child(odd) { border-left: none; }
  .metagrid > div:nth-child(n+3) { border-top: 1px solid var(--line); }
}

/* ---- Figures ---- */
.case-figure { margin: clamp(40px, 5vw, 64px) 0 0; }
.project__frame--pair { display: flex; gap: clamp(20px, 4vw, 48px); }
.case-figure__caption {
  font-size: 14px; color: var(--ink-faint); margin: 16px 0 0; max-width: 60ch;
}

/* ---- Feature grid (Solution) ---- */
.feature-grid {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
  margin-top: 12px;
}
.feature { background: var(--bg); padding: clamp(24px, 3vw, 34px); }
.feature h3 {
  font-family: var(--sans); font-size: 17px; font-weight: 600;
  margin: 0 0 8px; color: var(--ink);
}
.feature p { margin: 0; font-size: 15px; line-height: 1.55; color: var(--ink-soft); }
@media (max-width: 620px) { .feature-grid { grid-template-columns: 1fr; } }

/* ---- Big number (Logins) ---- */
.bignum {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 16px;
  margin: 8px 0 44px; font-family: var(--serif);
}
.bignum__from { font-size: clamp(40px, 7vw, 76px); color: var(--ink-faint); -webkit-text-stroke: 0.3px currentColor; }
.bignum__arrow { font-size: clamp(28px, 4vw, 44px); color: var(--ink-faint); }
.bignum__to {
  font-size: clamp(40px, 7vw, 76px); -webkit-text-stroke: 0.3px currentColor;
  background: linear-gradient(100deg, var(--accent), var(--accent-soft));
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
.bignum__label {
  font-family: var(--sans); font-size: 15px; color: var(--ink-soft);
  width: 100%; margin-top: 4px;
}

/* ---- Steps (Logins) ---- */
.case-steps { list-style: none; padding: 0; margin: 0; max-width: 68ch; }
.case-steps li {
  display: flex; gap: 20px; padding: 20px 0; border-top: 1px solid var(--line);
  font-size: 18px; line-height: 1.6; color: var(--ink-soft);
}
.case-steps li > span {
  font-family: var(--serif); font-size: 15px; color: var(--ink-faint);
  flex: none; width: 28px; padding-top: 2px;
}
.case-steps strong { color: var(--ink); font-weight: 600; }

/* ---- Stats (Impact) ---- */
.stats {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(20px, 3vw, 40px);
  margin: 0 0 40px;
}
.stat { display: flex; flex-direction: column; gap: 8px; }
.stat__num {
  font-family: var(--serif); font-weight: 400; -webkit-text-stroke: 0.3px currentColor;
  font-size: clamp(38px, 5vw, 60px); letter-spacing: -0.02em; line-height: 1;
  color: var(--ink);
}
.stat__label { font-size: 15px; line-height: 1.5; color: var(--ink-soft); max-width: 22ch; }
@media (max-width: 620px) { .stats { grid-template-columns: 1fr; gap: 26px; } }

/* clickable project (links to a case study) */
.project__link { display: block; }
.project__link .project__frame { transition: transform 0.35s var(--ease); }
.project__link:hover .project__frame { transform: translateY(-3px); }
.project__cta {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 14px; font-weight: 500; color: var(--bg); white-space: nowrap;
  background: var(--cta-bg); border: 1px solid var(--cta-bg);
  padding: 8px 17px; border-radius: 999px;
  transition: transform 0.25s var(--ease), opacity 0.25s;
}
.project__cta:hover { transform: translateY(-1px); opacity: 0.9; }

/* Other-work card reuses the homepage .project card inside a padded case section */
.case-section .project { padding: 0; }

/* <picture> wrappers (desktop/mobile art swaps) fill their container */
.problem-figure picture, .gantt picture { display: block; }

/* carousel position counter (hidden until the mobile carousel is active) */
.carousel-count { display: none; }

/* ---- Problem cards ---- */
.problem-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(16px, 2vw, 22px);
  margin-top: clamp(36px, 4vw, 52px);
}
.problem-card {
  border: 1px solid var(--line); border-radius: 16px;
  padding: clamp(24px, 2.6vw, 32px);
  background: var(--bg);
}
.problem-card__icon {
  display: grid; place-items: center;
  width: 46px; height: 46px; border-radius: 13px; margin-bottom: 20px;
  background: color-mix(in srgb, var(--accent) 12%, var(--bg));
  color: var(--accent);
}
.problem-card__icon svg { width: 23px; height: 23px; }
.problem-card h3 {
  font-family: var(--sans); font-size: 18px; font-weight: 600;
  color: var(--ink); margin: 0 0 10px; letter-spacing: -0.01em;
}
.problem-card p { font-size: 16px; line-height: 1.6; color: var(--ink-soft); margin: 0; }
@media (max-width: 860px) { .problem-grid { grid-template-columns: 1fr; } }

/* ---- Data callout (Problem) ---- */
.data-callout {
  display: flex; align-items: center; gap: clamp(22px, 4vw, 46px);
  margin-top: clamp(20px, 2.5vw, 28px);
  padding: clamp(26px, 3.5vw, 44px);
  border-radius: 18px;
  background: color-mix(in srgb, var(--accent) 7%, var(--bg));
  border: 1px solid color-mix(in srgb, var(--accent) 16%, var(--line));
}
.data-callout__num {
  flex: none;
  font-family: var(--serif); font-weight: 400; -webkit-text-stroke: 0.3px currentColor;
  font-size: clamp(46px, 7vw, 84px); line-height: 1; letter-spacing: -0.02em;
  color: var(--accent);
}
.data-callout__body strong {
  display: block; margin-bottom: 8px;
  font-size: clamp(18px, 2vw, 22px); font-weight: 600; color: var(--ink);
}
.data-callout__body p { margin: 0; font-size: 16px; line-height: 1.6; color: var(--ink-soft); max-width: 54ch; }
@media (max-width: 620px) { .data-callout { flex-direction: column; align-items: flex-start; gap: 12px; } }

/* ---- Overview sub-sections (80px apart) ---- */
.case-block + .case-block { margin-top: 80px; }
.case-block > :last-child { margin-bottom: 0; }
.case-subhead {
  font-family: var(--serif); font-weight: 400; -webkit-text-stroke: 0.3px currentColor;
  font-size: clamp(24px, 4vw, 30px); letter-spacing: -0.015em;
  color: var(--ink); margin: 0 0 18px;
}
.case-meta {
  display: grid; grid-template-columns: 1.5fr 1fr 1fr; gap: clamp(24px, 4vw, 56px);
}
.case-meta__item .case-subhead { margin-bottom: 14px; }
.case-meta__item .case-body { max-width: none; margin: 0; }
@media (max-width: 720px) { .case-meta { grid-template-columns: 1fr; gap: 44px; } }

/* ---- Impact: bordered stat cells ---- */
.stats.stats--bordered {
  grid-template-columns: repeat(3, 1fr); gap: 0; margin: 0;
  border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
}
.stats--bordered .stat {
  padding: clamp(24px, 3vw, 32px); gap: 12px;
  border-left: 1px solid var(--line);
}
.stats--bordered .stat:first-child { border-left: none; }
.stats--bordered .stat__num { font-size: clamp(30px, 4vw, 44px); }
.stats--bordered .stat__label { font-size: 16px; max-width: none; }
@media (max-width: 620px) {
  .stats.stats--bordered { grid-template-columns: 1fr; }
  .stats--bordered .stat { border-left: none; border-top: 1px solid var(--line); }
  .stats--bordered .stat:first-child { border-top: none; }
}

/* ---- Problem: numbered problems with image slots ---- */
.problem-item + .problem-item { margin-top: 80px; }
.problem-item .case-subhead { margin-bottom: 18px; }
.problem-item__num { margin-right: 2px; }

.problem-bullets { list-style: none; padding: 0; margin: 0; }
.problem-bullets li {
  position: relative; padding-left: 24px; margin-bottom: 10px;
  font-size: 18px; line-height: 1.6; color: var(--ink-soft); max-width: none;
}
.problem-bullets li:last-child { margin-bottom: 0; }
.problem-bullets li::before {
  content: ""; position: absolute; left: 5px; top: 11px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--ink-soft);
}

.problem-figure { margin: 28px 0 0; }
.problem-figure__img {
  width: 100%; height: auto; display: block; border-radius: 20px;
}
/* empty state — the placeholder itself is the gray drop box */
.problem-figure__placeholder {
  display: grid; place-items: center;
  width: 100%; min-height: 300px;
  background: var(--device-bg); border-radius: 20px;
  color: var(--ink-faint); font-size: 14px; letter-spacing: 0.03em;
}

/* ---- Solution: Launch Phases (Gantt) + Care Plan plate ---- */
.sol-group .case-subhead { margin-bottom: 18px; }

/* Phased-rollout timeline */
.gantt { margin-top: 28px; }
.gantt__img { display: block; width: 100%; height: auto; }

/* ---- Reusable phone plate: a grey box holding a portrait phone video/image.
   Reuse anywhere in Solution:
     <div class="sol-plate" data-reveal>
       <div class="sol-plate__phone">
         <video class="case-video" autoplay muted loop playsinline preload="metadata">
           <source src="screens/your-clip.mp4" type="video/mp4" />
         </video>
         <!-- or an image: <img src="screens/your-shot.png" alt="…"> -->
       </div>
     </div>
   Set --plate-h on .sol-plate to change the phone height (width follows automatically). ---- */
.sol-plate {
  --plate-h: 492px;                           /* phone height; width derives from the 726:1554 ratio */
  background: var(--device-bg);
  border-radius: 20px;
  padding: clamp(16px, 2.2vw, 28px) clamp(28px, 5vw, 60px);  /* reduced top/bottom; sides unchanged */
  margin-top: 28px;
  display: grid; place-items: center;
}
.sol-plate__phone {
  height: var(--plate-h);
  /* Slightly narrower than the clip's true cover width so object-fit: cover trims
     only the faint grey edge baked into the left/right of the recording —
     full height is preserved, so nothing is cut off top or bottom. */
  width: calc(var(--plate-h) * 710 / 1546 - 6px);
  overflow: hidden;
}
.sol-plate__phone video,
.sol-plate__phone img {
  width: 100%; height: 100%; object-fit: cover; object-position: center; display: block;
}

/* Landscape web-walkthrough video in a grey box (same lazy-load video system) */
.sol-plate--web { display: block; padding: clamp(16px, 2.5vw, 40px); }
.sol-plate__web {
  width: 100%; aspect-ratio: 2352 / 1546; border-radius: 10px; overflow: hidden;
  background: var(--device-bg);
}
.sol-plate__web video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- Other Screens: a grid of phone-portrait shots in one grey box ----
   Drop other-1.png … other-8.png into /screens and each cell fills in
   automatically (no code change). Empty cells show a faint placeholder. */
.sol-plate--grid { display: block; padding: clamp(18px, 2.6vw, 32px); }
.other-shots {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(12px, 1.6vw, 20px);
}
/* Real <img> rather than a background-image, so the eight shots (~1MB) can
   carry loading="lazy" and stay unfetched until the carousel is scrolled to.
   A CSS background is always fetched during layout, wherever it sits on the page. */
.other-shot {
  aspect-ratio: 220 / 492;           /* matches the video box footprint (same size) */
  width: 100%; height: auto; display: block;
  overflow: hidden;
  object-fit: cover; object-position: center;
  background: var(--device-bg);      /* holds the cell's tone until the file arrives */
}
@media (max-width: 900px) { .other-shots { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 460px) { .other-shots { grid-template-columns: 1fr; max-width: 260px; margin: 0 auto; } }

/* ---- Bulleted list (What We Did, etc.) ---- */
.case-bullets { list-style: none; padding: 0; margin: 20px 0 0; max-width: none; }
.case-bullets li {
  position: relative; padding-left: 24px; margin-bottom: 12px;
  font-size: 18px; line-height: 1.6; color: var(--ink-soft);
}
.case-bullets li:last-child { margin-bottom: 0; }
.case-bullets li::before {
  content: ""; position: absolute; left: 5px; top: 12px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--ink-soft);
}
.case-bullets strong { color: var(--ink); font-weight: 600; }

/* ---- Two discovery images in one grey box ---- */
.sol-plate--duo { display: block; padding: clamp(20px, 3vw, 40px); }
.duo-shots {
  display: flex; justify-content: center; align-items: center; flex-wrap: wrap;
  gap: clamp(20px, 3vw, 40px);   /* matches the box's padding */
}
.duo-shot {
  height: clamp(220px, 30vw, 300px); width: auto; max-width: 100%; display: block;
}
@media (max-width: 620px) { .duo-shot { height: auto; width: 100%; max-width: 300px; } }

/* ---- Testimonials (avatar + quote, revealed one-by-one) ---- */
.sol-plate--quotes { display: block; padding: clamp(28px, 4vw, 60px) clamp(24px, 4vw, 64px); margin-top: 80px; }
.quote { display: flex; align-items: center; gap: clamp(20px, 3vw, 44px); margin: 0; }
.quote + .quote { margin-top: clamp(36px, 5vw, 64px); }
.quote__avatar {
  flex: 0 0 auto; width: clamp(84px, 9vw, 120px); aspect-ratio: 1; border-radius: 50%;
  background-color: color-mix(in srgb, var(--ink) 8%, transparent);
  background-size: cover; background-position: center; background-repeat: no-repeat;
}
.quote__body { margin: 0; }
.quote__name {
  margin: 0 0 8px; font-family: var(--serif); font-weight: 400;
  -webkit-text-stroke: 0.3px currentColor; letter-spacing: -0.015em;
  font-size: clamp(19px, 2.2vw, 26px); color: var(--ink);
}
.quote__text { margin: 0; font-style: italic; font-size: 18px; line-height: 1.5; color: var(--ink-soft); max-width: 44ch; }
@media (max-width: 560px) { .quote { flex-direction: column; align-items: flex-start; gap: 14px; } }

/* ---- Learnings: numbered points ---- */
.learning__head { padding-left: clamp(14px, 1.6vw, 20px); }
.learning__num { margin-right: 10px; }
#learnings .case-block + .case-block { margin-top: 44px; }

/* =========================================================
   Second case study — extra components
   ========================================================= */

/* Wide media/figure in a grey box (desktop screenshots, diagrams) */
.case-plate {
  background: var(--device-bg); border-radius: 20px;
  padding: clamp(20px, 3vw, 44px); margin-top: 28px;
}
.case-plate__img {
  display: block; width: 100%; border-radius: 10px; aspect-ratio: 16 / 10;
  background-size: cover; background-position: center top; background-repeat: no-repeat;
  background-color: color-mix(in srgb, var(--ink) 6%, transparent);
}
.case-plate--dark { background: #1a1c22; }
.case-plate--dark .case-plate__img { background-color: rgba(255,255,255,0.06); }

/* Colored "notes" under a screenshot */
.note { border-radius: 16px; padding: clamp(18px, 2.6vw, 30px); margin-top: 20px; }
.note__title { margin: 0 0 10px; font-weight: 600; font-size: 15px; color: var(--ink); }
.note ul { list-style: none; margin: 0; padding: 0; }
.note li {
  position: relative; padding-left: 20px; margin-bottom: 8px;
  font-size: 16px; line-height: 1.55; color: var(--ink-soft);
}
.note li:last-child { margin-bottom: 0; }
.note li::before {
  content: ""; position: absolute; left: 5px; top: 10px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--ink-faint);
}
.note--neutral { background: color-mix(in srgb, var(--ink) 4%, transparent); }
.note--blue    { background: color-mix(in srgb, #2f80d8 12%, transparent); }
.note--green   { background: color-mix(in srgb, #2f9e63 14%, transparent); }

/* Full-width insight callout */
.callout {
  border-radius: 20px; padding: clamp(28px, 4vw, 48px); margin-top: 28px;
  background: linear-gradient(120deg, #2f6fd6, #5a7ff0); color: #fff;
}
.callout__label {
  margin: 0 0 12px; font-size: 13px; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; opacity: 0.85;
}
.callout p:last-child { margin: 0; font-size: clamp(18px, 2.4vw, 24px); line-height: 1.4; }

/* Problem: serif statement + 2×2 bordered card grid */
.prob-statement { max-width: none; }
.prob-grid {
  margin-top: 28px;
  display: grid; grid-template-columns: 1fr 1fr;
  border: 1px solid var(--line); border-radius: 20px; overflow: hidden;
}
.prob-cell {
  padding: clamp(26px, 3.5vw, 44px);
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.prob-cell:nth-child(2n) { border-right: none; }
.prob-cell:nth-last-child(-n+2) { border-bottom: none; }
.prob-cell__icon { display: block; color: var(--ink); margin-bottom: 20px; }
.prob-cell__icon svg { width: 56px; height: 56px; display: block; }
.prob-cell p { margin: 0; font-size: 18px; line-height: 1.55; color: var(--ink-soft); max-width: 32ch; }
@media (max-width: 620px) {
  .prob-grid { grid-template-columns: 1fr; }
  .prob-cell { border-right: none; }
  /* single column: divide every cell except the last (fixes the missing
     divider between the 3rd and 4th problem) */
  .prob-cell:nth-last-child(-n+2) { border-bottom: 1px solid var(--line); }
  .prob-cell:last-child { border-bottom: none; }
  .prob-cell__icon { margin-bottom: 20px; }
}

/* 3-up mini cards (operational inefficiencies) */
.mini-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(16px, 2vw, 24px); margin-top: 28px; }
.mini-card { background: var(--device-bg); border-radius: 16px; padding: clamp(20px, 2.5vw, 28px); }
.mini-card__icon {
  width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center;
  font-size: 20px; background: color-mix(in srgb, var(--ink) 6%, transparent); margin-bottom: 14px;
}
.mini-card h4 { margin: 0 0 6px; font-size: 16px; font-weight: 600; color: var(--ink); }
.mini-card p { margin: 0; font-size: 15px; line-height: 1.5; color: var(--ink-soft); }
@media (max-width: 720px) { .mini-cards { grid-template-columns: 1fr; } }

/* Personas (photo + role) */
.persona-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(16px, 2.5vw, 32px); margin-top: 28px; }
.persona { margin: 0; }
.persona__photo {
  width: 100%; aspect-ratio: 1; border-radius: 16px; margin-bottom: 14px;
  background-size: cover; background-position: center; background-repeat: no-repeat;
  background-color: color-mix(in srgb, var(--ink) 6%, transparent);
}
.persona h4 { margin: 0 0 4px; font-size: 17px; font-weight: 600; color: var(--ink); }
.persona__name {
  font-family: var(--serif); font-weight: 400; -webkit-text-stroke: 0;
  letter-spacing: -0.01em; font-size: clamp(18px, 2vw, 22px); color: var(--ink); margin: 0 0 6px;
}
.persona p { margin: 0; font-size: 16px; line-height: 1.5; color: var(--ink-soft); }
@media (max-width: 720px) { .persona-grid { grid-template-columns: 1fr; max-width: 340px; } }

/* "What the research surfaced" — two-column numbered box */
.surfaced {
  margin-top: 28px;
  display: grid; grid-template-columns: 1fr 1fr;
  border: 1px solid var(--line); border-radius: 20px; overflow: hidden;
}
.surfaced__col { padding: clamp(26px, 3.5vw, 44px); }
.surfaced__col:first-child { border-right: 1px solid var(--line); }
.surfaced__title {
  font-family: var(--serif); font-weight: 400; -webkit-text-stroke: 0.3px currentColor;
  letter-spacing: -0.01em; font-size: clamp(20px, 2.6vw, 26px); color: var(--ink); margin: 0 0 24px;
}
.numlist { list-style: none; margin: 0; padding: 0; }
.numlist li {
  display: flex; gap: 14px; align-items: flex-start; margin-bottom: 18px;
  font-size: 18px; line-height: 1.5; color: var(--ink-soft);
}
.numlist li:last-child { margin-bottom: 0; }
.numlist__n {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%; margin-top: 1px;
  background: color-mix(in srgb, var(--ink) 7%, transparent); color: var(--ink-soft);
  font-size: 13px; font-weight: 600; display: grid; place-items: center;
}
@media (max-width: 620px) {
  .surfaced { grid-template-columns: 1fr; }
  .surfaced__col:first-child { border-right: none; border-bottom: 1px solid var(--line); }
}

/* Two-column pain points / opportunities */
.twocol { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(20px, 3vw, 40px); margin-top: 28px; }
.twocol__col { border-radius: 18px; padding: clamp(22px, 3vw, 32px); background: color-mix(in srgb, #e0607a 9%, transparent); }
.twocol__col--alt { background: color-mix(in srgb, #2f9e63 10%, transparent); }
.twocol h4 { margin: 0 0 14px; font-size: 16px; font-weight: 600; color: var(--ink); }
.note-list { list-style: none; margin: 0; padding: 0; }
.note-list li {
  position: relative; padding-left: 20px; margin-bottom: 8px;
  font-size: 16px; line-height: 1.55; color: var(--ink-soft);
}
.note-list li:last-child { margin-bottom: 0; }
.note-list li::before {
  content: ""; position: absolute; left: 5px; top: 10px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--ink-faint);
}
@media (max-width: 720px) { .twocol { grid-template-columns: 1fr; } }

/* Full-width figure image (annotated screenshots) */
.case-img { display: block; width: 100%; height: auto; margin-top: 28px; }

/* Solution: connected-flow steps in one bordered box */
.flow { margin-top: 28px; border: 1px solid var(--line); border-radius: 20px; overflow: hidden; }
.flow-step {
  display: grid; grid-template-columns: auto auto 1fr; gap: clamp(18px, 3vw, 40px);
  align-items: start; padding: clamp(26px, 3.5vw, 44px);
}
.flow-step + .flow-step { border-top: 1px solid var(--line); }
.flow-step__marker { align-self: stretch; display: flex; flex-direction: column; align-items: center; gap: 12px; }
.flow-step__num {
  flex: 0 0 auto;
  width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;
  background: color-mix(in srgb, var(--ink) 6%, transparent); color: var(--ink-soft);
  font-size: 18px; font-weight: 500;
}
/* chevrons fill the remaining height of the row, spaced evenly */
.flow-step__chevrons {
  flex: 1 1 auto; display: flex; flex-direction: column; justify-content: space-between;
  padding: 6px 0; color: var(--ink-faint); opacity: 0.55;
}
.flow-step__chevrons svg { width: 16px; height: 16px; display: block; }
.flow-step__illus {
  width: clamp(116px, 14vw, 150px); aspect-ratio: 1; border-radius: 14px;
  background-color: color-mix(in srgb, var(--ink) 6%, transparent);
  background-size: cover; background-position: center; background-repeat: no-repeat;
}
.flow-step__title {
  font-family: var(--serif); font-weight: 400; -webkit-text-stroke: 0.3px currentColor;
  letter-spacing: -0.01em; line-height: 1.2;
  font-size: clamp(20px, 2.5vw, 28px); color: var(--ink); margin: 0 0 8px;
}
.flow-step__title .muted { color: inherit; }
.flow-step__sub { margin: 0 0 16px; font-size: 17px; line-height: 1.5; color: var(--ink-soft); }
.flow-checks { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 10px 32px; }
.flow-checks li {
  display: flex; align-items: center; gap: 10px;
  font-size: 17px; color: #15803d; font-weight: 450;
}
.flow-checks li::before {
  content: ""; flex: 0 0 auto; width: 18px; height: 18px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2315803d' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6L9 17l-5-5'/%3E%3C/svg%3E") center/contain no-repeat;
}
@media (max-width: 700px) {
  .flow-step { grid-template-columns: auto 1fr; }
  .flow-step__illus { display: none; }
}

/* 3-up colored "connected flows" cards */
.accent-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(16px, 2vw, 24px); margin-top: 28px; }
.accent-card { border-radius: 16px; padding: clamp(20px, 2.5vw, 28px); color: #fff; }
.accent-card h4 { margin: 0 0 8px; font-size: 17px; font-weight: 600; }
.accent-card p { margin: 0; font-size: 15px; line-height: 1.5; opacity: 0.92; }
.accent-card--1 { background: #2f9e63; }
.accent-card--2 { background: #2f80d8; }
.accent-card--3 { background: #7461e6; }
@media (max-width: 720px) { .accent-cards { grid-template-columns: 1fr; } }

/* ---------- Case-study mobile: consistent, tighter spacing + type ---------- */
@media (max-width: 640px) {
  /* hide the back button in the case sub-nav */
  .casenav__back { display: none; }

  /* every section: 60px top/bottom padding */
  .case-section { padding-top: 60px; padding-bottom: 60px; }

  /* Recoleta headings that inherit 1.5 line-height → 1.1 */
  .case-subhead, .persona__name, .surfaced__title, .stat__num { line-height: 1.1; }

  /* more breathing room between the personas ("people involved") */
  .persona-grid { gap: 40px; }

  /* "A new, connected flow": drop the arrows and match the small number badges
     used in Business/User problems (.numlist__n). */
  .flow-step__chevrons { display: none; }
  .flow-step__num {
    width: 26px; height: 26px; font-size: 13px; font-weight: 600;
    background: color-mix(in srgb, var(--ink) 7%, transparent);
  }

  /* Section rhythm: heading → 50px → sub-section → 50px → sub-section … */
  .case-section__title { margin-bottom: 50px; }
  .case-overview__headline { margin-bottom: 50px; }
  .case-block + .case-block,
  .problem-item + .problem-item,
  #learnings .case-block + .case-block { margin-top: 50px; }
  .sol-plate--quotes { margin-top: 50px; }

  /* consistent, tighter gap before media/content blocks (was 28px) */
  .gantt, .sol-plate, .case-img, .case-plate, .prob-grid, .surfaced,
  .flow, .mini-cards, .persona-grid, .accent-cards, .twocol,
  .problem-figure { margin-top: 20px; }

  /* 18px / 16px body + list type down to 15px */
  .case-body,
  .problem-bullets li, .case-bullets li, .numlist li, .prob-cell p,
  .note li, .note-list li,
  .stats--bordered .stat__label,
  .persona p, .quote__text { font-size: 15px; }
}

/* ---------- Mobile carousels: Other Screens & Discover (placed last to win the cascade) ---------- */
@media (max-width: 640px) {
  .carousel-count {
    display: block; text-align: center; margin-top: 14px;
    font-size: 13px; letter-spacing: 0.04em; color: var(--ink-faint);
    font-variant-numeric: tabular-nums;
  }
  /* The scroll row runs edge-to-edge inside the box (no side padding), so images
     clip at the box border, not at inner padding. The box clips to its rounded
     corners; the row keeps a 16px inset so the first/last items sit off the edge. */
  .sol-plate--grid, .sol-plate--duo { padding-left: 0; padding-right: 0; overflow: hidden; }

  /* Other Screens → horizontal scroll row */
  .other-shots {
    display: flex; grid-template-columns: none; width: 100%; max-width: none; min-width: 0;
    flex-wrap: nowrap; gap: 16px; padding: 0 16px;
    overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding: 0 16px;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
  }
  .other-shots::-webkit-scrollbar { display: none; }
  .other-shots .other-shot {
    flex: 0 0 auto; width: min(64%, 230px); scroll-snap-align: center;
  }
  /* Making the App Easier to Discover → horizontal scroll row */
  .duo-shots {
    width: 100%; min-width: 0;
    flex-wrap: nowrap; justify-content: flex-start; align-items: stretch;
    gap: 16px; padding: 0 16px;
    overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding: 0 16px;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
  }
  .duo-shots::-webkit-scrollbar { display: none; }
  .duo-shots .duo-shot {
    flex: 0 0 auto; height: clamp(300px, 82vw, 360px); width: auto; max-width: none;
    object-fit: cover; scroll-snap-align: center;
  }
}

/* ---------- Fullscreen media viewer (mobile) ---------- */
.media-fs { position: relative; display: block; }
.media-fs-btn { display: none; }
@media (max-width: 640px) {
  .media-fs { cursor: zoom-in; }
  .media-fs-btn {
    display: grid; place-items: center;
    position: absolute; top: 10px; right: 10px; z-index: 5;
    width: 34px; height: 34px; padding: 0; border: none; cursor: pointer;
    border-radius: 9px; background: rgba(20,20,22,0.55); color: #fff;
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  }
  .media-fs-btn svg { width: 18px; height: 18px; display: block; }
  .media-fs-btn:active { background: rgba(20,20,22,0.72); }
}
.lightbox {
  position: fixed; inset: 0; z-index: 200; padding: 62px 16px 22px;
  background: rgba(8,8,10,0.94); display: none; place-items: center;
}
.lightbox.is-open { display: grid; }
.lightbox__media { display: grid; place-items: center; max-width: 100%; max-height: 100%; }
.lightbox__media img, .lightbox__media video {
  max-width: 100%; max-height: calc(100vh - 96px); width: auto; height: auto;
  display: block; border-radius: 10px;
}
.lightbox__close {
  position: absolute; top: 14px; right: 14px; z-index: 1;
  width: 40px; height: 40px; border-radius: 999px; border: none; cursor: pointer;
  background: rgba(255,255,255,0.16); color: #fff;
  display: grid; place-items: center;
}
.lightbox__close svg { width: 18px; height: 18px; }

/* ============================================================
   Lite mode — auto-applied on low-end / constrained / reduced-motion
   devices (see liteMode in script.js). Removes the compositor-heavy
   effects that cause scroll jank on weak GPUs, without touching layout.
   ============================================================ */
html.lite .nav,
html.lite .case-header {
  background: var(--bg);
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
html.lite .chip {
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
/* Halt the never-ending sky work (twinkling stars, drifting blurred clouds). */
html.lite .star { animation: none; }
html.lite .sky__clouds { display: none; }
/* The cursor dot-field is desktop decoration; drop it entirely. */
html.lite .canvas-dots { display: none; }
/* One-shot entrance animations add nothing here and can stutter on load. */
html.lite .hero__content { animation: none; }

/* ============================================================
   Mobile: dock the navigation to the bottom of the screen.
   Appended LAST on purpose — the sticky-top rules for .nav and
   .case-header are defined later in this file and share the same
   specificity, so an earlier override would silently lose.
   ============================================================ */
@media (max-width: 640px) {
  .nav,
  .case-header {
    position: fixed;
    top: auto; bottom: 0; left: 0; right: 0;
    background: var(--bg);
    backdrop-filter: none; -webkit-backdrop-filter: none;
    border-bottom: none;
    border-top: 1px solid var(--line);
    /* white strip beneath the nav for the device's native home bar */
    padding-bottom: max(10px, env(safe-area-inset-bottom));
  }
  /* Order the docked bar: main nav, then the progress bar directly above the
     sub-nav it belongs to, with the sub-nav last so the most-tapped control is
     closest to the thumb. Keeping the bar off the very bottom edge also avoids
     the OS home-swipe zone, where a scrub gesture would fight the system. */
  .case-header { display: flex; flex-direction: column; }
  .case-header .nav__inner { order: 0; }
  .casenav__progress { order: 1; margin-bottom: 0; }
  .case-header .casenav { order: 2; }

  /* Reserve room so the footer can't hide behind the docked nav.
     --bottom-nav-h is measured in script.js; the fallback covers no-JS. */
  .frame { padding-bottom: var(--bottom-nav-h, 116px); }

  /* No top nav any more, so anchors mustn't reserve space for one. */
  .case-section,
  #impact { scroll-margin-top: 0; }
}

/* ---------- Main nav as an icon tab bar (mobile only) ---------- */
.nav__icon { display: none; }        /* desktop nav stays text-only */

@media (max-width: 640px) {
  /* display:contents flattens the two link groups so all four tabs become
     direct flex children and can share the width evenly. */
  .nav__inner { padding: 9px 4px 5px; align-items: stretch; }
  .nav__group { display: contents; }
  .nav__link {
    flex: 1 1 0;
    display: flex; flex-direction: column; align-items: center; gap: 4px;
    font-size: 11px; font-weight: 500; line-height: 1.2;
    color: var(--ink-soft); text-align: center;
  }
  .nav__link::after { display: none; }   /* sliding underline doesn't suit a tab bar */
  .nav__icon { display: block; width: 22px; height: 22px; }
}

/* ---------- Mobile nav: frosted material + final row order ---------- */
@media (max-width: 640px) {
  /* Restore the desktop nav's frosted look. The earlier opaque override existed
     because the sticky-TOP nav jittered on backdrop-filter repaint while
     scrolling; docked to the bottom it no longer moves, so blur is safe. */
  .nav,
  .case-header {
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    backdrop-filter: saturate(160%) blur(14px);
    -webkit-backdrop-filter: saturate(160%) blur(14px);
  }
  /* The home-bar strip is just the bar's own bottom padding, so the frosted
     background and blur carry all the way to the bottom edge — no separate
     opaque strip (which read as an unblurred white band). */

  /* Case studies: progress bar + sub-nav grouped at the top of the bar, with
     the main portfolio nav as the bottom-most row. */
  .casenav__progress { order: 0; }
  .case-header .casenav { order: 1; }
  .case-header .nav__inner {
    order: 2;
    border-bottom: none;
    border-top: 1px solid var(--line);
  }
  /* with the sub-nav collapsed this hairline would double up with the header's */
  .case-header.casenav-hidden .nav__inner { border-top-color: transparent; }
}

/* ---------- Mobile: auto-hide the main nav on scroll ---------- */
@media (max-width: 640px) {
  /* Both pages collapse the main-nav row itself, so the icons and labels squash
     away as it closes rather than the bar sliding out whole. The bar is anchored
     to bottom:0, so whatever remains — the progress bar and sub-nav on case
     studies — settles down into the space and keeps its home-bar clearance. */
  /* `.case-header .nav__inner` is listed explicitly to match the specificity of
     the base rule `.case-header .nav__inner { transition: border-color }` —
     a bare `.nav__inner` loses to it and the collapse ends up instant. */
  .nav__inner,
  .case-header .nav__inner {
    overflow: hidden;
    max-height: var(--mainnav-h, 120px);
    transition: max-height 0.32s var(--ease), opacity 0.22s var(--ease),
                padding 0.32s var(--ease), border-color 0.24s var(--ease);
  }
  .mainnav-hidden .nav__inner {
    max-height: 0; opacity: 0;
    padding-top: 0; padding-bottom: 0;
    border-top-width: 0;        /* width, not just colour — transparent still takes 1px */
  }
  /* Homepage only: that row IS the whole bar, so collapse the bar's own
     safe-area padding and border too, or an ~11px sliver would be left behind. */
  .nav { transition: padding 0.32s var(--ease); }
  .nav.mainnav-hidden { padding-bottom: 0; border-top-width: 0; }
}
/* Reduced motion: no sliding (the JS also leaves the nav permanently visible). */
@media (prefers-reduced-motion: reduce) {
  .nav, .case-header { transition: none; }
}

/* ---------- Mobile: progress bar touch target + tooltip side ---------- */
@media (max-width: 640px) {
  /* Bigger swipe target: 30px tall instead of 13px. Extended mostly upward into
     the content — touch-action:pan-y means a vertical swipe there still scrolls
     the page — and downward only into the sub-nav's 12px top padding, so it
     never sits over a section link. */
  .casenav__progress::before { top: -25px; bottom: -8px; }

  /* Tooltip above the bar: below it would land behind the sub-nav row. Lifted
     clear of the bar so the swiping thumb doesn't cover it. */
  .casenav__tip { top: auto; bottom: 24px; }
}

/* ---------- One-time hint: the progress bar is scrubbable ---------- */
.casenav__hint {
  position: absolute; left: 50%; top: 10px;
  transform: translateX(-50%) translateY(-5px);
  max-width: calc(100vw - 24px);
  padding: 6px 13px; border-radius: 999px;
  background: var(--ink); color: var(--bg);
  font-family: var(--sans); font-size: 12px; font-weight: 500;
  letter-spacing: 0.01em; white-space: nowrap;
  pointer-events: none; z-index: 3;
  opacity: 0;
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
/* Caret pointing at the bar — above the pill on desktop (bar is overhead) */
.casenav__hint::after {
  content: "";
  position: absolute; left: 50%; bottom: 100%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-bottom-color: var(--ink);
}
.casenav__hint.is-in { opacity: 1; transform: translateX(-50%) translateY(0); }
/* Mobile: the bar sits in the bottom-docked header, so the hint goes above it
   and the caret flips to point down at it. */
@media (max-width: 640px) {
  .casenav__hint {
    top: auto; bottom: 10px;
    transform: translateX(-50%) translateY(5px);
    font-size: 11.5px; padding: 6px 11px;
  }
  .casenav__hint::after {
    bottom: auto; top: 100%;
    border-bottom-color: transparent;
    border-top-color: var(--ink);
  }
  .casenav__hint.is-in { transform: translateX(-50%) translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .casenav__hint { transition: opacity 0.3s var(--ease); }
}

/* ---------- Section takeaway ----------
   A one-line orientation directly under each section title, so a visitor who
   lands mid-page — or jumps straight to Impact from the sub-nav — can read that
   section cold without reconstructing the project from the top. */
.case-takeaway {
  position: relative;
  overflow: hidden;                       /* clips the accent bar to the radius */
  background: var(--device-bg);           /* same grey as every media plate */
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 18px 24px;
  margin: 0 0 80px;     /* clear gap to the section content below */
  max-width: none;      /* fill the column, matching .case-body */
}
/* The single blue element: a crisp bar hugging the left edge. Drawn as a
   pseudo-element rather than a thick border-left, so it doesn't fight the
   1px hairline at the rounded corners. */
.case-takeaway::before {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--accent);
}
.case-takeaway__text {
  margin: 0;
  font-family: var(--sans);
  font-size: 17px; font-weight: 400; line-height: 1.55;
  color: var(--ink);                      /* darker than body — this is the line to read */
}
/* Pull the title closer when a takeaway follows it, so the two read as a pair
   and the takeaway carries the spacing to the content below. */
.case-section__title:has(+ .case-takeaway) { margin-bottom: 18px; }
@media (max-width: 640px) {
  .case-takeaway { padding: 15px 17px; border-radius: 14px; margin-bottom: 50px; }
  .case-takeaway__text { font-size: 15px; }
  .case-section__title:has(+ .case-takeaway) { margin-bottom: 14px; }
}

/* ---------- CLS: reserve each image's box before it loads (placed last to win the cascade) ----------
   These images are width:100% / height:auto and lazy-loaded, so without a known
   ratio they occupied zero height and every one shoved the page down as it
   arrived — Clarity measured CLS 1.94 on this page against 0.016 on the
   homepage. The width/height attributes in the HTML cover the default case.
   The <picture> ones need a breakpoint override too: their mobile sources are a
   different shape from the desktop files, and the img attributes describe only
   the desktop file. "auto <ratio>" means the placeholder uses this ratio and
   the loaded image still uses its own, so nothing is ever stretched. */
.problem-figure__img { aspect-ratio: auto 1840 / 534; }
.gantt__img          { aspect-ratio: auto 1840 / 634; }
@media (max-width: 640px) {
  .problem-figure__img { aspect-ratio: auto 650 / 534; }
  .gantt__img          { aspect-ratio: auto 650 / 564; }
}
