/* ==========================================================================
   Selected work (after cosmos.studio)
   A bone section that rides up over the held hero (.hero-pin in hero.css),
   then a stack of project cards. Every card holds at the same place
   (sticky) and the next rides up over it, with the year on its left and the
   niche on its right. A card is one link: the project's backdrop, a picture
   of the work inset on it, and a tag with the logo, the name and "See work".
   Each card floats on a glow of its own colours and drifts toward where
   you look from (pointer, or the tilt of a phone or tablet) with its layers
   drifting apart and a light sliding across it, like the hero's type. work.js adds the feel: a card lands
   from slightly large, and the labels and glow of the card being covered
   lift away.
   ========================================================================== */

.work {
  --vh: 100svh;
  /* Card geometry. The width follows the screen's width or height, whichever
     is tighter, so a held card always fits under the header's bar with room
     above and below. */
  --card-ratio: 1.4; /* width / height */
  --stack-edge: clamp(20px, calc(var(--vh) * 0.06), 72px); /* least space above and below a held card */
  --stack-gap: calc(var(--vh) * 0.16); /* scroll from one card landing to the next arriving */
  --meta-h: 0px; /* the year and niche row above the card (narrow screens) */
  --card-w: min(56vw, 1180px, (var(--vh) - var(--header-compact) - 2 * var(--stack-edge)) * var(--card-ratio));
  --card-h: calc(var(--card-w) / var(--card-ratio));

  position: relative;
  z-index: 1; /* over the sticky hero */
  margin-top: calc(var(--vh) * -1); /* up over the pin's extra screen */
  padding: clamp(88px, calc(var(--vh) * 0.15), 170px) 0 clamp(96px, calc(var(--vh) * 0.16), 180px);
  background: var(--bone);
}

@supports not (height: 100svh) {
  .work {
    --vh: 100vh;
  }
}

/* Paper grain, as on the hero. Everything after it is positioned, so it
   paints over the grain. */
.work::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url("../assets/textures/grain.webp") repeat;
  background-size: 160px 160px;
  opacity: 0.55;
  pointer-events: none;
}

/* The section's shadow on the held hero (Atelier North's). It only draws
   outside the section, and it fades in as the section starts to climb
   (work.js), so none of it sits on the hero at rest. */
.work__shadow {
  position: absolute;
  inset: 0;
  box-shadow: 0 -40px 90px -40px rgb(17 17 17 / 0.45);
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
}

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

.work__head {
  position: relative;
  display: grid;
  justify-items: center;
  gap: clamp(16px, calc(var(--vh) * 0.024), 26px);
  margin-bottom: clamp(64px, calc(var(--vh) * 0.12), 136px);
  padding: 0 var(--gutter);
  text-align: center;
}

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

.work__title {
  font-size: clamp(2.75rem, 7.6vw, 8.75rem);
  font-stretch: var(--wdth-display);
  font-weight: 500;
  line-height: 0.94;
  letter-spacing: -0.012em;
  text-transform: uppercase;
  font-synthesis: none;
  text-wrap: balance;
}

/* Each word rises out of its own mask (work.js arms it, then plays it in
   view). The padding gives the glyphs room; the negative margin cancels it. */
.work__mask {
  display: inline-block;
  overflow: clip;
  padding: 0.08em 0.03em 0.06em;
  margin: -0.08em -0.03em -0.06em;
  vertical-align: top;
}

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

.work__count {
  display: inline-block;
  vertical-align: top;
  margin: 0.9em 0 0 0.9em;
  color: var(--text-mute);
}

.work.is-armed .work__mask > span {
  transform: translateY(110%);
  transition: transform 1150ms var(--ease-out);
  transition-delay: calc(var(--i, 0) * 90ms);
}

.work.is-armed .work__label,
.work.is-armed .work__count {
  opacity: 0;
  transition: opacity 900ms ease 250ms;
}

.work.is-in .work__mask > span {
  transform: none;
}

.work.is-in .work__label,
.work.is-in .work__count {
  opacity: 1;
}

/* ------------------------------------------------------------------ stack */

.work__list {
  display: flex;
  flex-direction: column;
  gap: var(--stack-gap);
}

/* Every row holds at the same place, centred in the space under the header's
   bar. Later rows paint over earlier ones (document order), so each card
   covers the last. */
.work-item {
  position: sticky;
  top: calc(var(--header-compact) + (var(--vh) - var(--header-compact) - var(--card-h) - var(--meta-h)) / 2);
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--card-w) minmax(0, 1fr);
  grid-template-areas: "year card niche";
  align-items: center;
}

.work-meta {
  display: grid;
  gap: 6px;
  justify-self: center;
  margin: 0;
  padding: 0 12px;
}

.work-meta--year {
  grid-area: year;
}

.work-meta--niche {
  grid-area: niche;
  text-align: right;
}

.work-meta dt {
  color: var(--text-faint);
}

.work-meta dd {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.01em;
}

/* ------------------------------------------------------------------ card */

/* The stage holds the card and its glow. work.js scales it as a card lands
   (from the top edge, so that stays true); the card itself turns in 3D. */
.work-stage {
  grid-area: card;
  position: relative;
  isolation: isolate;
  width: var(--card-w);
  height: var(--card-h);
  transform-origin: 50% 0;
  will-change: transform;
  transition: scale 260ms var(--ease-out);
}

/* Pressed. On the stage: GSAP owns the card's transform (and writes
   scale: none inline there), so the card can't carry its own. */
.work-stage:has(> .work-card:active) {
  scale: 0.985;
}

/* Something to float over: the project's own colours, blurred into a haze
   under the card and dropped a little, like light through it onto the page.
   A baked image (tools/work-art/render.py), so nothing blurs live. It slides
   against the card's turn (work.js). Placed with left/top, not translate:
   GSAP moves it and would fold a CSS translate into its own. */
.work-stage__glow {
  position: absolute;
  left: -26%;
  top: -12%;
  z-index: -1;
  width: 152%;
  max-width: none;
  height: 152%;
  pointer-events: none;
  user-select: none;
  will-change: transform;
  /* The haze is baked almost to nothing at its edges; the mask takes the
     last of it to zero, so no faint rectangle can ever show. */
  -webkit-mask-image: radial-gradient(closest-side, #000 78%, transparent);
  mask-image: radial-gradient(closest-side, #000 78%, transparent);
}

.work-card {
  /* Proportions measured off the reference card: the tag sits in the top
     padding, the picture of the work fills the rest. */
  --pad-t: max(62px, var(--card-w) * 0.072);
  --pad-x: max(12px, var(--card-w) * 0.043);
  --pad-b: max(12px, var(--card-w) * 0.062);
  --radius: max(16px, var(--card-w) * 0.022);

  position: relative;
  isolation: isolate;
  display: block;
  width: 100%;
  height: 100%;
  padding: var(--pad-t) var(--pad-x) var(--pad-b);
  overflow: hidden;
  border-radius: var(--radius);
  color: var(--white);
  background: var(--ink-soft);
  will-change: transform; /* it drifts with the pointer or the device's tilt (work.js) */
  -webkit-tap-highlight-color: transparent;
}

/* base.css rounds every focus ring to 2px: keep the card's own corners */
.work-card:focus-visible {
  border-radius: var(--radius);
  outline-offset: 6px;
}

/* The backdrop, the picture and the tag drift apart in depth as the card
   turns (work.js), so each is its own layer. The backdrop is overscanned
   (scale 1.04), so its drift never shows an edge. */
.work-card__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  will-change: transform;
}

.work-card__bg img,
.work-card__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

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

/* The picture of the work. Cover-cropped from the top, so a page screenshot
   keeps its nav and headline on any card shape. */
.work-card__shot {
  position: relative;
  display: block;
  height: 100%;
  overflow: hidden;
  border-radius: calc(var(--radius) * 0.72);
  background: rgb(255 255 255 / 0.06);
  box-shadow: 0 30px 60px -24px rgb(0 0 0 / 0.6);
  will-change: transform;
}

.work-card__shot img {
  object-position: 50% 0;
  transition: scale 1100ms var(--ease-out);
}

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

/* Logo, name, "See work": frosted glass on the top edge of the picture.
   Centred with auto margins, not translate: GSAP drifts it (work.js) and
   would fold a CSS translate into its own. */
.work-card__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;
}

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

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

/* What the project is (Concept / Our own), beside its name, so a concept never passes for a
   client's site. */
.work-card__kind {
  flex: none;
  padding: 5px 7px 4px;
  border-radius: 999px;
  font-size: 0.625rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgb(255 255 255 / 0.82);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.28);
}

.work-card__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);
}

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

/* The text roll (.roll, header.css) and the rest of the hover, for the whole card */
@media (hover: hover) and (pointer: fine) {
  .work-card:hover .work-card__shot img {
    scale: 1.03;
  }

  .work-card:hover .work-card__bg img {
    scale: 1.1;
  }

  .work-card:hover .work-card__tag {
    background: rgb(12 12 12 / 0.82);
  }

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

  .work-card:hover .roll > span + span {
    transform: none;
  }

  .work-card:hover .work-card__arrow {
    translate: 3px 0;
  }
}

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

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

/* A soft light over the card that slides with the pointer or tilt (added
   by work.js), so the card reads as a lit, raised surface without turning. */
.work-card__sheen {
  position: absolute;
  inset: -40%;
  z-index: 2;
  background: radial-gradient(closest-side, rgb(255 255 255 / 0.1), rgb(255 255 255 / 0) 72%);
  pointer-events: none;
  will-change: transform;
}

/* ------------------------------------------------------------------ view all */

.work__all {
  position: relative;
  display: flex;
  justify-content: center;
  margin-top: clamp(64px, calc(var(--vh) * 0.11), 128px);
  padding: 0 var(--gutter);
}

/* header.css hides its own CTA on the smallest phones; this one stays */
.work__all .btn-cta {
  display: inline-flex;
}

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

/* Narrow screens: the card takes the width, with year and niche in a row
   above it. */
@media (max-width: 1099px) {
  .work {
    --meta-h: 44px;
    --card-w: min(100vw - 2 * var(--gutter), (var(--vh) - var(--header-compact) - 2 * var(--stack-edge) - var(--meta-h)) * var(--card-ratio));
  }

  .work-item {
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "year niche"
      "card card";
    align-items: start;
    width: var(--card-w);
    margin-inline: auto;
  }

  .work-meta {
    grid-auto-flow: column;
    align-items: baseline;
    align-content: end;
    justify-self: stretch;
    justify-content: start;
    gap: 10px;
    height: var(--meta-h);
    padding: 0 0 14px;
  }

  .work-meta--niche {
    justify-content: end;
  }
}

/* Portrait: squarer cards (the picture is cover-cropped from the top). */
@media (max-aspect-ratio: 1/1) {
  .work {
    --card-ratio: 1;
    /* As tall as the picture needs: the site's screen shows whole (16:10)
       instead of being cropped to a square. Tag padding + picture + foot. */
    --card-h: calc(max(62px, var(--card-w) * 0.072) + (var(--card-w) - 2 * max(12px, var(--card-w) * 0.043)) / 1.6 + max(12px, var(--card-w) * 0.062));
  }
}

@media (max-width: 719px) {
  .work-card {
    --pad-t: 58px;
  }

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

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

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

/* Without JS: a plain list of cards, no stacking (nothing to lift the
   labels of a covered card away). */
.no-js .work__list {
  gap: clamp(40px, 8vh, 96px);
}

.no-js .work-item {
  position: relative;
  top: auto;
}

@media (prefers-reduced-motion: reduce) {
  .work.is-armed .work__mask > span {
    transform: none;
    opacity: 0;
    transition: opacity 700ms ease;
  }

  .work.is-in .work__mask > span {
    opacity: 1;
  }

  .work-stage,
  .work-card__bg img,
  .work-card__shot img,
  .work-card__arrow {
    transition: none;
  }
}

/* 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) {
  .work {
    padding-bottom: var(--sec-bot);
    /* The card fits the picture whole (16:10) under the phone's 58px tag row. */
    --card-h: calc(58px + (var(--card-w) - 2 * max(12px, var(--card-w) * 0.043)) / 1.6 + max(12px, var(--card-w) * 0.062));
  }
}
