/* ==========================================================================
   Contact
   --------------------------------------------------------------------------
   One column, read top to bottom, on the site's ink. Everything is driven by
   the shared ink and bone tokens - there is no Contact palette - and by the
   few type sizes declared below, which are deliberately editorial rather than
   display: this page is a set of details someone needs to read and copy, not
   a poster.
   ========================================================================== */

.nsn-contact {
	/* The rail's column, so the content can keep clear of it on desktop. */
	--nsn-rail-w: clamp(54px, 5vw, 76px);

	/*
	 * Type. The statement is the largest thing here and still lands well under
	 * the Work display sizes. Every step is fluid, so nothing needs correcting
	 * per breakpoint and nothing can collide at a width in between.
	 */
	--nsn-contact-heading: clamp(1.75rem, 1.1rem + 2.2vw, 2.75rem);
	--nsn-contact-intro:   clamp(1rem, 0.95rem + 0.35vw, 1.25rem);
	--nsn-contact-value:   clamp(1.0625rem, 0.98rem + 0.45vw, 1.375rem);

	/* The reading measure, matching the Journal article. */
	--nsn-contact-measure: 62ch;

	/*
	 * The close control's diameter, mirroring .nsn-rail__close in the Work
	 * detail sheet. Used on narrow screens to keep text out of its corner.
	 */
	--nsn-contact-control: 44px;

	/*
	 * The one accent on the site. It belongs to the email and nothing else:
	 * this page has a single thing it wants you to do, and the colour says so.
	 * 6.6:1 against the ink, so it is readable type rather than decoration.
	 */
	--nsn-contact-accent: #FF6548;

	/*
	 * The email is the page's action and the largest thing on it - set across
	 * the portrait rather than beside it, so the two read as one composition.
	 * Held below the point where a long address would reach the gutters.
	 */
	--nsn-contact-email: clamp(2rem, 1.1rem + 5.4vw, 5.5rem);

	/*
	 * How far the address may spread. It is deliberately not the picture's
	 * width - crossing the picture is the composition - but it stops at the
	 * page's own measure so it can never reach the gutters.
	 */
	--nsn-contact-cross: min(calc(100vw - var(--nsn-gutter) * 2), 1180px);

	position: relative;
	background: var(--nsn-ink);
	color: var(--nsn-bone);
}

/* --------------------------------------------------------------------------
   The composition

   One screen, read as one picture. The band stands down the middle on the
   site's ink, the address is set across its centre, and two hairlines bracket
   the whole thing: the statement sits above the first, the details below the
   second. Nothing scrolls on a desktop screen - the page is the composition.

   The band is positioned rather than placed in flow, and the address on top of
   it likewise, so the two are centred on the viewport itself rather than on
   whatever height the statement and the details happen to need. That is what
   keeps the address on the centre line at every width without a number
   anywhere that has to be kept in step.
   -------------------------------------------------------------------------- */

.nsn-contact {
	min-height: 100svh;

	/*
	 * The band's width, measured off the reference at 1440. Held to a share of
	 * the viewport as well, so it narrows with the page rather than crowding
	 * the corners on a small desktop.
	 */
	--nsn-contact-band: min(36vw, 507px);

	/* The bracket the rules and the corners are set to. */
	--nsn-contact-frame: min(1180px, calc(100vw - var(--nsn-gutter) * 2));

	/*
	 * How far each hairline sits from the centre line. The reference's own
	 * distance is 329.5px on a 900px screen - 36.6% of it - and this keeps that
	 * proportion while holding the pair clear of the address on a short screen
	 * and clear of the corners on a tall one.
	 */
	--nsn-contact-bracket: clamp(150px, 30svh, 320px);

	/*
	 * Snapped to whole pixels, and held clear of the header.
	 *
	 * A single-pixel rule at a fractional y is spread across two rows of
	 * device pixels, and the two rules landed on different fractions -
	 * measured at 1440x900, the upper at y=120.594 and the lower at 779.391.
	 * Both were exactly 1px tall; one simply antialiased harder than the other,
	 * which is what made the lower one look the thicker of the two. round()
	 * puts both on a whole pixel, so both draw as one crisp row.
	 *
	 * The min() is the header clearance: without it the upper rule sat eight
	 * pixels under the navigation. Stated after the plain value above so a
	 * browser without round() keeps the simpler one rather than nothing.
	 */
	--nsn-contact-bracket: round(
		min(
			clamp(150px, 30svh, 320px),
			calc(50svh - var(--nsn-header-h) - var(--nsn-space-4))
		),
		1px
	);

	/*
	 * How far each hairline reaches past the picture on either side. Derived
	 * from the band rather than set as a width of its own, so the two always
	 * frame the picture by the same margin whatever the picture is doing - and
	 * clamped, so the overhang shrinks with the page instead of a desktop
	 * figure running off a phone. The width is then capped at the frame, which
	 * stops it reaching the gutters - and floored at the band, because a rule
	 * narrower than the picture it is meant to frame reads as a mistake. On a
	 * phone, where the picture already takes almost the whole screen, those two
	 * meet and the overhang simply goes to nothing.
	 */
	--nsn-contact-overhang: clamp(24px, 16vw, 230px);
	--nsn-contact-rule-w: max(
		var(--nsn-contact-band),
		min(calc(var(--nsn-contact-band) + var(--nsn-contact-overhang) * 2), 100%)
	);

	/*
	 * The band is taller than the screen and the shutters travel past their own
	 * edges, so both would widen the document if anything could see past the
	 * section. clip rather than hidden: hidden would make this a scroll
	 * container and take the close control's sticky positioning with it.
	 */
	overflow: clip;
}

.nsn-contact__band {
	position: absolute;
	top: 0;
	left: 50%;
	z-index: 1;
	width: var(--nsn-contact-band);
	height: 100%;
	min-height: 100svh;
	margin: 0;
	transform: translateX(-50%);
	overflow: hidden;
}

/*
 * The scrim.
 *
 * The picture is the ground of this composition rather than its subject: the
 * details sit over the top and foot of it and the address across its middle,
 * and a photograph at full strength makes every one of those unreadable -
 * measured on the first build, the type all but disappeared into a bright road
 * surface. Heavier at the two ends, where the labels are, and lighter through
 * the middle, where the picture is doing the work and only the address crosses
 * it.
 */
.nsn-contact__band::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background: linear-gradient(
		to bottom,
		rgba(15, 15, 14, 0.9) 0%,
		rgba(15, 15, 14, 0.62) 26%,
		rgba(15, 15, 14, 0.46) 48%,
		rgba(15, 15, 14, 0.66) 72%,
		rgba(15, 15, 14, 0.92) 100%
	);
	pointer-events: none;
}

.nsn-contact__band-img {
	display: block;
	width: 100%;
	height: 100%;

	/*
	 * The band is a shape the layout chose, so the picture is cropped to it
	 * rather than letterboxed inside it. Centred, because the subject of a
	 * portrait is in the middle of the frame.
	 */
	object-fit: cover;
	object-position: 50% 50%;
}

/*
 * The two ink shutters.
 *
 * At rest they meet in the middle and the band is simply part of the black
 * ground - which is what the page looks like before the entrance runs, and
 * what it looks like if the entrance never runs at all. Drawing them apart is
 * the reveal: the picture is uncovered from its centre outward and the black
 * appears to retract off both edges.
 *
 * Slightly past half each, so a fractional band width cannot leave a hairline
 * of picture showing down the middle before they move.
 */
.nsn-contact__shutter {
	position: absolute;
	z-index: 2;
	top: -1px;
	bottom: -1px;
	width: 50.5%;
	background: var(--nsn-ink);
	will-change: transform;
}

.nsn-contact__shutter--start {
	left: 0;
}

.nsn-contact__shutter--end {
	right: 0;
}

/* --------------------------------------------------------------------------
   The column over it
   -------------------------------------------------------------------------- */

.nsn-contact__inner {
	position: relative;
	z-index: 2;
	min-height: 100svh;
	max-width: var(--nsn-contact-frame);
	margin: 0 auto;

	/*
	 * Clears the fixed header at the top and the rail on the left. The rail's
	 * column is reserved on both sides, not just the one it sits in: padding
	 * only on the left pushed the whole composition right of the page's centre,
	 * which is visible on a page whose subject is a centred picture.
	 */
	padding: calc(var(--nsn-header-h) + var(--nsn-space-3)) 0 var(--nsn-space-4);
}

/* --------------------------------------------------------------------------
   The two hairlines

   Drawn as scaled boxes rather than borders, because they are animated: the
   upper one is drawn from the left and the lower one from the right, and a
   border cannot be drawn from an end. transform-origin is what says which end,
   and it is the only difference between them.
   -------------------------------------------------------------------------- */

.nsn-contact__rule {
	position: absolute;
	left: 50%;
	z-index: 2;
	width: var(--nsn-contact-rule-w);
	height: 1px;
	margin-left: calc(var(--nsn-contact-rule-w) / -2);

	/*
	 * Full white, not the bone the rest of the site rules are drawn in. These
	 * two are structure rather than furniture - they are what frames the
	 * picture and holds the address between them - and at a single pixel over
	 * a photograph the bone tint at 28% read as grey. There is no full-white
	 * token on the site, so the value is stated: every other white here is
	 * bone, and this is deliberately not that.
	 */
	background: #fff;
	transform-origin: left center;
	will-change: transform;
}

/*
 * One above the address and one below it, the same distance from the centre
 * line each - which is the reference's own arrangement: measured at 1440 its
 * rules sit at y=120 and y=779 with the address centred at 449.5, exactly
 * 329.5px either side. Stated as a single offset from the centre, so the two
 * cannot drift apart when the type size or the viewport changes.
 *
 * transform-origin is the only difference between them, and it is what lets
 * one be drawn from the left while the other is drawn from the right.
 */
.nsn-contact__rule--start {
	top: calc(50% - var(--nsn-contact-bracket));
}

.nsn-contact__rule--end {
	top: calc(50% + var(--nsn-contact-bracket));
	transform-origin: right center;
}

/* --------------------------------------------------------------------------
   The address, across the middle of the picture
   -------------------------------------------------------------------------- */

/*
 * Centred on the viewport rather than on the column, and out of flow, so the
 * statement above it and the details below it can be whatever height they need
 * without moving it off the centre line.
 *
 * It takes no pointer events of its own - only the link does - or an invisible
 * box the width of the page would sit over the details and swallow every click
 * meant for them.
 */
.nsn-contact__centre {
	position: absolute;
	top: 50%;
	left: 50%;
	z-index: 3;
	width: var(--nsn-contact-cross);
	transform: translate(-50%, -50%);
	text-align: center;
	pointer-events: none;
}

.nsn-contact__email {
	display: inline-flex;
	align-items: baseline;
	gap: 0.18em;
	position: relative;
	max-width: 100%;
	font-size: var(--nsn-contact-email);
	line-height: 1.04;
	letter-spacing: -0.02em;
	text-transform: lowercase;
	color: var(--nsn-contact-accent);
	pointer-events: auto;
}

/*
 * The clipped line the two faces roll through.
 *
 * Its height is one line box, so whichever face is outside it is simply not
 * painted - there is nothing to fade and no ghost to leave behind. The leading
 * is loose enough to carry the descenders of the face that is arriving, since
 * the clip cuts at the padding edge and a tight line box would slice them.
 */
.nsn-contact__email-line {
	display: block;
	position: relative;
	overflow: hidden;

	/*
	 * The clip and one character have to be exactly the same height, because
	 * a character is moved by 100% of itself and has to land exactly one clip
	 * away. Padding here broke that: the box was taller than the characters
	 * in it, so the waiting copy came to rest a padding short of gone and the
	 * tops of its letters showed along the bottom edge as a row of dashes.
	 * The leading is what carries the descenders instead - generous enough
	 * that nothing is sliced, and identical on both.
	 */
	line-height: 1.2;
}

.nsn-contact__email-face {
	display: block;
	white-space: nowrap;
}

/*
 * The copy that waits a line below. Stated here as well as in JavaScript so it
 * is already out of sight on the first paint, and so the page is correct if the
 * script never runs.
 */
.nsn-contact__email-face--in {
	position: absolute;
	inset: 0;
}

/*
 * The waiting copy is held down a line character by character, not face by
 * face. The characters are what move on hover - each on its own clock - so
 * they have to be what carries the resting offset too, or the offset and the
 * movement would be on two different boxes and the copy would start a line
 * further down than it should.
 */
.nsn-contact__email-face--in .nsn-contact__char {
	transform: translateY(100%);
}

/*
 * One box per character. inline-block is what gives each its own transform;
 * without it they are text and move as one line.
 */
.nsn-contact__char {
	display: inline-block;
	will-change: transform;
}

/* --------------------------------------------------------------------------
   The arrow, and the line under the whole thing
   -------------------------------------------------------------------------- */

.nsn-contact__email-arrow {
	display: block;
	position: relative;
	flex: none;
	width: 0.62em;
	height: 0.62em;
	overflow: hidden;
}

.nsn-contact__arrow-face {
	display: block;
	position: absolute;
	inset: 0;
}

/* The copy waiting below, exactly as the address has one. */
.nsn-contact__arrow-face--in {
	transform: translateY(100%);
}

.nsn-contact__arrow-svg {
	display: block;
	width: 100%;
	height: 100%;
	overflow: visible;
}

.nsn-contact__email:focus-visible {
	outline: 2px solid var(--nsn-contact-accent);
	outline-offset: 6px;
}

/* --------------------------------------------------------------------------
   The corners

   Where the studio is, at the top left; where else to find it, at the bottom
   right. Out of flow, so neither can push the address off the centre line
   however long it is, and each is anchored to its own corner of the frame
   rather than to the one before it.
   -------------------------------------------------------------------------- */

.nsn-contact__aside {
	position: absolute;
	z-index: 2;
	max-width: min(34%, 22rem);
}

/*
 * The insets are stated rather than left to the frame's padding. An absolutely
 * positioned box is placed against its ancestor's padding box, so padding does
 * not move it: the label sat at the very top of the page, under the header,
 * until these were written out.
 */
/*
 * Set to the upper rule rather than to the frame: its left edge is the rule's
 * left edge, and it sits just under it. Both are placed against the same
 * containing block and both are driven by --nsn-contact-rule-w, so the two
 * cannot drift apart at any width.
 */
.nsn-contact__aside--start {
	top: calc(50% - var(--nsn-contact-bracket) + var(--nsn-space-2));
	left: calc(50% - var(--nsn-contact-rule-w) / 2);
}

/*
 * White, and stated on both classes the element carries. .nsn-contact__label
 * sets the bone all the other labels use and sits later in this sheet, so on
 * one class alone it won the tie and the word stayed bone.
 */
.nsn-contact__aside--start.nsn-contact__label {
	color: #fff;
}

/*
 * The foot is one row set to the lower rule, exactly as the address corner is
 * set to the upper one: the word at the left end of the rule, the links at the
 * right end, and the whole row sitting just above the line rather than under
 * it. Kept as a single block rather than two, so the label and the list it
 * labels stay associated for anything reading the page rather than looking at
 * it, while still landing in the two corners the composition wants.
 *
 * bottom rather than top, and the same step of clearance the address gets, so
 * the two corners are the same distance from their own rule - the composition
 * reads as symmetrical because it is.
 */
.nsn-contact__aside--end {
	bottom: calc(50% - var(--nsn-contact-bracket) + var(--nsn-space-2));
	right: calc(50% - var(--nsn-contact-rule-w) / 2);
	left: calc(50% - var(--nsn-contact-rule-w) / 2);
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: var(--nsn-space-3);
	max-width: none;
	text-align: right;
}

/*
 * White, and stated on both classes for the same reason the address label is:
 * .nsn-contact__label sets the bone every other label uses and sits later in
 * this sheet, so on one class alone it wins the tie.
 */
.nsn-contact__aside--end .nsn-contact__label {
	color: #fff;
}

.nsn-contact__label {
	margin: 0;
	font-size: var(--nsn-step-xs);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--nsn-bone-46);
}

.nsn-contact__social {
	margin: 0;
	padding: 0;
	list-style: none;
}

.nsn-contact__social li + li {
	margin-top: 0.35rem;
}

.nsn-contact__link {
	font-size: var(--nsn-contact-value);
	line-height: 1.4;
	color: var(--nsn-bone);
	text-decoration: none;
	border-bottom: 1px solid var(--nsn-bone-28);
	transition: border-color var(--nsn-dur, 0.28s) ease, color var(--nsn-dur, 0.28s) ease;
}

.nsn-contact__link:hover,
.nsn-contact__link:focus-visible {
	color: var(--nsn-contact-accent);
	border-bottom-color: var(--nsn-contact-accent);
}

/* --------------------------------------------------------------------------
   The resting state the entrance starts from

   Armed only when JavaScript is present, so without it the page is simply
   whole: the shutters are open, the rules are drawn and the address is there.
   -------------------------------------------------------------------------- */

html.nsn-js .nsn-contact__aside,
html.nsn-js .nsn-contact__centre {
	opacity: 0;
}

html.nsn-js .nsn-contact__rule {
	transform: scaleX(0);
}

/*
 * No script, or reduced motion: the picture is simply uncovered from the
 * start. Everywhere else the shutters belong to GSAP, which draws them apart
 * as part of the entrance.
 */
html:not(.nsn-js) .nsn-contact__shutter--start,
html.nsn-reduced-motion .nsn-contact__shutter--start {
	transform: translateX(-101%);
}

html:not(.nsn-js) .nsn-contact__shutter--end,
html.nsn-reduced-motion .nsn-contact__shutter--end {
	transform: translateX(101%);
}

/*
 * Reduced motion, and the no-JavaScript page: everything is simply present.
 * The entrance is the only thing that goes.
 */
html:not(.nsn-js) .nsn-contact__aside,
html:not(.nsn-js) .nsn-contact__centre,
html.nsn-reduced-motion .nsn-contact__aside,
html.nsn-reduced-motion .nsn-contact__centre,
.nsn-contact.is-entered .nsn-contact__aside,
.nsn-contact.is-entered .nsn-contact__centre {
	opacity: 1;
}

html:not(.nsn-js) .nsn-contact__rule,
html.nsn-reduced-motion .nsn-contact__rule,
.nsn-contact.is-entered .nsn-contact__rule {
	transform: scaleX(1);
}


/* --------------------------------------------------------------------------
   Narrow viewports

   There is no left gutter to put a rail in on a phone, so the control moves
   to the foot of the screen where it is easy to reach, and the column takes
   the full width.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
	.nsn-contact {
		--nsn-rail-w: 0px;

		/*
		 * The band takes nearly the width of the screen. It is still the
		 * middle of the composition; there is simply no room either side of it
		 * for the black to be a margin rather than an edge.
		 */
		--nsn-contact-band: min(92vw, 520px);
		--nsn-contact-cross: calc(100vw - var(--nsn-gutter) * 2);
	}

	/*
	 * The frame keeps its own width here rather than going full-bleed with
	 * padding. The corners are absolutely positioned, and an absolutely
	 * positioned box is placed against the padding box - so padding gave them
	 * no inset at all and the labels sat flush against the screen edge.
	 * Narrowing the frame is what moves them in.
	 */
	.nsn-contact__inner {
		padding-top: calc(var(--nsn-header-h) + var(--nsn-space-4));
	}

	/*
	 * The details stack rather than sitting either side. Both read left, in
	 * reading order, because a right-aligned column of two links against the
	 * screen edge is harder to scan than a second block under the first.
	 */
	/*
	 * The corners stay corners, but they are given less of the width so the
	 * one at the top and the one at the foot cannot reach the middle.
	 */
	/* The foot is a full-width row; only the corner at the top is narrowed. */
	.nsn-contact__aside--start {
		max-width: 46%;
	}

	/* The corner is set to the rule at every width, so it keeps that left edge. */
	.nsn-contact__aside--start {
		left: calc(50% - var(--nsn-contact-rule-w) / 2);
	}

}

/*
 * Very narrow: the address is the one thing that cannot be allowed to reach
 * the gutters, and at this width even the fluid size gets close.
 */
@media (max-width: 480px) {
	.nsn-contact {
		--nsn-contact-email: clamp(1.5rem, 1rem + 4.4vw, 2rem);
	}
}

@media (prefers-reduced-motion: reduce) {
	.nsn-contact__aside,
	.nsn-contact__centre {
		opacity: 1 !important;
	}

	.nsn-contact__rule {
		transform: scaleX(1) !important;
	}

	/*
	 * The roll is the part that goes. Both faces stay exactly where they rest,
	 * so the address is one address and the copy below it is never seen; the
	 * underline is a state rather than motion, so it is drawn at once.
	 */
	.nsn-contact__char,
	.nsn-contact__arrow-face {
		transition: none !important;
	}

	.nsn-contact__link {
		transition-duration: 0.01ms;
	}
}
