/* ==========================================================================
   Work (work.html)
   Every project. The giant title (as the pricing page's), the line and the
   figures; then a bar that holds under the header with the filters (an ink
   pill slides to the one chosen, a white-on-ink copy of the pills cut to it,
   as the pricing strip's) and the view; then the projects.
   - Grid: the home page's card (a backdrop, the picture of the work inset on
     it, a glass tag with the logo, the name and "See work") over its own
     colour haze, with the name, year, line and tags under it. Rows pair a
     wide card with a narrow one, the sides swapping row to row, every row
     the same height (the wide card is about 16:10, the narrow one squarer);
     a narrow card shows the site on a phone. An odd one out spans the row,
     its picture framed in the middle. work-page.js sets the shapes for the
     projects showing (is-wide, is-narrow, is-full).
   - List: an index, one row per project; on a desktop the picture follows
     the pointer down it and the other rows dim.
   Motion (work-page.js): the head plays in as the type loads; each card
   wipes up from its foot as it comes into view, its picture settling and
   its words rising after; hover drifts the card's layers with the pointer.
   A change of filter or view: what's showing fades up and out, the new set
   wipes in. The projects come from tools/work/build.py.
   ========================================================================== */

/* The row height is worked out once, on the grid (from the section's
   width), and handed down as a length, so the cards inside can size from
   it whatever container they sit in. */
@property --row-h {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

.wpage {
  position: relative;
  background: var(--bone);
}

/* Paper grain, as on the rest of the site. */
.wpage::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url("../assets/textures/grain.webp") repeat;
  background-size: 160px 160px;
  opacity: 0.55;
  pointer-events: none;
}

.wpage > * {
  position: relative;
}

/* Anything toggled with `hidden` that also has a display of its own. */
.wpage [hidden] {
  display: none !important;
}

/* ------------------------------------------------------------------ head */

.whead {
  padding: calc(var(--header-h) + clamp(24px, 5vh, 64px)) var(--gutter) 0;
}

.whead__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
}

.whead__index {
  color: var(--text-mute);
  text-align: right;
}

/* The title and, on wide screens, the line beside it at its foot. */
.whead__main {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(20px, 3vh, 32px) clamp(32px, 4vw, 80px);
  margin-top: clamp(20px, 4vh, 44px);
}

.whead__title {
  display: flex;
  align-items: flex-start;
  font-size: clamp(3.5rem, 14.6vw, 17rem);
  font-stretch: var(--wdth-display);
  font-weight: 500;
  line-height: 0.84;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  font-synthesis: none;
}

/* The word rises out of its mask. The padding keeps the glyphs whole. */
.whead__w {
  display: inline-block;
  overflow: clip;
  padding: 0.06em 0.02em 0.02em;
  margin: -0.06em -0.02em -0.02em;
}

.whead__w > span {
  display: inline-block;
}

.whead__count {
  margin: 0.55em 0 0 0.6em;
  font-size: clamp(0.75rem, 0.6rem + 0.45vw, 1.125rem);
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
  transition: opacity 300ms ease;
}

.whead__count.is-changing {
  opacity: 0;
}

.whead__lede {
  max-width: 15ch;
  padding-bottom: 0.08em;
  font-size: clamp(1.875rem, 3.3vw, 4rem);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.038em;
  text-wrap: balance;
}

.whead__lw {
  display: inline-block;
  overflow: clip;
  padding-bottom: 0.1em;
  margin-bottom: -0.1em;
  vertical-align: top;
}

.whead__lw > span {
  display: inline-block;
}

/* The figures and a line on who made all of it, under a hairline. */
.whead__foot {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  align-items: end;
  gap: 28px clamp(32px, 5vw, 96px);
  margin-top: clamp(36px, 6.5vh, 76px);
  padding-top: clamp(18px, 2.6vh, 26px);
  border-top: 1px solid var(--line);
}

.wfacts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px clamp(16px, 2vw, 40px);
  margin: 0;
}

.wfact {
  display: flex;
  flex-direction: column-reverse;
  justify-content: flex-end;
  gap: 12px;
  margin: 0;
}

.wfact__label {
  color: var(--text-mute);
}

.wfact__num {
  margin: 0;
  font-size: clamp(2.5rem, 1.6rem + 2.6vw, 5rem);
  font-weight: 500;
  line-height: 0.9;
  letter-spacing: -0.055em;
  font-variant-numeric: tabular-nums;
}

.whead__intro {
  justify-self: end;
  max-width: 44ch;
  font-size: clamp(0.9375rem, 0.88rem + 0.2vw, 1.0625rem);
  line-height: 1.5;
  color: rgb(17 17 17 / 0.72);
  text-wrap: pretty;
}

/* Entrance (html.is-entered, from the inline script after the head): the
   word and the line rise, then the rest comes up one by one. Hidden only
   while JS is on and the head hasn't entered, so nothing shows, vanishes
   and shows again. */
html.js:not(.is-entered) .whead__w > span,
html.js:not(.is-entered) .whead__lw > span {
  transform: translateY(108%);
}

html.js:not(.is-entered) .whead [data-enter] {
  opacity: 0;
  translate: 0 16px;
}

.whead__w > span {
  transition: transform 1300ms var(--ease-out) 80ms;
}

.whead__lw > span {
  transition: transform 1150ms var(--ease-out) calc(260ms + var(--w, 0) * 45ms);
}

.whead [data-enter] {
  transition:
    opacity 900ms ease calc(320ms + var(--e, 0) * 80ms),
    translate 1100ms var(--ease-out) calc(320ms + var(--e, 0) * 80ms);
}

/* the count's own fade (a change of filter) stays quick */
.whead .whead__count.is-changing {
  transition: opacity 200ms ease;
}

/* ------------------------------------------------------------------ the bar */

/* The bar and the projects: the bar holds only while they're on screen. */
.wbody {
  position: relative;
}

/* Holds under the header's bar while the projects scroll by. Once held,
   a frosted strip runs edge to edge behind it (is-stuck, work-page.js). */
.wbar {
  position: sticky;
  top: var(--header-compact);
  z-index: 6;
  margin-top: clamp(40px, 7vh, 88px);
  padding: 0 var(--gutter);
}

.wbar::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgb(247 245 242 / 0.86);
  -webkit-backdrop-filter: blur(18px) saturate(1.25);
  backdrop-filter: blur(18px) saturate(1.25);
  border-bottom: 1px solid var(--line);
  opacity: 0;
  transition: opacity 450ms var(--ease-out);
  pointer-events: none;
}

.wbar.is-stuck::before {
  opacity: 1;
}

.wbar__inner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 68px;
  border-block: 1px solid var(--line);
  transition: border-color 450ms ease;
}

.wbar.is-stuck .wbar__inner {
  border-color: transparent;
}

/* The filters swipe sideways when they don't fit (the scrollbar hidden). */
.wbar__scroll {
  min-width: 0;
  margin-left: calc(var(--gutter) * -1);
  padding: 12px 0 12px var(--gutter);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

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

/* More filters off to the right: the strip fades out at its edge (work-page.js). */
.wbar__scroll.is-more {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 36px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 36px), transparent);
}

.wbar__filters,
.wbar__views,
.wink {
  display: flex;
  align-items: center;
  gap: 6px;
}

.wbar__filters,
.wbar__views {
  position: relative;
  width: max-content;
}

.wfilter,
.wview {
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 42px;
  padding: 0 16px 0 18px;
  border-radius: 999px;
  font-size: 0.9375rem;
  font-weight: 500;
  letter-spacing: -0.005em;
  line-height: 1;
  color: var(--ink);
  white-space: nowrap;
  box-shadow: inset 0 0 0 1px var(--line);
  transition:
    background-color 300ms ease,
    box-shadow 300ms ease,
    scale 240ms var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}

.wfilter__n {
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
}

.wview {
  gap: 9px;
  padding: 0 16px 0 14px;
}

.wview svg {
  flex: none;
  width: 15px;
  height: 15px;
}

@media (hover: hover) and (pointer: fine) {
  .wfilter:hover,
  .wview:hover {
    background: rgb(255 255 255 / 0.62);
    box-shadow: inset 0 0 0 1px rgb(17 17 17 / 0.3);
  }
}

.wfilter:active,
.wview:active {
  scale: 0.96;
}

.wfilter:focus-visible,
.wview:focus-visible {
  border-radius: 999px;
}

/* The ink pill: the same row of pills, white on ink, cut to the one chosen
   (work-page.js moves the cut, its front edge leading). Without the script,
   the chosen pill is simply ink. */
.wink {
  position: absolute;
  inset: 0;
  color: var(--white);
  background: var(--ink);
  clip-path: inset(0 100% 0 0 round 999px);
  pointer-events: none;
}

.wink > * {
  box-shadow: none;
  color: var(--white);
  transition: none;
}

.wink .wfilter__n {
  color: var(--on-ink-mute);
}

.wbar:not(.is-inked) [aria-pressed="true"] {
  color: var(--white);
  background: var(--ink);
}

.wbar:not(.is-inked) [aria-pressed="true"] .wfilter__n {
  color: var(--on-ink-mute);
}

/* ------------------------------------------------------------------ projects */

.wwork {
  container-type: inline-size;
  padding: clamp(40px, 6.5vh, 80px) var(--gutter) clamp(110px, 16vh, 190px);
}

/* Every row the same height: wide (7 of 12) about 16:10, narrow (5 of 12)
   squarer, full the whole width. */
.wgrid {
  --gap: clamp(14px, 1.5vw, 28px);
  --row-h: calc((100cqw - var(--gap)) * 0.36);
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(64px, 10vh, 128px) var(--gap);
}

.wtile {
  grid-column: span 7;
  min-width: 0;
}

.wtile.is-narrow {
  grid-column: span 5;
}

.wtile.is-full {
  grid-column: 1 / -1;
}

.wtile__link {
  display: block;
  -webkit-tap-highlight-color: transparent;
}

.wtile__link:focus-visible {
  outline: none;
}

.wtile__link:focus-visible .wtile__card {
  outline: 2px solid var(--ink);
  outline-offset: 6px;
}

/* ---- the card */

.wtile__stage {
  position: relative;
  isolation: isolate;
  height: var(--row-h);
}

/* Its own colours, blurred into a haze under it (baked by tools/work/art.py,
   so nothing blurs live). Smaller than the home page's: cards sit side by
   side here, and two hazes shouldn't pool. */
.wtile__glow {
  position: absolute;
  left: -13%;
  top: -7%;
  z-index: -1;
  width: 126%;
  max-width: none;
  height: 126%;
  opacity: 0.72;
  pointer-events: none;
  user-select: none;
  /* Takes the last of the baked haze to zero at its edges (no faint box). */
  -webkit-mask-image: radial-gradient(closest-side, #000 78%, transparent);
  mask-image: radial-gradient(closest-side, #000 78%, transparent);
}

.wtile__card {
  --r: clamp(14px, 1.15vw, 22px);
  --pad-t: clamp(58px, 4.4vw, 84px);
  --pad-x: clamp(12px, 2vw, 40px);
  --pad-b: clamp(12px, 2.3vw, 44px);
  --in-h: calc(var(--row-h) - var(--pad-t) - var(--pad-b));
  position: relative;
  isolation: isolate;
  container: card / inline-size;
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 100%;
  padding: var(--pad-t) var(--pad-x) var(--pad-b);
  overflow: hidden;
  border-radius: var(--r);
  color: var(--white);
  background: var(--ink-soft);
  clip-path: inset(0 0 0 0 round var(--r));
  transition: scale 260ms var(--ease-out);
}

/* The backdrop and the picture drift apart as the pointer moves over the
   card (work-page.js), so each is its own layer; the backdrop is
   overscanned, so its drift never shows an edge. */
.wtile__bg {
  position: absolute;
  inset: -2%;
  z-index: -1;
}

.wtile__bg img,
.wtile__pic img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.wtile__bg img {
  scale: 1.04;
  transition: scale 1400ms var(--ease-out);
}

/* The picture of the work. A site's first screen is cover-cropped from the
   top, so its nav and headline always show. */
.wtile__pic {
  position: relative;
  display: block;
  flex: none;
  height: 100%;
  overflow: hidden;
  border-radius: calc(var(--r) * 0.72);
  background: rgb(255 255 255 / 0.06);
  box-shadow: 0 30px 60px -24px rgb(0 0 0 / 0.6);
}

.wtile__pic img {
  object-position: 50% 0;
  transition:
    scale 1100ms var(--ease-out),
    transform 1700ms var(--ease-out) calc(var(--d, 0) * 140ms);
}

/* hairline rim over the picture */
.wtile__pic::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.09);
  pointer-events: none;
}

/* The site on a phone: a narrow card shows it upright in the middle. */
.wtile__pic--phone {
  display: none;
  width: auto;
  height: 100%;
  aspect-ratio: 390 / 844;
  margin-inline: auto;
  border-radius: clamp(14px, 1.6vw, 30px);
  box-shadow:
    0 0 0 clamp(3px, 0.32vw, 6px) rgb(10 10 10 / 0.92),
    0 0 0 calc(clamp(3px, 0.32vw, 6px) + 1px) rgb(255 255 255 / 0.12),
    0 40px 70px -26px rgb(0 0 0 / 0.75);
}

.wtile.is-narrow.has-phone .wtile__pic--desk {
  display: none;
}

.wtile.is-narrow.has-phone .wtile__pic--phone {
  display: block;
}

/* A card across the row: the picture framed at 16:10 in the middle and,
   for a site, the phone beside it, standing a little proud. */
.wtile.is-full .wtile__pic--desk {
  width: auto;
  aspect-ratio: 16 / 10;
  max-width: 100%;
  margin-inline: auto;
}

.wtile.is-full.has-phone .wtile__pic--phone {
  position: absolute;
  bottom: calc(var(--pad-b) * 0.6);
  left: calc(50% + var(--in-h) * 0.8 - var(--in-h) * 0.86 * 390 / 844 * 0.62);
  display: block;
  height: calc(var(--in-h) * 0.86);
}

/* Logo, name, "See work": frosted glass on the top edge of the picture, as
   on the home page. Centred with auto margins, not translate: GSAP drifts
   it (work-page.js) and would fold a CSS translate into its own. */
.wtile__tag {
  --tag-h: 46px;
  position: absolute;
  top: calc(var(--pad-t) - var(--tag-h) + 3px);
  left: 0;
  right: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  width: fit-content;
  max-width: calc(100% - 2 * var(--pad-x));
  height: var(--tag-h);
  margin-inline: auto;
  padding: 0 16px 0 7px;
  border: 1px solid rgb(255 255 255 / 0.14);
  border-radius: 13px;
  background: rgb(22 22 22 / 0.66);
  -webkit-backdrop-filter: blur(16px) saturate(1.3);
  backdrop-filter: blur(16px) saturate(1.3);
  box-shadow: 0 12px 30px -8px rgb(0 0 0 / 0.4);
  font-size: 0.9375rem;
  line-height: 1;
  white-space: nowrap;
  transition:
    background-color 300ms ease,
    opacity 800ms ease calc(320ms + var(--d, 0) * 140ms);
}

.wtile__logo {
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: 8px;
}

.wtile__tagname {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.wtile__cta {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 18px;
  padding-left: 12px;
  border-left: 1px solid rgb(255 255 255 / 0.22);
  font-size: 0.875rem;
  color: rgb(255 255 255 / 0.74);
}

.wtile__cta svg {
  flex: none;
  width: 13px;
  height: 13px;
  transition: translate 420ms var(--ease-out);
}

/* A soft light over the card that follows the pointer (added by work-page.js). */
.wtile__sheen {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 2;
  width: 70%;
  aspect-ratio: 1;
  margin: -35% 0 0 -35%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(255 255 255 / 0.13), rgb(255 255 255 / 0) 72%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 500ms ease;
}

/* ---- the words under it */

.wtile__meta {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: 14px;
  margin-top: clamp(18px, 2.4vh, 26px);
}

.wtile__n {
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.wtile__name {
  justify-self: start;
  font-size: clamp(1.375rem, 0.95rem + 0.9vw, 2.125rem);
  font-weight: 500;
  line-height: 1.08;
  letter-spacing: -0.028em;
  background: linear-gradient(currentColor, currentColor) 0 100% / 0 1px no-repeat;
  transition: background-size 600ms var(--ease-out);
}

.wtile__year {
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
}

.wtile__what {
  grid-column: 2 / -1;
  margin-top: 8px;
  font-size: 0.9375rem;
  line-height: 1.4;
  color: var(--text-mute);
}

.wtile__tags {
  grid-column: 2 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 14px;
  font-size: 0.625rem;
}

.wtile__tags li {
  padding: 6px 9px 5px;
  border-radius: 999px;
  color: rgb(17 17 17 / 0.72);
  box-shadow: inset 0 0 0 1px var(--line);
}

.wtile__tags li.is-own {
  color: var(--white);
  background: var(--ink);
  box-shadow: none;
}

/* A website concept: said first, in its own colour, so no one takes it for a client's site. */
.wtile__tags li.is-concept {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--ink);
  background: var(--bone-deep);
  box-shadow: inset 0 0 0 1px rgb(17 17 17 / 0.22);
}

.wtile__tags li.is-concept::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
}

/* ---- hover, for the whole card and its words */

@media (hover: hover) and (pointer: fine) {
  .wtile__link:hover .wtile__bg img {
    scale: 1.1;
  }

  .wtile__link:hover .wtile__pic img {
    scale: 1.035;
  }

  .wtile__link:hover .wtile__tag {
    background: rgb(12 12 12 / 0.82);
  }

  .wtile__link:hover .roll > span:first-child {
    transform: translateY(-105%);
  }

  .wtile__link:hover .roll > span + span {
    transform: none;
  }

  .wtile__link:hover .wtile__cta svg {
    translate: 3px 0;
  }

  .wtile__link:hover .wtile__name {
    background-size: 100% 1px;
  }

  .wtile__link:hover .wtile__sheen {
    opacity: 1;
  }
}

.wtile__link:focus-visible .roll > span:first-child {
  transform: translateY(-105%);
}

.wtile__link:focus-visible .roll > span + span {
  transform: none;
}

.wtile__link:active .wtile__card {
  scale: 0.99;
}

/* ---- reveal: the card wipes up from its foot, its picture settles, the
   tag and the words follow (work-page.js adds .is-in as it comes into
   view; --d staggers cards that arrive together). */
.wtile__card {
  transition:
    scale 260ms var(--ease-out),
    clip-path 1250ms var(--ease-out) calc(var(--d, 0) * 140ms);
}

.wtile__glow {
  transition: opacity 1400ms ease calc(200ms + var(--d, 0) * 140ms);
}

.wtile__meta > * {
  transition:
    opacity 800ms ease calc(380ms + var(--d, 0) * 140ms + var(--m, 0) * 70ms),
    translate 1000ms var(--ease-out) calc(380ms + var(--d, 0) * 140ms + var(--m, 0) * 70ms),
    background-size 600ms var(--ease-out);
}

.wtile__name,
.wtile__n,
.wtile__year {
  --m: 0;
}

.wtile__what {
  --m: 1;
}

.wtile__tags {
  --m: 2;
}

html.js:not(.work-static) .wtile:not(.is-in) .wtile__card {
  clip-path: inset(100% 0 0 0 round var(--r));
}

html.js:not(.work-static) .wtile:not(.is-in) .wtile__pic img {
  transform: scale(1.16);
}

html.js:not(.work-static) .wtile:not(.is-in) .wtile__glow,
html.js:not(.work-static) .wtile:not(.is-in) .wtile__tag {
  opacity: 0;
}

html.js:not(.work-static) .wtile:not(.is-in) .wtile__meta > * {
  opacity: 0;
  translate: 0 14px;
}

/* A fresh set (a change of filter or view) starts from its hidden state at
   once, then plays in. */
.wwork.is-reset *,
.wwork.is-reset *::before,
.wwork.is-reset *::after {
  transition: none !important;
}

/* What's showing leaves, quickly, before the new set comes in. */
.wgrid > *,
.wlist {
  transition:
    opacity 300ms ease,
    transform 380ms var(--ease-out);
}

.wwork.is-leaving .wgrid > *,
.wwork.is-leaving .wlist {
  opacity: 0;
  transform: translateY(-14px);
}

/* ------------------------------------------------------------------ the list */

.wlist__head,
.wrow__link {
  display: grid;
  grid-template-columns: 4.25rem minmax(0, 2.15fr) minmax(0, 1.25fr) minmax(0, 1.75fr) 4.5rem 1.75rem;
  align-items: center;
  column-gap: clamp(16px, 2vw, 40px);
}

.wlist__head {
  padding-bottom: 16px;
  border-bottom: 1px solid var(--ink);
  color: var(--text-mute);
}

.wlist__head span:nth-child(5) {
  text-align: right;
}

.wlist__rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

.wrow__link {
  position: relative;
  min-height: clamp(88px, 11.5vh, 128px);
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
  transition: opacity 450ms ease;
  -webkit-tap-highlight-color: transparent;
}

/* An ink line draws along the foot from the left on hover. */
.wrow__link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 700ms var(--ease-out);
}

.wrow__n,
.wrow__year {
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
}

.wrow__year {
  text-align: right;
}

.wrow__name {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
  font-size: clamp(1.75rem, 0.95rem + 1.75vw, 3.25rem);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.04em;
  transition: translate 600ms var(--ease-out);
}

.wrow__own {
  flex: none;
  padding: 6px 9px 5px;
  border-radius: 999px;
  font-size: 0.625rem;
  letter-spacing: 0.04em;
  color: var(--white);
  background: var(--ink);
}

.wrow__own--concept {
  color: var(--ink);
  background: var(--bone-deep);
  box-shadow: inset 0 0 0 1px rgb(17 17 17 / 0.22);
}

.wrow__sector {
  font-size: 1rem;
  line-height: 1.35;
}

.wrow__label {
  color: var(--text-mute);
}

.wrow__services {
  font-size: 0.9375rem;
  line-height: 1.4;
  color: var(--text-mute);
  text-wrap: pretty;
}

.wrow__go {
  justify-self: end;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
}

.wrow__go svg {
  width: 16px;
  height: 16px;
  rotate: -45deg;
  transition: rotate 500ms var(--ease-out);
}

/* The small picture: phones and tablets only (desktops get the one that
   follows the pointer). */
.wrow__thumb {
  display: none;
}

@media (hover: hover) and (pointer: fine) {
  .wlist__rows:hover .wrow__link:not(:hover) {
    opacity: 0.32;
  }

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

  .wrow__link:hover .wrow__name {
    translate: 14px 0;
  }

  .wrow__link:hover .wrow__go svg {
    rotate: 0deg;
  }
}

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

/* Reveal: each row's words rise as it arrives. */
.wrow__link > * {
  transition:
    opacity 700ms ease calc(var(--d, 0) * 70ms),
    translate 900ms var(--ease-out) calc(var(--d, 0) * 70ms);
}

.wrow__link > .wrow__name {
  transition:
    opacity 700ms ease calc(var(--d, 0) * 70ms),
    translate 900ms var(--ease-out) calc(var(--d, 0) * 70ms);
}

html.js:not(.work-static) .wrow:not(.is-in) .wrow__link > * {
  opacity: 0;
  translate: 0 18px;
}

html.js:not(.work-static) .wlist .wlist__head {
  transition: opacity 700ms ease;
}

html.js:not(.work-static) .wlist:not(.is-in) .wlist__head {
  opacity: 0;
}

/* Settled rows take the hover's own timing again. */
.wrow.is-settled .wrow__link > * {
  transition-delay: 0ms;
}

.wrow.is-settled .wrow__link > .wrow__name {
  transition: translate 600ms var(--ease-out);
}

/* The picture that follows the pointer down the list (work-page.js). Placed
   with margins, not translate: GSAP moves it. */
.wprev {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 30;
  width: clamp(260px, 21vw, 400px);
  aspect-ratio: 16 / 10;
  margin: calc(clamp(260px, 21vw, 400px) * -0.3125) 0 0 calc(clamp(260px, 21vw, 400px) * -0.5);
  border-radius: 14px;
  overflow: hidden;
  background: var(--ink-soft);
  box-shadow: var(--shadow-deep);
  opacity: 0;
  scale: 0.86;
  pointer-events: none;
  transition:
    opacity 300ms ease,
    scale 500ms var(--ease-out);
}

.wprev.is-shown {
  opacity: 1;
  scale: 1;
}

.wprev img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 0;
  clip-path: inset(100% 0 0 0);
  transition: clip-path 600ms var(--ease-out);
}

.wprev img.is-on {
  z-index: 1;
  clip-path: inset(0 0 0 0);
}

/* ------------------------------------------------------------------ the close */

.wclose {
  display: grid;
  justify-items: start;
  gap: clamp(18px, 3vh, 28px);
  padding: clamp(96px, 16vh, 180px) var(--gutter) clamp(110px, 18vh, 200px);
  border-top: 1px solid var(--line);
}

.wclose__label {
  color: var(--text-mute);
}

.wclose__title {
  font-size: clamp(3.5rem, 11vw, 13rem);
  font-stretch: var(--wdth-display);
  font-weight: 500;
  line-height: 0.86;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  font-synthesis: none;
}

.wclose__w {
  display: inline-block;
  overflow: clip;
  padding: 0.06em 0.02em 0.02em;
  margin: -0.06em -0.02em -0.02em;
  vertical-align: top;
}

.wclose__w > span {
  display: inline-block;
}

.wclose__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px 48px;
  width: 100%;
  margin-top: 8px;
}

.wclose__text {
  max-width: 34ch;
  font-size: clamp(1.125rem, 1rem + 0.45vw, 1.5rem);
  line-height: 1.38;
  letter-spacing: -0.015em;
  text-wrap: pretty;
}

.wclose__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 28px;
}

.wclose__actions .btn-cta {
  display: inline-flex;
}

.wclose__link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 1rem;
  font-weight: 500;
}

.wclose__link svg {
  width: 15px;
  height: 15px;
  transition: translate 420ms var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .wclose__link:hover svg {
    translate: 3px 0;
  }

  .wclose__link:hover > .roll > span:first-child {
    transform: translateY(-105%);
  }

  .wclose__link:hover > .roll > span + span {
    transform: none;
  }
}

.wclose.is-armed .wclose__w > span {
  transform: translateY(108%);
  transition: transform 1150ms var(--ease-out) calc(var(--w, 0) * 80ms);
}

.wclose.is-armed .wclose__label,
.wclose.is-armed .wclose__foot {
  opacity: 0;
  translate: 0 16px;
  transition:
    opacity 800ms ease 300ms,
    translate 1000ms var(--ease-out) 300ms;
}

.wclose.is-in .wclose__w > span {
  transform: none;
}

.wclose.is-in .wclose__label,
.wclose.is-in .wclose__foot {
  opacity: 1;
  translate: none;
}

/* ------------------------------------------------------------------ responsive */

/* Wide screens keep the line beside the title; narrower ones put it under. */
@media (max-width: 1099px) {
  .whead__foot {
    grid-template-columns: 1fr;
  }

  .whead__intro {
    justify-self: start;
  }

  /* Two even columns: wide cards show the site on a desktop, narrow ones on a phone. */
  .wgrid {
    --row-h: calc((100cqw - var(--gap)) / 2 / 1.12);
  }

  .wtile,
  .wtile.is-narrow {
    grid-column: span 6;
  }

  /* A site's first screen whole, framed at 16:10 in the middle of a squarer card. */
  .wtile.has-phone:not(.is-full) .wtile__pic--desk {
    height: auto;
    aspect-ratio: 16 / 10;
  }

  /* The list: the services make way. */
  .wlist__head,
  .wrow__link {
    grid-template-columns: 3rem minmax(0, 1.6fr) minmax(0, 1fr) 4rem 1.75rem;
  }

  .wlist__head span:nth-child(4),
  .wrow__services {
    display: none;
  }

  .wlist__head span:nth-child(5) {
    text-align: right;
  }
}

@container card (max-width: 380px) {
  .wtile__cta .roll {
    display: none;
  }

  .wtile__cta {
    padding-left: 10px;
  }
}

@media (max-width: 719px) {
  /* One row, as on desktop; the index drops under the label only if it must. */
  .whead__top {
    flex-wrap: wrap;
    gap: 6px 24px;
  }

  .whead__index {
    text-align: left;
  }

  .wfacts {
    gap: 16px;
  }

  .wfact__num {
    font-size: clamp(2.125rem, 9vw, 2.75rem);
  }

  .wfact__label {
    font-size: 0.625rem;
  }

  .wbar__inner {
    min-height: 60px;
    gap: 10px;
  }

  .wbar__scroll {
    padding-block: 9px;
  }

  .wfilter,
  .wview {
    height: 40px;
    padding: 0 13px 0 15px;
    font-size: 0.875rem;
  }

  /* The view: icons only. */
  .wview {
    width: 40px;
    padding: 0;
    justify-content: center;
  }

  .wview__name {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  /* One column, every card squarer; a lone card is just a card here. */
  .wgrid {
    --row-h: calc(100cqw / 1.04);
    gap: clamp(48px, 8vh, 72px) 0;
  }

  .wtile,
  .wtile.is-narrow,
  .wtile.is-full {
    grid-column: 1 / -1;
  }

  .wtile.is-full:not(.has-phone) .wtile__pic--desk {
    width: auto;
    aspect-ratio: auto;
  }

  .wtile.is-full.has-phone .wtile__pic--desk {
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 10;
  }

  .wtile.is-full.has-phone .wtile__pic--phone {
    display: none;
  }

  .wtile__card {
    --pad-t: 58px;
  }

  .wtile__tag {
    --tag-h: 42px;
    gap: 10px;
    padding: 0 14px 0 6px;
    border-radius: 12px;
    font-size: 0.875rem;
  }

  .wtile__logo {
    width: 28px;
    height: 28px;
    border-radius: 7px;
  }

  .wtile__cta {
    padding-left: 10px;
    font-size: 0.8125rem;
  }

  /* The list: name and sector on the left, a picture on the right. */
  .wlist__head {
    display: none;
  }

  .wrow__link {
    grid-template-columns: minmax(0, 1fr) clamp(96px, 30vw, 128px);
    grid-template-areas:
      "name thumb"
      "sector thumb"
      "year thumb";
    align-items: start;
    row-gap: 6px;
    column-gap: 16px;
    min-height: 0;
    padding: 20px 0;
  }

  .wrow__n,
  .wrow__go {
    display: none;
  }

  .wrow__name {
    grid-area: name;
    flex-wrap: wrap;
    gap: 6px 10px;
    font-size: clamp(1.625rem, 7.4vw, 2rem);
  }

  .wrow__sector {
    grid-area: sector;
    font-size: 0.9375rem;
  }

  .wrow__year {
    grid-area: year;
    margin-top: 4px;
    text-align: left;
  }

  .wrow__thumb {
    grid-area: thumb;
    display: block;
    align-self: center;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    border-radius: 10px;
    background: var(--ink-soft);
  }

  .wrow__thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 0;
  }

  .wclose__foot {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* Tablets and phones: the small picture shows in the list (no pointer to follow). */
@media (hover: none), (pointer: coarse) {
  @media (min-width: 720px) {
    .wlist__head,
    .wrow__link {
      grid-template-columns: 3rem minmax(0, 1.6fr) minmax(0, 1fr) 4rem clamp(96px, 12vw, 140px);
    }

    .wlist__head span:nth-child(4),
    .wrow__services,
    .wrow__go {
      display: none;
    }

    .wrow__thumb {
      display: block;
      aspect-ratio: 16 / 10;
      overflow: hidden;
      border-radius: 10px;
      background: var(--ink-soft);
    }

    .wrow__thumb img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      object-position: 50% 0;
    }
  }
}

/* Short, wide screens (a laptop at 125-150%): a lower head. */
@media (min-width: 1100px) and (max-height: 760px) {
  .whead__title {
    font-size: clamp(5rem, 12.5vw, 17rem);
  }

  .whead__foot {
    margin-top: clamp(28px, 4.5vh, 48px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .wpage *,
  .wpage *::before,
  .wpage *::after {
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
  }
}

/* Phones: the site's section rhythm (--sec-top / --sec-bot / --sec-end, tokens.css),
   so every gap between two sections reads the same. */
@media (max-width: 719px) {
  .wwork {
    padding-bottom: var(--sec-bot);
  }

  .wclose {
    padding-top: var(--sec-top);
    padding-bottom: var(--sec-end);
  }
}
