/* ==========================================================================
   Fonts
   The design uses Korataki (Typodermic). It is a commercial font, so it is
   not bundled. If it is installed locally it is picked up via local();
   to ship it, buy a webfont licence, drop the files in assets/fonts/ and add
   them to the src lists below. Until then the stacks fall back to Anybody
   (Extra Bold) and Michroma (Book), normalised to Korataki's cap height by
   font-size-adjust.
   ========================================================================== */
@font-face {
  font-family: "Korataki";
  src: local("Korataki Extra Bold"), local("Korataki-ExtraBold");
  font-weight: 800 900;
  font-display: swap;
}
@font-face {
  font-family: "Korataki";
  src: local("Korataki Book"), local("Korataki-Book");
  font-weight: 300 400;
  font-display: swap;
}

/* ==========================================================================
   Tokens
   ========================================================================== */
:root {
  --ink: #111;
  --paper: #fff;
  --muted: #787878;
  --line: #e1e1e1;
  --line-dark: rgb(255 255 255 / 0.2);
  --fill-dark: rgb(255 255 255 / 0.1);

  --font-mono: "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
  --font-display: "Korataki", "Anybody", "Arial Black", sans-serif;
  --font-wide: "Korataki", "Michroma", sans-serif;
  --font-sans: "Archivo", "Helvetica Neue", Arial, sans-serif;

  --pad: 60px;
  --fs-display: 8.3333vw; /* 160px @ 1920 */
  --fs-hero: 7.2917vw;    /* 140px @ 1920 */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

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

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

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-mono);
  font-weight: 400;
  text-transform: uppercase;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

p,
h1,
h2,
ul,
ol,
dl,
dd,
figure {
  margin: 0;
}

:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Trim leading so boxes hug the capitals, as in the design. */
.trim {
  text-box: trim-both cap alphabetic;
}

.display,
.display > span {
  text-box: trim-both cap alphabetic;
}

.display {
  font-family: var(--font-display);
  font-weight: 900;
  font-stretch: 150%;
  font-size-adjust: cap-height 0.8;
  font-synthesis: none;
  line-height: 1;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Display-title sweep (js/main.js): the real text stays hidden until its
   WebGL canvas has played, then the canvas is removed and the text shown. */
.gl-reveal [data-reveal]:not(.is-revealed) {
  -webkit-text-fill-color: transparent;
}

.gl-canvas {
  position: absolute;
  pointer-events: none;
}

/* Small-text type-in (js/main.js): characters appear left to right behind a
   few inverted blocks showing random symbols. --type-bg is the colour under
   the text, used for the symbol inside each block. */
.type-reveal [data-scramble]:not(.is-split) {
  visibility: hidden;
}

.type-word {
  white-space: nowrap;
}

.type-char {
  position: relative;
}

.type-char.is-off {
  visibility: hidden;
}

.type-char.is-block,
.type-char.is-ghost {
  -webkit-text-fill-color: transparent;
}

.type-char.is-block::after,
.type-char.is-ghost::after {
  content: attr(data-glyph);
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: currentColor;
  -webkit-text-fill-color: var(--type-bg, var(--paper));
  line-height: 1;
}

.type-char.is-ghost::after {
  opacity: 0.35;
}

.dark {
  --type-bg: var(--ink);

  background: var(--ink);
  color: var(--paper);
}

/* Three-column grid shared by nav, hero and about.
   Column starts land on the design's 60px / 33.33%+23px / 66.67%-15px lines. */
.grid3 {
  display: grid;
  grid-template-columns: calc(33.333% + 3px) calc(33.333% + 2px) minmax(0, 1fr);
  padding-inline: var(--pad);
}

/* 14px "plus" mark with an open centre, drawn in currentColor. */
.plus {
  display: block;
  flex: none;
  width: 14px;
  height: 14px;
  color: var(--muted);
  background:
    linear-gradient(currentColor 0 0) 8px 6px / 6px 2px no-repeat,
    linear-gradient(currentColor 0 0) 6px 0 / 2px 6px no-repeat,
    linear-gradient(currentColor 0 0) 6px 8px / 2px 6px no-repeat,
    linear-gradient(currentColor 0 0) 0 6px / 6px 2px no-repeat;
}

.dark .plus {
  color: var(--paper);
}

.label {
  display: grid;
  grid-template-columns: 151px auto;
  align-items: center;
  font-size: 14px;
  line-height: 1;
  letter-spacing: -0.02em;
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 100px;
  font-size: 14px;
  line-height: 1.2;
  white-space: nowrap;
  transition:
    transform 0.4s var(--ease),
    background-color 0.25s ease,
    color 0.25s ease,
    border-color 0.25s ease;
}

.btn--outline,
.btn--light {
  --type-bg: var(--paper);
}

.btn--outline:hover,
.btn--dark {
  --type-bg: var(--ink);
}

.btn--outline {
  padding: 14px 24px;
  border: 1px solid var(--line);
  border-radius: 48px;
  background: var(--paper);
  font-size: 16px;
  letter-spacing: -0.02em;
}

.btn--outline:hover {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--paper);
}

.btn--dark {
  min-width: 122px;
  height: 51px;
  padding-inline: 19px;
  background: var(--ink);
  color: var(--paper);
  box-shadow:
    -9.5px 10.5px 1.8px rgb(0 0 0 / 0.03),
    -11.8px 13px 2.4px rgb(0 0 0 / 0.03),
    -14.7px 16.2px 3.2px rgb(0 0 0 / 0.03),
    -18.3px 20.2px 4.4px rgb(0 0 0 / 0.04),
    -23px 25.4px 6px rgb(0 0 0 / 0.04),
    -29.3px 32.3px 8.2px rgb(0 0 0 / 0.04),
    -37.8px 41.8px 11.3px rgb(0 0 0 / 0.04),
    -49.9px 55.1px 15.9px rgb(0 0 0 / 0.04);
}

.btn--light {
  min-width: 131px;
  height: 51px;
  padding-inline: 20px;
  background: var(--paper);
  color: var(--ink);
}

.btn--dark:hover,
.btn--light:hover {
  transform: translateY(-2px);
}

/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: max(100svh, 720px);
  overflow: clip;
  background: var(--paper);
}

/* Nav: fixed to the top of the screen. Its header keeps the room it used
   to take in the flow (--nav-h, measured in js/main.js). It has no fill,
   so its text is drawn by a copy laid over it (.nav--ink, also js/main.js)
   that is blended as a whole: Chrome skips the blend for text blended on
   its own over the pinned Work stage. Over a .dark section the button's
   outline turns light (.nav--dark). */
header:has(> .nav) {
  padding-top: var(--nav-h, 97px);
}

.nav {
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  z-index: 100;
  align-items: center;
  padding-block: 24px;
  border-right: 1px solid var(--line);
  font-size: 16px;
  letter-spacing: -0.02em;
  transition: border-color 0.3s ease;
}

/* The nav keeps the avatar, dot and button outline; its text is left to
   the copy. The button only fills on hover. */
.nav.has-ink :is(.nav__name, .status > span, .nav__links, .btn) {
  --type-bg: transparent;

  color: transparent;
}

.nav .btn--outline:not(:hover) {
  background: transparent;
}

/* The copy: white text blended by difference, so it reads dark on light
   and light on dark, photos included. */
.nav--ink {
  --type-bg: var(--ink);

  z-index: 101;
  border-color: transparent;
  color: var(--paper);
  mix-blend-mode: difference;
  pointer-events: none;
}

.nav--ink :is(.nav__avatar, .status > img) {
  visibility: hidden;
}

.nav--ink .btn {
  --type-bg: var(--ink);

  border-color: transparent;
}

.nav--dark {
  border-color: var(--line-dark);
}

.nav--dark .btn--outline {
  border-color: var(--line-dark);
}

.nav--dark .btn--outline:hover {
  border-color: var(--paper);
  background: var(--paper);
}

.nav__identity {
  display: flex;
  align-items: center;
  gap: 12px;
  width: fit-content;
}

.nav__avatar {
  display: flex;
  width: 44px;
  height: 44px;
  border-radius: 4px;
  background: #ddd;
}

.nav__avatar img {
  width: 100%;
  height: 100%;
  border-radius: 8px;
  object-fit: cover;
}

.nav__name,
.status {
  line-height: 1;
}

.status {
  display: flex;
  align-items: center;
  gap: 4px;
}

.nav__end {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.nav__links {
  display: flex;
  gap: 24px;
  padding: 0;
  list-style: none;
  line-height: 1;
  white-space: nowrap;
}

.nav__links a {
  display: flex;
  align-items: center;
  gap: 4px;
  transition: opacity 0.2s ease;
}

/* On the blended copy the hover is mirrored from the real link (js/main.js). */
.nav__links a:hover,
.nav--ink .nav__links a.is-hover {
  opacity: 0.5;
}

.nav__dash {
  font-size: 14px;
}

/* Intro (right column) */
.hero__intro {
  margin-top: 144px;
}

.hero__pitch {
  grid-column: 3;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 80px;
  width: 251px;
}

.hero__pitch-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
}

.hero__summary {
  font-size: 16px;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

/* Bottom row: headline + selected work */
.hero__bottom {
  align-items: end;
  margin-top: auto;
  padding-top: 40px;
  padding-bottom: 60px;
}

.hero__headline {
  grid-column: 1 / 3;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.hero__headline > .plus {
  margin-bottom: 25px;
}

.hero__lead {
  margin-bottom: 33px;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.2;
  letter-spacing: -0.02em;
  white-space: nowrap;
}

.hero__title {
  font-size: var(--fs-hero);
}

/* Each line is a span so js/main.js can align its first letter. */

.hero__work {
  grid-column: 3;
  display: flex;
  flex-direction: column;
  gap: 27px;
}

.hero__thumbs {
  display: flex;
  gap: 8px;
  max-width: 594px;
  padding: 0;
  list-style: none;
}

.hero__thumbs li {
  flex: 1 0 0;
  min-width: 0;
}

.hero__thumb {
  aspect-ratio: 142.5 / 106;
  overflow: hidden;
  border-radius: 4px;
  clip-path: inset(0 0 0 0 round 4px);
  /* Ease-out, so the lift starts the moment the pointer arrives. */
  transition: transform 0.35s var(--ease);
}

/* Hover (display only, no link): the card rises by 12% of its height. The
   <li> takes the hover so the pointer stays over it while the card lifts. */
.hero__thumbs li:hover .hero__thumb {
  transform: translateY(-12%);
}

.hero__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ==========================================================================
   Work
   Storyboard while the stage is pinned:
   1. "WORK" arrives whole and centred, and holds for --work-hold.
   2. "WO" / "RK" slide apart over --work-split; the first image peeks in.
   3. The first image rises to the centre (--work-rise) while the index and
      caption fade in, then the gallery scrolls project by project.
   ========================================================================== */
/* Registered so js/main.js can read them back in px for the fallback. */
@property --work-hold {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

@property --work-split {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

.work {
  --work-w: clamp(260px, 35.417vw, 1200px); /* 680px @ 1920 */
  --work-h: calc(var(--work-w) * 440 / 680);
  --work-col: calc(33.333% + 23px);
  --work-hold: 10svh;
  --work-split: 60svh;
  --work-peek: 91.333svh; /* first image top once the split ends: 906px @ 992 */
  --work-rise: calc(var(--work-peek) - 50svh + var(--work-h) / 2);
  --work-more-h: 50px;
  --work-more-gap: 40px; /* last project's bottom edge to the button, as in the design */

  position: relative;
  margin-top: 200px;
  background: var(--paper);
}

@media (prefers-reduced-motion: reduce) {
  .work {
    --work-hold: 0px;
    --work-split: 0px;
    --work-peek: calc(50svh - var(--work-h) / 2);
  }
}

.work__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
}

.work__title {
  position: absolute;
  inset: 0;
  /* 160px @ 1920; shrinks on narrower screens so "WO" clears the project index. */
  font-size: min(var(--fs-display), (33.333vw - 155px) / 3.03);
  pointer-events: none;
}

.work__title > span {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
}

/* "WO" ends just left of the image column, "RK" starts just right of it. */
.work__title > span:first-child {
  right: calc(66.667% - 5px);
}

.work__title > span:last-child {
  left: calc(var(--work-col) + var(--work-w) + 20px);
}

.work__index {
  position: absolute;
  top: 50%;
  left: var(--pad);
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 0;
  list-style: none;
  translate: 0 calc(-50% + 0.5px);
}

.work__index a {
  position: relative;
  display: block;
  width: 40px;
  height: 1px;
  border-radius: 20px;
  background: var(--line);
  transition:
    width 0.45s var(--ease),
    height 0.45s var(--ease),
    border-radius 0.45s var(--ease),
    background-color 0.3s ease,
    box-shadow 0.3s ease;
}

/* Bigger hit area for the 1px markers. */
.work__index a::after {
  content: "";
  position: absolute;
  inset: -4px 0;
}

.work__index a[data-dist="1"] {
  width: 57px;
}

.work__index a[data-dist="2"] {
  width: 48px;
}

.work__index a[data-dist="0"] {
  width: 80px;
  height: 50px;
  border-radius: 4px;
  background: var(--paper);
  box-shadow: inset 0 0 0 1px var(--ink);
}

.work__index a:not([data-dist="0"]):hover {
  background: var(--muted);
}

.work__meta {
  position: absolute;
  top: 50%;
  right: var(--pad);
  left: calc(var(--work-col) + var(--work-w) + 75px);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  max-width: 392px;
  height: var(--work-h);
  translate: 0 -50%;
}

.work__meta-top {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 30px;
}

.work__name {
  font-size: 16px;
  line-height: 1.4;
  letter-spacing: -0.02em;
}

.work__year {
  font-size: 16px;
  line-height: 1;
}

.work__list {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: var(--work-w);
  margin-top: -100svh;
  margin-left: var(--work-col);
  /* The pin releases on the design's last frame: the last project
     --work-more-gap above the centred button. From there they leave together. */
  padding-block:
    calc(var(--work-hold) + var(--work-split) + var(--work-peek))
    calc(50svh + var(--work-more-h) / 2 + var(--work-more-gap));
  /* The list's empty padding overlies the stage; only the projects take clicks. */
  pointer-events: none;
}

/* "view more work": fixed at the centre of the stage, in the image column
   between "WO" and "RK". Hidden while the projects pass over that spot, so
   it never shows through the gaps between them; js/main.js turns it on
   under the last one, which then uncovers it as it slides up. */
.work__more {
  position: absolute;
  top: 50%;
  left: calc(var(--work-col) + var(--work-w) / 2);
  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--work-more-h);
  translate: -50% -50%;
  pointer-events: auto;
  visibility: hidden;
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity 0.6s var(--ease),
    transform 0.8s var(--ease),
    visibility 0s 0.6s;
}

.work__more.is-shown {
  visibility: visible;
  opacity: 1;
  transform: none;
  transition:
    opacity 0.6s var(--ease),
    transform 0.8s var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  .work__more {
    transform: none;
  }
}

.work__item {
  position: relative;
  display: flex;
  pointer-events: auto;
  align-items: center;
  height: var(--work-h);
  overflow: clip;
  border-radius: 8px;
  scroll-margin-block: calc(50svh - var(--work-h) / 2);
}

.work__media {
  flex: none;
  width: 100%;
  height: calc(var(--work-h) * 510 / 440);
  overflow: hidden;
  border-radius: 12px;
}

.work__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: scale 0.8s var(--ease);
}

.work__item:hover .work__media img {
  scale: 1.03;
}

/* Covers the project; opens it in the project modal (js/main.js). */
.work__link {
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.work__link:focus-visible {
  outline-offset: -4px;
}

/* Decorative parallax: the taller image drifts inside its clipped frame. */
@keyframes work-parallax {
  from {
    translate: 0 calc(var(--work-h) * -35 / 440);
  }
  to {
    translate: 0 calc(var(--work-h) * 35 / 440);
  }
}

/* --wo-dx / --rk-dx / --work-dy are measured in js/main.js: the offsets
   that put "WO" and "RK" side by side as one word, centred on the stage. */
@keyframes work-split-wo {
  from {
    transform: translate(var(--wo-dx), var(--work-dy));
  }
}

@keyframes work-split-rk {
  from {
    transform: translate(var(--rk-dx), var(--work-dy));
  }
}

@keyframes reveal-up {
  from {
    visibility: hidden;
    opacity: 0;
    transform: translateY(24px);
  }
}

@supports ((animation-timeline: view()) and (animation-range: entry)) {
  @media (prefers-reduced-motion: no-preference) {
    .work {
      view-timeline: --work block;
    }

    .work__title > span:first-child {
      animation: work-split-wo cubic-bezier(0.65, 0, 0.35, 1) both;
      animation-timeline: --work;
      animation-range: contain var(--work-hold) contain calc(var(--work-hold) + var(--work-split));
    }

    .work__title > span:last-child {
      animation: work-split-rk cubic-bezier(0.65, 0, 0.35, 1) both;
      animation-timeline: --work;
      animation-range: contain var(--work-hold) contain calc(var(--work-hold) + var(--work-split));
    }

    /* Fade the caption's children, not the caption, so its mobile band stays opaque. */
    .work__index,
    .work__meta > * {
      animation: reveal-up linear both;
      animation-timeline: --work;
      animation-range:
        contain calc(var(--work-hold) + var(--work-split) + var(--work-rise) * 0.4)
        contain calc(var(--work-hold) + var(--work-split) + var(--work-rise));
    }

    .work__item {
      view-timeline: --work-item;
    }

    .work__media {
      animation: work-parallax linear both;
      animation-timeline: --work-item;
    }
  }
}

/* ==========================================================================
   About
   ========================================================================== */
/* With the rising edge (js/main.js), Work's stage stays pinned for an
   extra screen (--work-curtain, added under the projects) while the
   projects scroll on up and away, and About, tucked under that screen,
   rises over the stage like a curtain; by the time the stage lets go,
   About covers it. About paints above Work (the list and the mobile
   bands are stacked) and its clipped-off top lets Work show and take
   clicks. */
.work.has-curtain {
  --work-curtain: 100svh;
}

.work.has-curtain .work__list {
  padding-bottom: calc(50svh + var(--work-more-h) / 2 + var(--work-more-gap) + var(--work-curtain));
}

.about.is-rising {
  position: relative;
  z-index: 3;
  margin-top: -100svh; /* --work-curtain */
}

.about {
  padding-block: 161px 368px;
}

.about__title {
  font-size: var(--fs-display);
}

/* Shrink-wrap each half so js/main.js can measure the word "ABOUT". */
.about__title > span {
  justify-self: start;
}

.about__body {
  align-items: start;
  margin-top: 112px;
}

.about__body > * {
  grid-row: 1;
}

.about__content {
  grid-column: 2 / 4;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 48px;
  max-width: min(753px, calc(100% - 273px));
  min-height: 423px;
  font-size: 18px;
  letter-spacing: -0.02em;
}

.about__name {
  line-height: 1;
}

.about__bio {
  display: flex;
  flex-direction: column;
  gap: 16px;
  line-height: 1.2;
}

.about__facts {
  display: flex;
  flex-direction: column;
  gap: 4px;
  line-height: 1.2;
}

.about__facts div {
  display: flex;
}

.about__facts dt {
  flex: none;
  width: 156px;
  opacity: 0.5;
}

.about__photo {
  grid-column: 3;
  justify-self: end;
  width: 233px;
  height: 271px;
  border-radius: 8px;
  object-fit: cover;
}

/* Storyboard while the stage is pinned:
   1. "ABOUT" sits whole, vertically centred, and holds for --about-hold.
   2. Over --about-split it rises to the top as "A" / "BOUT" split apart.
   3. Over --about-reveal the label, name and bio fade in and the photo
      wipes open top-down, then the finished layout rests for --about-rest.
   Only where scroll-driven animations exist and the layout fits the
   viewport; everywhere else About stays the static layout above.
   --about-dy / --bout-dx are measured in js/main.js. */
@keyframes about-rise {
  from {
    transform: translateY(var(--about-dy));
  }
}

@keyframes about-split {
  from {
    transform: translateX(var(--bout-dx));
  }
}

@keyframes about-photo {
  from {
    clip-path: inset(0 0 100% 0 round 8px);
  }
  to {
    clip-path: inset(0 0 0 0 round 8px);
  }
}

@supports ((animation-timeline: view()) and (animation-range: entry)) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 901px) and (min-height: 760px) {
    .about {
      --about-hold: 10svh;
      --about-split: 60svh;
      --about-reveal: 60svh;
      --about-rest: 20svh;
      --about-pin: calc(var(--about-hold) + var(--about-split) + var(--about-reveal) + var(--about-rest));
      /* Pinned stage + scroll distance; Expertise tucks in right after. */
      height: calc(100svh + var(--about-pin));
      padding-block: 0;
      view-timeline: --about block;
    }

    .about__stage {
      position: sticky;
      top: 0;
      height: 100svh;
      padding-top: min(161px, 16.23svh);
    }

    .about__body {
      margin-top: min(112px, 11.29svh);
    }

    .about__title,
    .about__title > span:last-child {
      animation: about-rise cubic-bezier(0.65, 0, 0.35, 1) both;
      animation-timeline: --about;
      animation-range: contain var(--about-hold) contain calc(var(--about-hold) + var(--about-split));
    }

    .about__title > span:last-child {
      animation-name: about-split;
    }

    /* Each piece fades in over its own slice of the reveal. */
    .about__body .label,
    .about__name,
    .about__bio p,
    .about__facts {
      animation: reveal-up linear both;
      animation-timeline: --about;
      animation-range:
        contain calc(var(--about-hold) + var(--about-split) * var(--from-split, 1) + var(--about-reveal) * var(--from, 0))
        contain calc(var(--about-hold) + var(--about-split) + var(--about-reveal) * var(--to));
    }

    .about__body .label,
    .about__name {
      --from-split: 0.6;
      --to: 0.2;
    }

    .about__bio p:first-child {
      --from-split: 0.7;
      --to: 0.3;
    }

    .about__bio p:last-child {
      --from: 0.3;
      --to: 0.7;
    }

    .about__facts {
      --from: 0.5;
      --to: 1;
    }

    .about__photo {
      animation: about-photo linear both;
      animation-timeline: --about;
      animation-range:
        contain calc(var(--about-hold) + var(--about-split) * 0.6)
        contain calc(var(--about-hold) + var(--about-split) + var(--about-reveal) * 0.8);
    }
  }
}

/* ==========================================================================
   Expertise
   ========================================================================== */
.expertise {
  padding: 0 var(--pad) 282px;
}

.expertise__stage {
  padding-top: 161px;
}

.expertise__title {
  font-size: var(--fs-display);
}

.expertise__body {
  display: grid;
  grid-template-columns: minmax(0, 1047px) minmax(220px, 292px);
  justify-content: space-between;
  align-items: start;
  column-gap: 40px;
  margin-top: 81px;
}

.services .label {
  margin-bottom: 40px;
}

.services__list {
  display: flex;
  flex-direction: column;
}

.service {
  display: grid;
  grid-template-columns: 110px minmax(0, 1fr) 14px;
  align-items: center;
  column-gap: 40px;
  width: 100%;
  padding: 32px 0;
  border: 0;
  border-bottom: 1px solid var(--line-dark);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  text-transform: inherit;
  cursor: pointer;
  transition:
    background-color 0.35s ease,
    padding 0.45s var(--ease),
    grid-template-columns 0.45s var(--ease);
}

.service:first-child {
  border-top: 1px solid var(--line-dark);
}

.service[aria-selected="true"] {
  grid-template-columns: 87px minmax(0, 1fr) 14px;
  padding-inline: 24px;
  background: var(--fill-dark);
}

.service:focus-visible {
  outline-offset: -2px;
}

.service__num {
  font-size: 14px;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

.service__name {
  font-family: var(--font-wide);
  font-size: 32px;
  font-size-adjust: cap-height 0.8;
  font-synthesis: none;
  line-height: 1;
  transition: opacity 0.3s ease;
}

.service[aria-selected="false"]:not(:hover) .service__name {
  opacity: 0.9;
}

.services__panels {
  display: grid;
  margin-top: 54px;
}

.service-panel {
  grid-area: 1 / 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 26px;
  visibility: hidden;
  opacity: 0;
  transition:
    opacity 0.35s ease,
    visibility 0.35s;
}

.service-panel.is-active {
  visibility: visible;
  opacity: 1;
}

/* The outgoing description goes at once; the new one types in over the image crossfade. */
.service-panel:not(.is-active) p {
  visibility: hidden;
}

.service-panel img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  border-radius: 12px;
  object-fit: cover;
}

.service-panel p {
  max-width: 216px;
  font-size: 12px;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

/* Storyboard while the stage is pinned, like About:
   1. "EXPERTISE" sits whole, vertically centred, and holds for --expertise-hold.
   2. Over --expertise-rise it rises to the top.
   3. Over --expertise-reveal the label and the services fade in row by
      row while the preview wipes open top-down, then the finished layout
      rests for --expertise-rest.
   Vertical spacing tightens with the viewport height; js/main.js measures
   --expertise-dy and adds .no-pin where the finished layout still would
   not fit, leaving the static layout above. */
@keyframes expertise-rise {
  from {
    transform: translateY(var(--expertise-dy));
  }
}

@keyframes expertise-photo {
  from {
    clip-path: inset(0 0 100% 0 round 12px);
  }
  to {
    clip-path: inset(0 0 0 0 round 12px);
  }
}

@supports ((animation-timeline: view()) and (animation-range: entry)) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 901px) and (min-height: 760px) {
    .expertise:not(.no-pin) {
      --expertise-hold: 10svh;
      --expertise-rise: 60svh;
      --expertise-reveal: 60svh;
      --expertise-rest: 20svh;
      --expertise-pin: calc(var(--expertise-hold) + var(--expertise-rise) + var(--expertise-reveal) + var(--expertise-rest));
      --expertise-overlap: 30svh;

      position: relative;
      z-index: 4; /* above About, which stacks at 3 */
      /* Pinned stage + scroll distance; the footer follows right after, as
         in the design, where the 992px Expertise frame meets it directly. */
      height: calc(100svh + var(--expertise-pin));
      padding-bottom: 0;
      /* Starts under the end of About; that strip is see-through, so About's
         last lines scroll away behind the incoming title instead of leaving
         an empty screen before it. */
      margin-top: calc(-1 * var(--expertise-overlap));
      background: linear-gradient(transparent var(--expertise-overlap), var(--ink) 0);
      view-timeline: --expertise block;
    }

    .expertise:not(.no-pin) .expertise__stage {
      position: sticky;
      top: 0;
      height: 100svh;
      padding-top: min(161px, 16.23svh);
    }

    .expertise:not(.no-pin) .expertise__body {
      margin-top: min(81px, 8.17svh);
    }

    .expertise:not(.no-pin) .services .label {
      margin-bottom: min(40px, 4.03svh);
    }

    .expertise:not(.no-pin) .service {
      padding-block: min(32px, 3.23svh);
    }

    .expertise:not(.no-pin) .services__panels {
      margin-top: calc(14px + min(40px, 4.03svh)); /* level with the first row */
    }

    .expertise:not(.no-pin) .expertise__title {
      animation: expertise-rise cubic-bezier(0.65, 0, 0.35, 1) both;
      animation-timeline: --expertise;
      animation-range: contain var(--expertise-hold) contain calc(var(--expertise-hold) + var(--expertise-rise));
    }

    /* Each piece fades in over its own slice of the reveal. */
    .expertise:not(.no-pin) :is(.services .label, .service, .services__panels) {
      animation: reveal-up linear both;
      animation-timeline: --expertise;
      animation-range:
        contain calc(var(--expertise-hold) + var(--expertise-rise) * var(--from-rise, 1) + var(--expertise-reveal) * var(--from, 0))
        contain calc(var(--expertise-hold) + var(--expertise-rise) + var(--expertise-reveal) * var(--to));
    }

    .expertise:not(.no-pin) .services .label {
      --from-rise: 0.6;
      --to: 0.2;
    }

    .expertise:not(.no-pin) .service:nth-child(1) { --from-rise: 0.8; --to: 0.3; }
    .expertise:not(.no-pin) .service:nth-child(2) { --from: 0.1; --to: 0.4; }
    .expertise:not(.no-pin) .service:nth-child(3) { --from: 0.2; --to: 0.5; }
    .expertise:not(.no-pin) .service:nth-child(4) { --from: 0.3; --to: 0.6; }
    .expertise:not(.no-pin) .service:nth-child(5) { --from: 0.4; --to: 0.7; }

    .expertise:not(.no-pin) .services__panels {
      --from: 0.3;
      --to: 0.6;
    }

    .expertise:not(.no-pin) .service-panel img {
      animation: expertise-photo linear both;
      animation-timeline: --expertise;
      animation-range:
        contain calc(var(--expertise-hold) + var(--expertise-rise) + var(--expertise-reveal) * 0.3)
        contain calc(var(--expertise-hold) + var(--expertise-rise) + var(--expertise-reveal) * 0.9);
    }
  }
}

/* ==========================================================================
   Contact
   ========================================================================== */
.contact {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  min-height: max(100svh, 720px);
}

.contact > * {
  grid-area: 1 / 1;
}

.contact__bottom {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-self: end;
  gap: 40px;
  padding: 40px;
}

.socials {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
  max-width: 100%;
}

.socials--end {
  align-items: flex-end;
}

.socials__label {
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: normal;
  opacity: 0.5;
}

.socials__list {
  display: flex;
  gap: 40px;
  padding: 0;
  list-style: none;
}

.social {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  height: 24px;
}

.social > .plus {
  margin-top: 5px;
  opacity: 0.3;
  transition: opacity 0.25s ease;
}

.social__text {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: var(--w);
}

.social__abbr {
  font-family: var(--font-wide);
  font-size: 24px;
  font-size-adjust: cap-height 0.8;
  font-synthesis: none;
  line-height: 1;
  white-space: nowrap;
}

.social__full {
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: normal;
  white-space: nowrap;
  opacity: 0;
  translate: 0 -4px;
  transition:
    opacity 0.25s ease,
    translate 0.35s var(--ease);
}

.social:hover > .plus,
.social:focus-visible > .plus {
  opacity: 1;
}

.social:hover .social__full,
.social:focus-visible .social__full {
  opacity: 1;
  translate: 0 0;
}

.contact__cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  align-self: center;
  justify-self: center;
  gap: 0.325em;
  font-size: var(--fs-display);
}

/* While js/main.js folds the title in WebGL, the text itself stays put
   but unseen, so it keeps its layout and stays readable to assistive tech. */
.contact__cta.is-rippling {
  -webkit-text-fill-color: transparent;
}

/* ==========================================================================
   Our Work page (work.html)
   Two views of every project, switched by the toggle beside the title:
   - slider: one screen tall; the cards run in a row that the wheel, a
     mouse drag or the scrubber below moves sideways (js/main.js).
   - grid: two columns down the page, the scrubber pinned on the right.
   ========================================================================== */
.page-work {
  display: flex;
  flex-direction: column;
  min-height: 100svh;
}

.page-work:has(.gallery[data-view="slider"]) {
  height: max(100svh, 640px);
}

.gallery {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
}

.gallery__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  padding: 60px var(--pad) 0;
}

.gallery__title {
  font-size: max(40px, 6.25vw); /* 120px @ 1920 */
}

.view-toggle {
  display: flex;
  gap: 4px;
  padding: 2px;
  border: 1px solid var(--line);
  border-radius: 10px;
}

.view-toggle button {
  display: flex;
  padding: 10px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: #292d32;
  cursor: pointer;
  transition:
    background-color 0.3s ease,
    color 0.3s ease;
}

.view-toggle button[aria-pressed="true"] {
  background: var(--ink);
  color: var(--paper);
}

/* The icon files are used as masks so they take the button's colour. */
.view-toggle__icon {
  width: 16px;
  height: 16px;
  background: currentColor;
  -webkit-mask: center / 16px 16px no-repeat;
  mask: center / 16px 16px no-repeat;
}

.view-toggle__icon--slider {
  -webkit-mask-image: url("../assets/icons/view-slider.svg");
  mask-image: url("../assets/icons/view-slider.svg");
}

.view-toggle__icon--grid {
  -webkit-mask-image: url("../assets/icons/view-grid.svg");
  mask-image: url("../assets/icons/view-grid.svg");
}

/* Slider */
.gallery__track {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: min(57px, 5.75svh) 0 0;
  padding: 0 var(--pad);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  list-style: none;
  cursor: grab;
}

.gallery__track::-webkit-scrollbar {
  display: none;
}

/* Positioned so the cards' offsetLeft is measured from the row (js/main.js). */
.gallery__track {
  position: relative;
}

.gallery__track.is-dragging {
  cursor: grabbing;
}

/* Bend: while the slider moves, the row wraps round a drum that bulges
   toward the viewer. js/main.js sets, per card, where it moves to along
   the drum (--bend-x, --bend-z), its turn (--bend-r), how much it darkens
   (--bend-shade) and the image's drift inside it (--drift, -1 to 1, also
   at rest). The row holds the perspective so every card shares one
   vanishing point, fixed at the middle of the visible row; it scales with
   the screen width, like the drum, so the bend looks the same on any
   screen (1600px @ 1231). The outer
   translates pivot each card on its centre, since the origin stays
   top-left for the view switch. Set for the whole slider view, on layers
   of their own, so a bend never starts with a hitch. */
.gallery[data-view="slider"] .gallery__track {
  perspective: 130vw;
  perspective-origin: 50% 50%;
}

.gallery[data-view="slider"] .card {
  backface-visibility: hidden;
  will-change: transform;
  transform: translate(50%, 50%) translate3d(var(--bend-x, 0px), 0, var(--bend-z, 0px)) rotateY(var(--bend-r, 0deg)) translate(-50%, -50%);
}

.gallery[data-view="slider"] .card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #000;
  opacity: var(--bend-shade, 0);
  pointer-events: none;
}

/* Overscanned so the image can drift against the card's movement. */
.gallery[data-view="slider"] .card img {
  scale: 1.12;
  translate: calc(var(--drift, 0) * -5%) 0;
}

.gallery__track:focus-visible {
  outline-offset: -2px;
}

.card {
  position: relative;
  flex: none;
  height: min(100%, 460px, (100vw - 2 * var(--pad)) * 0.7667);
  aspect-ratio: 600 / 460;
  overflow: hidden;
  border-radius: 12px;
  background: #f2f2f2;
  transform-origin: 0 0; /* view switches scale from the top-left corner */
}

.card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-user-drag: none;
  user-select: none;
  transition: transform 0.8s var(--ease);
}

.card__caption {
  --type-bg: var(--ink);

  position: absolute;
  inset: auto 0 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 12px;
  height: 159px;
  padding: 24px;
  background: linear-gradient(to top, rgb(0 0 0 / 0.5) 4.412%, rgb(0 0 0 / 0));
  color: var(--paper);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
}

.card__caption > * {
  transform: translateY(12px);
  transition: transform 0.6s var(--ease);
}

.card:hover img {
  transform: scale(1.03);
}

.card:hover .card__caption {
  opacity: 1;
}

.card:hover .card__caption > * {
  transform: none;
}

@media (hover: none) {
  .card__caption {
    opacity: 1;
  }

  .card__caption > * {
    transform: none;
  }
}

/* Covers the card; opens the project modal. */
.card__link {
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.card__link:focus-visible {
  outline-offset: -4px;
}

.card:has(.card__link:focus-visible) .card__caption {
  opacity: 1;
}

.card:has(.card__link:focus-visible) .card__caption > * {
  transform: none;
}

.card__name {
  font-family: var(--font-wide);
  font-size: 32px;
  font-size-adjust: cap-height 0.8;
  font-synthesis: none;
  line-height: 1;
}

.card__type {
  font-size: 16px;
  line-height: 1.4;
  letter-spacing: -0.02em;
}

/* Scrubber: one tick per project and a thumb that follows the progress;
   the ticks it passes step to its other side. js/main.js sets --ticks,
   each tick's --i, the thumb offset --t (px) and --k, the number of
   ticks before the thumb. */
.scrubber {
  --k: 0;
  --t: 0;

  position: relative;
  justify-self: center;
  width: calc(var(--ticks, 13) * 9px + 80px);
  height: 50px;
  margin: min(120px, 12.1svh) 0 min(52px, 5.24svh);
  cursor: pointer;
  touch-action: none;
}

.scrubber__tick {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 1px;
  height: 40px;
  border-radius: 20px;
  background: var(--line);
  transform: translateX(calc(var(--i) * 9px + 88px * clamp(0, var(--i) - var(--k) + 1, 1)));
  transition: transform 0.45s var(--ease);
}

.scrubber__thumb {
  position: absolute;
  top: 0;
  left: 0;
  width: 80px;
  height: 50px;
  border: 1px solid var(--ink);
  border-radius: 4px;
  background: var(--paper);
  transform: translateX(calc(var(--t) * 1px));
}

.scrubber.is-switching .scrubber__tick {
  transition: none;
}

/* Grid */
.gallery[data-view="grid"] {
  grid-template-columns: minmax(0, 1212px) 80px;
  grid-template-rows: auto auto;
  justify-content: space-between;
  column-gap: 40px;
  padding: 0 var(--pad) var(--pad);
}

.gallery[data-view="grid"] .gallery__head {
  grid-column: 1 / -1;
  padding-inline: 0;
}

.gallery[data-view="grid"] .gallery__track {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  margin-top: 60px;
  padding: 0;
  overflow: visible;
  cursor: auto;
}

.gallery[data-view="grid"] .card {
  width: 100%;
  height: auto;
}

.gallery[data-view="grid"] .scrubber {
  position: sticky;
  top: calc(217px + max(32px, 5vw)); /* its resting spot, level with the first cards (313px @ 1920) */
  align-self: start;
  width: 80px;
  height: calc(var(--ticks, 13) * 9px + 50px);
  margin: 60px 0 0;
}

.gallery[data-view="grid"] .scrubber__tick {
  top: 0;
  right: 0;
  bottom: auto;
  left: auto;
  width: 40px;
  height: 1px;
  transform: translateY(calc(var(--i) * 9px + 58px * clamp(0, var(--i) - var(--k) + 1, 1)));
}

.gallery[data-view="grid"] .scrubber__thumb {
  transform: translateY(calc(var(--t) * 1px));
}

@media (max-width: 900px) {
  .gallery__track {
    margin-top: 40px;
  }

  .scrubber {
    margin-block: 40px 32px;
  }

  .card__name {
    font-size: 24px;
  }

  .gallery[data-view="grid"] {
    grid-template-columns: minmax(0, 1fr);
  }

  .gallery[data-view="grid"] .gallery__track {
    grid-template-columns: minmax(0, 1fr);
    margin-top: 40px;
  }

  .gallery[data-view="grid"] .scrubber {
    display: none;
  }
}

/* Project modal: the info panel stays put on the left while the dialog
   scrolls the images on the right. */
html.has-modal {
  overflow: hidden;
}

.project {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 20px var(--pad);
  border: 0;
  background: transparent;
  color: var(--ink);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
}

.project::-webkit-scrollbar {
  display: none;
}

.project[open] {
  display: grid;
  grid-template-columns: 444px minmax(0, 1fr);
  align-items: start;
  column-gap: 8px;
}

.project::backdrop {
  background: rgb(17 17 17 / 0.4);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.project__info {
  position: sticky;
  top: 0; /* measured inside the dialog's 20px padding */
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 40px;
  height: calc(100svh - 40px);
  padding: 24px;
  overflow-y: auto;
  border-radius: 8px;
  background: var(--paper);
  scrollbar-width: none;
}

.project__intro {
  display: flex;
  flex-direction: column;
  gap: 40px;
}

.project__heading {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.project__bar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  color: var(--muted);
}

.project__close {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transition: opacity 0.2s ease;
}

.project__close:hover {
  opacity: 0.5;
}

.project__close img {
  display: block;
}

.project__title {
  font-family: var(--font-wide);
  font-size: 32px;
  font-size-adjust: cap-height 0.8;
  font-synthesis: none;
  font-weight: 400;
  line-height: 1;
}

.project__desc {
  font-size: 18px;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

.project__desc:empty {
  display: none;
}

.project__end {
  display: flex;
  flex-direction: column;
  gap: 60px;
}

/* "Create a design like this": full width under the specs. */
.project__cta {
  width: 100%;
  height: 49px;
  font-size: 16px;
  letter-spacing: -0.02em;
}

.project__specs {
  margin: 0;
  color: #000;
  font-size: 16px;
  line-height: normal;
}

.project__specs div {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding-block: 11px;
  border-bottom: 1px solid #dadada;
}

.project__specs dd {
  margin: 0;
  font-weight: 600;
  text-align: right;
}

.project__gallery {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.project__gallery img {
  width: 100%;
  height: calc(100svh - 40px);
  border-radius: 12px;
  background: #f2f2f2;
  object-fit: cover;
}

/* Full-page captures keep their own height. */
.project__gallery img.is-page {
  height: auto;
}

@media (max-width: 900px) {
  .project {
    padding: 12px;
  }

  .project[open] {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 8px;
  }

  .project__info {
    position: static;
    height: auto;
    overflow: visible;
  }

  .project__gallery img {
    height: auto;
    aspect-ratio: 4 / 3;
  }

  .project__gallery img.is-page {
    aspect-ratio: auto;
  }
}

/* ==========================================================================
   Cursor (js/main.js), after bykins.com: the pointer becomes a ✦ that
   inverts whatever it passes over and sheds ⊹ sparks while it moves.
   Mouse and pen only.
   ========================================================================== */
.has-cursor,
.has-cursor * {
  cursor: none !important;
}

.cursor,
.cursor-spark {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10000;
  color: #fff;
  font-family: system-ui, sans-serif;
  line-height: 1;
  mix-blend-mode: difference;
  pointer-events: none;
  user-select: none;
}

/* Placed with `translate` (set per move); `transform` carries the
   centring and the eased tilt / grow. */
.cursor {
  width: 20px;
  height: 20px;
  font-size: 20px;
  line-height: 20px;
  text-align: center;
  opacity: 0;
  transform: translate(-50%, -50%) rotate(0deg) scale(1);
  transition:
    transform 0.2s cubic-bezier(0.16, 1, 0.3, 1),
    opacity 0.2s ease;
}

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

.cursor__mark {
  display: block;
  transition:
    opacity 0.2s ease,
    scale 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

/* [data-cursor] swaps the ✦ for a word, centred on the pointer. */
.cursor__label {
  position: absolute;
  top: 50%;
  left: 50%;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1;
  letter-spacing: -0.02em;
  white-space: nowrap;
  opacity: 0;
  translate: -50% -50%;
  transition: opacity 0.2s ease;
}

.cursor.has-label .cursor__mark {
  opacity: 0;
  scale: 0.4;
}

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

.cursor.is-hover {
  transform: translate(-50%, -50%) rotate(-20deg) scale(1.5);
}

.cursor.is-press {
  transform: translate(-50%, -50%) rotate(10deg) scale(1);
}

/* Each spark pops out toward (--x, --y), spinning through --r0/--r1/--r2,
   and shrinks away. */
.cursor-spark {
  z-index: 9999;
  animation: cursor-spark 900ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes cursor-spark {
  0% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(0.4) rotate(var(--r0));
  }
  35% {
    opacity: 1;
    transform: translate(calc(-50% + var(--x) * 0.35), calc(-50% + var(--y) * 0.35)) scale(1.25) rotate(var(--r1));
  }
  100% {
    opacity: 0;
    transform: translate(calc(-50% + var(--x)), calc(-50% + var(--y))) scale(0.15) rotate(var(--r2));
  }
}

/* ==========================================================================
   Tablet / mobile
   ========================================================================== */
@media (max-width: 1280px) and (min-width: 901px) {
  /* Column C gets too narrow for links + button; let them extend left over column B. */
  .nav__end {
    grid-column: 2 / 4;
    grid-row: 1;
    justify-self: end;
    gap: 40px;
  }

  .status {
    grid-column: 2;
    grid-row: 1;
  }

  /* Narrow caption column: keep the project name clear of "RK". */
  .work__meta-top > .plus {
    display: none;
  }

  .work__name {
    font-size: 14px;
  }
}

@media (max-width: 900px) {
  :root {
    --pad: 20px;
  }

  .grid3 {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Nav */
  .nav {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    border-right: 0;
  }

  .nav__end {
    justify-content: flex-end;
  }

  .nav__links {
    display: none;
  }

  /* Hero */
  .hero {
    min-height: 100svh;
  }

  .hero__intro {
    margin-top: 56px;
  }

  .hero__pitch {
    grid-column: 1;
    gap: 32px;
    width: auto;
    max-width: 320px;
  }

  .hero__bottom {
    row-gap: 48px;
    padding-bottom: 32px;
  }

  .hero__headline,
  .hero__work {
    grid-column: 1;
  }

  .hero__lead {
    white-space: normal;
  }

  .hero__title {
    font-size: 12.5vw;
  }

  /* Work */
  .work {
    --work-w: calc(100vw - 2 * var(--pad));
    --work-col: var(--pad);

    margin-top: 96px;
  }

  /* Heading and caption sit on white bands above the scrolling images. */
  .work__stage {
    z-index: 2;
    pointer-events: none;
  }

  .work__title {
    inset: 0 0 auto;
    display: flex;
    justify-content: space-between;
    padding: 24px var(--pad) 20px;
    background: var(--paper);
    font-size: 12vw;
  }

  .work__title > span {
    position: static;
    translate: none;
  }

  .work__index,
  .work__meta-top > .plus {
    display: none;
  }

  .work__meta {
    inset: auto 0 0;
    flex-direction: row;
    align-items: flex-end;
    gap: 16px;
    max-width: none;
    height: auto;
    padding: 16px var(--pad) 24px;
    background: var(--paper);
    translate: none;
  }

  /* About */
  .about {
    padding-block: 96px 160px;
  }

  .about__title {
    display: flex;
    font-size: 13.5vw;
  }

  .about__body {
    row-gap: 40px;
    margin-top: 56px;
  }

  .about__body > * {
    grid-row: auto;
    grid-column: 1;
  }

  .about__content {
    max-width: none;
    min-height: 0;
    font-size: 16px;
  }

  .about__photo {
    justify-self: start;
  }

  /* Expertise */
  .expertise {
    padding-bottom: 160px;
  }

  .expertise__stage {
    padding-top: 96px;
  }

  .expertise__title {
    font-size: 8.8vw;
  }

  .expertise__body {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 48px;
    margin-top: 56px;
  }

  .service {
    grid-template-columns: 40px minmax(0, 1fr) 14px;
    column-gap: 16px;
    padding-block: 24px;
  }

  .service[aria-selected="true"] {
    grid-template-columns: 28px minmax(0, 1fr) 14px;
    padding-inline: 12px;
  }

  .service__name {
    font-size: 20px;
  }

  .services__panels {
    max-width: 320px;
    margin-top: 0;
  }

  /* Contact */
  .contact__bottom {
    flex-direction: column;
    padding: 32px var(--pad);
  }

  .socials--end {
    align-items: flex-start;
  }

  .socials__list {
    flex-wrap: wrap;
    gap: 24px 28px;
  }

  .contact__cta {
    font-size: 13vw;
  }
}

@media (max-width: 560px) {
  .status {
    display: none;
  }
}
