/* ==========================================================================
   About
   --------------------------------------------------------------------------
   An editorial page: six sections, each one a held breath after the last. The
   rhythm is carried by space rather than by rules or boxes, so most of this
   file is vertical measure.

   Everything is scoped to .nsn-about, so no rule here can reach another page.
   The palette, the face and the gutter all come from the site's own tokens -
   nothing is restated.
   ========================================================================== */

/*
 * The base stylesheet sets overflow-x: hidden on the body. That makes the body
 * a scrolling box, and position: sticky inside a scrolling box sticks to that
 * box rather than to the viewport - which silently stops the pinned stage from
 * pinning at all. clip does the same clipping without creating the scroll box,
 * so the stage works and the page still cannot be scrolled sideways.
 *
 * Scoped to this page's own body class: no other page's overflow changes, and
 * the base rule itself is untouched.
 */
body.nsn-is-about {
	overflow-x: clip;
}

.nsn-about {
	--nsn-about-accent: #FF6548;

	/* The measure a paragraph is allowed to reach before it gets hard to read. */
	--nsn-about-measure: 54ch;

	/* The air between sections. This single value sets the page's pace. */
	--nsn-about-gap: clamp(5rem, 13vh, 11rem);

	position: relative;

	/*
	 * Clear of the header.
	 *
	 * The header is fixed, so it takes no space in the flow and a page has to
	 * leave room for it. Every other route does - base.css, Journal, Contact
	 * and Work all open with calc(var(--nsn-header-h) + ...) - and this page
	 * was the one that did not. Its 9vh of top padding came to 81px against a
	 * 92px header, so the first line of the heading started above the bottom
	 * of the header band and the ascenders of "Wij maken" ran into the
	 * wordmark.
	 *
	 * The page's own rhythm is untouched: the header's height is simply added
	 * in front of the spacing that was already here. --nsn-header-h itself is
	 * not redefined, so no other route moves.
	 */
	padding-block: calc(var(--nsn-header-h) + clamp(3rem, 9vh, 7rem)) var(--nsn-about-gap);

	/* The page's own side gutter, set once here and inherited by every section
	   below, so no section can drift out of alignment with another. */
	padding-inline: var(--nsn-gutter);

	background: var(--nsn-ink);
	color: var(--nsn-bone);

	/* Nothing inside may widen the document. The parallax and the hover moves
	   are transforms, which can overhang; this is the backstop. */
	overflow-x: clip;
}

.nsn-about > * + * {
	margin-top: var(--nsn-about-gap);
}

/* The small uppercase label that opens a section. */
.nsn-about__label {
	margin: 0 0 clamp(2rem, 5vh, 3.5rem);
	font-size: var(--nsn-step-xs);
	font-weight: 600;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--nsn-bone-28, rgba(240, 236, 228, 0.42));
}

/* --------------------------------------------------------------------------
   1. Intro
   -------------------------------------------------------------------------- */
.nsn-about__intro {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: clamp(2rem, 6vh, 4rem);
}

.nsn-about__title {
	/*
	 * The measure that holds the heading to its three lines.
	 *
	 * In ch, so it scales with the type size and behaves the same at every
	 * width. Measured on the real text, "wat er speelt." needs 9.3ch and
	 * "Wij maken zichtbaar" fits by 14.2ch, so anything between those two
	 * breaks the heading exactly where it is written; 13ch sits inside that
	 * window with room on both sides.
	 *
	 * It was 18ch, which left the break to whatever happened to fit: three
	 * lines at 1440, but four at 1024 and two at 820 and below.
	 */
	max-width: 13ch;
	margin: 0;

	/* A step down from clamp(2.4rem, 1rem + 6vw, 6rem) - same curve, same
	   behaviour at every width, simply set smaller. */
	font-size: clamp(2rem, 0.9rem + 4.6vw, 4.5rem);
	font-weight: 700;
	line-height: 1.02;
	letter-spacing: -0.02em;
	text-wrap: balance;
}

/*
 * Each word is a clipping box with the word inside it, so the word can be
 * lifted into place from below rather than faded in flat. inline-block on the
 * outer span keeps the line breaking normally between words.
 */
.nsn-about__word {
	display: inline-block;
	overflow: hidden;
	vertical-align: bottom;
}

.nsn-about__word > span {
	display: inline-block;
	will-change: transform;
}

.nsn-about__lede {
	max-width: var(--nsn-about-measure);
	justify-self: end;
	font-size: var(--nsn-step-md, 1.15rem);
	line-height: 1.65;
	color: var(--nsn-bone-70, rgba(240, 236, 228, 0.72));
}

.nsn-about__lede p {
	margin: 0 0 1em;
}

.nsn-about__lede p:last-child {
	margin-bottom: 0;
}

@media (min-width: 901px) {
	.nsn-about__intro {
		grid-template-columns: 1.15fr 1fr;
		align-items: end;
		gap: clamp(2rem, 5vw, 5rem);
	}
}

/* --------------------------------------------------------------------------
   2. What we do - the pinned sequence

   Three columns: the blocks pass down the outer two while one upright picture
   holds the middle. The pin is position: sticky, not a scripted one, so it
   cannot fall out of step with the scroll and needs nothing rebuilt on resize.

   The stage spans every row of the grid, which is why the row count is set on
   the container from PHP: without explicit rows, "grid-row: 1 / -1" resolves to
   a single row and the picture would scroll away with the first block.
   -------------------------------------------------------------------------- */
.nsn-about__sequence {
	--nsn-about-stage: min(30vw, 27rem);

	/* Shared with each row, so the row's own columns line up with these. */
	--nsn-about-colgap: clamp(1.5rem, 4vw, 4rem);

	display: grid;
	grid-template-columns: minmax(0, 1fr) var(--nsn-about-stage) minmax(0, 1fr);
	grid-template-rows: repeat(var(--nsn-about-rows, 4), auto);
	column-gap: var(--nsn-about-colgap);
	align-items: center;
}

/* The pinned picture. */
.nsn-about__stage {
	grid-column: 2;
	grid-row: 1 / -1;

	/*
	 * Sticky against the top, offset by the header's band plus half the gap
	 * that is left over - which centres the frame in the space below the
	 * header rather than in the viewport, so the header never sits on it.
	 */
	position: sticky;
	top: calc(var(--nsn-header-h, 92px) + (100svh - var(--nsn-header-h, 92px) - var(--nsn-about-stage) * 1.25) / 2);
	align-self: start;
	z-index: 1;
	pointer-events: none;
}

.nsn-about__stage-frame {
	position: relative;
	overflow: hidden;
	aspect-ratio: 4 / 5;
	background: var(--nsn-ink-2, #17171a);
}

/*
 * The two crossfading layers. Both are stacked in the same box with the same
 * crop, so the incoming picture is already exactly where the outgoing one is
 * and a change cannot shift anything.
 */
.nsn-about__layer {
	position: absolute;
	inset: 0;
	opacity: 0;
	will-change: opacity, transform;
}

.nsn-about__layer.is-active {
	opacity: 1;
}

.nsn-about__layer-img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	filter: grayscale(1) contrast(1.03);
}

/*
 * No picture anywhere yet: the middle track closes up and the rows are simply
 * two columns, rather than two halves either side of an empty space.
 */
.nsn-about__sequence.is-textonly,
.nsn-about__sequence.is-textonly .nsn-about__panel {
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

.nsn-about__sequence.is-textonly .nsn-about__panel {
	min-height: 0;
	padding-block: clamp(2rem, 6vh, 4rem);
	opacity: 1;
}

.nsn-about__sequence.is-textonly .nsn-about__panel-body {
	grid-column: 2;
}

/* --------------------------------------------------------------------------
   The blocks

   Every row is the same shape: the number and title on the left, the
   description on the right, and the picture between them. The row spans all
   three columns and repeats the container's own track definition inside
   itself, so its two halves sit exactly under the outer columns and its middle
   is left empty for the pinned frame to show through. Nothing is alternated -
   the composition stays put and only the picture changes.
   -------------------------------------------------------------------------- */
.nsn-about__panel {
	grid-column: 1 / -1;
	grid-row: var(--nsn-row, auto);

	/* Tall enough that only one block is ever level with the picture. */
	min-height: min(88svh, 46rem);

	display: grid;
	grid-template-columns: minmax(0, 1fr) var(--nsn-about-stage) minmax(0, 1fr);
	column-gap: var(--nsn-about-colgap);
	align-items: center;

	/* Held back until this block is the one the picture is showing. */
	opacity: 0.32;
	transition: opacity 620ms cubic-bezier(0.22, 1, 0.36, 1);
}

.nsn-about__panel.is-current {
	opacity: 1;
}

/* Left: ranged towards the picture, so the gutter between them stays even. */
.nsn-about__panel-head {
	grid-column: 1;
	min-width: 0;
	text-align: right;
}

/* Right: ranged away from it, for the same reason. */
.nsn-about__panel-body {
	grid-column: 3;
	min-width: 0;
	max-width: 34ch;
	text-align: left;
}

.nsn-about__num {
	display: block;
	margin-bottom: 0.9em;
	font-size: var(--nsn-step-xs);
	font-weight: 600;
	letter-spacing: 0.14em;
	color: var(--nsn-about-accent);
}

.nsn-about__item-title {
	margin: 0;
	font-size: clamp(1.5rem, 0.9rem + 2vw, 2.8rem);
	font-weight: 700;
	line-height: 1.08;
	letter-spacing: -0.015em;
	text-wrap: balance;
}

.nsn-about__text {
	margin: 0;
	font-size: var(--nsn-step-sm);
	line-height: 1.65;
	color: var(--nsn-bone-70, rgba(240, 236, 228, 0.72));
}
/*
 * Each block also carries its own picture, for the two cases where the pinned
 * stage is not doing the work: no JavaScript, and narrow screens. It is hidden
 * wherever the stage is in charge.
 */
.nsn-about__panel-figure {
	margin: 1.6rem 0 0;
	overflow: hidden;
	aspect-ratio: 4 / 5;
	background: var(--nsn-ink-2, #17171a);
}

.nsn-about__panel-img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	filter: grayscale(1) contrast(1.03);
}

@media (min-width: 901px) {
	/* The stage is showing the picture, so the per-block copy is redundant. */
	html.nsn-js .nsn-about__panel-figure {
		display: none;
	}

	/* Without the script there is no crossfade, so the stage would only ever
	   show the first picture - the per-block ones are the honest version. */
	html:not(.nsn-js) .nsn-about__stage {
		display: none;
	}
}

/* --------------------------------------------------------------------------
   Narrow screens

   The pin is dropped here, and deliberately. A sticky picture holds its place
   while everything else scrolls, which on a wide screen is the whole point -
   the blocks pass it in the side columns. On a phone there are no side columns
   for them to pass in, so the blocks would scroll underneath the picture and
   read through it.

   So the composition stacks instead: every block carries its own picture, in
   order, and each one fades up as its block arrives. The change of image still
   follows the section - it is simply carried by the page rather than by a
   pinned frame.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
	.nsn-about__sequence {
		display: block;
	}

	/* The stage has nowhere to pin to without covering the text. */
	.nsn-about__stage {
		display: none;
	}

	/*
	 * One column, in reading order: number and title, description, picture.
	 * Nothing is dimmed here - every block is read in turn rather than chosen,
	 * so holding three of them back would only make them hard to read.
	 */
	.nsn-about__panel {
		min-height: 0;
		display: block;
		padding-block: clamp(1.5rem, 4vh, 2.5rem);
		opacity: 1;
	}

	.nsn-about__panel-head {
		text-align: left;
	}

	.nsn-about__panel-body {
		max-width: none;
		margin-top: 1rem;
	}

	/* Each block's own picture is the one that is seen. */
	.nsn-about__panel-figure {
		display: block;
	}

	/*
	 * The arrival. Armed only when there is a script to add the class, so
	 * without one every picture is simply visible.
	 */
	html.nsn-js .nsn-about__panel-figure {
		opacity: 0;
		transform: scale(1.03);
		transition:
			opacity 760ms cubic-bezier(0.22, 1, 0.36, 1),
			transform 900ms cubic-bezier(0.22, 1, 0.36, 1);
	}

	html.nsn-js .nsn-about__panel-figure.is-inview,
	html.nsn-reduced-motion .nsn-about__panel-figure {
		opacity: 1;
		transform: none;
	}

	html.nsn-reduced-motion .nsn-about__panel-figure {
		transition: none;
	}
}
/* --------------------------------------------------------------------------
   3. Reel
   -------------------------------------------------------------------------- */
.nsn-about__reel-frame {
	margin: 0;
}

/*
 * The frame is the crop and the inner span is what moves inside it, which is
 * why the overflow is here and the transform is one level down.
 */
.nsn-about__reel-inner {
	display: block;
	overflow: hidden;
	aspect-ratio: 16 / 9;
	background: var(--nsn-ink-2, #17171a);
}

.nsn-about__reel-media {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;

	/* Slightly over-tall, so a few percent of parallax never exposes an edge. */
	transform: scale(1.06);
	will-change: transform;
}

.nsn-about__reel-caption {
	margin-top: 1.1rem;
	font-size: var(--nsn-step-xs);
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--nsn-bone-28, rgba(240, 236, 228, 0.42));
}

@media (max-width: 700px) {
	.nsn-about__reel-inner {
		aspect-ratio: 4 / 3;
	}
}

/* --------------------------------------------------------------------------
   4. Team
   -------------------------------------------------------------------------- */
/*
 * The upper bound on the track is the point of this rule. With a 1fr track,
 * auto-fit stretches however few people there are across the whole row - two
 * members on a wide screen become two 660px columns, and a 3:4 portrait in one
 * of those is nearly 900px tall. Capping the track keeps a portrait a portrait
 * whether the client adds one person or six, and justify-content starts the
 * row rather than centring a lonely first column.
 */
.nsn-about__people {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 19rem));
	justify-content: start;
	gap: clamp(2rem, 5vw, 3.5rem) clamp(1.5rem, 3vw, 2.5rem);

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

.nsn-about__person {
	min-width: 0;
}

.nsn-about__portrait {
	margin: 0 0 1.1rem;
	overflow: hidden;
	aspect-ratio: 3 / 4;
	background: var(--nsn-ink-2, #17171a);
}

.nsn-about__portrait-img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	filter: grayscale(1);
	transition: transform 700ms cubic-bezier(0.22, 1, 0.36, 1), filter 700ms ease;
}

.nsn-about__person:hover .nsn-about__portrait-img,
.nsn-about__person:focus-within .nsn-about__portrait-img {
	transform: scale(1.04);
	filter: grayscale(0.55);
}

.nsn-about__person-name {
	margin: 0;
	font-size: var(--nsn-step-md, 1.15rem);
	font-weight: 700;
	line-height: 1.2;
}

/* The role and the date sit on one line, wrapping together if they must. */
.nsn-about__person-meta {
	display: flex;
	flex-wrap: wrap;
	gap: 0.15em 0.7em;
	align-items: baseline;
	margin: 0.35em 0 0;
	font-size: var(--nsn-step-xs);
	letter-spacing: 0.1em;
	text-transform: uppercase;
}

.nsn-about__person-role {
	color: var(--nsn-about-accent);
}

.nsn-about__person-date {
	color: var(--nsn-bone-28, rgba(240, 236, 228, 0.42));
	white-space: nowrap;
}

.nsn-about__person-text {
	margin: 0.9em 0 0;
	font-size: var(--nsn-step-sm);
	line-height: 1.6;
	color: var(--nsn-bone-70, rgba(240, 236, 228, 0.72));
}

/* --------------------------------------------------------------------------
   5. Press
   -------------------------------------------------------------------------- */
.nsn-about__press-list {
	margin: 0;
	padding: 0;
	list-style: none;
	border-top: 1px solid var(--nsn-rule, rgba(240, 236, 228, 0.14));
}

.nsn-about__press-item {
	border-bottom: 1px solid var(--nsn-rule, rgba(240, 236, 228, 0.14));
}

.nsn-about__press-row {
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto auto;
	gap: clamp(0.8rem, 3vw, 2rem);
	align-items: baseline;

	padding-block: clamp(1rem, 2.6vh, 1.6rem);
	color: inherit;
	text-decoration: none;
	transition: color 380ms cubic-bezier(0.22, 1, 0.36, 1);
}

a.nsn-about__press-row:hover,
a.nsn-about__press-row:focus-visible {
	color: var(--nsn-about-accent);
}

a.nsn-about__press-row:focus-visible {
	outline: 2px solid var(--nsn-about-accent);
	outline-offset: 3px;
}

.nsn-about__press-name {
	min-width: 0;
	font-size: clamp(1.05rem, 0.9rem + 0.6vw, 1.5rem);
	font-weight: 600;
	line-height: 1.25;
}

.nsn-about__press-type,
.nsn-about__press-year {
	font-size: var(--nsn-step-xs);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--nsn-bone-28, rgba(240, 236, 228, 0.42));
	white-space: nowrap;
}

@media (max-width: 560px) {
	.nsn-about__press-row {
		grid-template-columns: minmax(0, 1fr) auto;
	}

	.nsn-about__press-type {
		grid-column: 1;
		grid-row: 2;
	}
}

/* --------------------------------------------------------------------------
   6. Closing
   -------------------------------------------------------------------------- */
.nsn-about__cta {
	max-width: 34ch;
}

.nsn-about__cta-title {
	margin: 0;
	font-size: clamp(1.9rem, 1rem + 3.6vw, 3.6rem);
	font-weight: 700;
	line-height: 1.06;
	letter-spacing: -0.015em;
	text-wrap: balance;
}

.nsn-about__cta-body {
	max-width: var(--nsn-about-measure);
	margin-top: 1.2rem;
	font-size: var(--nsn-step-sm);
	line-height: 1.65;
	color: var(--nsn-bone-70, rgba(240, 236, 228, 0.72));
}

.nsn-about__cta-body p {
	margin: 0 0 1em;
}

.nsn-about__cta-body p:last-child {
	margin-bottom: 0;
}

.nsn-about__cta-action {
	margin: clamp(1.8rem, 4vh, 2.6rem) 0 0;
}

.nsn-about__cta-link {
	display: inline-flex;
	gap: 0.7em;
	align-items: baseline;

	font-size: var(--nsn-step-sm);
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	text-decoration: none;
	color: var(--nsn-about-accent);

	padding-bottom: 0.35em;
	border-bottom: 1px solid currentColor;
	transition: opacity 320ms ease;
}

.nsn-about__cta-link:hover,
.nsn-about__cta-link:focus-visible {
	opacity: 0.72;
}

.nsn-about__cta-arrow {
	transition: transform 420ms cubic-bezier(0.22, 1, 0.36, 1);
}

.nsn-about__cta-link:hover .nsn-about__cta-arrow,
.nsn-about__cta-link:focus-visible .nsn-about__cta-arrow {
	transform: translateX(5px);
}

/* ==========================================================================
   Reveal
   --------------------------------------------------------------------------
   Armed only once JavaScript is present, so without it every section is simply
   visible from the start. The reduced-motion class the head script sets is
   honoured here too, so nothing is ever hidden waiting for an animation that
   will not run.
   ========================================================================== */

html.nsn-js .nsn-about [data-nsn-about-reveal],
html.nsn-js .nsn-about [data-nsn-about-row] {
	opacity: 0;
}

html.nsn-js .nsn-about__word > span {
	transform: translateY(105%);
}

/*
 * The revealing rules carry html.nsn-js too. Without it they weigh 0,3,0
 * against the hiding rule's 0,3,1 and lose, and an element would then only
 * ever be visible because of the inline opacity GSAP leaves behind - so any
 * path that reveals without tweening, or that clears those inline styles
 * afterwards, would silently leave the element at zero.
 */
html:not(.nsn-js) .nsn-about [data-nsn-about-reveal],
html:not(.nsn-js) .nsn-about [data-nsn-about-row],
html.nsn-reduced-motion .nsn-about [data-nsn-about-reveal],
html.nsn-reduced-motion .nsn-about [data-nsn-about-row],
html.nsn-js .nsn-about [data-nsn-about-reveal].is-revealed,
html.nsn-js .nsn-about [data-nsn-about-row].is-revealed {
	opacity: 1;
}

html:not(.nsn-js) .nsn-about__word > span,
html.nsn-reduced-motion .nsn-about__word > span,
.nsn-about__title.is-revealed .nsn-about__word > span {
	transform: none;
}

@media (prefers-reduced-motion: reduce) {
	.nsn-about [data-nsn-about-reveal],
	.nsn-about [data-nsn-about-row] {
		opacity: 1 !important;
	}

	.nsn-about__word > span {
		transform: none !important;
	}

	.nsn-about__body,
	.nsn-about__thumb,
	.nsn-about__portrait-img,
	.nsn-about__reel-media,
	.nsn-about__cta-arrow {
		transition: none !important;
	}

	.nsn-about__reel-media {
		transform: none;
	}
}
