/* ==========================================================================
   Sections.
   ========================================================================== */

/* Header ------------------------------------------------------------------ */

.header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  height: var(--header-h);
  display: flex;
  align-items: center;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}

.header.is-stuck {
  background: rgba(242, 241, 236, 0.86);
  backdrop-filter: blur(6px);
  border-bottom-color: var(--line);
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  width: 100%;
}

.brand {
  font-weight: 600;
  letter-spacing: var(--tracking-tight);
  text-decoration: none;
  font-size: 1rem;
  white-space: nowrap;
}

.brand span {
  color: var(--muted);
  font-weight: 400;
}

.nav {
  display: none;
}

@media (min-width: 56rem) {
  .nav {
    display: flex;
    align-items: center;
    gap: var(--sp-6);
  }
}

.nav__link {
  position: relative;
  font-size: var(--fs-small);
  text-decoration: none;
  color: var(--ink-2);
  padding-block: var(--sp-2);
  transition: color var(--dur-fast) var(--ease);
}

.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 2px;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
}

.nav__link:hover {
  color: var(--ink);
}

.nav__link:hover::after {
  transform: scaleX(1);
}

.header__cta {
  display: none;
}

@media (min-width: 56rem) {
  .header__cta {
    display: inline-flex;
  }
}

/* Menu button and mobile panel -------------------------------------------- */

.menu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  margin-right: -0.5rem;
  background: none;
  border: 0;
  cursor: pointer;
}

@media (min-width: 56rem) {
  .menu-btn {
    display: none;
  }
}

.menu-btn__bars {
  position: relative;
  width: 22px;
  height: 12px;
}

.menu-btn__bars span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: var(--ink);
  transition: transform var(--dur) var(--ease);
}

.menu-btn__bars span:nth-child(1) {
  top: 0;
}

.menu-btn__bars span:nth-child(2) {
  bottom: 0;
}

.menu-btn[aria-expanded="true"] .menu-btn__bars span:nth-child(1) {
  transform: translateY(5px) rotate(45deg);
}

.menu-btn[aria-expanded="true"] .menu-btn__bars span:nth-child(2) {
  transform: translateY(-5px) rotate(-45deg);
}

.menu {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  grid-template-rows: var(--header-h) 1fr;
  background: var(--paper);
  visibility: hidden;
  opacity: 0;
  transition: opacity var(--dur) var(--ease), visibility var(--dur) var(--ease);
}

.menu.is-open {
  visibility: visible;
  opacity: 1;
}

@media (min-width: 56rem) {
  .menu {
    display: none;
  }
}

.menu__panel {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--sp-6);
  padding: var(--sp-6) var(--gutter)
    calc(var(--sp-7) + env(safe-area-inset-bottom));
  overflow-y: auto;
}

.menu__list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--line);
}

.menu__link {
  display: flex;
  align-items: baseline;
  gap: var(--sp-4);
  padding: var(--sp-5) 0;
  border-bottom: 1px solid var(--line);
  font-size: clamp(1.5rem, 1.1rem + 2.4vw, 2.25rem);
  font-weight: 600;
  letter-spacing: var(--tracking-tight);
  text-decoration: none;
}

.menu__link span {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 400;
  letter-spacing: var(--tracking-label);
  color: var(--muted);
}

.menu__foot {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

.menu__mail {
  font-size: var(--fs-lead);
  font-weight: 500;
  text-decoration: none;
  word-break: break-word;
}

/* Hero -------------------------------------------------------------------- */

.hero {
  position: relative;
  padding-top: calc(var(--header-h) + clamp(3.5rem, 2rem + 8vw, 8rem));
  padding-bottom: clamp(3rem, 2rem + 5vw, 6rem);
}

.hero__label {
  margin-bottom: var(--sp-5);
}

.hero__title {
  font-size: var(--fs-display);
  line-height: var(--lh-tight);
  max-width: 17ch;
}

.hero__title em {
  font-style: normal;
  color: var(--accent);
}

.hero__lead {
  margin-top: var(--sp-5);
  max-width: 52ch;
  font-size: var(--fs-lead);
  color: var(--ink-2);
}

.hero__actions {
  margin-top: var(--sp-6);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

@media (max-width: 26rem) {
  .hero__actions .btn {
    flex: 1 1 100%;
  }
}

.hero__meta {
  margin-top: clamp(3rem, 2rem + 4vw, 5rem);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
  display: grid;
  gap: var(--sp-5);
}

@media (min-width: 40rem) {
  .hero__meta {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--sp-6);
  }
}

.hero__meta dt {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--sp-2);
}

.hero__meta dd {
  margin: 0;
  font-size: var(--fs-small);
}

/* Work -------------------------------------------------------------------- */

.work-list {
  display: flex;
  flex-direction: column;
  gap: clamp(3.5rem, 2rem + 7vw, 7rem);
}

.work {
  display: grid;
  gap: var(--sp-5);
}

@media (min-width: 64rem) {
  .work {
    grid-template-columns: repeat(12, 1fr);
    align-items: center;
    gap: var(--sp-7);
  }

  .work__media {
    grid-column: 1 / span 7;
  }

  .work__body {
    grid-column: 9 / -1;
  }

  .work--flip .work__media {
    grid-column: 6 / -1;
    grid-row: 1;
  }

  .work--flip .work__body {
    grid-column: 1 / span 4;
    grid-row: 1;
  }
}

.work__media {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--paper-2);
  border: 1px solid var(--line);
  aspect-ratio: 4 / 3;
}

@media (min-width: 64rem) {
  .work__media {
    aspect-ratio: 16 / 11;
  }
}

.work__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  transition: transform var(--dur-slow) var(--ease);
}

@media (hover: hover) {
  .work__media:hover img {
    transform: scale(1.03);
  }
}

.work__top {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

.work__index {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  color: var(--accent);
}

.work__cat {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--muted);
}

.work__title {
  font-size: var(--fs-h3);
  margin-bottom: var(--sp-3);
}

.work__desc {
  color: var(--muted);
  max-width: 46ch;
  margin-bottom: var(--sp-4);
}

.work__body .tags {
  margin-bottom: var(--sp-5);
}

.work__link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 2.75rem;
  font-size: var(--fs-small);
  font-weight: 500;
  text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 2px;
  transition: color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}

.work__link:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.work__link .btn__arrow {
  transition: transform var(--dur) var(--ease);
}

.work__link:hover .btn__arrow {
  transform: translate(3px, -3px);
}

.work__soon {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 2.75rem;
  font-size: var(--fs-small);
  color: var(--muted);
}

/* About ------------------------------------------------------------------- */

.about {
  display: grid;
  gap: clamp(2.5rem, 1.5rem + 4vw, 4.5rem);
}

@media (min-width: 60rem) {
  .about {
    grid-template-columns: 1.35fr 1fr;
    align-items: start;
  }
}

.about__title {
  font-size: var(--fs-h2);
  max-width: 18ch;
  margin-bottom: var(--sp-5);
}

.about__text {
  color: var(--ink-2);
  max-width: var(--measure);
  font-size: var(--fs-lead);
}

.about__text + .about__text {
  margin-top: var(--sp-4);
}

.facts {
  display: grid;
  border-top: 1px solid var(--line);
}

.facts__row {
  display: grid;
  grid-template-columns: minmax(6.5rem, auto) 1fr;
  gap: var(--sp-4);
  padding-block: var(--sp-4);
  border-bottom: 1px solid var(--line);
  align-items: baseline;
}

.facts dt {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--muted);
}

.facts dd {
  margin: 0;
  font-size: var(--fs-small);
}

/* Footer ------------------------------------------------------------------ */

.footer {
  padding-block: var(--sp-6) calc(var(--sp-6) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
}

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  font-size: var(--fs-small);
  color: var(--muted);
}

.footer__links {
  display: flex;
  gap: var(--sp-5);
}

.footer__links a {
  text-decoration: none;
  min-height: 2.75rem;
  display: inline-flex;
  align-items: center;
  transition: color var(--dur-fast) var(--ease);
}

.footer__links a:hover {
  color: var(--ink);
}

/* Without JavaScript the panel cannot open, so keep the links reachable. */
.no-js .menu-btn { display: none; }
.no-js .menu { display: none; }

@media (max-width: 55.99rem) {
  .no-js .nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-4);
  }
}

/* ==========================================================================
   Mobile composition. Not just a narrower desktop: photography runs to the
   screen edges, meta becomes ruled rows, and the reading rhythm is rebuilt.
   ========================================================================== */

@media (max-width: 47.99rem) {
  /* Hero meta reads as a small spec sheet rather than three loose blocks. */
  .hero__meta {
    gap: 0;
    margin-top: 2.5rem;
  }

  .hero__meta > div {
    display: grid;
    grid-template-columns: 8.5rem 1fr;
    gap: var(--sp-4);
    align-items: baseline;
    padding-block: var(--sp-4);
    border-bottom: 1px solid var(--line);
  }

  .hero__meta dt { margin-bottom: 0; }

  /* Section heads: the note sits under a hairline so it reads as a caption. */
  .section__head { gap: var(--sp-3); }

  .section__note {
    max-width: none;
    padding-top: var(--sp-4);
    border-top: 1px solid var(--line);
    font-size: var(--fs-small);
  }

  /* Each project is shown as it looks on a phone, sitting on a tinted panel
     rather than cropped into a card. A desktop screenshot squeezed into a
     portrait frame loses its own navigation and headline. */
  .work-list { gap: 4rem; }

  .work__media {
    margin-inline: calc(var(--gutter) * -1);
    width: calc(100% + var(--gutter) * 2);
    border-radius: 0;
    border-inline: 0;
    border-block: 1px solid var(--line);
    background: var(--paper-2);
    aspect-ratio: auto;
    display: grid;
    place-items: center;
    padding-block: clamp(1.75rem, 1rem + 4vw, 3rem);
  }

  .work__media picture { display: block; width: min(58%, 13.5rem); }

  .work__media img {
    width: 100%;
    height: auto;
    aspect-ratio: 390 / 844;
    border-radius: 14px;
    border: 1px solid rgba(20, 18, 15, 0.12);
    box-shadow: 0 18px 40px -18px rgba(20, 18, 15, 0.45);
    object-position: top center;
  }

  /* The lift on hover belongs to pointer devices, not to a tap. */
  .work__media:hover img { transform: none; }

  /* Index and category sit on one ruled line above the title. */
  .work__top {
    justify-content: space-between;
    padding-bottom: var(--sp-3);
    border-bottom: 1px solid var(--line);
    margin-top: var(--sp-5);
  }

  .work__desc { max-width: none; }

  /* The hero starts a little higher so the headline lands sooner. */
  .hero { padding-top: calc(var(--header-h) + 2.5rem); }
  .hero__meta > div { padding-block: var(--sp-3); }

  .about__text { font-size: var(--fs-body); }

  .facts__row { grid-template-columns: 7.5rem 1fr; }

  /* Footer stacks into two clear rows instead of wrapping awkwardly. */
  .footer__inner { flex-direction: column; align-items: flex-start; gap: var(--sp-2); }
  .footer__links { flex-wrap: wrap; gap: var(--sp-4); }
}

@media (max-width: 26rem) {
  .hero__meta > div,
  .facts__row { grid-template-columns: 1fr; gap: var(--sp-1); }
}

/* About byline (photo + eyebrow) -------------------------------------------- */

.about__byline {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}

/* Same cut out as the hero, sitting on a soft disc, so the two portraits
   belong to one treatment instead of two. */
.about__photo {
  display: grid;
  place-items: end center;
  width: 3.75rem;
  height: 3.75rem;
  border-radius: 50%;
  background: var(--accent-soft);
  overflow: hidden;
}

.about__photo img {
  width: 88%;
  height: auto;
}

.about__byline .label { margin: 0; }

/* ==========================================================================
   Interactive layer. Everything below is the part of the page that answers
   back: the hero portrait composition, the pointer effects, the marquee, and
   a mobile layout drawn for a phone rather than folded down from the desktop.
   ========================================================================== */

/* Hero composition ---------------------------------------------------------
   The figure is a cut out, so it can stand in front of the arch instead of
   sitting inside a photo frame. Each layer carries its own --depth, and the
   pointer script writes --px / --py once per frame. */

.hero { overflow: hidden; }

.hero__grid {
  position: relative;
  display: grid;
  gap: clamp(2rem, 1rem + 4vw, 3rem);
  align-items: end;
}

/* Phones read headline, face, then the detail. Wide screens put the whole
   argument in one column with the figure holding the other. */
@media (min-width: 60rem) {
  .hero__grid {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
    grid-template-areas:
      "text portrait"
      "say  portrait";
    gap: clamp(2rem, 1rem + 3vw, 4rem);
  }

  .hero__text { grid-area: text; }
  .hero__say { grid-area: say; }
  .portrait { grid-area: portrait; }
}

.hero__title em { font-style: normal; color: var(--accent); }

/* Split lines rise out of their own mask. The wrapper only exists once the
   script has run, so the headline stays readable without JavaScript. */
.line-mask { display: block; overflow: hidden; }

.portrait {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-self: center;
  width: min(100%, 24rem);
  aspect-ratio: 1 / 1.16;
}

@media (min-width: 60rem) {
  .portrait { justify-self: end; }
}

.portrait__shape,
.portrait__rule,
.portrait__img,
.badge {
  transform: translate3d(
    calc(var(--px, 0) * var(--depth, 0) * 1px),
    calc((var(--py, 0) + var(--sy, 0)) * var(--depth, 0) * 1px),
    0
  );
}

.portrait__shape {
  position: absolute;
  inset: 20% 4% 0 4%;
  background: var(--accent);
  border-radius: 999px 999px var(--radius-lg) var(--radius-lg);
  transform-origin: 50% 100%;
  transform: translate3d(
      calc(var(--px, 0) * var(--depth, 0) * 1px),
      calc((var(--py, 0) + var(--sy, 0)) * var(--depth, 0) * 1px),
      0
    )
    scaleY(calc(1 + var(--grow, 0) * 0.16));
  --depth: 14;
}

/* A hairline running past the arch, so the figure sits in a composition
   rather than on top of a coloured blob. */
.portrait__rule {
  position: absolute;
  left: -12%;
  right: -12%;
  bottom: 26%;
  height: 1px;
  background: var(--line-strong);
  --depth: 30;
}

.portrait__img {
  position: relative;
  z-index: 2;
  width: 104%;
  margin-inline-start: -2%;
  height: auto;
  filter: drop-shadow(0 26px 34px rgba(16, 21, 19, 0.26));
  /* The photograph ends mid torso. Fading the last strip into the arch reads
     as a deliberate crop instead of a straight cut through the shirt. */
  -webkit-mask-image: linear-gradient(to bottom, #000 93%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 93%, transparent 100%);
  --depth: 50;
}

/* Rotating stamp. Slow enough to read, and it stops for reduced motion. */
.badge {
  position: absolute;
  z-index: 3;
  left: -1.5rem;
  bottom: 1.5rem;
  width: 7rem;
  height: 7rem;
  --depth: 75;
}

.badge__disc { fill: var(--paper); stroke: var(--ink); stroke-width: 0.8; }

.badge__text {
  font-family: var(--font-mono);
  font-size: 8.2px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  fill: var(--ink);
}

.badge__arrow {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.badge__spin { transform-origin: 60px 60px; animation: badge-spin 24s linear infinite; }

@keyframes badge-spin { to { transform: rotate(360deg); } }

/* Walking character --------------------------------------------------------
   Follows the pointer along the hero baseline, idles when it arrives, and
   hops when tapped. Decorative, so it never takes a click. */

/* It stands on the page's own hairlines and walks along whichever one it is
   on. Absolute, not fixed, so the line it stands on is a real line in the
   document rather than a spot on the glass. */
.critter {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 4;
  width: 3rem;
  opacity: 0;
  pointer-events: none;
}

.critter svg { display: block; width: 100%; height: auto; overflow: visible; }

.critter__body,
.critter__neck,
.critter__leg,
.critter__beak,
.critter__wing {
  fill: none;
  stroke: var(--ink);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.critter__body { fill: var(--paper); }
.critter__head { fill: var(--paper); stroke: var(--ink); stroke-width: 2.6; }
.critter__eye { fill: var(--ink); }
.critter__beak { fill: var(--accent); stroke: var(--accent); }

/* Marquee ------------------------------------------------------------------ */

.marquee {
  overflow: hidden;
  padding-block: 0.9rem;
  background: var(--paper-2);
  border-block: 1px solid var(--line);
  transition: background-color 0.6s var(--ease), border-color 0.6s var(--ease);
}

.marquee.is-green {
  background: var(--accent);
  border-block-color: var(--accent);
}


/* Pointer ring, scroll progress, paper grain ------------------------------- */

.cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  margin: -1.125rem 0 0 -1.125rem;
  border: 1px solid var(--accent);
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  transition: width var(--dur) var(--ease), height var(--dur) var(--ease),
    margin var(--dur) var(--ease), background-color var(--dur) var(--ease),
    opacity var(--dur) var(--ease);
}

.cursor__label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
}

.cursor.is-label {
  width: 5.5rem;
  height: 5.5rem;
  margin: -2.75rem 0 0 -2.75rem;
  background: var(--accent);
}

.cursor.is-label .cursor__label { opacity: 1; }

/* Over a preview the ring is the cursor, so the arrow gets out of the way. */
@media (hover: hover) and (pointer: fine) {
  .work__media { cursor: none; }
}

.progress {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 200;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* Fine paper grain over the page. Normal blend on purpose: a fixed layer with
   mix-blend-mode made the compositor re-blend the whole viewport every frame, and
   it keeps the flat colour areas from looking like plastic. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 150;
  pointer-events: none;
  opacity: 0.1;
  mix-blend-mode: normal;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}

/* Copy to clipboard -------------------------------------------------------- */

.contact__mailrow {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--sp-4);
  margin-top: var(--sp-6);
}

.copy {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 2.5rem;
  padding: 0 var(--sp-4);
  border: 1px solid rgba(236, 238, 233, 0.35);
  border-radius: 999px;
  background: transparent;
  color: var(--on-dark);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
    border-color var(--dur) var(--ease), transform var(--dur-fast) var(--ease);
}

.copy:hover { border-color: var(--on-dark); background: rgba(236, 238, 233, 0.08); }
.copy:active { transform: scale(0.97); }
.copy.is-done { background: var(--accent-on-dark); border-color: var(--accent-on-dark); color: var(--dark); }

/* Sticky action bar, phones only ------------------------------------------- */

.bookbar {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 95;
  display: none;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--gutter) calc(var(--sp-3) + env(safe-area-inset-bottom));
  background: rgba(242, 241, 236, 0.94);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--line);
  transform: translateY(110%);
  transition: transform var(--dur) var(--ease);
}

.bookbar.is-up { transform: translateY(0); }
.bookbar .btn { flex: 1; min-height: 3rem; }

@media (max-width: 47.99rem) {
  .bookbar { display: flex; }
}

/* Touch feedback: a press should visibly land. */
@media (hover: none) {
  .btn:active,
}

/* ==========================================================================
   Phone composition. Drawn for the screen rather than folded down from the
   desktop layout: the hero becomes a poster, the projects a swipeable deck,
   and capabilities collapse into rows opened one at a time.
   ========================================================================== */

@media (max-width: 47.99rem) {
  .hero {
    padding-top: calc(var(--header-h) + 1.5rem);
    padding-bottom: 0;
  }

  .hero__grid { gap: var(--sp-6); }

  .hero__meta { margin-top: var(--sp-6); }

  /* Projects become a deck you swipe, one card at a time, with each phone
     screenshot shown whole inside its card. */
  .work-list {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 85%;
    gap: var(--sp-4);
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    padding-bottom: var(--sp-2);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--gutter);
    scrollbar-width: none;
  }

  .work-list::-webkit-scrollbar { display: none; }

  .work {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    scroll-snap-align: start;
  }

  .work__media {
    width: 100%;
    margin: 0;
    padding-block: var(--sp-5);
    background: var(--paper-2);
    border: 0;
    border-bottom: 1px solid var(--line);
    border-radius: 0;
  }

  .work__media picture { width: min(52%, 11rem); }
  .work__body { padding: var(--sp-5); }
  .work__top { margin-top: 0; }
  .work__desc { font-size: var(--fs-small); }

  /* Position in the deck, and a tap target for moving through it. */
  .work-dots {
    display: flex;
    justify-content: center;
    gap: var(--sp-2);
    margin-top: var(--sp-5);
  }

  .work-dot {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    background: none;
    border: 0;
    cursor: pointer;
  }

  .work-dot::before {
    content: "";
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: var(--line-strong);
    transition: background-color var(--dur) var(--ease), transform var(--dur) var(--ease);
  }

  .work-dot.is-on::before { background: var(--accent); transform: scale(1.25); }

  /* Room for the sticky bar, so it never covers the last line of the page. */
  .footer { padding-bottom: 5.5rem; }
}

@media (min-width: 48rem) {
  .work-dots { display: none; }
}

/* Single column territory, phones and tablets: the figure becomes a poster
   cropped by the screen edges rather than a small picture floating in the
   middle of the column. */
@media (max-width: 59.99rem) {
  /* The figure stands on a full bleed arch, cropped by the screen edges the
     way a printed poster would be. */
  .portrait {
    justify-self: stretch;
    width: calc(100% + var(--gutter) * 2);
    margin-inline: calc(var(--gutter) * -1);
    aspect-ratio: auto;
    height: clamp(22rem, 92vw, 28rem);
  }

  /* Bleeds to both edges on a phone. On a tablet the arch stops growing and
     centres instead, so the face never becomes billboard sized. */
  .portrait__shape {
    inset: 12% max(0px, calc(50% - 14rem)) 0;
    border-radius: 999px 999px 0 0;
  }

  .portrait__img {
    width: min(74%, 19rem);
    margin-inline: auto;
    filter: drop-shadow(0 -10px 30px rgba(16, 21, 19, 0.2));
  }

  /* The horizon line is desktop scaffolding. Full bleed it would just cut
     across the figure's chest. */
  .portrait__rule { display: none; }

  /* Sits on the arch's left edge, wherever that lands: hard against the
     gutter on a phone, tucked beside the centred arch on a tablet. */
  .badge {
    left: max(var(--gutter), calc(50% - 15.5rem));
    bottom: 1rem;
    width: 5.5rem;
    height: 5.5rem;
  }
}

/* Reduced motion: keep the layout, drop the movement. ---------------------- */

@media (prefers-reduced-motion: reduce) {
  .badge__spin { animation: none; }

  .portrait__shape,
  .portrait__rule,
  .portrait__img,
  .badge { transform: none; }

  .critter,
  .cursor { display: none; }
}

/* The preview frame carries the scroll parallax, so the image inside keeps
   its own hover scale. */
@media (min-width: 48rem) {
  .work__media picture {
    display: block;
    width: 100%;
    height: 100%;
  }
}

@media (max-width: 47.99rem) {
  /* Tighter rows so more of the list is visible at once on a phone. */
  .hero__say { margin-top: var(--sp-2); }
}

/* A hairline opens each section. It gives the page a consistent editorial
   rhythm, and it gives the walking character a line to stand on through the
   long stretches that had none. */
.section--ruled {
  padding-top: clamp(2rem, 1rem + 3vw, 3.5rem);
  border-top: 1px solid var(--line);
}

/* Each project row gets the same hairline the rest of the page uses. It gives
   the section the ruled structure the capabilities table has, and it gives the
   character something to stand on through the longest stretch of the page. */
@media (min-width: 48rem) {
  .work {
    padding-top: clamp(2rem, 1rem + 3vw, 3.5rem);
    border-top: 1px solid var(--line);
  }
}

/* ==========================================================================
   Intro curtain, and the layer of small answers: nearly everything on the
   page reacts to being touched, quietly enough that the work stays the star.
   ========================================================================== */

/* Intro -------------------------------------------------------------------- */

.intro {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: flex;
  flex-direction: column;
}

.intro__half {
  position: relative;
  flex: 1;
  background: var(--paper);
}

.intro__half--top { border-bottom: 1px solid var(--line-strong); }

.intro__mask {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 2rem;
  overflow: hidden;
  text-align: center;
}

.intro__name {
  font-size: clamp(1.5rem, 1rem + 2.4vw, 2.5rem);
  font-weight: 600;
  letter-spacing: var(--tracking-tight);
}

.intro__goose {
  position: absolute;
  bottom: -1px;
  left: 0;
  width: 3.2rem;
  transform: translateX(-5rem);
  overflow: visible;
}

/* Without JavaScript the curtain would never open, so it never appears; and
   it plays once per session, so a second visit goes straight to the page. */
.no-js .intro,
.intro-skip .intro { display: none; }

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

/* The peek over the arch ---------------------------------------------------- */

.portrait__shape { z-index: 1; }

/* Leans out sideways from behind the right edge of the arch. Hidden state is
   tucked back behind the shape; the hero clips anything past the screen. */
.peekaboo {
  position: absolute;
  z-index: 0;
  top: 46%;
  right: -1.4rem;
  width: 4.6rem;
  height: auto;
  overflow: visible;
  transform: translateX(-4.5rem);
}

.peekaboo path,
.peekaboo circle { stroke-width: 3; }

/* honk ---------------------------------------------------------------------- */

.critter__say {
  position: absolute;
  left: 50%;
  top: -1.9rem;
  padding: 0.15rem 0.55rem;
  background: var(--paper);
  border: 1.5px solid var(--ink);
  border-radius: 999px 999px 999px 4px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--ink);
  white-space: nowrap;
  opacity: 0;
  transform: translateX(-50%) scale(0.6);
  transform-origin: 20% 100%;
}

/* Small answers everywhere -------------------------------------------------- */

.btn .btn__arrow { transition: transform var(--dur) var(--ease); }
.btn:hover .btn__arrow { transform: translateX(4px); }

/* The brand introduces itself. */
.brand__tag {
  position: relative;
  display: inline-block;
}

.brand__tag i {
  font-style: normal;
  transition: opacity var(--dur) var(--ease);
}

.brand__tag i + i {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  color: var(--accent);
  white-space: nowrap;
}

@media (hover: hover) {
  .brand:hover .brand__tag i:first-child { opacity: 0; }
  .brand:hover .brand__tag i + i { opacity: 1; }
}

/* The two words that matter roll letter by letter under the pointer. The
   letters are only wrapped on fine pointer devices; on touch the spans would
   stick mid-roll after a tap. */
.roll {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}

.roll .l {
  position: relative;
  display: inline-block;
  transition: transform 0.35s var(--ease);
  transition-delay: calc(var(--i) * 50ms);
}

.roll .l::after {
  content: attr(data-letter);
  position: absolute;
  left: 0;
  top: 100%;
}

.roll:hover .l { transform: translateY(-100%); }

/* Project rows answer the pointer as one piece. */
@media (hover: hover) {
  .work__title { transition: color var(--dur) var(--ease); }
  .work:hover .work__title { color: var(--accent); }

  .tag {
    transition: transform var(--dur) var(--ease),
      border-color var(--dur) var(--ease);
  }

  .work:hover .tag { transform: translateY(-2px); border-color: var(--line-strong); }
  .work:hover .tag:nth-child(2) { transition-delay: 40ms; }
  .work:hover .tag:nth-child(3) { transition-delay: 80ms; }
  .work:hover .tag:nth-child(4) { transition-delay: 120ms; }
  .work:hover .tag:nth-child(5) { transition-delay: 160ms; }

  /* Spec rows lean in slightly when read with the pointer. */
  .hero__meta > div,
  .facts__row,
  .person__rows > div {
    transition: padding-left var(--dur) var(--ease);
  }

  .hero__meta > div:hover,
  .facts__row:hover,
  .person__rows > div:hover { padding-left: var(--sp-2); }

  .hero__meta dt,
  .facts__row dt,
  .person__rows dt { transition: color var(--dur) var(--ease); }

  .hero__meta > div:hover dt,
  .facts__row:hover dt { color: var(--accent); }
  .person__rows > div:hover dt { color: var(--accent-on-dark); }

  /* The closed plus swings a quarter turn before it opens. */

  /* The stamp is a real doorway to the contact section. */
  .badge { cursor: pointer; }
  .badge .badge__arrow { transition: transform var(--dur) var(--ease); }
  .badge:hover .badge__arrow { transform: translateX(5px); }

  /* Both small portraits duck upward, the same move as the hero cut out. */
  .about__photo img,
  .person__photo img { transition: transform var(--dur) var(--ease); }
  .about__photo:hover img,
  .person__photo:hover img { transform: translateY(-3px); }

  /* Footer links get the same underline the navigation has. */
  .footer__links a { position: relative; }

  .footer__links a::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: -2px;
    width: 100%;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--dur) var(--ease);
  }

  .footer__links a:hover::after { transform: scaleX(1); }
}

/* The character can be talked to. */
.critter { pointer-events: auto; cursor: pointer; }

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

/* Phone hero, resolved --------------------------------------------------
   The green no longer stops dead at the bottom of the photograph. The lead
   and the actions sit inside the colour as the poster's caption, and the
   block closes with a rounded edge before the page returns to paper. */

@media (max-width: 47.99rem) {
  .hero__grid { gap: 0; }

  .hero__text { margin-bottom: var(--sp-6); }

  .hero__say {
    margin-top: -1px;
    margin-inline: calc(var(--gutter) * -1);
    padding: var(--sp-5) var(--gutter) var(--sp-6);
    background: var(--accent);
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  }

  .hero__say .hero__lead {
    margin-top: 0;
    max-width: none;
    color: var(--on-dark);
    font-size: var(--fs-body);
  }

  /* Buttons flip for the green ground. */
  .hero__say .btn--primary { background: var(--paper); color: var(--ink); }
  .hero__say .btn--primary:hover { background: var(--surface); }
  .hero__say .btn--ghost { border-color: rgba(236, 238, 233, 0.4); color: var(--on-dark); }
  .hero__say .btn--ghost:hover { border-color: var(--on-dark); background: rgba(236, 238, 233, 0.1); }

  .hero__meta { margin-top: var(--sp-7); }
}

/* ==========================================================================
   The green stage and the paper sheet.

   The hero does not scroll away. Once its bottom reaches the bottom of the
   screen it holds there as a stage, and everything after it rises over it
   like a sheet: first the green strip, then the paper Work section with
   rounded corners and a shadow, like a card being laid on top. While it is
   being covered the stage stays as it is; on desktop it sinks back a touch.
   Every width; the phone adds the green spec rows.
   ========================================================================== */

.hero {
  position: sticky;
  /* Holds by its bottom edge: the offset is the part that does not fit. */
  top: min(0px, calc(100vh - var(--hero-h, 100vh)));
  top: min(0px, calc(100svh - var(--hero-h, 100svh)));
  z-index: 0;
  transform-origin: 50% 72%;
  will-change: transform;
}

/* Everything after the hero is the sheet. It needs its own paint and a
   higher layer, otherwise the stage would show through it. */
.marquee,
.section,
.contact,
.footer {
  position: relative;
  z-index: 1;
}

.section,
.footer { background: var(--paper); }

/* The paper section lands on the green strip like a card. */
#work {
  z-index: 2;
  margin-top: calc(var(--radius-lg) * -1);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: 0 -18px 40px rgba(16, 21, 19, 0.2);
}

@media (max-width: 47.99rem) {
  /* The green runs to the very end of the hero: the spec rows sit on it, so
     the sheet rises over green rather than over a strip of paper. */
  .hero__say {
    border-radius: 0;
    padding-bottom: var(--sp-4);
  }

  .hero__meta {
    margin: 0 calc(var(--gutter) * -1) 0;
    padding: var(--sp-2) var(--gutter) var(--sp-6);
    background: var(--accent);
    border-top: 0;
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  }

  .hero__meta > div { border-bottom: 1px solid rgba(236, 238, 233, 0.18); }
  .hero__meta > div:last-child { border-bottom: 0; }
  .hero__meta dt { color: rgba(236, 238, 233, 0.72); }
  .hero__meta dd { color: var(--on-dark); }
  .hero__meta > div:hover dt { color: var(--on-dark); }
}

@media (prefers-reduced-motion: reduce) {
  .hero { position: relative; top: auto; transform: none; will-change: auto; }
}

/* Pocket peek --------------------------------------------------------------
   On desktop, hovering a button or a nav link makes a small goose head lean
   out from behind it, from a side chosen at random. The pocket is a clipped
   box that sits just outside the button on that side, so the head appears to
   come from behind the button's edge without any stacking tricks. */

.btn { position: relative; }

.peek-pocket {
  position: absolute;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
}

.peek-pocket--top,
.peek-pocket--bottom { width: 2.75rem; height: 2.4rem; }
.peek-pocket--left,
.peek-pocket--right { width: 2.6rem; height: 2.75rem; }

.peek-pocket--top { bottom: calc(100% - 2px); }
.peek-pocket--bottom { top: calc(100% - 2px); }
.peek-pocket--left { right: calc(100% - 2px); }
.peek-pocket--right { left: calc(100% - 2px); }

.peek-pocket svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.peek-pocket .critter__neck,
.peek-pocket .critter__head { stroke-width: 3; }

/* On the dark band the line art flips to light so it still reads. */
.contact .peek-pocket .critter__neck,
.contact .peek-pocket .critter__head { stroke: var(--on-dark); }
.contact .peek-pocket .critter__head { fill: var(--dark); }
.contact .peek-pocket .critter__eye { fill: var(--on-dark); }
.contact .peek-pocket .critter__beak { fill: var(--accent-on-dark); stroke: var(--accent-on-dark); }

/* Dark buttons on paper: same treatment, the head comes out light. */
.btn--primary .peek-pocket .critter__neck,
.btn--primary .peek-pocket .critter__head { stroke: var(--ink); }

/* Paint strokes over the stage ----------------------------------------------
   A zero-height row directly above the green strip. Its strokes hang upward
   from the strip's top edge and are scaled open by scroll, in the strip's
   exact green, so strip and strokes read as one shape spreading up over the
   hero. Nine strokes on desktop, fifteen thinner ones on a phone. */

.paint {
  position: relative;
  z-index: 1;
  height: 0;
  --n: 15;
}

.paint__band {
  position: absolute;
  bottom: 0;
  left: calc(100% / var(--n) * var(--i) - 0.3%);
  width: calc(100% / var(--n) + 0.6%);
  height: 110vh;
  background: var(--accent);
  border-radius: 999px 999px 0 0;
  transform: scaleY(0);
  transform-origin: 50% 100%;
  will-change: transform;
}

.paint__band:nth-child(1) { --i: 0; left: 0; }
.paint__band:nth-child(2) { --i: 1; }
.paint__band:nth-child(3) { --i: 2; }
.paint__band:nth-child(4) { --i: 3; }
.paint__band:nth-child(5) { --i: 4; }
.paint__band:nth-child(6) { --i: 5; }
.paint__band:nth-child(7) { --i: 6; }
.paint__band:nth-child(8) { --i: 7; }
.paint__band:nth-child(9) { --i: 8; }
.paint__band:nth-child(10) { --i: 9; }
.paint__band:nth-child(11) { --i: 10; }
.paint__band:nth-child(12) { --i: 11; }
.paint__band:nth-child(13) { --i: 12; }
.paint__band:nth-child(14) { --i: 13; }
/* The last visible stroke is pinned to the right edge, so the seam overlap
   never pokes past the page and opens a sideways scroll. */
.paint__band:nth-child(15) { --i: 14; left: auto; right: 0; }

@media (min-width: 48rem) {
  .paint { --n: 9; }
  .paint__band:nth-child(n + 10) { display: none; }
  .paint__band:nth-child(9) { left: auto; right: 0; }
}

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

/* Header over the green -------------------------------------------------------
   While the paint and the strip are under it, the bar takes the same green,
   so it reads as part of the shape rather than an empty paper strip on top. */

.header.is-green {
  background: var(--accent);
  border-bottom-color: transparent;
  backdrop-filter: none;
}

.header.is-green .brand,
.header.is-green .nav__link,
.header.is-green .nav__link:hover { color: var(--on-dark); }
.header.is-green .brand span,
.header.is-green .brand__tag i { color: rgba(236, 238, 233, 0.72); }
.header.is-green .brand__tag i + i { color: var(--on-dark); }
.header.is-green .menu-btn__bars span { background: var(--on-dark); }
.header.is-green .header__cta { background: var(--paper); color: var(--ink); }
.header.is-green .header__cta:hover { background: var(--surface); }

/* ==========================================================================
   Case studies, second pass. One horizontal composition per study on a
   twelve column grid: the before and after comparison on seven columns,
   the story and its evidence on the remaining five, both starting on the
   same line. Phones stack in reading order.
   ========================================================================== */

.work-list--studies { gap: clamp(4.5rem, 3rem + 6vw, 8rem); }

.study {
  display: grid;
  gap: var(--sp-5);
  align-items: start;
}
@media (min-width: 64rem) {
  .study {
    grid-template-columns: repeat(12, 1fr);
    column-gap: clamp(3rem, 1.5rem + 3vw, 4.5rem);
  }
  .study__visual { grid-column: 1 / span 7; }
  .study__body { grid-column: 8 / -1; padding-top: 0.15rem; }
  .study--flip .study__visual { grid-column: 6 / -1; grid-row: 1; }
  .study--flip .study__body { grid-column: 1 / span 5; grid-row: 1; }
}

/* The comparison. Before above, after below, full width of the visual
   column, so each screenshot is large enough to read and the pair stands
   as tall as the copy beside it. */
.compare {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5rem;
  padding: 0.5rem;
  border-radius: var(--radius);
  background: var(--paper-2);
  border: 1px solid var(--line);
}
.compare__pane {
  position: relative;
  display: block;
  margin: 0;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  border-radius: calc(var(--radius) - 0.4rem);
  border: 1px solid var(--line);
  background: var(--surface);
}
/* The image sits in a slightly taller layer than its pane, so the scroll
   drift never shows an edge. Hover scale stays on the image itself, one
   transform per element. */
.compare__shift {
  position: absolute;
  inset: -4% 0;
  display: block;
}
.compare__pane img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 30%;
  transition: transform var(--dur-slow) var(--ease);
}
.compare__pane--before { filter: saturate(0.9); }
.compare__pane--after { cursor: pointer; }
@media (hover: hover) { .compare__pane--after:hover img { transform: scale(1.025); } }
.compare__label {
  position: absolute;
  left: 0.65rem;
  bottom: 0.65rem;
  z-index: 1;
  padding: 0.25rem 0.55rem;
  border-radius: 999px;
  background: rgba(251, 250, 248, 0.92);
  color: var(--ink-2);
  font-size: 0.66rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border: 1px solid var(--line);
}
.compare__label--after { background: var(--accent); color: #fff; border-color: var(--accent); }

.study__head { margin-bottom: var(--sp-5); }
.study__title {
  font-size: clamp(1.875rem, 1.3rem + 2vw, 2.875rem);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  margin: 0;
}
.study__meta {
  margin-top: 0.55rem;
  font-size: var(--fs-small);
  color: var(--muted);
}
/* The story column is shorter than the comparison, so it holds in view
   while the screenshots scroll past. The column itself stretches to the
   height of that row, which is what stops the text before the band. */
@media (min-width: 64rem) {
  .study[data-study="curtain"] .study__body { align-self: stretch; }
  .study[data-study="curtain"] .study__hold {
    position: sticky;
    top: calc(var(--header-h) + 2.5rem);
  }
}

/* ==========================================================================
   Case studies, results first. The name, then the number that matters at
   display size, then the smaller results and the structural proof, then a
   few lines of story. Nothing is boxed; the scale and the space do the work.
   Each study arranges its numbers differently.
   ========================================================================== */

.study__outcome { margin-top: var(--sp-2); }
.result { margin: 0; }
.result__num {
  display: block;
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 0.92;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.result__cap {
  display: block;
  margin-top: 0.45rem;
  color: var(--ink-2);
  text-wrap: pretty;
}
.result--main .result__num { font-size: clamp(4rem, 2.4rem + 6.4vw, 7.5rem); }
.result--main .result__cap { font-size: var(--fs-lead); max-width: 18ch; }
.result--sub .result__num { font-size: clamp(1.9rem, 1.4rem + 1.8vw, 2.9rem); }
.result--sub .result__cap { font-size: var(--fs-small); color: var(--muted); margin-top: 0.25rem; }

/* Structural proof: what changed, in one or two short lines. */
.proof {
  margin: var(--sp-5) 0 0;
  padding: 0;
  list-style: none;
  font-weight: 600;
  color: var(--ink);
  font-size: var(--fs-body);
  font-variant-numeric: tabular-nums;
}
.proof li + li { margin-top: 0.3rem; }
.proof--row { display: flex; flex-wrap: wrap; gap: 0.3rem var(--sp-5); }
.study__note {
  margin: var(--sp-3) 0 0;
  font-size: 0.8125rem;
  color: var(--muted);
}

/* Spellbound: one column, the main result on top of the rest. */
.outcome--stack .result--sub { margin-top: var(--sp-5); }

/* The Make Up Room: the two results share a baseline, proof in a row. */
.outcome--pair {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-3) var(--sp-6);
}
.outcome--pair .result--main { flex: 0 0 auto; }
.outcome--pair .result--main .result__num { font-size: clamp(3.5rem, 2rem + 5.6vw, 6.5rem); }
.outcome--pair .result--sub { flex: 1 1 8rem; max-width: 15rem; }
.outcome--pair .proof,
.outcome--pair .study__note { flex-basis: 100%; }
.outcome--pair .study__note { margin-top: 0; }

/* Fuse Concept: the main result, then two smaller ones side by side. */
.result-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4) var(--sp-6);
  margin-top: var(--sp-5);
}
.result-row .result--sub { flex: 1 1 9rem; }

/* The story is short and sits under the numbers. */
.study__copy { margin-top: var(--sp-6); }
.study__copy p {
  color: var(--ink-2);
  max-width: 42ch;
}
.study__cta { display: flex; margin-top: var(--sp-5); }

/* What Fuse has behind it, across both columns, set as words on a line. */
.study__built {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-5);
  margin: var(--sp-4) 0 0;
  padding-inline: 0.5rem 0;
}
.study__built-label {
  font-size: var(--fs-small);
  color: var(--muted);
}
.study__built-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem var(--sp-4);
  font-size: clamp(1.125rem, 1rem + 0.6vw, 1.5rem);
  font-weight: 600;
  letter-spacing: var(--tracking-tight);
  color: var(--ink);
}
.study__built-list span + span::before {
  content: "\00b7";
  margin-right: var(--sp-4);
  color: var(--line-strong);
  font-weight: 400;
}
@media (min-width: 64rem) {
  .study__built { grid-column: 1 / -1; }
}

/* Phones and tablets: name, result, proof, story, then the comparison. */
@media (max-width: 63.99rem) {
  .study { gap: var(--sp-5); }
  .study__visual { order: 2; }
  .study__body,
  .study__hold { display: contents; }
  .study__head { order: 0; margin-bottom: 0; }
  .study__outcome { order: 1; margin-top: 0; }
  .study__copy { order: 1; margin-top: 0; }
  .study__built { order: 3; }
  .study__cta { order: 5; margin-top: 0; }
}
@media (min-width: 48rem) and (max-width: 63.99rem) {
  .compare { grid-template-columns: 1fr 1fr; }
}

/* Built for. The three rebuilds by name, one line, centred; the strip
   takes the green with the stage the way it always did. */
.marquee { padding-block: 1.1rem 1.4rem; }
.marquee__label {
  text-align: center;
  margin-bottom: 0.7rem;
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--muted);
  transition: color 0.6s var(--ease);
}
.marquee.is-green .marquee__label { color: var(--on-dark-muted); }
.built-for {
  display: flex;
  align-items: baseline;
  gap: 0 1.6rem;
  white-space: nowrap;
  will-change: transform;
  padding-inline: 0;
}
.built-for a,
.built-for span,
.built-for i {
  flex: none;
  transition: color 0.6s var(--ease);
}
.built-for__name {
  font-size: var(--fs-lead);
  font-weight: 500;
  letter-spacing: var(--tracking-tight);
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color 0.6s var(--ease), border-color var(--dur) var(--ease);
}
.built-for__name:hover { border-bottom-color: currentColor; }
.built-for span {
  font-size: var(--fs-body);
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.built-for i {
  font-style: normal;
  color: var(--line-strong);
  font-size: var(--fs-body);
}
.marquee.is-green .built-for__name { color: var(--on-dark); }
.marquee.is-green .built-for span { color: var(--on-dark-muted); }
.marquee.is-green .built-for i { color: var(--accent-on-dark); }
@media (prefers-reduced-motion: reduce) {
  .built-for { flex-wrap: wrap; justify-content: center; white-space: normal; row-gap: 0.4rem; padding-inline: var(--gutter); }
}

/* The Work head carries one sentence under its title. */
.section__lead {
  margin-top: var(--sp-4);
  color: var(--ink-2);
  max-width: 46ch;
}

/* Arrows inside a result read lighter than the numbers. */
.result__arrow {
  font-weight: 400;
  font-size: 0.6em;
  color: var(--muted);
  margin-inline: 0.05em 0.12em;
  vertical-align: 0.12em;
}

/* The demo account for the Fuse admin. */
.study__demo {
  margin-top: var(--sp-4);
  font-size: 0.8125rem;
  line-height: 1.55;
  color: var(--muted);
  max-width: 42ch;
}
.study__demo a { color: var(--ink); }
.study__demo b { font-weight: 600; color: var(--ink); }

/* The studies list never becomes the phone-width horizontal scroller. */
@media (max-width: 47.99rem) {
  .work-list--studies {
    display: flex;
    flex-direction: column;
    gap: clamp(3.5rem, 2rem + 6vw, 5rem);
    margin-inline: 0;
    padding-inline: 0;
    padding-bottom: 0;
    overflow: visible;
    scroll-snap-type: none;
  }
}


/* ==========================================================================
   Editorial pass. The sections after the hero lose their labels, their
   numbering and most of their rules; hierarchy comes from scale and space.
   ========================================================================== */

/* Section heads without the label: the title sits on the rule by itself. */
.section__head--caps { align-items: baseline; }
.section__head--caps .section__note { max-width: 40ch; }

/* About: the portrait and one plain word. */
.about__word {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--muted);
}
.about__facts {
  font-size: var(--fs-body);
  color: var(--ink-2);
  max-width: 38ch;
}
@media (min-width: 60rem) {
  .about__facts { padding-top: 0.4rem; }
}
.about__facts p + p { margin-top: var(--sp-4); }
.about__tools { color: var(--muted); font-size: var(--fs-small); }


/* Contact band ------------------------------------------------------------- */
.contact {
  background: var(--dark);
  color: var(--on-dark);
}


/* ==========================================================================
   Capabilities: a reading line.

   The heading and one statement hold on the left. The five capabilities are
   set large on the right and drift a step to the right and back as they go
   down, like stanzas. As the page scrolls, whichever one is crossing the
   middle of the screen is set in ink and shifts a little to the right; the
   others wait in grey. Reduced motion and no JavaScript show them all in ink.
   ========================================================================== */

.caps {
  display: grid;
  gap: var(--sp-6);
}
@media (min-width: 60rem) {
  .caps {
    grid-template-columns: repeat(12, 1fr);
    column-gap: clamp(2rem, 1rem + 2vw, 3.5rem);
    align-items: start;
  }
  .caps__anchor { grid-column: 1 / span 4; grid-row: 1; align-self: stretch; }
  .caps__list { grid-column: 6 / -1; grid-row: 1; }
  .caps__trail { display: block; grid-column: 5 / 6; grid-row: 1; align-self: stretch; position: relative; }
  .caps__hold {
    position: sticky;
    top: calc(var(--header-h) + 3rem);
  }
}
/* Narrow screens: no gap column, so the goose's tracks run down a thin
   column of their own on the left of the list. */
@media (max-width: 59.99rem) {
  .caps { position: relative; }
  .caps .caps__list { padding-left: 1.6rem; }
  .caps__trail { display: block; position: absolute; left: 0; width: 1.4rem; pointer-events: none; }
}
.caps__trail svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.caps__trail path {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.5;
}
.caps__title {
  font-size: var(--fs-h2);
  margin: 0;
}
.caps__lead {
  margin-top: var(--sp-5);
  color: var(--muted);
  max-width: 30ch;
}

.caps__list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.cap {
  padding-block: clamp(1.5rem, 0.8rem + 2.4vw, 3rem);
  --step: clamp(1.25rem, 5%, 4.5rem);
}
.cap:nth-child(2),
.cap:nth-child(4) { padding-left: var(--step); }
.cap:nth-child(3) { padding-left: calc(var(--step) * 2); }
.cap:first-child { padding-top: 0.35rem; }

.cap__name {
  margin: 0;
  font-size: clamp(1.75rem, 1.05rem + 2.6vw, 3.5rem);
  line-height: 1.02;
  letter-spacing: var(--tracking-tight);
  color: var(--muted);
  max-width: 14ch;
  transition: color 0.55s var(--ease), transform 0.55s var(--ease);
}
.cap__text {
  margin: 0.8rem 0 0;
  max-width: 38ch;
  font-size: var(--fs-small);
  line-height: 1.55;
  color: var(--muted);
  opacity: 0.55;
  transition: opacity 0.55s var(--ease), transform 0.55s var(--ease);
}
.cap.is-read .cap__name { color: var(--ink); transform: translateX(0.6rem); }
.cap.is-read .cap__text { opacity: 1; transform: translateX(0.6rem); }

@media (max-width: 59.99rem) {
  .cap { --step: 0.9rem; }
  .cap__name { max-width: none; }
}
@media (prefers-reduced-motion: reduce) {
  .cap__name { color: var(--ink); transform: none; }
  .cap__text { opacity: 1; transform: none; }
}
.no-js .cap__name { color: var(--ink); }
.no-js .cap__text { opacity: 1; }

/* ==========================================================================
   Contact: the last chapter.

   The question and its answer at the top, the address across the whole
   page as the one thing to remember, the ways to act underneath it, and the
   portrait standing on the bottom edge the way it stood in the hero.
   ========================================================================== */

.contact {
  padding-block: var(--section-y) 0;
  overflow: hidden;
}

.contact__open {
  display: grid;
  gap: var(--sp-5);
}
@media (min-width: 60rem) {
  .contact__open {
    grid-template-columns: repeat(12, 1fr);
    column-gap: clamp(2rem, 1rem + 2vw, 3.5rem);
    align-items: end;
  }
  .contact__open .contact__title { grid-column: 1 / span 7; }
  .contact__open .contact__text { grid-column: 8 / -1; margin-top: 0; }
}
.contact__title {
  margin: 0;
  font-size: var(--fs-h2);
  max-width: 14ch;
}
.contact__text {
  color: var(--on-dark-muted);
  max-width: 44ch;
}

/* The address. Display size, one line on anything wider than a phone, with
   a drawn underline that is stroked in as the section arrives and again
   under the pointer. */
.contact__line {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-5);
  margin-top: clamp(3rem, 2rem + 4vw, 6rem);
}
.contact__mail {
  position: relative;
  display: inline-block;
  padding-bottom: 0.18em;
  font-size: clamp(1.55rem, 0.4rem + 4.6vw, 5.25rem);
  font-weight: 500;
  letter-spacing: -0.035em;
  line-height: 1;
  color: var(--on-dark);
  text-decoration: none;
  border: 0;
  word-break: normal;
  overflow-wrap: anywhere;
}
.contact__mail:hover { border: 0; }
.contact__stroke {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 0.16em;
  overflow: visible;
}
.contact__stroke path {
  fill: none;
  stroke: var(--accent-on-dark);
  stroke-width: 2.4;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.contact__line .copy { align-self: center; }

/* The close: actions and a few sentences on the left, the portrait on the
   right, its arch running off the bottom of the section. */
.contact__close {
  display: grid;
  gap: var(--sp-6);
  margin-top: clamp(2.5rem, 1.5rem + 3vw, 4.5rem);
}
@media (min-width: 60rem) {
  .contact__close {
    grid-template-columns: repeat(12, 1fr);
    column-gap: clamp(2rem, 1rem + 2vw, 3.5rem);
    align-items: end;
  }
  .contact__actions { grid-column: 1 / span 6; grid-row: 1; align-self: start; margin-top: 0; }
  .contact__person { grid-column: 1 / span 5; grid-row: 2; padding-bottom: clamp(3rem, 2rem + 3vw, 5rem); }
  .contact__mini { grid-column: 8 / -1; grid-row: 1 / span 2; justify-self: end; }
}
.contact__person { margin-top: var(--sp-4); }
.person__note {
  margin: 0;
  color: var(--on-dark-muted);
  max-width: 40ch;
}
.person__time {
  margin-top: var(--sp-4);
  font-size: var(--fs-small);
  color: var(--on-dark-muted);
}

.contact__mini {
  position: relative;
  display: flex;
  align-items: flex-end;
  width: min(100%, 24rem);
  aspect-ratio: 1 / 0.96;
  margin-top: var(--sp-6);
}
.contact__arch {
  position: absolute;
  inset: 16% 4% 0 4%;
  background: var(--accent);
  border-radius: 999px 999px 0 0;
}
.contact__img {
  position: relative;
  z-index: 1;
  width: 84%;
  margin-inline: auto;
  height: auto;
  filter: drop-shadow(0 18px 26px rgba(0, 0, 0, 0.45));
}

@media (max-width: 59.99rem) {
  .contact__line { margin-top: clamp(2.5rem, 1.5rem + 4vw, 4rem); }
  .contact__actions { margin-top: 0; }
  .contact__mini { width: min(68%, 18rem); margin-top: var(--sp-4); }
}
@media (max-width: 47.99rem) {
  .contact__actions .btn--light { flex: 1 1 100%; }
  .contact__actions .btn--outline-light { flex: 1 1 0; }
}

/* The clock is the one thing on the page that is true only right now. */
#localTime {
  font-weight: 600;
  color: var(--accent-on-dark);
  font-variant-numeric: tabular-nums;
}


/* ==========================================================================
   Rebuilds (redesign, Oct 2026). My rebuild owns the frame, labelled and
   linked to the live site; their 2019 site is a small grey reference card
   tucked under its corner. The story beside it leads with the number an
   agency would check. Seven columns of evidence, five of story, alternating.
   ========================================================================== */

.rb-list { display: grid; }
.rb {
  display: grid;
  /* One explicit, shrinkable column: an implicit auto column grew to the
     min-content of the big numbers and pushed phones wider than the screen. */
  grid-template-columns: minmax(0, 1fr);
  gap: 1.75rem;
  padding: clamp(3rem, 8vh, 6.5rem) 0;
  /* A hairline on top of every rebuild: it separates them and it is the line
     the goose walks on, one per project. */
  border-top: 1px solid var(--line);
}
/* The section's own rule already sits right above the first rebuild. */
.rb:first-child { border-top: 0; padding-top: clamp(2rem, 5vh, 4rem); }
@media (min-width: 64rem) {
  .rb {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: clamp(2rem, 4vw, 4.5rem);
    align-items: center;
  }
  .rb__visual { grid-column: 1 / span 7; }
  .rb__body { grid-column: 8 / -1; }
  .rb--flip .rb__visual { grid-column: 6 / -1; grid-row: 1; }
  .rb--flip .rb__body { grid-column: 1 / span 5; grid-row: 1; }
}

.rb__visual { position: relative; padding-bottom: 7%; }
.rb__above {
  display: flex; align-items: center; gap: 0.85rem; margin: 0 0 0.9rem;
  font-size: 0.8125rem; color: var(--muted);
}
.rb__above a { text-decoration: none; border-bottom: 1px solid transparent; transition: color 0.3s, border-color 0.3s; }
.rb__above a:hover { color: var(--ink); border-color: currentColor; }
.rb__chip {
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.42rem 0.75rem 0.42rem 0.6rem; border-radius: 999px; background: var(--accent); color: #fff;
  font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase;
}
.rb__chip i { width: 0.42rem; height: 0.42rem; border-radius: 50%; background: #8fd6b2; box-shadow: 0 0 0 3px rgba(143, 214, 178, 0.25); }

.rb__frame {
  position: relative; display: block; aspect-ratio: 16 / 11; overflow: hidden; border-radius: 12px;
  background: var(--line); isolation: isolate;
  box-shadow: 0 0 0 1px rgba(16, 21, 19, 0.06), 0 2px 4px rgba(16, 21, 19, 0.04), 0 40px 70px -45px rgba(16, 21, 19, 0.45);
}
/* Starts at the screenshot's top edge, so the site's own header is never
   cropped; the scroll drift moves it up inside the frame. */
.rb__img { position: absolute; left: 0; top: 0; width: 100%; height: 108%; object-fit: cover; object-position: 50% 0; }

.rb__old {
  position: absolute; z-index: 3; right: -3%; bottom: 0; width: 36%; margin: 0;
  padding: 0.4rem 0.4rem 0.55rem; background: #fff; border-radius: 7px;
  box-shadow: 0 1px 0 rgba(16, 21, 19, 0.06), 0 22px 40px -24px rgba(16, 21, 19, 0.5);
  transform: rotate(-3deg); transform-origin: 80% 90%; cursor: zoom-in;
}
.rb--flip .rb__old { right: auto; left: -3%; transform: rotate(3deg); transform-origin: 20% 90%; }
.rb__old-img { overflow: hidden; border-radius: 4px; aspect-ratio: 16 / 10; }
.rb__old-img img { width: 100%; height: 100%; object-fit: cover; object-position: top; filter: grayscale(1) contrast(0.92) brightness(1.02); transition: filter 0.45s var(--ease); }
.rb__old figcaption { display: flex; justify-content: space-between; margin-top: 0.45rem; font-size: 0.5625rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
.rb__old:hover .rb__old-img img { filter: none; }

.rb__title { margin: 0; font-size: clamp(2rem, 1.3rem + 2.2vw, 3.25rem); font-weight: 600; letter-spacing: -0.035em; line-height: 1.02; color: var(--ink); }
.rb__sub { margin: 0.55rem 0 0; color: var(--ink-2); font-size: var(--fs-small); }

.rb__big { margin-top: 2rem; }
.rb__num {
  display: flex; align-items: baseline; gap: 0.16em; margin: 0; color: var(--accent); font-variant-numeric: tabular-nums;
  font-size: clamp(4.25rem, 2.2rem + 5.6vw, 7.25rem); font-weight: 600; letter-spacing: -0.055em; line-height: 0.86;
}
.rb__was { color: var(--line-strong); }
.rb__arrow { font-size: 0.38em; font-weight: 400; color: var(--muted); transform: translateY(-0.6em); }
.rb__cap { margin: 0.75rem 0 0; font-size: var(--fs-lead); color: var(--ink-2); max-width: 22ch; line-height: 1.35; }

.rb__facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 1.75rem; border-top: 1px solid var(--line); }
.rb__facts > div { padding: 0.95rem 1rem 0.95rem 0; }
.rb__facts > div + div { padding-left: 1rem; border-left: 1px solid var(--line); }
.rb__facts b { display: block; font-size: clamp(1.5rem, 1.1rem + 1.1vw, 2.1rem); font-weight: 600; letter-spacing: -0.035em; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1.05; }
.rb__facts > div > span { display: block; margin-top: 0.3rem; font-size: 0.8125rem; color: var(--muted); line-height: 1.4; }

.rb__tags { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 1.25rem 0 0; padding: 0; }
.rb__tags li { list-style: none; padding: 0.32rem 0.6rem; border: 1px solid var(--line-strong); border-radius: 999px; font-size: 0.75rem; color: var(--ink-2); }
.rb__story { margin: 1.4rem 0 0; color: var(--ink-2); line-height: 1.7; max-width: 44ch; }
.rb__demo { margin: 0.8rem 0 0; font-size: 0.8125rem; color: var(--muted); line-height: 1.55; }
.rb__demo b { color: var(--ink); font-weight: 600; }
.rb__cta { display: flex; flex-wrap: wrap; gap: 0.75rem 1.25rem; align-items: center; margin-top: 1.75rem; }
.rb__link { font-size: 0.9375rem; text-underline-offset: 4px; }

/* Phones and tablets: the story first, then the rebuild with their card beneath. */
@media (max-width: 63.99rem) {
  .rb__visual { order: 2; padding-bottom: 0; }
  .rb__old, .rb--flip .rb__old { position: relative; left: auto; right: auto; width: 54%; max-width: 20rem; margin: -14% 0 0 auto; }
  .rb__big { margin-top: 1.4rem; }
}

/* The headline letter roll now sits on split characters: hovering anywhere on
   the accent phrase rolls every letter in it, as before. */
@media (hover: hover) and (pointer: fine) {
  .roll-group:hover .l { transform: translateY(-100%); }
}

/* ==========================================================================
   Phones: a separate, quieter read (Oct 2026). Long paragraphs that work on
   a wide screen are swapped for one short line, the rebuilds keep only the
   number, two facts and the button, and the capabilities become a list of
   names. Nothing here changes the desktop layout.
   ========================================================================== */

.m-only { display: none; }
@media (max-width: 47.99rem) {
  .m-only { display: block; }
  span.m-only { display: inline; }
  .d-only, .m-hide { display: none !important; }

  .rb__story, .rb__tags, .rb__demo, .rb__link { display: none; }
  .rb__sub { font-size: 0.8125rem; }
  .rb__facts { margin-top: 1.25rem; }
  .rb__cta { margin-top: 1.25rem; }


  .about__title { font-size: clamp(1.9rem, 1.2rem + 3.5vw, 2.4rem); }
}

/* Rebuilds on phones: a card, not the desktop chapter. The rebuild first,
   labelled on the image; their 2019 site as a small strip right under it;
   then the name, the one number, two facts and the button. */
@media (max-width: 47.99rem) {
  .rb { gap: 1.1rem; padding: 2.25rem 0 2.75rem; }
  .rb:first-child { padding-top: 1.75rem; }
  .rb__visual { order: 0; position: relative; padding-bottom: 0; }
  .rb__above { position: absolute; z-index: 2; top: 0.7rem; left: 0.7rem; margin: 0; }
  .rb__above a { display: none; }
  .rb__chip { padding: 0.32rem 0.6rem 0.32rem 0.5rem; font-size: 0.625rem; }
  .rb__frame { border-radius: 10px; }

  .rb__old,
  .rb--flip .rb__old {
    position: static; display: grid; grid-template-columns: 4.5rem 1fr; align-items: center; gap: 0.75rem;
    width: 100%; max-width: none; margin: 0.6rem 0 0; padding: 0;
    background: none; box-shadow: none; transform: none; cursor: auto;
  }
  .rb__old-img { border-radius: 4px; box-shadow: 0 0 0 1px var(--line); }
  .rb__old figcaption { margin: 0; justify-content: flex-start; gap: 0.4rem; font-size: 0.6875rem; }
  .rb__old figcaption span + span::before { content: "\00B7"; margin-right: 0.4rem; }

  .rb__sub { display: none; }
  .rb__title { font-size: 1.75rem; }
  .rb__big { margin-top: 0.9rem; display: flex; align-items: baseline; gap: 0.75rem; flex-wrap: wrap; }
  .rb__num { font-size: 3.4rem; }
  .rb__cap { margin: 0; font-size: 0.9375rem; max-width: 14ch; }
  .rb__facts { margin-top: 1rem; }
  .rb__facts b { font-size: 1.35rem; }
  .rb__facts > div > span { font-size: 0.75rem; }
  .rb__cta { margin-top: 1.1rem; }
  .rb__cta .btn { width: 100%; justify-content: center; }
}
