/*
 * runway.css — the whole site's look: the homepage's fullscreen scroll
 * runway, the Index page, the About page and a hidden project's own page.
 * Plain hand-written CSS, no build step. Behaviour in assets/js/runway.js.
 *
 * THE RUNWAY: .runway is one fixed element covering the viewport, with its
 * OWN scroll container inside it (.runway-scroll). Every panel is exactly
 * one container-height tall; native scroll-snap does the stepping, and
 * runway.js reads scrollTop / clientHeight as the single source of truth for
 * everything else (text cross-fade, the progress line).
 *
 * Below 600px there's no fixed stage at all: runway.js sets `stepped =
 * false` and this becomes a plain, ordinary page — one panel after another
 * in the document's own scroll, no snap-stepping.
 */

/* The site's one typeface, Founders Grotesk: Regular, and Bold — your name,
   the menus and the filters, bold words, project titles. Both are fetched
   early (snippet-head.php), so they're in hand for the first paint. */
@font-face {
	font-family: 'Founders Grotesk';
	font-style: normal;
	font-weight: 400;
	src: url('../fonts/founders-grotesk-regular.woff2') format('woff2');
	font-display: block;
}

@font-face {
	font-family: 'Founders Grotesk';
	font-style: normal;
	font-weight: 700;
	src: url('../fonts/founders-grotesk-bold.woff2') format('woff2');
	font-display: block;
}

html.runway-active {
	overflow: hidden;
}

/* Dark mode is a class on the page itself (html.runway-dark), set before
   anything's drawn (snippet-head.php) and kept up to date by runway.js —
   so a page never first shows in the other theme and then fades across.
   The page behind everything goes dark with it, so there's no white edge
   when a page is pulled past its end. */
html {
	background: #fff;
}

html.runway-dark {
	background: #000;
}

/* No pinch or double-tap zoom on a phone — scrolling still works. (iPhone
   Safari ignores the viewport tag's user-scalable=no, snippet-head.php, but
   goes by this.) */
html {
	touch-action: pan-x pan-y;
}

/* A page brought back into line with the theme chosen elsewhere
   (runway.js) goes straight there, not through the switch's own fade. */
html.runway-theme-sync,
html.runway-theme-sync * {
	transition: none !important;
}

/* Going between the site's pages (all of which load this file),
   the browser fades one into the next rather than cutting — where it can
   (Chrome, Edge, Safari 18.2+); elsewhere it's the usual page load. */
@view-transition {
	navigation: auto;
}

/* The fade itself: through the page's own background rather than a straight
   cross-fade — the old page fades away, then the new one fades up. Switching
   the Index page's filters (runway.js) uses the very same fade. None at all
   with reduced motion. */
@keyframes runway-fade-out {
	to { opacity: 0; }
}

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

::view-transition-old(root) {
	animation: runway-fade-out 0.25s ease-in both;
}

::view-transition-new(root) {
	animation: runway-fade-in 0.35s ease-out 0.25s both;
}

/* What stays put through a fade — the site title, the top-right menu and
   the Index page's filters are the same from page to page, so they're lifted
   out of it (each its own view-transition-name) and simply shown as the new
   page has them, no fade, no movement. On the runway they're drawn with the
   difference blend (.runway-title, .runway-links) — lifted out, they'd lose
   the backdrop it blends against, so it's put back on them below. */
.runway > .runway-title,
.runway-work-header .runway-title {
	view-transition-name: site-title;
}

.runway-links,
.runway-page-links {
	view-transition-name: site-menu;
}

.runway-work-filters {
	view-transition-name: site-filters;
}

/* Each is kept on a layer of its own all along. Otherwise lifting it out
   as a fade starts makes Chrome redraw the strip of the page it sat in (the
   top quarter of the screen), and now and then the old page's picture is
   taken before that's done — the strip blank, a white band over the top of
   the page as it fades away (plain to see in dark mode). */
.runway-title,
.runway-links,
.runway-page-links,
.runway-menu-button,
.runway-work-filters {
	will-change: transform;
}

::view-transition-group(site-title),
::view-transition-group(site-menu),
::view-transition-group(site-filters),
::view-transition-new(site-title),
::view-transition-new(site-menu),
::view-transition-new(site-filters) {
	animation: none;
}

::view-transition-old(site-title),
::view-transition-old(site-menu),
::view-transition-old(site-filters) {
	display: none;
}

/* Each only stays put when both pages have it — the filters going from
   Index to Index (switching filter), say. Where only one page has it (the
   filters arriving at or leaving the Index page, the top-right menu leaving
   the Index slideshow) it fades with the page instead, at its timing. */
::view-transition-new(site-title):only-child,
::view-transition-new(site-menu):only-child,
::view-transition-new(site-filters):only-child {
	animation: runway-fade-in 0.35s ease-out 0.25s both;
}

::view-transition-old(site-title):only-child,
::view-transition-old(site-menu):only-child,
::view-transition-old(site-filters):only-child {
	display: block;
	animation: runway-fade-out 0.25s ease-in both;
}

/* Arriving at the Index page, and switching its filters, its thumbnails are
   left out of the fade in (html.runway-arriving, snippet-head.php and
   runway.js, just while it lasts): the grid shows straight away, its
   thumbnails still hidden, and they fade up by themselves once the old
   page, or the old set, has gone (runway.js) — not faded twice over.
   Leaving the page, the grid fades with it. */
html.runway-arriving .runway-work-grid {
	view-transition-name: site-grid;
}

::view-transition-group(site-grid),
::view-transition-new(site-grid) {
	animation: none;
}

/* With the full-screen slideshow open, the page's own name and menu are
   hidden under it: the slideshow's name top left is the one that stays put
   (it sits exactly where the page's does), and the page's menu and filters
   are left out, so they don't show up over the slideshow as it fades. */
html.runway-viewer-open .runway > .runway-title,
html.runway-viewer-open .runway-work-header .runway-title,
html.runway-viewer-open .runway-links,
html.runway-viewer-open .runway-page-links,
html.runway-viewer-open .runway-menu-button,
html.runway-viewer-open .runway-work-filters {
	view-transition-name: none;
}

/* Likewise under the phone's open menu: the Index page's filters are hidden
   beneath it, so they fade with the page instead of showing up over the
   menu as it goes. */
html.runway-menu-open .runway-work-filters {
	view-transition-name: none;
}

html.runway-viewer-open .runway-viewer .runway-title {
	view-transition-name: site-title;
}

/* The difference blend goes on the group, not the picture inside it: the
   picture sits in its own isolated layer (::view-transition-image-pair), so
   blended there it would only see empty space and come out plain white —
   invisible on a white page until the real title returned. The group sits
   straight over the fading page, as the real title does over the runway. */
html:has(.runway)::view-transition-group(site-title),
html:has(.runway)::view-transition-group(site-menu),
html:has(.runway-work-grid)::view-transition-group(site-title),
html:has(.runway-work-grid)::view-transition-group(site-menu),
html:has(.runway-work-grid)::view-transition-group(site-filters) {
	mix-blend-mode: difference;
}

/* …and what's behind the fading pages is the page colour, not empty space:
   blended against empty space the title comes out white, so between the old
   page fading away and the new one fading up it vanished on a white page
   and came back as the runway appeared. */
::view-transition {
	background: #fff;
}

html.runway-dark::view-transition {
	background: #000;
}

/* ================================================================ first load */

/* Arriving from outside the site (html.runway-arrive: snippet-head.php) the
   text fades in, a little after one another: your name, then the menu and
   filters, then the big text (rising a touch), then the small print. On the
   homepage the opening picture fades in between your name and the big text. Only
   the start is set (backwards), so each ends wherever the page has it —
   including the menu, which is out of sight on the opening slideshow. */
@keyframes runway-arrive {
	from { opacity: 0; }
}

@keyframes runway-arrive-rise {
	from { opacity: 0; transform: translateY(14px); }
}

html.runway-arrive .runway-title {
	animation: runway-arrive 1s ease-out 0.1s backwards;
}

html.runway-arrive .runway-links,
html.runway-arrive .runway-page-links,
html.runway-arrive .runway-menu-button,
html.runway-arrive .runway-work-filters {
	animation: runway-arrive 0.9s ease-out 0.4s backwards;
}

html.runway-arrive .runway-about-clients {
	animation: runway-arrive-rise 1.3s cubic-bezier(0.2, 0.6, 0.2, 1) 0.5s backwards;
}

html.runway-arrive .runway-about-text {
	animation: runway-arrive-rise 1.3s cubic-bezier(0.2, 0.6, 0.2, 1) 0.65s backwards;
}

html.runway-arrive .runway-theme {
	animation: runway-arrive 1s ease-out 1.3s backwards;
}

/* The homepage's opening picture fades in as soon as it has loaded, and the
   big text and the Scroll hint come after it: until then (html.runway-image-
   wait, taken off by runway.js) they're held back, and their fades only
   start once it's off. */
.runway-intro-media,
.runway-panel--intro::after {
	transition: opacity 1.6s ease;
}

html.runway-image-wait .runway-intro-media,
html.runway-image-wait .runway-panel--intro::after {
	opacity: 0;
}

html.runway-image-wait .runway-intro-text,
html.runway-image-wait .runway-hint,
html.runway-image-wait .runway-theme {
	opacity: 0 !important;
}

html.runway-arrive:not(.runway-image-wait) .runway-intro-text {
	animation: runway-arrive-rise 1.3s cubic-bezier(0.2, 0.6, 0.2, 1) 0.7s backwards;
}

html.runway-arrive:not(.runway-image-wait) .runway-hint {
	animation: runway-arrive 1s ease-out 1.5s backwards;
}

@media (prefers-reduced-motion: reduce) {
	::view-transition-old(root),
	::view-transition-new(root),
	::view-transition-old(site-title):only-child,
	::view-transition-new(site-title):only-child,
	::view-transition-old(site-menu):only-child,
	::view-transition-new(site-menu):only-child,
	::view-transition-old(site-filters):only-child,
	::view-transition-new(site-filters):only-child {
		animation: none;
	}
}

/* The browser's own default body margin never shows on the homepage —
   .runway is position: fixed, inset: 0 — but the Index page is a plain,
   normal-flow page, where it would show as a border round everything. */
html,
body {
	margin: 0;
	padding: 0;
}

/* No scrollbar on the Index page's own (document) scroll, same as the
   runway's scroller (.runway-scroll). Otherwise, where the system shows
   classic scrollbars (a Mac with a mouse attached, say), the page first
   draws full width, then a scrollbar appears once enough of it has loaded to
   scroll and everything shrinks to fit — and going between the runway and
   the Index page shifts the edges by the scrollbar's width. */
html {
	scrollbar-width: none;
}

html::-webkit-scrollbar {
	display: none;
}

/* A clean baseline for everything inside the site's two page shells
   (.runway, .runway-work). */
.runway,
.runway-work,
.runway *,
.runway-work *,
.runway *::before,
.runway *::after,
.runway-work *::before,
.runway-work *::after {
	box-sizing: border-box;
	margin: 0;
	padding: 0;
	border: 0;
	font: inherit;
	text-transform: none;
	text-decoration: none;
	color: inherit;
	vertical-align: baseline;
	-webkit-tap-highlight-color: transparent;
}

.runway button,
.runway-work button {
	background: none;
	appearance: none;
	-webkit-appearance: none;
}

.runway {
	position: fixed;
	inset: 0;
	z-index: 2000;
	/* The theme (.runway-theme, bottom right, same idea as 8minutes20.studio's
	   own light/dark toggle) flips these — light by default, remembered
	   in localStorage once changed (runway.js). */
	--panel-bg: #fff;
	--panel-ink: #000;
	/* The slideshow's ‹ › pointer (.runway-viewer.is-cursor-prev), a fine line
	   in the theme's ink. */
	--cursor-prev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M19 7l-9 9 9 9' fill='none' stroke='%23000' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 16 16, w-resize;
	--cursor-next: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M13 7l9 9-9 9' fill='none' stroke='%23000' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 16 16, e-resize;
	background: var(--panel-bg);
	color: var(--panel-ink);
	transition: background-color 0.35s ease, color 0.35s ease;
	font-family: 'Founders Grotesk', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

html.runway-dark .runway {
	--panel-bg: #000;
	--panel-ink: #fff;
	--cursor-prev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M19 7l-9 9 9 9' fill='none' stroke='%23fff' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 16 16, w-resize;
	--cursor-next: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M13 7l9 9-9 9' fill='none' stroke='%23fff' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 16 16, e-resize;
}

/* ============================================================ the runway */

.runway-scroll {
	position: absolute;
	inset: 0;
	z-index: 1;
	overflow-x: hidden;
	overflow-y: scroll;
	scroll-snap-type: y mandatory;
	overscroll-behavior: contain;
	scrollbar-width: none;
}

.runway-scroll::-webkit-scrollbar {
	width: 0;
	height: 0;
}

.runway-panel {
	position: relative;
	display: flex;
	align-items: center;
	height: 100%;
	/* Wider than the chrome's own 3vw margin (title, hint) — the
	   deeper indent the Figma layout gives the actual content column. */
	padding: 0 9vw;
	scroll-snap-align: start;
	scroll-snap-stop: always;
}

/* ==================================================================== intro */

.runway-panel--intro {
	background: #000;
	/* A click on it goes on to the first project (runway.js). */
	cursor: pointer;
}

.runway-intro-media,
.runway-intro-slide {
	position: absolute;
	inset: 0;
}

.runway-intro-media {
	z-index: 0;
}

.runway-intro-slide {
	opacity: 0;
	transition: opacity var(--intro-fade, 1.2s) ease;
}

.runway-intro-slide.is-active {
	opacity: 1;
	z-index: 1;
}

.runway-intro-slide img,
.runway-intro-slide video {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Cropped to fill the screen — tall on a phone, wide on a desktop — the
   landing slideshow's pictures keep their focal point in view: set on each
   picture in the Panel (its Focus), passed on as --focus (runway.php); the
   middle, without one. A picture set to Focus on phones only keeps to the
   middle on a bigger screen. */
.runway-intro-slide img {
	object-position: var(--focus, 50% 50%);
}

@media (min-width: 601px) {
	.runway-intro-slide img.runway-focus-phone {
		object-position: 50% 50%;
	}
}

/* A scrim so the intro text holds over a bright photograph. */
.runway-panel--intro::after {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 1;
	background: linear-gradient(to top, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0.05) 55%, rgba(0, 0, 0, 0.2));
	pointer-events: none;
}

.runway-intro-text {
	position: relative;
	z-index: 2;
	width: min(20em, 60vw);
	color: #fff;
	/* Matches .runway-project-title exactly — one size and weight for both. */
	font-size: clamp(30px, 4vw, 56px);
	font-weight: 700;
	line-height: 1.15;
	letter-spacing: -0.01em;
}

.runway-intro-text a {
	color: inherit;
}

/* ========================================================= project panels */

.runway-panel--project {
	display: block;
	background: var(--panel-bg);
	transition: background-color 0.35s ease;
}

/* Two columns: the title's stays a fixed width at the panel's own indent,
   clear of the picture; the picture's is whatever's left, and the picture
   centres within THAT — not pinned to the outer edge — so it reads as
   sitting in its own bay rather than flush against the page. */
.runway-project-link {
	display: grid;
	grid-template-columns: min(22em, 30vw) 1fr;
	align-items: center;
	gap: 4vw;
	width: 100%;
	height: 100%;
	color: inherit;
	text-decoration: none;
}

/* The box itself is a stable rectangle — the full column width, the panel's
   generous height allowance — and never resizes between slides. Only the
   picture inside it (object-fit: contain, below) differs per slide, sized
   instantly and independently by the browser same as any image: a portrait
   picture is naturally height-bound here (tall, narrow, well short of the
   column's width) and a landscape one width-bound (wide, short of the
   height), each simply rendering at its own true size with no transition to
   fight — which is exactly what stopped the crossfade's opacity fade from
   also looking like the picture was resizing under it. */
.runway-media {
	position: relative;
	grid-row: 1;
	grid-column: 2;
	width: 100%;
	height: 84vh;
}


.runway-media img,
.runway-media video {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: contain;
}

.runway-panel-text {
	grid-row: 1;
	grid-column: 1;
	z-index: 1;
	text-align: left;
}

/* ?is-alt: the picture and title swap sides, project to project. */
.runway-panel--project.is-alt .runway-project-link {
	grid-template-columns: 1fr min(22em, 30vw);
}

.runway-panel--project.is-alt .runway-media {
	grid-column: 1;
}

.runway-panel--project.is-alt .runway-panel-text {
	grid-column: 2;
}

/* A runway project's picture or title opens it full screen (runway.js). */
.runway-panel--project .runway-project-title,
.runway-panel--project .runway-media {
	cursor: pointer;
}

.runway-project-title {
	display: block;
	font-family: 'Founders Grotesk', sans-serif;
	font-weight: 700;
	font-size: clamp(30px, 4vw, 56px);
	line-height: 1.08;
	letter-spacing: -0.01em;
}

.runway-project-blurb {
	display: block;
	font-family: 'Founders Grotesk', sans-serif;
	font-weight: 400;
	font-size: clamp(13px, 1vw, 16px);
	line-height: 1.4;
	margin-top: 0.75em;
}

.runway-project-blurb p {
	margin: 0 0 0.75em;
}

.runway-project-blurb p:last-child {
	margin-bottom: 0;
}

/* Its categories under the blurb, in the blurb's own type, each a link to
   that filter on the Index page (runway-project.php) — quiet, the commas
   between them too, until one's pointed at. */
.runway-project-tags {
	font-family: 'Founders Grotesk', sans-serif;
	font-weight: 400;
	font-size: clamp(13px, 1vw, 16px);
	line-height: 1.4;
	margin-top: 0.75em;
}

.runway-project-tags a,
.runway-project-tags span {
	opacity: 0.4;
	transition: opacity 0.2s ease;
}

.runway-project-tags a:hover {
	opacity: 1;
}

/* ============================================================ about page */

/* The About page's two columns — the bio, then the contact details — both
   set as the full-screen slideshow's project title (.runway-viewer-title):
   the same face and size, a little more space between lines. */
.runway-about-text,
.runway-about-clients {
	font-family: 'Founders Grotesk', sans-serif;
	font-size: 18px;
	font-weight: 400;
	line-height: 1.4;
}

/* Both: paragraphs, bold links in the text's own colour, and what the
   Panel's buttons add (about.yml) — bold, italic and lists, which the reset
   above (.runway-work *) would otherwise flatten back to plain text. A
   paragraph (a blank line in the Panel) is one whole line apart, just as it
   looks there; a single return is just the next line. */
.runway-about-text p,
.runway-about-clients p {
	margin: 0 0 1.4em;
}

.runway-about-text > :last-child,
.runway-about-clients > :last-child {
	margin-bottom: 0;
}

.runway-about-text a,
.runway-about-clients a {
	color: inherit;
	/* Plain, like the words around them — underlined on mouse over. */
	font-weight: normal;
	text-decoration: none;
}

.runway-about-text a:hover,
.runway-about-clients a:hover {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.runway-about-text strong,
.runway-about-text b,
.runway-about-clients strong,
.runway-about-clients b {
	font-weight: bold;
}

.runway-about-text em,
.runway-about-text i,
.runway-about-clients em,
.runway-about-clients i {
	font-style: italic;
}

.runway-about-text ul,
.runway-about-text ol,
.runway-about-clients ul,
.runway-about-clients ol {
	margin: 0 0 1.4em;
	padding-left: 1.2em;
}

.runway-about-text ul,
.runway-about-clients ul {
	list-style: disc;
}

.runway-about-text ol,
.runway-about-clients ol {
	list-style: decimal;
}

/* ============================================================== slideshow */

/* A crossfade (runway.js) through the project's own pictures — it advances
   on its own until a click steps it by hand. */
.runway-slide {
	position: absolute;
	inset: 0;
	opacity: 0;
	transition: opacity 0.6s ease;
}

.runway-slide.is-current {
	opacity: 1;
}

/* Only the showing slide takes clicks (its Play button, say) — the rest,
   stacked invisibly on top of it, let them through. */
.runway-slide:not(.is-current) {
	pointer-events: none;
}

/* Play, over a clip that has a full version: a plain play button — a fine
   ring round a solid triangle — centred on the picture (which sits centred
   in its box). The same difference trick as the title so it reads over any
   frame. */
.runway-play {
	position: absolute;
	top: 50%;
	left: 50%;
	z-index: 1;
	width: 64px;
	height: 64px;
	transform: translate(-50%, -50%);
	color: #fff;
	mix-blend-mode: difference;
	cursor: pointer;
	transition: transform 0.2s ease;
}

.runway-play svg {
	display: block;
	width: 100%;
	height: 100%;
}

.runway-play circle {
	fill: none;
	stroke: currentColor;
	stroke-width: 1;
}

.runway-play path {
	fill: currentColor;
}

.runway-play:hover,
.runway-play:focus-visible {
	transform: translate(-50%, -50%) scale(1.08);
}

/* The full video, with sound and controls, over the whole page on black.
   Close (or Esc) stops it and goes back to the runway where it was. */
.runway-player {
	position: fixed;
	inset: 0;
	z-index: 20;
	background: #000;
	color: #fff;
}

.runway-player[hidden] {
	display: none;
}

.runway-player-stage {
	position: absolute;
	inset: calc(3vw + 40px) 3vw 3vw;
}

.runway-player-stage video,
.runway-player-stage iframe {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: contain;
	border: 0;
}

.runway-player-close {
	position: absolute;
	top: 3vw;
	right: 3vw;
}

/* A pair in the Index page's slideshow (a file marked "Pair this media with
   the next one" in the Panel, and the one after it): side by side, each in
   its own half, meeting in the middle across a gap — the first sits against
   the gap from the left, the second from the right. */
.runway-slide.is-pair {
	display: flex;
	gap: 3vw;
}

.runway-slide.is-pair img,
.runway-slide.is-pair video {
	flex: 1 1 0;
	min-width: 0;
	object-position: right center;
}

.runway-slide.is-pair > :last-child {
	object-position: left center;
}

/* ============================================================== thumbnails */

/* The Index page's thumbnails (runway-thumb.php): uncropped, each tile's
   width following from its picture's true ratio (set inline as its
   aspect-ratio, from Kirby's own file dimensions, so it's known immediately
   rather than jumping once the image itself loads), growing a touch under
   the pointer. Laid out in rows by the Index page's own rules, below. */
.runway-grid-rows {
	display: flex;
	flex-wrap: wrap;
	justify-content: flex-start;
	/* Capped, in step with the thumbnails' own height (--row-h, below), so
	   on a big monitor the spaces don't outgrow the pictures. */
	gap: min(1.5vw, 24px);
	width: 100%;
	/* Room above and below for a tile to grow on hover; none at the sides,
	   so the rows line up with the header's own edges (a tile growing at
	   either end spills into the page margin). */
	padding: 0.5vw 0;
}

.runway-grid-item {
	position: relative;
	display: block;
	flex: 0 0 auto;
	cursor: pointer;
	transition: transform 500ms;
}

/* A project's own address (runway-work.php): the grid stays out of sight
   until runway.js has the project's slideshow open over it. */
.runway-work[data-open] .runway-work-grid {
	visibility: hidden;
}

@media (hover: hover) {
	.runway-grid-item:hover {
		transform: scale(1.05);
	}
}

/* The thumbnails arrive much as the old site's did: once its picture has
   arrived and its moment has come (.is-loaded, not .is-waiting: runway.js),
   each fades up and grows to full size from 90%. runway.js sets
   .is-fading-thumbs once it's watching for them, so without it they just
   show. Only the coming up is animated: as the page opens they're hidden
   at once, before they come up. */
.is-fading-thumbs .runway-grid-item img,
.is-fading-thumbs .runway-grid-item video {
	opacity: 0;
	transform: scale(0.9);
}

.is-fading-thumbs .runway-grid-item.is-loaded:not(.is-waiting) img,
.is-fading-thumbs .runway-grid-item.is-loaded:not(.is-waiting) video {
	opacity: 1;
	transform: none;
	transition: opacity 850ms, transform 450ms;
}

/* Opening the slideshow, they first fade quickly away (runway.js). */
.is-fading-thumbs.is-leaving .runway-grid-item img,
.is-fading-thumbs.is-leaving .runway-grid-item video {
	transition: opacity 320ms, transform 320ms;
}

@media (prefers-reduced-motion: reduce) {
	.is-fading-thumbs .runway-grid-item img,
	.is-fading-thumbs .runway-grid-item video {
		transform: none;
	}
}

.runway-grid-item img,
.runway-grid-item video {
	display: block;
	width: 100%;
	height: 100%;
	/* The tile's aspect-ratio (inline style) already matches the picture's
	   own, so this is only ever a rounding-error crop, never a real one. */
	object-fit: cover;
}

/* =============================================================== the chrome */

.runway-title {
	position: absolute;
	top: 3vw;
	left: 3vw;
	z-index: 3;
	margin: 0;
	max-width: min(640px, 85vw);
	font-family: 'Founders Grotesk', sans-serif;
	font-weight: bold;
	text-transform: uppercase;
	font-size: 18px;
	/* The font's own spacing, as the name's set in Figma. */
	letter-spacing: 0;
	color: #fff;
	mix-blend-mode: difference;
	pointer-events: none;
}

/* Over the opening slideshow (html.runway-title-solid: snippet-head.php,
   runway.js) it's plain white, not blended with the picture behind it —
   through a page fade too. */
html.runway-title-solid .runway > .runway-title {
	mix-blend-mode: normal;
}

html.runway-title-solid:has(.runway)::view-transition-group(site-title) {
	mix-blend-mode: normal;
}

.runway-title a {
	color: inherit;
	text-decoration: none;
	pointer-events: auto;
}

/* Top right: Index and About (runway-menu.php), each to a page of its own.
   The same small caps also set the Index page's filters and the
   slideshow's and player's Close. */
.runway-work-link,
.runway-info-link,
.runway-player-close,
.runway-work-filter,
.runway-viewer-close {
	font-family: 'Founders Grotesk', sans-serif;
	font-weight: bold;
	text-transform: uppercase;
	/* The site title's own size (.runway-title), so the top of every page
	   reads as one line. */
	font-size: 18px;
	letter-spacing: 0.02em;
	cursor: pointer;
}

.runway-links {
	position: absolute;
	top: 3vw;
	right: 3vw;
	z-index: 3;
	display: flex;
	gap: 1.4em;
	color: #fff;
	mix-blend-mode: difference;
	transition: opacity 0.3s ease, visibility 0s;
}

/* Out of the way on the opening slideshow (html.runway-at-intro:
   snippet-head.php, runway.js), in on the way down to the first project. */
html.runway-at-intro .runway-links,
html.runway-at-intro .runway-menu-button {
	opacity: 0;
	visibility: hidden;
	transition: opacity 0.3s ease, visibility 0s 0.3s;
}

/* The band behind the phone's top line on the homepage (runway.php) —
   likewise only there. */
.runway-topbar {
	display: none;
}

/* The phone's menu button (runway-menu.php) — see the phone layout, below;
   elsewhere the links just show. */
.runway-menu-button {
	display: none;
}

.runway-hint {
	position: absolute;
	left: 3vw;
	bottom: 3vw;
	z-index: 3;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 12px;
	font-family: 'Founders Grotesk', sans-serif;
	font-size: 13px;
	letter-spacing: normal;
	color: #fff;
	mix-blend-mode: difference;
	pointer-events: none;
	user-select: none;
	transition: opacity 0.3s ease;
}

.runway-hint-line {
	position: relative;
	width: 1px;
	height: 34px;
	overflow: hidden;
	background: rgba(255, 255, 255, 0.35);
}

.runway-hint-line::after {
	content: '';
	position: absolute;
	inset: 0;
	background: #fff;
	transform: translateY(-100%);
	animation: runway-hint-fall 2.4s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

@keyframes runway-hint-fall {
	0%        { transform: translateY(-100%); }
	60%, 100% { transform: translateY(100%); }
}

.runway-progress {
	position: absolute;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 3;
	height: 1px;
	background: rgba(0, 0, 0, 0.1);
	pointer-events: none;
}

.runway-progress span {
	display: block;
	height: 100%;
	background: rgba(0, 0, 0, 0.55);
	transform: scaleX(0);
	transform-origin: 0 50%;
	transition: transform 0.25s ease;
}

/* THE DOTS: one per panel, right-hand margin, vertically centred — kept
   quiet: small, and never at full strength even for the current panel
   (runway.js fades them, 0.12 up to 0.5). Same difference trick as the title
   so they read over a photo or either theme's flat colour alike. */
.runway-dots {
	position: absolute;
	top: 50%;
	right: 3vw;
	z-index: 3;
	display: flex;
	flex-direction: column;
	align-items: center;
	transform: translateY(-50%);
	pointer-events: none;
	color: #fff;
	mix-blend-mode: difference;
}

.runway-dot {
	position: relative;
	width: 16px;
	height: 14px;
	cursor: pointer;
	pointer-events: auto;
}

.runway-dot::before {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	width: 4px;
	height: 4px;
	margin: -2px 0 0 -2px;
	border-radius: 50%;
	background: currentColor;
	transition: transform 0.2s ease;
}

.runway-dot:hover::before,
.runway-dot:focus-visible::before {
	transform: scale(1.6);
}

/* THE THEME BUTTON: bottom right, same idea and mark as 8minutes20.studio's
   own — a small half-filled circle that flips light/dark (runway.js). Same
   difference trick as the title/hint so it reads over a photo too.
   Fixed rather than absolute, so it stays put on the Index page, which
   scrolls normally (on the runway it makes no difference). */
.runway-theme {
	position: fixed;
	right: 3vw;
	bottom: 3vw;
	z-index: 3;
	width: 32px;
	height: 32px;
	cursor: pointer;
	color: #fff;
	mix-blend-mode: difference;
}

.runway-theme::before {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	width: 14px;
	height: 14px;
	margin: -7px 0 0 -7px;
	border: 1.5px solid currentColor;
	border-radius: 50%;
	/* Left half solid, right half empty: the usual light/dark mark. */
	background: linear-gradient(90deg, currentColor 50%, transparent 50%);
}

.runway-theme:focus-visible {
	outline: 1px solid currentColor;
	outline-offset: -4px;
}

/* ============================================================== index page */

/* A plain, natively scrolling page — the document itself scrolls, no
   scroll-snap. Same --panel-bg/--panel-ink theme as .runway, flipped by the
   same .runway-theme button and remembered in the same localStorage key.

   --gap is the space between thumbnails and, the same, round the page's
   edges, so the spacing reads as one consistent measure. It's the runway's
   own edge margin (3vw: .runway-title, .runway-links), so the site title
   and the top-right link sit exactly where they do there and nothing jumps
   going between the two pages. */
.runway-work {
	--gap: 3vw;
	position: relative;
	min-height: 100vh;
	padding: 0 var(--gap) var(--gap);
	--panel-bg: #fff;
	--panel-ink: #000;
	/* The slideshow's ‹ › pointer (.runway-viewer.is-cursor-prev), a fine line
	   in the theme's ink. */
	--cursor-prev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M19 7l-9 9 9 9' fill='none' stroke='%23000' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 16 16, w-resize;
	--cursor-next: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M13 7l9 9-9 9' fill='none' stroke='%23000' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 16 16, e-resize;
	background: var(--panel-bg);
	color: var(--panel-ink);
	transition: background-color 0.35s ease, color 0.35s ease;
	font-family: 'Founders Grotesk', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

html.runway-dark .runway-work {
	--panel-bg: #000;
	--panel-ink: #fff;
	--cursor-prev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M19 7l-9 9 9 9' fill='none' stroke='%23fff' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 16 16, w-resize;
	--cursor-next: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M13 7l9 9-9 9' fill='none' stroke='%23fff' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 16 16, e-resize;
}

/* The site title and the filters, one line across the top, staying put as
   the grid scrolls under it. */
.runway-work-header {
	position: sticky;
	top: 0;
	z-index: 5;
	display: flex;
	flex-wrap: wrap;
	/* Tops aligned, as the runway's title and top-right link are. */
	align-items: flex-start;
	gap: 0.4em 4vw;
	padding: var(--gap) 0;
	background: var(--panel-bg);
	transition: background-color 0.35s ease;
}

.runway-work-header .runway-title {
	position: static;
	color: inherit;
	mix-blend-mode: normal;
}

/* On the Index page (and a hidden project's) the header has no background
   of its own: the thumbnails scroll up under it and off the top. It's drawn
   white in the difference blend, as the runway's title is, so it reads over
   a picture as well as the page, in either theme. */
.runway-work:has(.runway-work-grid) .runway-work-header {
	background: none;
	color: #fff;
	mix-blend-mode: difference;
}

/* The Index and About pages' own top-right links — the runway's Index and
   About, the page you're on underlined. */
.runway-page-links {
	display: flex;
	gap: 1.4em;
	margin-left: auto;
}

.runway-page-links [aria-current="page"] {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

/* The About page's text, straight under the header, on the site title's
   own left edge — the same place the Index page's thumbnails start: two
   columns side by side, the bio and the contact details, their tops level
   (their type: .runway-about-text / -clients, above). */
.runway-info-body {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 34em));
	/* A generous gap, so the two read as separate columns. */
	column-gap: 8vw;
	row-gap: 2em;
	align-items: start;
	justify-content: start;
	padding: 3vw 0 var(--gap);
}

/* The current one underlined. */
.runway-work-filters {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4em 1.4em;
}

.runway-work-filter {
	opacity: 0.4;
	transition: opacity 0.2s ease;
}

.runway-work-filter:hover,
.runway-work-filter.is-active {
	opacity: 1;
}

.runway-work-filter.is-active {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

/* The name of the project under the pointer, following it over its
   thumbnails (runway.js) — in the project titles' own bold, and the same
   difference blend as the header, so it reads over a picture as well as
   the page. Mouse only. */
.runway-hover-title {
	position: fixed;
	top: 0;
	left: 0;
	z-index: 6;
	font-family: 'Founders Grotesk', sans-serif;
	font-weight: 700;
	font-size: 18px;
	line-height: 1.2;
	white-space: nowrap;
	color: #fff;
	mix-blend-mode: difference;
	pointer-events: none;
	opacity: 0;
	transition: opacity 0.15s ease;
}

.runway-hover-title.is-shown {
	opacity: 1;
}

/* Every project, one after another down the page, its thumbnails the full
   width. On a hidden project's own page, its title and blurb sit above
   them. */
.runway-work-grid {
	display: flex;
	flex-direction: column;
	/* Capped in step with the thumbnails (--row-h), as the gaps between
	   them are. */
	gap: min(6vw, 94px);
	padding: min(3vw, 47px) 0;
}

.runway-work-project {
	display: flex;
	flex-direction: column;
	gap: 3vw;
}

.runway-work-text {
	max-width: 40em;
}

/* Each row of thumbnails fills the width exactly — no ragged gap down the
   right. runway.js lays them out (layoutRows()): it splits a project's
   pictures into however many rows keeps them nearest 7vw tall (--row-h —
   but no more than 110px, so on a big monitor they don't just keep
   growing),
   evenly, and sizes each row to reach the right edge — every tile in a row
   the same height, and uncropped. Until then (and without it) the same
   idea in plain CSS: tiles 7vw tall, each row grown to fit in proportion
   to their shapes (--ratio, runway-thumb.php), a spacer after the last
   tile soaking up most of the last row's spare room. */
.runway-work .runway-grid-rows {
	--row-h: min(7vw, 110px);
	align-items: flex-start;
}

.runway-work .runway-grid-item {
	height: auto;
	flex: var(--ratio) 1 calc(var(--ratio) * var(--row-h));
}

.runway-work .runway-grid-rows::after {
	content: '';
	flex: 3 1 0;
}

.runway-work .runway-grid-rows.is-justified {
	flex-direction: column;
	flex-wrap: nowrap;
}

.runway-work .runway-grid-line {
	display: flex;
	gap: inherit;
}

.runway-work .runway-grid-rows.is-justified .runway-grid-item {
	flex: none;
}

.runway-work .runway-grid-rows.is-justified::after {
	display: none;
}

.runway-work-project[hidden] {
	display: none;
}

/* Featured shows the homepage's projects in the homepage's own order
   (--featured, runway-work.php) — every other filter, the Visible Projects
   order. */
.runway-work-grid.is-featured .runway-work-project {
	order: var(--featured);
}

/* A project's slideshow, over the page: its pictures crossfading in a box
   inset from the edges (the same .runway-media/.runway-slide styles as the
   homepage's), its title top left where the site title sits, Close top
   right, and a count bottom left. Click the picture or → for the next, ←
   for the one before, Esc or Close to shut it. */
.runway-viewer {
	position: fixed;
	inset: 0;
	z-index: 10;
	/* Pictures sliding in and out between projects stay inside it. */
	overflow: hidden;
	background: var(--panel-bg);
	color: var(--panel-ink);
}

.runway-viewer[hidden] {
	display: none;
}

/* It takes the focus on opening (runway.js) — no ring round the whole of it. */
.runway-viewer:focus {
	outline: none;
}

/* Swipes change picture and project (runway.js), so the page doesn't pan
   (or zoom) under a finger. */
.runway-viewer {
	touch-action: none;
}

.runway-viewer-stage.runway-media {
	position: absolute;
	inset: 8vw 9vw;
	width: auto;
	height: auto;
}

/* Your name top left (.runway-title, exactly as on the runway), the
   project's title top centre, level with it. */
.runway-viewer-title {
	position: absolute;
	top: 3vw;
	left: 50%;
	transform: translateX(-50%);
	max-width: 50vw;
	font-family: 'Founders Grotesk', sans-serif;
	font-weight: 700;
	font-size: 18px;
	line-height: 1.2;
	text-align: center;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.runway-viewer-close {
	position: absolute;
	top: 3vw;
	right: 3vw;
}

.runway-viewer-count {
	position: absolute;
	left: 3vw;
	bottom: 3vw;
	font-family: 'Founders Grotesk', sans-serif;
	font-weight: bold;
	text-transform: uppercase;
	font-size: 16px;
	letter-spacing: 0.02em;
	font-variant-numeric: tabular-nums;
}

/* Clicking through, quick clicks would otherwise count as double-clicks
   and select (highlight) the picture or text under the pointer, and a small
   slip drag a picture's ghost off — off in the whole of the slideshow,
   where a click steps it. */
.runway-viewer {
	-webkit-user-select: none;
	user-select: none;
}

.runway-viewer img,
.runway-viewer video,
.runway-slide img,
.runway-slide video {
	-webkit-user-drag: none;
}

/* The slideshow's own ‹ › pointer: the left half of the screen steps back,
   the right forward (runway.js sets .is-cursor-prev / .is-cursor-next). */
.runway-viewer.is-cursor-prev,
.runway-viewer.is-cursor-prev :not(a, a *, button, button *) {
	cursor: var(--cursor-prev);
}

.runway-viewer.is-cursor-next,
.runway-viewer.is-cursor-next :not(a, a *, button, button *) {
	cursor: var(--cursor-next);
}

html.runway-viewer-open {
	overflow: hidden;
}

@media (max-width: 600px) {
	html.runway-active {
		overflow: visible;
	}

	html.runway-viewer-open,
	html.runway-menu-open {
		overflow: hidden;
	}

	.runway {
		position: static;
		inset: auto;
		z-index: auto;
	}

	.runway-scroll {
		position: static;
		inset: auto;
		z-index: auto;
		overflow: visible;
		scroll-snap-type: none;
	}

	/* Your name stays put at the top as the list scrolls under it,
	   drawn with the same difference blend as on the big runway, so it reads
	   over a photo as well as the page. */
	.runway-title,
	.runway-hint {
		position: fixed;
	}

	.runway-title {
		top: 20px;
		left: 20px;
		z-index: 9;
	}

	.runway-hint {
		display: none;
	}

	.runway-progress,
	.runway-dots,
	.runway-theme {
		display: none;
	}

	/* Overview and the About page fold away behind a menu button, top right
	   (runway-menu.php, runway.js): three lines, level with your name, which
	   open the links over the whole page and turn into a cross. Your name
	   and the button stay on top. Through a page fade it's the button that
	   stays put. */
	.runway-menu-button {
		display: block;
		/* The lines are 22px by 14px; the padding round them makes it an
		   easy tap, the margins put the lines back on the 20px edge. */
		width: 46px;
		height: 42px;
		margin: -12px -12px -14px 0;
		padding: 0;
		border: 0;
		color: var(--panel-ink);
		cursor: pointer;
		view-transition-name: site-menu;
		transition: opacity 0.3s ease, visibility 0s;
	}

	/* Its three lines: the middle one, and one above and below it — open,
	   the middle one goes and the other two cross. */
	.runway-menu-button span,
	.runway-menu-button span::before,
	.runway-menu-button span::after {
		position: absolute;
		left: 12px;
		width: 22px;
		height: 2px;
		background: currentColor;
		transition: transform 0.2s ease, background-color 0.2s ease;
	}

	.runway-menu-button span {
		top: 22px;
	}

	.runway-menu-button span::before,
	.runway-menu-button span::after {
		content: '';
		left: 0;
		top: 0;
	}

	.runway-menu-button span::before {
		transform: translateY(-6px);
	}

	.runway-menu-button span::after {
		transform: translateY(6px);
	}

	html.runway-menu-open .runway-menu-button span {
		background: transparent;
	}

	html.runway-menu-open .runway-menu-button span::before {
		transform: rotate(45deg);
	}

	html.runway-menu-open .runway-menu-button span::after {
		transform: rotate(-45deg);
	}

	.runway .runway-menu-button {
		position: fixed;
		top: 20px;
		right: 20px;
		z-index: 9;
		margin: -12px -12px 0 0;
		color: #fff;
		mix-blend-mode: difference;
	}

	.runway-work-header .runway-title,
	.runway-work-header .runway-menu-button {
		position: relative;
		top: auto;
		left: auto;
		z-index: 9;
	}

	/* The Index page's header: your name and the button on the top line, the
	   filters on their own line under them. */
	.runway-work-header .runway-menu-button {
		order: 1;
		margin-left: auto;
		/* The header's own colour, as your name is: white in the Index
		   page's difference blend, the page's ink otherwise. */
		color: inherit;
	}

	/* The links themselves: over the whole page, up top under your name,
	   in the menu's own bold capitals. */
	.runway-links,
	.runway-page-links {
		position: fixed;
		inset: 0;
		top: 0;
		right: 0;
		z-index: 8;
		display: none;
		flex-direction: column;
		justify-content: flex-start;
		align-items: flex-start;
		gap: 0.5em;
		margin: 0;
		padding: 90px 20px 60px;
		background: var(--panel-bg);
		color: var(--panel-ink);
		mix-blend-mode: normal;
		view-transition-name: none;
		will-change: auto;
	}

	/* (The button's what's out of the way on the opening slideshow here.) */
	html.runway-at-intro .runway-links {
		opacity: 1;
		visibility: visible;
	}

	html.runway-menu-open .runway-links,
	html.runway-menu-open .runway-page-links {
		display: flex;
		animation: runway-fade-in 0.25s ease-out;
	}

	/* Light/dark, hidden on a phone otherwise, is in the open menu: the same
	   mark, under the links, its left edge on theirs (the mark's 14px wide
	   in its 32px button). */
	html.runway-menu-open .runway-theme {
		display: block;
		top: 153px;
		left: 11px;
		right: auto;
		bottom: auto;
		z-index: 10;
		animation: runway-fade-in 0.25s ease-out;
	}

	/* On a phone the top line has the page's colour behind it, so your name
	   and the menu button don't run into the pictures scrolling under them.
	   The Index page's header has its background back (no difference blend
	   here, nor on its name, button and filters through a page fade)… */
	.runway-work:has(.runway-work-grid) .runway-work-header {
		background: var(--panel-bg);
		color: inherit;
		mix-blend-mode: normal;
	}

	html:has(.runway-work-grid)::view-transition-group(site-title),
	html:has(.runway-work-grid)::view-transition-group(site-menu),
	html:has(.runway-work-grid)::view-transition-group(site-filters) {
		mix-blend-mode: normal;
	}

	/* …and the homepage has a band of it behind them from the first project
	   on: it sits at the top of that project (runway.php), so it scrolls up
	   as the opening slideshow goes and stays put at the top from there —
	   nothing over the slideshow itself. Your name and the button are still
	   drawn in the difference blend over it, which comes out as the page's
	   ink. (Sticky, as the Index page's header is: iPhone Safari goes by an
	   element like that for what it draws behind the status bar. The
	   margin takes its height back out of the list.) */
	.runway-topbar {
		display: block;
		position: sticky;
		top: 0;
		z-index: 8;
		height: 61px;
		margin-bottom: -61px;
		background: var(--panel-bg);
		transition: background-color 0.35s ease;
	}

	.runway-work-filters {
		order: 2;
		flex-basis: 100%;
	}

	/* A little more room under your name (and under the filters, below). */
	.runway-work-header {
		row-gap: 16px;
	}

	/* The runway's own phone margin (20px). */
	.runway-work {
		--gap: 20px;
	}

	.runway-work-grid {
		padding: 36px 0 20px;
		gap: 40px;
	}

	.runway-work-project {
		gap: 16px;
	}

	.runway-work .runway-grid-rows {
		--row-h: 18vw;
	}

	.runway-viewer-stage.runway-media {
		inset: 60px 20px;
	}

	.runway-viewer-count {
		left: 20px;
	}

	/* Bottom right, level with the count — there's no room for it between
	   your name and Close. */
	.runway-viewer-title {
		top: auto;
		bottom: 20px;
		left: auto;
		right: 20px;
		transform: none;
		max-width: calc(100% - 120px);
		text-align: right;
	}

	.runway-viewer-count {
		bottom: 20px;
	}

	.runway-viewer-close {
		top: 20px;
		right: 20px;
	}

	.runway-panel {
		display: block;
		height: auto;
		padding: 60px 20px;
	}

	/* The projects one after another, a gap between each, each just its
	   first picture (runway.js takes out the rest) with the title right
	   under it: the box takes that picture's shape (--ratio,
	   runway-project.php), the picture at its bottom left corner should
	   70vh cut the box short. A tap opens the project full screen
	   (runway.js). */
	.runway-panel--project {
		padding: 80px 20px 0;
	}

	.runway-panel--project:last-child {
		padding-bottom: 80px;
	}

	/* The title under each picture in the About page's type and the
	   menu's (.runway-about-text), smaller than on the big runway. */
	.runway-panel--project .runway-project-title {
		font-size: 18px;
		line-height: 1.4;
		letter-spacing: normal;
	}

	.runway-panel .runway-media,
	.runway-panel--project.is-alt .runway-media {
		height: auto;
		aspect-ratio: var(--ratio, 1.5);
		margin-bottom: 12px;
	}

	.runway-panel .runway-media img,
	.runway-panel .runway-media video {
		object-position: left bottom;
	}

	/* A plain list on a phone: it just ends. */
	.runway-panel.runway-panel--loop {
		display: none;
	}

	.runway-panel--intro {
		min-height: 100dvh;
		display: flex;
		flex-direction: column;
		justify-content: center;
		padding: 90px 20px 60px;
	}

	.runway-intro-text {
		width: auto;
	}

	/* The About page's two columns, one above the other. */
	.runway-info-body {
		grid-template-columns: minmax(0, 1fr);
	}

	/* The Index page's own phone spacing under the header. */
	.runway-info-body {
		padding: 36px 0 20px;
	}

	.runway-project-link {
		display: block;
	}

	.runway-panel .runway-media,
	.runway-panel--project.is-alt .runway-media {
		order: 0;
		width: 100%;
		max-height: 70vh;
	}

	.runway-panel-text,
	.runway-panel--project.is-alt .runway-panel-text {
		order: 0;
		width: auto;
		text-align: left;
		margin-left: 0;
	}

	/* Title, blurb and categories closer together under the picture. */
	.runway-project-blurb,
	.runway-project-tags {
		margin-top: 0.4em;
		/* A step down from the title's 18px, but big enough to read. */
		font-size: 15px;
	}
}

/* ============================================================ reduced motion */

@media (prefers-reduced-motion: reduce) {
	.runway-hint,
	.runway-progress,
	.runway-dots,
	.runway-theme {
		display: none;
	}

	.runway-scroll {
		scroll-snap-type: none;
	}

	.runway-panel.runway-panel--loop {
		display: none;
	}

	.runway-panel {
		display: block;
		height: auto;
		padding: 60px 4vw;
	}

	.runway-project-link {
		display: block;
	}

	.runway-panel .runway-media,
	.runway-panel--project.is-alt .runway-media {
		order: 0;
		width: 100%;
		max-height: 70vh;
		margin-bottom: 20px;
	}

	.runway-panel-text,
	.runway-panel--project.is-alt .runway-panel-text {
		order: 0;
		width: auto;
		text-align: left;
		margin-left: 0;
	}
}
