/* =====================================================================
   HOMEPAGE PATCH BUNDLE — concatenation of the 14 formerly-separate
   stylesheets below, in their EXACT original <link> order. No rule
   content, selector, or !important was changed — this is a pure
   file-count reduction (14 requests -> 1). Cascade behavior is
   byte-identical to before the merge.
   ===================================================================== */

/* ===== begin codex-final-pass-v2.css ===== */
:root {
  --codex-section-title: clamp(2.45rem, 4.1vw, 4.75rem);
  --codex-body: clamp(1rem, 1.2vw, 1.25rem);
  --volt: #4dce16;
  --volt-ink: #347e12;
  --premium-ease: var(--ease-out);
  --header-content-clearance: clamp(7rem, 12vh, 8.5rem);
}
h1,
h2,
h3 { text-wrap: balance; }
:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid #4dce16;
  outline-offset: 3px;
}

/* Sharp central portrait with progressively softened peripheral frames. */

.hero { height: 300svh !important; }

/* A [hidden] section must actually disappear. `.section` sets display, which
   outranks the UA sheet's [hidden] rule, so every hidden band needed its own
   opt-out — hence the scattered one-off rules below. One rule covers them. */
main > section[hidden],
main > footer[hidden] { display: none !important; }
#about[hidden] { display: none !important; }

.pill {
  min-height: 44px;
  padding: 11px 20px;
  gap: 7px;
  font-size: var(--fs-small);
}
.sw__ov-close,
.cs__close {
  backdrop-filter: blur(18px) saturate(1.35) !important;
  -webkit-backdrop-filter: blur(18px) saturate(1.35) !important;
  font-size: var(--fs-small);
  transition: background var(--dur-2) ease, color var(--dur-2) ease, border-color var(--dur-2) ease, box-shadow var(--dur-2) ease, transform var(--dur-2) ease !important;
}
.sw__ov-close:hover,
.sw__ov-close:focus-visible,
.cs__close:hover,
.cs__close:focus-visible {
  border-color: rgba(255, 255, 255, 0.9) !important;
  background: rgba(255, 255, 255, 0.92) !important;
  color: #111 !important;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.22) !important;
  transform: translateY(-1px);
}
.sw__overlay {
  will-change: clip-path;
  transform: translateZ(0);
  backface-visibility: hidden;
}
#swLeft,
#swRight { line-height: 1.35; }
#swLeft li,
#swRight li { margin-block: 0.22rem; }

#services .help__sub {
  font-size: clamp(0.98rem, 1.2vw, 1.15rem) !important;
  line-height: 1.5 !important;
  max-width: 62ch !important;
}

#testimonialQuote {
  max-width: 31ch !important;
  font-size: clamp(1.3rem, 1.75vw, 1.75rem) !important;
  line-height: 1.22 !important;
  letter-spacing: -0.025em !important;
}

#swThumb,
.cs__hero { background: transparent !important; }
#swThumb img,
.cs__hero img {
  width: 100% !important;
  height: 100% !important;
  object-fit: cover !important;
  object-position: center !important;
}

#nav {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  transition: background var(--dur-3) ease, backdrop-filter var(--dur-3) ease, box-shadow var(--dur-3) ease !important;
}
#nav::before {
  opacity: 0 !important;
  transition: opacity var(--dur-3) ease !important;
}
#nav.nav--post-hero {
  border: 1px solid rgba(255, 255, 255, 0.18) !important;
  background: linear-gradient(180deg, rgba(34, 37, 40, 0.66), rgba(15, 17, 20, 0.58)) !important;
  backdrop-filter: blur(20px) saturate(1.35) !important;
  -webkit-backdrop-filter: blur(20px) saturate(1.35) !important;
  box-shadow:
    0 10px 30px rgba(0, 0, 0, 0.16),
    0 2px 8px rgba(0, 0, 0, 0.1),
    inset 0 1px 0 rgba(255, 255, 255, 0.16) !important;
  color: #fff !important;
}
#nav.nav--post-hero::before { opacity: 0 !important; }

#stackFlow > .flow__ship[hidden] { display: none !important; }

/* CONTACT — premium profile card with calm enterprise glassmorphism. */
.contact-showcase {
  display: grid;
  min-height: min(820px, 88svh);
  grid-template-columns: minmax(0, 1.15fr) minmax(360px, .85fr);
  gap: clamp(52px, 8vw, 130px);
  align-items: center;
}
.contact-showcase__copy { align-self: center; }
.contact-showcase__kicker {
  margin-bottom: clamp(24px, 3vw, 42px);
  color: rgba(255,255,255,.46);
  letter-spacing: .16em;
}
#contact .contact__display {
  max-width: 11ch;
  margin-bottom: clamp(26px, 3vw, 42px);
  font-size: clamp(3.6rem, 6.4vw, 7.4rem) !important;
  font-weight: 500 !important;
  line-height: .9 !important;
  letter-spacing: -.065em !important;
}
#contact .contact__sub {
  max-width: 47ch;
  margin: 0;
  color: rgba(255,255,255,.64);
  font-size: clamp(1.1rem, 1.55vw, 1.45rem);
  line-height: 1.5;
}
.contact-profile {
  position: relative;
  width: min(100%, 520px);
  justify-self: end;
  overflow: hidden;
  border: 1px solid rgba(255,255,255,.16);
  border-radius: clamp(28px, 3vw, 42px);
  background:
    linear-gradient(145deg, rgba(255,255,255,.12), rgba(255,255,255,.035) 58%),
    rgba(16,20,24,.58);
  box-shadow: 0 38px 110px rgba(0,0,0,.34), inset 0 1px rgba(255,255,255,.18);
  backdrop-filter: blur(30px) saturate(1.25);
  -webkit-backdrop-filter: blur(30px) saturate(1.25);
  isolation: isolate;
  animation: contact-card-float 7s ease-in-out infinite;
}

.contact-profile dl { display:grid; gap:0; margin:0; }
.contact-profile dl div { display:grid; grid-template-columns: 90px 1fr; gap:16px; padding:12px 0; border-top:1px solid rgba(255,255,255,.1); }
.contact-profile dt { color:rgba(255,255,255,.36); font-size:var(--fs-caption); letter-spacing:.1em; text-transform:uppercase; }
.contact-profile dd { color:rgba(255,255,255,.74); font-size:var(--fs-tag); line-height:1.4; }

@keyframes contact-card-float { 50% { transform: translateY(-9px); } }
@keyframes contact-availability { 50% { opacity:.55; transform:scale(.82); } }

@media (max-width: 900px) {
  .contact-showcase { min-height:0; grid-template-columns:1fr; gap:64px; }
  .contact-profile { width:min(100%, 680px); justify-self:center; }
  
}
@media (max-width: 620px) {
  .contact-showcase { gap:46px; }
  #contact .contact__display { font-size:clamp(3rem, 14vw, 4.5rem) !important; }
  #contact .contact__sub { font-size:1rem; }
  .contact-profile { border-radius:26px; }

  .contact-profile dl div { grid-template-columns:74px 1fr; gap:12px; }
}

/* Editorial footer — reference-led grid, cropped wordmark, utility rail. */
.contact .editorial-footer {
  position: relative;
  display: block;
  min-height: max(860px, 100svh);
  margin: clamp(92px, 13vh, 170px) calc(var(--gutter) * -1) -24px;
  padding: clamp(44px, 5vw, 76px) var(--gutter) 0;
  overflow: hidden;
  border: 0;
  color: #15141c;
  background-color: #ededed;
  background-image: linear-gradient(90deg, rgba(21,20,28,.018) 1px, transparent 1px);
  background-size: calc(100% / 12) 100%;
}
.contact .editorial-footer::after {
  content: none;
}

.editorial-footer__lead { grid-column: 1 / span 6; grid-row: 1; }
.editorial-footer__lead h2 {
  max-width: 22ch;
  margin: 0;
  color: #15141c;
  font-family: var(--font-mono);
  font-size: clamp(2.35rem, 3vw, 3.7rem);
  font-weight: 500;
  line-height: .98;
  letter-spacing: -.055em;
  text-transform: uppercase;
}

.editorial-footer__signal {
  display: flex;
  gap: 10px;
  align-items: center;
  align-self: start;
  color: #24232a;
  font-family: var(--font-mono);
  font-size: clamp(11px, .86vw, 14px);
  font-weight: 550;
  letter-spacing: .02em;
  text-transform: uppercase;
}
.editorial-footer__signal i { width: 6px; height: 6px; flex: 0 0 6px; border-radius: 50%; background: #15141c; }

.editorial-footer__nav,
.editorial-footer__social,
.editorial-footer__availability {
  align-self: center;
  color: #1b1a21;
  font-family: var(--font-mono);
  text-transform: uppercase;
}
.editorial-footer__nav { grid-column: 1 / span 3; grid-row: 2; }
.editorial-footer__social { grid-column: 5 / span 3; grid-row: 2; }
.editorial-footer__availability { grid-column: 9 / span 4; grid-row: 2; }
.editorial-footer__nav,
.editorial-footer__social { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.editorial-footer__nav h3,
.editorial-footer__social h3 { margin-bottom:12px; font-size:var(--fs-label); font-weight:650; letter-spacing:.12em; text-transform:uppercase; }
.editorial-footer__nav ul,
.editorial-footer__social ul { display:grid; gap:2px; margin:0; padding:0; list-style:none; }
.editorial-footer__nav li,
.editorial-footer__social li { color:#1b1a21; font-size:clamp(12px, .92vw, 15px); line-height:1.35; letter-spacing:-.02em; }
.editorial-footer__social a,
.editorial-footer__availability { font-size: clamp(11px, .9vw, 14px); line-height: 1.45; }
.editorial-footer__availability { display: grid; gap: 6px; }
.editorial-footer__availability span { color: rgba(21,20,28,.5); }
.editorial-footer__availability a {
  display:inline-flex;
  width:fit-content;
  min-height:42px;
  align-items:center;
  margin-top:4px;
  padding:0 17px;
  border:1px solid rgba(21,20,28,.16);
  border-radius:999px;
  color:#15141c;
  background:rgba(255,255,255,.48);
  box-shadow:0 10px 28px rgba(21,20,28,.07), inset 0 1px rgba(255,255,255,.84);
  -webkit-backdrop-filter:blur(16px) saturate(140%);
  backdrop-filter:blur(16px) saturate(140%);
  transition: color var(--dur-2) ease, background-color var(--dur-2) ease, border-color var(--dur-2) ease, box-shadow var(--dur-2) ease, transform var(--dur-3) var(--ease-out);
}

.editorial-footer__nav a,
.editorial-footer__social a,
.editorial-footer__availability a { transition: opacity var(--dur-2) ease, transform var(--dur-2) var(--ease-out); }
.editorial-footer__nav a:hover,
.editorial-footer__social a:hover { opacity: .55; transform: translateX(4px); }
.editorial-footer__availability a:hover,
.editorial-footer__availability a:focus-visible {
  color:#fff;
  border-color:#15141c;
  background:#15141c;
  box-shadow:0 14px 30px rgba(21,20,28,.16);
  transform:translateY(-2px);
}

.editorial-footer__wordmark-wrap {
  position: absolute;
  z-index: 1;
  left: 4px;
  right: 4px;
  bottom: 32px;
  height: var(--footer-word-height, clamp(9rem, 27vw, 31rem));
  pointer-events: none;
  transform-origin: 50% 100%;
}

.editorial-footer__wordmark--sharp {
  -webkit-mask-image:linear-gradient(to bottom,#000 0%,#000 46%,rgba(0,0,0,.82) 64%,rgba(0,0,0,.28) 84%,transparent 100%);
  mask-image:linear-gradient(to bottom,#000 0%,#000 46%,rgba(0,0,0,.82) 64%,rgba(0,0,0,.28) 84%,transparent 100%);
}

@media (max-width: 900px) {
  .contact .editorial-footer { min-height: 960px; }
  
  .editorial-footer__lead { grid-column: 1 / -1; }
  .editorial-footer__lead h2 { max-width: 18ch; font-size: clamp(2.25rem, 5.8vw, 4rem); }

  .editorial-footer__nav { grid-column: 1 / span 2; grid-row: 3; }
  .editorial-footer__social { grid-column: 3 / span 2; grid-row: 3; }
  .editorial-footer__availability { grid-column: 5 / span 2; grid-row: 3; }
  .editorial-footer__wordmark-wrap { height:var(--footer-word-height, clamp(8rem, 27vw, 16rem)); }
  
}
@media (max-width: 620px) {
  .contact .editorial-footer {
    min-height: 1080px;
    padding-top: 42px;
    background-size: calc(100% / 6) 100%;
  }
  
  .editorial-footer__lead { grid-column: 1 / -1; }
  .editorial-footer__lead h2 { max-width:16ch; font-size: clamp(2rem, 9.5vw, 3.15rem); }

  .editorial-footer__nav { grid-column: 1; grid-row: 4; align-self: start; margin-top: 14px; }
  .editorial-footer__social { grid-column: 2; grid-row: 4; align-self: start; margin-top: 14px; }
  .editorial-footer__availability { display:grid; grid-column:1 / -1; grid-row:5; align-self:start; }
  .editorial-footer__nav li,
  .editorial-footer__social li { font-size:var(--fs-label); }
  .editorial-footer__wordmark-wrap { bottom:34px; height:var(--footer-word-height, 27vw); }

}

/* Experience Design — individual interface parts assemble into a wireframe. */

@keyframes cap-path-flow { to { stroke-dashoffset: -72; } }
@keyframes cap-hub-pulse { 50% { transform: translate(-50%,-50%) scale(1.16); box-shadow: 0 0 44px rgba(173,255,82,.38); } }
@keyframes cap-wire-one { 0%,12%,100%{opacity:.48;transform:translate(-64%,-64%) rotate(-7deg)} 42%,72%{opacity:.1;transform:translate(-50%,-50%) rotate(0)} }
@keyframes cap-wire-two { 0%,12%,100%{opacity:.45;transform:translate(-36%,-42%) rotate(7deg)} 46%,72%{opacity:.1;transform:translate(-50%,-50%) rotate(0)} }
@keyframes cap-wire-three { 0%,12%,100%{opacity:.52;transform:translate(-50%,-50%) rotate(1deg)} 50%,72%{opacity:.08;transform:translate(-50%,-50%) rotate(0)} }
@keyframes cap-product-resolve { 0%,28%,100%{opacity:0;transform:translate(-50%,-45%) scale(.92)} 52%,76%{opacity:1;transform:translate(-50%,-50%) scale(1)} }
@keyframes cap-cursor { 50% { opacity: 0; } }
@keyframes cap-prompt-progress { 0%,10%,100%{width:0;opacity:.3} 52%,72%{width:100%;opacity:1} }
@keyframes cap-prompt-product { 0%,24%,100%{opacity:.24;transform:translateY(12px) scale(.96);filter:saturate(.2)} 54%,78%{opacity:1;transform:translateY(0) scale(1);filter:saturate(1)} }
@keyframes cap-experience-flow { to { stroke-dashoffset:-72; } }
@keyframes cap-experience-step { 0%,18%,100%{border-color:rgba(255,255,255,.12);background:rgba(255,255,255,.045)} 32%,58%{border-color:rgba(103,183,255,.45);background:rgba(103,183,255,.12)} }
@keyframes cap-experience-resolve { 0%,18%,100%{opacity:.66;transform:translateX(9px) scale(.96)} 42%,78%{opacity:1;transform:translateX(0) scale(1)} }
@keyframes cap-experience-cursor { 0%,18%,100%{opacity:.35;transform:translateX(-18px) scale(.82)} 48%,76%{opacity:1;transform:translateX(18px) scale(1)} }
@keyframes cap-wireframe-frame { 0%,14%,100%{opacity:.5;transform:scale(.965)} 40%,84%{opacity:1;transform:scale(1)} }
@keyframes cap-ui-assemble { 0%,14%,100%{opacity:.4;transform:translate(var(--from-x),var(--from-y)) rotate(var(--from-r)) scale(.82)} 43%,84%{opacity:1;transform:translate(0,0) rotate(0) scale(1)} }
@keyframes cap-wireframe-status { 0%,44%,100%{opacity:0;transform:translateY(6px)} 57%,84%{opacity:1;transform:translateY(0)} }

/* Selected Work — focused enterprise case-study introduction. */
#work { height:var(--sw-story-height,316svh); }
#work .sw__inner {
  position:sticky;
  top:0;
  height:100svh;
  min-height:100svh;
  /* The inner pins at top:0 for the whole story, so this padding is the
     only thing holding the "Work" title clear of the fixed header (nav
     sits at top:14px and runs 64px tall, so it ends at 78px). The old
     48px cap left the title under the pill for the entire pin. */
  padding-top: clamp(92px, 11vh, 118px);
}
#work .sw__head { position:relative; }
#work .sw__intro { width:100%;gap:0; }
#work .sw__pills {
  position:absolute;
  top:0;
  right:0;
  grid-template-columns:repeat(4,minmax(0,1fr));
}
#work .sw__list { gap:8px; }
#work .sw-li { line-height:1.08; }
#work .sw__center { min-width:0;display:grid;align-self:center; }
#work .sw__center .sw__thumb { width:100%; }
#work .sw__overlay { --sw-meta-height:clamp(160px,20vh,200px); }
#work .sw__ov-cover { z-index:0; }
#work .sw__ov-grad { z-index:1; }
#work .sw__ov-copy { z-index:2;bottom:calc(var(--sw-meta-height) + 24px); }
#work .sw__ov-title { opacity:1;visibility:visible; }
#work .sw__ov-sub { width:auto;max-width:none; }
#work .sw__overlay[aria-hidden="true"] .sw__ov-copy { opacity:0;visibility:hidden; }
#work .sw__overlay[aria-hidden="false"] .sw__ov-copy { visibility:visible; }
#work .sw__ov-meta {
  z-index:4;
  min-height:var(--sw-meta-height);
  grid-template-columns:repeat(4,minmax(0,1fr));
}
#work .sw-meta-col { min-width:0; }
#work .sw-meta-col span { overflow-wrap:anywhere; }
#work .sw__ov-close { z-index:5; }
#work .sw__ov-close {
  gap:0;
}
#work .sw__ov-close .sw-close-x { font-size:var(--fs-meta); }
#work .sw__ov-cover {
  will-change:transform;
  transform:translate3d(-50%,-50%,0);
  backface-visibility:hidden;
  -webkit-backface-visibility:hidden;
}
@media (max-width:900px) {
  :root { --header-content-clearance:6.75rem; }
  #work { height:auto; }
  #work .sw__inner { height:auto;min-height:auto; }
      #work .sw__pills {
    position:static;
  }
    #work .sw__center { order:1;width:100%; }
  #work .sw__overlay { --sw-meta-height:clamp(260px,36vh,320px); }
  #work .sw__ov-meta { grid-template-columns:repeat(2,minmax(0,1fr)); }
  #work .sw__ov-copy { bottom:calc(var(--sw-meta-height) + 20px); }
}

/* Section titles begin with a consistent visual buffer. */
#process .design-process__intro { padding-top: var(--section-space, clamp(28px, 4vw, 52px)); }

/* Detail captions use the full available line before they wrap. */
.cs__tagline { max-width:none; }

/* Viewport-composed desktop sections: show the primary story before scrolling on. */
@media (min-width:901px) and (min-height:700px) {
  
  #services {
    min-height:100svh;
  }

  #services .help__sub {
    max-width:none !important;
    margin-top:12px;
    font-size:clamp(.92rem, 1.1vw, 1.05rem) !important;
  }

    }

/* Keep concise section summaries editorially horizontal on roomy screens. */
@media (min-width:1180px) {
  }

/* Responsive guardrails shared by every section and case-study view. */
html,
body {  max-width: 100%; overflow-x: clip; }
main,
section,
article,
.section,
.sw__inner,
.cs__body { min-width: 0; }
img,
video,
svg,
canvas { max-width: 100%; }
.nav__toggle {
  width: 44px;
  height: 44px;
  justify-content: center;
}

@media (prefers-reduced-motion: reduce) {
  #nav,
  #nav::before { transition-duration: var(--dur-1) !important; }
}

@media (max-width: 1100px) {
  .nav__toggle { display: inline-flex; }
  .nav__links { display: none; }
}
@media (max-width: 900px) {
  :root { --codex-section-title: clamp(2.1rem, 7vw, 3.8rem); }
    
}
@media (max-width: 620px) {

  .hero { height: 300svh !important; }

    }
@media (max-width: 420px) {
  :root { --codex-section-title: clamp(2rem, 12vw, 3.2rem); }

}

/* Short landscape devices need width-led typography and tighter vertical framing. */
@media (max-width: 1180px) and (max-height: 620px) and (orientation: landscape) {
  .nav { inset-block-start: 8px !important; }

  .sw__inner { padding-block: 64px 24px !important; }
  
}
/* ========================================================================== */
/* DESIGN PROCESS — editorial scroll story                                    */
/* ========================================================================== */

.design-process {
  --process-ink: #0f0f0f;
  --process-muted: rgba(16, 17, 15, 0.62); /* .48 was 3.3:1 on the white steps; .62 is 5.2:1 */
  --process-line: rgba(16, 17, 15, 0.14);
  position: relative;
  overflow: clip;
  background: #f4f4f4;
}

.design-process__intro {
  width: 100%;
  min-height: 0;
  flex: 0 0 auto;
  border-bottom: 0;
  padding: clamp(5.75rem, 7vw, 6.75rem) 0 clamp(1.25rem, 2vw, 2rem);
  display: grid;
  grid-template-areas: "title lede";
  align-content: center;
  column-gap: clamp(2rem, 8vw, 10rem);
  row-gap: clamp(0.8rem, 1.4vw, 1.4rem);
}

.design-process__intro h2 {
  grid-area: title;
  max-width: 12ch;
}

.design-process__lede {
  grid-area: lede;
  align-self: end;
}

.design-process__story {
  position: relative;
  height: 400svh;
}

.design-process__stage {
  top: 0;
  height: 100svh;
  display: flex;
  flex-direction: column;
  background: #f4f4f4;
}

.design-process__steps {
  min-height: 0;
  display: flex;
  list-style: none;
}

.design-process__step {
  position: relative;
  min-width: 0;
  overflow: hidden;
  container-type: inline-size;
  flex: 0.72 1 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  opacity: 0.24;
  will-change: flex-grow, opacity;
}

.design-process__step.is-active { opacity: 1; }

.design-process__number {
  display: block;
  max-width: 100%;
  font-size: clamp(7rem, 14vw, 15rem);
  font-size: clamp(6rem, 50cqi, 20rem);
  white-space: nowrap;
  transform-origin: left bottom;
  will-change: transform;
}

.design-process__visual {
  width: min(100%, 28rem);
  height: clamp(10.5rem, 29.4vh, 16.8rem);
  opacity: 0.78;
  transform: translateZ(0);
}

.design-process__visual svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.design-process__visual video {
  display:block;
  width:100%;
  height:100%;
  object-fit:contain;
  object-position:center;
}
.design-process__visual--discover video {
  transform:scale(1.32);
  transform-origin:center;
}

.design-process__visual--prototype video,
.design-process__visual--launch video {
  transform:scale(1.3);
  transform-origin:center;
}

/* Static fallback for browsers whose <video> element doesn't composite
   alpha (everything except iOS/WebKit's HEVC-alpha path) — see the HTML
   comment beside each <video>. js/main.js adds is-video-fallback on
   .design-process__visual when the platform isn't iOS; that hides the
   video (which would otherwise paint its transparent regions solid
   black) and shows this icon instead. Hidden by default so iOS, where
   the video renders correctly, never sees it. */

.design-process__visual :is(path, ellipse, circle, rect) {
  fill: none;
  stroke: rgba(16, 17, 15, 0.54);
  stroke-width: 1.15;
  vector-effect: non-scaling-stroke;
}

@keyframes processSignalFlow {
  from { stroke-dashoffset: 38; }
  to { stroke-dashoffset: 0; }
}

@keyframes processOrbitTrace {
  from { stroke-dashoffset: 34; }
  to { stroke-dashoffset: 0; }
}

@keyframes processBreathe {
  0%, 100% { transform: translateY(0) scale(1); }
  50% { transform: translateY(-4px) scale(1.025); }
}

@keyframes processAssemble {
  0%, 100% { transform: translateY(4px) scale(0.97); }
  45%, 70% { transform: translateY(0) scale(1); }
}

@keyframes processFrameDrift {
  0%, 100% { transform: translate(-4px, 3px); opacity: 0.5; }
  45%, 70% { transform: translate(0, 0); opacity: 1; }
}

@keyframes processRipple {
  0%, 100% { opacity: 0.35; transform: scale(0.94); }
  50% { opacity: 1; transform: scale(1.04); }
}

@keyframes processPointPulse {
  0%, 100% { transform: scale(0.75); opacity: 0.45; }
  50% { transform: scale(1.35); opacity: 1; }
}

@keyframes processAccentPulse {
  0%, 100% { opacity: 0.65; filter: drop-shadow(0 0 3px rgba(77, 206, 22, 0.22)); }
  50% { opacity: 1; filter: drop-shadow(0 0 9px rgba(77, 206, 22, 0.62)); }
}

.design-process__copy {
  padding-top: 2rem;
  will-change: transform, opacity;
}

.design-process__step.is-active .design-process__copy h3::before {
  content: none;
  display: none;
}

@media (max-width: 900px), (max-height: 619px) {
  .design-process__intro {
    min-height: auto;
    padding: clamp(6rem, 17vw, 8rem) 0 clamp(1.5rem, 5vw, 2.5rem);
    grid-template-areas: "kicker" "title" "lede";
  }

  .design-process__story { height: auto; }

  .design-process__stage {
    height: auto;
    background: #f4f4f4;
  }

  .design-process__step,
  .design-process__step.is-active {
    opacity: 1;
  }

  .design-process__visual {
    width: min(92vw, 30.8rem);
    height: clamp(14rem, 61.6vw, 19.6rem);
  }

      }

@media (prefers-reduced-motion: reduce) {
  .design-process__story { height: auto; }
  .design-process__stage { position: relative; height: auto; min-height: 0; }
  .design-process__step { opacity: 1; }
  .design-process__visual * { animation: none !important; }
}

/* ========================================================================== */
/* CAPABILITIES — unified brand palette, flat interaction                     */

/* Product Delivery — AI prompt resolves into a responsive product. */

@keyframes capDeliverySearch {
  0% { opacity: 0; transform: translateY(12px) scale(0.97); filter: blur(6px); }
  6%, 30% { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
  38%, 100% { opacity: 0; transform: translateY(-8px) scale(0.98); filter: blur(6px); }
}

@keyframes capDeliveryTyping {
  0%, 7% { width: 0; }
  29%, 100% { width: 38ch; }
}

@keyframes capDeliveryCursor {
  0%, 8%, 12%, 16%, 20%, 24%, 28%, 32% { opacity: 1; }
  10%, 14%, 18%, 22%, 26%, 30%, 34%, 100% { opacity: 0; }
}

@keyframes capDeliveryMorph {
  0%, 37% { width: 74%; height: 52%; border-radius: 14px; opacity: 0; transform: translate(-50%, -40%) scale(0.94); filter: blur(7px); }
  45%, 57% { width: 74%; height: 52%; border-radius: 14px; opacity: 1; transform: translate(-50%, -50%) scale(1); filter: blur(0); }
  65%, 72% { width: 52%; height: 61%; border-radius: 17px; opacity: 1; transform: translate(-50%, -50%) scale(1); filter: blur(0); }
  81%, 93% { width: 31%; height: 68%; border-radius: 20px; opacity: 1; transform: translate(-50%, -50%) scale(1); filter: blur(0); }
  100% { width: 31%; height: 68%; border-radius: 20px; opacity: 0; transform: translate(-50%, -54%) scale(0.96); filter: blur(6px); }
}

@keyframes capDeliveryBars {
  from { transform: scaleY(0.58); opacity: 0.52; }
  to { transform: scaleY(1); opacity: 1; }
}

@keyframes capDeliveryModeDesktop { 0%, 42%, 60%, 100% { opacity: 0; } 46%, 56% { opacity: 1; } }
@keyframes capDeliveryModeTablet { 0%, 61%, 75%, 100% { opacity: 0; } 65%, 72% { opacity: 1; } }
@keyframes capDeliveryModeMobile { 0%, 77%, 96%, 100% { opacity: 0; } 81%, 93% { opacity: 1; } }

/* ========================================================================== */
/* ADAPTIVE MOTION — lighter compositing when the governor selects 30fps       */
/* ========================================================================== */

html[data-motion-fps="30"] #nav.nav--post-hero {
  backdrop-filter: blur(14px) saturate(1.18) !important;
  -webkit-backdrop-filter: blur(14px) saturate(1.18) !important;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.14), inset 0 1px rgba(255, 255, 255, 0.12) !important;
}

html[data-motion-fps="30"] :is(.cap-card, .contact-profile, .stack-bento__card) {
  backdrop-filter: blur(12px) saturate(1.04);
  -webkit-backdrop-filter: blur(12px) saturate(1.04);
}

html[data-motion-fps="30"] :is(.sw__ov-close, .cs__close) {
  backdrop-filter: blur(10px) saturate(1.12) !important;
  -webkit-backdrop-filter: blur(10px) saturate(1.12) !important;
}

html[data-motion-fps="30"] :is(
  .hero-story__word,
  .nav-reveal__char,
  .design-process__copy,
  .cap-delivery__search,
  .cap-delivery__dashboard
) {
  filter: none !important;
}

html[data-motion-fps="30"] :is(.cap-card, .contact-profile) {
  box-shadow: 0 18px 48px rgba(18, 25, 20, 0.13), inset 0 1px rgba(255, 255, 255, 0.12);
}

html[data-motion-fps="30"] .contact-profile { animation-duration: 10s; }

/* ========================================================================== */
/* ANNOTATED CAPABILITIES — quiet light cards with restrained depth           */

/* Design Process annotation refinements. */
@media (min-width:901px) {
  }

/* Unified editorial section headers — follows the Design Process hierarchy. */
#work .sw__head,
#toolkit .toolkit__head,
#partnerships .editorial-head {
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(18rem,.46fr);
  column-gap:clamp(2rem,8vw,10rem);
  row-gap:clamp(.8rem,1.4vw,1.4rem);
  text-align:left;
}

#work .sw__title,
#services #capabilitiesTitle,
#toolkit #toolkitTitle,
#perspective #perspectiveTitle,
#partnerships #partnershipsTitle,
#contact #contactTitle {
  max-width:12ch;
  margin:0;
  text-align:left;
}

#work .sw__support,
#services .help__sub,
#client-value .client-value__lede,
#toolkit .toolkit__lede,
#perspective .pg-gallery__summary,
#partnerships .editorial-head > p {
  align-self:end;
  justify-self:end;
  text-align:left;
}

/* Playground keeps its filters on a dedicated row beneath the shared header. */
#perspective {
  grid-template-rows:auto auto auto;
  column-gap:clamp(2rem,8vw,10rem);
}
#perspective #perspectiveTitle {
  grid-column:1;
  grid-row:1;
}
#perspective .pg-gallery__summary {
  grid-column:2;
  grid-row:1;
}
#perspective .pg-gallery__tabs {
  grid-column:1 / -1;
  grid-row:2;
}
#perspective .pg-gallery__stage {
  grid-column:1 / -1;
  grid-row:3;
}

@media (max-width:900px) {
  #toolkit .toolkit__head,
#partnerships .editorial-head {
    gap:14px;
  }
  #work .sw__support,
#services .help__sub,
#client-value .client-value__lede,
#toolkit .toolkit__lede,
#partnerships .editorial-head > p {
    justify-self:start;
  }
  #perspective {
    grid-template-rows:auto auto auto auto;
    column-gap:0;
  }
  #perspective #perspectiveTitle {
    grid-column:1;
    grid-row:1;
  }
  #perspective .pg-gallery__summary {
    grid-column:1;
    grid-row:2;
    justify-self:start;
  }
  #perspective .pg-gallery__tabs {
    grid-column:1;
    grid-row:3;
  }
  #perspective .pg-gallery__stage {
    grid-column:1;
    grid-row:4;
  }
}

/* v147 — consistent section headers and one-view editorial layouts. */
@media (min-width:901px) {
  #work .sw__head,
#toolkit .toolkit__head,
#partnerships .editorial-head {
    grid-template-columns:minmax(0,1fr) minmax(24rem,.68fr);
  }

  #work .sw__support,
#services .help__sub,
#process .design-process__lede,
#client-value .client-value__lede,
#toolkit .toolkit__lede,
#perspective .pg-gallery__summary,
#partnerships .editorial-head > p {
    align-self:center !important;
    justify-self:end !important;
    display:-webkit-box;
    overflow:hidden;
    -webkit-box-orient:vertical;
    -webkit-line-clamp:2;
  }

  #work .sw__pills {
    grid-column:1 / -1 !important;
    justify-self:center !important;
    margin-top:clamp(20px,2.6vh,30px) !important;
  }

  #work .sw__inner {
    padding-bottom:18px;
  }

  #work .sw__main {
    min-height:0;
  }

  #work .sw__thumb {
    width:min(42vw,650px) !important;
    max-height:min(48vh,470px) !important;
  }

  #work .sw__pager {
    min-height:24px;
    padding-top:2px;
  }

  #perspective #perspectiveTitle,
  #perspective .pg-gallery__summary {
    align-self:center !important;
  }

  }

@media (min-width:1180px) {
  #toolkit #toolkitTitle,
  #partnerships #partnershipsTitle,
  #contact #contactTitle {
    white-space:nowrap !important;
  }
}

/* opacity deliberately NOT !important: an important declaration outranks
   both the inline hidden state and Motion's WAAPI entrance, which left
   the cards visible before the Approach heading had revealed. */
#process .design-process__step:not(.is-active) {
  opacity:1;
  background:#e4e4e4;
}

#process .design-process__step:not(.is-active) .design-process__number,
#process .design-process__step:not(.is-active) .design-process__visual,
#process .design-process__step:not(.is-active) .design-process__copy {
  opacity:1 !important;
  filter:none !important;
}

@media (max-width:900px) {
  #work .sw__support,
#services .help__sub,
#process .design-process__lede,
#client-value .client-value__lede,
#toolkit .toolkit__lede,
#perspective .pg-gallery__summary,
#partnerships .editorial-head > p {
    display:block;
    overflow:visible;
    -webkit-line-clamp:unset;
  }

  }

/* Experience Design — 12s editorial interface film: clarity, speed, adoption. */

@keyframes exp-guides { 0%,4%,100%{opacity:0} 10%,24%{opacity:.72} 32%,91%{opacity:0} }
@keyframes exp-focus { 0%,100%{opacity:0;transform:translate(-50%,-50%) scale(.5)} 7%,19%{opacity:1;transform:translate(-50%,-50%) scale(1)} 24%,87%{opacity:0;transform:translate(-50%,-50%) scale(1.6)} 94%{opacity:1;transform:translate(-50%,-50%) scale(.72)} }
@keyframes exp-window { 0%,5%,100%{opacity:.34;transform:translate(-50%,-50%) scale(.98)} 26%,86%{opacity:1;transform:translate(-50%,-50%) scale(1)} 94%{opacity:.45;transform:translate(-50%,-50%) scale(.985)} }
@keyframes exp-fragment { 0%,6%,100%{opacity:.22;transform:translate(var(--dx),var(--dy)) scale(.985)} 25%,88%{opacity:1;transform:translate(0,0) scale(1)} 95%{opacity:.28;transform:translate(calc(var(--dx) * .55),calc(var(--dy) * .55)) scale(.99)} }
@keyframes exp-signal { 0%,29%,61%,100%{opacity:0;transform:scaleX(.04)} 35%{opacity:.85;transform:scaleX(.12)} 53%{opacity:.62;transform:scaleX(1)} }
@keyframes exp-bars { 0%,35%,100%{transform:scaleY(.18);opacity:.38} 48%,88%{transform:scaleY(1);opacity:1} 96%{transform:scaleY(.3);opacity:.45} }
@keyframes exp-progress { 0%,36%,100%{transform:scaleX(.12)} 61%,88%{transform:scaleX(1)} 96%{transform:scaleX(.2)} }
@keyframes exp-cursor { 0%,45%,100%{opacity:0;transform:translate(38px,-18px)} 51%{opacity:1;transform:translate(14px,4px)} 61%,70%{opacity:1;transform:translate(76px,72px)} 76%{opacity:0;transform:translate(76px,72px)} }
@keyframes exp-click { 0%,59%,100%{opacity:0;transform:scale(.4)} 62%{opacity:.8;transform:scale(1.8)} 66%{opacity:0;transform:scale(2.5)} }
@keyframes exp-action { 0%,57%,100%{transform:scale(1);box-shadow:none} 62%{transform:scale(.98);box-shadow:0 0 0 5px rgba(77,206,22,.08)} 69%,86%{transform:scale(1);box-shadow:0 8px 18px rgba(77,206,22,.12)} }
@keyframes exp-success { 0%,61%,100%{opacity:0;transform:scale(.65)} 67%,86%{opacity:1;transform:scale(1)} 92%{opacity:0;transform:scale(.8)} }
@keyframes exp-person { 0%,67%,100%{opacity:0;transform:scale(.7)} 74%,87%{opacity:1;transform:scale(1);border-color:#4dce16;background:#ddf4d5} 94%{opacity:0;transform:scale(.75)} }

/* Experience Design v2 — one control unfolds into a responsive product. */

@keyframes xd-app {
  0%{opacity:0;width:18%;height:15%;border-radius:999px;transform:translate(-50%,-35%) scale(.96)}
  6%,16%{opacity:1;width:18%;height:15%;border-radius:999px;transform:translate(-50%,-50%) scale(1)}
  18%{border-color:rgba(77,206,22,.48);box-shadow:0 14px 30px rgba(54,67,56,.14)}
  20%{transform:translate(-50%,-50%) scale(.97)}
  22%{transform:translate(-50%,-50%) scale(1)}
  33%,77%{opacity:1;width:66%;height:62%;border-radius:15px;transform:translate(-50%,-50%) scale(1)}
  91%,100%{opacity:1;width:29%;height:82%;border-radius:18px;transform:translate(-50%,-50%) scale(1);box-shadow:0 18px 38px rgba(54,67,56,.13),0 0 0 1px rgba(77,206,22,.08)}
}
@keyframes xd-seed { 0%,5%{opacity:0;transform:translateY(5px)} 8%,18%{opacity:1;transform:none} 23%,100%{opacity:0;transform:scale(.8)} }
@keyframes xd-bar { 0%,24%{opacity:0;transform:translateY(-5px)} 32%,77%{opacity:1;transform:none} 90%,100%{opacity:1;height:14%;transform:none} }
@keyframes xd-nav { 0%,32%{opacity:0;transform:scaleY(.12)} 36%,72%{opacity:1;transform:scaleY(1)} 82%,100%{opacity:0;width:0;transform:translateX(-100%)} }

@keyframes xd-kpi {
  0%,43%{opacity:0;left:var(--desktop-left);top:8%;width:27%;height:29%;padding:4%;transform:translateY(8px) scale(.96)}
  49%,76%{opacity:1;left:var(--desktop-left);top:8%;width:27%;height:29%;padding:4%;transform:none}
  88%,100%{opacity:1;left:8%;top:var(--mobile-top);width:84%;height:14%;padding:5%;transform:none}
}
@keyframes xd-insights { 0%,56%{opacity:0;transform:translateY(9px)} 64%,76%{opacity:1;transform:none} 88%,100%{opacity:0;transform:translateY(8px) scale(.96)} }
@keyframes xd-chart {
  0%,56%{opacity:0;left:5%;top:46%;width:57%;height:45%;padding:4%;transform:translateY(9px)}
  64%,76%{opacity:1;left:5%;top:46%;width:57%;height:45%;padding:4%;transform:none}
  88%,100%{opacity:1;left:8%;top:74%;width:84%;height:18%;padding:3%;transform:none}
}
@keyframes xd-bars { 0%,58%{transform:scaleY(.08)} 68%,78%{transform:scaleY(1)} 90%,100%{transform:scaleY(.55)} }
@keyframes xd-responsive { 0%,68%{background:#fff;border-color:#d9ded8;transform:none} 72%,77%{background:#edf9e8;border-color:rgba(77,206,22,.4);transform:translateY(-1px)} 80%,100%{background:#edf9e8;border-color:#4dce16;transform:none} }
@keyframes xd-mobile-control { 0%,73%{border-color:#8e958e} 78%,100%{border-color:#4dce16;background:#ddf4d5} }
@keyframes xd-cursor { 0%,10%{opacity:0;transform:translate(30px,20px)} 13%{opacity:1;transform:translate(8px,-4px)} 17%,21%{opacity:1;transform:translate(-22vw,-19vh)} 25%{opacity:0;transform:translate(-22vw,-19vh)} 67%{opacity:0;transform:translate(-12vw,-12vh)} 71%{opacity:1;transform:translate(-21vw,-29vh)} 77%,91%{opacity:1;transform:translate(-17vw,-28vh)} 96%,100%{opacity:0;transform:translate(-15vw,-27vh)} }
@keyframes xd-cursor-click { 0%,18%,100%{opacity:0;transform:scale(.5)} 20%{opacity:.8;transform:scale(1.8)} 22%{opacity:0;transform:scale(2.6)} 73%{opacity:0;transform:scale(.5)} 76%{opacity:.8;transform:scale(1.8)} 78%{opacity:0;transform:scale(2.6)} }
@keyframes xd-pulse { 0%,18%,100%{opacity:0;transform:translate(-50%,-50%) scale(.5)} 20%{opacity:.65;transform:translate(-50%,-50%) scale(1)} 24%{opacity:0;transform:translate(-50%,-50%) scale(4)} }
@keyframes xd-glow { 0%,70%{opacity:.5;transform:translate(-50%,-50%) scale(.72)} 91%,100%{opacity:1;transform:translate(-50%,-50%) scale(1)} }

/* Capabilities v3 — editorial selector rail with a 75% motion stage. */

#services .capability-stage {
  --pointer-x:50%;--pointer-y:42%;
  position:relative;
  min-width:0;
  min-height:inherit;
  overflow:hidden;
  border:1px solid rgba(37,39,37,.16);
  border-radius:24px;
  background:
    radial-gradient(circle at var(--pointer-x) var(--pointer-y),rgba(77,206,22,.1),transparent 28%),
    linear-gradient(145deg,#fafafa,#f4f4f4 68%,#edf5e9);
  box-shadow:0 24px 64px rgba(35,44,37,.09),inset 0 1px #fff;
}
#services .capability-stage::after {
  content:"";
  position:absolute;
  inset:0;
  pointer-events:none;
  background:linear-gradient(115deg,transparent 18%,rgba(255,255,255,.55) 38%,transparent 55%);
  opacity:.28;
  transform:translateX(-100%);
  animation:cap-stage-sheen 9s ease-in-out infinite;
}
@keyframes cap-stage-sheen { 0%,62%,100%{transform:translateX(-100%)} 78%{transform:translateX(100%)} }

/* Experience Design — enterprise grid resolves into clarity, speed and adoption. */

@keyframes clarity-grid { 0%{opacity:0} 14%,100%{opacity:1} }
@keyframes clarity-dashboard { 0%,8%{opacity:0;transform:translate(-50%,-47%) scale(.97)} 20%,100%{opacity:1;transform:translate(-50%,-50%) scale(1)} 42%{border-color:#4dce16;box-shadow:0 26px 58px rgba(49,65,53,.1),0 0 0 3px rgba(77,206,22,.08)} 52%,100%{border-color:#d9ded8} }
@keyframes clarity-magnet { 0%,20%{opacity:0;transform:translate(var(--fx),var(--fy)) scale(.96)} 38%,100%{opacity:1;transform:none} 43%{border-color:#4dce16} 51%,100%{border-color:#d9ded8} }
@keyframes clarity-fragment { 0%,18%{opacity:0} 24%,33%{opacity:.72} 42%,100%{opacity:0;transform:translate(0,0) scale(.65)} }
@keyframes clarity-load { 0%,45%{transform:scaleX(.04)} 55%,100%{transform:scaleX(1)} }
@keyframes clarity-flow-show { 0%,48%{opacity:0} 55%,100%{opacity:1} }
@keyframes clarity-flow { 0%,48%{stroke-dashoffset:440} 60%,100%{stroke-dashoffset:0} }
@keyframes clarity-saved { 0%,55%{opacity:0;transform:scale(.65)} 61%,100%{opacity:1;transform:scale(1)} }
@keyframes clarity-bars { 0%,45%{transform:scaleY(.12)} 58%,100%{transform:scaleY(1)} }
@keyframes clarity-ripple { 0%{opacity:.45;transform:scale(.35)} 78%,100%{opacity:0;transform:scale(2.4)} }
@keyframes clarity-user { 0%,60%{opacity:0;transform:scale(.75)} 72%,100%{opacity:1;transform:scale(1);border-color:rgba(77,206,22,.52);box-shadow:0 0 0 5px rgba(77,206,22,.06)} }

/* Existing Strategy and Delivery motion, now driven by the active stage panel. */

@media (max-width:900px) {

  #services .capability-stage { min-height:clamp(390px,72vw,520px); }
}
@media (prefers-reduced-motion:reduce) {
  #services .capability-stage::after { animation:none !important; }

}

/* Pinned capability narrative: scroll advances the active left-rail item. */
@media (min-width:901px) and (min-height:700px) {
  #services {
    height:300svh;
    min-height:300svh;
    overflow:visible;
    padding-top:0 !important;
    padding-bottom:0 !important;
  }

}

/* Final capability spacing and boundary refinements. */
#services { border-bottom:0 !important; }

/* Simplified touch-first capability selector for phones and small tablets. */
@media (max-width:900px) {
  #services {
    height:auto;
    min-height:0;
    overflow:hidden;
  }

  #services .capability-stage {
    min-height:clamp(360px,92vw,470px);
    border-radius:18px;
  }

}

@media (max-width:430px) {
  
  #services .capability-stage { min-height:clamp(330px,90vw,390px); }
}

/* Soft brand-green stage surface to lift the capability illustrations. */
#services .capability-stage {
  background:
    radial-gradient(circle at var(--pointer-x) var(--pointer-y),rgba(77,206,22,.23),transparent 32%),
    radial-gradient(ellipse at 58% 52%,rgba(212,239,201,.92),transparent 58%),
    linear-gradient(145deg,#f0f9eb,#e6f5df 60%,#deefd8);
  border-color:rgba(61,108,43,.2);
  box-shadow:none;
}

/* Rule-free capability composition: spacing carries the hierarchy. */
#services .capability-stage { border:0; }

/* Unified homepage canvas: soft editorial paper between every content section. */
body,
main {
  background:#f4f4f4;
}
#work,
#services,
#process,
#experience,
#stack,
#testimonials,
#insights,
#contact,
#process .design-process__stage {
  background:#f4f4f4 !important;
}

/* Product Strategy film: signals resolve into one confident direction. */

@keyframes strategy-cloud {
  0%{opacity:.76;transform:rotate(-4deg) scale(1)}
  25%{opacity:.9;transform:rotate(5deg) scale(1.025)}
  43%{opacity:.2;transform:translate(125px) rotate(0) scale(.58)}
  50%,100%{opacity:0;transform:translate(160px) scale(.25)}
}
@keyframes strategy-signal {
  from{opacity:.32;transform:translateY(-2px);box-shadow:0 3px 10px rgba(67,79,69,.03)}
  to{opacity:.86;transform:translateY(2px);box-shadow:0 7px 18px rgba(67,79,69,.09)}
}
@keyframes strategy-funnel {
  0%,18%{stroke-dashoffset:700;opacity:0}
  35%,100%{stroke-dashoffset:0;opacity:1}
}
@keyframes strategy-need {
  0%,20%{opacity:0;transform:translate(-37cqw,var(--need-y)) scale(.7)}
  31%{opacity:1;transform:translate(-37cqw,var(--need-y)) scale(1)}
  53%{opacity:1;transform:translate(0,0) scale(.8)}
  62%,100%{opacity:0;transform:translate(0,0) scale(.2)}
}
@keyframes strategy-key {
  0%,54%{opacity:0;transform:translate(-50%,-50%) scale(.68)}
  64%{opacity:1;transform:translate(-50%,-50%) scale(1.08)}
  70%,100%{opacity:1;transform:translate(-50%,-50%) scale(1)}
}
@keyframes strategy-core-settle {
  0%,60%{transform:scale(.88);box-shadow:0 0 0 6px rgba(77,206,22,.06),0 0 18px rgba(77,206,22,.2)}
  68%{transform:scale(1.08);box-shadow:0 0 0 14px rgba(77,206,22,.13),0 0 34px rgba(77,206,22,.42)}
  76%,100%{transform:scale(1);box-shadow:0 0 0 10px rgba(77,206,22,.1),0 0 28px rgba(77,206,22,.32)}
}
@keyframes strategy-direction-show {0%,62%{opacity:0}68%,100%{opacity:1}}
@keyframes strategy-line {0%,63%{transform:scaleX(0)}75%,100%{transform:scaleX(1)}}
@keyframes strategy-particle {
  0%,71%{left:0;opacity:0;transform:translateY(-50%) scale(.5)}
  76%{opacity:1}
  89%{left:calc(100% - 6px);opacity:1;transform:translateY(-50%) scale(1)}
  94%,100%{left:calc(100% - 6px);opacity:0;transform:translateY(-50%) scale(1)}
}

#services .capability-stage:has([data-capability-panel="0"].is-active)::after {
  animation:none;
  opacity:.12;
  transform:none;
}

/* Capability stage storytelling: labeled, self-contained, and single-play. */
#services .capability-stage::after { animation:none;opacity:.1;transform:none; }

@keyframes capability-meta-in { to{opacity:1;transform:none} }

@keyframes strategy-label-signals { 0%{opacity:0;transform:translateY(5px)} 9%,28%{opacity:1;transform:none} 45%,100%{opacity:0} }
@keyframes strategy-label-needs { 0%,18%{opacity:0;transform:translateY(5px)} 28%,52%{opacity:1;transform:none} 65%,100%{opacity:0} }
@keyframes strategy-label-result { 0%,55%{opacity:0;transform:translateY(5px)} 70%,100%{opacity:1;transform:none} }

@keyframes film-label-settle { 0%,30%{opacity:0;transform:translateY(6px)} 50%,100%{opacity:1;transform:none} }

/* Delivery resolves from prompt to responsive product once in 2.4 seconds. */

@keyframes capDeliveryMorphOnce {
  0%,37%{width:74%;height:52%;border-radius:14px;opacity:0;transform:translate(-50%,-40%) scale(.94);filter:blur(7px)}
  45%,57%{width:74%;height:52%;border-radius:14px;opacity:1;transform:translate(-50%,-50%) scale(1);filter:none}
  65%,72%{width:52%;height:61%;border-radius:17px;opacity:1;transform:translate(-50%,-50%) scale(1);filter:none}
  81%,100%{width:31%;height:68%;border-radius:20px;opacity:1;transform:translate(-50%,-50%) scale(1);filter:none}
}
@keyframes capDeliveryBarsOnce { from{transform:scaleY(.16);opacity:.35} to{transform:scaleY(1);opacity:1} }
@keyframes capDeliveryModeMobileOnce { 0%,77%{opacity:0} 83%,100%{opacity:1} }

/* Product Strategy v2 — Signal → Direction, overlapping 1.8s loop. */

@keyframes strategy-trail-breathe { 0%,100%{opacity:.2} 25%,58%{opacity:.48} 82%{opacity:.26} }
@keyframes strategy-node-ingest {
  0%,100%{opacity:.3;transform:scale(1)}
  12%,30%{opacity:1;transform:scale(.92)}
  48%{opacity:.82;transform:scale(.4)}
  58%,78%{opacity:0;transform:scale(.3)}
  82%{opacity:.18;transform:scale(.82)}
}
@keyframes strategy-source-parallax {
  0%,100%{opacity:.3;transform:translateY(8px)}
  10%,30%{opacity:1;transform:translateY(0)}
  48%,78%{opacity:0;transform:translate(22px,-5px) scale(.96)}
  82%{opacity:.18;transform:translateY(8px)}
}
@keyframes strategy-neck-glow { 0%,28%,100%{opacity:.24} 48%,62%{opacity:1;box-shadow:0 0 18px rgba(77,206,22,.48)} 80%{opacity:.45} }
@keyframes strategy-need-compress {
  0%,20%{opacity:0;transform:translate(-4cqw,var(--need-y)) scale(1)}
  24%,38%{opacity:1;transform:translate(-2cqw,var(--need-y)) scale(1)}
  49%{opacity:1;transform:translate(18cqw,0) scale(.45,.85)}
  56%,100%{opacity:0;transform:translate(18cqw,0) scale(.18)}
}
@keyframes strategy-impact-flash { 0%,44%,48%,51%,55%,100%{opacity:0;box-shadow:none} 46%,50%,53%{opacity:1;box-shadow:0 0 0 16px rgba(255,255,255,.38)} }
@keyframes strategy-problem-lock {
  0%,47%{opacity:0;transform:translateY(-12px) scale(.97);border-color:rgba(125,134,128,.3)}
  52%{opacity:1;transform:translateY(4px) scale(1.015);border-color:#39ff6e}
  58%,91%{opacity:1;transform:none;border-color:rgba(77,206,22,.42)}
  96%,100%{opacity:0;transform:translateY(2px) scale(.985)}
}
@keyframes strategy-sphere-ignite {
  0%,45%{background:#aab2ac;transform:scale(.88);box-shadow:0 0 0 0 rgba(77,206,22,0)}
  50%{background:#4dce16;transform:scale(1.08);box-shadow:0 0 0 24px rgba(77,206,22,.16),0 0 34px rgba(77,206,22,.45)}
  58%,88%{background:#4dce16;transform:scale(1);box-shadow:0 0 0 9px rgba(77,206,22,.09),0 0 22px rgba(77,206,22,.3)}
  92%{background:#4dce16;transform:scale(1.06);box-shadow:0 0 0 12px rgba(77,206,22,.1),0 0 26px rgba(77,206,22,.34)}
  100%{background:#aab2ac;transform:scale(.88);box-shadow:none}
}
@keyframes strategy-arrow-fire { 0%,68%{transform:scaleX(0);opacity:0} 75%,92%{transform:scaleX(1);opacity:1} 98%,100%{transform:scaleX(0);opacity:0} }
@keyframes strategy-fire-particle {
  0%,70%{left:0;opacity:0;transform:translateY(-50%) scale(.6)}
  76%{opacity:1}
  91%{left:44%;opacity:1;transform:translateY(-50%) scale(1)}
  97%,100%{left:44%;opacity:0;transform:translateY(-50%) scale(.7)}
}
@keyframes strategy-direction-arrive {
  0%,74%{opacity:0;transform:translateY(-50%) scale(.92)}
  83%,93%{opacity:1;transform:translateY(-50%) scale(1)}
  98%,100%{opacity:0;transform:translateY(-50%) scale(.94)}
}

/* Client Value — scientific editorial outcomes grid. */
#client-value {
  position:relative;
  overflow:hidden;
  color:#1a1a1a;
  background:#fff;
}

#client-value h2 {
  margin:0;
  color:#141414;
  font-size:clamp(2.25rem,3.5vw,3rem);
  font-weight:500;
  line-height:.98;
  letter-spacing:-.055em;
}
#client-value .client-value__lede {
  max-width:720px;
  margin:0 0 3px auto;
  color:#343a35;
  font-size:clamp(.98rem,1.25vw,1.16rem);
  font-weight:430;
  line-height:1.48;
  letter-spacing:-.018em;
}

#client-value .value-card {
  position:relative;
  display:grid;
  min-width:0;
  min-height:clamp(350px,45vh,430px);
  grid-template-rows:minmax(210px,1fr) auto;
  overflow:hidden;
  border:1px solid #d8ddd9;
  border-radius:20px;
  background:#fff;
  box-shadow:0 16px 36px rgba(30,39,32,.045);
  transition: transform var(--dur-3) var(--ease-out),border-color var(--dur-3) ease,box-shadow var(--dur-3) ease;
}
#client-value .value-card:hover {
  border-color:rgba(77,206,22,.42);
  box-shadow:0 22px 46px rgba(30,50,34,.075);
  transform:translateY(-5px);
}

/* Flowing end-to-end network */

@keyframes value-sheen { 0%,12%{transform:translateX(-100%)} 58%,100%{transform:translateX(100%)} }
@keyframes value-orbit-spin { to{transform:rotate(360deg)} }
@keyframes value-orbit-counter { to{transform:rotate(-360deg)} }
@keyframes value-core-pulse { 0%,100%{transform:scale(.88);filter:drop-shadow(0 0 0 rgba(77,206,22,0))} 50%{transform:scale(1.13);filter:drop-shadow(0 0 8px rgba(77,206,22,.45))} }
@keyframes value-layer-float { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-8px)} }
@keyframes value-particle-wave { 0%,100%{opacity:.28;transform:scale(.7)} 50%{fill:#4dce16;opacity:1;transform:scale(1.35)} }
@keyframes value-path-flow { to{stroke-dashoffset:-120} }
@keyframes value-network-flow { 0%,100%{stroke-dashoffset:340;opacity:.28} 52%{stroke-dashoffset:0;opacity:.8} }
@keyframes value-node-pulse { 0%,100%{transform:scale(.82)} 50%{transform:scale(1.14);fill:#eaf7e5;stroke:#4dce16} }

@media (max-width:1100px) {
  #client-value { min-height:0; }
  
  #client-value .client-value__lede { max-width:760px;margin-left:0; }
  
  #client-value .value-card { min-height:380px; }
}
@media (max-width:640px) {
  #client-value h2 { font-size:clamp(2.3rem,12vw,3.6rem); }
  
  #client-value .value-card { min-height:350px;grid-template-rows:230px auto; }
  
}
@media (prefers-reduced-motion:reduce) {
  #client-value .value-card,
  #client-value .value-card * { animation:none !important;transition:none !important; }
}

/* Toolkit — bright asymmetrical frosted bento. */
#toolkit {
  position:relative;
  overflow:hidden;
  color:#1a1a1a;
  background:
    radial-gradient(circle at 14% 28%,rgba(229,222,246,.55),transparent 28%),
    radial-gradient(circle at 82% 62%,rgba(221,244,213,.46),transparent 30%),
    radial-gradient(circle at 55% 15%,rgba(237,230,248,.4),transparent 25%),
    #fff;
}

#toolkit .toolkit-tile {
  position:relative;
  display:flex;
  min-width:0;
  align-items:center;
  justify-content:center;
  overflow:hidden;
  border:1px solid rgba(150,147,164,.25);
  border-radius:22px;
  background:rgba(255,255,255,.72);
  box-shadow:
    0 22px 48px rgba(112,103,139,.105),
    inset 0 1px rgba(255,255,255,.98);
  backdrop-filter:blur(18px) saturate(1.08);
  transform-style:preserve-3d;
  transition: transform var(--dur-4) var(--ease-out),border-color var(--dur-3) ease,box-shadow var(--dur-4) ease;
}
#toolkit .toolkit-tile::before {
  content:"";
  position:absolute;
  width:72%;
  aspect-ratio:1;
  border-radius:50%;
  background:radial-gradient(circle,rgba(222,213,245,.58),rgba(232,226,247,.12) 46%,transparent 70%);
  filter:blur(4px);
  transform:translate3d(-18%,-12%,0);
  transition: transform var(--dur-4) var(--ease-out),opacity var(--dur-3) ease;
}
#toolkit .toolkit-tile::after {
  content:"";
  position:absolute;
  inset:0;
  border-radius:inherit;
  pointer-events:none;
  background:linear-gradient(128deg,rgba(255,255,255,.92),transparent 28%,transparent 70%,rgba(210,245,199,.46));
  opacity:.5;
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  padding:1px;
  mask-composite:exclude;
}
#toolkit .toolkit-tile:hover {
  border-color:rgba(167,156,199,.42);
  box-shadow:0 28px 58px rgba(111,101,140,.15),0 0 0 1px rgba(255,255,255,.7) inset;
  transform:perspective(900px) translateY(-6px) rotateX(var(--tile-rx)) rotateY(var(--tile-ry));
}
#toolkit .toolkit-tile:hover::before { opacity:.86;transform:translate3d(8%,-2%,18px) scale(1.08); }
#toolkit .toolkit-tile h3 {
  position:absolute;
  z-index:3;
  left:20px;
  bottom:17px;
  margin:0;
  color:#1a1a1a;
  font-size:clamp(1rem,1.25vw,1.22rem);
  font-weight:500;
  line-height:1;
  letter-spacing:-.035em;
  transform:translateZ(18px);
}

@media (max-width:1050px) {
        
}
@media (max-width:560px) {
  
  #toolkit .toolkit-tile { border-radius:18px; }
  #toolkit .toolkit-tile h3 { left:18px;bottom:16px; }
}
@media (prefers-reduced-motion:reduce) {
  #toolkit .toolkit-tile,
  #toolkit .toolkit-tile * { transition:none !important; }
  #toolkit .toolkit-tile:hover { transform:none; }
}

/* Toolkit v2 — softly molded honeycomb inspired by the supplied reference. */

#toolkit .toolkit-tile {
  --base-y:0px;
  z-index:1;
  width:100%;
  max-width:238px;
  height:clamp(176px,20vh,198px);
  justify-self:center;
  border:0;
  border-radius:0;
  background:transparent;
  box-shadow:none;
  filter:drop-shadow(0 17px 18px rgba(111,103,137,.11));
  clip-path:path("M54 2 Q46 2 41 12 L3 84 Q-1 92 3 101 L41 173 Q46 183 57 183 H181 Q192 183 197 173 L235 101 Q239 92 235 84 L197 12 Q192 2 181 2 Z");
  transform:translateY(var(--base-y));
  transition: transform var(--dur-4) var(--ease-out),filter var(--dur-4) ease;
}
#toolkit .toolkit-tile:nth-child(n+4) { --base-y:-22px; }
#toolkit .toolkit-tile::before {
  z-index:0;
  inset:0;
  width:auto;
  aspect-ratio:auto;
  border-radius:0;
  background:linear-gradient(138deg,rgba(255,255,255,.98),rgba(213,208,228,.48) 48%,rgba(255,255,255,.92));
  filter:none;
  clip-path:inherit;
  opacity:1;
  transform:none;
}
#toolkit .toolkit-tile::after {
  z-index:1;
  inset:2px;
  border-radius:0;
  padding:0;
  background:
    radial-gradient(circle at 38% 28%,rgba(255,255,255,.96),transparent 34%),
    radial-gradient(circle at 68% 72%,rgba(229,222,246,.42),transparent 48%),
    rgba(255,255,255,.79);
  clip-path:inherit;
  opacity:1;
  mask:none;
}
#toolkit .toolkit-tile:hover {
  border:0;
  filter:drop-shadow(0 24px 25px rgba(105,96,136,.18));
  transform:perspective(900px) translateY(calc(var(--base-y) - 8px)) rotateX(var(--tile-rx)) rotateY(var(--tile-ry));
}
#toolkit .toolkit-tile:hover::before { opacity:1;transform:none; }
#toolkit .toolkit-tile:hover::after {
  background:
    radial-gradient(circle at 38% 28%,rgba(255,255,255,1),transparent 34%),
    radial-gradient(circle at 68% 72%,rgba(216,245,207,.48),transparent 49%),
    rgba(255,255,255,.86);
}

#toolkit .toolkit-tile h3 {
  left:50%;
  bottom:23px;
  width:max-content;
  max-width:78%;
  color:#242725;
  font-size:.9rem;
  letter-spacing:-.025em;
  transform:translate3d(-50%,0,18px);
}

@media (max-width:1050px) {
  
  #toolkit .toolkit-tile:nth-child(n+4) { --base-y:0px; }
  #toolkit .toolkit-tile:nth-child(n+6) { --base-y:-18px; }
  #toolkit .toolkit-tile {
    clip-path:polygon(23% 2%,77% 2%,100% 50%,77% 98%,23% 98%,0 50%);
  }
}
@media (max-width:620px) {
  
  #toolkit .toolkit-tile,
  #toolkit .toolkit-tile:nth-child(n+4),
  #toolkit .toolkit-tile:nth-child(n+6) { --base-y:0px;max-width:178px;height:150px; }

  #toolkit .toolkit-tile h3 { bottom:18px;font-size:.78rem; }
}

/* Toolkit v3 — brand-balanced asymmetric bento. */
#toolkit {
  background:#fff;
}
#toolkit .toolkit__head {
  flex-direction:column;
  text-align:center;
}

#toolkit #toolkitTitle { order:1; }
#toolkit .toolkit__lede {
  order:2;
}

#toolkit .toolkit-tile,
#toolkit .toolkit-tile:nth-child(n+4),
#toolkit .toolkit-tile:nth-child(n+6) {
  --base-y:0px;
  position:relative;
  z-index:1;
  width:auto;
  max-width:none;
  height:auto;
  min-height:0;
  justify-self:stretch;
  overflow:hidden;
  clip-path:none;
  border:1px solid rgba(31,39,32,.12);
  border-radius:22px;
  background:#fafafa;
  box-shadow:0 14px 34px rgba(20,29,21,.055);
  filter:none;
  transform:none;
  transition: transform var(--dur-4) var(--ease-out),box-shadow var(--dur-4) ease,border-color var(--dur-3) ease;
}
#toolkit .toolkit-tile::before {
  content:"";
  position:absolute;
  z-index:0;
  inset:-28%;
  width:auto;
  aspect-ratio:auto;
  border-radius:50%;
  clip-path:none;
  background:radial-gradient(circle at 54% 46%,rgba(77,206,22,.13),rgba(221,244,213,.28) 27%,transparent 61%);
  filter:blur(10px);
  opacity:.72;
  transform:translate3d(12%,-10%,0);
  transition: opacity var(--dur-3) ease,transform var(--dur-4) var(--ease-out);
}
#toolkit .toolkit-tile::after {
  content:"";
  position:absolute;
  z-index:0;
  inset:14px;
  border-radius:15px;
  clip-path:none;
  background:
    linear-gradient(90deg,transparent 0 62%,rgba(77,206,22,.08) 62% 62.5%,transparent 62.5%),
    linear-gradient(0deg,transparent 0 68%,rgba(31,39,32,.045) 68% 68.5%,transparent 68.5%);
  opacity:.55;
  mask:none;
  pointer-events:none;
}
#toolkit .toolkit-tile:hover {
  border-color:rgba(77,206,22,.38);
  box-shadow:0 24px 52px rgba(20,29,21,.1);
  filter:none;
  transform:translate3d(0,-7px,0) scale(1.006);
}
#toolkit .toolkit-tile:hover::before { opacity:1;transform:translate3d(7%,-6%,0) scale(1.05); }
#toolkit .toolkit-tile:hover::after { background:linear-gradient(135deg,rgba(255,255,255,.2),transparent 45%); }

#toolkit .toolkit-tile h3 {
  position:absolute;
  z-index:2;
  left:26px;
  bottom:42px;
  width:auto;
  max-width:calc(100% - 52px);
  margin:0;
  color:#1a1a1a;
  font-family:var(--font-sans,Geist,sans-serif);
  font-size:clamp(1.05rem,1.35vw,1.35rem);
  font-weight:500;
  letter-spacing:-.035em;
  transform:none;
}
#toolkit .toolkit-tile p {
  position:absolute;
  z-index:2;
  left:26px;
  bottom:21px;
  margin:0;
  color:#747a74;
  font-size:.72rem;
  line-height:1.2;
}

@media (max-width:1050px) {
  #toolkit .toolkit__head { text-align:left; }
    
}
@media (max-width:620px) {
  
  #toolkit .toolkit-tile { max-width:none;height:auto;border-radius:18px; }

  #toolkit .toolkit-tile h3 { left:22px;bottom:39px;font-size:1.05rem; }
  #toolkit .toolkit-tile p { left:22px;bottom:19px; }

}

/* Capabilities v2 — three oversized light-mode product cards. */

.capabilities-v2__grid {
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(14px,1.4vw,22px);
}
.capability-card-v2 {
  position:relative;
  min-width:0;
  overflow:hidden;
  border:1px solid rgba(28,34,29,.15);
  border-radius:24px;
  background:linear-gradient(145deg,#fff 0%,#fafafa 76%,#f4f4f4 100%);
  box-shadow:0 18px 42px rgba(20,27,21,.055);
  transform:translateZ(0);
  transition: transform var(--dur-4) var(--ease-out),box-shadow var(--dur-4) ease,border-color var(--dur-3) ease;
}
.capability-card-v2:hover {
  border-color:rgba(77,206,22,.38);
  box-shadow:0 28px 58px rgba(20,27,21,.095);
  transform:translate3d(0,-8px,0);
}

@keyframes capMapDraw { from{stroke-dashoffset:80;opacity:.22}to{stroke-dashoffset:0;opacity:.6} }
@keyframes capNodeIn { 0%{transform:translateX(-18px);opacity:.2}100%{transform:translateX(0);opacity:1} }
@keyframes capDecision { 50%{transform:scale(1.12);transform-origin:center;filter:drop-shadow(0 0 22px rgba(77,206,22,.58))} }

@keyframes capPolish { 0%{filter:grayscale(1);transform:scale(.97)}60%{filter:grayscale(.3);transform:scale(1.015)}100%{filter:none;transform:scale(1)} }

@keyframes capCursor { 50%{opacity:0} }

/* Creative perspective — luminous editorial image bento. */
.partnerships { background:#fff;scroll-margin-top:96px; }
.editorial-head { gap:14px 56px; }
.editorial-head .mono-label { grid-column:1/-1;color:#696e69; }
.editorial-head > p:last-child { max-width:650px;margin:0 0 4px auto;color:#646a64;font-size:clamp(.94rem,1.15vw,1.08rem);line-height:1.55; }

/* Trusted partnerships — readable floating glass cards. */
.partnerships { position:relative;overflow:hidden; }
.partnerships::before { content:"";position:absolute;inset:28% -8% 3%;background:radial-gradient(circle at 22% 48%,rgba(77,206,22,.11),transparent 29%),radial-gradient(circle at 52% 42%,rgba(248,214,205,.24),transparent 28%),radial-gradient(circle at 82% 53%,rgba(197,223,244,.28),transparent 30%);filter:blur(24px);pointer-events:none; }
.partnerships__cards { position:relative;display:grid;grid-template-columns:repeat(12,minmax(0,1fr));min-height:clamp(390px,45vw,560px); }
.partnership-card { position:relative;display:flex;flex-direction:column;justify-content:space-between;border:1px solid rgba(28,34,29,.13);background:rgba(255,255,255,.8);backdrop-filter:blur(20px);animation:partnershipFloat 7s ease-in-out infinite; }

.partnership-card blockquote { position:relative;color:#141414; }
.partnership-card figcaption { position:relative;color:#1b1f1b; }.partnership-card figcaption > span:last-child { display:flex;flex-direction:column;gap:3px; }
@keyframes partnershipFloat { 0%,100%{translate:0 0}50%{translate:0 -10px} }

@media (max-width:900px) {
    .editorial-head > p:last-child { margin-left:0; }
  .capabilities-v2__grid { grid-template-columns:1fr; }
  .capability-card-v2 { display:grid;grid-template-columns:minmax(280px,.9fr) 1fr; }

  .partnerships__cards { grid-template-columns:1fr;min-height:0; }
}
@media (max-width:620px) {
    .capability-card-v2 { display:block; }
  
  }
@media (prefers-reduced-motion:reduce) {
  .capability-card-v2,
.capability-card-v2 *,
.partnership-card { animation:none !important;transition:none !important; }
}

/* Contact + footer v2 — light editorial system. */
#stack[hidden],#testimonials[hidden] { display:none !important; }

@keyframes contactCardFloat { 0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)} }

/* Hero → Selected Work: one continuous center-origin scroll handoff. */

#work .sw__head,
#work .sw__list,
#work .sw__pager,
#work .sw__thumb {
  will-change:transform,opacity,filter;
}

/* Annotation pass: flatter capabilities and a better-balanced process story. */
#services .capability-card-v2,
#services .capability-card-v2:hover {
  box-shadow:none;
}

/* Supplied animated capability cards. */
#services .capability-card-v2--video {
  aspect-ratio:912 / 1178;
  border:0;
  border-radius:clamp(20px,1.8vw,24px);
  background:linear-gradient(145deg,#e6f8df 0%,#f4fbed 48%,#fff 100%);
  box-shadow:none;
  isolation:isolate;
  overflow:hidden;
}
html.capability-video-sequence #services .capability-card-v2--video {
  opacity:0;
  transform:translate3d(0,28px,0) scale(.985);
  transition: opacity var(--dur-3) var(--ease-out),
    transform var(--dur-3) var(--ease-out),
    box-shadow var(--dur-3) ease;
}
html.capability-video-sequence #services .capability-card-v2--video.is-revealed {
  opacity:1;
  transform:translate3d(0,0,0) scale(1);
}

@media (hover:hover) and (pointer:fine) {
  html.capability-video-sequence #services .capability-card-v2--video.is-revealed:hover,
  #services .capability-card-v2--video:hover {
    border-color:transparent;
    box-shadow:0 18px 38px rgba(30,58,34,.09);
    transform:translate3d(0,-7px,0);
  }
}
@media (max-width:900px) {

  #services .capabilities-v2__grid {
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:clamp(14px,2.4vw,20px);
  }
  #services .capability-card-v2--video {
    display:block;
    width:100%;
    margin:0;
  }
  #services .capability-card-v2--video:last-child {
    grid-column:1 / -1;
    width:calc((100% - clamp(14px,2.4vw,20px)) / 2);
    justify-self:center;
  }
}
@media (max-width:620px) {

  #services .capabilities-v2__grid {
    grid-template-columns:minmax(0,1fr);
    gap:16px;
  }
  #services .capability-card-v2--video,
  #services .capability-card-v2--video:last-child {
    grid-column:auto;
    width:min(100%,420px);
    justify-self:center;
  }
}
@media (prefers-reduced-motion:reduce) {
  
  html.capability-video-sequence #services .capability-card-v2--video {
    opacity:1;
    transform:none;
  }
  
  #services .capability-card-v2--video:hover { transform:none; }
}

#process .design-process__step {
  opacity:1;
  background:#e4e4e4;
  transition: background-color var(--dur-3) ease,
    background-image var(--dur-3) ease,
    opacity var(--dur-3) ease,
    flex-grow var(--dur-3) var(--ease-out);
}
#process .design-process__visual,
#process .design-process__visual video {
  opacity:1 !important;
  filter:none !important;
}
#process .design-process__step:not(.is-active) .design-process__number,
#process .design-process__step:not(.is-active) .design-process__copy {
  opacity:.24;
}
#process .design-process__step:focus-visible {
  outline:2px solid #4dce16;
  outline-offset:3px;
}
@media (min-width:901px) and (min-height:620px) {
  #process .design-process__step.is-active {
    background:#fff;
    border:1px solid #dededb !important;
  }
  }
@media (max-width:900px), (max-height:619px) {
    #process .design-process__step,
  #process .design-process__step.is-active {
    background:#ededed;
  }
  #process .design-process__step.is-active {
    background:#fff;
    border:1px solid #dededb !important;
  }
}
@media (max-width:620px) {
  .design-process__visual--discover video { transform:scale(1.24); }
}

/* Hero story → Selected Work: pinned, in-place scene replacement. */
#hero {
  z-index:2;
}
#work {
  position:relative;
  margin-top:-100svh;
}
html.is-work-stage-live #hero { z-index:0; }
html.is-work-stage-live #work { z-index:2; }
html.is-hero-work-handoff #work {
  z-index:20;
  background:transparent !important;
}
html.is-hero-work-handoff #swRoot {
  position:fixed !important;
  z-index:20;
  inset:0;
  top:0 !important;
  width:100%;
  height:100svh !important;
  min-height:100svh !important;
  overflow:hidden;
  background:#f4f4f4;
}

#work .sw__inner {
  transform-origin:50% 50%;
  will-change:transform,opacity,filter;
}

@media (max-width:900px) {
  html.is-hero-work-handoff #swRoot {
    display:grid;
    grid-template-rows:auto minmax(0,1fr);
    padding:var(--header-content-clearance) 20px 18px !important;
  }
  html.is-hero-work-handoff #work .sw__head { align-self:start; }
  html.is-hero-work-handoff #work .sw__support { margin-top:12px; }
  html.is-hero-work-handoff #work .sw__pills { margin-top:14px; }
  html.is-hero-work-handoff #work .sw__main {
    min-height:0;
    padding:16px 0 0;
  }
  html.is-hero-work-handoff #work .sw__list,
  html.is-hero-work-handoff #work .sw__pager { display:none; }
  html.is-hero-work-handoff #work .sw__center { min-height:0;height:100%; }
html.is-hero-work-handoff #work .sw__thumb {
    width:min(100%,620px);
    height:100%;
    max-height:55vh;
    aspect-ratio:4/5;
    /* .sw__thumb's resting mobile card is intentionally rounded, but
       blown up to this size mid-handoff it read as a large, unexplained
       rounded rectangle floating over the dissolving hero. Square it off
       for the duration of the handoff only; CSS hands the corners back
       once is-hero-work-handoff is removed and the card settles into its
       normal grid position. */
    border-radius:0 !important;
  }
}

/* Playground — cross-disciplinary product thinking. */
.playground {
  position:relative;
  overflow:clip;
  background:#fff;
  scroll-margin-top:96px;
}
.playground::before {
  content:"";
  position:absolute;
  z-index:0;
  top:9%;
  right:-14%;
  width:min(54vw,760px);
  aspect-ratio:1;
  border-radius:50%;
  background:radial-gradient(circle,rgba(77,206,22,.085),rgba(77,206,22,.025) 42%,transparent 70%);
  pointer-events:none;
}

@keyframes playgroundPhotoDrift { to { transform:translate3d(1.5%,-1.5%,0) scale(1.045); } }
@keyframes playgroundWave { 0%{transform:scaleY(.72);opacity:.62} 100%{transform:scaleY(1.14);opacity:1} }
@keyframes playgroundMidi { to { transform:translateX(9px);filter:saturate(1.2); } }
@keyframes playgroundStoryboard { to { transform:translate3d(18px,-3px,0) rotate(-5deg) scale(1.06);background:rgba(77,206,22,.48); } }
@keyframes playgroundChart { to { transform:scaleY(.76);opacity:.72; } }

@media (max-width:680px) {
  .playground { padding-top:78px;padding-bottom:78px; }

}

/* Playground media players. */

.playground-video-player {
  isolation:isolate;
  color:#fff;
  background:#14251d;
}
.playground-video-player::after { z-index:2;background:linear-gradient(135deg,rgba(255,255,255,.12),transparent 35%,rgba(77,206,22,.05)); }

@keyframes playgroundRecordSpin { to { transform:translate(-50%,-50%) rotate(360deg); } }

/* Playground desktop: complete story within one viewport. */
@media (min-width:1051px) {
  .playground {
    min-height:100svh;
    padding-top:clamp(74px,8.5vh,94px);
    padding-bottom:clamp(38px,5vh,58px);
  }

}

/* Playground refinement — quiet, informative, and device-aware. */
.playground::before { opacity:.5; }

@media (min-width:1051px) {
  .playground {
    padding-top:clamp(72px,8vh,88px);
    padding-bottom:clamp(34px,4.5vh,50px);
  }

}

@media (max-width:1050px) {
  .playground { padding-top:76px;padding-bottom:76px; }

}

@media (max-width:680px) {
  .playground { padding:68px 18px; }

}
/* Playground filtered gallery — supplied four-layout reference. */
.pg-gallery {
  position:relative;
  background:#fafafa;
  scroll-margin-top:72px;
}
.pg-gallery > h2 {
  margin:0 0 clamp(42px,6vh,68px);
  font-size:clamp(3rem,5.2vw,5.35rem);
  font-weight:500;
  line-height:.94;
  letter-spacing:-.065em;
}
.pg-gallery__navrow {
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(260px,32%);
  align-items:center;
  gap:clamp(24px,5vw,80px);
}
.pg-gallery__tabs {
  display:grid;
}
.pg-gallery__tabs button {
  transition: color var(--dur-3) ease,background var(--dur-3) ease,transform var(--dur-3) var(--ease-out);
}
.pg-gallery__tabs button:hover { color:#212121;transform:translateY(-2px); }
.pg-gallery__tabs button:focus-visible { outline:2px solid #4dce16;outline-offset:3px; }
.pg-gallery__summary {
  justify-self:end;
  transition:none;
}
.pg-gallery__stage { position:relative;height:clamp(500px,59vh,650px); }
.pg-panel {
  position:absolute;
  inset:0;
  opacity:0;
  visibility:hidden;
  pointer-events:none;
  filter:blur(7px);
  transform:translate3d(0,16px,0) scale(.988);
  transform-origin:50% 20%;
  /* Was `transition: none`, which made every scroll-driven tab change a
     hard cut: opacity, a 7px blur and a transform all snapped at once
     while only the panel's children got an eased stagger. That mismatch
     is what read as harsh. Transitioning the container too gives a real
     crossfade in both directions.
     visibility is delayed by the fade duration on the way OUT so the
     outgoing panel stays painted while it fades, and switched instantly
     on the way IN (see .is-active) so the incoming one is visible from
     the first frame. */
  transition:
    opacity var(--dur-3) var(--ease-out, var(--ease-out)),
    filter var(--dur-3) var(--ease-out, var(--ease-out)),
    transform var(--dur-3) var(--ease-out, var(--ease-out)),
    visibility 0s linear .52s;
}
.pg-panel.is-active {
  transition: opacity var(--dur-3) var(--ease-out, var(--ease-out)),
    filter var(--dur-3) var(--ease-out, var(--ease-out)),
    transform var(--dur-3) var(--ease-out, var(--ease-out)),
    visibility 0s linear 0s;
}
.pg-panel.is-active {
  z-index:2;
  opacity:1;
  visibility:visible;
  pointer-events:auto;
  filter:none;
  transform:none;
}
@media (prefers-reduced-motion: reduce) {
  .pg-panel,
  .pg-panel.is-active { transition: none; }
}
.pg-panel figure { overflow:hidden;border-radius:20px;background:#ededed; }
.pg-panel img,.pg-panel video { display:block;width:100%;height:100%;object-fit:cover; }
.pg-panel--aerial { display:grid;grid-template-columns:minmax(0,3.15fr) minmax(210px,1fr); }
.pg-aerial__viewer::after { content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,.08),transparent 58%,rgba(0,0,0,.16));pointer-events:none; }
.pg-aerial__portrait img { transform:scale(1.02); }
.pg-frost {
  z-index:3;
  -webkit-backdrop-filter:blur(18px) saturate(1.2);
  backdrop-filter:blur(18px) saturate(1.2);
  transition: background var(--dur-2) ease,transform var(--dur-3) var(--ease-out);
}
.pg-frost:hover { background:rgba(30,30,30,.62);transform:translateY(-2px); }
.pg-aerial__more { position:absolute;top:24px;left:24px;padding:14px 24px;font:500 .9rem/1 var(--font-sans,Arial,sans-serif); }
.pg-aerial__fullscreen { position:absolute;top:24px;right:24px; }
.pg-aerial__fullscreen svg { width:21px;stroke-width:1.7; }
.pg-aerial__dots { position:absolute;z-index:3;left:50%;bottom:22px;gap:7px;transform:translateX(-50%); }
.pg-aerial__dots i { width:25px;height:6px;border-radius:99px;background:rgba(255,255,255,.34);transition: width var(--dur-3) ease,background var(--dur-3) ease; }
.pg-aerial__dots i.is-active { width:34px;background:rgba(255,255,255,.86); }
.pg-panel--sound { display:grid;grid-template-columns:repeat(3,minmax(0,1fr)); }
.pg-track {
  position:relative;
  overflow:hidden;
  isolation:isolate;
  transition: transform var(--dur-3) var(--ease-out),filter var(--dur-3) ease;
}
.pg-track::before { content:"";position:absolute;z-index:-1;inset:-10%;background:radial-gradient(circle at 50% 53%,rgba(255,255,255,.18),transparent 30%);transform:scale(.86);opacity:.5;transition: transform var(--dur-4) var(--ease-out),opacity var(--dur-3) ease; }
.pg-track:hover { transform:translateY(-5px);filter:saturate(1.06); }
.pg-track:hover::before,.pg-track.is-playing::before { opacity:1;transform:scale(1.2); }
.pg-track--one { background:linear-gradient(180deg,#260006 0%,#7a0b05 28%,#df2307 59%,#dc6a26 100%); }
.pg-track--two { background:linear-gradient(180deg,#09122d 0%,#141565 29%,#486cd0 57%,#68b6cc 76%,#111a44 100%); }
.pg-track--three { background:linear-gradient(180deg,#001908 0%,#073914 28%,#34b960 60%,#48c56c 100%); }
.pg-track__number { position:absolute;top:28px;right:28px;font-size:clamp(4.8rem,8vw,8.5rem);line-height:.8;letter-spacing:-.075em; }
.pg-track__play { position:absolute;top:50%;left:50%;width:68px;height:68px;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);transform:translate(-50%,-50%); }
.pg-track__play::before { content:"";position:absolute;top:50%;left:53%;border-top:9px solid transparent;border-bottom:9px solid transparent;border-left:14px solid #141414;transform:translate(-50%,-50%); }
.pg-track.is-playing .pg-track__play::before { left:50%;width:12px;height:17px;border:0;border-left:4px solid #141414;border-right:4px solid #141414; }
.pg-track.is-playing .pg-track__play { animation:pgPulse 1.8s ease-in-out infinite; }
@keyframes pgPulse { 50% { transform:translate(-50%,-50%) scale(1.07) } }
.pg-track__meta { position:absolute;right:28px;bottom:28px;left:28px; }
.pg-track__meta strong,.pg-track__meta small { display:block; }
.pg-track__meta strong { font-weight:500;letter-spacing:-.025em; }
.pg-track__meta small { margin-top:8px;color:rgba(255,255,255,.65);font-size:.82rem; }
.pg-panel--motion { grid-template-columns:repeat(3,minmax(0,1fr)); }
.pg-panel--motion figure { position:relative; }
.pg-panel--travel { display:grid;grid-template-columns:repeat(12,minmax(0,1fr));grid-template-rows:repeat(2,minmax(0,1fr)); }
.pg-panel--travel img { transform:translateZ(0); }

@media (max-width:980px) {
  .pg-gallery__navrow { grid-template-columns:1fr; }
  .pg-gallery__summary { justify-self:start; }
    .pg-gallery__stage { height:600px; }
  .pg-panel--aerial { grid-template-columns:2.35fr 1fr; }
}
@media (max-width:720px) {
  .pg-gallery { min-height:auto;overflow:hidden; }
  .pg-gallery > h2 { margin-bottom:30px;font-size:clamp(2.7rem,14vw,4.2rem); }
    .pg-gallery__tabs { display:flex;scrollbar-width:none; }
  .pg-gallery__tabs::-webkit-scrollbar { display:none; }
      .pg-gallery__stage[data-active="aerial"] { height:560px; }
  .pg-gallery__stage[data-active="sound"],.pg-gallery__stage[data-active="motion"] { height:500px; }
  .pg-gallery__stage[data-active="travel"] { height:680px; }
  .pg-panel--aerial { grid-template-columns:1fr;grid-template-rows:390px 156px; }
  .pg-aerial__more { top:14px;left:14px;padding:11px 17px; }
  .pg-aerial__fullscreen { top:14px;right:14px; }
  .pg-panel--sound,.pg-panel--motion { scrollbar-width:none; }
  .pg-panel--sound::-webkit-scrollbar,.pg-panel--motion::-webkit-scrollbar { display:none; }
  .pg-track,.pg-panel--motion figure { flex:0 0 min(78vw,330px);scroll-snap-align:start; }
  .pg-track__number { top:24px;right:24px; }
  .pg-panel--travel { grid-template-columns:repeat(2,1fr);grid-template-rows:repeat(4,1fr); }

  .pg-panel figure,.pg-aerial__viewer,.pg-track { border-radius:15px; }
}
@media (prefers-reduced-motion:reduce) {
  .pg-panel,.pg-gallery__summary,.pg-track,.pg-panel--travel img,.pg-panel--motion video { animation:none !important;transition:none !important; }
}

/* Unified paper surface. Preserve imagery, video, and intentional gradient artwork. */
:root { --site-paper:#f4f4f4; }
html,
body,
main,
main > section,
.section,
.pg-gallery,
.partnerships,
.editorial-footer { background-color:var(--site-paper) !important; }
.pg-gallery__tabs button,
.capability-card-v2,
.value-card,
.partnership-card,
.toolkit-tile,
.design-process__step { background-color:var(--site-paper); }

/* Playground media refinements. */
.pg-gallery { background:var(--site-paper); }
.pg-gallery__stage { height:clamp(500px,42vw,650px); }
.pg-gallery__stage[data-active="aerial"] { height:clamp(440px,calc((100vw - (var(--page-pad,54px) * 2) - 14px) / 2.3402778),646px); }
.pg-panel--aerial { grid-template-columns:minmax(0,16fr) minmax(0,5.0625fr); }
.pg-aerial__viewer,.pg-aerial__portrait { position:relative;overflow:hidden;background:#111; }
.pg-youtube-player,.pg-youtube-player iframe { width:100%;height:100%; }
.pg-youtube-player iframe { display:block; }
.pg-youtube-player { opacity:0;transition: opacity var(--dur-4) var(--ease-out); }
.pg-gallery.is-media-ready .pg-youtube-player,
.pg-media-expanded .pg-youtube-player { opacity:1; }
@media (prefers-reduced-motion:reduce) {
  .pg-youtube-player { transition:none; }
}
.pg-aerial__preview-trigger { position:absolute;z-index:2;inset:0;width:100%;height:100%; }
.pg-media-expanded .pg-aerial__preview-trigger { display:none; }
.pg-aerial__more { display:inline-flex;align-items:center; }
.pg-reels__follow { position:absolute;top:24px;left:24px;padding:14px 20px;font:500 .8rem/1 var(--font-sans,Arial,sans-serif); }
.pg-reels__dots { position:absolute;z-index:3;left:50%;bottom:22px;gap:6px;transform:translateX(-50%); }
.pg-reels__dots i { width:18px;height:5px;border-radius:99px;background:rgba(255,255,255,.32);transition: width var(--dur-3) ease,background var(--dur-3) ease; }
.pg-reels__dots i.is-active { width:27px;background:rgba(255,255,255,.88); }
.pg-aerial__dots i[role="button"]:focus-visible,.pg-reels__dots i[role="button"]:focus-visible { box-shadow:0 0 0 2px rgba(255,255,255,.95),0 0 0 4px rgba(77,206,22,.82); }
.pg-aerial__fullscreen--portrait { z-index:5; }

/* No overflow:hidden while a film is expanded: it released the Playground's
   sticky pin and the film collapsed toward a tile that had moved. The page
   is held by js/playground-media.js (lockPageScroll) instead. */
.pg-media-backdrop { position:fixed;z-index:10000;inset:0;background:rgba(5,5,7,.92);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px); }
.pg-media-placeholder { min-width:0;min-height:0;border-radius:20px;background:#111; }
/* The expand/collapse transition is a uniform scale plus an animated
   clip-path window (js/playground-media.js), so the corner rounding
   comes from the clip-path — the box itself must stay square, or the
   tile's own 20px radius would survive at full screen. */
.pg-media-expanded { overflow:hidden !important;isolation:isolate;box-shadow:0 28px 90px rgba(0,0,0,.38);border-radius: var(--radius-media, 16px) !important;backface-visibility:hidden;will-change:transform,clip-path; }
/* Chrome (follow pill, collapse control, dots, edge gradient) is pinned
   to the container, so during the flight it would be scaled down with
   it and read as tiny floating furniture. Hidden while the media is in
   motion, faded back once it lands. */
.pg-media-expanded .pg-frost,
.pg-media-expanded .pg-aerial__dots,
.pg-media-expanded .pg-reels__dots,
.pg-media-expanded::after { transition: opacity var(--dur-2) ease; }
.pg-media-expanded.is-transitioning .pg-frost,
.pg-media-expanded.is-transitioning .pg-aerial__dots,
.pg-media-expanded.is-transitioning .pg-reels__dots,
.pg-media-expanded.is-transitioning::after { opacity:0;pointer-events:none; }
.pg-media-expanded.is-closing { pointer-events:none; }
.pg-media-expanded::after { content:"";position:absolute;z-index:2;inset:0;pointer-events:none;background:linear-gradient(180deg,rgba(0,0,0,.14),transparent 62%,rgba(0,0,0,.28)); }
.pg-media-expanded .pg-youtube-player { position:absolute;z-index:0;top:50%;left:50%;width:max(100vw,177.778dvh);height:max(100dvh,56.25vw);transform:translate(-50%,-50%); }
.pg-media-expanded--portrait .pg-youtube-player { z-index:1;width:min(100vw,56.25dvh);height:min(100dvh,177.778vw);object-fit:contain;background:transparent; }
.pg-portrait-blur { position:absolute;z-index:0;inset:-7%;width:114%;height:114%;object-fit:cover;pointer-events:none;opacity:.92;filter:blur(38px) brightness(.5) saturate(.9);transform:scale(1.08); }
.pg-media-expanded .pg-youtube-player iframe { position:absolute;inset:0;width:100%;height:100%; }
.pg-media-expanded .pg-frost,.pg-media-expanded .pg-aerial__dots,.pg-media-expanded .pg-reels__dots { z-index:5; }
.pg-media-expanded .pg-aerial__fullscreen { position:fixed;z-index:10005;top:max(18px,env(safe-area-inset-top));right:max(18px,env(safe-area-inset-right));pointer-events:auto; }
.pg-media-expanded.is-closing .pg-aerial__fullscreen { pointer-events:none; }

.pg-track { --track-progress:0%;background-size:180% 180%;background-position:50% 50%; }
.pg-track.is-playing { animation:pgGradientShift 7s ease-in-out infinite alternate; }
@keyframes pgGradientShift { 0%{background-position:10% 20%} 50%{background-position:90% 70%} 100%{background-position:25% 95%} }
.pg-track__play::after {
  content:"";
  position:absolute;
  inset:-10px;
  border-radius:50%;
  opacity:0;
  background:conic-gradient(rgba(255,255,255,.96) var(--track-progress),rgba(255,255,255,.2) 0);
  -webkit-mask:radial-gradient(farthest-side,transparent calc(100% - 3px),#000 0);
  mask:radial-gradient(farthest-side,transparent calc(100% - 3px),#000 0);
  transition: opacity var(--dur-2) ease;
}
.pg-track.has-progress .pg-track__play::after { opacity:1; }
.pg-track__canvas { position:absolute;z-index:0;inset:0;width:100%;height:100%;opacity:.96;pointer-events:none; }
.pg-track__number,.pg-track__play,.pg-track__meta { z-index:2; }
.pg-track__play::before { display:none; }
.pg-track__play > svg { position:absolute;top:50%;left:50%;width:23px;height:23px;transform:translate(-50%,-50%); }
.pg-track.is-playing .pg-track__icon-play { display:none; }
.pg-track.is-playing .pg-track__icon-pause { display:block; }

.pg-preview { position:fixed;z-index:10020;inset:0;display:grid;place-items:center;background:#0f0f0f;opacity:0;visibility:hidden;pointer-events:none;overflow:hidden;isolation:isolate;transition:none; }
.pg-preview.is-open { opacity:1;visibility:visible;pointer-events:auto;transition-delay:0s; }
/* max-width:none is load-bearing. The global `img,video,svg,canvas
   { max-width:100% }` reset clamped this backdrop's 112% width back to
   100% while its -6% left inset still applied, so the element began 6% to
   the left of the frame but was 6% too narrow to reach the other side —
   scale(1.06) closed only part of that, leaving a ~39px strip of bare page
   down the right edge of the lightbox. Height was never affected because
   no max-height is set, which is why the cut showed on one side only. */
.pg-preview__backdrop { position:absolute;z-index:-1;inset:-6%;width:112%;max-width:none;height:112%;object-fit:cover;opacity:0;filter:blur(38px) brightness(.38) saturate(.82);transform:scale(1.06);transition: opacity var(--dur-3) ease; }
.pg-preview.is-portrait .pg-preview__backdrop { opacity:1; }
/* Every photo is now fitted whole into the window rather than cropped to
   fill it, so the blurred backdrop is the surround for landscape and the
   panorama too, not only for portraits. */
.pg-preview.is-open .pg-preview__backdrop { opacity:1; }
/* Positioned like #work's .sw__ov-cover: JS sizes this to the image's own
   cover-fit box and centers it, so the same natural-dimension FLIP math
   can scale/translate it from the clicked thumbnail's exact rect up to
   full screen, instead of just cross-fading a fixed full-bleed box. */
/* max-width/max-height:none for the same reason as the backdrop above: the
   global img reset clamps the box to the window, and in Fill or Original
   the photo is deliberately larger than the window. Clamped, its box lost
   its aspect ratio and object-fit then letterboxed inside it — bands, in
   the one mode that exists to avoid them. */
.pg-preview__image { position:absolute;left:50%;top:50%;max-width:none;object-fit:cover;will-change:transform,opacity,border-radius;backface-visibility:hidden;transform-origin:center; }
.pg-preview.is-portrait .pg-preview__image { object-fit:contain; }
/* One frosted gradient carries the whole bottom edge, so the caption text
   and the thumbnail strip both read clearly without either needing its
   own boxed background. */
.pg-preview::after {
  content:"";
  position:absolute;
  z-index:2;
  inset:auto 0 0;
  height:34%;
  background:linear-gradient(to top,rgba(6,8,7,.72),rgba(6,8,7,.32) 55%,transparent);
  -webkit-backdrop-filter:blur(6px);
  backdrop-filter:blur(6px);
  -webkit-mask-image:linear-gradient(to top,#000,#000 40%,transparent);
  mask-image:linear-gradient(to top,#000,#000 40%,transparent);
  pointer-events:none;
}
/* Deliberately NOT overflow:hidden. That destroys the scroll container,
   which drops position:sticky out of its context — the pinned Playground
   stage snapped ~830px out of place the instant the lightbox opened, and
   the document resize took the scrollbar with it. The photo then flew to
   a target that had moved, the caption clipped and a white band showed
   below the viewport. Scrolling is held by stopping Lenis and swallowing
   wheel/touch instead (see js/playground-media.js), which locks the page
   without touching layout. */
html.pg-travel-preview-open #nav { opacity:0 !important;transform:translate3d(0,-14px,0) !important;pointer-events:none !important; }
.pg-preview__close,.pg-preview__nav { position:absolute;z-index:3;display:grid;place-items:center;width:46px;height:46px;border:1px solid rgba(255,255,255,.24);color:#fff;background:rgba(18,22,19,.42);-webkit-backdrop-filter:blur(18px) saturate(1.15);backdrop-filter:blur(18px) saturate(1.15);box-shadow:0 10px 32px rgba(0,0,0,.16),inset 0 1px 0 rgba(255,255,255,.12);transition: transform var(--dur-2) var(--ease-out),background var(--dur-2) ease; }
.pg-preview__close:hover,.pg-preview__nav:hover { transform:scale(1.06);background:rgba(28,28,28,.62); }
.pg-preview__close:focus-visible,.pg-preview__nav:focus-visible { outline:1px solid rgba(255,255,255,.72) !important;outline-offset:4px; }
.pg-preview__close { top:24px;right:24px; }.pg-preview__nav { top:50%;transform:translateY(-50%); }.pg-preview__nav:hover { transform:translateY(-50%) scale(1.06); }
.pg-preview__nav--prev { left:30px; }.pg-preview__nav--next { right:30px; }
.pg-preview__close svg,.pg-preview__nav svg { width:19px;height:19px; }
/* Overlaps the frosted bottom edge, right-aligned, so the slideshow reads
   as one minimal composition rather than a separate control bar. */
.pg-preview__rail {
  position:absolute;
  z-index:4;
  right:clamp(24px,4vw,68px);
  bottom:clamp(20px,3.4vw,44px);
  display:flex;
  gap:8px;
  pointer-events:auto;
}
.pg-preview__rail::-webkit-scrollbar { display:none; }
.pg-preview__rail button { flex:0 0 46px;width:46px;height:46px;min-width:0;overflow:hidden;border:2px solid rgba(255,255,255,.3);background:#ddd;opacity:.6;box-shadow:0 6px 18px rgba(0,0,0,.28);transition: opacity var(--dur-2) ease,border-color var(--dur-2) ease,transform var(--dur-2) ease; }
.pg-preview__rail button:hover { opacity:.85;transform:translateY(-2px); }.pg-preview__rail button.is-active { opacity:1;border-color:#4dce16; }
.pg-preview__rail img { width:100%;height:100%;object-fit:cover; }
@media (max-width:720px) {
  .pg-preview__rail { display:none; }
}

@media (max-width:720px) {
  .pg-gallery__stage[data-active="aerial"] { height:720px; }
  .pg-panel--aerial { flex-direction:column; }
  .pg-aerial__viewer { width:100%;height:auto;aspect-ratio:16/9;flex:0 0 auto; }
  .pg-aerial__portrait { width:min(58%,240px);height:auto;aspect-ratio:9/16;flex:0 0 auto; }
  .pg-reels__follow { top:12px;left:12px;padding:10px 13px; }
  .pg-aerial__fullscreen--portrait { top:12px;right:12px; }
  .pg-reels__dots { bottom:12px; }
  .pg-preview__close { top:max(14px,env(safe-area-inset-top));right:14px; }.pg-preview__nav { width:40px;height:40px; }.pg-preview__nav--prev { left:12px; }.pg-preview__nav--next { right:12px; }
}
@media (prefers-reduced-motion:reduce) { .pg-track.is-playing { animation:none !important; } }

/* Lucide utility icon system; brand artwork and editorial diagrams remain bespoke. */
.lucide { display:inline-block;height:1em;stroke:currentColor;vertical-align:-.12em; }
.accordion__icon .lucide { width:17px;height:17px; }
.pill__arrow .lucide { width:17px;height:17px; }
.cs__back { display:inline-flex;align-items:center;gap:8px; }.cs__back .lucide { width:16px;height:16px; }
.cs__close { justify-content:center;gap:7px; }.cs__close .lucide { width:18px;height:18px; }

.pg-track__play .pg-track__icon-pause { display:none; }
.pg-track.is-playing .pg-track__play .pg-track__icon-play { display:none; }
.pg-track.is-playing .pg-track__play .pg-track__icon-pause { display:block; }

/* Playground entrance, tab changes, media hover and fullscreen transitions
   are owned by Motion in js/playground-media.js. */

/* Mobile-first section system: compact rhythm, touch-sized controls and
   horizontal storytelling where a vertical stack would become excessive. */
@media (max-width:1024px) {
  .toolkit__head,
.editorial-head {
    gap:12px;
  }
  .toolkit__head > *,
.editorial-head > * {
    max-width:44rem;
    text-align:left !important;
  }
}

@media (max-width:760px) {
  :root { --mobile-gutter:max(20px,env(safe-area-inset-left)); }

  #services h2,
  #process h2,
  #client-value h2,
  #toolkit h2,
  #perspective h2,
  #partnerships h2,
  #experience h2,
  #insights h2,
  #contact h2 {
    text-wrap:balance;
  }

  /* Filter navigation behaves like a native segmented carousel. */
  .sw__pills,
  .pg-gallery__tabs {
    display:flex !important;
    overscroll-behavior-inline:contain;
    scroll-padding-inline:var(--mobile-gutter);
    scrollbar-width:none;
  }
  .sw__pills::-webkit-scrollbar,
.pg-gallery__tabs::-webkit-scrollbar,
#services .capabilities-v2__grid::-webkit-scrollbar,
#process .design-process__steps::-webkit-scrollbar,
#partnerships .partnerships__cards::-webkit-scrollbar { display:none; }

  .sw-pill,
  .pg-gallery__tabs button {
    touch-action:manipulation;
  }

  /* Capabilities: swipeable full-card previews rather than a long stack. */
  #services .capabilities-v2__grid {
    display:flex !important;
    grid-template-columns:none !important;
    width:calc(100% + (var(--mobile-gutter) * 2)) !important;
    margin-inline:calc(var(--mobile-gutter) * -1) !important;
    padding:10px var(--mobile-gutter) 20px !important;
    gap:12px !important;
    overflow-x:auto !important;
    scroll-padding-inline:var(--mobile-gutter);
    scroll-snap-type:x mandatory;
    scrollbar-width:none;
    -webkit-overflow-scrolling:touch;
  }
  #services .capability-card-v2--video,
  #services .capability-card-v2--video:last-child {
    flex:0 0 min(84vw,380px) !important;
    width:auto !important;
    max-width:none !important;
    margin:0 !important;
    scroll-snap-align:center;
    scroll-snap-stop:always;
  }

  /* Design Process keeps all four steps available in one horizontal story. */
  #process .design-process__story {
    height:auto;
  }
  #process .design-process__stage {
    inset:auto !important;
    height:auto;
    padding:64px var(--mobile-gutter) 72px;
  }
  #process .design-process__intro {
    gap:10px;
    padding:0 0 24px;
  }
  #process .design-process__intro > * {
    margin:0;
  }
  #process .design-process__steps {
    padding:2px var(--mobile-gutter) 14px;
    scroll-padding-inline:var(--mobile-gutter);
    scrollbar-width:none;
  }
  #process .design-process__step {
    flex:0 0 min(84vw,420px);
    background:#ededed;
    scroll-snap-stop:always;
  }
  #process .design-process__step.is-active {
    background:#fff !important;
    box-shadow:inset 0 0 0 1px #d4d4d4 !important;
  }
  #process .design-process__step:not(.is-active) .design-process__number,
  #process .design-process__step:not(.is-active) .design-process__visual,
  #process .design-process__step:not(.is-active) .design-process__copy {
    opacity:.72 !important;
  }
    #process .design-process__visual {
    height:210px;
    min-height:0;
  }
  
  /* Outcome cards and testimonials remain browsable without four screenfuls. */
  #partnerships .partnerships__cards {
    display:flex;
    grid-template-columns:none;
    padding:8px var(--mobile-gutter) 18px !important;
    scroll-padding-inline:var(--mobile-gutter);
  }
  #client-value .value-card,
  #partnerships .partnership-card {
    width:auto;
    min-width:0;
    scroll-snap-stop:always;
  }
  
  /* Toolkit preserves its bento character in a compact two-column system. */
  
  #toolkit .toolkit-tile {
    grid-area:auto !important;
    grid-column:auto !important;
    width:auto !important;
    max-width:none !important;
    min-height:150px !important;
    height:auto !important;
    transform:none !important;
  }

  /* Playground touch targets and media proportions. */
  .pg-gallery__navrow {
    display:grid !important;
    grid-template-columns:1fr !important;
  }
  .pg-gallery__summary {
    min-height:2.8em;
    text-align:left !important;
  }

  /* Experience metrics stay scannable in one compact row. */
  .exp__counters {
    grid-template-columns:repeat(3,minmax(0,1fr));
    grid-template-rows:auto minmax(150px,auto);
  }
  
      .counter__value {
    letter-spacing:-.07em !important;
  }
  .exp__row {
    grid-template-columns:1fr;
    gap:8px;
    padding-block:18px !important;
  }

  /* FAQ and contact controls remain comfortably tappable. */
  .accordion--faq .accordion__head {
    min-height:64px;
    padding:14px 0 !important;
  }
    .accordion--faq .accordion__icon {
    flex:0 0 36px !important;
    height:36px !important;
  }
  
}

@media (max-width:430px) {
  #toolkit .toolkit-tile { min-height:138px !important; }

}

@media (max-width:960px) and (orientation:landscape) and (max-height:620px) {
  #services,
  #client-value,
  #toolkit,
  #perspective,
  #partnerships,
  #experience,
  #insights,
  #contact { padding-block:56px !important; }
  #services .capability-card-v2--video { flex-basis:min(42vw,360px) !important; }
  #process .design-process__step {
    flex-basis:min(58vw,420px) !important;
    min-height:440px !important;
  }
}

/* v146 — clean section edges and a flatter editorial contact finish. */
#experience,
#experience .exp__education,
#experience .exp__list--education,
#experience .exp__list--education .exp__row:last-child {
  border-bottom:0 !important;
}

#insights .accordion--faq .accordion__item:last-child {
  border-bottom:0 !important;
}

@media (hover:none) and (pointer:coarse) {
  .value-card:hover,
  .partnership-card:hover,
  .toolkit-tile:hover,
  .capability-card-v2--video:hover {
    transform:none !important;
  }
}

/* Playground follows the same editorial header and filter system as
   Selected Work: title and filters share one row, supporting copy sits
   beneath, and both sections use identical control dimensions. */
#perspective {
  display:grid;
  grid-template-rows:auto auto auto;
  column-gap:clamp(28px,4vw,72px);
}
#perspectiveTitle {
  grid-column:1;
  grid-row:1;
  align-self:start;
  max-width:20ch;
}
#perspective .pg-gallery__navrow { display:contents; }
#perspective .pg-gallery__tabs {
  grid-column:2;
  grid-row:1;
  align-self:start;
  display:grid;
}
#perspective .pg-gallery__tabs button {
  min-width:0;
  height:36px;
}
#perspective .pg-gallery__summary {
  grid-column:1 / -1;
  grid-row:2;
  justify-self:start;
}
#perspective .pg-gallery__stage {
  grid-column:1 / -1;
  grid-row:3;
  width:100%;
}

@media (max-width:900px) {
  #perspective {
    grid-template-rows:auto auto auto auto;
    column-gap:0;
  }
  #perspectiveTitle {
    grid-column:1;
    grid-row:1;
  }
  #perspective .pg-gallery__tabs {
    grid-column:1;
    grid-row:2;
  }
  #perspective .pg-gallery__summary {
    grid-column:1;
    grid-row:3;
  }
  #perspective .pg-gallery__stage {
    grid-column:1;
    grid-row:4;
  }
}

@media (max-width:760px) {
  #perspective .pg-gallery__tabs {
    display:flex !important;
  }
    #perspective .pg-gallery__summary {
    min-height:0;
  }
  }

/* Annotation pass — cleaner capability, work, and process surfaces. */
#services .capability-card-v2,
#services .capability-card-v2--video,
#services .capability-card-v2:hover {
  background:#fff !important;
}

#process .design-process__step.is-active {
  border:0 !important;
}

@media (min-width:901px) {
  #work .sw__head {
    display:grid;
    grid-template-columns:minmax(0,1fr) minmax(360px,min(43vw,520px));
    grid-template-rows:auto auto;
    column-gap:clamp(28px,4vw,72px);
  }
    #work .sw__title {
    grid-column:1;
    grid-row:1;
  }
  #work .sw__support {
    grid-column:2;
    grid-row:1;
  }
  #work .sw__pills {
    position:static;
    grid-column:1 / -1;
    grid-row:2;
    justify-self:end;
    margin-top:clamp(28px,3.5vw,44px);
  }
  }

/* Final header system: one Design Process rhythm across all paired sections. */
#work .sw__head,
#toolkit .toolkit__head,
#partnerships .editorial-head {
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(18rem,.46fr);
  justify-content:normal;
  column-gap:clamp(2rem,8vw,10rem);
  row-gap:clamp(.8rem,1.4vw,1.4rem);
  text-align:left;
}

#work .sw__title,
#services #capabilitiesTitle,
#toolkit #toolkitTitle,
#perspective #perspectiveTitle,
#partnerships #partnershipsTitle,
#contact #contactTitle {
  max-width:12ch !important;
  margin:0 !important;
  text-align:left !important;
}

#work .sw__support,
#services .help__sub,
#client-value .client-value__lede,
#toolkit .toolkit__lede,
#perspective .pg-gallery__summary,
#partnerships .editorial-head > p {
  align-self:end !important;
  justify-self:end !important;
  text-align:left !important;
}

#toolkit .toolkit__head > div {
  display:block;
}

@media (min-width:901px) {
  #work .sw__head {
    grid-template-rows:auto auto;
  }
    #work .sw__title {
    grid-column:1;
    grid-row:1;
  }
  #work .sw__support {
    grid-column:2;
    grid-row:1;
  }
  #work .sw__pills {
    position:static;
    grid-column:1 / -1;
    grid-row:2;
    justify-self:end;
    margin-top:clamp(28px,3.5vw,44px);
  }

  #perspective {
    grid-template-columns:minmax(0,1fr) minmax(18rem,.46fr);
    grid-template-rows:auto auto auto;
    column-gap:clamp(2rem,8vw,10rem);
  }
  #perspective #perspectiveTitle {
    grid-column:1;
    grid-row:1;
  }
  #perspective .pg-gallery__summary {
    grid-column:2;
    grid-row:1;
  }
  #perspective .pg-gallery__tabs {
    grid-column:1 / -1;
    grid-row:2;
  }
  #perspective .pg-gallery__stage {
    grid-column:1 / -1;
    grid-row:3;
  }
}

@media (max-width:900px) {
  #toolkit .toolkit__head,
#partnerships .editorial-head {
    grid-template-columns:minmax(0,1fr);
    align-items:start;
    gap:14px;
  }
  #services .help__sub,
#client-value .client-value__lede,
#toolkit .toolkit__lede,
#partnerships .editorial-head > p {
    justify-self:start !important;
    margin:0 !important;
  }
  #perspective {
    grid-template-rows:auto auto auto auto;
    column-gap:0;
  }
  #perspective #perspectiveTitle {
    grid-column:1;
    grid-row:1;
  }
  #perspective .pg-gallery__summary {
    grid-column:1;
    grid-row:2;
    justify-self:start !important;
  }
  #perspective .pg-gallery__tabs {
    grid-column:1;
    grid-row:3;
  }
  #perspective .pg-gallery__stage {
    grid-column:1;
    grid-row:4;
    margin-top:28px;
  }
}
/* ===== end codex-final-pass-v2.css ===== */

/* ===== begin codex-annotations-v147.css ===== */
/* Final annotation overrides: loaded after the accumulated portfolio styles. */
@media (min-width:901px) {
  #work .sw__head,
#toolkit .toolkit__head,
#partnerships .editorial-head {
    grid-template-columns:minmax(0,1fr) minmax(24rem,.68fr) !important;
    align-items:center !important;
  }

  #work .sw__support,
#services .help__sub,
#process .design-process__lede,
#client-value .client-value__lede,
#toolkit .toolkit__lede,
#perspective .pg-gallery__summary,
#partnerships .editorial-head > p {
    align-self:center !important;
    justify-self:end !important;
    max-width:48ch !important;
    display:-webkit-box;
    overflow:hidden;
    -webkit-box-orient:vertical;
    -webkit-line-clamp:2;
  }

  #work .sw__pills {
    grid-column:1 / -1 !important;
    justify-self:center !important;
    margin-top:clamp(20px,2.6vh,30px) !important;
  }

  #work .sw__inner {
    padding-bottom:18px;
  }

  #work .sw__main {
    min-height:0;
  }

  #work .sw__thumb {
    width:min(42vw,650px) !important;
    max-height:min(48vh,470px) !important;
  }

  #work .sw__pager {
    min-height:24px;
    padding-top:2px;
  }

  #perspective #perspectiveTitle,
  #perspective .pg-gallery__summary {
    align-self:center !important;
  }

  }

@media (min-width:1180px) {
  #toolkit #toolkitTitle,
  #partnerships #partnershipsTitle,
  #contact #contactTitle {
    max-width:none !important;
    white-space:nowrap !important;
  }
}

/* Latest browser annotations. */
#experience > .exp__list > .exp__row:last-child,
#partnerships,
#perspective {
  border-bottom:0 !important;
}

.pg-local-preview {
  width:100%;
  height:100%;
  object-fit:cover;
  background:#141414;
}

@media (min-width:901px) {
  #toolkit .toolkit__head {
    justify-content:center !important;
    text-align:center !important;
  }

  #toolkit .toolkit__head > div,
  #toolkit .toolkit__lede {
    width:100%;
    justify-self:center;
    text-align:center;
  }

  #toolkit #toolkitTitle {
    white-space:nowrap !important;
  }
}

/* opacity deliberately NOT !important: an important declaration outranks
   both the inline hidden state and Motion's WAAPI entrance, which left
   the cards visible before the Approach heading had revealed. */
#process .design-process__step:not(.is-active) {
  opacity:1;
  background:#e4e4e4;
}

#process .design-process__step:not(.is-active) .design-process__number,
#process .design-process__step:not(.is-active) .design-process__visual,
#process .design-process__step:not(.is-active) .design-process__copy {
  opacity:1 !important;
  filter:none !important;
}

@media (max-width:900px) {
  #work .sw__support,
#services .help__sub,
#process .design-process__lede,
#client-value .client-value__lede,
#toolkit .toolkit__lede,
#perspective .pg-gallery__summary,
#partnerships .editorial-head > p {
    display:block;
    overflow:visible;
    -webkit-line-clamp:unset;
  }

  }
/* ===== end codex-annotations-v147.css ===== */

/* ===== begin ui-consistency.css ===== */
/*
 * Portfolio UI consistency layer
 * Keeps shared editorial typography, controls, radii and motion aligned
 * without flattening the bespoke hero, work, process or media choreography.
 */
:root {
  /* These were a second, hand-tuned copy of the scale in tokens.css —
     different ink, different muted, a 48px title against the token
     scale's 64px — and they were applied with !important, so the
     duplicate always won. They are now pure aliases: tokens.css is the
     single source of truth and this layer only decides WHERE the scale
     is applied, never WHAT the values are. */
  --ui-ink: var(--ink-strong);
  --ui-muted: var(--ink-muted);
  --ui-paper: var(--surface-page);
  --ui-surface: var(--surface-card);
  --ui-line: var(--line);
  --ui-accent: var(--accent);

  --ui-title: var(--fs-section-title);
  --ui-card-title: var(--fs-card-title);
  --ui-body: var(--fs-body);
  --ui-small: var(--fs-small);
  --ui-label: var(--fs-label);

  --ui-radius-card: var(--radius-card);
  --ui-radius-panel: var(--radius-panel);
  --ui-radius-control: var(--radius-control);
  --ui-gap: clamp(12px, 1.35vw, 20px);
  --ui-heading-gap: clamp(22px, 3vw, 40px);
  --ui-ease: var(--ease-out);

  /* Single source of truth for the gap between sections (top and bottom,
     mobile and desktop). No dividers — spacing alone separates sections. */
  --section-space: clamp(28px, 4vw, 52px);
  --ui-fast: var(--dur-fast);
  --ui-medium: var(--dur-mid);
}

/* One editorial scale for section-level headings. */
:is(
  #work .sw__title,
  #services #capabilitiesTitle,
  #process #processTitle,
  #client-value #clientValueTitle,
  #toolkit #toolkitTitle,
  #perspective #perspectiveTitle,
  #partnerships #partnershipsTitle,
  #experience .section__label,
  #experience #educationTitle,
  #insights > .section__label,
  #contact #contactTitle
) {
  font-size: var(--ui-title) !important;
  line-height: 0.98 !important;
  letter-spacing: -0.055em !important;
  text-wrap: balance;
}

/* Supporting copy follows the same readable two-line rhythm on wide screens. */
:is(
  #work .sw__support,
  #services .help__sub,
  #process .design-process__lede,
  #client-value .client-value__lede,
  #toolkit .toolkit__lede,
  #perspective .pg-gallery__summary,
  #partnerships .editorial-head > p,
  #contact .contact-light__head > p
) {
  color: var(--ui-muted) !important;
  letter-spacing: -0.02em !important;
  text-wrap: pretty;
}

/* Selected Work and Playground now share one segmented-control language. */

:is(#work .sw-pill, #perspective .pg-gallery__tabs button) {
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  border: 0 !important;
  background: var(--ui-surface) !important;
  color: #626662 !important;
  font-family: var(--font-body) !important;
  letter-spacing: -0.02em !important;
  transition:
    color var(--ui-fast) ease,
    background-color var(--ui-fast) ease,
    box-shadow var(--ui-fast) ease,
    transform var(--ui-medium) var(--ui-ease) !important;
}

:is(#work .sw-pill, #perspective .pg-gallery__tabs button):is(:hover, :focus-visible) {
  color: var(--ui-ink) !important;
  transform: translate3d(0, -2px, 0);
}

:is(#work .sw-pill.is-active, #perspective .pg-gallery__tabs button.is-active) {
  background: #212121 !important;
  color: #ffffff !important;
}

:is(#work .sw-pill, #perspective .pg-gallery__tabs button):focus-visible {
  outline: 2px solid var(--ui-accent) !important;
  outline-offset: 3px !important;
}

/* Obsolete: sized for the old "Aerial Photography" label. Now reads
   "Explore", so it uses the standard control padding like every other tab. */

/* Shared geometry: two radii are enough for the complete editorial system. */
:is(
  #services .capability-card-v2,
  #process .design-process__step,
  #client-value .client-value__card,
  #client-value .value-card,
  #toolkit .toolkit-tile,
  #partnerships .partnership-card,
  #perspective .pg-track,
  #perspective .pg-panel figure
) {
  border-radius: var(--ui-radius-card);
}

:is(
  #contact .contact-light__card,
  #perspective .pg-aerial__viewer,
  #perspective .pg-aerial__portrait
) {
  border-radius: var(--ui-radius-panel) !important;
}

/* Repeated card titles and descriptions use a predictable local scale. */
:is(
  #process .design-process__copy h3,
  #client-value .client-value__card h3,
  #client-value .value-card h3,
  #toolkit .toolkit-tile h3,
  #partnerships .partnership-card strong
) {
  font-weight: 500 !important;
  letter-spacing: -0.035em !important;
}

:is(
  #process .design-process__copy p,
  #client-value .client-value__card p,
  #client-value .value-card p,
  #toolkit .toolkit-tile p
) {
  letter-spacing: -0.02em !important;
}

/* Use one restrained lift treatment only where hover is genuinely available. */
@media (hover: hover) and (pointer: fine) {
  :is(
    #client-value .client-value__card,
    #client-value .value-card,
    #toolkit .toolkit-tile
  ) {
    transition:
      transform var(--ui-medium) var(--ui-ease),
      border-color var(--ui-fast) ease,
      box-shadow var(--ui-medium) var(--ui-ease) !important;
  }

  :is(
    #client-value .client-value__card,
    #client-value .value-card,
    #toolkit .toolkit-tile
  ):hover {
    transform: translate3d(0, -4px, 0);
  }
}

/* Tablet: preserve the editorial hierarchy while removing forced one-line widths. */
@media (max-width: 900px) {
  :root {
    /* --fs-section-title already clamps down to 2.25rem on narrow
       viewports; re-declaring a second ceiling here is what pinned every
       section head to 48px. Only the heading gap is tablet-specific. */
    --ui-heading-gap: clamp(18px, 4vw, 30px);
  }

  :is(
    #work .sw__title,
    #services #capabilitiesTitle,
    #process #processTitle,
    #client-value #clientValueTitle,
    #toolkit #toolkitTitle,
    #perspective #perspectiveTitle,
    #partnerships #partnershipsTitle,
    #experience .section__label,
    #experience #educationTitle,
    #insights > .section__label,
    #contact #contactTitle
  ) {
    max-width: none !important;
    white-space: normal !important;
  }

  :is(
    #work .sw__support,
    #services .help__sub,
    #process .design-process__lede,
    #client-value .client-value__lede,
    #toolkit .toolkit__lede,
    #perspective .pg-gallery__summary,
    #partnerships .editorial-head > p,
    #contact .contact-light__head > p
  ) {
    max-width: 48ch !important;
  }
}

/* Mobile filters are touch-sized, horizontally scrollable and never wrap labels. */
@media (max-width: 760px) {
  :root {
    --ui-radius-card: 18px;
    --ui-radius-panel: 20px;
  }

  /* Retired with the rest of the scrolling filter strip — the phone
     treatment for these rows is the shared component in
     css/editorial-polish.css. The snap/flex floors here were the last
     thing holding the old layout together, and #perspective #pgTabAerial
     in particular is a two-ID selector, so its 166px floor survived every
     container-scoped override and stretched Photography to four times the
     width of its neighbours. Only the tap-target minimum is worth keeping,
     and the shared rule already sets it. */
  :is(#work .sw-pill, #perspective .pg-gallery__tabs button) {
    touch-action: manipulation;
  }

  .mobnav__links a span {
    font-size: clamp(2rem, 9vw, 2.75rem);
    font-weight: 600;
    line-height: 1.06;
  }
}

/* Motion/accessibility and low-power defaults. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
  }

  :is(
    #work .sw-pill,
    #perspective .pg-gallery__tabs button,
    #client-value .client-value__card,
    #client-value .value-card,
    #toolkit .toolkit-tile
  ) {
    animation: none !important;
    transition-duration: var(--dur-1) !important;
    transform: none !important;
  }
}

/* =====================================================================
   MOBILE OVERHAUL (2026-07-23)
   Fixes: Selected Work head layout, vertical card stacks, section
   separators, floating back-to-top, Lucide menu icon.
   Everything below is scoped to <=900px so desktop choreography is
   untouched.  ui-consistency.css loads last, so these win the cascade.
   ===================================================================== */
@media (max-width: 900px) {

  /* ---- 1/2/3 · SELECTED WORK head ------------------------------------
     A no-media-query rule in codex-final-pass-v2.css forces
     `.sw__head{display:grid …!important}`, which collapses the intro to a
     ~15px column on phones and drops the pills on top of the subtitle.
     Rebuild it as a clean vertical stack. */
  #work .sw__head {
    display: flex;
    flex-direction: column;
    align-items: stretch !important;
    grid-template-columns: none !important;
    grid-template-rows: none;
    column-gap: 0;
    row-gap: 0;
    gap: 16px;
    text-align: left;
  }
  #work .sw__intro {
    display: block;
    max-width: none;
  }
  /* 2 · Title on a single line */
  #work .sw__title {
    grid-column: auto !important;
    grid-row: auto !important;
    width: 100% !important;
  }
  /* 3 · Subtitle: full width, sits directly under the title */
  #work .sw__support {
    grid-column: auto !important;
    grid-row: auto !important;
    justify-self: start !important;
    align-self: start !important;
    margin: 10px 0 0 !important;
    text-align: left !important;
  }
  /* 1 · All four filters visible upfront, two rows, no horizontal swipe */
  #work .sw__pills {
    position: static !important;
    margin: 4px 0 0 !important;
    justify-self: stretch !important;
  }
  
  /* ---- 5/6 · Horizontal card rows -> vertical stacks ------------------ */
  #services .capabilities-v2__grid,
#process .design-process__steps,
#partnerships .partnerships__cards {
    display: flex;
    flex-direction: column;
    /* Desktop uses negative side-margins + own padding for a full-bleed
       horizontal rail; on mobile that leaves a ~40px gap on the right.
       Neutralise it so cards fill the section content width edge-to-edge. */
    width: auto !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
    overflow: visible !important;
  }
  #services .capability-card-v2,
  #client-value .value-card,
  #partnerships .partnership-card,
  #process .design-process__step {
    flex: 0 0 auto !important;
    width: 100%;
    min-width: 0;
    scroll-snap-align: none !important;
  }
  /* Expertise cards are icon + text cards: stack the icon visual over the
     body, natural height. */
  #services .capability-card-v2 {
    display: block !important;
    height: auto !important;
    min-height: 0 !important;
    aspect-ratio: auto !important;
    flex: 0 0 auto !important;
  }

  /* Design Process: stack number/visual/copy per step, kill the filmstrip */
  #process .design-process__stage {
    align-items: stretch;
  }
  #process .design-process__step {
    display: grid;
    grid-template-columns: 1fr;
    gap: 12px;
  }
  #process .design-process__visual {
    width: 100%;
    height: auto;
  }
  #process .design-process__visual video {
    width: 100% !important;
    height: 100% !important;
    object-fit: cover !important;
  }

  /* ---- 7 · Section rhythm: trim negative space, add hairline separators
     (same hairline used between the two footer statements). */
  #services,
  #process,
  #about-me,
  #client-value,
  #toolkit,
  #perspective,
  #partnerships,
  #experience,
  #insights,
  #contact {
    /* One spacing formula for every section, top and bottom, at every width. */
    padding-top: var(--section-space) !important;
    /* No dividers between sections — spacing alone separates them. */
    border-top: 0 !important;
    border-bottom: 0 !important;
  }

  /* Process intro carries its own top padding once the section pad shrinks */
  #process .design-process__intro {
    padding-top: 0;
  }
}

/* ---- 4 · CASE STUDY: only the close button, no chapter navigation
   (applies on every viewport, per request). */
.cs .cs__rail {
  display: none;
}

/* ---- 8 · Floating "back to top" button ------------------------------- */
.to-top {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 120;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px);
  transition: opacity var(--dur-2) ease, transform var(--dur-2) ease, visibility var(--dur-2) ease;
}
.to-top.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.to-top:hover {
  background: #000000;
}
.to-top:focus-visible {
  outline: 2px solid var(--ui-accent, #4dce16);
  outline-offset: 3px;
}
.to-top svg {
  width: 22px;
  height: 22px;
}
/* Never overlap the open case study (overlay z-index 200+). */
html.cs-open .to-top {
  display: none !important;
}

/* ---- 9 · Lucide menu icon in the nav toggle -------------------------- */
/* NB: do not set `display` here — the base rule keeps the toggle hidden on
   desktop (shown only < 900px). Only center the icon. */
.nav__toggle {
  justify-content: center;
}
.nav__toggle svg {
  width: 26px;
  height: 26px;
  color: #ffffff; /* hamburger icon always white */
  transition: color var(--dur-2) ease;
}
html.mobnav-open .nav__toggle svg {
  color: #ffffff !important;
}

/* =====================================================================
   PLAYGROUND (#perspective) — mobile responsive pass (2026-07-23)
   The stage uses fixed heights (720px aerial, etc.) with absolutely
   positioned panels, which leaves large empty space and forces odd
   aspect ratios on phones. Below: let the stage flow to content, put
   filters in a 2x2 grid (no horizontal swipe), and give every panel a
   balanced vertical layout.
   ===================================================================== */
@media (max-width: 900px) {
  /* Nav row: title/filters stacked over the summary line */
  #perspective .pg-gallery__navrow {
    display: flex !important;
    flex-direction: column !important;
    gap: 16px !important;
  }

  /* Filters: 2x2 grid, full width, no horizontal scroll (matches Selected Work) */
  #perspective .pg-gallery__tabs {
    display: grid !important;
    scroll-snap-type: none !important;
  }
  #perspective .pg-gallery__tabs button {
    scroll-snap-align: none !important;
  }
  /* Order: title (h2) → subtitle → filters. Summary sits above the tabs. */
  #perspective .pg-gallery__summary {
    order: -1 !important;
  }
  
  /* Stage flows to the active panel's real height — kills the dead space */
  #perspective .pg-gallery__stage,
  #perspective .pg-gallery__stage[data-active="aerial"],
  #perspective .pg-gallery__stage[data-active="sound"],
  #perspective .pg-gallery__stage[data-active="motion"],
  #perspective .pg-gallery__stage[data-active="travel"] {
    height: auto !important;
    min-height: 0 !important;
  }
  #perspective .pg-panel {
    position: relative;
    inset: auto !important;
    filter: none !important;
    transform: none !important;
    opacity: 1 !important;
  }
  #perspective .pg-panel:not(.is-active) {
    display: none !important;
  }
  #perspective .pg-panel.is-active {
    visibility: visible !important;
  }

  /* Aerial: landscape film (16:9) on top, portrait reel (9:16) centered below */
  #perspective .pg-panel--aerial {
    flex-direction: column;
    grid-template-columns: none;
  }
  /* Both films share the full column width (equal width, stacked) */
  #perspective .pg-aerial__viewer {
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    align-self: stretch;
    flex: 0 0 auto;
  }
  #perspective .pg-aerial__portrait {
    width: 100%;
    height: auto;
    aspect-ratio: 9 / 16;
    align-self: stretch;
    flex: 0 0 auto;
  }

  /* Sound + Motion: vertical stacks, no horizontal swipe */
  #perspective .pg-panel--sound,
  #perspective .pg-panel--motion {
    flex-direction: column;
    grid-template-columns: none;
    padding: 0;
    overflow: visible;
    scroll-snap-type: none;
  }
  #perspective .pg-panel--sound .pg-track,
  #perspective .pg-panel--motion figure {
    flex: 0 0 auto;
    width: 100%;
    scroll-snap-align: none;
  }
  /* These recordings are portrait (912x1178). Forcing a 16/10 landscape
     card on to them cropped away most of the composition — only a thin
     band through the middle survived, which is what made the tab look
     like a flat panel rather than three motion studies. The card takes the
     footage's own ratio instead, so nothing is cut. */
  #perspective .pg-panel--motion figure {
    aspect-ratio: 912 / 1178;
  }
  /* Sound track internals are absolutely positioned, so the card has no
     intrinsic height once the fixed grid cell is gone — give it one. */
  #perspective .pg-panel--sound .pg-track {
    aspect-ratio: 4 / 3;
    min-height: 300px;
    height: auto;
  }
  #perspective .pg-panel--sound .pg-track__number {
    font-size: 5rem;
  }

  /* The travel mosaic's own responsive placements live in
     css/playground-all.css. The square-tile override that used to sit here
     forced grid-column/grid-row to auto and squared every cell, which
     flattened those placements below 900px. */
}

/* ---- Landscape (horizontal) film: rotate to fill the screen in fullscreen
   on a portrait phone, so it plays in true horizontal view. Applied by
   playground-media.js via the .pg-media-expanded--rotate class. --------- */
.pg-media-expanded.pg-media-expanded--rotate {
  top: 50% !important;
  left: 50% !important;
  width: 100dvh !important;
  height: 100vw !important;
  transform: translate(-50%, -50%) rotate(90deg) !important;
  transform-origin: center center !important;
  border-radius: var(--radius-media, 16px) !important;
}
.pg-media-expanded--rotate .pg-youtube-player,
.pg-media-expanded--rotate video {
  position: absolute !important;
  top: 0 !important;
  left: 0 !important;
  width: 100% !important;
  height: 100% !important;
  transform: none !important;
  object-fit: contain !important;
  background: #000 !important;
}
/* Keep the collapse control reachable/upright in the rotated frame */
.pg-media-expanded--rotate .pg-aerial__fullscreen {
  transform: rotate(-90deg) !important;
}

/* ---- Mobile: hide the header while scrolling down, reveal on scroll up.
   JS toggles .nav--hidden; the transform is scoped to mobile so desktop is
   unaffected regardless of the class. */
@media (max-width: 900px) {
  .nav {
    transition: transform var(--dur-3) var(--ease-out),
      background-color var(--dur-3) var(--ease-out),
      box-shadow var(--dur-3) var(--ease-out) !important;
  }
  .nav.nav--hidden {
    transform: translateY(-160%) !important;
  }
  /* Never keep it hidden while the mobile menu is open */
  html.mobnav-open .nav.nav--hidden {
    transform: none !important;
  }
}

/* ---- Desktop: tighten the section rhythm. Sections were carrying ~114px
   of padding top and bottom, which stacked to ~228px of dead space between
   every section. ---------------------------------------------------------- */
@media (min-width: 901px) {
  #services,
  #process,
  #about-me,
  #client-value,
  #toolkit,
  #perspective,
  #partnerships,
  #experience,
  #insights {
    border-bottom: 0 !important;
  }

}

/* ---- Universal tracking: -2% for body copy, labels and control text.
   Headings keep their own tighter display tracking. ---------------------- */
body,
p,
li,
dd,
dt,
small,
figcaption,
label,
blockquote,
input,
textarea,
select,
button,
a,
.mono-label,
.nav__links a,
.mobnav__links a,
.sw-pill,
.pg-gallery__tabs button,
.nav__talk,
.to-top,
.exp__dates,
.counter__key,
.counter__value {
  letter-spacing: -0.02em;
}

/* ---- Header: everything on one optical centre line -------------------- */
.nav {
  display: flex;
  align-items: center;
}
/* NB: never set `display` here — the base rule hides these links below 900px
   so the mobile menu takes over. Setting display:flex globally (as this rule
   previously did) leaks the desktop nav onto mobile. Align only. */
.nav__links {
  align-items: center;
  /* push the whole tab group (and the CTA) to the right edge */
  margin-left: auto !important;
}
/* ---- One nav switch-over point. Previously the desktop links and the
   hamburger were both visible around 1024px (iPad landscape) because they
   were governed by different breakpoints. Below 1024 = menu, above = links. */
@media (max-width: 1024px) {
  .nav__links {
    display: none;
  }
  .nav__toggle {
    display: flex;
  }
}
@media (min-width: 1025px) {
  .nav__links {
    display: flex;
  }
  .nav__toggle {
    display: none;
  }
}

/* ---- Touch ergonomics (coarse pointers: phones + tablets) -------------- */
@media (pointer: coarse) {
  a,
  button,
  [role="tab"],
  [role="button"],
  input,
  select,
  textarea,
  summary {
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation; /* removes the legacy 300ms tap delay */
  }

  /* Meet the 44px minimum touch target. */
  .mobnav__links a,
.nav__talk,
#work .sw-pill,
#perspective .pg-gallery__tabs button,
.cs__close,
.to-top,
.pg-aerial__fullscreen,
.sw__pager button {
    min-height: 44px !important;
  }
  .nav__toggle {
    min-width: 44px !important;
    min-height: 44px !important;
  }
}

/* Hover effects must not stick after a tap on touch devices. */
@media (hover: none) {
  #services .capability-card-v2:hover,
#partnerships .partnership-card:hover,
#work .sw-pill:hover,
#perspective .pg-gallery__tabs button:hover,
.nav__talk:hover,
.to-top:hover,
#toolkit .toolkit-tile:hover {
    transform: none !important;
  }
}

/* ---- Tablet: 3 capability columns get cramped; use 2 up to 1200px ------ */
@media (min-width: 901px) and (max-width: 1200px) {
  #services .capabilities-v2__grid {
    display: grid !important;
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: clamp(14px, 1.6vw, 22px) !important;
    overflow: visible !important;
  }
  #services .capability-card-v2 {
    width: auto !important;
  }
}
.nav__links a {
  display: inline-flex;
  align-items: center;
  line-height: 1;
}
.nav__brand {
  display: flex;
  align-items: center;
}
/* NB: never set `display` here — the base rule keeps the hamburger hidden on
   desktop and only shows it below 900px. Centre it without unhiding it. */
.nav__toggle {
  justify-content: center;
}
.nav__brand svg {
  display: block;
}

/* ---- Compositing hints: keep scroll-driven elements on the GPU so they
   stay in the 60fps budget instead of triggering paint on the main thread. */
.nav,
.to-top,
#services .capability-card-v2 {
  backface-visibility: hidden;
}
.nav,
.to-top {
  will-change: transform;
}

/* ---- Header: "Let's Talk" CTA (white bg, black text) ------------------ */
.nav__links .nav__talk {
  justify-content: center;
  margin-left: clamp(10px, 1vw, 20px);
  padding: 10px 20px;
  font-weight: 500;
  white-space: nowrap;
  box-shadow: 0 2px 10px rgba(17, 17, 17, 0.14);
  transition: background-color var(--dur-2) ease, color var(--dur-2) ease, transform var(--dur-3) var(--ui-ease, ease);
}
.nav__links .nav__talk:hover,
.nav__links .nav__talk:focus-visible {
  background: #f4f4f4 !important;
  color: #000000 !important;
}

/* Menu links use the body face (Geist) at every width — this was previously
   only applied under 760px, so tablets fell back to Zalando Sans. */
.mobnav__links a,
.mobnav__links a span {
  font-family: var(--font-body);
  letter-spacing: -0.02em !important;
}

/* "Let's Talk" lives in the menu footer, bottom-left above the email. */
.mobnav__foot {
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
}
.mobnav__foot .mobnav__talk {
  order: -1; /* sits above the email address */
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  align-self: flex-start;
  padding: 11px 22px;
  background: #ffffff;
  color: #0f0f0f;
  font-size: 0.9rem;
  font-weight: 500;
  line-height: 1;
  text-transform: none;
}
.mobnav__foot .mobnav__talk:hover {
  background: #f4f4f4 !important;
}

/* =====================================================================
   Mobile nav tray — full-bleed, no stroke, readable CTA
   ===================================================================== */

/* The dark-theme sweep below paints every link in the tray with
   --text-primary, which caught the one link that sits ON white and left
   "Let's Talk" as #f4f4f4 on #fff. The button keeps its own ink. */
:root[data-theme="dark"] .mobnav .mobnav__talk,
:root[data-theme="dark"] .mobnav__foot .mobnav__talk {
  color: #0f0f0f !important;
}

/* The tray was an inset card (78px/12px) with a 1px stroke, a 30px radius
   and a drop shadow — it read as a panel floating over the page rather
   than as the page's own menu. It now fills the viewport; the header sits
   above it (z-130 vs z-120) so the close control stays reachable. */
.mobnav {
  inset: 0 !important;
  border: 0;
  border-radius: var(--radius-card, 16px);
  box-shadow: none;
  padding-top: clamp(96px, 14vh, 132px);
  clip-path: inset(0 0 100% 0) !important;
}
.mobnav-open .mobnav {
  clip-path: inset(0 0 0% 0) !important;
}

/* ---- Hero name + designation: Geist semibold, -2% tracking ------------- */
.hero__titles,
.hero__titles span,
.hero__title-line,
.hero__title-text,
.hero__title-char {
  font-family: var(--font-body) !important;
  font-weight: 600 !important;
  letter-spacing: -0.02em !important;
  /* keep the authored Title Case — the base styles uppercase these */
  text-transform: none !important;
}
/* Name flush to FOLIO's left edge, designation flush to its right edge.
   Padding is set at runtime from the wordmark's measured box. */
.hero__titles {
  display: flex !important;
  justify-content: space-between !important;
  align-items: baseline !important;
  /* width auto so the runtime margins define the row's edges */
  width: auto !important;
  box-sizing: border-box !important;
}
.hero__titles > span:first-child {
  text-align: left !important;
}
.hero__titles > span:last-child {
  text-align: right !important;
}

/* ---- Header hides on scroll-down / returns on scroll-up (all viewports).
   JS toggles .nav--hidden; previously this transform was mobile-only. ---- */
.nav {
  transition: transform var(--dur-3) var(--ease-out),
    opacity var(--dur-3) var(--ease-out),
    background-color var(--dur-3) var(--ease-out),
    box-shadow var(--dur-3) var(--ease-out) !important;
  will-change: transform, opacity;
}
.nav.nav--hidden {
  transform: translate3d(0, -180%, 0) !important;
  opacity: 0 !important;
  pointer-events: none !important;
}

/* Work media transitions use two alternating layers. Keeping both layers
   promoted avoids full-cover repaints while momentum scrolling. */
#swThumb .sw__cover {
  backface-visibility: hidden;
  transform: translate3d(0, 0, 0);
  will-change: transform, opacity;
}
html.mobnav-open .nav.nav--hidden {
  transform: none !important;
  opacity: 1 !important;
  pointer-events: auto !important;
}

/* ---- Beyond: tighten the gap between filters and the media stage ------- */
#perspective .pg-gallery__navrow {
  margin-bottom: clamp(10px, 1.2vw, 16px) !important;
}

/* ---- Modern Product Stack: keep the lede beside the heading on desktop.
   It was wrapping underneath instead of sitting right at heading level. -- */
@media (min-width: 901px) {
  #toolkit .toolkit__head {
    display: grid !important;
    grid-template-columns: minmax(0, 1fr) minmax(18rem, 0.46fr) !important;
    align-items: end !important;
    column-gap: clamp(2rem, 8vw, 10rem) !important;
    row-gap: 0 !important;
  }
  #toolkit .toolkit__head > div {
    grid-column: 1;
    grid-row: 1;
  }
  #toolkit .toolkit__lede {
    grid-column: 2;
    grid-row: 1;
    align-self: end;
    margin: 0 0 0.35rem !important;
  }
}

/* ---- Experience: description sits right of the heading (desktop) ------- */
#experience .exp__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(18rem, 0.46fr);
  align-items: end;
  column-gap: clamp(2rem, 8vw, 10rem);
  row-gap: 10px;
}
/* The section label ships a ~49px bottom margin, which opened a large gap
   between "Experience" and its description. Let row-gap own that spacing. */
#experience .exp__head .section__label {
  margin-bottom: 0;
}
#experience .exp__head-desc {
  align-self: end;
}

/* Counters as three proper boxes; stacked vertically on mobile. */
#experience .exp__counters {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(12px, 1.4vw, 20px) !important;
  /* Drop the wrapper chrome — only the three cards should read as boxes.
     Also clear the inherited fixed rows, which left ~319px of empty grid
     below the cards. */
  border: 0 !important;
  border-radius: var(--radius-card, 16px);
  grid-template-rows: none !important;
}
#experience .exp__counters .counter {
  display: flex !important;
  flex-direction: column;
  justify-content: space-between;
  gap: 14px !important;
  min-height: 150px !important;
  border-radius: var(--ui-radius-card, 20px);
  background: rgba(255, 255, 255, 0.72);
}
@media (max-width: 900px) {
  #experience .exp__head {
    grid-template-columns: 1fr;
    /* breathing room after the text block, before the counters */
    margin-bottom: 44px;
  }
  #experience .exp__head-desc {
    max-width: none;
  }
  #experience .exp__counters {
    grid-template-columns: 1fr !important;
  }
  #experience .exp__counters .counter {
    min-height: 0 !important;
  }
}

/* ---- About section ---------------------------------------------------- */
/* Two columns: bottom-weighted copy on the left, tall portrait on the right. */

.about-v2 .about-v2__title {
  overflow: hidden; /* mask for the line reveal */
  margin: 0;
  font-family: var(--font-body);
  font-size: clamp(2.6rem, 6vw, 5.4rem) !important;
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.055em;
  color: var(--ui-ink, #222);
}
.about-v2 .about-v2__title span {
  display: block;
  will-change: transform;
}
.about-v2 .about-v2__lede {
  margin: 0 0 clamp(20px, 2.4vw, 32px);
  max-width: 30ch;
  color: var(--ui-ink, #111);
  font-family: var(--font-body);
  font-size: var(--fs-lede);
  font-weight: 400;
  line-height: 1.24;
  letter-spacing: -0.03em;
}

.about-v2 .about-v2__figure {
  position: relative;
  overflow: hidden;
  margin: 0;
  min-height: clamp(420px, 62vw, 780px);
  border-radius: clamp(20px, 2vw, 30px);
  /* neutral tone behind the portrait while it decodes */
  background: #e4e4e4;
}
.about-v2 .about-v2__figure img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---- Reveal choreography (fires when the section scrolls into view) ---- */
/* Reveal choreography is driven from JS via inline styles (see index.html).
   A CSS class-toggle approach was not reliably winning the cascade here, and
   inline styles are what the rest of this page's reveals already use. Only
   the structural bits the animation depends on live here. */
.about-v2 .about-v2__word {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}
.about-v2 .about-v2__word > i {
  display: inline-block;
  font-style: inherit;
}

@media (max-width: 900px) {

  .about-v2 .about-v2__title {
    order: 1;
  }
  .about-v2 .about-v2__figure {
    order: 2;
    min-height: 0;
    aspect-ratio: 3 / 4;
  }
  
  .about-v2 .about-v2__lede {
    max-width: none;
    font-size: var(--fs-lede);
  }

}

/* ---- Case study: honesty label above the metric row ------------------ */
.cs__disclaimer {
  margin: 0 0 clamp(18px, 2vw, 26px);
  padding-left: 12px;
  border-left: 2px solid var(--line-strong, rgba(28, 34, 29, 0.22));
  font-style: italic;
}

/* =====================================================================
   PERFORMANCE — will-change hygiene
   ---------------------------------------------------------------------
   Audit found 436 elements holding a compositor layer at rest. The count
   comes from per-character / per-word selectors (one hint x hundreds of
   glyphs), not from the containers that actually animate. will-change is
   a targeted hint for a known-imminent animation, not a baseline.
   Released at rest; re-applied only while animating.
   ===================================================================== */
.hero__title-char,
.sw-title-line,
.cs__hero-inner > *,
[class*="__char"],
[class*="__word"],
[class$="word"] {
  will-change: auto !important;
}

/* Aurora blobs: 18 layers held permanently. Hint only while the card is
   hovered, which is the only time they move. */
#partnerships .pc__aurora i {
  will-change: auto !important;
}
@media (hover: hover) and (pointer: fine) {
  #partnerships .partnership-card:hover .pc__aurora i {
    will-change: transform !important;
  }
}

/* =====================================================================
   QC NORMALISATION — resolves the 13 audited inconsistencies via tokens.
   Everything here reads from tokens.css. Do not hard-code values.
   ===================================================================== */

/* 1 · About title was 85.5px against 48px everywhere else. */
#about-me .about-v2__title {
  font-size: var(--fs-section-title) !important;
  letter-spacing: var(--track-tight) !important;
}

/* 2 · Experience lede tracked -1%; every other lede is -2%. */

/* 7 · About figure radius was 28.5px; all other media is 20px. */
.about-v2 .about-v2__figure {
  border-radius: var(--radius-media) !important;
}

/* 4/5/6 · One card contract: white surface, one border, one padding.
   (#partnerships is intentionally borderless — excluded by design.) */
#client-value .value-card,
#experience .exp__counters .counter,
#toolkit .toolkit-tile {
  background: var(--surface-card) !important;
  border-radius: var(--radius-card);
}
#client-value .value-card,
#experience .exp__counters .counter {
  padding: var(--card-pad) !important;
}

/* 10/11 · Two control sizes only: 40px standard, 48px primary. */
.nav__links .nav__talk {
  min-height: var(--control-h);
}
.to-top {
  min-height: var(--control-h-lg);
}
@media (pointer: coarse) {
  #work .sw-pill,
  #perspective .pg-gallery__tabs button,
  .nav__links .nav__talk {
    min-height: var(--control-h-touch) !important;
  }
}

/* 12 · Head -> content gap ranged 36-66px across five sections. */
#toolkit .toolkit__head,
#partnerships .editorial-head,
#experience .exp__head {
  margin-bottom: var(--head-gap);
}

/* 13 · Work and Beyond sat on a 22px gutter; everything else 20px. */
@media (max-width: 900px) {
  }

/* =====================================================================
   REVEAL SYSTEM — structural support for js/reveal.js
   ===================================================================== */
[data-reveal-line] {
  overflow: hidden;           /* the heading is the mask */
}

/* =====================================================================
   TRUSTED PARTNERSHIPS — testimonial grid with aurora bloom
   Borderless, shadowless. No lift on hover: only the aurora and the
   sibling dimming change, so nothing shifts position.
   ===================================================================== */
#partnerships .partnerships__cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  min-height: 0;
}
#partnerships .partnership-card {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  border: 0;
  background: #ffffff;
  backdrop-filter: none !important;
  transform: none !important;
  animation: none !important;
  transition: opacity var(--dur-3) ease !important;
}
#partnerships .partnership-card::before,
#partnerships .partnership-card::after { content: none !important; }

/* ---- aurora: three blurred blobs drifting on different cycles -------- */
#partnerships .pc__aurora {
  position: absolute;
  inset: 0;
  overflow: hidden;
  opacity: 0;
  border-radius: inherit;
  pointer-events: none;
  transition: opacity var(--dur-3) ease;
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 55%, rgba(0,0,0,.55) 78%, transparent 96%);
          mask-image: linear-gradient(to top, #000 0%, #000 55%, rgba(0,0,0,.55) 78%, transparent 96%);
}
#partnerships .pc__aurora i {
  position: absolute;
  bottom: -30%;
  width: 118%;
  height: 120%;
  border-radius: 50%;
  filter: blur(46px);
  will-change: transform;
}
#partnerships .pc__aurora i:nth-child(1) {
  left: -34%;
  background: radial-gradient(circle, rgba(122, 222, 74, 0.46), transparent 70%);
  animation: pcAurora1 7s ease-in-out infinite alternate;
}
#partnerships .pc__aurora i:nth-child(2) {
  left: 6%;
  background: radial-gradient(circle, rgba(178, 246, 138, 0.42), transparent 70%);
  animation: pcAurora2 9.5s ease-in-out infinite alternate;
}
#partnerships .pc__aurora i:nth-child(3) {
  left: 44%;
  background: radial-gradient(circle, rgba(96, 206, 52, 0.38), transparent 72%);
  animation: pcAurora3 12s ease-in-out infinite alternate;
}
@keyframes pcAurora1 { 0% { transform: translate(0, 10%) scale(1); }    100% { transform: translate(26%, -8%) scale(1.28); } }
@keyframes pcAurora2 { 0% { transform: translate(0, 14%) scale(1.12); } 100% { transform: translate(-20%, -5%) scale(.88); } }
@keyframes pcAurora3 { 0% { transform: translate(0, 7%) scale(.94); }   100% { transform: translate(-28%, -12%) scale(1.22); } }

/* ---- copy ------------------------------------------------------------ */
#partnerships .partnership-card blockquote {
  position: relative;
  z-index: 2;
  color: #14170f !important;
}
#partnerships .partnership-card figcaption {
  position: relative;
  z-index: 2;
  display: flex !important;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px !important;
}
#partnerships .partnership-card figcaption strong {
  color: #0f1a0c !important;
  font-weight: 600 !important;
  letter-spacing: -0.02em !important;
}
#partnerships .partnership-card figcaption small {
  color: #38452f !important;
  font-weight: 500;
  letter-spacing: -0.02em !important;
}

/* ---- hover: bloom in, siblings recede. Nothing moves. ---------------- */
@media (hover: hover) and (pointer: fine) {
  #partnerships .partnership-card:hover .pc__aurora { opacity: 1; }
  #partnerships .partnerships__cards:hover .partnership-card:not(:hover) { opacity: 0.55; }
}
@media (hover: none) {
  #partnerships .pc__aurora { opacity: 0.6; }
}

@media (max-width: 1024px) {
  #partnerships .partnerships__cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 760px) {
  #partnerships .partnerships__cards { grid-template-columns: 1fr; }
  }
@media (prefers-reduced-motion: reduce) {
  #partnerships .pc__aurora,
  #partnerships .pc__aurora i { animation: none !important; transition: none !important; }
}
/* ===== end ui-consistency.css ===== */

/* ===== begin annotation-pass-v3.css ===== */
/* Annotation pass v3 — shared polish and motion consistency */
:root {
  --page-surface: #f4f4f4;
  --card-surface: #fff;
  --soft-surface: #ededed;
  --accent-green: #4dce16;
  --premium-ease: var(--ease-out);
}

html { scroll-padding-top: 84px; }
body,
main,
main > section,
#process .design-process__stage { background-color: var(--page-surface) !important; }

/* Four scroll beats: intro, first statement, second statement and Work. */

/* The case-study overlay replaces the site header, so its close control sits
   in the same visual band as the normal header controls. */
#work .sw__ov-close {
  top: clamp(16px, 2vw, 26px);
  right: clamp(16px, 2vw, 28px);
}

/* About */
#about-me .about-v2__lede { font-weight: 500 !important; }

@media (min-width: 901px) {
  #about-me {
    display: grid !important;
    align-items: center !important;
    min-height: 100svh !important;
    min-height: 100dvh !important;
    padding-block: clamp(76px, 8.5vh, 104px) !important;
  }

  /* The lede is a subheading and has to read as one. It was on
     --fs-lede (15px at 1440) while the body beneath sat at 12.7px — a
     1.18x step, too small to establish any hierarchy, which is why the
     two blocks read as the same level. The lede now takes a genuine
     subheading size and the body drops onto the shared --fs-body token
     the rest of the site uses for supporting copy, giving a ~1.5x step. */
  #about-me .about-v2__lede {
    margin-bottom: clamp(16px, 2vh, 26px) !important;
    font-size: clamp(1.25rem, 1.6vw, 1.75rem) !important;
    line-height: 1.24 !important;
  }
  
  #about-me .about-v2__figure {
    height: 100% !important;
    min-height: 0 !important;
  }
}

/* Design Process: compact pinned story with quick, single-owner states. */
@media (min-width: 901px) and (min-height: 620px) {
  #process .design-process__story { height: 280svh; }
  #process .design-process__step {
    background: var(--soft-surface);
    transition: flex-grow var(--dur-2) var(--premium-ease),
      background-color var(--dur-2) var(--premium-ease),
      opacity var(--dur-2) var(--premium-ease) !important;
  }
  #process .design-process__step.is-active { background: var(--card-surface) !important; }
}

/* Client value now uses the same borderless, softly illuminated language. */
#client-value .value-card {
  border: 0 !important;
  background: var(--card-surface) !important;
  box-shadow: none !important;
  overflow: hidden;
}

@keyframes valueAmbient { to { opacity: 1; transform: scale(1.12); } }
@keyframes valueFloat { to { transform: translateY(-5px) scale(1.015); } }

/* Toolkit */
#toolkit .toolkit__lede { text-align: left; }
#toolkit .toolkit-tile {
  --tile-sequence: 0;
  box-shadow: none !important;
}
#toolkit .toolkit-tile::before {
  opacity: 0 !important;
  transition: opacity var(--dur-3) ease !important;
}
@media (hover:hover) and (pointer:fine) {
  #toolkit .toolkit-tile:hover::before { opacity: 1 !important; }
}

/* Playground / Beyond heading row and mobile content order. */
#perspective .pg-gallery__navrow { display: contents !important; }
#perspective .pg-gallery__summary {
  grid-column: 2 !important;
  grid-row: 1 !important;
  align-self: center !important;
  justify-self: start !important;
  margin: 0 !important;
}
#perspective .pg-gallery__tabs {
  grid-column: 1 / -1 !important;
  grid-row: 2 !important;
  justify-self: center !important;
  margin-top: clamp(22px, 3vw, 38px) !important;
}
#perspective .pg-gallery__stage { grid-row: 3 !important; }

/* Partnership canvas should continue the same neutral surface. */
#partnerships {
  background: var(--page-surface);
}
#partnerships::before,
#partnerships::after { content: none !important; }

/* Restore the restrained testimonial bloom. The aurora sits above the card
   surface but below its copy, and only runs while a precise pointer hovers. */
#partnerships .pc__aurora {
  z-index: 0;
  opacity: 0 !important;
  transition: opacity var(--dur-3) var(--ease-out) !important;
}
#partnerships .pc__aurora i {
  animation-play-state: paused !important;
}
#partnerships .partnership-card {
  border: 1px solid rgba(255, 255, 255, .96);
}

@media (hover: hover) and (pointer: fine) {
  #partnerships .partnership-card {
    transition: opacity var(--dur-3) ease,
      box-shadow var(--dur-3) var(--ease-out) !important;
  }
  #partnerships .partnership-card:hover {
    box-shadow:
      0 18px 46px rgba(77, 206, 22, .10),
      0 0 0 1px rgba(255, 255, 255, .88) !important;
  }
  #partnerships .partnership-card:hover .pc__aurora {
    opacity: .68 !important;
  }
  #partnerships .partnership-card:hover .pc__aurora i {
    animation-play-state: running !important;
  }
  #partnerships .partnerships__cards:hover .partnership-card:not(:hover) {
    opacity: 1;
  }
}

@media (hover: none) {
  #partnerships .pc__aurora { opacity: 0 !important; }
}

/* Experience: equal counters and single-line labels on desktop. */
#experience .exp__counters { align-items: stretch !important; }
#experience .counter__key {
  white-space: nowrap;
  /* Was clamp(.58rem, .72vw, .76rem) — 9.5px at desktop, a size the type
     scale does not define, so the label read as shrunken rather than as a
     deliberate step. --fs-small is the scale's 12px label size and
     --fw-medium the weight every other meta label in the site carries. */
  font-size: var(--fs-small) !important;
  font-weight: var(--fw-medium);
  /* The base 18ch cap was measured against the old 9.5px label; at the
     scale size it clips "Years of Product Experience" mid-word. The card
     is ~392px wide, so the single line has room without it. */
  max-width: none !important;
}

/* FAQ without ruled separators. */
#insights,
#insights .accordion,
#insights .accordion__item,
#insights .accordion__head,
#insights .accordion__body {
  border-top: 0 !important;
}
/* Tighter question pitch: the ruled separators are gone here, so the
   generous row padding the bordered accordion needs only reads as drift. */
#insights .accordion--faq .accordion__head { padding: clamp(10px, 1.4vh, 16px) 0; }
/* The 38px circle was setting the row height floor once the padding came
   down, so it shrinks with it — still well past the 44px tap target once
   the row padding is counted. */
#insights .accordion--faq .accordion__icon { width: 32px; height: 32px; }

/* tokens.css sets padding-block: var(--section-pad) on :root body
   #contact[id], which out-specifies #contact.folio-footer below and was
   restoring a full section's worth of space (126px) under the copyright
   row. The footer ends the page: its bottom only needs to clear the
   baseline, not open another section gap — so this has to match that
   selector's weight to win. */
:root body #contact.folio-footer[id] {
  padding-bottom: clamp(16px, 1.4vw, 24px) !important;
}

/* Unified contact footer — reference-led editorial layout. */
#contact.folio-footer {
  width: 100% !important;
  padding: var(--section-space) clamp(20px, 3.8vw, 64px) clamp(22px, 2.4vw, 38px) !important;

  border: 0 !important;
  background: var(--page-surface) !important;
  color: #1a1a1a;
  overflow: hidden;
}

.folio-footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(360px, .65fr);
  gap: var(--head-gap);
  align-items: start;
  width: min(100%, 1880px);
  margin: 0 auto var(--head-gap);
}
.folio-footer__intro { min-width: 0; }
.folio-footer__intro h2 {
  margin: 0 0 clamp(16px, 1.8vw, 24px) !important;
  white-space: nowrap;
}
.folio-footer__lede {
  max-width: 49rem;
  color: #222 !important;
}
.folio-footer__actions {
  display: flex;
  flex-wrap: wrap;
  margin-top: clamp(24px, 2.5vw, 36px);
}
.folio-footer__button {
  display: inline-flex;
  min-height: var(--control-h-lg);
  padding: 0 var(--control-pad-x);
  align-items: center;
  justify-content: center;
  gap: 14px;
  border: 1px solid #141414;
  color: #141414;
  background: transparent;
  font-weight: var(--fw-semi);
  line-height: 1;
  text-decoration: none;
  transition: color var(--dur-3) var(--premium-ease), background-color var(--dur-3) var(--premium-ease), transform var(--dur-3) var(--premium-ease);
}
.folio-footer__button--primary { color: #fff; background: #1a1a1a; }
.folio-footer__button .lucide { width: 20px; height: 20px; stroke-width: 1.8; }
@media (hover: hover) and (pointer: fine) {
  .folio-footer__button:hover { transform: translateY(-2px); }
  .folio-footer__button--primary:hover { background: #303030; }
  .folio-footer__button--secondary:hover { color: #fff; background: #1a1a1a; }
}

.folio-footer__directories {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(32px, 4vw, 64px);
  padding-top: 4px;
}
.folio-footer__column h3 {
  margin: 0 0 16px;
  font-weight: var(--fw-semi);
  letter-spacing: var(--track-heading);
}
.folio-footer__column ul {   list-style: none; }
.folio-footer__column li {
  margin: 0 0 8px;
  color: var(--text-secondary);
}

.folio-footer__media {
  position: relative;
  width: min(100%, 1880px);
  height: clamp(540px, 48vw, 860px);
  margin: 0 auto;
  overflow: hidden;
  background: #36322d;
  isolation: isolate;
}
.folio-footer__media::after {
  content: "";
  position: absolute;
  z-index: 1;
  inset: auto 0 0;
  height: 48%;
  background: linear-gradient(to bottom, transparent, rgba(0,0,0,.28));
  pointer-events: none;
}
.folio-footer__media > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 54%;
  transform: scale(1.015);
  transition: transform 1.8s var(--premium-ease);
}
.folio-footer__availability {
  position: absolute;
  z-index: 3;
  top: clamp(18px, 2vw, 34px);
  left: clamp(18px, 2vw, 34px);
  display: inline-flex;
  min-height: var(--control-h);
  padding: 0 var(--control-pad-x);
  align-items: center;
  gap: 12px;
  border: 1px solid rgba(255,255,255,.72);
  border-radius: var(--radius-control);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  font-weight: var(--fw-medium);
  letter-spacing: var(--track-body);
}
.folio-footer__availability i {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent-green);
  box-shadow: 0 0 0 5px rgba(77,206,22,.12);
}
.folio-footer__wordmark {
  position: absolute;
  z-index: 2;
  right: 1.5%;
  bottom: clamp(14px, 1.5vw, 28px);
  left: 1.5%;
  pointer-events: none;
}
.folio-footer__word {
  grid-area: 1 / 1;
  width: max-content;
  color: #fafafa;
  font-family: var(--font-hero, "Zalando Sans Expanded", "Helvetica Neue", sans-serif);
  font-size: clamp(9rem, 23vw, 29rem);
  line-height: .88;
  letter-spacing: -.045em;
  white-space: nowrap;
  user-select: none;
}
.folio-footer__word--s1 {
  filter: blur(5px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 44%, #000 56%, #000 66%, transparent 78%);
  mask-image: linear-gradient(to bottom, transparent 44%, #000 56%, #000 66%, transparent 78%);
}
.folio-footer__word--s2 {
  filter: blur(12px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 58%, #000 72%, #000 82%, transparent 94%);
  mask-image: linear-gradient(to bottom, transparent 58%, #000 72%, #000 82%, transparent 94%);
}
.folio-footer__word--s3 {
  filter: blur(24px);
  opacity: .95;
  -webkit-mask-image: linear-gradient(to bottom, transparent 72%, #000 88%, #000 100%);
  mask-image: linear-gradient(to bottom, transparent 72%, #000 88%, #000 100%);
}
.folio-footer__base {
  display: grid;
  align-items: center;
  gap: 20px;
  width: min(100%, 1880px);
  margin: 0 auto;
  padding: 22px 2px 0;
  color: var(--text-secondary);
  letter-spacing: .02em;
}
.folio-footer__base a { color: inherit; text-decoration: none; }
.folio-footer__base > a[href^="mailto:"] { text-transform: lowercase; }
.folio-footer__base > a:last-child { display: inline-flex; justify-self: end; align-items: center; gap: 7px; }
.folio-footer__base .lucide { width: 13px; height: 13px; }

@media (max-width: 980px) {
  .folio-footer__top { grid-template-columns: 1fr;  }
  .folio-footer__directories { max-width: 650px; }
    .folio-footer__word { font-size: clamp(8rem, 23vw, 17rem); }
}

@media (max-width: 620px) {
  #contact.folio-footer { padding: 64px 16px 18px !important; }
  .folio-footer__top { gap: 42px; margin-bottom: 38px; }
  .folio-footer__intro h2 {
    margin-bottom: 20px !important;
  }
    .folio-footer__lede br { display: none; }
  .folio-footer__actions { display: grid;   margin-top: 26px; }
  .folio-footer__button {  min-height: 54px; padding-inline: 22px; }
  .folio-footer__directories { gap: 28px; }
      .folio-footer__media {
    height: min(154vw, 680px);
    min-height: 520px;
  }
  .folio-footer__media > img { object-position: 50% 48%; }
  .folio-footer__availability { top: 14px; left: 14px; min-height: 42px; padding-inline: 15px;  }
  .folio-footer__wordmark { right: 1%; bottom: 10px; left: 1%; }
  .folio-footer__word { font-size: 24.5vw; }
    .folio-footer__base > a:last-child { justify-self: start; }
}

/* Final annotated refinements ------------------------------------------------ */

/* Keep the hero metadata on the exact same optical edges as the FOLIO word. */
#hero .hero__titles {
  display: grid !important;
  grid-template-columns: max-content minmax(0, 1fr) !important;
  width: var(--folio-vis, 100vw) !important;
  max-width: none !important;
  margin-left: calc(50% - 50vw + var(--folio-overhang, 0px)) !important;
  padding-inline: var(--folio-pad, clamp(14px, 2vw, 34px)) !important;
  justify-content: initial !important;
}
#hero .hero__titles > :first-child { justify-self: start !important; }
#hero .hero__titles > :last-child { justify-self: end !important; text-align: right !important; }

/* Expertise cards stay planted; their internal media remains interactive. */
@media (hover: hover) and (pointer: fine) {
  #services .capability-card-v2:hover {
    transform: none !important;
    translate: 0 !important;
  }
}

/* Outcome cards use the same compact illustration-to-copy rhythm as the rest. */
#client-value .value-card {
  min-height: clamp(310px, 38vh, 370px) !important;
  grid-template-rows: minmax(178px, 1fr) auto !important;
}

/* Travel preview metadata belongs to the image and follows every selection.
   No boxed card of its own any more — a single frosted gradient along the
   bottom edge of the preview (see .pg-preview::after) carries legibility
   for the whole strip, so the text can just swipe up over the photo
   instead of sitting inside its own dark pill fighting for attention. */
.pg-preview__caption {
  position: absolute;
  z-index: 3;
  left: clamp(24px, 4vw, 68px);
  right: clamp(24px, 4vw, 190px);
  bottom: clamp(20px, 3.4vw, 44px);
  color: #fff;
  pointer-events: none;
}
.pg-preview__caption h3 {
  margin: 0 0 7px;
  font-size: clamp(1.25rem, 2vw, 1.8rem);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -.035em;
}
.pg-preview__caption p {
  max-width: 48ch;
  margin: 0 0 11px;
  color: rgba(255,255,255,.88);
  font-size: clamp(.84rem, 1vw, .98rem);
  line-height: 1.42;
}
.pg-preview__caption span {
  color: rgba(255,255,255,.66);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
}

/* Camera line, with an aperture mark ahead of it. inline-flex rather than
   flex so the row is only as wide as its contents — the caption is left
   aligned on the web and centre aligned on a phone, and an inline box
   follows the parent's text-align on its own, with no second rule. */
.pg-preview__device {
  display: inline-flex !important;
  align-items: center;
  gap: 6px;
}
.pg-preview__device em {
  font: inherit;
  font-style: normal;
}
.pg-preview__device svg {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
  opacity: .82;
}

/* Fit / Fill / Original, top-left, opposite the close button.

   tokens.css enforces square corners site-wide via `:root body [id] [class]
   { border-radius: var(--radius-control, 16px) !important }` and exempts `button` from it, which is
   why the three buttons came out round inside a hard-cornered tray.

   The tray needs the same exemption. That sweep's top tier is id-scoped
   (`:is(#hero, ..., #perspective, ...)`), and an id outranks any number of
   classes, so this has to carry an id of its own — no amount of repeated
   [class] reaches it. */
:root body #perspective .pg-preview__modes[class][class] {
  border-radius: 999px;
}
.pg-preview__modes {
  position: absolute;
  z-index: 4;
  top: clamp(14px, 2vw, 26px);
  left: clamp(14px, 2vw, 26px);
  display: flex;
  gap: 4px;
  padding: 4px;
  border-radius: 999px;
  background: rgba(18, 20, 18, .52);
  -webkit-backdrop-filter: blur(14px);
          backdrop-filter: blur(14px);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .13);
}
.pg-preview__modes button {
  min-height: 32px;
  padding: 0 12px;
  border: 0;
  background: transparent;
  color: rgba(255, 255, 255, .72);
  font: inherit;
  font-size: .74rem;
  font-weight: 500;
  letter-spacing: .01em;
  line-height: 1;
  transition: background var(--dur-2) ease, color var(--dur-2) ease;
}
.pg-preview__modes button:hover { color: #fff; }
.pg-preview__modes button.is-active {
  background: rgba(255, 255, 255, .92);
  color: #141414;
}
/* While zoomed the photo is draggable, so say so on the cursor. */
.pg-preview.is-zoomed { cursor: grab; }
.pg-preview.is-zoomed:active { cursor: grabbing; }

@media (max-width: 640px) {
  .pg-preview__modes {
    top: max(12px, env(safe-area-inset-top));
    left: 50%;
    transform: translateX(-50%);
  }
  .pg-preview__modes button { padding: 0 10px; font-size: .7rem; }
}

/* FAQ responses begin on precisely the same content edge as their questions. */
#insights .accordion--faq .accordion__body p {
  padding-left: 0 !important;
  /* The right pad is the only reserve the answer needs: it clears the
     open/close circle that sits at the row's right edge. */
  padding-right: clamp(48px, 6vw, 76px) !important;
  /* The base sheet caps answers at 68ch, which on a wide row wrapped a
     one-line answer onto two while half the row sat empty. The row itself
     is already width-constrained by the page gutters, so let the answer
     use it and wrap only when it genuinely runs out. */
  max-width: none !important;
}
/* Narrow screens have no width to spare, and the base sheet already drops
   the reserve to 12px there — the !important above would otherwise keep
   the desktop gutter and cost ~36px of every mobile line. */
@media (max-width: 700px) {
  #insights .accordion--faq .accordion__body p {
    padding-right: 12px !important;
  }
}

@media (max-width: 900px) {
  #hero .hero__titles {
    width: var(--folio-vis, 100vw) !important;
    margin-left: calc(50% - 50vw + var(--folio-overhang, 0px)) !important;
    padding-inline: var(--folio-pad, 16px) !important;
  }
  #client-value .value-card {
    min-height: 310px !important;
    grid-template-rows: 190px auto !important;
  }
  
  .pg-preview__caption {
    left: 50%;
    right: auto;
    width: min(calc(100vw - 32px), 470px);
    padding: 17px 18px 16px;
    text-align: center;
    transform: translateX(-50%);
  }
  }

@media (max-width: 900px) {
  html { scroll-padding-top: 68px; }
  main > section { scroll-margin-top: 72px; }
  #process .design-process__story { height: auto; }
  #process .design-process__steps {
    scroll-snap-type: x mandatory !important;
    scroll-padding-inline: var(--mobile-gutter, 20px);
  }
  #process .design-process__step { scroll-snap-stop: always; }
    #about-me {
    min-height: auto !important;
    padding-block: 72px 48px !important;
  }
  
  #about-me .about-v2__figure {
    /* `min-height` combined with `aspect-ratio` drives the WIDTH, not just
       the height: 210px * (16/8.5) = 395.3px, which overflowed the 335px
       column on a 375px screen by 40px. Constrain the width explicitly and
       let the ratio derive the height from it instead. */
    width: 100% !important;
    max-width: 100% !important;
    aspect-ratio: 16 / 9.5 !important;
    min-height: 0 !important;
  }
  #about-me .about-v2__lede {
    margin-bottom: 13px !important;
    font-size: var(--fs-lede) !important;
    line-height: 1.22 !important;
  }

  #toolkit .toolkit__head { text-align: left; }
  
  #perspective .pg-gallery__navrow { display: contents !important; }
  #perspective .pg-gallery__summary {
    grid-column: 1 !important;
    grid-row: 2 !important;
    margin-top: 12px !important;
    text-align: left !important;
  }
  #perspective .pg-gallery__tabs {
    grid-column: 1 !important;
    grid-row: 3 !important;
    scrollbar-width: none;
  }
  #perspective .pg-gallery__stage { grid-row: 4 !important; }
  
  #experience .counter__key { white-space: normal; }
}

/* ===== end annotation-pass-v3.css ===== */

/* ===== begin annotation-pass-v4.css ===== */
/* Annotation pass v4 — final component consistency refinements */

/* Education begins without an orphaned divider. */

/* Experience metrics remain distinct through surface and spacing, not rules. */

/* The frosted navigation reads as one uninterrupted glass surface. */
#nav,
#nav::before {
  border: 0 !important;
}
#nav::before {
  box-shadow: none !important;
}

/* Outcome cards use the same compact image-to-copy cadence as the site. */
#client-value .value-card {
  min-height: clamp(278px, 32vh, 320px) !important;
  grid-template-rows: minmax(146px, 1fr) auto !important;
  border-radius: var(--ui-radius-card, 20px) !important;
}

@media (max-width: 900px) {

  #client-value .value-card {
    min-height: 250px !important;
    grid-template-rows: 132px auto !important;
  }
  
}

@media (prefers-reduced-motion: reduce) {
  #services .capability-card-v2,
  #client-value .value-card {
    transition-duration: var(--dur-1) !important;
  }
}
/* ===== end annotation-pass-v4.css ===== */

/* ===== begin theme-bridge.css ===== */
/* =====================================================================
   folio-v2 — THEME BRIDGE

   The site accumulated four independent colour vocabularies, each with
   its own :root block and none of them theme-aware:

     css/styles.css          --bg --surface --media --card --card-border
                             --card-shadow --ink --ink-dim --ink-faint
                             --rule --volt --volt-ink
     ui-consistency.css      --ui-ink --ui-muted --ui-paper --ui-surface
                             --ui-line --ui-accent
     annotation-pass-v3.css  --page-surface --card-surface --soft-surface
                             --accent-green
     codex-final-pass-v2.css --volt --volt-ink --site-paper

   Because those blocks only ever declared light values, every rule that
   read them kept painting light-mode colours under [data-theme="dark"] —
   which is where the black-on-black text came from (.accordion__title
   reads --ink-dim, .section__label reads --ink, .pill reads --surface).

   This file re-declares each legacy name as an ALIAS of the semantic
   token in tokens.css. Nothing is renamed and nothing is removed, so
   every existing rule keeps working — it just resolves to a
   theme-correct value now.

   Load order matters: this must come AFTER the four files above so the
   aliases win on "later rule, equal specificity", and BEFORE
   theme-system.css so component rules can build on it.
   ===================================================================== */

:root {
  /* ---- css/styles.css ---------------------------------------------- */
  --bg:          var(--n-100);
  --surface:     var(--n-25);
  --media:       var(--surface-media);
  --card:        var(--surface-card);
  --card-border: var(--line-soft);
  --card-shadow: 0 1px 2px rgba(17, 17, 17, 0.03), 0 12px 32px rgba(17, 17, 17, 0.06);
  --ink:         var(--ink-strong);
  --ink-dim:     var(--text-secondary);
  --ink-faint:   var(--text-tertiary);
  --rule:        var(--line-strong);
  --volt:        var(--accent);
  --volt-ink:    var(--accent-ink);

  /* ---- ui-consistency.css ------------------------------------------ */
  --ui-ink:     var(--text-primary);
  --ui-muted:   var(--text-secondary);
  --ui-paper:   var(--surface-page);
  --ui-surface: var(--surface-card);
  --ui-line:    var(--line);
  --ui-accent:  var(--accent);

  /* ---- annotation-pass-v3.css -------------------------------------- */
  --page-surface: var(--surface-page);
  --card-surface: var(--surface-card);
  --soft-surface: var(--surface-sunken);
  --accent-green: var(--accent);

  /* ---- codex-final-pass-v2.css ------------------------------------- */
  --site-paper: var(--surface-page);
}

:root[data-theme="dark"] {
  /* The frame grey sits BELOW the page surface in dark mode, so the two
     read as distinct planes — the same relationship #ededed has to
     #f4f4f4 in light mode, inverted. */
  --bg:          var(--n-1000);
  --surface:     var(--n-900);
  --card-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 12px 32px rgba(0, 0, 0, 0.45);
}

/* =====================================================================
   Hard-coded light values that predate the token system.
   These were authored as literal hex/rgba, so token aliasing cannot
   reach them — each needs an explicit dark override.
   ===================================================================== */

/* ---- Case-study overlay -------------------------------------------- */
:root[data-theme="dark"] .cs { background: var(--surface-page); }
:root[data-theme="dark"] :is(.cs__meta, .cs__section, .cs__body) { color: var(--text-secondary); }
:root[data-theme="dark"] :is(.cs__title, .cs__h, .cs__lede, .cs__metric span) { color: var(--text-primary); }
:root[data-theme="dark"] .cs__disclaimer { color: var(--text-tertiary); }

/* The Work overlay's meta strip was a literal light panel, so it stayed
   white beneath a dark cover image. */
:root[data-theme="dark"] .sw__ov-meta {
  background: var(--surface-page);
  border-top-color: var(--line);
}
/* Columns are built by selected-work.js as <b>LABEL</b><span>value</span>. */
:root[data-theme="dark"] .sw__ov-meta .sw-meta-col b { color: var(--text-tertiary) !important; }
:root[data-theme="dark"] .sw__ov-meta .sw-meta-col span,
:root[data-theme="dark"] .sw__ov-meta .sw-meta-col { color: var(--text-primary) !important; }

/* ---- Case-study dialog vs. site header ------------------------------
   The overlay lives inside #work, which is `position:relative;
   z-index:20; isolation:isolate` — a stacking context. Its own
   z-index:150 therefore competes only with #work's siblings at z-index
   20, so it can never out-stack the fixed header at z-index 130.

   Raising the overlay's z-index cannot fix this from inside the trapped
   context, so the header steps aside instead: a full-screen dialog
   should not have the site chrome floating over it, and the overlay
   ships its own close control.
   -------------------------------------------------------------------- */
html.sw-overlay-open #nav {
  opacity: 0 !important;
  pointer-events: none !important;
  visibility: hidden !important;
  transition: opacity var(--dur-2) ease, visibility var(--dur-2) ease !important;
}
/* ===== end theme-bridge.css ===== */

/* ===== begin theme-system.css ===== */
/* Semantic light/dark system + animated header control. Loaded last on purpose. */
html { background:var(--surface-page); }
body {
  background:var(--surface-page);
  transition:background-color var(--dur-mid) var(--ease-out),color var(--dur-fast) ease;
}

body,
.section,
#work,
#work .sw__inner,
#perspective,
#partnerships,
#experience,
#insights,
#contact,
.about-v2,
.client-value,
.toolkit,
.design-process {
  background-color:var(--surface-page) !important;
  color:var(--text-primary);
}

:is(h1,h2,h3,h4,.counter__value,.faq__question,.accordion__item button) { color:var(--text-primary) !important; }
:is(p,.sw__support,.pg-gallery__summary,.exp__head-desc,.about-v2__lede,.client-value__lede,.toolkit__lede) { color:var(--text-secondary); }

/* The hero story sits on the portrait video behind a dark frost, so it is
   white in BOTH themes. This has to be stated explicitly: the panels are <p>
   elements, so the rule above painted the first block muted grey, while the
   second only looked white because GSAP writes an inline #fff onto its
   per-character spans. */
:is(.hero-story__headline,.hero-story__support,.hero-story__word) {
  color:#fff !important;
}
/* Same reasoning as the hero story above: the travel-preview caption sits
   over a full-bleed photo, not a theme surface, so its heading must stay
   white in both themes. The blanket h1-h4 rule above painted it var(
   --text-primary) — near-black in light mode — which read as broken text
   on a dark photo. */
#perspective .pg-preview__caption h3 {
  color:#fff !important;
}
/* NOTE: .hero-story__char is deliberately NOT in the list above.

   The second panel lights its characters one at a time from dim grey to
   white, and GSAP drives that by writing an inline `color` onto each
   span. A stylesheet `!important` outranks a non-important inline
   declaration, so including the chars here (or restating their dim start
   with !important) froze all 96 of them at rgba(255,255,255,.22) and
   killed the fill outright.

   The dim start already comes from codex-final-pass-v2.css without
   !important, which is exactly what lets the inline animation win.
   Primary-panel chars have no colour of their own and inherit #fff from
   the headline. */

:is(small,.mono-label,.counter__key,.exp__meta,.folio-footer__eyebrow) { color:var(--text-tertiary); }

:is(.capability-card-v2,.value-card,.toolkit-tile,.partnership-card,.counter) {
  background-color:var(--surface-card);
  border-color:var(--line);
}

/* Editorial rows stay integrated with the page instead of becoming cards. */
#experience .exp__row,
#experience .exp__list--education .exp__row,
#insights .accordion__item,
#contact .folio-footer__directories,
#contact .folio-footer__column {
  background:transparent !important;
}

:is(#work .sw-pill,#perspective .pg-gallery__tabs button) {
  background:var(--surface-control) !important;
  color:var(--text-secondary) !important;
  border-color:var(--line) !important;
}
:is(#work .sw-pill,#perspective .pg-gallery__tabs button).is-active {
  background:var(--surface-active) !important;
  color:var(--text-on-active) !important;
}

/* ---- Header ---------------------------------------------------------
   codex-final-pass-v2.css declares `#nav { background:transparent
   !important }` and only paints a capsule once `.nav--post-hero` lands.
   An ID + !important outranks `.nav--glass`, so in every state where
   post-hero was off — at the top of the page, and any time the class went
   stale while an overlay owned the scroll — the header rendered with no
   background at all and its labels sat directly on the page.

   These rules match that specificity (#nav + !important, later in the
   cascade) so the capsule is ALWAYS painted. post-hero now only deepens
   it rather than being the thing that creates it.
   -------------------------------------------------------------------- */
#nav,
#nav.nav--glass {
  backdrop-filter:blur(20px) saturate(1.3) !important;
  -webkit-backdrop-filter:blur(20px) saturate(1.3) !important;
}
#nav.nav--post-hero {
  background:var(--surface-glass) !important;
  border-color:var(--line-strong) !important;
  color:var(--text-primary) !important;
}
.nav__brand svg { fill:currentColor; }

.theme-toggle {
  appearance:none;
  display:grid;
  place-items:center;
  width:40px;
  height:40px;
  flex:0 0 40px;
  border:1px solid var(--line-strong);
  transition:transform var(--dur-fast) var(--ease-out),background-color var(--dur-fast) ease,border-color var(--dur-fast) ease;
}
.theme-toggle:hover { transform:translateY(-2px) rotate(-4deg); }
.theme-toggle:active { transform:translateY(0) scale(.94); }
.theme-toggle:focus-visible { outline:2px solid var(--focus-ring);outline-offset:3px; }
.theme-toggle__track { display:grid;place-items:center;width:22px;height:22px; }
.theme-toggle__icon { width:22px;height:22px;overflow:visible;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round; }
.theme-toggle__sun-core,.theme-toggle__sun-rays,.theme-toggle__moon { transform-origin:12px 12px;transition: transform var(--dur-2) var(--ease-out),opacity var(--dur-2) ease; }
.theme-toggle__moon { opacity:0;transform:rotate(-95deg) scale(.25);fill:currentColor;stroke:none; }
:root[data-theme="dark"] .theme-toggle__sun-core { opacity:0;transform:rotate(70deg) scale(.2); }
:root[data-theme="dark"] .theme-toggle__sun-rays { opacity:0;transform:rotate(105deg) scale(.45); }
:root[data-theme="dark"] .theme-toggle__moon { opacity:1;transform:rotate(0) scale(1); }
.theme-toggle--mobile { display:none; }

/* The three Motion studies intentionally remain white in both themes —
   the recordings themselves have white backgrounds, so a dark panel would
   just put a hard seam around the footage. */
#perspective .pg-panel--motion figure {
  background:#fff;
  border:1px solid rgba(20,24,21,.09);
}
#perspective .pg-panel--motion video { background:#fff; }

/* In dark mode that white slab needs to read as a deliberate light media
   surface rather than a hole in the page: inset the frame slightly, and
   swap the light-on-light border and shadow for ones that actually
   register against black. */
/* No drop shadow on these. The dark-theme pair in particular ended with a
   0 0 0 1px spread, which against a white card renders as a hard dark ring
   just inside the edge and reads as an inner shadow rather than depth. The
   hairline border alone is what separates the white slab from the page. */
:root[data-theme="dark"] #perspective .pg-panel--motion figure {
  border-color:rgba(255,255,255,.14);
}

/* Dark mode used a near-opaque .96 fill, which flattened the tray and
   killed the frost the light theme shows. Both themes now use the same
   translucent tint so the blur behind it actually reads. */
:root[data-theme="dark"] .mobnav a,:root[data-theme="dark"] .mobnav span { color:var(--text-primary); }
:root[data-theme="dark"] body :is(#services,#contact,.capabilities-v2,.folio-footer) {
  color:var(--text-primary) !important;
}
:root[data-theme="dark"] body :is(.capability-card-v2,.value-card,.toolkit-tile,.partnership-card,.counter) {
  background:var(--surface-card);
  color:var(--text-primary);
}

/* Dark-mode sections that still carried authored light surfaces. */
:root[data-theme="dark"] body #process .design-process__step,
:root[data-theme="dark"] body #process .design-process__step.is-active {
  background:var(--surface-card);
}
/* codex-final-pass-v2.css tints the inactive process steps with literal
   rgba(17,17,17,…) — near-black ink that vanished on the dark step surface.
   Recede them with the theme's own text ramp instead. */
:root[data-theme="dark"] body #process .design-process__step:not(.is-active) {
  background:var(--surface-sunken);
}
:root[data-theme="dark"] body #process .design-process__step:not(.is-active) .design-process__copy p {
  color:var(--text-tertiary) !important;
}

/* Work pagination retains both its base rail and active progress in dark mode. */
#work .sw__track > i { background:var(--text-primary); }
:root[data-theme="dark"] body #services .capability-card-v2,
:root[data-theme="dark"] body #client-value .value-card,
:root[data-theme="dark"] body #partnerships .partnership-card {
  background:var(--surface-card);
  color:var(--text-primary);
}
:root[data-theme="dark"] body :is(#work,#services,#process,#about-me,#client-value,#toolkit,#perspective,#partnerships,#experience,#insights,#contact) :is(h1,h2,h3,h4,h5,h6,strong,blockquote) {
  color:var(--text-primary) !important;
}
:root[data-theme="dark"] body :is(#work,#services,#process,#about-me,#client-value,#toolkit,#perspective,#partnerships,#experience,#insights,#contact) :is(p,small,.sw-li,.counter__key,.exp__meta,.capability-card-v2__tags span,.folio-footer__link) {
  color:var(--text-secondary) !important;
}
/* --text-on-active is for text sitting ON the filled active chip. The work
   list items are plain text on the page background — not chips — so giving
   them the inverted colour rendered them black on black. */
:root[data-theme="dark"] body :is(#work .sw-pill,#perspective .pg-gallery__tabs button).is-active {
  color:var(--text-on-active) !important;
}
#work .sw-li:not(.is-active) { color:var(--text-tertiary) !important; }
:root[data-theme="dark"] body :is(.capability-card-v2,.value-card,.toolkit-tile,.partnership-card,.folio-footer__column) p {
  color:var(--text-secondary) !important;
}
:root[data-theme="dark"] :is(.capability-card-v2__visual,.value-card__visual,.toolkit-tile__visual) { background:var(--surface-media) !important; }
:root[data-theme="dark"] :is(.folio-footer__link,.accordion__item,.exp__row) { border-color:var(--line) !important; }

/* ---- Footer CTAs ----------------------------------------------------
   annotation-pass-v3.css authored these with literal #141414 / #1a1a1a,
   so "Book a Call" (the --secondary variant: dark text and dark border on
   a transparent fill) disappeared completely against the dark page.
   Both variants are now token-driven and invert with the theme.
   -------------------------------------------------------------------- */
.folio-footer__button {
  border-color:var(--text-primary);
  color:var(--text-primary);
  background:transparent;
}
.folio-footer__button--primary {
  background:var(--surface-active);
  border-color:var(--surface-active);
  color:var(--text-on-active);
}
@media (hover:hover) and (pointer:fine) {
  .folio-footer__button--primary:hover {
    background:var(--accent) !important;
    border-color:var(--accent) !important;
    color:#111 !important;
  }
  .folio-footer__button--secondary:hover {
    background:var(--surface-active) !important;
    border-color:var(--surface-active) !important;
    color:var(--text-on-active) !important;
  }
}

/* Primary pill: base .pill paints background:var(--ink) / color:var(--surface),
   which the bridge now inverts per theme. Hover flips to the brand green, so
   the label must go dark to stay legible on it in both themes. */
.pill:hover,.pill:focus-visible { color:#111 !important; }

@media (max-width:1024px) {
  .theme-toggle--mobile { display:grid;margin-left:auto; }
}

@media (prefers-reduced-motion:reduce) {
  .theme-toggle,.theme-toggle__sun-core,.theme-toggle__sun-rays,.theme-toggle__moon { transition:none; }
}
/* ===== end theme-system.css ===== */

/* ===== begin css/micro-interactions.css ===== */
/* =====================================================================
   MICRO-INTERACTIONS

   Three shared motion primitives, used site-wide so the small moments
   feel like one system rather than per-component one-offs:

     .reel        digits spin into place like an odometer
     .stagger     lines rise + defocus in, then leave as one quiet fade
     .icon-swap   one glyph dissolves into another

   All three respect prefers-reduced-motion, and each hands its
   compositor layer back once it settles — the site keeps a deliberately
   low permanent layer count and these must not erode it.
   ===================================================================== */

:root {
  /* Reel */
  --reel-dur:     1400ms;
  --reel-stagger: 90ms;
  --reel-blur:    3px;
  --reel-ease:    var(--ease-out);

  /* Staggered reveal */
  --stagger-dur:      500ms;
  --stagger-distance: 12px;
  --stagger-step:     40ms;
  --stagger-blur:     3px;
  --stagger-ease:     var(--ease-out);
  --stagger-exit:     200ms;

  /* Icon swap */
  --icon-swap-dur:         250ms;
  --icon-swap-blur:        2px;
  --icon-swap-start-scale: .25;
  --icon-swap-ease:        ease-in-out;
}

/* =====================================================================
   1 · REEL — spinning digits

   Each digit is a clipped window onto a strip of 0-9 repeated; the strip
   translates up (spins x 10 + digit) cells, so it spins before landing.
   Columns are staggered left-to-right.

   `--reel-cell` is set per instance from JS, derived from the element's
   own computed line-height. One hardcoded cell height cannot work here:
   the Experience stats run a 100.8px line box, the Work pager 20.48px.
   ===================================================================== */
.reel {
  display: inline-flex;
  align-items: center;
  /* Deliberately NOT tabular. Tabular figures were here to stop the
     columns resizing mid-spin, but they are a different set of glyphs:
     in Geist the tabular "1" is 120px wide against the proportional
     one's 76.8px (at 200px), with a different shape — so the number
     visibly changed face as it landed. Column width is now pinned from
     the settled text's own advances (see measureAdvances in
     js/micro-interactions.js), which holds the layout steady without
     swapping figure sets. */
  font-variant-numeric: normal;
  line-height: inherit;
}
.reel__col {
  position: relative;
  height: var(--reel-cell, 1em);
  flex: 0 0 auto;
  width: 1ch;
  /* clip-path, not `overflow: hidden`. The reel only needs a VERTICAL
     window for digits to roll through; overflow clipped horizontally too,
     and because .counter__value sets letter-spacing: -.055em the digit
     ink is wider than its column box, so the tightest glyphs were being
     shaved on mobile. Clipping to the box top/bottom while extending
     sideways keeps the roll intact and stops the trimming. */
  clip-path: inset(0 -0.12em);
  /* Fade the window edges rather than hard-cropping, so digits arrive
     and leave instead of being sliced. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 22%, #000 78%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 22%, #000 78%, transparent 100%);
}
.reel__strip {
  display: flex;
  flex-direction: column;
}
.reel__digit {
  height: var(--reel-cell, 1em);
  display: flex;
  align-items: center;
  justify-content: center;
  /* Pin every cell to one tabular figure width. When the reel finishes it
     tears itself down and re-renders the value as plain text; if the
     rolling cells and the settled text measure differently the number
     visibly jumps at the end. 1ch under font-variant-numeric: tabular-nums
     is exactly one figure advance, which is what the plain text will use
     too, so the two states line up. */
  /* The column carries the width (JS pins it to the advance the character
     occupies in the settled text); the cell just fills it. letter-spacing
     is zeroed here because that tracking already lives in the column
     width — applying it again would push the glyph off-centre. */
  width: 100%;
  letter-spacing: 0;
  box-sizing: border-box;
}
/* Separators and suffixes ride along without a reel of their own. */
.reel__fixed {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  letter-spacing: 0;
  height: var(--reel-cell, 1em);
}

@media (prefers-reduced-motion: reduce) {
  .reel__strip { transition: none !important; filter: none !important; }
}

/* =====================================================================
   3 · ICON SWAP — one glyph dissolves into another

   Both glyphs occupy the same grid cell, so nothing reflows. The
   outgoing one shrinks and defocuses as the incoming one resolves.
   ===================================================================== */
.icon-swap {
  position: relative;
  display: inline-grid;
  place-items: center;
}
.icon-swap > * {
  grid-area: 1 / 1;
  transition:
    opacity   var(--icon-swap-dur) var(--icon-swap-ease),
    filter    var(--icon-swap-dur) var(--icon-swap-ease),
    transform var(--icon-swap-dur) var(--icon-swap-ease);
}
.icon-swap[data-state="a"] [data-icon="a"],
.icon-swap[data-state="b"] [data-icon="b"] {
  opacity: 1;
  filter: blur(0);
  transform: scale(1);
}
.icon-swap[data-state="a"] [data-icon="b"],
.icon-swap[data-state="b"] [data-icon="a"] {
  opacity: 0;
  filter: blur(var(--icon-swap-blur));
  transform: scale(var(--icon-swap-start-scale));
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .icon-swap > * { transition: none !important; filter: none !important; }
}

/* ===== end css/micro-interactions.css ===== */

/* ===== begin css/theme-dissolve.css ===== */
.theme-dissolve {
  position: fixed;
  z-index: 2147482500;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  will-change: opacity, transform;
  transform: translateZ(0);
  backface-visibility: hidden;
  contain: strict;
  /* The fill is opaque, so there is nothing for a backdrop blur to do. */
}

/* The veil is now a circle that grows from the control that was pressed,
   painted in the incoming theme's own page colour (set inline by
   js/theme.js) — so the switch reads as the new theme sweeping in rather
   than as a scrim passing over the old one. It is fully opaque, which is
   why the old midpoint flash cannot return: the page underneath is never
   seen half-changed. */
/* js/theme.js sets the fill inline (a white frost going to light, the
   solid page colour going to dark); these are the fallbacks if it does
   not. Anything painted ON the sweep is white, which is legible over the
   frost while the darker page still shows through it. */
.theme-dissolve { color: #ffffff; }

@media (prefers-reduced-motion: reduce) {
  .theme-dissolve { display: none; }
}

/* Theme reveal (View Transitions). The browser snapshots the page in the
   old theme, js/theme.js commits the new one, and the new page is unmasked
   by a circle growing from the pressed control. The circle's edge is a
   wide feathered falloff rather than a hard clip, so the two themes melt
   into each other along the wavefront instead of being cut. The .theme-
   dissolve veil above stays as the fallback where the API is missing. */
@property --theme-reveal-r {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

html.is-theme-revealing::view-transition-old(root),
html.is-theme-revealing::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}

html.is-theme-revealing::view-transition-new(root) {
  --theme-reveal-soft: var(--theme-reveal-feather, 220px);
  -webkit-mask-image: radial-gradient(
    circle at var(--theme-reveal-x, 50%) var(--theme-reveal-y, 0px),
    #000 calc(var(--theme-reveal-r) - var(--theme-reveal-soft)),
    rgba(0, 0, 0, .86) calc(var(--theme-reveal-r) - var(--theme-reveal-soft) * .75),
    rgba(0, 0, 0, .5) calc(var(--theme-reveal-r) - var(--theme-reveal-soft) * .5),
    rgba(0, 0, 0, .14) calc(var(--theme-reveal-r) - var(--theme-reveal-soft) * .25),
    transparent var(--theme-reveal-r)
  );
  mask-image: radial-gradient(
    circle at var(--theme-reveal-x, 50%) var(--theme-reveal-y, 0px),
    #000 calc(var(--theme-reveal-r) - var(--theme-reveal-soft)),
    rgba(0, 0, 0, .86) calc(var(--theme-reveal-r) - var(--theme-reveal-soft) * .75),
    rgba(0, 0, 0, .5) calc(var(--theme-reveal-r) - var(--theme-reveal-soft) * .5),
    rgba(0, 0, 0, .14) calc(var(--theme-reveal-r) - var(--theme-reveal-soft) * .25),
    transparent var(--theme-reveal-r)
  );
}

/* The pressed toggle is lifted into its own layer above the sweep. Its new
   view is live, so the sun/moon morph plays in real time on top while the
   page around it changes; the old icon simply steps out of the way. */
/* Other named elements (the case-study cover) would otherwise get their
   own groups and crossfade outside the mask; fold them back into root. */
html.is-theme-revealing #swThumb,
html.is-theme-revealing [style*="view-transition-name"]:not(.theme-toggle) {
  view-transition-name: none !important;
}

html.is-theme-revealing::view-transition-group(theme-toggle) {
  animation: none;
  z-index: 1;
}
html.is-theme-revealing::view-transition-old(theme-toggle) { display: none; }
html.is-theme-revealing::view-transition-new(theme-toggle) { animation: none; }

/* Everything else holds still while the snapshot swaps, so colour
   transitions on the live page never race the mask. The toggle's own icon
   is exempt: its morph is the one motion that should read. */
html.is-theme-revealing *:not(.theme-toggle__sun-core, .theme-toggle__sun-rays, .theme-toggle__moon) {
  transition: none !important;
}
html.is-theme-revealing .theme-toggle__sun-core,
html.is-theme-revealing .theme-toggle__sun-rays,
html.is-theme-revealing .theme-toggle__moon {
  transition:
    transform 620ms var(--ease-toggle),
    opacity 380ms var(--ease-in-out) !important;
}
/* ===== end css/theme-dissolve.css ===== */

/* ===== begin css/custom-cursor.css ===== */
/* Desktop-only portfolio cursor. Native cursors remain intact for touch,
   coarse pointers, and devices that do not support hover. */
@media (hover: hover) and (pointer: fine) {
  html.has-portfolio-cursor,
  html.has-portfolio-cursor body,
  html.has-portfolio-cursor body * {
    cursor: none !important;
  }

  /* The root carries only the position — it has no size of its own, so the
     arrow's tip can sit exactly on the pointer while the bubble centres
     on the same point. */
  .portfolio-cursor {
    position: fixed;
    z-index: 2147483000;
    top: 0;
    left: 0;
    width: 0;
    height: 0;
    opacity: 0;
    pointer-events: none;
    transform: translate3d(-40px, -40px, 0);
    transition: opacity var(--dur-1) ease;
    will-change: transform;
  }

  .portfolio-cursor.is-visible { opacity: 1; }

  /* Tip at 0,0: the path's origin is the arrow point, so no offset is
     needed to line it up with the real pointer position. */
  .portfolio-cursor__arrow {
    position: absolute;
    top: 0;
    left: 0;
    display: block;
    width: 24px;
    height: 24px;
    /* The global `img, video, svg, canvas { max-width: 100% }` resolves
       against the zero-width root and collapsed the arrow to 0. */
    max-width: none;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .28));
    transform-origin: 2px 2px;
    transition: opacity var(--dur-2) ease, transform var(--dur-2) var(--ease-out);
  }

  .portfolio-cursor__bubble {
    position: absolute;
    top: 0;
    left: 0;
    display: grid;
    width: 0;
    height: 0;
    place-items: center;
    overflow: hidden;
    color: #f4f4f4;
    background: rgba(12, 14, 15, .68);
    box-shadow:
      0 18px 44px rgba(0, 0, 0, .24),
      inset 0 1px 0 rgba(255, 255, 255, .14);
    -webkit-backdrop-filter: blur(18px) saturate(1.45);
    backdrop-filter: blur(18px) saturate(1.45);
    opacity: 0;
    transform: translate(-50%, -50%);
    transition: width var(--dur-3) var(--ease-out),
      height var(--dur-3) var(--ease-out),
      opacity var(--dur-2) ease;
    will-change: width, height;
  }

  /* Over the Work cover the arrow gives way to the bubble. */
  .portfolio-cursor.is-work-cover .portfolio-cursor__arrow {
    opacity: 0;
    transform: scale(.6);
  }

  .portfolio-cursor.is-work-cover .portfolio-cursor__bubble {
    width: 118px;
    height: 118px;
    opacity: 1;
  }

  .portfolio-cursor__label {
    font: 400 13px/1.18 var(--font-sans, Arial, sans-serif);
    letter-spacing: -.01em;
    white-space: nowrap;
    opacity: 0;
    transform: translateY(4px) scale(.94);
    transition: opacity var(--dur-1) ease,
      transform var(--dur-3) var(--ease-out);
  }

  .portfolio-cursor.is-work-cover .portfolio-cursor__label {
    opacity: 1;
    transform: translateY(0) scale(1);
    transition-delay: .06s;
  }

  .portfolio-cursor.is-pressed .portfolio-cursor__arrow {
    transform: scale(.86);
  }

  .portfolio-cursor.is-work-cover.is-pressed .portfolio-cursor__bubble {
    width: 110px;
    height: 110px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .portfolio-cursor,
  .portfolio-cursor__arrow,
  .portfolio-cursor__bubble,
  .portfolio-cursor__label {
    transition-duration: var(--dur-1) !important;
  }
}

@media (prefers-reduced-transparency: reduce) {
  .portfolio-cursor__bubble {
    background: #1a1a1a;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}
/* ===== end css/custom-cursor.css ===== */

/* ===== begin refinements.css ===== */
/* =====================================================================
   folio-v2 — REFINEMENT PASS

   Loaded last, on purpose. Targeted layout/aesthetic corrections sitting
   on top of the token + theme layers. Kept in one reviewable file rather
   than edited into the five historical pass stylesheets, so any single
   item is easy to find or revert.
   ===================================================================== */

/* =====================================================================
   1 · HEADER — bare over the hero, frosted below it

   Two states, driven by one class:

     .nav--on-dark   the hero portrait is still painted behind the header.
                     No fill, no border, no blur — just white labels over
                     the video, so nothing boxes in the opening shot.

     (no class)      the hero has handed off and the page surface is
                     behind the header. The frosted capsule fades in and
                     the labels take the theme's ink.

   js/nav-contrast.js owns that class. It tests the hero media's geometry
   and opacity directly rather than reading `.nav--post-hero`, which is
   unreliable: #hero is 400svh, so that class stays false long after the
   video has visually handed off.

   Every property that differs between the two states is transitioned, so
   the capsule materialises rather than snapping in.
   ===================================================================== */
#nav {
  transition: background-color var(--dur-4) var(--ease-out),
    backdrop-filter var(--dur-4) var(--ease-out),
    -webkit-backdrop-filter var(--dur-4) var(--ease-out),
    box-shadow var(--dur-4) var(--ease-out),
    border-color var(--dur-4) var(--ease-out),
    opacity var(--dur-3) var(--ease-out),
    visibility var(--dur-3) var(--ease-out) !important;
}

/* Over the hero — nothing behind the labels. */
#nav.nav--on-dark,
#nav.nav--on-dark.nav--glass,
#nav.nav--on-dark.nav--post-hero {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  /* Returning upward into the hero must be bare before the header becomes
     visible. Material transitions here briefly interpolated the light
     capsule edge over the second story block. The reverse transition,
     into the post-hero capsule, remains smooth via the base #nav rule. */
  transition: opacity var(--dur-3) var(--ease-out),
    visibility var(--dur-3) var(--ease-out) !important;
}

/* Past the hero — the frosted capsule, theme-driven so it reads in both
   light and dark. codex-final-pass-v2.css declares
   `#nav { background: transparent !important }` at ID specificity, so
   this has to match it and come later to land. */
#nav:not(.nav--on-dark),
#nav:not(.nav--on-dark).nav--glass,
#nav:not(.nav--on-dark).nav--post-hero {
  background: var(--surface-glass) !important;
  border: 0 !important;
  box-shadow: var(--shadow-soft) !important;
  backdrop-filter: blur(20px) saturate(1.3) !important;
  -webkit-backdrop-filter: blur(20px) saturate(1.3) !important;
}

/* ---- Fade in / out on scroll ----------------------------------------
   js/nav-contrast.js adds `.nav--hidden` on a downward scroll past 24px,
   and ui-consistency.css hides it by sliding it off the top edge. A
   slide is abrupt and fights the capsule's own fade, so the hide becomes
   an opacity fade instead. js/nav-contrast.js brings it back once
   scrolling settles, in either direction.

   `!important` is required: GSAP writes an inline `opacity` onto #nav
   for its own reveal, and an inline declaration outranks a normal rule.
   -------------------------------------------------------------------- */
/* UPDATE — hiding by opacity alone is what caused the "frosted bar with no
   nav in it" bug on mobile. #nav carries backdrop-filter: blur(20px), and
   iOS Safari composites a backdrop-filter layer independently of its
   element's opacity: mid-fade (and sometimes after it) the blurred plate
   kept painting while the logo and controls did not, so scrolling back up
   showed a frosted strip with nothing in it.

   Translating the bar off the top edge as well means the frosted plate
   physically leaves the viewport instead of relying on opacity to stop it
   painting, which no compositor can get wrong. The opacity fade is kept so
   the motion still reads as a fade rather than a hard slide. The old
   comment about GSAP writing an inline transform no longer applies — the
   GSAP nav reveal was removed with the previous hero. */
#nav.nav--hidden {
  opacity: 0 !important;
  transform: translate3d(0, -140%, 0) !important;
  pointer-events: none !important;
}

/* js/nav-contrast.js sets `.nav--on-dark` whenever the hero media is
   still painted behind the header box, and clears it otherwise — so the
   labels are white over the portrait and take the theme's ink everywhere
   else.

   `.nav__talk` is excluded: it is an <a> inside .nav__links, so it would
   otherwise pick up this colour and go light-on-light against its own
   filled pill. */
#nav.nav--on-dark :is(.nav__brand, .nav__links a:not(.nav__talk), .theme-toggle, .nav__toggle) {
  color: #fff !important;
}
#nav:not(.nav--on-dark) :is(.nav__brand, .nav__links a:not(.nav__talk), .theme-toggle, .nav__toggle) {
  color: var(--text-primary) !important;
}

/* The CTA keeps its filled treatment in both scroll states and both
   themes — it is the one element in the bar that should read as a button. */
#nav .nav__links a.nav__talk {
  background: var(--surface-active) !important;
  color: var(--text-on-active) !important;
  border-color: var(--surface-active);
}

/* The toggle loses its own fill too, so the bar reads as one transparent
   unit rather than a chip floating on nothing. Border uses --line-strong
   (the shared control-border token) instead of currentColor so the ring
   reads as a muted grey, not the same near-white as the sun/moon glyph. */
#nav .theme-toggle {
  background: transparent !important;
}

/* Theme toggle + Let's Talk read as one stacked group, closer together
   than the regular nav-link rhythm around them. */
#nav .nav__cta-group {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* =====================================================================
   2 · WORK — no text movement, unmistakable active state
   ===================================================================== */

/* styles.css:5599-5600 nudged list items ±5px on hover. Colour alone
   carries the hover now, so nothing shifts under the cursor. */
#work .sw__list--left .sw-li:hover,
#work .sw__list--right .sw-li:hover,
#work .sw-li:hover,
#work .sw-li {
  transform: none !important;
  transition: color var(--dur-2) ease, opacity var(--dur-2) ease !important;
}

/* Work detail remains an image-overlay composition in both themes. Global
   heading/body theme rules must not turn this copy black in light mode. */
#work .sw__overlay .sw__ov-title,
#work .sw__overlay .sw__ov-sub {
  color: #fff !important;
  text-shadow: 0 2px 20px rgba(0, 0, 0, .28);
}
#work .sw__overlay .sw-meta-col span { font-weight: 500 !important; }

/* The selected case study goes to the absolute end of the ramp — pure
   black on light, pure white on dark — so which one is active is never
   in question. Everything else recedes well behind it. */
#work .sw-li.is-active {
  color: #000 !important;
  font-weight: 600;
  opacity: 1 !important;
}
:root[data-theme="dark"] #work .sw-li.is-active {
  color: #fff !important;
}
/* theme-system.css reaches these through an :is(#work, …) group, which
   carries ID specificity — so the recede has to match it to land. */
:root[data-theme] body #work .sw-li:not(.is-active),
:root body #work .sw-li:not(.is-active) {
  color: var(--text-tertiary) !important;
  font-weight: 500 !important;
  opacity: .72 !important;
}
:root[data-theme] body #work .sw-li:not(.is-active):hover,
:root body #work .sw-li:not(.is-active):hover {
  color: var(--text-secondary) !important;
  opacity: 1 !important;
}

/* =====================================================================
   3 · BUILT AROUND OUTCOMES — tighter cards
   The card carried 32px of its own padding and then nested another ~18px
   inside .value-card__copy — a ~50px inset on a 293px card. Other
   sections sit nearer 8-9% of card width; this brings it in line.
   ===================================================================== */
#client-value .value-card {
  padding: 18px !important;
}

/* =====================================================================
   5 · PLAYGROUND / MOTION — smaller, bottom-anchored artwork
   `cover` was zooming each 912x1178 frame to 1.086x to fill the 399x560
   card, cropping ~34px off both sides and inflating the artwork.
   Switching to `contain` removes that zoom — the artwork comes down to
   its true proportion and stops running edge-to-edge — and
   `object-position: 50% 100%` pins the frame to the bottom of the card.

   Applied to all three cards, not just Design and Delivery: the title
   and pills live in the same baked frame as the artwork, so scaling only
   two of them dropped their titles out of line with Strategy's. Uniform
   treatment keeps the row aligned.

   NOTE: a further reduction of just the Design/Delivery artwork is not
   reachable from CSS — it needs those two .webm files re-exported with a
   smaller illustration.
   ===================================================================== */
#perspective .pg-panel--motion video {
  object-fit: contain;
  object-position: 50% 100%;
}

/* =====================================================================
   6 · One card surface across every section in dark mode
   ===================================================================== */
:root[data-theme="dark"] body :is(
  .capability-card-v2,
  .value-card,
  .value-card__copy,
  .toolkit-tile,
  .partnership-card,
  .counter,
  .accordion__item
) {
  background-color: var(--surface-card) !important;
}

/* =====================================================================
   7 · TRUSTED PARTNERSHIPS — green stroke on hover (dark mode)
   ===================================================================== */
.partnership-card {
  transition: border-color var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  :root[data-theme="dark"] #partnerships .partnership-card:hover {
    border-color: var(--accent) !important;
    box-shadow: 0 0 0 1px var(--accent), 0 18px 48px rgba(0, 0, 0, .55) !important;
  }
}

/* =====================================================================
   9 · MODERN PRODUCT STACK — kill the hairlines, rebuild the glow

   The stray lines: .toolkit-tile::after painted two 0.5%-wide gradient
   stops — a green one at 62% and a dark one at 68% — as faux grid rules.
   Hard colour stops render as crisp 1px lines, and on a dark card the
   green one reads as a scratch across the tile. Removed outright.

   The glow: .toolkit-tile::before is a radial whose midpoint is
   rgba(221,244,213,.28) — a pale mint that turns milky-grey over #1a1a1a
   — and it sat at opacity .72 permanently, so every tile glowed at rest.
   Replaced with a soft, brand-tinted bloom that is invisible until hover
   and reads correctly in both themes.
   ===================================================================== */
#toolkit .toolkit-tile::after {
  background: none !important;
  opacity: 0 !important;
}
#toolkit .toolkit-tile:hover::after {
  background: none !important;
}

#toolkit .toolkit-tile::before {
  inset: 0 !important;
  border-radius: inherit;
  background: radial-gradient(
    circle at var(--mx, 50%) var(--my, 45%),
    color-mix(in srgb, var(--accent) 30%, transparent),
    color-mix(in srgb, var(--accent) 10%, transparent) 42%,
    transparent 68%
  ) !important;
  filter: blur(26px) !important;
  /* At rest the tile is clean; the bloom is a hover reward. Kept inset:0
     (was -35%) and clipped by the tile's own overflow:hidden, so the glow
     tracks the cursor and stays inside the card instead of blooming past
     its edges. */
  opacity: 0 !important;
  transition: opacity var(--dur-3) var(--ease-out) !important;
}
#toolkit .toolkit-tile:hover::before {
  opacity: .85 !important;
}

/* The lift, the accent edge and a soft ambient shadow that picks up the
   theme's own accent, so the effect matches in light and dark instead of
   being tuned for white cards only. filter:drop-shadow (not box-shadow) is
   deliberate — box-shadow is clipped by this element's own overflow:hidden
   (needed to contain the glow above), which trimmed the shadow to a hard
   edge at the card boundary instead of spreading softly behind it. */
#toolkit .toolkit-tile {
  transition: transform var(--dur-3) var(--ease-out),
    filter var(--dur-3) var(--ease-out),
    border-color var(--dur-3) ease !important;
  filter: drop-shadow(0 0 0 rgba(0, 0, 0, 0)) !important;
}
@media (hover: hover) and (pointer: fine) {
  #toolkit .toolkit-tile:hover {
    border-color: color-mix(in srgb, var(--accent) 45%, transparent) !important;
    transform: translate3d(0, -6px, 0) !important;
    filter:
      drop-shadow(0 10px 20px color-mix(in srgb, var(--accent) 18%, transparent))
      drop-shadow(0 24px 44px rgba(0, 0, 0, .16)) !important;
  }
  :root[data-theme="dark"] #toolkit .toolkit-tile:hover {
    filter:
      drop-shadow(0 10px 24px color-mix(in srgb, var(--accent) 24%, transparent))
      drop-shadow(0 26px 52px rgba(0, 0, 0, .55)) !important;
  }
}

/* Next.js, Three.js and the ChatGPT mark are supplied as black-on-
   transparent artwork, so they sank into the dark tile. These three are
   pure monochrome, so a straight inversion is exact — it does not shift
   any hue, unlike the multi-colour marks (Figma, Framer, Gemini,
   Supabase, Claude), which are left alone. */
:root[data-theme="dark"] #toolkit :is(
  .toolkit-logo--next,
  .toolkit-logo--three,
  .toolkit-ai__logo--chatgpt
) {
  filter: invert(1) !important;
}

/* The copy panel's divider was a literal #e4e4e4 hairline. */

:root[data-theme="dark"] #client-value {
  --vv-stroke: rgba(255, 255, 255, .58);
  --vv-stroke-soft: rgba(255, 255, 255, .3);
  --vv-fill: #2b2b2b;
}

/* value-node-pulse animates toward a light mint mid-keyframe; on dark
   that flashes white, so it pulses to the accent instead. */

@keyframes value-node-pulse-dark {
  0%, 100% { transform: scale(.82); }
  50% {
    transform: scale(1.14);
    fill: color-mix(in srgb, var(--accent) 34%, transparent);
    stroke: var(--accent);
  }
}

/* =====================================================================
   11 · WORK — promote the cover layers for the dissolve
   selected-work.js cross-fades the two covers through a blur. Declaring
   the properties it animates keeps that off the scroll repaint path.
   ===================================================================== */
#work :is(#swCoverPrev, #swCoverTop) {
  will-change: opacity, transform, filter;
  backface-visibility: hidden;
}

/* =====================================================================
   12 · Old Expertise section — hidden pending review
   The `hidden` attribute is only a UA-level `display:none`, and both the
   section and the nav links carry authored `display` rules that override
   it. Restore by removing the `hidden` attributes in index.html; this
   block then matches nothing.
   ===================================================================== */
#services[hidden],
[data-expertise-link][hidden] {
  display: none !important;
}

/* =====================================================================
   8 · FOOTER WORDMARK — full-bleed FOLIO with room for its own blur
   The wordmark is four stacked copies, blurred 5/12/24px and masked into
   a progressive falloff. .folio-footer__media is overflow:hidden for its
   corner radius, so those blurred copies were sliced off at the left,
   right and bottom edges.

   Two changes: size the word off the CONTAINER rather than the viewport
   so it actually spans the image, and inset it by enough pixels that the
   softest layer finishes inside the frame instead of against it.

   3.351 is the measured width-to-font-size ratio of "FOLIO" in Zalando
   Sans Expanded at -0.045em tracking; it is em-proportional, so it holds
   at every size.
   ===================================================================== */
.folio-footer__media {
  container-type: inline-size;
}
.folio-footer__wordmark {
  --fm-inset-x: clamp(20px, 2.4cqw, 46px);
  --fm-inset-b: clamp(24px, 3cqw, 58px);
  right: 0 !important;
  left: 0 !important;
  bottom: var(--fm-inset-b) !important;
}
.folio-footer__word {
  font-size: min(calc((100cqw - (2 * var(--fm-inset-x))) / 3.351), 29rem) !important;
  /* -0.045em of tracking is still applied after the final "O", so the
     element box ends short of the ink and justify-self:center parks the
     word off-centre. Cancelling that trailing advance re-centres it. */
  margin-inline-start: -.0225em;
}

@media (max-width: 900px) {
  .folio-footer__wordmark {
    --fm-inset-x: clamp(14px, 3cqw, 26px);
    --fm-inset-b: clamp(16px, 3.6cqw, 32px);
  }
}

/* =====================================================================
   9 · JULY 2026 INTERACTION REFINEMENT
   ===================================================================== */

/* Playground tabs sit directly on the section in both themes. */

/* Hover changes color, never geometry: the card remains a single 1px line. */
#partnerships .partnership-card,
#partnerships .partnership-card:hover {
  border-width: 1px !important;
  border-style: solid !important;
}
#partnerships .partnership-card { border-color: var(--line); }
#partnerships .partnership-card:hover {
  border-color: var(--line-strong) !important;
  box-shadow: 0 18px 46px color-mix(in srgb, var(--text-primary) 7%, transparent) !important;
}

/* Footer base now uses the former back-to-top position for the email. */
.folio-footer__base {
  grid-template-columns: minmax(0, 1fr) auto;
}
.folio-footer__base > a[href^="mailto:"] {
  display: inline-flex;
  justify-self: end;
  text-align: right;
}

/* Neutral CTA feedback: no brand green in either theme. */
@media (hover: hover) and (pointer: fine) {
  .folio-footer__button--primary:hover,
  .folio-footer__button--primary:focus-visible {
    background: #212121 !important;
    border-color: #212121 !important;
    color: #fff !important;
  }
  :root[data-theme="dark"] .folio-footer__button--primary:hover,
  :root[data-theme="dark"] .folio-footer__button--primary:focus-visible {
    background: #3a3a3a !important;
    border-color: #3a3a3a !important;
    color: #fff !important;
  }
}

/* =====================================================================
   12 · MOBILE SELECTED WORK
   Desktop retains the pinned three-column story. Mobile becomes a focused
   image-led case-study browser derived from the supplied reference.
   ===================================================================== */

.sw__mobile-copy,
.sw__mobile-nav { display: none; }

@media (max-width: 900px) {
  #work .sw__inner {
    padding: var(--header-content-clearance) 20px 52px;
  }
  #work .sw__head { display: block; }
      #work .sw__pills,
  #work .sw__list,
  #work .sw__pager { display: none !important; }
  #work .sw__main {
    display: block;
    min-height: 0;
  }
    #work .sw__thumb {
    width: 100% !important;
    max-height: none !important;
    isolation: isolate;
  }
  #work .sw__thumb::after {
    content: "";
    position: absolute;
    z-index: 1;
    inset: 38% 0 0;
    pointer-events: none;
    background: linear-gradient(to bottom, transparent, rgba(8, 7, 6, .2) 25%, rgba(8, 7, 6, .88) 100%);
  }
  #work .sw__mobile-copy {
    position: absolute;
    z-index: 2;
    right: clamp(24px, 7vw, 34px);
    bottom: clamp(28px, 8vw, 40px);
    left: clamp(24px, 7vw, 34px);
    display: block;
    color: #fff;
    pointer-events: none;
  }
  #work .sw__mobile-copy h3 {
    max-width: 16ch;
    color: #fff !important;
    font-weight: 500;
    letter-spacing: -.045em;
  }
  #work .sw__mobile-copy p {
    margin: 14px 0 0;
    color: rgba(255, 255, 255, .58);
  }
    #work .sw__mobile-nav button {
    font: inherit;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--dur-2) ease, color var(--dur-2) ease, border-color var(--dur-2) ease, transform var(--dur-1) ease;
  }
  #work .sw__mobile-nav button:active { transform: scale(.975); }
  #work .sw__mobile-prev {
    place-items: center;
    border: 1.5px solid var(--text-primary);
  }
  #work .sw__mobile-prev svg {
    width: 28px;
    height: 28px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
  }
  #work .sw__mobile-next {
    width: 100%;
    padding: 0 24px;
    border: 1px solid var(--surface-active);
    color: var(--text-on-active);
    letter-spacing: -.025em;
  }
  #work .sw__mobile-nav.is-first { grid-template-columns: minmax(0, 1fr); }
  #work .sw__mobile-nav.is-first .sw__mobile-next { grid-column: 1 / -1; }
  #work .sw__mobile-nav [hidden] { display: none; }
  html.is-hero-work-handoff #work .sw__mobile-nav { display: none; }
}

@media (max-width: 420px) {
      #work .sw__mobile-nav {
    grid-template-columns: 58px minmax(0, 1fr);
    gap: 12px;
  }
  #work .sw__mobile-nav button { min-height: 58px; }
  #work .sw__mobile-prev { width: 58px; }
}

/* =====================================================================
   11 · WORK STORY + THEME CONTROL POLISH
   ===================================================================== */

/* Work remains the foreground scroll story until its sticky track has
   completed. Expertise then enters in normal document flow underneath it. */
#work {
  /* Work starts one layer below the pinned hero. Promoting it here caused
     its dark surface to cut across the secondary hero statement before the
     handoff had begun. */
  z-index: 1 !important;
  isolation: isolate;
  background: var(--surface-page);
}
#work .sw__inner { background: var(--surface-page); }
#work + #expertise,
#expertise {
  position: relative;
  z-index: 1;
  background: var(--surface-page);
}
html.is-hero-work-handoff #work {
  z-index: 20 !important;
  background: transparent !important;
}
html.is-work-stage-live #work { z-index: 2 !important; }

/* Directional cover wipes are clip-driven; keeping that property promoted
   avoids a final-frame repaint when the old cover is discarded. */
#work :is(#swCoverPrev, #swCoverTop) {
  will-change: clip-path, opacity, transform;
  backface-visibility: hidden;
}
#work .sw-no-trans { transition: none !important; }

/* The control stays still while the page-level Motion sweep communicates
   the theme change. Long per-ray and moon animations were removed so one
   interaction owns the transition timeline. */
.theme-toggle,
.theme-toggle:hover,
.theme-toggle:active {
  transform: none !important;
}
.theme-toggle__sun-ray { opacity: 1; }

/* =====================================================================
   15 · MOBILE WORK — ONE CONTINUOUS PINNED STORY

   The hero handoff, the mobile case-study browser and the release into
   Expertise now share one viewport-sized sticky stage. Scroll advances
   through the project sequence; the Back/Next controls move the same
   ScrollTrigger, so neither input can leave the card in an in-between
   state. Expertise enters only after the final project step completes.
   ===================================================================== */

@media (max-width: 900px) {
  #work {
    height: var(--sw-story-height, 478svh);
  }
  #work .sw__inner,
  html.is-hero-work-handoff #swRoot {
    height: 100svh;
    min-height: 100svh;
    grid-template-rows: auto minmax(0, 1fr) auto;
    padding: clamp(82px, 10svh, 98px) 20px 18px;
    background: var(--surface-page);
  }
  html.is-hero-work-handoff #swRoot {
    position: fixed !important;
    inset: 0 !important;
    z-index: 20 !important;
  }
  #work .sw__head,
  html.is-hero-work-handoff #work .sw__head {
    display: block !important;
  }
  /* The handoff selector deliberately does NOT appear here. Including it
     gave the description a larger font-size while the hero->work handoff
     was active than it has once settled (measured 20.64px vs 15px at
     430px), so the copy visibly shrank the moment the class dropped —
     the "text size larger on reveal" artifact. The handoff now inherits
     whatever governs the resting state, so both read identically. */
  #work .sw__support {
    margin-top: 16px !important;
  }
  #work .sw__main,
  html.is-hero-work-handoff #work .sw__main {
    display: block;
    align-self: stretch;
    min-height: 0;
    height: 100%;
  }
  #work .sw__center,
  html.is-hero-work-handoff #work .sw__center {
    display: grid;
    width: 100%;
    height: 100%;
    min-height: 0;
    place-items: center;
  }
  #work .sw__thumb,
  html.is-hero-work-handoff #work .sw__thumb {
    width: 100% !important;
    max-width: 100% !important;
    height: 100% !important;
    max-height: 100% !important;
    aspect-ratio: auto !important;
  }
  #work .sw__mobile-nav,
  html.is-hero-work-handoff #work .sw__mobile-nav {
    align-self: end;
  }
  #work .sw__mobile-nav[style*="opacity: 0"] {
    pointer-events: none !important;
  }
}

@media (max-width: 420px) {
  #work .sw__inner,
  html.is-hero-work-handoff #swRoot {
    padding: clamp(78px, 9.5svh, 88px) 16px 14px;
  }
  #work .sw__main,
  html.is-hero-work-handoff #work .sw__main {
    padding-top: var(--gutter) !important;
  }
  #work .sw__mobile-nav,
  html.is-hero-work-handoff #work .sw__mobile-nav {
    margin-top: 12px !important;
  }
}

@media (max-width: 900px) {
    .folio-footer__base > a[href^="mailto:"] { justify-self: end !important; }
}

/* =====================================================================
   10 · SECTION STATE AND REVEAL STABILITY
   ===================================================================== */

/* Work tabs float directly on the section, matching Playground. */

/* Approach: selected is always a high-contrast white card. The former
   selected grey becomes the inactive surface in dark mode. */
:root body #process .design-process__step.is-active,
:root[data-theme] body #process .design-process__step.is-active {
  background: #f4f4f4 !important;
  border: 1px solid #d4d4d4 !important;
  color: #111 !important;
}
:root body #process .design-process__step.is-active .design-process__number,
:root[data-theme] body #process .design-process__step.is-active .design-process__number {
  color: #111 !important;
}
:root body #process .design-process__step.is-active .design-process__copy h3,
:root[data-theme] body #process .design-process__step.is-active .design-process__copy h3 {
  color: #111 !important;
}
:root body #process .design-process__step.is-active .design-process__copy p,
:root[data-theme] body #process .design-process__step.is-active .design-process__copy p {
  color: #555 !important;
}
:root[data-theme="dark"] body #process .design-process__step:not(.is-active) {
  background: #1a1a1a;
}

/* Partnership hover preserves the exact default stroke geometry. */
#partnerships .partnership-card:hover {
  outline: 0 !important;
  border-width: 1px !important;
  box-shadow: 0 18px 46px color-mix(in srgb, var(--text-primary) 7%, transparent) !important;
}

/* =====================================================================
   13 · MOBILE HERO + AI TOOLKIT TILE
   The portrait reference uses a smaller media field, a sharp central
   subject with four-sided progressive blur, and centered identity copy.
   Everything here is phone-only; desktop geometry and motion are untouched.
   ===================================================================== */

@media (max-width: 600px) and (orientation: portrait) {

  #hero .hero__titles {
    display: grid !important;
    grid-template-columns: 1fr !important;
    justify-items: center !important;
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 0 clamp(22px, 4.2vh, 36px) !important;
    padding-inline: 22px !important;
    gap: 7px !important;
    text-align: center !important;
  }
  #hero .hero__titles > span {
    justify-self: center !important;
    width: 100%;
    max-width: 100%;
    overflow: visible;
    padding: 0;
    text-align: center !important;
  }
  #hero .hero__titles .hero__title-text { display: none !important; }
  #hero .hero__titles > span::after {
    content: attr(data-mobile-label);
    display: block;
    white-space: normal;
  }
  #hero .hero__titles > span:first-child::after {
    color: #fff;
    font-size: clamp(1.5rem, 7.2vw, 1.85rem);
    font-weight: 500;
    line-height: 1.05;
    letter-spacing: -.045em;
  }
  #hero .hero__titles > span:last-child::after {
    color: rgba(255, 255, 255, .36);
    font-size: clamp(.78rem, 3.8vw, .94rem);
    font-weight: 600;
    line-height: 1.15;
    letter-spacing: -.025em;
  }

}

/* =====================================================================
   MOBILE LINEAR EXPERIENCE
   Desktop retains the pinned hero and Selected Work explorer. Phones use
   normal document flow: scaled film, two readable statements, filterable
   stacked project cards. Keep this final in the cascade intentionally.
   ===================================================================== */
.sw-mobile-cards {
  display: none;
}

@media (max-width: 900px) {
  
  /* The desktop story remains in the DOM for accessibility and breakpoint
     continuity, but the visual mobile version lives immediately below. */

  #work.sw {
    height: auto;
    background: var(--surface, #fafafa);
  }

  #work .sw__inner {
    min-height: 0;
    padding: 76px 20px 96px;
  }

  #work .sw__head {
    gap: 24px;
  }

  #work .sw__pills {
    margin-top: 2px;
  }

  #work .sw__main,
  #work .sw__mobile-nav,
  #work .sw__pager,
  #work .sw__overlay {
    display: none;
  }

  .sw-mobile-cards {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
  }

  .sw-mobile-card {
    -webkit-tap-highlight-color: transparent;
  }

  .sw-mobile-card:active {
    transform: scale(.985);
  }

  .sw-mobile-card__media {
    position: relative;
    overflow: hidden;
  }

  .sw-mobile-card__media :is(img, video) {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
  }

  .sw-mobile-card__copy {
    display: grid;
  }

  .sw-mobile-card__description {
    max-width: 42ch;
  }

}

@media (prefers-reduced-motion: reduce) and (max-width: 900px) {
  .sw-mobile-card {
    opacity: 1 !important;
    transform: none !important;
  }
}

/* Final footer baseline guard. Kept at the end of the cascade because this
   legacy stylesheet contains earlier footer annotation passes. */
.folio-footer__wordmark {
  --fm-inset-b: clamp(100px, 7cqw, 140px) !important;
}
@media (max-width: 900px) {
  .folio-footer__wordmark {
    --fm-inset-b: clamp(68px, 12cqw, 88px) !important;
  }
}

/* =====================================================================
   SOHAN J. LOCKUP — FINAL SPACING AND FOOTER FIT
   ===================================================================== */

.folio-footer__wordmark {
  --fm-inset-b: clamp(80px, 7cqw, 124px) !important;
}
.folio-footer__word {
  font-size: min(calc((100cqw - (2 * var(--fm-inset-x))) / 5.19), 22rem) !important;
  letter-spacing: -.045em !important;
}

@media (max-width: 900px) {
  
  .folio-footer__wordmark {
    --fm-inset-b: clamp(68px, 12cqw, 88px) !important;
  }
}

/* =====================================================================
   AUGUST 2026 ANNOTATION PASS — precise mobile ordering and polish
   ===================================================================== */

/* Footer wordmark: one crisp layer, equally inset from the three image
   edges. The former stacked masks intentionally blurred its lower half. */
.folio-footer__wordmark {
  --fm-inset-x: clamp(14px, 3cqw, 30px) !important;
  --fm-inset-b: clamp(14px, 3cqw, 30px) !important;
}
.folio-footer__word--base {
  opacity: 1 !important;
  filter: none !important;
}
.folio-footer__word--s1,
.folio-footer__word--s2,
.folio-footer__word--s3 {
  display: none !important;
}

/* A slightly taller, unmasked viewport preserves the rolling reel while
   preventing large mobile numerals from losing their top/bottom pixels. */
#expCounters .reel__col {
  height: var(--reel-cell, 1em) !important;
  margin-block: 0;
  -webkit-mask-image: none !important;
  mask-image: none !important;
}

/* Process indices use the same tight display tracking as the site type
   system instead of the browser's loose numeric spacing. */
#process .design-process__number {
  font-variant-numeric: tabular-nums;
}

/* Expertise ends cleanly and its mobile live cover sits between the title
   control and the panel containing description and tags. */
#expertise::after {
  display: none !important;
}

/* Work detail titles are typographic only and remain optically centred. */
#work .sw__ov-copy {
  align-items: center;
  text-align: center;
}
#work .sw__ov-title::before,
#work .sw__ov-title > svg,
#work .sw__ov-title > i {
  display: none !important;
  content: none !important;
}

/* FAQ height follows its content intrinsically; the icon rotation and
   answer movement share one easing curve for a single smooth gesture. */
#insights .accordion__body {
  will-change: grid-template-rows, opacity;
  transition: grid-template-rows var(--dur-3) var(--ease-out),
    opacity var(--dur-3) ease !important;
}
#insights .accordion__body > p {
  transition: transform var(--dur-3) var(--ease-out),
    padding-bottom var(--dur-3) var(--ease-out) !important;
}
#insights .accordion__icon {
  transition: transform var(--dur-3) var(--ease-out), border-color var(--dur-2) ease !important;
}
#insights .accordion__item.is-open .accordion__icon {
  transform: rotate(45deg);
}

@media (max-width: 760px) {
  /* Logos are centred in the visual region above the fixed copy block. */

  #contact #contactTitle {
    margin-bottom: 30px !important;
  }
  }

/* =====================================================================
   14 · MOBILE HANDOFF GEOMETRY + PERSISTENT NATIVE HEADER

   The fixed hero-to-work handoff used an older 4:5 thumbnail stage. That
   made the first painted frame wider and shorter than the final 13:20
   mobile case-study card, so the card visibly snapped after the handoff.
   The fixed and in-flow states now share the exact same geometry.

   Mobile also keeps one capsule header throughout the experience. Only
   its material and foreground colours respond to the surface beneath it;
   dimensions and control placement never change during a theme switch.
   ===================================================================== */

@media (max-width: 600px) {
  html.is-hero-work-handoff #swRoot {
    display: block !important;
    box-sizing: border-box !important;
    width: 100% !important;
    max-width: 100vw !important;
    padding: var(--header-content-clearance) 20px 52px !important;
    overflow: hidden !important;
    background: var(--surface-page) !important;
  }
  html.is-hero-work-handoff #work .sw__head {
    display: block !important;
  }
  html.is-hero-work-handoff #work .sw__support {
    max-width: 29ch !important;
    margin-top: 28px !important;
  }
  html.is-hero-work-handoff #work .sw__main {
    display: block !important;
    min-height: 0 !important;
    padding: 42px 0 0 !important;
  }
  html.is-hero-work-handoff #work .sw__center {
    display: block !important;
    width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
  }
  html.is-hero-work-handoff #work .sw__thumb {
    width: 100% !important;
    max-width: 100% !important;
    height: auto !important;
    max-height: none !important;
    aspect-ratio: 13 / 20 !important;
    border-radius: clamp(20px, 6vw, 28px) !important;
  }

  #nav,
  #nav.nav--on-dark,
  #nav:not(.nav--on-dark) {
    inset: 12px 12px auto !important;
    backdrop-filter: blur(20px) saturate(1.3) !important;
    -webkit-backdrop-filter: blur(20px) saturate(1.3) !important;
  }
    #nav:not(.nav--on-dark) {
    border-color: var(--line) !important;
    background: var(--surface-glass) !important;
    box-shadow: var(--shadow-soft) !important;
  }
  #nav::before { display: none !important; }
  #nav .nav__brand {
    width: 42px;
  }
  #nav .theme-toggle--mobile {
    margin-left: auto;
    border: 0 !important;
    background: color-mix(in srgb, currentColor 10%, transparent) !important;
  }
  #nav .nav__toggle {
    width: 46px;
    height: 46px;
  }
  #nav .nav__toggle svg {
    stroke-width: 1.7;
  }
}

@media (max-width: 420px) {
  html.is-hero-work-handoff #swRoot {
    padding-inline: 16px !important;
  }
  html.is-hero-work-handoff #work .sw__main {
    padding-top: 36px !important;
  }
}

/* Mobile Work filters stay visible inside the pinned stage. The default
   four-column row fits current phone widths; genuinely narrow screens use
   a complete 2x2 grid instead of horizontal scrolling or clipped labels. */
@media (max-width: 900px) {
  #work .sw__pills,
  html.is-hero-work-handoff #work .sw__pills {
    position: static !important;
    margin: 14px 0 0 !important;
  }
  }

/* Final cascade guard: the pinned mobile stage must retain the same grid
   while the hero handoff class is present. This intentionally follows the
   earlier geometry migration rules so there is no intermediate block-flow
   frame before sticky ownership begins. */
@media (max-width: 900px) {
  html.is-hero-work-handoff #swRoot {
    position: fixed !important;
    inset: 0 !important;
    display: grid !important;
    height: 100svh !important;
    min-height: 100svh !important;
    grid-template-rows: auto minmax(0, 1fr) auto !important;
    padding: clamp(82px, 10svh, 98px) 20px 18px !important;
  }
  html.is-hero-work-handoff #work .sw__support {
    max-width: 31ch !important;
    margin-top: 16px !important;
  }
  html.is-hero-work-handoff #work .sw__main {
    height: 100% !important;
    padding-top: 24px !important;
  }
  html.is-hero-work-handoff #work .sw__center {
    display: grid !important;
    height: 100% !important;
    place-items: center !important;
  }
  html.is-hero-work-handoff #work .sw__thumb {
    width: 100% !important;
    height: 100% !important;
    max-height: 100% !important;
    aspect-ratio: auto !important;
  }
}

@media (max-width: 420px) {
  html.is-hero-work-handoff #swRoot {
    padding: clamp(78px, 9.5svh, 88px) 16px 14px !important;
  }
  html.is-hero-work-handoff #work .sw__main {
    padding-top: var(--gutter) !important;
  }
}

/* =====================================================================
   MOBILE CONTROL CONTRAST

   Icon artwork inherits the semantic colour of its control. This defeats
   the historical "hamburger always white" rule while preserving white
   icons on genuinely dark media/overlay controls. The parent surface now
   owns contrast in light, dark, scrolled and menu-open states.
   ===================================================================== */
@media (max-width: 1024px) {
  #nav .nav__toggle svg {
    color: inherit;
    stroke: currentColor;
  }
  #nav:not(.nav--on-dark) .nav__toggle,
  #nav:not(.nav--on-dark) .theme-toggle--mobile {
    color: var(--text-primary) !important;
  }
  
  :is(
    #nav .theme-toggle--mobile,
    #work .sw__mobile-prev,
    #work .sw__mobile-next,
    .pg-aerial__fullscreen,
    .pg-preview__close,
    .pg-preview__nav,
    .cs__close,
    .to-top
  ) :is(svg, .lucide) {
    stroke: currentColor;
  }

    #work .sw__mobile-next {
    color: var(--text-on-active);
  }
}

/* Static and hover-driven components: the hint is only useful while the
   pointer is actually on them, so it is withdrawn by default and granted
   on hover. `.tool` chips alone accounted for 22 permanent layers. */
:is(.toolkit-tile, .capability-card-v2, .partnership-card, .value-card,
    .exp__row, .accordion__item, .design-process__step,
    .design-process__number, .design-process__copy,
    .tool, .flow__stage, .hero__slice-inner) {
  will-change: auto !important;
}
@media (hover: hover) and (pointer: fine) {
  :is(.toolkit-tile, .capability-card-v2, .partnership-card, .value-card, .tool):hover {
    will-change: transform !important;
  }
}

/* The hero story's per-character spans are hinted permanently in
   codex-final-pass-v2.css. They only animate inside the hero, so once the
   handoff to Work has happened the layers are pure overhead. */
html.is-work-stage-live :is(.hero-story__char, .hero-story__word, .nav-reveal__char) {
  will-change: auto !important;
}

/* =====================================================================
   MOBILE ANNOTATION PASS — compact controls, collision-free grids
   ===================================================================== */

/* Intrinsic grid-height interpolation keeps FAQ answers smooth regardless
   of their copy length; fixed max-height values snapped on shorter answers. */
#insights .accordion__body {
  display: grid !important;
  grid-template-rows: 0fr;
  max-height: none !important;
  opacity: 0;
  overflow: hidden;
  transition: grid-template-rows var(--dur-3) var(--ease-out),
    opacity var(--dur-2) ease !important;
}
#insights .accordion__body > p {
  min-height: 0;
  overflow: hidden;
  transform: translateY(-6px);
  transition: transform var(--dur-3) var(--ease-out),
    padding-bottom var(--dur-3) var(--ease-out) !important;
}
#insights .accordion__item.is-open .accordion__body {
  grid-template-rows: 1fr;
  opacity: 1;
}
#insights .accordion__item.is-open .accordion__body > p {
  /* --gutter is a page-level 4vw measure (~53px here) — far more air than
     a two-line answer needs, and it read as dead space before the next
     question once the row padding came down. */
  padding-bottom: clamp(14px, 1.8vh, 22px) !important;
  transform: translateY(0);
}

@media (max-width: 760px) {
  /* Process artwork is uniformly reduced so no alpha-video frame clips. */
  #process .design-process__visual {
    display: grid;
    height: 190px;
    place-items: center;
  }
  #process .design-process__visual video {
    width: 84% !important;
    height: 84% !important;
    margin: auto !important;
    object-fit: contain !important;
    object-position: center !important;
    transform: scale(1) !important;
  }

  /* Explicit automatic rows prevent the full-width AI tile from sharing a
     stale fixed row with the two tiles below it. */

  #experience .counter__key {
    max-width: 20ch !important;
    white-space: normal;
    font-family: var(--font-sans, Geist, sans-serif);
    letter-spacing: -.01em !important;
  }
  
  }

@media (max-width: 600px) {
  #nav,
  #nav.nav--on-dark,
  #nav:not(.nav--on-dark) {
    padding: 7px 8px 7px 14px !important;
  }
  #nav .nav__brand {
    width: 34px;
  }
  #nav .theme-toggle--mobile {
    margin-right: 4px !important;
  }
  #nav .nav__toggle {
    width: 40px;
    height: 40px;
  }
  #nav .nav__toggle svg {
    width: 24px !important;
    height: 24px !important;
  }
}

/* Final cascade guard for the August annotation pass. This file contains
   several historical responsive passes; these rules intentionally sit last
   so the current component decisions cannot be undone by an older pass. */
.folio-footer__wordmark {
  --fm-inset-x: clamp(14px, 3cqw, 30px) !important;
  /* Was clamp(4px, 0.6cqw, 10px) — near-zero, so FOLIO's baseline sat
     right against the footer image's bottom edge with no breathing room. */
  --fm-inset-b: clamp(16px, 2.4cqw, 40px) !important;
}
.folio-footer__word--base {
  opacity: 1 !important;
  filter: none !important;
}
.folio-footer__word--s1,
.folio-footer__word--s2,
.folio-footer__word--s3 { display: none !important; }

#expCounters .reel__col {
  height: var(--reel-cell, 1em) !important;
  margin-block: 0;
  -webkit-mask-image: none !important;
  mask-image: none !important;
}
#process .design-process__number {
  font-variant-numeric: tabular-nums;
}
#expertise::after { display: none !important; }
#work .sw__ov-copy {
  align-items: center;
  text-align: center;
}
#work .sw__ov-title::before,
#work .sw__ov-title > svg,
#work .sw__ov-title > i {
  display: none !important;
  content: none !important;
}
#insights .accordion__body {
  will-change: grid-template-rows, opacity;
  transition: grid-template-rows var(--dur-3) var(--ease-out),
    opacity var(--dur-3) ease !important;
}
#insights .accordion__body > p {
  transition: transform var(--dur-3) var(--ease-out),
    padding-bottom var(--dur-3) var(--ease-out) !important;
}
#insights .accordion__icon {
  transition: transform var(--dur-3) var(--ease-out), border-color var(--dur-2) ease !important;
}
#insights .accordion__item.is-open .accordion__icon { transform: rotate(45deg); }

@media (max-width: 760px) {

  #contact #contactTitle { margin-bottom: 30px !important; }
  }

/* =====================================================================
   AUGUST 2026 MOTION + FOOTER DETAIL PASS
   ===================================================================== */

/* CTA hover communicates through a quiet dissolve only—never geometry. */
.folio-footer__button,
.folio-footer__button:hover,
.folio-footer__button:focus-visible {
  transform: none !important;
  translate: 0 0 !important;
}
.folio-footer__button {
  transition: color var(--dur-3) var(--premium-ease),
    background-color var(--dur-3) var(--premium-ease),
    border-color var(--dur-3) var(--premium-ease),
    opacity var(--dur-3) ease !important;
}

/* =====================================================================
   Availability badge — pill shape + live dot

   The site's control radius is square (--radius-control: 0px), which is
   right for buttons but reads as a hard chip on a status label. These
   badges are pills wherever they appear, and their dot pulses so the
   "available" state looks live rather than printed.
   ===================================================================== */
/* tokens.css runs an id-scoped square-radius sweep over everything inside
   the page sections, with !important — so the pill has to out-specify it
   (an id plus repeated attribute selectors), the same trick the FAQ icon
   uses in css/playground-all.css. */
:root body #contact .folio-footer__availability[class][class],
.folio-footer__availability {
  border-radius: 999px;
}
:root body #contact .folio-footer__availability[class][class] > i {
  border-radius: 50%;
}

@keyframes availability-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(77,206,22,.45); }
  70%  { box-shadow: 0 0 0 9px rgba(77,206,22,0); }
  100% { box-shadow: 0 0 0 0 rgba(77,206,22,0); }
}
.folio-footer__availability i {
  animation: availability-pulse 2.1s var(--ease-in-out) infinite;
}
@media (prefers-reduced-motion: reduce) {
  .folio-footer__availability i {
    animation: none;
  }
}

/* Dark frosted controls remain legible over both light and dark media. */
.folio-footer__availability,
:root[data-theme="dark"] .folio-footer__availability {
  color: rgba(255,255,255,.94);
  border-color: rgba(255,255,255,.22);
  background: rgba(13,16,17,.68);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.14),
    0 16px 42px rgba(0,0,0,.24);
  -webkit-backdrop-filter: blur(18px) saturate(1.18) !important;
  backdrop-filter: blur(18px) saturate(1.18) !important;
}
.to-top,
.to-top:hover,
.to-top:focus-visible {
  color: #fff !important;
  border: 1px solid rgba(255,255,255,.18) !important;
  background: rgba(13,16,17,.72) !important;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.13),
    0 14px 38px rgba(0,0,0,.28) !important;
  -webkit-backdrop-filter: blur(18px) saturate(1.15) !important;
  backdrop-filter: blur(18px) saturate(1.15) !important;
}

@media (max-width: 760px) {
  /* A fixed three-row composition centres every transparent process clip
     between its large index and copy, independent of the asset bounds. */
  #process .design-process__step {
    align-items: stretch;
  }
  #process .design-process__visual {
    place-self: center;
    display: grid;
    width: min(100%, 300px);
    height: 244px;
    aspect-ratio: auto !important;
    place-items: center;
  }
  #process .design-process__visual video {
    width: 100% !important;
    height: 100% !important;
    max-width: 300px !important;
    max-height: 244px !important;
    margin: 0 !important;
    object-fit: contain !important;
    object-position: center !important;
    transform: none !important;
  }

  /* Keep the odometer glyphs intact at the right edge without loosening
     the site's display tracking. */
  #expCounters .counter__value {
    box-sizing: content-box;
  }
  #expCounters .reel,
  #expCounters .reel__fixed { overflow: visible !important; }

  /* Footer legal and email become a readable, centred two-line epilogue. */
  #contact .folio-footer__base {
    grid-template-columns: 1fr !important;
    justify-items: center;
    gap: 8px !important;
    padding-top: var(--gutter);
    line-height: 1.45;
    text-align: center;
  }
  #contact .folio-footer__base > span,
  #contact .folio-footer__base > a[href^="mailto:"] {
    justify-self: center;
  }
}

/* =====================================================================
   10 · PLAYGROUND — stop the tab row from shifting on selection

   #perspective is display:grid with three `auto` row tracks inside a
   fixed min-height:100svh box. Default align-content:stretch distributes
   that fixed space across all three tracks proportionally to their
   content size — so switching panels (whose stage row has a different
   intrinsic height) reflows rows 1 and 2 as well, even though the title
   and tabs never changed. Pinning content to the top stops the tab row
   from moving when only the stage panel changes.
   ===================================================================== */
#perspective {
  align-content: start !important;
}

/* Playground keeps the same visual composition while scroll advances its
   four authored tabs. The inner stage pins for the story; the outer section
   provides only the measured travel, then releases directly into Trusted
   Partnerships. Small screens retain the existing touch-first tab layout. */
#perspective .pg-gallery__sticky {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: minmax(0, 1fr) minmax(260px, 32%);
  grid-template-rows: auto auto minmax(0, 1fr);
  width: 100%;
  min-width: 0;
}

@media (min-width: 901px) and (min-height: 700px) and (prefers-reduced-motion: no-preference) {
  #perspective {
    /* 340svh gives each of the four tabs 50-65vh of scroll. At 265svh the
       middle tabs flashed past in ~200px, so one trackpad flick skipped two. */
    height: 340svh !important;
    min-height: 340svh !important;
    align-content: stretch !important;
  }
  #perspective .pg-gallery__sticky {
    position: sticky;
    top: 0;
    grid-template-rows: auto auto minmax(0, 1fr);
    align-content: start;
    width: 100%;
    height: 100svh;
    min-height: 100svh;
    padding-top: clamp(88px, 10vh, 116px);
    padding-bottom: clamp(38px, 5vh, 58px);
    overflow: hidden;
  }
      #perspective .pg-gallery__stage {
    height: auto !important;
    min-height: 0;
  }
}

@media (max-width: 900px) {
  #perspective .pg-gallery__sticky {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto auto minmax(0, 1fr);
  }
}

@media (max-width: 720px) {
  }

/* =====================================================================
   11 · HERO -> WORK HANDOFF — no rounded corners mid-transition

   .sw__thumb's resting mobile card is intentionally rounded (border-radius:
   clamp(20px,6vw,28px), set at several responsive breakpoints above). Blown
   up to near-fullscreen size during the hero handoff, that same rounding
   read as a large, unexplained rounded rectangle floating over the
   dissolving hero for the ~1s the crossfade takes. Sits last in the file so
   it beats every earlier breakpoint-specific radius while the handoff is
   active; CSS hands the corners back the instant is-hero-work-handoff is
   removed and the card settles into its normal grid position.
   ===================================================================== */
html.is-hero-work-handoff #work .sw__thumb {
  /* The handoff now uses the exact resting card geometry. */
  border-radius: 0 !important;
}
@media (max-width: 900px) {
  html.is-hero-work-handoff #work .sw__thumb {
    border-radius: clamp(20px, 6vw, 28px) !important;
  }
}

/* =====================================================================
   12 · HERO PRELOAD GATE
   The same monogram-to-portrait choreography now opens every viewport.
   ===================================================================== */
#pageLoader {
  align-items: center;
  justify-content: center;
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: #0a0a0a;
  will-change: opacity;
}
/* Belt-and-braces with the `hidden` attribute in the markup: nothing,
   including the mobile rule below, can reveal the loader until the
   inline script deliberately clears `hidden`. */
#pageLoader[hidden] { display: none; }

.page-loader__mark {
  position: relative;
  width: clamp(37.12px, 3.84vw, 58.88px);
  transform-origin: 50% 50%;
  will-change: transform, opacity;
}
.page-loader__layer {
  display: block;
  width: 100%;
}
.page-loader__base { fill: #545454; }
  /* Reveal window: the script clips it back from the top as real buffered
     bytes arrive, so the white mark fills the grey one bottom-up. The
     inner svg is full size and bottom-pinned, so this uncovers it rather
     than squashing it.

     clip-path is applied to THIS wrapper, never to the <svg> itself:
     clip-path on an svg element does not interpolate here (measured
     frozen near inset(100%) whether fed a calc()+custom-property or a
     plain inset()), whereas on a regular element it tracks exactly.
     Clipping also keeps this off the layout path — an earlier version
     animated the wrapper's height, which worked but thrashed layout. */
.page-loader__fillclip {
  position: absolute;
  inset: 0;
  overflow: hidden;
  clip-path: inset(100% 0 0 0);
  transition: clip-path var(--dur-2) linear;
}
.page-loader__fill {
  position: absolute;
  left: 0;
  bottom: 0;
  fill: #fff;
}
@media (max-width: 900px) {
  .page-loader__mark { width: clamp(33.28px, 8.96vw, 43.52px); }
  
}

@media (prefers-reduced-motion: reduce) {
  .page-loader__fillclip { transition: none; }
}

/* Mobile identity lockup: location is desktop-only, while the role and
   name remain one compact unit above the fold. This sits after the older
   responsive title rules so no generated duplicate label reserves space. */
@media (max-width: 900px) {
  #hero .hero__titles {
    gap: 0 !important;
    margin-bottom: 4px !important;
  }
  #hero .hero__titles > .hero__title-line:first-child {
    display: none !important;
  }
  #hero .hero__titles > .hero__title-line:last-child .hero__title-text {
    font-size: 1rem !important;
    line-height: 1.15 !important;
  }
}

/* =====================================================================
   APPROACH ICON SYSTEM
   Animated line icons replace codec-dependent transparent videos. The
   active card restarts its own concise motion; hover/focus can replay it.
   ===================================================================== */
#process .design-process__visual {
  display: grid;
  place-items: center;
  overflow: visible !important;
}

/* Launch - plane darts forward off its resting position, fold-line draws
   in behind it, then it settles back for the next replay. */

@keyframes process-needle-settle { 0%{transform:rotate(0)} 30%{transform:rotate(48deg)} 55%{transform:rotate(-26deg)} 78%{transform:rotate(10deg)} 100%{transform:rotate(0)} }
@keyframes process-layer-shift { 0%,100%{transform:translateY(0);opacity:.7} 45%{transform:translateY(var(--layer-shift, 0));opacity:1} }
@keyframes process-draw { from{stroke-dashoffset:70;opacity:.15} to{stroke-dashoffset:0;opacity:1} }
@keyframes process-bubble-rise { 0%{opacity:0;transform:translateY(0) scale(.4)} 40%{opacity:1;transform:translateY(-2px) scale(1)} 100%{opacity:0;transform:translateY(-6px) scale(.8)} }
@keyframes process-plane-launch { 0%,100%{transform:translate(0,0);opacity:1} 55%{transform:translate(4px,-4px);opacity:.55} }

/* Final state ownership: the hero uses a completely bare header; the
   frosted capsule only materialises after the hero has left the header
   band, and dissolves again when the user returns. This final block also
   beats the historical mobile rule that painted the capsule constantly. */
#nav.nav--on-dark,
#nav.nav--on-dark.nav--glass,
#nav.nav--on-dark.nav--post-hero {
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
  transition: opacity var(--dur-3) var(--ease-out),
    visibility var(--dur-3) var(--ease-out) !important;
}
#nav:not(.nav--on-dark),
#nav:not(.nav--on-dark).nav--glass,
#nav:not(.nav--on-dark).nav--post-hero {
  background: var(--surface-glass) !important;
  border: 0 !important;
  box-shadow: var(--shadow-soft) !important;
  -webkit-backdrop-filter: blur(20px) saturate(1.3) !important;
  backdrop-filter: blur(20px) saturate(1.3) !important;
}

/* Give the footer wordmark a true optical baseline inset. Large display
   glyphs extend below their line box, so this is intentionally roomier
   than ordinary text padding. */
.folio-footer__wordmark {
  --fm-inset-b: clamp(72px, 6.5cqw, 112px) !important;
}
@media (max-width: 900px) {
  .folio-footer__wordmark {
    --fm-inset-b: clamp(52px, 11cqw, 74px) !important;
  }
}

/* Absolute final footer baseline guard: this must follow every historical
   footer annotation block in the stylesheet. */
.folio-footer__wordmark {
  --fm-inset-b: clamp(10px, 1.25cqw, 22px) !important;
}
@media (max-width: 900px) {
  .folio-footer__wordmark {
    --fm-inset-b: clamp(8px, 2.4cqw, 14px) !important;
  }
}

/* =====================================================================
   MOBILE — simplified scroll architecture

   The hero's ~360-400svh height (annotation-pass-v3.css, !important) and
   the .hero__sticky position:sticky pin exist to give GSAP's storyTimeline
   scrub something to scroll through on desktop. js/main.js already skips
   that scrub entirely on mobile (max-width: 900px) — this is the matching
   CSS half: without it, the empty multi-screen-tall track would still be
   there even though nothing scrubs through it anymore. #work's own mobile
   override two files over (codex-final-pass-v2.css) already does the same
   thing for the Work carousel — position: relative, not static, so
   position: absolute children (.hero__frames, the video/fallback image)
   keep a containing block and don't fall out of layout.

   scroll-snap-type: y PROXIMITY, not mandatory — it settles on a section
   boundary once a scroll gesture ends near one, but never fights a
   gesture mid-flight or traps keyboard/assistive scrolling the way
   mandatory is known to. Lenis isn't in the way here: it only intercepts
   wheel input (smoothWheel: true, no syncTouch), so touch scrolling on
   this site has always been native. ===================================== */
@media (max-width: 900px) {
  /* Reverted the previous "stack everything statically" restructuring —
     it read as broken/flat rather than simplified. Back to the original
     sticky + absolute-overlay crossfade pattern (.hero__sticky stays
     position: sticky, .hero__story/.hero-story__panel stay position:
     absolute; inset: 0, all untouched, unlike the last pass), just with
     a much shorter track: 300svh (portrait hold+fade, text block 1,
     text block 2) instead of desktop's ~360-400svh. js/main.js drives
     the crossfade with a scrub timeline scoped to opacity/scale/y only —
     no filter: blur(), no per-word splitting — which is what actually
     caused the jank complained about earlier, not scroll-linked opacity
     itself. */
  
  /* Work overlaps the final hero viewport and rises over it as a real layer.
     Desktop uses a fixed handoff coordinator; mobile gets the same visual
     replacement through normal document movement, which stays reliable as
     Safari's browser chrome expands and collapses. */
  
  /* Native touch momentum owns mobile scrolling. Proximity snapping could
     jump across an entire hero chapter when Safari collapsed its address
     bar, skipping the Motion transition and exposing a blank sticky frame. */
    
  /* Use the same real, split title characters as desktop. An older mobile
     presentation hid them and painted duplicate ::after labels, so Motion
     was animating invisible content while the replacement text stayed still. */
  #hero .hero__titles .hero__title-text {
    display: inline-flex !important;
    flex-wrap: wrap;
    justify-content: center;
    align-items: baseline;
  }

  #hero .hero__titles > span::after {
    content: none !important;
    display: none !important;
  }

  #hero .hero__titles > span:first-child .hero__title-text {
    color: #fff;
    font-size: clamp(1.5rem, 7.2vw, 1.85rem);
    font-weight: 500;
    line-height: 1.05;
    letter-spacing: -.045em;
  }

  #hero .hero__titles > span:last-child .hero__title-text {
    color: rgba(255, 255, 255, .46);
    font-size: clamp(.78rem, 3.8vw, .94rem);
    font-weight: 600;
    line-height: 1.15;
    letter-spacing: -.025em;
  }

  #hero .hero__title-char {
    display: inline-block;
    backface-visibility: hidden;
  }

}

/* Mobile navigation uses one shared control box for the mark, theme button
   and hamburger. Their glyphs differ optically, but their hit areas now sit
   on the exact same vertical axis at every mobile breakpoint. */
@media (max-width: 900px) {
  #nav {
    --mobile-nav-control: 46px;
    align-items: center;
  }

  #nav .nav__brand {
    flex: 0 0 var(--mobile-nav-control) !important;
    width: var(--mobile-nav-control);
    height: var(--mobile-nav-control) !important;
    align-self: center;
  }

  #nav .nav__brand svg {
    width: 42px;
    height: 27px;
  }

  #nav .theme-toggle--mobile,
  #nav .nav__toggle {
    flex-basis: var(--mobile-nav-control);
    width: var(--mobile-nav-control);
    height: var(--mobile-nav-control);
    min-width: var(--mobile-nav-control) !important;
    min-height: var(--mobile-nav-control) !important;
    align-self: center;
  }

  #nav .nav__toggle {
    line-height: 0;
  }

  }

@media (max-width: 600px) {
  #nav {
    --mobile-nav-control: 42px;
  }

  #nav .nav__brand svg {
    width: 34px !important;
    height: 22px !important;
  }
}
/* ===== end refinements.css ===== */

/* ===== begin css/expertise.css ===== */
/* =====================================================================
   EXPERTISE — scroll-pinned discipline explorer (ported into the site)

   The standalone preview carried its own warm-Swiss palette and a
   Zalando display face. This version drops all of that and reads only
   from tokens.css, so the section inherits the site's surfaces, ink
   ramp, brand accent, radii, easing and type scale. Nothing here
   hard-codes a colour that a token already covers.

   Type follows the other section heads: Geist at --codex-section-title,
   weight 500, -0.055em — the same treatment as "Modern Product Stack"
   and "Playground", rather than the spec's 11vw expanded display.
   ===================================================================== */

#expertise {
  position: relative;
  background: var(--surface-page);
  /* The .section class contributes its own vertical padding, which
     pushed the pinned stage 94px down inside the section — so a stage
     that is exactly 100svh tall overflowed the viewport by that much.
     The stage owns its own top/bottom padding, so the section's is
     zeroed rather than double-counted. */
  padding-block: 0 !important;
}

/* The pinned element — exactly one viewport tall.

   Content is top-aligned and the body then STRETCHES to fill what is
   left, rather than the whole block being centred. Centring a 635px
   composition inside a 933px viewport left ~149px of dead space above
   the title and the same below, which read as far looser than every
   other section. Filling instead means the section is as dense as its
   neighbours and the title sits just under the header. */
.xp__stage {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  /* Slightly more top clearance than the other sections (they sit their
     h2 ~85px down) and that is deliberate: those titles only pass under
     the floating header for a moment while scrolling, whereas this
     section is PINNED, so its title would sit behind the capsule the
     whole time. ~103px at a 933px viewport clears the header's bottom
     edge (80px) with room to breathe. */
  padding-block: clamp(5.5rem, 11vh, 7.5rem) clamp(2rem, 5vh, 3.5rem);
}

/* ---- Head: mirrors .toolkit__head / .client-value__head -------------- */
.xp__head {
  display: grid;
  gap: clamp(2rem, 5vw, 6rem);
  align-items: start;
}
.xp__title {
  /* --fs-section-title, not --codex-section-title: measured against the
     live page, every other section head ("Modern Product Stack",
     "Playground", "Trusted Partnerships") resolves to 48px via
     ui-consistency's cap, and the codex scale would have run this one
     ~7px larger than its neighbours. */
  font-size: var(--fs-section-title);
}
.xp__lede {
  justify-self: end;
  max-width: 34ch;
}

/* ---- Accordion ------------------------------------------------------
   One list of six. The active row expands in place; the others stay put
   as plain names. Every panel is given the SAME reserved height (set in
   JS from the tallest content), so the list never changes overall size
   and rows do not jump as the selection moves.

   Deliberately absent, to match the reference: step number, 01-06 rail
   numbers, progress hairline, travelling marker.
   -------------------------------------------------------------------- */

.xp__item-btn {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 1.55vw, 1.7rem);
  font-weight: var(--fw-medium);
  letter-spacing: var(--track-heading);
  line-height: 1.7;
  color: var(--text-tertiary);
  text-align: left;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.xp__item-btn:hover { color: var(--text-secondary); }
.xp__item-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; }

/* Height is animated in JS; overflow hides the collapsed content. */
.xp__panel {
  overflow: hidden;
}

/* Tags match .capability-card-v2__tags span. */
.xp__tags {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  list-style: none;
}
.xp__tags li {
  white-space: nowrap;
}

/* ---- Cover ----------------------------------------------------------- */
.xp__cover {
  position: relative;
  background: var(--surface-media);
  align-self: stretch;
  min-height: 0;
  /* No aspect-ratio while pinned: the row supplies the height and
     object-fit:cover crops. A fixed ratio here capped the body at 542px
     and reintroduced the slack. The stacked breakpoint restores one. */
}
/* Exactly two layers are ever composited; they swap roles each switch.
   Each layer is a triptych — three panels tiling the frame at the fixed
   394 : 584 : 366 ratio the comps were cut at (flex-grow is set inline
   from PANEL_RATIO in js/expertise.js). Identical proportions across
   disciplines keep both seams stationary through a switch. */
.xp__layer {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: stretch;
}
.xp__panel {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  overflow: hidden;
  /* Panels animate their own clip-path and transform on a switch; keeping
     them on their own layer stops the stagger repainting its neighbours. */
  will-change: clip-path, transform, opacity;
  transform: translateZ(0);
}
.xp__layer img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Travels with the clip edge during the wipe. */
.xp__wipe {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 1px;
  background: var(--accent);
  opacity: 0;
  pointer-events: none;
}

/* Below the pinned desktop layout the cover is a portrait/landscape card
   inside an accordion, and a three-across triptych there would reduce each
   panel to a sliver. The wide centre panel carries the topic on its own;
   the flanking two are dropped rather than squeezed. */
@media (max-width: 1024px) {
  .xp__panel:first-child,
  .xp__panel:last-child { display: none; }
  .xp__panel { flex-grow: 1 !important; }
}

/* =====================================================================
   Responsive
   <=1024px  unpinned single column (matches the site's nav breakpoint)
   <=600px   index rail becomes a snapping chip row
   ===================================================================== */
@media (max-width: 1024px) {
    
  .xp__head { grid-template-columns: 1fr;  }
    .xp__lede { display: none; }

  /* The cover is moved by JS into the active accordion item after its panel. */

  .xp__item-btn { line-height: 1.45; }
}

/* =====================================================================
   Reduced motion — no pin, no split, no wipe.
   ===================================================================== */
@media (prefers-reduced-motion: reduce) {
  .xp__stage { min-height: 0; }
  .xp__wipe { display: none; }
  .xp__item-btn { transition: none; }
}
/* ===== end css/expertise.css ===== */

/* ===== begin css/mobile-linear.css ===== */
/* Mobile hero + linear Work ownership. This file is deliberately last in
   the cascade so the rebuilt phone experience cannot inherit old sticky
   story or hero-to-Work overlap patches. Desktop is intentionally untouched. */

.sw-mobile-cards {
  display: none;
}

@media (max-width: 900px) {
  :root {
    --mobile-section-space: clamp(60px, 15vw, 72px);
  }

  .portfolio-cursor {
    display: none;
  }

  #hero,
  .hero {
    /* The film is one stable viewport. Story copy no longer lives in this
       sticky region; it has normal-flow chapters immediately below. */
    height: 100svh !important;
  }

  /* A separate opaque layer owns the final story-to-Work handoff. Keeping
     this out of the frosted background avoids `!important` cascade fights
     and guarantees that no portrait/frame can bleed through while Work
     rises into view. */

  #work.sw {
    height: auto;
    margin-top: 0;
    opacity: 1 !important;
    transform: none !important;
    overflow: visible;
    background: var(--surface, #fafafa);
    z-index: 6 !important;
  }

  #work .sw__inner {
    position: relative;
    display: block;
    height: auto;
    min-height: 0;
    padding: 76px 20px 104px;
    overflow: visible;
  }

  #work .sw__head {
    gap: 24px;
  }

  #work .sw__pills {
    margin-top: 2px;
  }

  #work .sw__main,
  #work .sw__mobile-nav,
  #work .sw__pager,
  #work .sw__overlay {
    display: none;
  }

  .sw-mobile-cards {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
  }

  .sw-mobile-card {
    position: relative;
    min-height: 0;
    -webkit-tap-highlight-color: transparent;
  }

  .sw-mobile-card:active {
    transform: scale(.985);
  }

  .sw-mobile-card__media {
    position: relative;
    overflow: hidden;
  }

  .sw-mobile-card__media :is(img, video) {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
  }

  .sw-mobile-card__media::after {
    content: "";
    position: absolute;
    inset: auto 0 0;
    height: 42%;
    pointer-events: none;
    background: linear-gradient(to bottom, transparent, rgba(10, 11, 13, .62));
  }

  .sw-mobile-card__copy {
    z-index: 2;
    display: grid;
  }

  .sw-mobile-card__copy::before {
    content: "";
    position: absolute;
    z-index: -1;
    inset: 0;
    border-radius: inherit;
    background: linear-gradient(
      to bottom,
      rgba(18, 19, 22, .12) 0%,
      rgba(18, 19, 22, .62) 34%,
      rgba(18, 19, 22, .86) 66%,
      rgba(18, 19, 22, .94) 100%
    );
    -webkit-backdrop-filter: blur(16px) saturate(1.08);
    backdrop-filter: blur(16px) saturate(1.08);
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,.38) 20%, #000 42%);
    mask-image: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,.38) 20%, #000 42%);
  }

  .sw-mobile-card__description {
    max-width: 42ch;
  }

}

@media (prefers-reduced-motion: reduce) and (max-width: 900px) {
  .sw-mobile-card {
    opacity: 1 !important;
    transform: none !important;
  }
}

/* August mobile annotation pass. Kept in this last-loaded ownership layer
   so none of the legacy responsive experiments can override it. */
@media (max-width: 900px) {
  /* Real touch screens have no pointer. Fine-pointer phone emulation does,
     so restore the native cursor instead of leaving an invisible custom one. */
  html.has-portfolio-cursor,
  html.has-portfolio-cursor body,
  html.has-portfolio-cursor body * {
    cursor: default !important;
  }
  html.has-portfolio-cursor :is(a, button, [role="button"]) {
    cursor: pointer !important;
  }
  .portfolio-cursor { display: none; }

  /* Work cards: the media and frosted copy are one continuous, fully
     tappable surface. The mask feathers only the top of the frost. */
    .sw-mobile-card__copy::before {
    border-radius: 0 0 22px 22px !important;
    background: linear-gradient(to bottom,
      rgba(12,14,18,0) 0%,
      rgba(12,14,18,.62) 25%,
      rgba(12,14,18,.9) 50%,
      rgba(12,14,18,.96) 100%) !important;
    -webkit-backdrop-filter: blur(28px) saturate(1.18) !important;
    backdrop-filter: blur(28px) saturate(1.18) !important;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,.5) 22%, #000 43%) !important;
    mask-image: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,.5) 22%, #000 43%) !important;
  }

  /* The two narrative chapters and Work now read as one continuous mobile
     surface instead of changing black values at the handoff. Scoped to
     .is-handoff only — the unscoped .mobile-hero-narrative/__stage
     selectors previously here painted this light surface color over the
     narrative's negative-margin overlap with #hero at rest (before any
     scroll), hiding the hero name/designation behind an opaque white
     patch in light theme. */
  #work.sw {
    background: var(--surface, #fafafa);
  }

  /* One consistent vertical rhythm between normal-flow mobile sections.
     Sticky stories retain their own viewport tracks, while their content
     insets use the same shared value at the boundary. */
  #work .sw__inner,
  #process .design-process__stage,
  #about-me,
  #client-value,
  #toolkit,
  #perspective,
  #partnerships,
  #experience,
  #insights,
  #contact {
    padding-top: var(--mobile-section-space) !important;
    padding-bottom: var(--mobile-section-space) !important;
  }

  /* Scroll-owned Expertise story. */
  #expertise.is-mobile-scroll-story {
    position: relative;
    min-height: 100svh;
    padding: 0;
  }
  #expertise.is-mobile-scroll-story .xp__stage {
    min-height: 100svh;
    padding: max(72px, env(safe-area-inset-top)) 20px 30px;
    gap: 14px;
    overflow: hidden;
    background: var(--surface, #fafafa);
  }
  #expertise.is-mobile-scroll-story .xp__head { gap: 0; }
  #expertise.is-mobile-scroll-story .xp__title {
    /* The shared section-title token, not a 12vw ramp of its own. That
       ramp resolved to 45px on a 375px phone while every other section
       heading sat at 36px, so Services read a size larger than the rest. */
    font-size: var(--fs-section-title) !important;
  }
  
    #expertise.is-mobile-scroll-story .xp__item-btn {
    padding-block: 3px !important;
    font-size: 1.05rem !important;
    line-height: 1.25 !important;
  }

    #expertise.is-mobile-scroll-story .xp__tags { gap: 5px; margin-top: 9px; }
  #expertise.is-mobile-scroll-story .xp__tags li { padding: 6px 9px;  }

  /* Compact Approach cards and keep every index anchored to the top-left. */
  #process .design-process__step,
  #process .design-process__step.is-active {
    height: 410px;
    grid-template-rows: auto 154px minmax(0, 1fr) !important;
  }
  #process .design-process__number {
    top: 24px;
    left: 24px;
    z-index: 2;
  }
  #process .design-process__visual { height: 154px;  }
  #process .design-process__copy {
    padding: 0;
    transform: none !important;
  }
    
  /* About lede annotation. */
  :root[data-theme] body #about-me .about-v2__lede,
  body #about-me .about-v2__lede {
    color: #fff !important;
    font-size: clamp(1.2rem, 5.3vw, 1.45rem) !important;
    line-height: 1.28 !important;
  }

  /* Aerial media stays user-initiated on phones. Existing transparent
     trigger becomes a clear frosted play control. */
  #perspective .pg-panel--aerial .pg-aerial__preview-trigger {
    z-index: 5;
    inset: 50% auto auto 50% !important;
    display: grid;
    width: 58px;
    height: 58px;
    place-items: center;
    background: rgba(12,14,16,.62) !important;
    -webkit-backdrop-filter: blur(18px) saturate(1.25) !important;
    backdrop-filter: blur(18px) saturate(1.25) !important;
    transform: translate(-50%, -50%) !important;
  }
  #perspective .pg-panel--aerial .pg-aerial__preview-trigger::before {
    content: "";
    width: 0;
    height: 0;
    margin-left: 4px;
    border-top: 8px solid transparent;
    border-bottom: 8px solid transparent;
    border-left: 13px solid #fff;
  }

  /* Mobile Music cards follow the supplied compact player composition:
     title top-left, visualizer/play control centered, and metadata split
     across the bottom edge. Desktop retains its existing centered layout. */
  #perspective .pg-panel--sound .pg-track__number { display: none; }
  #perspective .pg-panel--sound .pg-track {
    width: 100%;
    aspect-ratio: 4 / 3;
    min-height: 300px;
    transform: none !important;
  }
  #perspective .pg-panel--sound .pg-track__play {
    top: 50%;
    left: 50%;
    width: clamp(76px, 20vw, 88px);
    height: clamp(76px, 20vw, 88px);
    border: 1px solid rgba(255,255,255,.18);
    -webkit-backdrop-filter: blur(18px) saturate(1.15) !important;
    backdrop-filter: blur(18px) saturate(1.15) !important;
    transform: translate(-50%, -50%) !important;
  }
  #perspective .pg-panel--sound .pg-track__play > svg {
    width: 27px;
    height: 27px;
    stroke-width: 2;
  }
  #perspective .pg-panel--sound .pg-track__meta {
    inset: 0 !important;
    width: auto;
    text-align: left;
  }
  #perspective .pg-panel--sound .pg-track__meta strong {
    position: absolute;
    top: clamp(20px, 6vw, 28px);
    right: clamp(20px, 6vw, 28px);
    left: clamp(20px, 6vw, 28px);
    overflow: hidden;
    font-weight: 600;
    line-height: 1.08;
    letter-spacing: -.045em;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  :root body #perspective .pg-panel--sound .pg-track__meta small[class],
  :root body #perspective .pg-panel--sound .pg-track__meta small {
    position: absolute;
    right: clamp(20px, 6vw, 28px);
    bottom: clamp(20px, 5.4vw, 27px);
    left: clamp(20px, 6vw, 28px);
    justify-content: space-between;
    flex-direction: row !important;
    gap: 16px !important;
    margin: 0;
    color: rgba(255,255,255,.88) !important;
    font-size: clamp(.95rem, 4.3vw, 1.1rem);
    line-height: 1.15;
    letter-spacing: -.025em;
    white-space: nowrap;
  }
  :root body #perspective .pg-panel--sound .pg-track__genre[class],
  :root body #perspective .pg-panel--sound .pg-track__genre {
    overflow: hidden;
    text-overflow: ellipsis;
  }
  :root body #perspective .pg-panel--sound .pg-track__duration[class],
  :root body #perspective .pg-panel--sound .pg-track__duration {
    flex: 0 0 auto;
    margin-left: auto;
    color: #fff;
  }
  #perspective .pg-panel--sound .pg-track__canvas {
    inset: 0 !important;
    width: 100%;
    height: 100%;
    opacity: .34 !important;
    -webkit-mask-image: radial-gradient(circle at center, #000 28%, rgba(0,0,0,.78) 52%, transparent 78%);
    mask-image: radial-gradient(circle at center, #000 28%, rgba(0,0,0,.78) 52%, transparent 78%);
    transition: opacity var(--dur-3) ease !important;
  }
  #perspective .pg-panel--sound .pg-track.is-playing .pg-track__canvas { opacity: .96 !important; }

  /* Odometer glyphs need a little optical breathing room on both sides. */
    #expCounters .counter__value {
    display: inline-flex;
    max-width: none !important;
    padding-inline: .08em .28em !important;
    margin-inline: -.08em 0;
  }
}
/* ===== end css/mobile-linear.css ===== */

/* ===== begin css/home-dark-surfaces.css ===== */
/* Homepage dark-mode canvas
   ------------------------------------------------------------------
   Keep every post-hero section on one neutral charcoal plane. Component
   cards and media retain their own elevated surfaces; only the page shells,
   sticky stages and tab-panel beds are unified here. This file is loaded
   last so older section-specific backgrounds cannot reintroduce seams. */

:root[data-theme="dark"] {
  --home-section-canvas: var(--surface-page, #0f0f0f);
}

:root[data-theme="dark"] body > main,
:root[data-theme="dark"] body > main > :is(section:not(#hero), footer),
:root[data-theme="dark"] body :is(
  #mobileHeroNarrative .mobile-hero-narrative__stage,
  #work .sw__inner,
  #expertise .xp__stage,
  #process .design-process__stage,
  #perspective .pg-gallery__sticky,
  #perspective .pg-gallery__content,
  #perspective .pg-panel
) {
  background-color: var(--home-section-canvas) !important;
}

/* Avoid a thin contrasting strip where consecutive full-width sections
   meet during sticky/scroll handoffs. */
:root[data-theme="dark"] body > main > :is(section:not(#hero), footer)::before,
:root[data-theme="dark"] body > main > :is(section:not(#hero), footer)::after {
  border-color: var(--line-soft, rgba(255, 255, 255, .06));
}
/* ===== end css/home-dark-surfaces.css ===== */

/* ---------------------------------------------------------------------
   [hidden] must outrank the id-scoped display rules above (`#about-me {
   display: grid !important }` and friends), so it needs id specificity
   of its own. Last rule in the bundle, deliberately.
   --------------------------------------------------------------------- */
#about[hidden],
#about-me[hidden],
#services[hidden],
#stack[hidden],
#testimonials[hidden] { display: none !important; }

/* ---------------------------------------------------------------------
   Figma header (516:285-288) sets the nav item for the section in view
   at full opacity and the rest at 50%. The .nav--on-dark contrast rules
   above force a flat colour with !important, so the dimmed state needs
   to out-specify them rather than sit before them.
   --------------------------------------------------------------------- */
#nav.nav--on-dark .nav__links a:not(.nav__talk):not([aria-current]):not(:hover):not(:focus-visible) {
  color: rgba(255, 255, 255, 0.5) !important;
}
#nav:not(.nav--on-dark) .nav__links a:not(.nav__talk):not([aria-current]):not(:hover):not(:focus-visible) {
  color: color-mix(in srgb, var(--text-primary) 50%, transparent) !important;
}

/* ---------------------------------------------------------------------
   Work — centered editorial layout.

   Matches the reference comp: the title is centred and set in caps over
   the full width, the four filters lose their capsules and read as plain
   type, and the pager collapses from a progress track to "01 / 04".
   Desktop only — the ≤900px stack above is untouched. Last block in the
   bundle because every rule it replaces is itself !important.
   --------------------------------------------------------------------- */
@media (min-width: 901px) {
  #work .sw__head {
    display: block;
    text-align: center;
  }
  
  #work .sw__title {
    width: 100% !important;
    max-width: none !important;
    text-align: center !important;
    white-space: nowrap !important;
  }

  /* The comp runs the title straight into the filters. The line stays in
     the DOM for search and screen readers; only the desktop head drops it. */
  #work .sw__support {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip-path: inset(50%) !important;
    white-space: nowrap !important;
  }

  /* Filters read as type, not buttons. */
  #work .sw__pills {
    position: static !important;
    flex-wrap: wrap !important;
    width: auto !important;
    /* Same rhythm as .sw__main's columns, so the filters sit on the grid
       set by the two title lists and the thumbnail below them. */
    column-gap: clamp(28px, 4vw, 72px) !important;
    margin: clamp(26px, 3.6vh, 52px) auto 0 !important;
    text-align: center !important;
  }
  #work .sw-pill,
  #work .sw__pills .sw-pill {
    min-height: 0 !important;
    border: 0 !important;
    background: transparent !important;
    letter-spacing: -.01em !important;
    color: var(--text-tertiary) !important;
  }
  #work .sw-pill:is(:hover, :focus-visible),
  #work .sw__pills .sw-pill:is(:hover, :focus-visible) {
    transform: none !important;
    background: transparent !important;
    color: var(--text-primary) !important;
  }
  #work .sw-pill.is-active,
  #work .sw__pills .sw-pill.is-active {
    background: transparent !important;
    color: var(--filter-ink-active, var(--text-primary)) !important;
  }

  /* The active filter sits on the travelling .filter-thumb capsule
     (tokens.css), so it takes the thumb's paired ink rather than the page
     ink. --text-primary here put near-white text on the near-white dark
     thumb. --filter-ink-active is what Expertise and Playground use. */
  html:root[data-theme="dark"] body #work .sw__pills .sw-pill.is-active {
    background: transparent !important;
    color: var(--filter-ink-active, var(--text-primary)) !important;
  }

  /* Pull the filters down onto the thumbnail. .sw__main was the grid's
     1fr row, so it stretched to fill the stage and centred a ~420px
     thumbnail inside ~680px — leaving 137px between the filters and the
     image against the 51px the two lists sit from it. Giving main an auto
     row and handing the slack to the pager row instead lets .sw__main's
     own padding set that distance, and the pager still parks at the
     bottom of the stage. */
  #work .sw__inner {
    grid-template-rows: auto auto minmax(0, 1fr);
  }
  #work .sw__main {
    padding-bottom: 0;
  }

  /* Pager: "01 / 04" in place of the progress track. */
  #work .sw__pager {
    gap: .6rem;
    color: var(--text-tertiary);
  }
  #work .sw__track {
    width: auto;
    height: auto;
    background: none !important;
  }
  #work .sw__track > i { display: none; }
  #work .sw__track::before { content: "/"; }
}

/* ---------------------------------------------------------------------
   Section titles: Zalando Sans Expanded Semibold.

   The display line of every section takes the expanded cut at 600. The
   mono eyebrow labels (`.section__label`, `.words__label`) are not display
   titles and keep Geist. Every selector here already carries a
   `font-weight` !important from the passes above, so this has to match
   their id-level specificity and sit last.
   --------------------------------------------------------------------- */
:is(
  #work .sw__title,
  #expertise .xp__title,
  #services .help__title,
  #process #processTitle,
  #about-me .about-v2__title,
  #toolkit #toolkitTitle,
  #perspective #perspectiveTitle,
  #partnerships #partnershipsTitle,
  #education-recognition #educationTitle,
  #contact #contactTitle,
  .cs__title,
  .sw__ov-title
) {
  font-family: var(--font-title) !important;
  font-weight: 600 !important;
}

/* Section titles are set in caps. Scoped to the section headings only —
   the ledes beside them stay sentence case, and so do .cs__title /
   .sw__ov-title above, which are project names rather than headings. */
:is(
  #work .sw__title,
  #expertise .xp__title,
  #services .help__title,
  #process #processTitle,
  #about-me .about-v2__title,
  #toolkit #toolkitTitle,
  #perspective #perspectiveTitle,
  #partnerships #partnershipsTitle,
  #education-recognition #educationTitle,
  #contact #contactTitle
) {
  text-transform: uppercase !important;
}

/* ---------------------------------------------------------------------
   EXPERTISE — tab row over one wide cover.

   Replaces the left accordion + right cover split. The five disciplines
   read as a row of plain-type tabs (same treatment as the Work filters),
   and the active discipline's line and tags sit over the bottom of a
   single full-width cover. Legacy .xp__body / .xp__left / .xp__list /
   .xp__item rules above no longer match anything — the markup they
   targeted is gone.
   --------------------------------------------------------------------- */
#expertise .xp__tabs {
  flex-wrap: wrap;
}
#expertise .xp__tab {
  background: transparent;
  transition: color var(--dur-3) var(--ease-out);
}
#expertise .xp__tab:hover { color: var(--text-secondary); }
#expertise .xp__tab:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; }

/* The cover is now the section's whole lower half and carries the copy. */
#expertise .xp__cover {
  flex: 1 1 auto;
  align-self: stretch;
  min-height: 0;
}
/* Keeps the overlay legible over a bright photograph without dimming the
   whole frame — the scrim only covers the band the text sits in. */
#expertise .xp__cover::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 52%;
  background: linear-gradient(to top, rgba(0, 0, 0, .62), rgba(0, 0, 0, 0));
  pointer-events: none;
  z-index: 3;
}
#expertise .xp__overlay {
  position: absolute;
  z-index: 4;
  inset: auto 0 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(1rem, 2.5vw, 2.5rem);
  padding: clamp(1.25rem, 2.6vw, 2.25rem);
}
#expertise .xp__desc {
  /* The overlay is a flex row (description left, tags right). A 46ch cap
     wrapped the description onto a second line while ~320px of the row
     sat empty, so it takes the space the tags do not need instead. */
  flex: 1 1 auto;
  min-width: 0;
  color: #fff;
}
#expertise .xp__tags {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin: 0;
  list-style: none;
}
#expertise .xp__tags li {
  padding: .5rem 1rem;
  line-height: 1.2;
  color: #fff;
  white-space: nowrap;
}

@media (max-width: 900px) {
  #expertise .xp__tabs {
    scrollbar-width: none;
  }
  #expertise .xp__tabs::-webkit-scrollbar { display: none; }
      /* Stacked, the chips would crowd a portrait crop, so the copy takes the
     full width and the tags wrap beneath it. */
  #expertise .xp__overlay { flex-direction: column; align-items: flex-start; }
  #expertise .xp__cover::after { height: 68%; }
}

/* =====================================================================
   Typographic polish + square media.

   Two changes, both deliberately narrow:

   1 · The body scale steps down. Everything that is NOT a display title
       — ledes, body copy, meta, tags, labels — drops roughly one notch
       and loses a little weight. The Figma scale set body at 18px, which
       reads heavy next to the expanded caps titles; 16px at the comp
       width holds the same measure with far less shout. Because these
       are the shared tokens, mobile inherits the same restraint rather
       than needing a second set of numbers.

       --fs-display / --fs-section-title / --fs-card-title are untouched:
       the big titles keep their presence, and the widened gap between
       them and the body copy is the point.

   2 · Media goes square on desktop. Rounded corners stay on the mobile
       card stack, where the radius is doing real work separating stacked
       cards from the page.
   ===================================================================== */
/* The six reading-copy tokens that used to be redeclared here now live in
   tokens.css, which is the single source of truth. This block only ever
   restated them; type-scale.css loaded later and won, so what rendered was
   1rem / 0.875rem / 0.75rem. tokens.css now carries exactly those values. */

/* Controls and list type: one step down, one weight lighter. These carry
   !important because the passes above set their size that way. */
@media (min-width: 901px) {
    
    #work .sw-li.is-active { font-weight: 500 !important; }

  }

@media (max-width: 900px) {
  /* The mobile filters are capsules, not type — they only need the size
     step, not the weight change, or the active pill loses its read. */
        }

#expertise .xp__tags li {
  font-weight: 400;
  letter-spacing: 0;
}

/* ---- Square media (desktop only) ------------------------------------ */
@media (min-width: 901px) {
  :root { --radius-media: 0px; }
  #work .sw__thumb,
#work .sw__cover,
#expertise .xp__cover,
#expertise .xp__layer img,
.pg-panel figure,
.pg-panel--travel figure,
.pg-aerial__viewer,
.pg-track,
.pg-media-placeholder,
.about-v2 .about-v2__figure {
    border-radius: var(--radius-media, 16px) !important;
  }
}

/* ---------------------------------------------------------------------
   Expertise, mobile scroll story: the cover fills the stage.

   The rules above cap it at `aspect-ratio: 16/9; max-height: 31svh` —
   correct when the cover was a small inline image between an accordion
   row and its copy, wrong now that it IS the section's main surface. At
   31svh the overlay copy sat on top of the subject and the rest of the
   pinned stage was empty.
   --------------------------------------------------------------------- */
@media (max-width: 1024px) {
  #expertise.is-mobile-scroll-story .xp__tabs { flex: 0 0 auto; }
  #expertise.is-mobile-scroll-story .xp__cover {
    flex: 1 1 auto;
    aspect-ratio: auto !important;
    max-height: none !important;
    min-height: 0;
    margin: 4px 0 0 !important;
  }
}

/* ---------------------------------------------------------------------
   Expertise overlay: frosted scrim + frosted labels.

   The scrim carries the blur itself, masked so its TOP edge is fully
   transparent — mask-image fades a backdrop-filter along with the tint,
   which a plain gradient background cannot do (backdrop-filter has no
   gradient form of its own). Result: the image is untouched at the top
   of the band and progressively frosts toward the copy.

   The labels keep a dark tint under their own blur rather than a light
   one. Several covers are bright, and a white-tinted frost put white
   text on a near-white plate.
   --------------------------------------------------------------------- */
#expertise .xp__cover::after {
  background: linear-gradient(to top, rgba(0, 0, 0, .52), rgba(0, 0, 0, 0));
  -webkit-backdrop-filter: blur(20px) saturate(115%);
  backdrop-filter: blur(20px) saturate(115%);
  -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, .85) 45%, transparent 100%);
  mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, .85) 45%, transparent 100%);
}

#expertise .xp__tags li {
  background: rgba(16, 16, 16, .34);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  backdrop-filter: blur(16px) saturate(140%);
}

/* Safari on iOS drops backdrop-filter inside a masked ancestor often
   enough to matter; the tint alone still has to carry the contrast. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  #expertise .xp__tags li { background: rgba(16, 16, 16, .72); }
  #expertise .xp__cover::after { background: linear-gradient(to top, rgba(0, 0, 0, .66), rgba(0, 0, 0, 0)); }
}

/* ---------------------------------------------------------------------
   Expertise: true progressive blur + the reference's vertical rhythm.

   The single masked blur added earlier fades its STRENGTH but the whole
   band still blurs at one radius, which reads as a hard frost with a
   soft edge. A progressive blur needs several layers: each one starts
   lower than the last and doubles the radius, so at any height the
   accumulated blur is the sum of every layer covering that point — a
   smooth ramp from clean image to heavy frost.
   --------------------------------------------------------------------- */
#expertise .xp__frost {
  position: absolute;
  inset: auto 0 0 0;
  height: 46%;
  z-index: 3;
  pointer-events: none;
}
#expertise .xp__frost span {
  position: absolute;
  inset: 0;
}
/* Each layer: mask start -> blur radius. The masks overlap by design;
   the overlap is what makes the ramp continuous rather than banded. */
#expertise .xp__frost span:nth-child(1) {
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 22%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 22%);
}
#expertise .xp__frost span:nth-child(2) {
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 20%, #000 42%);
  mask-image: linear-gradient(to bottom, transparent 20%, #000 42%);
}
#expertise .xp__frost span:nth-child(3) {
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 40%, #000 62%);
  mask-image: linear-gradient(to bottom, transparent 40%, #000 62%);
}
#expertise .xp__frost span:nth-child(4) {
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 60%, #000 82%);
  mask-image: linear-gradient(to bottom, transparent 60%, #000 82%);
}
#expertise .xp__frost span:nth-child(5) {
  -webkit-backdrop-filter: blur(28px) saturate(115%);
  backdrop-filter: blur(28px) saturate(115%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 78%, #000 100%);
  mask-image: linear-gradient(to bottom, transparent 78%, #000 100%);
}

/* The ::after now only tints — the frost stack owns the blur. Kept
   shorter and lighter so the ramp above stays readable as a ramp. */
#expertise .xp__cover::after {
  height: 46%;
  background: linear-gradient(to top, rgba(0, 0, 0, .46), rgba(0, 0, 0, 0));
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  -webkit-mask-image: none;
  mask-image: none;
}

/* ---- Vertical rhythm, measured off the reference ---------------------
   Reference proportions at 1125px tall: title ~8%, tabs ~22%, cover
   26% -> 94%. The stage's single flex gap spaced head/tabs/cover evenly,
   which put the tabs too close to the title and left the cover starting
   late. Explicit margins reproduce the reference's uneven rhythm: a
   generous gap under the head, a tight one under the tabs. */
@media (min-width: 901px) {
  #expertise .xp__stage {
    gap: 0;
    padding-block: clamp(4.5rem, 9vh, 6.5rem) clamp(1.5rem, 3.5vh, 2.5rem);
  }
  #expertise .xp__tabs { margin-top: clamp(2.5rem, 6vh, 5rem); }
  }

/* ---------------------------------------------------------------------
   One spacing value for every plain-type filter row.

   The two rows had drifted apart: Expertise sat at 22-44px while the Work
   filters were on clamp(28px, 4vw, 72px) — that value was chosen to match
   .sw__main's column gap so the filters lined up with the grid below
   them, but at wide viewports 72px read as four unrelated words rather
   than one control. A single token, tighter than both, keeps them
   consistent and legible as a set.

   Playground's row is still capsule buttons in a 4-column grid, a
   different component with its own 8px gap; it is deliberately not
   folded in here.
   --------------------------------------------------------------------- */
:root { --filter-gap: clamp(1rem, 1.6vw, 1.75rem); }

@media (min-width: 901px) {
    }

@media (max-width: 900px) {
  /* Mobile Expertise scrolls horizontally, so it wants the same rhythm
     rather than the wider desktop one. Work keeps its capsule grid. */
  }

/* ---------------------------------------------------------------------
   Work pager sits on the same gap as everything else around the cover.

   It was parked at the bottom of the stage (align-self:end), which left
   it floating far below the thumbnail while the filters above and the
   two lists either side both sit clamp(28px, 4vw, 72px) away from it.
   Anchoring it to the top of its row with that same gap puts all four
   edges — top, left, right, bottom — on one measure; the leftover height
   falls below it, where nothing reads it as a gap.
   --------------------------------------------------------------------- */
@media (min-width: 901px) {
  #work .sw__pager {
    margin-top: clamp(28px, 4vw, 72px);
  }
}

/* ---------------------------------------------------------------------
   Work: centre everything below the title in the space the title leaves.

   The filters live inside .sw__head with the title, so they could not be
   centred with the cover while that wrapper stayed a layout box —
   `display: contents` dissolves .sw__head and .sw__intro so the title,
   filters, cover grid and pager all become direct children of
   .sw__inner. The stage then runs as a flex column and the group is
   centred with the auto-margin pair: `margin-top:auto` on the first item
   of the group and `margin-bottom:auto` on the last splits the leftover
   height evenly above and below them, leaving the title parked at the
   top on its own.

   The gaps inside the group (filters -> cover -> pager) are already set
   by their own margins and are untouched by this.
   --------------------------------------------------------------------- */
@media (min-width: 901px) {
  #work .sw__inner {
    display: flex;
    flex-direction: column;
    grid-template-rows: none !important;
  }
  #work .sw__head,
  #work .sw__intro { display: contents !important; }

  #work .sw__title { flex: 0 0 auto; }
  /* The group: filters, cover, pager. */
  #work .sw__pills { margin-top: auto !important; }
  #work .sw__main { flex: 0 0 auto; }
  #work .sw__pager {
    margin-bottom: auto;
    /* The grid-era rule above set align-self:start to pull the pager up
       to its gap. On a flex COLUMN align-self is the horizontal axis, so
       that same value shoved it to the left edge; the auto-margin pair
       owns the vertical placement now, and this just has to fill the
       width so the pager's own justify-content can centre the digits. */
    align-self: stretch !important;
  }
}

/* =====================================================================
   APPROACH — four equal white cards.

   Was a scroll-emphasis story: one card grew to 1.6fr while the other
   three sat at 0.24 opacity with shrunken numbers, separated by hairline
   rules on a grey ground. The comp is a flat set — four equal white
   panels on the grey, thin gaps, no borders, square corners, every card
   fully legible at once. js/main.js no longer writes the emphasis
   styles; this owns the rest.
   ===================================================================== */
#process .design-process__steps {
  align-items: stretch;
}

#process .design-process__step {
  flex: 1 1 0;
  /* The shared card contract earlier in the bundle paints these with
     --surface-card and --radius-card, both !important; the comp wants
     flat white squares. */
  border-radius: var(--radius-card, 16px) !important;
  background: #fff;
  display: flex;
  flex-direction: column;
  will-change: auto;
}

/* Big, light, and top-left — the comp's numeral is a thin weight at a
   fixed size rather than one that scales with the card's width. */
#process .design-process__number {
  font-size: clamp(3.25rem, 4.6vw, 5rem);
  font-weight: 300;
  line-height: 1;
  letter-spacing: -0.03em;
  transform: none !important;
  opacity: 1 !important;
}

/* The glyph floats in the middle of the card, copy sits at the bottom;
   the auto margins hold that split at any card height. */
#process .design-process__visual {
  width: auto;
  height: auto;
  opacity: 1 !important;
  display: grid;
  place-items: center;
}
#process .process-glyph {
  display: block;
  height: auto;
  max-width: none;
  transition: transform var(--dur-3) var(--ease-out);
}
/* The only thing the scroll position still changes. */
#process .design-process__step.is-active .process-glyph { transform: scale(1.06); }

#process .design-process__copy {
  transform: none !important;
  opacity: 1 !important;
}
#process .design-process__copy h3 {
  margin: 0 0 .4rem;
}

@media (prefers-reduced-motion: reduce) {
  #process .process-glyph { transition: none; }
}

@media (max-width: 900px) {
  /* Stacked, a card only needs to be tall enough to hold its glyph. */
      }

/* Theme passes paint these through `:root body #process .design-process__step`
   selectors carrying !important, which outrank the plain `#process .step`
   block above — so the flat white square has to match that specificity.
   Both states get the same surface: the comp has no active-card colour. */
:root body #process .design-process__step,
:root body #process .design-process__step.is-active,
:root[data-theme] body #process .design-process__step,
:root[data-theme] body #process .design-process__step.is-active {
  background: #fff;
  /* Deliberately NOT !important: the legacy base rule dims every step to
     0.24 and only lifted the scroll-focused one, so this has to beat it —
     but the stagger reveal sets opacity inline, and an !important here
     would outrank that and freeze the cards visible. Specificity alone is
     enough to win against the base rule while still yielding to inline. */
  opacity: 1;
}

/* The same themed pass dims the non-active card's number to 28% ink and
   its copy to 72/54% — carried over from the emphasis story, where only
   one card was ever meant to be readable. All four read equally now. */
:root body #process .design-process__number,
:root[data-theme] body #process .design-process__number {
  color: var(--process-ink) !important;
  opacity: 1 !important;
}
:root body #process .design-process__copy h3,
:root[data-theme] body #process .design-process__copy h3 {
  color: var(--process-ink) !important;
}
:root body #process .design-process__copy p,
:root[data-theme] body #process .design-process__copy p {
  color: var(--process-muted) !important;
}

/* Mobile ran a different card entirely: display:grid with a 410px
   min-height and the number absolutely positioned, which left the glyph
   sitting beside the numeral above a tall void. Stacked cards now use the
   same model as desktop — number, glyph, copy — sized to their content. */
@media (max-width: 900px) {
  :root body #process .design-process__step,
  :root[data-theme] body #process .design-process__step {
    display: flex;
    flex-direction: column;
    min-height: 0 !important;
    height: auto;
    padding: 1.5rem 1.25rem !important;
  }
  :root body #process .design-process__number,
  :root[data-theme] body #process .design-process__number {
    position: static !important;
    font-size: 2.75rem !important;
  }
  :root body #process .design-process__visual,
  :root[data-theme] body #process .design-process__visual {
    width: 100% !important;
    height: auto;
    margin: 1.5rem 0 !important;
    display: grid;
    place-items: center;
  }
    }

/* The stage is a sticky 100svh pin with .design-process__steps taking the
   remaining height — fine for a 4-across row, impossible for a stacked
   list, which then collapsed each card and clipped its copy. Mobile drops
   the pin and lets the section run at its natural height. */
@media (max-width: 900px) {
  :root body #process .design-process__story,
  :root[data-theme] body #process .design-process__story {
    height: auto;
  }
  :root body #process .design-process__stage,
  :root[data-theme] body #process .design-process__stage {
    position: static !important;
    height: auto;
  }
  :root body #process .design-process__steps,
  :root[data-theme] body #process .design-process__steps {
    flex: 0 0 auto;
    min-height: 0;
  }
  :root body #process .design-process__step { overflow: visible; }
}

/* Last piece of the old mobile card: the copy block was absolutely
   positioned against the step, so once the step stopped being a fixed
   410px box it collapsed to 48px and the copy floated free of its card. */
@media (max-width: 900px) {
  :root body #process .design-process__copy,
  :root[data-theme] body #process .design-process__copy {
    position: static !important;
    inset: auto !important;
    transform: none !important;
    /* align-self: end came from the absolutely-positioned mobile card,
       where it did nothing. Once the copy went back into flex flow it
       shrank the block to its text and pushed it against the card's right
       edge — every card but the longest one sat indented. */
    align-self: stretch !important;
  }
}

/* `flex: 1 1 0` was written unscoped for the desktop row, where it makes
   four equal columns. On mobile the same declaration lands in a COLUMN
   container, where a zero basis plus shrink collapsed every card to its
   own padding (48px) and spilled the content underneath. */
@media (max-width: 900px) {
  :root body #process .design-process__step,
  :root[data-theme] body #process .design-process__step {
    flex: 0 0 auto !important;
  }
}

/* The old mobile card indented its copy to clear the absolutely
   positioned numeral; in the stacked card they share the left edge. */
@media (max-width: 900px) {
  :root body #process .design-process__copy,
  :root[data-theme] body #process .design-process__copy {
    margin-top: 1.5rem !important;
    text-align: left;
  }
  }

/* =====================================================================
   Consolidated pass: Approach card geometry, one universal grid gap,
   square cards everywhere, aligned section ledes, and the Playground
   filters brought onto the shared plain-type tab styling.
   ===================================================================== */

/* ---- 1 · Approach is a plain section now, not a 400svh pin ----------
   The stagger reveal in js/main.js replaced the scroll story, so the
   track height and the sticky stage have nothing left to drive. */
@media (min-width: 901px) {
  #process .design-process__story {
    height: auto !important;
  }
  #process .design-process__stage {
    position: static;
    height: auto;
    min-height: 0;
    padding-bottom: clamp(3rem, 6vh, 5rem);
  }
  #process .design-process__steps {
    flex: 0 0 auto;
    min-height: 0;
  }
  #process .design-process__step {
    /* One padding on all four sides. The old bottom value was smaller
       than the top, which read as slack under the copy. */
    padding: clamp(0.9rem, 1.45vw, 1.5rem) !important;
    min-height: clamp(22rem, 34vw, 30rem);
  }
}

/* Every card reserves the same two lines for its description, so the
   titles sit on one line across the row. Without it the copy block grows
   with the longest description and pushes that card's h3 up (Discover sat
   19px above the other three). */
#process .design-process__copy {
  /* The copy block carried its own bottom padding on top of the card's,
     so the bottom inset measured 46px against 19px on the other three
     sides. The card's padding is the only inset now. */
  padding-bottom: 0 !important;
  max-width: none;
}
#process .design-process__copy p {
  /* Every card reserves the same two lines, so the titles stay on one
     line across the row; the 26ch cap is dropped so the description uses
     the card's full width at any card size. */
  min-height: calc(2 * 1.5em);
  max-width: none;
}

/* The glyph takes the whole middle band, so it centres between the
   numeral and the copy at any card height rather than being pushed by
   competing auto margins. +15% over the previous size. */
#process .design-process__visual {
  flex: 1 1 auto;
  display: grid;
  place-items: center;
}
#process .process-glyph {
  width: clamp(6.3rem, 10.9vw, 10.9rem) !important;
}

/* The four process glyphs are light-mode 3D art: their volume is drawn in
   pale near-white greens (the dominant tint is rgb(192,224,192), and the
   Discover magnifier's handle is a flat rgb(224,224,224)). On the dark
   card those washes read as milky white rather than as highlights. This
   damps them and pushes the remaining colour back toward the accent —
   a mitigation, not a cure. Replacing the four PNGs with dark-mode art
   is the real fix. */
:root[data-theme="dark"] #process .process-glyph,
:root[data-theme="dark"] body #process .process-glyph {
  filter: saturate(1.38) brightness(.82) contrast(1.06);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) #process .process-glyph {
    filter: saturate(1.38) brightness(.82) contrast(1.06);
  }
}

/* ---- 2 · One gap for every card grid, gallery and masonry gutter ---- */
#process .design-process__steps,
#perspective .pg-track,
.sw-mobile-cards {
  gap: var(--grid-gap);
}

/* ---- 3 · Square cards and media, everywhere ------------------------
   The radius tokens are 0 now; these are the surfaces that hardcoded a
   value instead of reading them. */
.pg-panel figure,
.pg-panel--travel figure,
.pg-aerial__viewer,
.pg-track,
.pg-media-placeholder,
.sw-mobile-cards > *,
#process .design-process__step,
#toolkit .toolkit-tile,
#client-value .value-card,
#services .capability-card-v2 {
  border-radius: var(--radius-card, 16px);
}

/* ---- 4 · Section ledes share one left edge -------------------------
   They were `justify-self: end`, so the block hugged the right margin
   and its LEFT edge moved with the character count — every section's
   sub-text started somewhere different. Anchoring them to the start of a
   fixed-width column puts them all on the same line. */
@media (min-width: 901px) {
  :is(
    #expertise .xp__lede,
    #process .design-process__lede,
    #services .help__sub,
    #client-value .client-value__lede,
    #toolkit .toolkit__lede,
    #perspective .pg-gallery__summary,
    #partnerships .editorial-head > p,
    #contact .contact-light__head > p
  ) {
    /* A FIXED width anchored to the container's right edge. max-width let
       a short lede shrink to its text, so its left edge moved with the
       character count — which is what made every section start at a
       different x. A fixed box right-anchored puts them all on one line. */
    justify-self: end !important;
    align-self: start !important;
    text-align: left !important;
    /* rem, not ch: `ch` is the width of the font's "0", so a section
       whose lede sits at a different size resolved 34ch to a different
       box and slipped out of the shared line. */
    width: 21.125rem !important;
    max-width: 21.125rem !important;
    min-width: 21.125rem !important;
  }
  /* Expertise sized its head columns as `auto 1fr`, so the lede column
     began wherever the title happened to end — far left of every other
     section. All heads now split the same way. */
  #expertise .xp__head,
  #process .design-process__intro {
    /* A hard 34ch second column. `auto` let the track shrink to the
       lede's own text width, which put Expertise's left edge 22px inside
       everyone else's. */
    grid-template-columns: minmax(0, 1fr) 21.125rem !important;
  }
  /* Every other section head spans the shared container (51px inset at a
     1297px viewport); the Approach stage used its own max(3.5vw, 2rem),
     landing 6px wider on each side and dragging its lede out of line. */
  #process .design-process__stage {
    padding-inline: max(3.94vw, 2rem);
  }
}

/* ---- 5 · Playground filters match Approach / Expertise -------------
   They were the last capsule row on the site; the other two filter rows
   read as plain type with a weight change for the active item. */
#perspective .pg-gallery__tabs {
  display: flex !important;
  flex-wrap: wrap !important;
  grid-template-columns: none !important;
  width: auto !important;
}
#perspective .pg-gallery__tabs button {
  min-height: 0 !important;
  box-shadow: none !important;
  letter-spacing: -.01em !important;
}
#perspective .pg-gallery__tabs button:is(:hover, :focus-visible) {
  transform: none !important;
  background: transparent !important;
  color: var(--text-primary) !important;
}
:is(#work) .sw__pills { display:none !important; }
