/* ==========================================================================
   Greta Volungevičiūtė — project case study
   Design canvas: 1440 wide, free height. Requires css/base.css.

   Shared by every project page, not written for Crabshakk alone. Nothing here
   invents a type size, a colour or a spacing unit — every value is one already
   in use on work.html or about.html, so a case study reads as another room in
   the same building rather than as a second website.

   The page is four things in order: a hero, the project described in one
   block, the printed work in one row, and the photographs three to a row.
   There are no section headings — the pictures are already grouped by what
   they are, and a word over each group only said what was visible.
   ========================================================================== */

/* Same white ground as the work index, re-pointed the same way, so the shared
   header, nav, footer and hover rules invert for free. */
:root {
  --bg: #ffffff;
  --bg-lift: #f1f0ec;                    /* image box before it loads      */
  --ink: #0d0d0d;
  --ink-dim: rgba(13, 13, 13, 0.55);
  --ink-faint: rgba(13, 13, 13, 0.34);
  --hairline: rgba(13, 13, 13, 0.14);

  --pad-x: 96px;               /* page gutter inside the 1440 canvas       */
  --col: 82px;                 /* 12 columns + 11 × 24px gutters = 1248px  */
  --gut: 24px;

  /* Two spacing steps, not five. --sec is the silence between one idea and
     the next; --row is the smaller pause inside one. 168 is the work index's
     own row gap, and 200 is the only value on the page that is larger than
     anything there — a case study earns more air than a contact sheet. */
  --sec: 200px;
  --row: 168px;

  /* the parallax travel from the work index, unchanged: an image drifts from
     +16px to -16px inside its frame across a full pass through the viewport */
  --travel: 32px;
}

/* dark text on white wants the opposite smoothing from the hero */
/* clip, not hidden — see the note on #shell below */
body {
  -webkit-font-smoothing: auto;
  -moz-osx-font-smoothing: auto;
  overflow-x: clip;
  overflow-y: auto;
}

/* --------------------------------------------------------------------------
   1440 canvas that scales down but keeps scrolling — identical to work.css
   -------------------------------------------------------------------------- */

/* clip rather than the hidden the other pages use, and this is not cosmetic.
   overflow: hidden makes an element a scroll container, and a scroll container
   becomes the scrollport that any sticky descendant is measured against — so
   the reel's sticky frame had no scroll range of its own and simply travelled
   up the page with the section. overflow: clip cuts the same overflow off
   without creating a scroll container, which leaves the window as the
   scrollport where the reel expects it. */
#shell {
  position: relative;
  width: 100%;
  overflow-x: clip;
}

#page {
  position: relative;
  width: var(--design-w);
  transform-origin: 0 0;
  padding: 0 var(--pad-x) 0;
}

/* --------------------------------------------------------------------------
   The frame — one component, every picture on the page

   Lifted wholesale from .project__frame + .project__img on the work index,
   because the parallax has to behave identically or the two pages would drift
   apart in the one way a visitor actually notices: how things move.

   The image is taller than its frame by the full --travel and starts half a
   travel high, so it can slide the whole range without ever exposing an edge.
   transform is left alone for js/project.js to drive.

   --ar is always the source photograph's own ratio, never a shape chosen for
   the layout. Every Crabshakk original is portrait, so every frame on this
   page is portrait: a vertical picture squeezed into a horizontal box is not a
   crop, it is a different photograph.
   -------------------------------------------------------------------------- */

.f {
  position: relative;
  display: block;
  margin: 0;
  overflow: hidden;
  aspect-ratio: var(--ar, 3 / 4);
  background: var(--bg-lift);
}

.f > img,
.f > video {
  position: absolute;
  left: 0;
  top: calc(var(--travel) / -2);
  width: 100%;
  height: calc(100% + var(--travel));
  object-fit: cover;
  transform: translate3d(0, var(--shift, 0px), 0);
  will-change: transform;
}

/* --------------------------------------------------------------------------
   The project, in one block

   .bio__body p from about.css, set bold and run across the full 1248 as a
   single piece — the whole description, before any picture, rather than
   fragments threaded between photographs. One paragraph, one measure, the
   full width of the page.
   -------------------------------------------------------------------------- */

.pr-say { padding-top: 168px; }

.pr-copy {
  margin: 0;
  font-size: 17px;
  font-weight: 600;
  line-height: 1.62;
  color: var(--ink);
}

/* a project whose copy arrived as more than one paragraph keeps the break.
   22px is .bio__body p's own gap on the About page. */
.pr-copy + .pr-copy { margin-top: 22px; }

/* .project__sub from work.css */
.pr-label {
  margin: 0;
  font-family: var(--font-nav);
  font-size: 16px;
  font-weight: 400;
  letter-spacing: 0.04em;
  line-height: 1.4;
  text-transform: uppercase;
  color: var(--ink-dim);
}

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

   900px, which is what "full screen" means everywhere else here: it is the
   home page's canvas height exactly, so the hero fills a 1080p window the same
   way index.html does rather than guessing at a viewport it cannot measure.
   -------------------------------------------------------------------------- */

.pr-hero {
  position: relative;
  height: 900px;
  display: grid;
  grid-template-columns: repeat(12, var(--col));
  column-gap: var(--gut);
  align-content: end;
  padding-bottom: 96px;
}

.pr-hero__meta {
  grid-column: 1 / span 6;
  align-self: end;
  /* clears the descender of the frame beside it so the two read as level */
  padding-bottom: 4px;
}

/* .intro__title's 56px, but tracked like .project__title because this one is
   set in caps and Unbounded's caps close up badly at -0.01em */
.pr-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 56px;
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.pr-year {
  margin: 26px 0 0;
  font-family: var(--font-nav);
  font-size: 16px;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.pr-cats {
  margin: 34px 0 0;
  padding: 0;
  list-style: none;
}

.pr-cats li {
  font-family: var(--font-nav);
  font-size: 16px;
  letter-spacing: 0.04em;
  line-height: 1.75;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* Five columns, not six, because the height has to come from the photograph.
   At six the frame is 612 wide, and 612 at the source's own 3:4 is 816 tall —
   which leaves 84px of the 900 block for the header that sits over it, and the
   nav would land on the picture. At five it is 506 × 675, and the 129px above
   clears the 88px header outright. The frame's height is never a number; it is
   the width times the ratio. */
.pr-hero__frame {
  grid-column: 8 / span 5;
  align-self: end;
}

/* Six columns, for a hero that is squarer than 3:4 and so does not run as tall
   for the width. At 612 a 1:1 frame is 612 high, which still leaves 192 of the
   900 block clear above it — more than the 88 the header needs. */
.pr-hero__frame--wide { grid-column: 7 / span 6; }

/* And a hero that is landscape, which on this site means a printed spread.
   The frame's height is its width times the ratio, and at five columns a 3:2
   spread stands 360 — a hero shorter than the paragraph under it, which reads
   as a thumbnail rather than an opening.

   So the row is regraded rather than the picture stretched: the meta gives up
   two columns and the frame takes them, 4 and 7 instead of 6 and 5. That is
   718 × 510 on Rangers, with 293 of the 900 block still clear above it. The
   meta's 400px is checked, not assumed — RANGERS is the longest word either
   title sets, and Unbounded takes it to 337 at the title's 56px. */
.pr-hero--land .pr-hero__meta { grid-column: 1 / span 4; }
.pr-hero--land .pr-hero__frame { grid-column: 6 / span 7; }

/* And the opposite case. Below the 88px header and above the 96px foot there
   are 716px of the block free; a negative taller than 3:4 spends more than
   that on five columns — Palace's 1 : 1.51 comes to 763 at 506 wide, and the
   nav would have landed on the photograph.

   Sizing it down costs nothing: the picture is smaller, never cropped, and
   justify-self keeps it against the right margin once its width is no longer
   the column's.

   The width is computed rather than the height capped. max-height plus
   width: auto looks like the tidy answer and collapses the frame to nothing —
   the figure's only child is absolutely positioned, so its fit-content width
   is zero and the ratio has no basis to work from. Given a definite width the
   height falls out of aspect-ratio the way it does everywhere else, and calc
   takes "1012 / 1526" straight out of --ar. */
.pr-hero__frame--tall {
  justify-self: end;
  width: calc(716px * var(--ar));
}

/* The slow zoom. It is a separate transform from the parallax --shift so the
   two can be driven independently; js/project.js only ever writes the two
   custom properties and never the transform itself. 1.08 down to 1.00 across
   one screen of scrolling is about half a pixel a frame — you notice it the
   way you notice a room getting lighter, which is the whole point. */
.pr-hero__frame > img {
  transform: translate3d(0, var(--shift, 0px), 0) scale(var(--zoom, 1.08));
  transform-origin: 50% 50%;
}

.pr-scroll {
  position: absolute;
  left: 0;
  bottom: 30px;
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-nav);
  font-size: 16px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  transition: color 0.35s ease;
}

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

/* a hairline that grows and retreats, rather than an arrow that bounces */
.pr-scroll__line {
  display: block;
  width: 64px;
  height: 1px;
  background: currentColor;
  transform-origin: 0 50%;
  animation: pr-scroll-run 2.6s cubic-bezier(0.16, 1, 0.3, 1) infinite;
}

@keyframes pr-scroll-run {
  0%        { transform: scaleX(0.15); }
  55%, 100% { transform: scaleX(1); }
}

/* --------------------------------------------------------------------------
   Timeline — a rail down the right-hand edge

   2023 at the top, 2026 at the bottom, and a dot carrying the page's scroll
   progress between them. Vertical and fixed, so it is on screen for the whole
   of a page this long rather than scrolling away in the first few seconds.

   It lives OUTSIDE #page, directly in the body. Everything inside the canvas
   is scaled by fit(), and a fixed element inside a transformed ancestor is
   positioned against that ancestor rather than the window — so the one thing
   on the page that must sit against the real viewport is the one thing that is
   not in the canvas. That also means its 16px is a real 16px at every window
   width, which is what you want from a marginal mark.

   Centred vertically, which is also what keeps it clear of the nav.
   -------------------------------------------------------------------------- */

.pr-rail {
  position: fixed;
  z-index: 50;
  right: 34px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  pointer-events: none;
}

/* vertical-rl turns the line, and the 180° flip sets it reading bottom-to-top,
   which is the way a spine reads and the way the eye expects a rail to run */
.pr-rail__year {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: var(--font-nav);
  font-size: 16px;
  letter-spacing: 0.14em;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.pr-rail__track {
  position: relative;
  width: 1px;
  height: 30vh;
  min-height: 160px;
  max-height: 300px;
  background: var(--hairline);
}

/* --p is 0 → 1, written by js/project.js. The calc keeps the dot inside the
   track at both ends instead of hanging half of it off. */
.pr-rail__dot {
  position: absolute;
  left: -2px;
  top: calc(var(--p, 0) * (100% - 5px));
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ink);
}

/* --------------------------------------------------------------------------
   Back — the way out of a case study

   These pages are long. Palace is a hero, four prints and thirty-seven
   negatives; Crabshakk is seven rows of photographs. On a phone the header
   scrolls away in the first second, and after that the only route back to the
   index is the menu — which means opening a panel to find a word. That is a
   thing to hunt for, and hunting is exactly what a visitor two thousand pixels
   into a page will not do.

   So it is fixed, and it is at the bottom: the top-right corner already has the
   menu, the top-left has the wordmark, and the foot of the screen is where a
   thumb already is. It goes to work.html rather than back through history —
   somebody who arrived from a search result has no history to go back through,
   and a control that sometimes leaves the site is worse than one that always
   does the same thing.

   Outside #page for the usual reason: fixed inside a transformed ancestor
   positions against that ancestor. See .pr-rail.

   Phones only. On a wide window the nav is on screen with Work already marked
   as the page you are in, and every case study ends with a card to the next.
   -------------------------------------------------------------------------- */

.pr-back {
  display: none;
  position: fixed;
  z-index: 45;
  left: 18px;
  bottom: 18px;
  align-items: center;
  gap: 10px;
  height: 44px;
  padding: 0 18px 0 14px;
  border: 1px solid var(--hairline);
  background: var(--bg);
  font-family: var(--font-nav);
  font-size: 14px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
}

/* the arrow, pointing the other way from .pr-next__arrow — the two are the
   only two directions this page offers */
.pr-back__arrow {
  display: block;
  font-size: 15px;
  line-height: 1;
}

/* Stands down once the next-project card is on screen. Two reasons, and the
   second is the real one: it was sitting on top of that card's own "View
   project" line, and by the time you are at the foot of the page the way out
   is already there in full size. A fixed control should not outstay the
   content it was standing in for. js/project.js sets the class. */
.pr-back {
  transition: opacity 0.35s ease, transform 0.35s ease;
}

.pr-back.is-away {
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   Rows

   Every group of pictures on the page is one of these: a flex row whose items
   share a height and take their width from the picture's own proportions, so
   the row finishes flush with both margins and nothing is cropped to a shape
   it was not shot in.

   flex-grow does the arithmetic. With flex-basis: 0 each item takes
   ratio / (sum of ratios) of the row, and because .f also carries
   aspect-ratio, its height is width / ratio — which comes out identical for
   every item in the row. That is a justified row, and it costs one number:
   calc(var(--ar)) turns "3 / 4" into 0.75 and "500 / 708" into 0.706, so a
   frame's shape and its share of the row can never drift apart.
   -------------------------------------------------------------------------- */

.pr-rows {
  display: flex;
  flex-direction: column;
  gap: var(--row);
  padding-top: var(--sec);
}

.pr-row {
  display: flex;
  gap: var(--gut);
}

.pr-row > .f {
  flex: calc(var(--ar)) 1 0;
}

/* A print, shown whole.

   The parallax overhang comes off, and this is why posters were being trimmed
   before. .f > img is deliberately 32px taller than its frame so it can drift
   without ever showing an edge, and object-fit: cover pays for that height by
   trimming the sides. On a 900px photograph 32px is nothing. On a 300px poster
   it is a ninth of the height, and cover was taking about 5% off each side —
   straight through the type on LATE NIGHT OYSTERS and through the right-hand
   rule on the Huntleys & Palmers sheet.

   contain rather than cover for the same reason .project__img--whole uses it
   on work.css: at the exact ratio the two are identical, and contain is the
   one that cannot crop if a rounding error ever puts them a pixel apart.

   Losing the drift is not a side effect either. A print is flat artwork, not a
   window — there is nothing behind it for it to move against. Nor is a film
   that was composed to a frame: HND's hero is a video and takes the same rule
   for the same reason, which is why the selector names both. */
.f--whole > img,
.f--whole > video {
  top: 0;
  height: 100%;
  object-fit: contain;
  transform: none;
  will-change: auto;
}

/* A row given a height instead of taking one.

   Crabshakk's five prints use it: at 300 they measure 1048 of the 1248, and
   space-between turns the remaining 200 into four 50px gutters — more air than
   the 24px grid gutter, which is what a row of prints wants. A row of four or
   fewer does not need it, because justified they come out large anyway. */
.pr-row--set { justify-content: space-between; }

.pr-row--set > .f {
  flex: 0 0 auto;
  height: 300px;
  width: auto;
}

.pr-prints { padding-top: var(--sec); }

/* A row that cannot fill: one with two photographs where its neighbours have
   three, or one holding a single upright document. Left to grow they would
   stretch far past their neighbours' height, so here the height is given and
   the width follows the ratio. What is left over is left over — a page built
   on whitespace can end on some.

   --part-h so a row can name its own height. 540 is the Crabshakk pair; an A4
   document on its own wants more, because the whole point of putting it alone
   is that it was too small beside something else. */
.pr-row--part > .f {
  flex: 0 0 auto;
  height: var(--part-h, 540px);
  width: auto;
}

/* A row that cannot fill, held in the middle rather than against the left
   margin. Left-aligned reads as a row that ran out — one sheet with 320px of
   white after it looks like the second one failed to load. Centred, the same
   leftover becomes a margin on both sides and the sheet reads as placed.

   Only for rows that are deliberately short: a justified row has nothing left
   over to centre, so this does nothing there. It survives the phone unchanged,
   where a lone frame goes full width and centring is simply a no-op. */
.pr-row--mid { justify-content: center; }

/* --------------------------------------------------------------------------
   The carousel — a photoshoot you push through sideways

   For a project with a whole roll of film behind it rather than four or five
   finished pieces. Thirty-seven negatives set down the page in rows would be
   most of a kilometre of scrolling; here they run in one line you drag, with a
   chevron at each end for anyone who would rather click than pull.

   It is a native scroll container with snap points, not a JavaScript slider.
   That is the whole design decision: touch gets the platform's own momentum
   and rubber-banding, the keyboard gets arrow keys, Home and End, and a screen
   reader gets a scrollable region it already knows how to announce — none of
   which anybody has to write or maintain. js/project.js adds only the two
   things the platform does not do by itself: dragging with a mouse, and the
   count.
   -------------------------------------------------------------------------- */

.pf-car { margin-top: var(--sec); }

.pf-car__stage { position: relative; }

.pf-car__view {
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  /* a flick past the last frame must not turn into a browser back-swipe */
  overscroll-behavior-x: contain;
  /* the count and the chevrons already say where you are, and a scrollbar
     under a row of photographs is a rule nobody drew */
  scrollbar-width: none;
  -ms-overflow-style: none;
  cursor: grab;
}

.pf-car__view::-webkit-scrollbar { display: none; }

/* Snapping and dragging cannot both be in charge. While a pointer is down the
   snap comes off, so the frames follow the hand exactly; letting go puts it
   back and the nearest frame settles into place. */
.pf-car__view.is-drag {
  cursor: grabbing;
  scroll-snap-type: none;
}

.pf-car__track {
  display: flex;
  gap: var(--gut);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* start, not center: the roll is a mix of portrait and landscape, and with
   frames of different widths a centre snap leaves the first and last with a
   resting place they can never reach. Aligned to the left edge every frame has
   one, and the line reads as a strip rather than as a slideshow. */
.pf-car__cell {
  flex: 0 0 auto;
  height: 620px;
  scroll-snap-align: start;
}

/* Width from the height, so a portrait and a landscape negative stand side by
   side at a common height and neither is cropped to match the other — the same
   rule the rows on every other page are built on.

   max-width undoes the 100% in base.css, which would otherwise squeeze the
   landscape frames back inside the column. */
.pf-car__cell img {
  display: block;
  width: auto;
  height: 100%;
  max-width: none;
  -webkit-user-drag: none;
  user-select: none;
}

/* The chevrons. Two hairline borders on a rotated box — the same 1px the nav
   strike, the footer rule and the burger are drawn with, rather than an icon
   font or an SVG that would be the only one on the site. */
.pf-car__go {
  position: absolute;
  z-index: 3;
  top: 50%;
  width: 44px;
  height: 44px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--hairline);
  background: var(--bg);
  color: var(--ink);
  cursor: pointer;
  transform: translateY(-50%);
  transition: opacity 0.3s ease, border-color 0.3s ease;
}

.pf-car__go--prev { left: 16px; }
.pf-car__go--next { right: 16px; }

.pf-car__go[disabled] {
  opacity: 0.22;
  cursor: default;
}

@media (hover: hover) {
  .pf-car__go:not([disabled]):hover { border-color: var(--ink); }
}

.pf-car__chev {
  display: block;
  width: 9px;
  height: 9px;
  border-left: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
}

/* the corner between the two borders points down-left; a quarter turn either
   way lands it on the horizontal, and the 3px keeps the arrow optically centred
   in the box rather than mathematically centred and looking off */
.pf-car__go--prev .pf-car__chev { transform: rotate(45deg); margin-left: 3px; }
.pf-car__go--next .pf-car__chev { transform: rotate(-135deg); margin-right: 3px; }

/* .project__index's setting — a count, not a control */
.pf-car__count {
  margin: 22px 0 0;
  font-family: var(--font-nav);
  font-size: 16px;
  letter-spacing: 0.14em;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

/* --------------------------------------------------------------------------
   Next project

   The page ends by pointing at the next one. The card is the work index's own
   language — a frame, an index number, a title, a discipline line — at the
   size the end of a page can afford.
   -------------------------------------------------------------------------- */

.pr-next {
  display: block;
  margin-top: var(--sec);
  padding-top: 56px;
  border-top: 1px solid var(--hairline);
}

.pr-next__mark {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  margin: 0 0 40px;
  font-family: var(--font-nav);
  font-size: 16px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.pr-next__card {
  display: grid;
  grid-template-columns: repeat(12, var(--col));
  column-gap: var(--gut);
  align-items: end;
}

.pr-next__frame {
  grid-column: 1 / span 5;
  /* the only frame on the page that answers the pointer, because it is the
     only one that is a link */
  transition: opacity 0.5s ease;
}

.pr-next__words {
  grid-column: 7 / span 6;
  padding-bottom: 6px;
}

/* .cta__link's 46px, minus the underline — the whole card is the target */
.pr-next__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 46px;
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.pr-next__sub { margin-top: 16px; }

.pr-next__go {
  display: inline-block;
  margin-top: 34px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--hairline);
  font-family: var(--font-nav);
  font-size: 16px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-dim);
  transition: border-color 0.4s ease, color 0.4s ease;
}

.pr-next__arrow {
  display: inline-block;
  margin-left: 10px;
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

@media (hover: hover) {
  .pr-next:hover .pr-next__frame { opacity: 0.86; }
  .pr-next:hover .pr-next__go { border-bottom-color: var(--ink); color: var(--ink); }
  .pr-next:hover .pr-next__arrow { transform: translateX(10px); }
}

/* --------------------------------------------------------------------------
   Foot

   The two-line credit stays — it is on every page — but it is the credit, not
   a footer. The thing that used to be the footer's job on a case study is the
   card above it.
   -------------------------------------------------------------------------- */

.site-foot {
  margin-top: 120px;
  padding: 34px 0 40px;
  border-top: 0;
}

/* --------------------------------------------------------------------------
   Phones

   Below the breakpoint the canvas is abandoned and everything becomes one
   column, exactly as the work index does.
   -------------------------------------------------------------------------- */

@media (max-width: 900px) {

  #shell {
    height: auto !important;
    overflow: visible;
  }

  #page {
    width: 100%;
    transform: none !important;
    padding: 0 18px;
  }

  :root {
    --sec: 84px;
    --row: 56px;
  }

  .header {
    position: static;
    width: 100%;
    height: 64px;
    padding: 0;
  }

  .brand { font-size: 12px; }

  /* the nav hands over to the burger — see the note in css/base.css */
  .has-burger .nav { display: none; }
  .has-burger .menu__toggle { display: flex; }
  .has-burger .menu { display: flex; }

  /* the hero stops being a two-column arrangement and becomes a title over a
     picture — 900px of canvas height means nothing here */
  .pr-hero {
    display: block;
    height: auto;
    padding: 24px 0 0;
  }

  /* 36 where it fits, and the width of the longest word where it does not.

     A title wraps between words, so what actually sets the ceiling is the one
     word that cannot break: TECHNOLOGIES, which Unbounded sets at 10.33em with
     this tracking — measured, not estimated, and my estimate was 15% light. At
     a flat 36px it ran 372px inside a 354px column and the last two letters
     were simply cut off.

     Dividing the column by that word turns the ceiling into a rule instead of
     a number, and 1cqw is 1% of the column it is actually set in. 100vw would
     have been the obvious unit and the wrong one: it counts the scrollbar,
     which a phone draws over the page and a desktop browser takes out of it,
     so a window dragged under 900px on Windows would have lost 15px the sum
     never heard about and clipped the word again. 9.6cqw is the column over
     10.4 — the word with a little air — and min() takes whichever is smaller,
     so a 390px phone gets 34, a 360px one 31, a 320px one 27, and no title is
     ever wider than the page it is set on. */
  .pr-hero__meta { container-type: inline-size; }

  .pr-title {
    font-size: min(36px, 9.6cqw);
    letter-spacing: 0.03em;
  }
  .pr-year { margin-top: 16px; }
  .pr-cats { margin-top: 20px; }
  .pr-cats li { font-size: 16px; line-height: 1.6; }

  .pr-hero__frame { margin-top: 28px; }

  /* The width the desktop rule computes from the 716px of clear canvas is a
     real 474px on Palace, and there is no canvas here to scale it down — the
     figure would have run 120px past the edge of the phone. Off the grid the
     hero is simply as wide as the column, like every other picture.

     justify-self has to go with it, the same way about.css hands its two
     columns back. It looks like a grid-only property and no longer is: Chrome
     honours box alignment in block layout now, so `end` survived the switch to
     display: block and turned the figure shrink-to-fit. The figure's only
     child is absolutely positioned, so shrink-to-fit is zero — the hero came
     out 0 × 0, hard against the right margin, and the photograph simply was
     not on the page. */
  .pr-hero__frame--tall {
    width: auto;
    justify-self: auto;
  }

  /* it would be pointing at something already on the screen */
  .pr-scroll { display: none; }

  /* A fixed rail costs a phone a third of its height and gives back a date
     range that is already in the hero. It goes — and the corner it frees is
     nothing like the one the way back needs, which is why they are not the
     same element. */
  .pr-rail { display: none; }

  .pr-back { display: inline-flex; }

  .pr-say { padding-top: var(--sec); }

  .pr-copy { font-size: 18px; }
  .pr-label { font-size: 15px; }

  /* Two across, not three, and not five.

     The rows are justified on the desktop canvas — every frame in a row shares
     a height and takes its width from its own proportions — and that is the
     right answer at 1248px wide. At 354 it is not: three photographs came out
     about 110px each and Crabshakk's five prints about 62, which is a thumbnail
     of a poster, not a poster. Type set at 8pt on the sheet was simply gone.

     So on a phone the row wraps at a fixed half-width instead. Every picture is
     the same width and the two columns line up down the page; the heights vary,
     because the frames still carry their own ratios and nothing here is going
     to crop a portrait to match a landscape. flex-start rather than the default
     stretch is what protects that: stretched to the line's height, a frame
     would take its size from its neighbour instead of from the photograph.

     A last frame with no partner takes the full width rather than sitting half
     empty beside nothing — so a row of three reads as two and then one large,
     and Crabshakk's five prints as two, two, and a full-width sheet. */
  .pr-row {
    flex-wrap: wrap;
    gap: 10px;
    align-items: flex-start;
  }

  .pr-row > .f,
  .pr-row--set > .f,
  .pr-row--part > .f {
    flex: 0 0 calc((100% - 10px) / 2);
    width: auto;
    height: auto;
  }

  .pr-row > .f:last-child:nth-child(odd) { flex-basis: 100%; }

  /* the prints have no fixed height left to space out against */
  .pr-row--set { justify-content: flex-start; }

  /* The reel in real units rather than canvas ones — 900px of sticky box is
     taller than most phones, and 150px a photograph would make a section the
     length of a street. */
  /* On a phone the common height has to go.

     Sharing a height is what every row on this site does, and on the 1248px
     canvas it is right: a portrait and a landscape negative stand level and
     each takes the width its own shape asks for. At 354px it stops working.
     The roll is mixed, and at any height that leaves a portrait frame worth
     looking at, a landscape one is wider than the phone — 56vh put it at 712
     against a 354px screen, so a third of the shoot could only be seen by
     dragging through it.

     So here the frames share a width and let their heights differ. 86% of the
     column, which leaves the next frame showing at the edge — the thing that
     says the strip moves sideways at all — and centred, so a landscape frame
     sits in the middle of the line a portrait one sets. */
  .pf-car__track {
    gap: 10px;
    align-items: center;
  }

  .pf-car__cell {
    width: 86%;
    height: auto;
  }

  .pf-car__cell img {
    width: 100%;
    height: auto;
  }

  /* in from the edge rather than over it: a 44px target under a thumb at the
     very edge of a phone screen is a target the palm keeps hitting */
  .pf-car__go--prev { left: 8px; }
  .pf-car__go--next { right: 8px; }

  .pf-car__count { margin-top: 16px; font-size: 14px; }

  .pr-next { margin-top: var(--sec); padding-top: 34px; }
  .pr-next__mark { display: block; margin-bottom: 22px; font-size: 14px; }

  .pr-next__card { display: block; }
  .pr-next__words { margin-top: 20px; padding-bottom: 0; }
  /* same ceiling as .pr-title, one step down — Katrick is somebody's next
     project too, so the card carries the same unbreakable word */
  .pr-next__words { container-type: inline-size; }
  .pr-next__title { font-size: min(32px, 9.6cqw); }
  .pr-next__go { margin-top: 22px; font-size: 14px; }

  .site-foot { margin-top: 64px; padding: 26px 0 32px; }
}

/* --------------------------------------------------------------------------
   Scroll reveal — the is-in class is set by js/project.js

   The same 28px and the same 0.9s as work.css and about.css. The delays are
   the only addition: a row of three arriving together looks like a page load,
   and arriving one after another looks like a page.
   -------------------------------------------------------------------------- */

.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.9s ease, transform 0.9s cubic-bezier(0.16, 1, 0.3, 1);
}

.reveal.is-in {
  opacity: 1;
  transform: none;
}

.reveal--d1 { transition-delay: 0.1s; }
.reveal--d2 { transition-delay: 0.2s; }
.reveal--d3 { transition-delay: 0.3s; }
.reveal--d4 { transition-delay: 0.4s; }

@media (prefers-reduced-motion: reduce) {
  .reveal,
  .pr-next__frame,
  .pr-next__go,
  .pr-next__arrow,
  .pr-back,
  .pr-scroll { transition: none; }

  .reveal {
    opacity: 1;
    transform: none;
  }

  .reveal--d1,
  .reveal--d2,
  .reveal--d3,
  .reveal--d4 { transition-delay: 0s; }

  /* js/project.js skips the parallax and the zoom too, so the images sit flush */
  .f > img,
  .f > video {
    top: 0;
    height: 100%;
  }

  .pr-hero__frame > img { transform: none; }

  .pr-scroll__line { animation: none; transform: scaleX(1); }

  /* The carousel keeps working — it is a scroll container, and scrolling is
     not an effect. Only the gliding goes: js/project.js jumps to a frame
     instead of easing to it, and the chevrons stop feeling like a ride. */
  .pf-car__go { transition: none; }
}
