/* ------------------------------------------------------------
   SETTINGS: change the look of the whole site from here.
   ------------------------------------------------------------ */
:root {
  /* The width of the film on a project page (and of its details below): half the screen width
     (like the reference), limited so the tallest common shape (4:3) never gets taller than
     67.5% of the screen height; wider films simply become shorter at this width. It lives here
     so the title list can stay clear of it. */
  --w: min(50vw, calc(67.5dvh * 1.3333), calc(100vw - 480px));
  --vw: 100vw;                    /* the window's width and height, as variables so they can glide too (see RESIZING below) */
  --sh: 100dvh;

  /* THE FONT: swap the typeface here, in one place.
     (When you move to Suisse Int'l, you change this line
     and add the font files. Nothing else.) */
  --font: "Roboto Mono", ui-monospace, Menlo, monospace;   /* (version 1 used "Instrument Sans") */
  --font-small: var(--font);   /* the small 11px text: role headers, categories, the film player */
  --font-title: var(--font);   /* the project titles, and the name in the corner */
  --case: uppercase;           /* none = as written; uppercase = ALL CAPS; capitalize = Every Word Capitalised */
  --tracking: 0.04em;          /* extra space between letters (looks better in capitals) */

  --white: #ffffff;
  --black: #000000;
  --grey: #b3b3b3;        /* the ONLY grey on the site: pale, for everything that is not black.
                             (#767676 would be the lightest grey that passes the 4.5:1 rule.) */

  --size: 13px;           /* THE text size: one size for everything on the site (titles, categories,
                             credits, labels, buttons). Hierarchy comes from colour
                             and spacing, never from extra sizes. The one exception is the
                             small size below. */
  --size-small: 10px;     /* the small size: role headers (Director, Colorist ...), the categories
                             in the corner, and everything in the film player ("Loading", time,
                             Unmute, Fullscreen) */
  --size-large: 21px;     /* PHONES ONLY: the project titles, the open project's title and the Info page's contact lines */
  --size-player: var(--size);         /* the film player's text (the time): the same as the main size */
  --line: 1.35;           /* space between lines (lower = tighter) */
  --edge: 16px;           /* space between text and the edge of the screen (the reference keeps it very small) */
  --edge-bottom: 12px;    /* the same distance at the bottom edge: a line of text has some empty space under its letters, so the box sits 4px closer to look equal */
  --caps-spacing: 0.08em; /* breathing room for anything in capitals */

  /* TIMINGS for switching between projects. Everything uses these, so it all
     moves together. script.js reads them from here. Slower = more luxurious. */
  --glide: 900ms;                           /* the video frame and text sliding to their new place */
  --glide-ease: cubic-bezier(0.5, 0, 0.1, 1);   /* soft start, long gentle landing */
  --fade-out: 250ms;                        /* text that changes: fading away */
  --fade-in: 500ms;                         /* text that changes: fading back in */
  --dip-out: 350ms;                         /* changing kind of page (home, project, info): fading to white */
  --dip-in: 550ms;                          /* ... and fading back in with the new page */
  --ratio-glide: 2000ms;                    /* the frame changing shape between projects (the film starts at once; this can be slower) */
  --resize-glide: 1000ms;                   /* the film and page growing or shrinking when the window changes size (like the reference) */
  --resize-ease: cubic-bezier(0.4, 0, 0.2, 1);
  --strip-ms: 800ms;                        /* the sliding film strip (only used by the "slide" hover option): one clip to the next */
  --strip-ease: cubic-bezier(0.5, 0, 0.1, 1);   /* ... soft start, long gentle landing */
  --strip-target: 0;                        /* (set by script.js: which clip rests at the right edge) */
  --white-out: 220ms;                       /* hover "fade through white": the old clip fading out ... */
  --white-in: 280ms;                        /* ... then the new clip fading up (so the whole dip takes both together) */
  --handover: 160ms;                        /* still -> film (paused, static): hides tiny colour differences. 0ms = hard cut */
}

/* ------------------------------------------------------------
   BASE
   ------------------------------------------------------------ */
*,
*::before,
*::after {
  box-sizing: border-box;
  font-weight: 400;       /* regular only. No bold anywhere. */
}

html,
body {
  margin: 0;
  background: var(--white);
}

html {
  -webkit-text-size-adjust: 100%;     /* iPhone and iPad: the text keeps exactly its size when the screen is turned */
  text-size-adjust: 100%;
}

html {
  height: 100%;
}

body {
  min-height: 100%;
  text-transform: var(--case);
  letter-spacing: var(--tracking);
  font-family: var(--font);
  font-size: var(--size);
  line-height: var(--line);
  color: var(--black);
}

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

/* buttons do not inherit these two by themselves */
button {
  text-transform: inherit;
  letter-spacing: inherit;
}

a:focus-visible {
  outline: 1px solid var(--black);
  outline-offset: 3px;
}

ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

p {
  margin: 0;
}

/* Use class="caps" on anything that should be in capitals */
.caps {
  text-transform: uppercase;
  letter-spacing: var(--caps-spacing);
}

/* ------------------------------------------------------------
   HOME: project list (bottom-left) and name block (bottom-right)
   ------------------------------------------------------------ */
.project-list {
  font-family: var(--font-title);
  position: fixed;
  left: var(--edge);
  bottom: var(--edge-bottom);
  max-height: calc(var(--sh) - var(--edge) * 2);
  max-width: calc((var(--vw) - var(--w)) / 2 - var(--edge) - 0.75rem);   /* stays in the margin left of the film: long rows wrap */
  overflow-y: auto;
  color: var(--grey);
}

.project-list a {
  transition: color 0.15s ease;
}

@media (hover: hover) {
  .project-list a:hover {
    color: var(--black);
  }
}

.project-list a:focus-visible,
.project-list a[aria-current="page"],
.project-list a.is-shown,
.project-list a.is-highlighted {
  color: var(--black);
}

/* While a category is highlighted, every title outside it goes grey
   (including the open project), unless you point at it directly */
.project-list.has-highlight a[aria-current="page"]:not(.is-highlighted):not(:hover):not(:focus-visible),
.project-list.has-highlight a.is-shown:not(.is-highlighted):not(:hover):not(:focus-visible) {
  color: var(--grey);
}

/* The dip to white: when you change kind of page, the list and the page fade to
   white, the new page is put in place, and everything fades back in. The name
   block in the corner never dips. */
.project-list,
#page {
  transition: opacity var(--dip-in) ease;
}

html.is-dipping .project-list,
html.is-dipping #page,
html.is-dipping .preview,
html.is-dipping .info-portrait {
  opacity: 0;
  transition: opacity var(--dip-out) ease;
}

/* On the Info page the portrait takes the place of the project list */
html.on-info .project-list {
  visibility: hidden;
}

/* Categories sit under the project titles */
.category-list {
  font-family: var(--font-small);
  display: flex;                  /* one row, like the reference */
  flex-wrap: wrap;
  gap: 0 1.4em;
  margin-top: 1.4rem;
  font-size: var(--size-small);
}

.category-list:empty {
  display: none;                  /* no categories = no gap */
}

.category-list button {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: color 0.15s ease;
}

@media (hover: hover) {
  .category-list button:hover {
    color: var(--black);
  }
}

.category-list button:focus-visible,
.category-list button[aria-pressed="true"],
.category-list button.is-current-category {
  color: var(--black);
}

.category-list button:focus-visible {
  outline: 1px solid var(--black);
  outline-offset: 3px;
}

/* ------------------------------------------------------------
   PROJECT PAGE: scrolls between two resting positions
   1. "hero": the video centred on the screen
   2. "details": credits all black, then stills below
   ------------------------------------------------------------ */

/* Lets the browser animate the film's shape smoothly between projects */
/* RESIZING: --w (the film's width), --vw (the window's width) and --sh (the window's height) are
   registered as lengths, so the browser can glide them to their new value when the window changes
   size, instead of jumping. Everything that depends on the window's size (the film, its control row,
   the credits, the home page's clip, the Info and Imprint columns) uses these three. */
@property --w {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

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

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

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  :root {
    transition: --w var(--resize-glide) var(--resize-ease), --vw var(--resize-glide) var(--resize-ease), --sh var(--resize-glide) var(--resize-ease);
  }
}

@property --ratio {
  syntax: "<number>";
  inherits: true;
  initial-value: 1.3333;
}

/* No browser snapping: the page scrolls freely. The only "magnet" is the focus view at the very top (script.js:
   see the FOCUS VIEW part), which pulls the page to the centred film when you stop scrolling near the top. */
html.on-project {
  scroll-snap-type: none;
}

.project-page {
  --ratio: 1.3333;                /* animates between projects */
  --target-ratio: 1.3333;         /* the open project's shape (changes instantly) */
  --h: calc(var(--w) / var(--ratio));
  transition: --ratio var(--ratio-glide) var(--glide-ease);   /* the frame glides to its new shape */
  --details-top: 15dvh;           /* where the credits rest once you have scrolled */
}

/* Hero: empty space above the video is chosen so the video sits dead centre */
.hero {
  width: var(--w);
  margin-inline: auto;
  padding-top: calc(var(--sh) / 2 - var(--h) / 2);
  scroll-snap-align: start;
}

/* The video area is a mask. Its height glides between projects; the picture inside
   always fills it (see .video-frame). */
.video-stage {
  position: relative;
  overflow: hidden;
  height: var(--h);
}

.video-frame {
  --r: 1.3333;                    /* this video's own shape */
  position: absolute;
  inset: 0 -1px;                  /* 1px more than the stage on each side (the stage clips it), so a
                                     fractional width can never leave a pale hairline at an edge.
                                     Always fills the frame, however its height is changing:
                                     the picture's top and bottom edges move with the frame,
                                     the same way in both directions between any two shapes */
  background: #e8e8e8;            /* opaque: hides a player that is still waiting underneath */
  container-type: inline-size;    /* the control row measures itself against the film's width (cqw), so it keeps the same proportions at any size */
}

.video-frame.has-poster {
  background: transparent;        /* the poster covers the frame */
}

/* The moment the film is really playing: the poster is gone (a cut, not a
   dissolve) and solid black sits behind the film */
.video-frame.is-playing {
  background: #000;
}

.video-frame.is-playing .poster {
  visibility: hidden;
}

/* The handover: the film is paused on the same frame as the still, and the still
   dissolves over a very short moment. Nothing moves, so it cannot read as a ghost;
   it only smooths any tiny colour difference between the still and the film. */
.video-frame.is-handover .poster {
  opacity: 0;
  transition: opacity var(--handover) linear;
}

/* The poster fills the frame; the player sits on top and takes over when ready */
.video-frame .poster {
  position: relative;
  z-index: 2;                     /* above the player: it covers the film while it warms up */
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.video-frame.has-poster iframe {
  position: absolute;
  inset: 0;
  z-index: 1;                     /* fully visible to the browser, so the film really plays,
                                     but covered by the poster until the cut */
}

/* Mux's player: fills the frame under our still. Its own interface is switched
   off completely; our own control row and a click on the film (pause / play) are the interface. */
.video-frame mux-player {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  pointer-events: none;
  --controls: none;
  --media-object-fit: cover;     /* same fit as our still (cover), so while the frame is changing shape the still and the film are the exact same picture */
  --media-background-color: #000;
}

/* Fullscreen: the film keeps its own shape (black bars where the screen is a different shape),
   instead of filling the whole screen the way it does in the changing frame */
.video-frame:fullscreen {
  background: #000;
}

.video-frame:fullscreen mux-player {
  --media-object-fit: contain;
}

.video-frame:fullscreen .poster {
  object-fit: contain;
}

/* (the same, for Safari's older fullscreen names; they must be separate rules) */
.video-frame:-webkit-full-screen {
  background: #000;
}

.video-frame:-webkit-full-screen mux-player {
  --media-object-fit: contain;
}

.video-frame:-webkit-full-screen .poster {
  object-fit: contain;
}

/* A double click on the film must not highlight it blue either */
.video-frame,
.film-bar {
  user-select: none;
  -webkit-user-select: none;
}

/* An invisible button over the whole film: click = pause / play */
.film-button {
  position: absolute;
  inset: 0;
  z-index: 3;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: default;
}

.film-button:focus-visible {
  outline: 1px solid #ffffff;
  outline-offset: -8px;
}

/* The loading ring in the middle, over the still, until the film is moving */
.film-loading {
  font-family: var(--font-small);
  position: absolute;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  color: #ffffff;
  font-size: var(--size-player);
  line-height: 1;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.3s ease;
}

.video-frame.is-playing:not(.is-buffering) .film-loading {
  opacity: 0;
  animation: none;
}

@keyframes film-loading-wait {
  0%, 60% { opacity: 0; }
  100%    { opacity: 1; }
}

/* A complete thin ring (so its round shape and exact middle are always visible) with a bright piece turning
   round it. 5.2% of the film's width across. Its size follows the film's width. */
.film-spinner {
  display: block;
  width: round(clamp(32px, 5.2cqw, 88px), 2px);     /* a whole, even number of pixels */
  height: round(clamp(32px, 5.2cqw, 88px), 2px);
  overflow: visible;
}

/* The control row along the bottom of the film (like the reference): it fades in
   while the pointer is over the film */
.film-bar {
  font-family: var(--font-small);
  position: absolute;
  left: 0;
  right: 0;
  /* Sizes and places measured on the reference player (a film 1236px wide): symbols 3.9% of the film's width, the bar
     0.65%, side margins 4.85%, the row's centre 6.8% of the width above the bottom edge. Then made about 15% smaller and
     a little lower (symbols 3.3%, bar 0.55%, margins 4.1%, centre 5.2% up, never higher than 10% of the film's height,
     so a wide film keeps the row low). Everything follows the film's width. */
  --sym: clamp(19px, 3.6cqw, 52px);        /* the size of a symbol (its drawing is 3/4 of this, the rest is air) */
  bottom: calc(min(5.7cqw, 11%) - 16px);   /* (the row is 32px high: this puts its centre where it should be) */
  height: 32px;
  z-index: 4;
  display: none;
  grid-template-columns: auto minmax(0, 1fr) auto;   /* the time, the bar (takes all the room left), the two symbols */
  align-items: center;
  gap: calc(var(--sym) * 0.5);              /* the space between the time and the bar: the same as between the bar and the sound symbol (see .film-actions) */
  padding: 0 clamp(16px, 4.4cqw, 64px);
  color: #ffffff;
  font-size: var(--size-player);
  line-height: 1;
  opacity: 0;
  transition: opacity 0.5s ease;
  pointer-events: none;
}

.video-frame.is-playing .film-bar {
  display: grid;
}

.video-frame:hover .film-bar,
.video-frame:focus-within .film-bar,
.video-frame:fullscreen .film-bar {
  opacity: 1;
}

/* The mouse rests still over the film (script.js adds is-idle): the control row fades away until the mouse moves
   again. (In fullscreen the pointer disappears too: see below.) */
.video-frame.is-idle .film-bar {
  opacity: 0;
  pointer-events: none;
}

.video-frame.is-idle .film-bar button,
.video-frame.is-idle .film-hit {
  pointer-events: none;
}

/* Fullscreen, film playing and nothing has moved for a moment (script.js adds is-idle): the row fades away
   (the normal fade of the row) and the pointer disappears. Both come back at the next movement. */
.video-frame:fullscreen.is-idle .film-bar {
  opacity: 0;
  pointer-events: none;
}

.video-frame:fullscreen.is-idle,
.video-frame:fullscreen.is-idle .film-button {
  cursor: none;
}

.video-frame:-webkit-full-screen.is-idle .film-bar {
  opacity: 0;
  pointer-events: none;
}

.video-frame:-webkit-full-screen.is-idle,
.video-frame:-webkit-full-screen.is-idle .film-button {
  cursor: none;
}

.video-frame:fullscreen.is-idle .film-bar button,
.video-frame:fullscreen.is-idle .film-hit,
.video-frame:-webkit-full-screen.is-idle .film-bar button,
.video-frame:-webkit-full-screen.is-idle .film-hit {
  pointer-events: none;
}

/* FULLSCREEN: the control row keeps exactly the sizes and places it has in the page (script.js writes them
   down just before: symbols, bar thickness, time, margins, height above the bottom). The only thing that changes
   is that the bar gets longer, because the row spans the film's own picture on the screen (which keeps its
   shape, with black bars round it). (Escape and a double click on the film also leave fullscreen.) The two
   blocks are the same, for Safari's older name. */
.video-frame:fullscreen .film-bar {
  --fw: min(100vw, calc(100vh * var(--r)));   /* the film's own picture on the screen (it keeps its shape) ... */
  --fh: calc(var(--fw) / var(--r));
  left: calc((100vw - var(--fw)) / 2);        /* ... and the row sits inside that picture, not at the screen's edges */
  right: calc((100vw - var(--fw)) / 2);
  --sym: var(--keep-sym, 42px);               /* the sizes written down by script.js just before: exactly as in the page */
  bottom: calc((100vh - var(--fh)) / 2 + var(--keep-bottom, 40px));
  padding: 0 var(--keep-pad, 53px);
  font-size: var(--keep-font, var(--size));
}

.video-frame:fullscreen .film-line {
  height: var(--keep-line, 7px);
}

.video-frame:-webkit-full-screen .film-bar {
  --fw: min(100vw, calc(100vh * var(--r)));   /* the film's own picture on the screen (it keeps its shape) ... */
  --fh: calc(var(--fw) / var(--r));
  left: calc((100vw - var(--fw)) / 2);        /* ... and the row sits inside that picture, not at the screen's edges */
  right: calc((100vw - var(--fw)) / 2);
  --sym: var(--keep-sym, 42px);               /* the sizes written down by script.js just before: exactly as in the page */
  bottom: calc((100vh - var(--fh)) / 2 + var(--keep-bottom, 40px));
  padding: 0 var(--keep-pad, 53px);
  font-size: var(--keep-font, var(--size));
}

.video-frame:-webkit-full-screen .film-line {
  height: var(--keep-line, 7px);
}

/* Fullscreen: the row follows the picture that the player really has (its own width and height), not the size of the
   screen: on an iPad the player is a little smaller than the screen (the clock and the home bar take room), and measuring
   the screen put the ends of the row out in the black bars. (Older browsers keep the rules above.) */
@supports (height: 1cqh) {
  .video-frame:fullscreen {
    container-type: size;
  }

  .video-frame:-webkit-full-screen {
    container-type: size;
  }

  .video-frame:fullscreen .film-bar {
    --fw: min(100cqw, calc(100cqh * var(--r)));
    --fh: calc(var(--fw) / var(--r));
    left: calc((100cqw - var(--fw)) / 2);
    right: calc((100cqw - var(--fw)) / 2);
    bottom: calc((100cqh - var(--fh)) / 2 + var(--keep-bottom, 40px));
  }

  .video-frame:-webkit-full-screen .film-bar {
    --fw: min(100cqw, calc(100cqh * var(--r)));
    --fh: calc(var(--fw) / var(--r));
    left: calc((100cqw - var(--fw)) / 2);
    right: calc((100cqw - var(--fw)) / 2);
    bottom: calc((100cqh - var(--fh)) / 2 + var(--keep-bottom, 40px));
  }
}

.film-bar button {
  padding: 10px;                  /* a big area to press ... */
  margin: -10px;                  /* ... that does not change the layout */
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  pointer-events: auto;
}

@media (hover: hover) {
  .film-bar button:hover {
    color: #bbbbbb;
  }
}

.film-bar button:focus-visible {
  color: #bbbbbb;
}

.film-bar button:focus-visible {
  outline: 1px solid #ffffff;
  outline-offset: 3px;
}

.film-time {
  display: flex;
  justify-self: start;                    /* the time: in the left zone */
  white-space: nowrap;
}

.film-restart {
  display: none;
}

@media (hover: hover) {
  .film-time:hover .film-elapsed {
    display: none;
  }

  .film-time:hover .film-restart {
    display: inline;
  }
}

.film-colon {
  animation: film-pulse 1.5s ease-in-out infinite;
}

@keyframes film-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

.film-sep {
  padding: 0 0.25em;
}

.film-track {
  position: relative;
  height: 100%;                           /* the bar: in the middle zone */
}

.film-line {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: clamp(3px, 0.65cqw, 12px);
  transform: translateY(-50%);
  border-radius: 6px;                     /* a pill: fully rounded ends */
  overflow: hidden;
  background: rgba(255, 255, 255, 0.25);  /* the part still to come: darker grey ... */
}

.film-buffered,
.film-played {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0;
  transition: width 0.25s linear;
}

.film-buffered {
  background: rgba(255, 255, 255, 0.55);  /* ... what has already loaded: lighter grey ... */
}

.film-played {
  background: #ffffff;                    /* ... and what has played: white */
}

.film-head {
  display: none;                          /* (no marker: the white end of the played part shows where you are) */
}

.film-hit {
  position: absolute;
  left: 0;
  right: 0;
  top: -8px;
  bottom: -8px;
  cursor: pointer;
  pointer-events: auto;
}

.film-hit:focus-visible {
  outline: 1px solid #ffffff;
  outline-offset: 2px;
}

.film-time .film-sep,
.film-time .film-total {
  display: none;                          /* only the elapsed time, so the bar can run long between the time and the symbols */
}

.film-actions {
  display: flex;
  justify-self: end;                      /* the symbols: in the right zone */
  gap: calc(var(--sym) * 0.25);
  margin: 0 calc(var(--sym) * -0.125);       /* the drawings have air round them: this puts their edges exactly at the margin and exactly the same distance from the bar as the time is */
  white-space: nowrap;
}

.film-bar button svg {
  display: block;                         /* the symbols (play, speaker, frame) are drawn in the text colour */
  width: var(--sym);
  height: var(--sym);
}


/* Play button over the poster, shown until you press it */
.play-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.play-overlay svg {
  width: 34px;
  height: 34px;
  fill: #ffffff;
  transition: transform 0.2s ease;
}

@media (hover: hover) {
  .play-overlay:hover svg {
    transform: scale(1.1);
  }
}

.play-overlay:focus-visible {
  outline: 1px solid #ffffff;
  outline-offset: -8px;
}

.video-frame.is-entering {
  opacity: 0;
}

.video-frame.is-entering.is-visible {
  opacity: 1;                     /* takes over at once: no fade */
}

.video-frame iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

.video-frame.placeholder {
  font-family: var(--font-small);
  display: grid;
  place-items: center;
  color: var(--grey);
  font-size: var(--size-player);
}

/* Details: credits, then stills. Same width as the video. */
.details {
  position: relative;
  width: var(--w);
  margin: 1.75rem auto 0;
  min-height: calc(var(--sh) - var(--details-top));
  /* extra space at the end lives INSIDE this block, so every scroll position
     down to the very bottom counts as a valid resting place for the snapping */
  padding-bottom: 20dvh;
  scroll-snap-align: start;
  scroll-margin-top: var(--details-top);
  font-size: var(--size);
  container-type: inline-size;    /* lets the credits below react to how wide THIS area is */
}

/* New video and credits fade in when you switch projects */
.fade-in {
  animation: fade-in var(--fade-in) ease var(--fade-out) both;
}

@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .project-page {
    transition: none;
  }
  .fade-in {
    animation: none;
  }
}

/* Grey text that turns black once you have scrolled (class set by script.js) */
.dim {
  color: var(--grey);
  transition: color 0.4s ease;
}

.is-expanded .dim {
  color: var(--black);
}

/* "more info" / "less info" */
.toggle-info {
  position: absolute;
  z-index: 1;                     /* stays above the credits so it can be clicked */
  top: 0;
  right: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--black);
  cursor: pointer;
}

.toggle-info:focus-visible {
  outline: 1px solid var(--black);
  outline-offset: 3px;
}

/* Credits.
   One grid with shared rows: in the left column the title (with category and year) is row 1, then Aspect Ratio is
   row 2 and Format row 3. The right area has two columns of credits that run down the SAME rows, so every Aspect
   Ratio / Format sits level with a row of credits (script.js sets --rows). */
.credits {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 2fr;
  grid-template-rows: repeat(var(--rows, 3), auto);
  column-gap: 1.5rem;
  row-gap: 1.1rem;
}

.credits-left {
  display: contents;               /* the title and the two facts become cells of the grid above */
}

.credit-title {
  grid-column: 1;
  grid-row: 1;
}

.credits .credits-left dl {
  display: contents;
  margin: 0;
}

.credits-left dl > div {
  grid-column: 1;
}

.credit-title span {
  display: block;
}

/* the title role: the project's title line and the name in the corner */
.credit-title span:first-child,
.identity p {
  font-family: var(--font-title);
}

/* containers whose items can be lifted out while they fade away */
.credit-title,
.credits-left dl,
.credits-people {
  position: relative;
}

.is-leaving {
  position: absolute;
  margin: 0;
  pointer-events: none;
}

/* text that is changing fades out, then back in with the new words */
.credit-title span {
  transition: opacity var(--fade-in) ease, color 0.4s ease;
}

.credits dd {
  transition: opacity var(--fade-in) ease;
}

.credit-title span.is-fading {
  transition: opacity var(--fade-out) ease, color 0.4s ease;
}

.credits dd.is-fading {
  transition: opacity var(--fade-out) ease;
}

/* every credit eases between grey and black, even when its grey/black state changes */
.credits-left dl > div,
.credits-people > div {
  transition: color 0.4s ease;
}

.is-fading {
  opacity: 0;
}

.credits dl {
  margin: 0;
}

.credits-people {
  grid-column: 2;
  grid-row: 1 / span var(--rows, 3);
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: subgrid;     /* the same rows as the left column */
  column-gap: 2rem;
  padding-right: 6rem;            /* keeps names clear of "more info" */
}

.credits dt {
  font-family: var(--font-small);
  font-size: var(--size-small);
}

.credits dd {
  margin: 0;
}

/* When the details area is narrow (a small desktop window, or wide letters), the credits
   switch to the compact arrangement: title on top, then two columns */
@container (max-width: 640px) {
  .credits {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    gap: 1.25rem;
  }

  .credit-title {
    grid-row: auto;
    padding-right: 7rem;          /* keeps the title clear of "more info" */
  }

  .credits .credits-left dl {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.9rem 1.25rem;
    margin-top: 0;
    order: 2;                     /* same priority as the wide layout: title, credits, then the facts */
  }

  .credits-left dl > div {
    grid-column: auto;
  }

  .credits-people {
    grid-column: 1;
    grid-row: auto;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto;
    gap: 0.9rem 1.25rem;
    padding-right: 0;
  }

  .stills,
  .process {
    grid-template-columns: 1fr;
    gap: 0.8rem;
  }
}

/* Stills: rows of screengrabs, label on the left like the credits */
.stills {
  display: grid;
  grid-template-columns: 1fr 2fr;       /* the same columns as the credits */
  gap: 1.5rem;
  margin-top: 10rem;
}

.stills-grid {
  display: grid;
  gap: 1.5rem;
}

.stills-grid img {
  display: block;
  width: 100%;
  height: auto;
  /* reserve the film's shape until the picture has loaded, so nothing jumps */
  aspect-ratio: auto var(--target-ratio);
}

.still-placeholder {
  aspect-ratio: var(--target-ratio);
  background: #e8e8e8;
}

/* Process: under the stills, the text on the left and the picture(s) on the right: the same columns as the stills */
.process {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 1.5rem;
  align-items: start;
  margin-top: 10rem;
}

.process-media {
  display: grid;
  gap: 1.5rem;
}

.process-media img {
  display: block;
  width: 100%;
  height: auto;
}

.process-video {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: #e8e8e8;
}

.process-box {
  aspect-ratio: 16 / 9;           /* 16:9, until the real picture is there */
  background: #e8e8e8;
}

.process-text p + p {
  margin-top: 0.9rem;
}

.process-text .dim + p {
  margin-top: 0.3rem;             /* the label sits close above its text */
}

/* THE FILM STRIP. Every preview sits on ONE belt, and the whole belt slides as a
   single rigid piece, so the gap between neighbouring clips never changes and
   they can never overlap.
   --strip-target is where the belt is heading (2 = clip number 2 rests at the right
   edge). Clip number i sits (i - target) screen widths from its resting spot, so the
   next clip is always exactly one screen width behind the previous one.
   Each clip slides with its own transform transition: the browser runs that on the
   graphics card, off the main thread, so it stays smooth. All clips always get the
   same duration and curve at the same moment, which keeps the belt rigid. */
.preview {
  --i: 0;                         /* this clip's place on the belt (set by script.js) */
  position: fixed;
  right: 0;                       /* the right edge touches the screen's edge, fully (no margin) */
  top: calc(var(--sh) / 2);       /* the middle of the window's height (a variable, so it glides when the window is resized) */
  transform: translate(calc((var(--i) - var(--strip-target)) * 100vw), -50%);
  transition: transform var(--strip-ms) var(--strip-ease);
  will-change: transform;
  /* its left edge is at the middle of the page and its right edge on the screen's edge, whatever the
     shape (the height follows from the shape) ... unless the window is so short that the clip
     would not fit: then it shrinks to fit */
  width: min(calc(var(--vw) / 2), calc((var(--sh) - 9rem) * var(--ratio)));
  overflow: hidden;
  aspect-ratio: var(--ratio);
  pointer-events: none;
  visibility: hidden;             /* only the clips passing through are shown */
}

.preview.is-on-belt {
  visibility: visible;
}

/* The clip resting at the right edge can be pressed to open its project */
.preview.is-current {
  pointer-events: auto;
}

/* HOVER TRANSITIONS other than the sliding strip: every clip sits in the same place (the
   right half of the screen) and only the one on show is visible. The new clip is "is-on";
   the one it replaces stays underneath as "is-out" until it has taken over. */
html:not([data-hover="slide"]) .preview {
  transform: translateY(-50%);
  transition: none;
  visibility: hidden;
  z-index: 1;
}

html:not([data-hover="slide"]) .preview.is-out {
  visibility: visible;
}

html:not([data-hover="slide"]) .preview.is-on {
  visibility: visible;
  z-index: 2;
}

html[data-hover="fade"] .preview.is-on {
  animation: hover-fade-in 180ms ease backwards;
}

html[data-hover="white"] .preview.is-out {
  animation: hover-fade-out var(--white-out) ease both;
}

html[data-hover="white"] .preview.is-on {
  animation: hover-fade-in var(--white-in) ease var(--white-out) backwards;
}

html[data-hover="wipe"] .preview.is-on {
  animation: hover-wipe 320ms var(--glide-ease) backwards;
}

html[data-hover="rise"] .preview.is-on {
  animation: hover-rise 320ms var(--glide-ease) backwards;
}

html[data-hover="settle"] .preview.is-on {
  animation: hover-settle 250ms ease backwards;
}

@keyframes hover-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes hover-fade-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

@keyframes hover-wipe {
  from { clip-path: inset(0 0 0 100%); }
  to   { clip-path: inset(0 0 0 0); }
}

@keyframes hover-rise {
  from { clip-path: inset(100% 0 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

@keyframes hover-settle {
  from { opacity: 0; transform: translateY(-50%) scale(0.97); }
  to   { opacity: 1; transform: translateY(-50%) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  html[data-hover] .preview.is-on,
  html[data-hover] .preview.is-out {
    animation: none;
  }
}

.preview-link {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: block;
  cursor: pointer;
}

.preview.is-fading {
  opacity: 0;
  transition: opacity 150ms ease;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --strip-ms: 1ms;
  }
}

.preview video,
.preview img {
  position: absolute;
  display: block;
  inset: 0 -1px;                  /* 1px more than the box on each side (the box clips it), so a fractional
                                     width can never leave a pale hairline at an edge */
  width: calc(100% + 2px);
  height: 100%;
  object-fit: cover;
}

.preview.placeholder {
  background: #e8e8e8;
}

.identity {
  position: fixed;
  right: var(--edge);
  bottom: var(--edge-bottom);
  text-align: right;
}

/* Hover tone for the name/role button and for Info.
   Pointing at either the name or the role lights up both. */
.identity a {
  transition: color 0.2s ease;
  -webkit-tap-highlight-color: transparent;   /* no grey tap rectangle on a phone (it showed the name and the role as two buttons) */
}

/* pressing the name or the role lights up both, like hovering (this is what shows on a phone) */
.identity:has(a.home:active) a.home {
  color: var(--grey);
}

@media (hover: hover) {
  .identity a.info-link:hover,
  .identity a.home:hover,
  .identity:has(a.home:hover) a.home {
    color: var(--grey);           /* hover: black goes pale */
  }
}

.identity a.info-link:focus-visible,
.identity a.home:focus-visible,
.identity:has(a.home:focus-visible) a.home {
  color: var(--grey);
}

/* Second line: "Info" on the left, role on the right */
.identity .second-line {
  display: flex;
  justify-content: flex-end;
  gap: 5em;
}

/* ------------------------------------------------------------
   INFO PAGE: portrait bottom-left, a small text column in the middle
   ------------------------------------------------------------ */
.info-portrait {
  display: block;
  align-self: flex-start;             /* its own width, at the left of the column */
  width: clamp(72px, 7vw, 104px);
  cursor: pointer;                    /* pressing it goes back to the home page */
  transition: opacity var(--dip-in) ease;
}

.info-portrait img,
.info-portrait-placeholder {
  display: block;
  width: 100%;
  height: auto;
}

/* the grey stand-in until there is a photo */
.info-portrait-placeholder {
  aspect-ratio: 2 / 3;
  background: #e8e8e8;
}

.info-portrait:focus-visible {
  outline: 1px solid var(--black);
  outline-offset: 3px;
}

.info-column {
  position: fixed;
  left: calc(var(--vw) / 2);                 /* the middle of the window's width (a variable, so it glides when the window is resized) */
  bottom: var(--edge-bottom);                /* its last line sits on the page margin, like the portrait and the name block */
  width: min(38ch, calc(var(--vw) * 0.4));
  max-height: calc(var(--sh) - var(--edge) * 2);   /* a very short window: the text scrolls instead of running off the top */
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1.6rem;
}

/* the free space goes above the first item, so the column sits on the bottom margin, and when
   it is taller than the window it scrolls with nothing out of reach */
.info-column > :first-child {
  margin-top: auto;
}

.info-column .bio {
  margin-bottom: 0.6rem;
}

.info-column dl {
  margin: 0;
}

.info-column dt,
.info-column .about-label {
  font-family: var(--font-small);
  font-size: var(--size-small);
}

.info-column dd {
  margin: 0;
  font-size: var(--size);
}

.info-column a {
  transition: color 0.2s ease;
}

@media (hover: hover) {
  .info-column a:hover {
    color: var(--grey);           /* hover: black goes pale */
  }
}

.info-column a:focus-visible {
  color: var(--grey);
}

.info-column .muted {
  color: var(--grey);
}

@media (hover: hover) {
  .info-column a.muted:hover {
    color: var(--black);          /* hover: pale goes black */
  }
}

.info-column a.muted:focus-visible {
  color: var(--black);
}

/* "Imprint" at the bottom of the Info column, on the page margin */
.info-column .imprint-link {
  align-self: flex-start;
  margin-top: 1.6rem;
}

/* The Imprint page: the same column, but wider and starting at the page margin
   (like the reference); long text scrolls if the window is very short */
.imprint-page .info-column {
  left: var(--edge);
  width: min(58ch, calc(var(--vw) / 2 - var(--edge)));
  max-height: calc(var(--sh) - var(--edge) * 2);
  overflow-y: auto;
}

/* A very short, wide window (a phone held sideways): the Info column is taller than the window and
   would run behind the name block in the corner, so it sits above it and scrolls */
@media (max-height: 520px) and (min-width: 701px) {
  .info-page:not(.imprint-page) .info-column {
    bottom: calc(var(--edge-bottom) + 3.4rem);
    max-height: calc(var(--sh) - var(--edge) - var(--edge-bottom) - 3.4rem);
  }
}

/* ------------------------------------------------------------
   TOUCH SCREENS (no mouse): there is no hovering, so the film's control row is shown while the film plays (until a
   moment of stillness, then it fades away; a touch brings it back), and the timeline is easier to hit
   ------------------------------------------------------------ */
@media (hover: none) {
  .video-frame.is-playing:not(.is-idle) .film-bar {      /* (it fades away after a moment of stillness, and comes back at a touch: script.js) */
    opacity: 1;
  }

  .film-hit {
    top: -14px;
    bottom: -14px;
    touch-action: pan-y;          /* sideways dragging along the timeline moves the film; swiping up or down still scrolls the page */
  }

  .film-bar button {
    padding: 14px;                /* an even bigger area for a finger */
    margin: -14px;
  }
}

/* A touch screen shows the control row all the time; when the page is scrolled away from the film it goes away at once
   (and comes back when you are back at the film). It stays in fullscreen. */
@media (hover: none) {
  .project-page.is-scrolled .video-frame:not(:fullscreen) .film-bar,
  .project-page.is-expanded .video-frame:not(:fullscreen) .film-bar {
    opacity: 0;
    pointer-events: none;
  }

  .project-page.is-scrolled .video-frame:not(:fullscreen) .film-hit,
  .project-page.is-expanded .video-frame:not(:fullscreen) .film-hit,
  .project-page.is-scrolled .video-frame:not(:fullscreen) .film-bar button,
  .project-page.is-expanded .video-frame:not(:fullscreen) .film-bar button {
    pointer-events: none;
  }
}

/* ------------------------------------------------------------
   PHONES AND UPRIGHT TABLETS (up to 900px wide, held upright; or up to 700px wide held
   sideways), like the reference. Wider screens held sideways use the desktop layout.
   - project titles list at the top left, categories at the top right
   - the clip plays over the titles; the name block sits bottom right with "Info" above it
   - project page: the film runs edge to edge, the credits are one column
   - Info and Imprint pages flow from the top
   Tap a title: its clip comes on show. Tap the title again (or the clip) to open it.
   ------------------------------------------------------------ */
@media (max-width: 900px) and (orientation: portrait), (max-width: 700px) and (orientation: landscape), (orientation: landscape) and (max-height: 500px), (pointer: coarse) {
  /* Changing page on a phone: the same fade as everywhere, but each part has its own moment instead of all at
     once: the titles go first and come back a little before the page, and the clip is quickest. Only opacity
     changes; nothing moves or changes size. (Out = while "is-dipping", in = the rest of the time.) */
  .project-list {
    transition: opacity 420ms ease 100ms;
  }

  #page {
    transition: opacity 520ms ease 220ms;
  }

  html.is-dipping .project-list {
    transition: opacity 200ms ease;
  }

  html.is-dipping #page {
    transition: opacity 260ms ease 70ms;
  }

  html.is-dipping .preview {
    transition: opacity 160ms ease;
  }

  /* Pressing the clip on the home page: the other titles and categories fade away softly just before the clip
     glides into the film (script.js adds "is-leaving-home" for that moment), instead of vanishing at once */
  html.is-leaving-home #project-list li:not(:has(a.is-shown)),
  html.is-leaving-home .category-list button:not(.is-leaving-keep) {
    opacity: 0;
    transition: opacity 170ms ease;
  }

  /* the loading ring waits a moment and then fades in: a film that starts at once never shows it (on a phone only;
     on a computer it is there from the first moment) */
  .film-loading {
    animation: film-loading-wait 1000ms ease backwards;
  }

  /* no flash of any kind when something is pressed */
  * {
    -webkit-tap-highlight-color: transparent;
  }

  /* no "pull down to refresh" (or bounce) at the top of a page on a phone */
  html,
  body {
    overscroll-behavior-y: none;
  }

  /* home: the titles scroll past a fixed spot at the top left (like the reference). The title resting on
     that spot is the selected one: it is black and its clip is on show. The list snaps so one always rests
     exactly there; the empty space at the bottom lets the last title get there too. */
  .project-list {
    top: 0;
    bottom: auto;
    left: 0;
    right: 0;
    box-sizing: border-box;
    height: calc(var(--sh) - 7.5rem);                          /* clear of the name block */
    max-height: none;
    padding: var(--edge) var(--edge) 0;
    scroll-padding-top: var(--edge);                           /* the selection spot */
    scroll-snap-type: y mandatory;
    overscroll-behavior: contain;
    font-size: var(--size-large);
    line-height: 1.12;
    scrollbar-width: none;
  }

  .project-list li {
    scroll-snap-align: start;
  }

  /* empty space after the last title, so that one can also scroll up to the spot */
  #project-list::after {
    content: "";
    display: block;
    height: calc(var(--sh) - 7.5rem);
  }

  html.on-project #project-list::after {
    display: none;
  }

  /* the title on the spot turns black the very moment it gets there (no fade), so you always see which one is selected */
  .project-list a {
    transition: none;
  }

  /* on a project page the list is only the open project's title: no scrolling, and it must not
     cover the film */
  html.on-project .project-list {
    top: var(--edge);
    left: var(--edge);
    right: auto;
    height: auto;
    padding: 0;
    overflow: visible;
    scroll-snap-type: none;
  }

  /* a very narrow phone: a long title wraps onto a second line instead of running into the category at the top right */
  @media (max-width: 400px) {
    html.on-project .project-list {
      max-width: calc(100vw - var(--edge) * 2 - 7rem);
    }
  }

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

  .project-list {
    max-width: none;
  }

  /* the clip does not catch touches: the list under it does, so a swipe anywhere scrolls the list, also one that
     starts on the clip (script.js opens the project when the clip itself is tapped) */
  .preview.is-current {
    pointer-events: none;
  }

  /* The list sits ABOVE the clip, invisible to the eye but catching every touch, so a swipe that starts on the clip
     scrolls the list too (a touch can never be taken by the clip). The part of the list that lies over the clip is
     cut out of what you see (script.js writes where the clip is into --hole-*), so the clip still looks like it
     lies on top of the titles. A mask only changes what is seen, not what is touched. */
  .project-list {
    z-index: 5;
    -webkit-mask-image: linear-gradient(#000 0 0), linear-gradient(#000 0 0);
    -webkit-mask-size: 100% 100%, var(--hole-w, 0px) var(--hole-h, 0px);
    -webkit-mask-position: 0 0, var(--hole-x, 0px) var(--hole-y, 0px);
    -webkit-mask-repeat: no-repeat;
    -webkit-mask-composite: xor;
    mask-image: linear-gradient(#000 0 0), linear-gradient(#000 0 0);
    mask-size: 100% 100%, var(--hole-w, 0px) var(--hole-h, 0px);
    mask-position: 0 0, var(--hole-x, 0px) var(--hole-y, 0px);
    mask-repeat: no-repeat;
    mask-composite: exclude;
  }

  html.on-project .project-list {
    -webkit-mask-image: none;
    mask-image: none;
  }

  /* pressing and holding must not start the phone's own link preview or text selection: it would
     take the touch away from the scrolling */
  .project-list,
  .project-list a,
  .preview {
    -webkit-tap-highlight-color: transparent;     /* no grey flash when a title is tapped */
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
  }

  .project-list a {
    display: inline-block;
    padding: 1px 0;               /* a taller area to tap */
  }

  /* categories: top right, one under the other */
  .category-list {
    position: fixed;
    top: var(--edge);
    right: var(--edge);
    flex-direction: column;
    align-items: flex-end;
    gap: 0;
    margin: 0;
    font-size: var(--size);
    line-height: 1.2;
  }

  .category-list button {
    padding: 2px 0;
    text-align: right;
  }

  /* on a project page the list shrinks to the open project's title and its category */
  html.on-project #project-list li:not(:has(a[aria-current="page"])) {
    display: none;
  }

  html.on-project .category-list button:not(.is-open-category) {
    display: none;
  }

  /* the film's control row: a little less air, so everything fits on a narrow screen */
  .film-bar {
    bottom: 8px;
    padding: 0 16px;
  }

  /* the clip: nearly the full width, flush right, playing over the titles */
  .preview {
    width: min(calc(var(--vw) * 0.88), calc((var(--sh) - 8rem) * var(--ratio)));   /* (a phone held sideways is short: shrink to fit) */
    top: calc(var(--sh) * 0.4);
  }

  /* name block: "Info" on its own line above the name and role */
  .identity {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 0 var(--edge) var(--edge-bottom);
    pointer-events: none;         /* the empty space around the text must not block taps on what is underneath */
  }

  .identity a {
    pointer-events: auto;
  }

  .identity .second-line {
    display: contents;
  }

  .identity .info-link {
    order: -1;
    margin-bottom: 1.4em;
  }

  .identity .second-line .home {
    order: 1;
  }

  /* the stills viewer: the number sits in the middle at the bottom (the cross stays top right) */
  .still-viewer .still-viewer-count {
    right: auto;
    left: 50%;
    transform: translateX(-50%);
    text-align: center;
  }

  /* project page */
  .project-page {
    --w: min(var(--vw), calc(var(--sh) * 0.85 * 1.3333));   /* the film runs edge to edge (a short screen: it shrinks to fit) */
  }

  @media (orientation: landscape) {
    /* the film is the hero: as big as the height allows (room is left below it for "more info" and the name block),
       but not so wide that it reaches the name block's corner */
    .project-page {
      --w: min(calc((var(--sh) - 153px) * 1.3333), calc(var(--vw) * 0.62));
    }

    /* the film and the title block under it are centred in the height as one (so a wider film, which is shorter, sits in
       the middle like on the desktop); a tall 4:3 film has no room to spare and starts 56px from the top, which leaves
       space for the whole title block under it in the focus view */
    .project-page .hero {
      padding-top: max(56px, calc((var(--sh) - var(--h) - 75px) / 2));
    }

    /* the credits, stills and process are exactly as wide as the film and sit under it (like on the desktop) */
    .project-page .details {
      width: var(--w);
      margin-top: 0.9rem;             /* the title block close under the film (it was 1.75rem) */
    }

    /* in the focus view only the most important is under the film: the title, the category and the year (and "more info");
       everything else about the film (the people, the facts) fades in when you scroll or press "more info" */
    html .project-page:not(.is-expanded) .details > .credits {
      opacity: 1 !important;
      pointer-events: auto;
    }

    .project-page .credits-people,
    .project-page .credits-left dl > div {
      transition: opacity 0.3s ease, color 0.4s ease;
    }

    html .project-page:not(.is-expanded) .credits-people,
    html .project-page:not(.is-expanded) .credits-left dl > div {
      opacity: 0;
    }

    /* the title in the normal text size (the same two sizes as everywhere) */
    .project-page .credit-title {
      font-size: var(--size);
      line-height: var(--line);
    }

    /* where there is room (an iPad) the credits have the desktop arrangement: the title and the facts on the left, and
       two columns of people beside them: three columns. A narrow screen (a phone) keeps the stacked one. */
    @container (min-width: 641px) {
      .project-page .credits {
        grid-template-columns: 1fr 2fr;
        grid-template-rows: repeat(var(--rows, 3), auto);
        column-gap: 1.5rem;
        row-gap: 1.1rem;
      }

      .project-page .credit-title {
        grid-row: 1;
        padding-right: 0;
      }

      .project-page .credits .credits-left dl {
        display: contents;
        order: 0;
      }

      .project-page .credits-left dl > div {
        grid-column: 1;
      }

      .project-page .credits-people {
        grid-column: 2;
        grid-row: 1 / span var(--rows, 3);
        grid-template-columns: 1fr 1fr;
        grid-template-rows: subgrid;
        gap: normal 2rem;
        padding-right: 6rem;          /* keeps names clear of "more info" */
      }
    }

    /* the stills (and the process) like on the desktop: the label at the top left and the pictures in the right
       two thirds, so they feel smaller than the film */
    .project-page .stills,
    .project-page .process {
      grid-template-columns: 1fr 2fr;
      gap: 1.5rem;
    }

    /* sideways: the categories lie in a row in the bottom left corner, and "Info" is alone in the top right corner
       (the name and the role stay in the bottom right corner) */
    .category-list {
      top: auto;
      right: auto;
      bottom: var(--edge-bottom);
      left: var(--edge);
      flex-direction: row;
      align-items: center;
      gap: 0 1.4em;
    }

    .category-list button {
      text-align: left;
    }

    .identity .info-link {
      position: fixed;
      top: var(--edge);
      right: var(--edge);
      margin: 0;
    }

    /* the clip sits where it does on the desktop: flush right and in the middle of the height, scaled down so that its
       lower edge stops above the name block (which then has the white page to itself) */
    .preview {
      width: min(calc(var(--vw) * 0.76), calc((var(--sh) - 186px) * var(--ratio)));
      top: calc(var(--sh) / 2);
    }

    /* the list reaches the whole height: the name block lies over it, not below it */
    .project-list {
      height: var(--sh);
    }

    #project-list::after {
      height: calc(var(--sh) - var(--edge) * 2);
    }

    .identity {
      z-index: 6;                       /* above the list (which reaches the whole height) */
    }

  }

  .details {
    width: calc(var(--vw) - var(--edge) * 2);
    padding-bottom: 9rem;         /* room under the last line for the name block */
  }

  /* At rest only the film shows (with "more info" under it). Scrolling down, or pressing
     "more info", snaps to the details, which fade in */
  .details > .credits,
  .details > .stills,
  .details > .process {
    transition: opacity 0.3s ease;
  }

  .project-page:not(.is-expanded) .details > .credits,
  .project-page:not(.is-expanded) .details > .stills,
  .project-page:not(.is-expanded) .details > .process {
    opacity: 0 !important;        /* !important so a fading-in project swap cannot show them early */
    pointer-events: none;
  }

  .credits {
    grid-template-columns: 1fr;
    grid-template-rows: auto;     /* the wide layout's shared rows are not used when stacked */
    gap: 1.25rem;
  }

  /* the title block is the one large line of text; the credits are two columns, like on the web */
  .credit-title {
    grid-row: auto;
    padding-right: 5.5rem;        /* keeps the title clear of "more info" */
    font-size: var(--size-large);
    line-height: 1.15;
  }

  .credits .credits-left dl {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.9rem 1.25rem;
    margin-top: 0;
    order: 2;                     /* same priority as the wide layout: title, credits, then the facts */
  }

  .credits-left dl > div {
    grid-column: auto;
  }

  .credits-left dl > div + div {
    margin-top: 0;
  }

  .credits-people {
    grid-column: 1;
    grid-row: auto;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto;
    gap: 0.9rem 1.25rem;
    padding-right: 0;
  }

  .stills,
  .process {
    grid-template-columns: 1fr;
    gap: 0.8rem;
    margin-top: 4rem;
  }

  /* Info and Imprint: the page flows from the top */
  .info-page {
    display: flex;
    flex-direction: column;
    padding: var(--edge) var(--edge) 9rem;
  }

  .info-portrait {
    width: clamp(72px, 26vw, 120px);
  }

  .info-column,
  .imprint-page .info-column {
    position: static;
    width: auto;
    max-height: none;
    overflow: visible;
    gap: 1.1rem;
  }

  .info-column .bio {
    margin-bottom: 0.4rem;
  }

  .info-column dd {
    /* the big contact lines shrink a little on very narrow screens, so the e-mail address always fits
       (it is the longest line: about 26 letters, each up to 0.65 of the text size wide) */
    font-size: min(var(--size-large), calc((100vw - var(--edge) * 2) / 17.5));
    line-height: 1.12;
    overflow-wrap: anywhere;
  }

  .imprint-page .info-column dd {
    font-size: var(--size);       /* long legal text stays at the normal size */
    line-height: var(--line);
  }

  .info-column .imprint-link {
    margin-top: 1rem;
  }
}

/* AN IPAD HELD UPRIGHT (tablet width, 700px and wider; an iPhone keeps its own): as sideways, "Info" is alone in the top right
   corner and the categories lie in a row in the bottom left corner (the name and the role stay in the bottom right) */
@media (orientation: portrait) and (min-width: 700px) and (max-width: 900px), (orientation: portrait) and (min-width: 700px) and (pointer: coarse) {
  .category-list {
    top: auto;
    right: auto;
    bottom: var(--edge-bottom);
    left: var(--edge);
    flex-direction: row;
    align-items: center;
    gap: 0 1.4em;
  }

  .category-list button {
    text-align: left;
  }

  .identity .info-link {
    position: fixed;
    top: var(--edge);
    right: var(--edge);
    margin: 0;
  }

  .identity {
    z-index: 6;                       /* above the title list, which now covers the whole page */
  }

  /* the title list's box reaches the whole height (as sideways): everything it carries, the categories in the corner too,
     is inside it, and the list's mask would otherwise hide what lies outside; the name block and Info lie above it */
  html:not(.on-project) .project-list {
    height: var(--sh);
  }

  html:not(.on-project) #project-list::after {
    height: calc(var(--sh) - var(--edge) * 2);
  }

  /* the project page as sideways: under the film stand the title, the category and the year (and "more info"); the
     people and the facts fade in when you scroll or press "more info". The film and that title block are centred in
     the height as one. */
  .project-page .hero {
    padding-top: max(56px, calc((var(--sh) - var(--h) - 75px) / 2));
  }

  .project-page .details {
    margin-top: 0.9rem;             /* the title block close under the film */
  }

  html .project-page:not(.is-expanded) .details > .credits {
    opacity: 1 !important;
    pointer-events: auto;
  }

  .project-page .credits-people,
  .project-page .credits-left dl > div {
    transition: opacity 0.3s ease, color 0.4s ease;
  }

  html .project-page:not(.is-expanded) .credits-people,
  html .project-page:not(.is-expanded) .credits-left dl > div {
    opacity: 0;
  }

  /* the title in the normal text size (the same two sizes as everywhere) */
  .project-page .credit-title {
    font-size: var(--size);
    line-height: var(--line);
  }
}

/* INFO AND IMPRINT ON AN IPAD (held either way; an iPhone keeps its own): as on the desktop, a half-screen layout: the text column
   starts at the middle of the screen and its last line sits on the bottom margin; the Imprint page's column starts at the left margin */
@media (min-width: 700px) and (min-height: 600px) and (pointer: coarse), (orientation: portrait) and (min-width: 700px) and (max-width: 900px) {
  .info-page {
    display: block;
    padding: 0;
  }

  .info-portrait {
    width: clamp(72px, 7vw, 104px);
  }

  /* the column is centred in the height (like the film and the home clip are on an iPad): its box reaches from the top margin to the
     bottom margin, and the free space is shared above the first item and under the last one; a taller text scrolls with nothing out of reach */
  .info-column {
    position: fixed;
    left: calc(var(--vw) / 2);
    top: var(--edge);
    bottom: var(--edge-bottom);
    width: min(38ch, calc(var(--vw) * 0.4));
    max-height: none;
    overflow-y: auto;
    gap: 1.6rem;
  }

  .imprint-page .info-column {
    position: fixed;
    left: var(--edge);
    top: var(--edge);
    bottom: var(--edge-bottom);
    width: min(58ch, calc(var(--vw) / 2 - var(--edge)));
    max-height: none;
    overflow-y: auto;
    gap: 1.6rem;
  }

  .info-column > :last-child {
    margin-bottom: auto;
  }

  .info-column .bio {
    margin-bottom: 0.6rem;
  }

  .info-column dd,
  .imprint-page .info-column dd {
    font-size: var(--size);
    line-height: var(--line);
  }

  .info-column .imprint-link {
    margin-top: 1.6rem;
  }
}

/* TOUCH SCREENS: heights that do not change while you scroll. On a phone the browser's address bar slides away as you scroll
   down, and "100dvh" then changes continuously: the film's place and the height of the details would shift under your
   finger. "100svh" is the height with the bars out, which stays put. */
@media (pointer: coarse) {
  :root {
    --sh: calc(100svh - var(--vgap, 0px));   /* (--vgap: see "viewport gap" in script.js; normally 0) */
  }

  /* the parts fixed to the bottom lift by the same gap, so they are never cut off at the screen's edge. (A margin, not a
     "translate": a transformed box becomes the anchor of everything fixed inside it, and "Info" is fixed inside the name block.) */
  .identity,
  .category-list {
    margin-bottom: var(--vgap, 0px);
  }

  .project-page {
    --details-top: 15svh;
  }

  .details {
    padding-bottom: 20svh;
  }
}

/* TOUCH SCREENS (an iPhone, an iPad): the page does not scroll itself. A container that fills the screen (the body) scrolls
   inside it, like on the reference. Safari only slides its toolbar away, and shifts everything under it, when the PAGE scrolls,
   so now the toolbar stays where it is and the page always has exactly the room it is given. (script.js: "THE PAGE SCROLLER".) */
@media (pointer: coarse) {
  html {
    height: 100%;
    overflow: hidden;
  }

  body {
    position: fixed;
    inset: 0;
    min-height: 0;
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: none;        /* no bounce at the ends, no pull to refresh */
  }

  html:not(.on-project):not(.on-info) body,   /* the home page is all fixed parts: nothing to scroll */
  html.viewer-open body {                     /* and the page behind the stills viewer stands still */
    overflow: hidden;
  }

  /* An iPhone with the stills viewer open (script.js adds "viewer-plain"): the page behind is an ordinary page again, because
     only then does Safari paint the strips under its bars black like the viewer (they stay white while an inner container scrolls) */
  html.viewer-plain {
    height: auto;
  }

  html.viewer-plain body {
    position: static;
    inset: auto;
    min-height: 100svh;
    overflow: visible;
  }
}

/* Text that is there for screen readers and search engines, but not shown */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The stills open in the full-screen viewer (script.js) */
.stills-grid img.still {
  cursor: pointer;                 /* the hand: it opens */
}

.stills-grid img.still:focus-visible {
  outline: 1px solid var(--black);
  outline-offset: 3px;
}

html.viewer-open {
  overflow: hidden;                /* the page behind stands still */
  color-scheme: dark;              /* the browser's own backing colour behind the page goes dark too */
}

/* The page behind is black too, so no white can show at the screen's edges, behind a phone's toolbar, or
   in the moment before the first picture has loaded */
html.viewer-open,
html.viewer-open body {
  background: #000;                /* (at once, like the viewer itself: a hard cut, nothing of the page shows through) */
}

.still-viewer {
  position: fixed;
  inset: 0;
  z-index: 2000;                   /* above everything, the name block too */
  background: #000;
  color: #fff;
  font-family: var(--font-small);
  font-size: var(--size);
  line-height: 1;
  touch-action: pan-y;
  outline: none;
  box-shadow: 0 0 0 120px #000;    /* black around the screen's edge too: no hairline of the page, and no strip under or over a phone's toolbars, can show between the viewer and the edge */
}

.still-viewer-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;             /* the whole still, as large as it fits, in its own shape */
  -webkit-user-drag: none;
  user-select: none;
  border: 0;
  outline: 0;
}

.still-viewer-image:not([src]) {
  visibility: hidden;              /* nothing at all (not even an empty picture's edge) until the first still has loaded */
}

/* The mouse pointer in the viewer: a bold chevron, always white (like the Material "chevron": a square bend with
   flat-cut ends and no shaft), pointing left over the left half of the screen (back one still) and right over the
   right half (on one). A real cursor, so it follows the mouse with no delay. */
.still-viewer {
  /* Where the cross and the number sit from the screen's right edge: the same for every still. Just a little in from
     the screen's edge (a fifth of the way to the edge of a 4:3 still, which is where "/ 10" comes from; "/ 2" would
     be all the way to it). On a screen that is not wider than 4:3 it is simply the page margin. */
  --side: calc(var(--edge) + (100vw - min(100vw, 133.333vh)) / 10);
  cursor: default;
  user-select: none;               /* a quick double or triple click must never highlight the picture blue */
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

.still-viewer.is-left {
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32' fill='none'><path d='M20.9 5.8L11.1 16L20.9 26.2' stroke='white' stroke-width='3.4' stroke-linecap='butt' stroke-linejoin='miter'/></svg>") 16 16, w-resize;
}

.still-viewer.is-right {
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32' fill='none'><path d='M11.1 5.8L20.9 16L11.1 26.2' stroke='white' stroke-width='3.4' stroke-linecap='butt' stroke-linejoin='miter'/></svg>") 16 16, e-resize;
}

/* No more stills that way (the first or the last one): a plain pointer, no arrow */
.still-viewer.is-left[data-first],
.still-viewer.is-right[data-last] {
  cursor: default;
}

/* top right: a cross (exit); bottom right: the number. They stay at the screen's right edge (the same for every still). Plain white, the small text size. */
.still-viewer-exit,
.still-viewer-count {
  position: absolute;
  bottom: calc(var(--edge-bottom) + 16px);   /* always the same height, whatever the picture's shape (16px up from the page's bottom margin) */
  text-transform: uppercase;
  font-size: var(--size-small);
}

.still-viewer-exit {
  top: 22px;                       /* the top right corner */
  bottom: auto;
  right: var(--side);
  padding: 10px;                   /* a big area to press, without changing the layout */
  margin: -10px -15px -10px -10px; /* (the cross has air inside its square: this puts its edge on the page's margin) */
  line-height: 0;
  border: 0;
  background: none;
  color: #fff;
  font: inherit;
  font-size: var(--size-small);    /* (after "font: inherit", which would reset it) */
  text-transform: uppercase;
  cursor: pointer;
}

.still-viewer-count {
  right: var(--side);
}

/* an iPad in real fullscreen brings its own "X" to close: ours steps aside, so there is only one cross */
.still-viewer.is-system-fullscreen .still-viewer-exit {
  display: none;
}

/* a touch screen: the number a tiny bit bigger (the normal text size instead of the small one) */
@media (pointer: coarse) {
  .still-viewer .still-viewer-count {
    font-size: var(--size);
  }
}

@media (hover: hover) {
  .still-viewer-exit:hover {
    color: #bbbbbb;                /* the same hover grey as the film player's buttons */
  }
}

.still-viewer-exit:focus-visible {
  color: #bbbbbb;                  /* ... and the same focus: that grey and a 1 px white outline */
  outline: 1px solid #fff;
  outline-offset: 3px;
}


