/* ==========================================================================
   DJ ART — Los Angeles & Orange County
   Black ground, off-white type, one warm note: the brass of the saxophone,
   sampled from ARTBAND.mp4. The footage is shot on a white cyclorama, so it
   is graded down to sit inside the dark page instead of burning through it.
   ========================================================================== */

:root {
	--bg:        #0B0B0D;
	--bg-sunk:   #121216;
	--bg-lift:   #17171C;

	--text:      #F5F5F5;
	--text-soft: rgba(255, 255, 255, .85);
	--text-mute: rgba(255, 255, 255, .55);

	--brass:      #C89A3C;  /* saxophone, sampled */
	--brass-deep: #A87C26;

	--line:      rgba(245, 245, 245, .13);
	--line-soft: rgba(245, 245, 245, .07);

	--display: 'Fragment Mono', ui-monospace, monospace;
	--mono:    'Fragment Mono', ui-monospace, monospace;
	--body:    'Plus Jakarta Sans', system-ui, sans-serif;
	--script:  'Mr Dafoe', cursive;

	--gutter: clamp(20px, 5vw, 56px);
	--header-h: 76px;

	/* One design pixel. Every hero and header number below is the literal value
	   from the Figma frame, which is 1797px wide, so the screen scales as one
	   piece instead of being re-derived by eye. */
	--u: calc(100vw / 1797);

	/* The frame draws square buttons, but the wordmark is a pill — a small
	   radius everywhere ties the two together. */
	--radius: 6px;

	/* elastic.out(0.5, 0.6) as GSAP actually resolves it: the period is divided
	   by the amplitude, so it is 1.2 — barely one oscillation across the tween,
	   which is why the arrival reads as a soft settle and not a wobble.
	   f(t) = 2^(-10t)·sin((t − 0.3)·2π/1.2) + 1, peaking 3.2% past the target.
	   A cubic-bezier cannot overshoot and return, hence linear(). */
	--ease-elastic: linear(0, 0.1663, 0.317, 0.4507, 0.567, 0.6664, 0.75, 0.819, 0.875, 0.9196, 0.9542, 0.9806, 1, 1.0137, 1.0229, 1.0284, 1.0312, 1.032, 1.0312, 1.0295, 1.0271, 1.0243, 1.0213, 1.0184, 1.0156, 1.013, 1.0107, 1.0086, 1.0068, 1.0052, 1.0039, 1.0028, 1.002, 1.0013, 1.0007, 1.0003, 1, 0.9998, 0.9996, 0.9996, 1);

	/* motion's spring(stiffness 280, damping 20, mass 1), sampled over 0.65s:
	   ζ ≈ 0.6, one 9.55% overshoot with a visible settle. Livelier than
	   --ease-elastic — used where a hover should feel like a real spring. */
	--ease-spring: linear(0, 0.0331, 0.1176, 0.2341, 0.3663, 0.5015, 0.6306, 0.7469, 0.8469, 0.9288, 0.9924, 1.0388, 1.0699, 1.088, 1.0955, 1.095, 1.0887, 1.0785, 1.0663, 1.0532, 1.0405, 1.0288, 1.0185, 1.0099, 1.003, 0.9979, 0.9943, 0.9921, 0.991, 0.9908, 0.9912, 0.992, 0.9931, 0.9944, 0.9956, 0.9968, 0.9978, 0.9987, 0.9995, 1, 1);
}

*, *::before, *::after { box-sizing: border-box; }

/* scroll-behavior здесь больше нет: он ломает замеры ScrollTrigger —
   закрепление галереи считалось от смещённой позиции. Плавность
   перехода по якорям меню делает скрипт. */
html {
	-webkit-text-size-adjust: 100%;
	/* Место под вертикальную полосу держится всегда. Иначе при открытии окна
	   (там у body снимается прокрутка) полоса исчезает, страница становится на
	   её ширину шире, а на закрытии возвращается — и в этот кадр содержимое
	   оказывается шире окна, отчего внизу мигает горизонтальная полоса. */
	scrollbar-gutter: stable;
	/* Отсечка по горизонтали. Именно clip, а не hidden: hidden делает элемент
	   контейнером прокрутки и ломает position: sticky и расчёты ScrollTrigger,
	   а clip просто обрезает и ничего не создаёт. */
	overflow-x: clip;
}

body {
	margin: 0;
	background: var(--bg);
	color: var(--text);
	font-family: var(--body);
	font-weight: 500;
	font-size: 16px;
	line-height: 1.6;
	letter-spacing: -.02em;
	-webkit-font-smoothing: antialiased;
}

img, video { display: block; max-width: 100%; }
ul { list-style: none; margin: 0; padding: 0; }
h1, h2, h3, p, blockquote, figure { margin: 0; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

:focus-visible {
	outline: 1px solid var(--brass);
	outline-offset: 4px;
}

/* Full-bleed: no centred measure, just a gutter on each side. */
.container {
	width: 100%;
	padding-inline: var(--gutter);
}

/* --------------------------------------------------------------- primitives */

.eyebrow {
	font-family: var(--mono);
	font-size: .72rem;
	font-weight: 400;
	letter-spacing: .26em;
	text-transform: uppercase;
	color: var(--brass);
}

.section__title {
	font-family: var(--display);
	font-weight: 400;
	font-size: clamp(1.6rem, 3.2vw, 2.6rem);
	line-height: 1.25;
	letter-spacing: -.01em;
	text-transform: uppercase;
}

/* One warm, hand-drawn note against all the thin Inter. Script faces sit
   small next to a sans, so it needs a size bump to match optically. */
.script {
	font-family: var(--script);
	font-style: normal;
	font-weight: 400;
	font-size: 1.35em;
	letter-spacing: 0;
	color: var(--brass);
}

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 1.05rem 2.2rem;
	background: var(--text);
	color: var(--bg);
	font-family: var(--mono);
	font-size: .76rem;
	letter-spacing: .18em;
	text-transform: uppercase;
	white-space: nowrap;
	border-radius: var(--radius);
	transition: background .3s ease, color .3s ease;
}

.btn:hover { background: var(--brass); color: #14100A; }

/* Flip on hover, ported from the FlipButton pattern: the label tips away over
   the top edge and the alternate face swings in from above, both riding the
   same elastic curve as the preloader cards. The frame — border, size,
   radius, type — never moves; only the faces travel. The back face is the
   button's old hover inversion, now carrying the phone number. */
.btn-flip {
	position: relative;
	perspective: 1000px;
	transition: transform .2s ease;
}

.btn-flip:active { transform: scale(.95); }

/* The faces carry the fill now — the frame itself must not repaint on hover. */
.btn:hover.btn-flip,
.hero__btn:hover.btn-flip { background: none; color: #F5F5F5; }

.btn-flip__face {
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
	border-radius: inherit;
	backface-visibility: hidden;
	/* In the original, opacity rides the same spring as the rotation — fading
	   faster than the turn flattens the flip into a crossfade and the 3D
	   never reads. Same duration, same curve (opacity just clamps at 1). */
	transition: transform .65s var(--ease-spring), opacity .65s var(--ease-spring);
}

.btn-flip__front { opacity: 1; transform: none; }

.btn-flip__back {
	opacity: 0;
	transform: translateY(-50%) rotateX(90deg);
	background: #F5F5F5;
	color: var(--bg);
}

.btn-flip:hover .btn-flip__front,
.btn-flip:focus-visible .btn-flip__front {
	opacity: 0;
	transform: translateY(50%) rotateX(90deg);
}

.btn-flip:hover .btn-flip__back,
.btn-flip:focus-visible .btn-flip__back {
	opacity: 1;
	transform: none;
}

.btn--sm {
	width: calc(165.2 * var(--u));
	height: calc(41.76 * var(--u));
	padding: 0;
	font-size: calc(10.88 * var(--u));
	line-height: calc(17.408 * var(--u));
	letter-spacing: calc(1.9584 * var(--u));
	text-indent: calc(1.9584 * var(--u));
}

.btn--ghost {
	background: none;
	color: var(--text);
	border: 1px solid var(--text);
}

.btn--ghost:hover { background: var(--text); color: var(--bg); border-color: var(--text); }

.link {
	display: inline-flex;
	align-items: baseline;
	gap: .6em;
	font-family: var(--mono);
	font-size: .76rem;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--text);
	padding-bottom: .4em;
	border-bottom: 1px solid var(--line);
	transition: color .3s ease, border-color .3s ease;
}

.link::after { content: '→'; transition: transform .3s ease; }

.link:hover { color: var(--brass); border-bottom-color: var(--brass); }
.link:hover::after { transform: translateX(4px); }

.logo { display: block; line-height: 0; }

.logo__img {
	width: calc(88.375 * var(--u));
	height: calc(40 * var(--u));
}

/* ---------------------------------------------------------------- preloader */

/* Intro: a brass field, then three cards fly in from the right on a stagger —
   the two ahead read as a motion trail, the last one carries the mark and lands
   on top. The pile then opens out to fill the screen, lifts away upward, and
   the page rises from below to take its place. */
.preloader {
	position: fixed;
	inset: 0;
	z-index: 100;
	overflow: hidden;
	transition: transform .95s cubic-bezier(.76, 0, .24, 1);
}

.preloader.is-done { transform: translateY(-100%); }

body.is-loading { overflow: hidden; }

/* Not a colour field — a dark room with one warm light off to the side. A
   saturated flash before a black page reads cheap and hurts the eye; the
   restraint is the luxury. */
.preloader__field {
	position: absolute;
	inset: 0;
	background: linear-gradient(118deg, #17171A 0%, #101013 48%, #08080A 100%);
}

/* All four cards are identical and land on exactly the same spot; the trail
   you see on the way in is only the .075s stagger between them. Every card
   runs the same duration and the same curve — only its delay differs. */
/* The track the cards ride along. It sits on the same centre line the cards
   travel down, so the motion reads as a playhead running across a waveform.
   Masked to nothing at both ends so it has no start and no finish. */
.preloader__wave {
	position: absolute;
	top: 50%;
	left: 0;
	right: 0;
	height: clamp(90px, 13vh, 150px);
	transform: translateY(-50%);
	opacity: 0;
	-webkit-mask-image: linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
	mask-image: linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
	animation: wave-in .45s ease .05s forwards;
}

@keyframes wave-in {
	to { opacity: 1; }
}

/* When the shader is live it owns the whole frame and tapers its own edges,
   so the host drops the strip height and the mask. */
.preloader__wave.is-gl {
	top: 0;
	height: 100%;
	transform: none;
	-webkit-mask-image: none;
	mask-image: none;
}

.preloader__wave canvas {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
}

.preloader__wave.is-gl .preloader__line { display: none; }

/* Fallback when WebGL2 is unavailable: one curve layered four times at
   different vertical scales and speeds, drifting out of phase. */
.preloader__line {
	position: absolute;
	inset: 0;
	background: url("../assets/img/wave.svg") center / 100% 100% no-repeat;
	transform-origin: 50% 50%;
}

.preloader__line:nth-child(1) { opacity: .85; animation: wave-a 3.1s ease-in-out infinite; }
.preloader__line:nth-child(2) { opacity: .5;  animation: wave-b 4.3s ease-in-out infinite; }
.preloader__line:nth-child(3) { opacity: .32; animation: wave-c 5.6s ease-in-out infinite; }
.preloader__line:nth-child(4) { opacity: .2;  animation: wave-d 7.2s ease-in-out infinite; }

@keyframes wave-a {
	0%, 100% { transform: scaleY(.55) translateX(0); }
	28%      { transform: scaleY(1.15) translateX(-1.5%); }
	61%      { transform: scaleY(.78) translateX(1.2%); }
}

@keyframes wave-b {
	0%, 100% { transform: scaleY(-.9) translateX(1%); }
	35%      { transform: scaleY(-.45) translateX(-2%); }
	70%      { transform: scaleY(-1.2) translateX(.8%); }
}

@keyframes wave-c {
	0%, 100% { transform: scaleY(1.35) translateX(-2.5%); }
	44%      { transform: scaleY(.7) translateX(1.8%); }
}

@keyframes wave-d {
	0%, 100% { transform: scaleY(-1.6) translateX(2%); }
	52%      { transform: scaleY(-.9) translateX(-2.2%); }
}

.preloader__card {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 723px;
	height: 425px;
	max-width: 92vw;
	max-height: 92vh;
	border-radius: 15px;
	display: grid;
	place-items: center;
	/* Left edge starts exactly on the right edge of the viewport. */
	transform: translate(-50%, -50%) translateX(calc(50vw + 50%));
	animation:
		card-fly .7s var(--ease-elastic) both,
		card-open .55s cubic-bezier(.76, 0, .24, 1) forwards;
}

@keyframes card-fly {
	to { transform: translate(-50%, -50%) translateX(0); }
}

/* Opening is staggered too, so the cards read as nested frames telescoping
   outwards — the back one first, the one holding the mark last. */
@keyframes card-open {
	to {
		width: 100vw;
		height: 100vh;
		max-width: none;
		max-height: none;
		border-radius: 0;
	}
}

/* Neutral greys stepping down to the page's own black — no colour cast, so the
   brass stays an accent of the site itself rather than of the intro. */
.preloader__card--1 { background: #9C9CA2;   z-index: 1; animation-delay: .15s,  1.15s; }
.preloader__card--2 { background: #5E5E65;   z-index: 2; animation-delay: .225s, 1.225s; }
.preloader__card--3 { background: #2C2C31;   z-index: 3; animation-delay: .3s,   1.3s; }
.preloader__card--4 { background: var(--bg); z-index: 4; animation-delay: .375s, 1.375s; }

/* No animation of its own — the mark simply rides in on the last card. Sized
   by height so the lockup's width follows rather than the other way round. */
/* The mark never travels on its own. It grows with the card that carries it —
   65px against a 723px card is the same ratio as 9vw against a full screen —
   and develops from the card's own tone up to full contrast as it opens. */
.preloader__mark {
	height: 65px;
	width: auto;
	max-width: 70%;
	filter: brightness(.16);
	animation:
		mark-grow .55s cubic-bezier(.76, 0, .24, 1) 1.375s forwards,
		mark-develop .6s ease 1.4s forwards;
}

@keyframes mark-grow {
	to { height: min(9vw, 190px); }
}

@keyframes mark-develop {
	to { filter: brightness(1); }
}

/* Belongs to the ground, not to the card: it is there from the first frame and
   stays put while the cards fly across and open over it. */
.preloader__since {
	position: absolute;
	inset: auto 0 clamp(1.5rem, 4.5vh, 3.25rem) 0;
	z-index: 5;
	text-align: center;
	font-family: var(--display);
	font-size: .66rem;
	letter-spacing: .42em;
	text-indent: .42em;   /* balances the trailing letter-space */
	text-transform: uppercase;
	color: rgba(255, 255, 255, .5);
	opacity: 0;
	animation: since-in .5s ease .1s forwards;
}

@keyframes since-in {
	to { opacity: 1; }
}

/* The curtain leaves upward by exactly one viewport while the page arrives
   from exactly one viewport below, so their edges travel as a single seam. */
main {
	transition: transform .95s cubic-bezier(.76, 0, .24, 1);
}

body.is-loading main { transform: translateY(100vh); }

.header { transition: opacity .5s ease .3s; }
body.is-loading .header { opacity: 0; }

/* Portrait screens: the card turns upright and is capped so it never fills
   the whole height before it is meant to open. */
@media (max-width: 1024px) {
	.preloader__card { width: 720px; height: 830px; }
}

@media (max-width: 640px) {
	.preloader__card { width: 290px; height: 512px; max-height: 60vh; }
	.preloader__mark { height: 50px; }
	/* 50px against a 290px card scales to ~17vw against the full width. */
	@keyframes mark-grow { to { height: 17vw; } }
}

@media (prefers-reduced-motion: reduce) {
	.preloader__field,
	.preloader__wave,
	.preloader__card--1,
	.preloader__card--2,
	.preloader__card--3 { display: none; }

	.preloader__card--4 {
		width: 100vw;
		height: 100vh;
		max-width: none;
		max-height: none;
		border-radius: 0;
		transform: translate(-50%, -50%);
		animation: none;
	}

	.preloader__mark { filter: none; animation: none; }
	.preloader__since { opacity: 1; animation: none; }

	body.is-loading main { transform: none; }
}

/* ------------------------------------------------------------------- header */

.header {
	position: fixed;
	inset: 0 0 auto 0;
	z-index: 30;
	transition: background .4s ease, border-color .4s ease;
	border-bottom: 1px solid transparent;
}

/* No plate once the page moves: the menu rides straight over the photo wall.
   The scrolled state is kept for the hairline only. */
.header.is-stuck {
	background: none;
	backdrop-filter: none;
	border-bottom-color: transparent;
}

.header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem;
	min-height: var(--header-h);
}

.header__brand { display: flex; align-items: center; gap: 1.1rem; }

.header__loc {
	padding-left: calc(17.6 * var(--u));
	border-left: 1px solid var(--line);
	font-family: var(--mono);
	font-size: calc(10.88 * var(--u));
	line-height: calc(17.408 * var(--u));
	letter-spacing: calc(2.3936 * var(--u));
	text-transform: uppercase;
	color: rgba(255, 255, 255, .55);
	white-space: nowrap;
}

/* Logo hard left, nav and the phone button grouped hard right. */
.nav { margin-left: auto; }

.nav__list {
	display: flex;
	gap: calc(41.6 * var(--u));
}

.nav__link {
	position: relative;
	display: block;
	/* Symmetric padding: the underline's bottom space would otherwise push the
	   label off the centre line shared with the logo and the button. */
	padding-block: .45em;
	font-family: var(--display);
	font-weight: 400;
	font-size: calc(18 * var(--u));
	line-height: calc(25.2 * var(--u));
	letter-spacing: calc(-.9 * var(--u));
	text-transform: uppercase;
	color: #F5F5F5;
	white-space: nowrap;
}

/* Underline slides in on hover and stays put on the section you're in. */
.nav__link::after {
	content: '';
	position: absolute;
	inset: auto 0 0 0;
	height: 1px;
	background: currentColor;
	transform: scaleX(0);
	transform-origin: left;
	transition: transform .35s cubic-bezier(.4, 0, .2, 1);
}

.nav__link:hover::after,
.nav__link.is-active::after { transform: scaleX(1); }

.header__side { display: flex; align-items: center; gap: 1.4rem; }

.burger {
	display: none;
	width: 40px;
	height: 40px;
	padding: 0;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 6px;
}

.burger__bar {
	display: block;
	width: 22px;
	height: 1px;
	background: var(--text);
	transition: transform .3s ease;
}

.burger.is-open .burger__bar:first-child { transform: translateY(3.5px) rotate(45deg); }
.burger.is-open .burger__bar:last-child  { transform: translateY(-3.5px) rotate(-45deg); }

/* --------------------------------------------------------------------- hero */

.hero {
	position: relative;
	display: flex;
	flex-direction: column;
	min-height: 100svh;
	padding-top: var(--header-h);
	/* Visible on purpose: the media bleeds past the fold into the next screen. */
	overflow: visible;
}

/* Runs 24svh past the hero so the footage sits in the second screen's
   backdrop and dissolves there instead of stopping dead at the fold. */
.hero__media {
	position: absolute;
	inset: 0 0 auto 0;
	height: calc(100% + 24svh);
	z-index: 0;
	overflow: hidden;
	background: var(--bg);
}

.hero__video {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 50% 42%;
}

/* Same stops as before across the hero itself (the box is now ~124% of it,
   so each old stop is scaled by 0.81), then the tail completes into the
   second screen's exact surface colour — no seam to see. */
.hero__scrim {
	position: absolute;
	inset: 0;
	background:
		linear-gradient(to bottom,
			rgba(11, 11, 13, .42) 0%,
			rgba(11, 11, 13, .06) 11%,
			rgba(11, 11, 13, 0) 27%,
			rgba(11, 11, 13, .55) 60%,
			rgba(11, 11, 13, .93) 81%,
			#121216 100%);
}

/* Rotated rails pinned to the edges of the frame. */
.hero__city,
.hero__socials {
	position: absolute;
	z-index: 3;
	margin: 0;
	color: #FFF;
	text-transform: uppercase;
	white-space: nowrap;
	line-height: calc(17.408 * var(--u));
}

/* Only the city needs preserved spaces — the four inside each bracket are the
   design's own padding. Applying it to both rails turned the socials' source
   indentation into real blank lines. */
.hero__city { white-space: pre; }

/* Both rails share one centre line so they read as a pair across the frame. */
.hero__city {
	left: calc(65 * var(--u));
	top: 47.37%;
	transform: translate(-50%, -50%) rotate(-90deg);
	font-family: var(--body);
	font-weight: 600;
	font-size: calc(12 * var(--u));
}

/* Frame node 24121:476 — an 18×116 box at 1724/374 holding text turned +90°,
   so the line reads downward. The city opposite is the same idea at −90°. */
.hero__socials {
	left: calc(1724 * var(--u));
	top: 47.37%;
	transform: translate(-50%, -50%) rotate(90deg);
	font-family: var(--display);
	font-size: calc(12 * var(--u));
	letter-spacing: calc(2.3936 * var(--u));
	text-align: center;
}

.hero__socials a { transition: opacity .25s ease; }
.hero__socials a:hover { opacity: .6; }

/* Headline and the Book Now button share a baseline 73px above the frame edge. */
.hero__title {
	position: absolute;
	z-index: 2;
	left: calc(46 * var(--u));
	bottom: calc(73 * var(--u));
	width: calc(1208 * var(--u));
	margin: 0;
	font-family: var(--body);
	font-weight: 400;
	font-size: calc(139.803 * var(--u));
	line-height: .81;
	letter-spacing: calc(-10.8829 * var(--u));
	text-transform: uppercase;
	color: #FFF;
	/* Keeps the double space between "DJ" and "AND" and lets the width wrap
	   "saxophonist" onto the second line, exactly as the frame does. */
	white-space: pre-wrap;
}

/* Node 24119:36, rebuilt the way the frame stores it: the rotated line lives
   inside its 443.296×109.26 axis-aligned bounding box at 570 across and
   208.74 up from the bottom edge, centred both ways. Copying that structure
   means the rotation needs no hand-measured compensation anywhere. */
.hero__script {
	position: absolute;
	z-index: 2;
	left: calc(570 * var(--u));
	bottom: calc(208.74 * var(--u));
	width: calc(443.296 * var(--u));
	height: calc(109.26 * var(--u));
	display: flex;
	align-items: center;
	justify-content: center;
	pointer-events: none;
	/* The write-on sweep lives on the text itself, driven by inherited --pen. */
}

.hero__script-rot {
	position: relative;
	flex: none;
	transform: rotate(-7.272deg);
}

.hero__script-text {
	display: block;
	font-family: var(--script);
	font-style: normal;
	font-weight: 400;
	font-size: calc(127.824 * var(--u));
	line-height: calc(53.023 * var(--u));
	white-space: nowrap;
	/* Gradient fill through the glyphs. The padding grows the paint box to
	   cover Mr Dafoe's overhanging ink (≈62 design units past the line box,
	   measured) — background-clip only paints inside the element's box, so
	   without it the swash tips would simply vanish. The negative margins
	   cancel the padding out of layout, keeping the centring untouched. */
	padding: calc(70 * var(--u)) calc(16 * var(--u));
	margin: calc(-70 * var(--u)) calc(-16 * var(--u));
	background: linear-gradient(89deg, #A0A0A4 -0.93%, #AFAEB4 96.47%);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	/* Write-on: a soft edge leaning with the letters (105deg ≈ the script's
	   slant), so the word flows in like ink rather than being cut by a
	   vertical shutter. The feathered edge is safe now because the padded box
	   covers all the overhanging ink — a soft mask used to fade to nothing at
	   the box border and half-erase the thin strokes. */
	-webkit-mask-image: linear-gradient(105deg, #000 calc(99% - var(--pen, 105%)), transparent calc(105% - var(--pen, 105%)));
	mask-image: linear-gradient(105deg, #000 calc(99% - var(--pen, 105%)), transparent calc(105% - var(--pen, 105%)));
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
}

/* Once written, the sweep comes off entirely: the resting state is exactly
   the frame's fill with nothing trimming it. */
.hero__script.is-written .hero__script-text {
	-webkit-mask-image: none;
	mask-image: none;
}

/* Tegaki handwriting has claimed the reveal (body.tegaki): the CSS sweep
   stands down, the gradient word waits invisible while the strokes draw,
   then fades in as the strokes fade out — ink drying into the final fill. */
body.tegaki .hero__script-text {
	animation: none;
	-webkit-mask-image: none;
	mask-image: none;
	opacity: 0;
	transition: opacity .5s ease .05s;
}

body.tegaki.is-inked .hero__script-text { opacity: 1; }

/* The drawing stage overlays the em's line box — the rot div's own box, since
   the em's padding and negative margins cancel exactly. Same line metrics,
   none of the em's padding/mask/gradient, so the engine's internal layout
   lands its baseline precisely where the browser lands the em's. The colour
   is the gradient's midpoint, so the final cross-fade barely moves. */
.hero__script-stage {
	position: absolute;
	inset: 0;
	font-family: var(--script);
	font-style: normal;
	font-weight: 400;
	font-size: calc(127.824 * var(--u));
	line-height: calc(53.023 * var(--u));
	white-space: nowrap;
	color: #A8A8AC;
	pointer-events: none;
	opacity: 1;
	transition: opacity .5s ease;
}

body.is-inked .hero__script-stage { opacity: 0; }

/* Inherited so one animated value on the wrapper drives both leaf sweeps. */
@property --pen {
	syntax: '<percentage>';
	inherits: true;
	initial-value: 105%;
}

body.is-revealed .hero__script {
	/* The easing writes like a hand: brisk through "art", a small hesitation
	   at the word gap (≈40% of the run — the pen lifts), then on through
	   "music". A cubic-bezier cannot pause mid-flight; linear() can. */
	animation: pen-write 2.2s linear(0, 0.13 12%, 0.27 22%, 0.36 30%, 0.40 40%, 0.42 48%, 0.55 60%, 0.70 72%, 0.85 84%, 0.95 93%, 1) .5s forwards;
}

/* Right inset walks from past the start of the word to past its end. */
@keyframes pen-write {
	from { --pen: 105%; }
	to   { --pen: -15%; }
}

.hero__aside {
	position: absolute;
	z-index: 2;
	left: calc(1039 * var(--u));
	bottom: calc(73 * var(--u));
	width: calc(396 * var(--u));
}

.hero__tagline {
	margin: 0 0 calc(17 * var(--u));
	font-family: var(--body);
	font-weight: 500;
	font-size: calc(18 * var(--u));
	line-height: 1.44;
	letter-spacing: calc(-.32 * var(--u));
	color: #F5F5F5;
}

.hero__btn {
	display: grid;
	place-items: center;
	width: calc(275 * var(--u));
	height: calc(56 * var(--u));
	border: 1px solid #F5F5F5;
	font-family: var(--display);
	font-size: calc(12.16 * var(--u));
	line-height: calc(19.456 * var(--u));
	letter-spacing: calc(2.1888 * var(--u));
	text-indent: calc(2.1888 * var(--u));
	text-transform: uppercase;
	color: #F5F5F5;
	border-radius: var(--radius);
	transition: background .3s ease, color .3s ease;
}

.hero__btn:hover { background: #F5F5F5; color: var(--bg); }

/* Not in the frame — added on request. Sits on the same baseline as the offer
   and the Book Now button, clear of the aside which ends at 1435 design px. */
.hero__scroll {
	position: absolute;
	right: calc(56 * var(--u));
	bottom: calc(73 * var(--u));
	z-index: 3;
	display: inline-flex;
	flex-direction: column;
	align-items: center;
	gap: calc(12 * var(--u));
	font-family: var(--display);
	font-size: calc(10.88 * var(--u));
	letter-spacing: calc(1.9584 * var(--u));
	color: #F5F5F5;
	opacity: .7;
	transition: opacity .25s ease;
}

.hero__scroll:hover { opacity: 1; }

/* Label set down the side, sitting above the line. */
.hero__scroll-label {
	writing-mode: vertical-rl;
	text-transform: uppercase;
	white-space: nowrap;
}

/* A brass rule that draws itself downward, holds, then clears downward — the
   motion always travels the way the page wants you to go. */
.hero__scroll-track {
	position: relative;
	width: 2px;
	height: calc(96 * var(--u));
	overflow: hidden;
}

.hero__scroll-bar {
	position: absolute;
	inset: 0;
	background: #F5F5F5;
	animation: scroll-draw 2.4s cubic-bezier(.5, 0, .35, 1) infinite;
}

@keyframes scroll-draw {
	0%        { transform: scaleY(0); transform-origin: 50% 0; }
	42%       { transform: scaleY(1); transform-origin: 50% 0; }
	52%       { transform: scaleY(1); transform-origin: 50% 100%; }
	94%, 100% { transform: scaleY(0); transform-origin: 50% 100%; }
}


/* ----------------------------------------------------------------- sections */

.section {
	padding-block: clamp(4.5rem, 12vh, 9rem);
	scroll-margin-top: var(--header-h);
}

/* -------------------------------------------------------------------- fluid */

/* The fluid sits over the whole page and never intercepts a click: it is a
   light wash that follows the cursor, not a surface. Screen blending means it
   can only lighten, so nothing underneath is ever obscured. */
.fluid {
	position: fixed;
	inset: 0;
	/* Ниже прелоадера: во время него холста всё равно ещё нет, он грузится
	   после (см. fluidAfterIntro в main.js). */
	z-index: 90;
	width: 100%;
	height: 100%;
	pointer-events: none;
	mix-blend-mode: screen;
	/* Ещё на 15% тише: было .6. */
	opacity: .51;
	transition: opacity .55s ease;
}

/* На последнем экране след от курсора приглушён ещё на 60%: там под
   плашкой формы уже ходят пряди, и два светящихся слоя разом спорят
   друг с другом. Класс ставит скрипт по подходу к контактам. */
body.is-contact .fluid { opacity: .204; }

/* --------------------------------------------------------------------- reel */

/* Variant 2 opening. One photo fills the screen, then the whole wall pulls
   back until fifteen nights are visible at once — the range of parties in a
   single move. The headline arrives in two parts as the wall recedes.
   Everything is scroll-driven: JS writes --z, --veil and --gap. */
/* Same ground as the slider below it, so the two blocks read as one surface
   and no seam shows where the wall ends. */
.reel {
	position: relative;
	background: var(--bg-sunk);
}

.reel__stage { height: 340vh; }

.reel__pin {
	position: sticky;
	top: 0;
	height: 100svh;
	overflow: hidden;
	background: var(--bg-sunk);
}

/* The wall is exactly one viewport at --z:1, so a 5×3 grid means each cell is
   a fifth of the width; --z:5 blows the middle cell up to full screen. */
.reel__grid {
	position: absolute;
	left: 50%;
	top: 50%;
	/* Laid out at full size — five frames across, three down — and only ever
	   scaled DOWN from there. Scaling a small box up would rasterise it at the
	   small size first and blow that up, which is what turned the opening shot
	   to mush. Percentages, not vw/vh: 100vw counts the scrollbar. */
	width: 500%;
	height: 300%;
	display: grid;
	grid-template-columns: repeat(5, 1fr);
	grid-template-rows: repeat(3, 1fr);
	/* Constant in grid space, so the seams widen as the wall comes closer —
	   the way the reference behaves. The opening zoom is computed from this
	   in JS, so the lead frame still covers the screen edge to edge. */
	gap: 36px;
	transform: translate(-50%, -50%) scale(var(--z, 1));
	will-change: transform;
}

.reel__cell {
	margin: 0;
	overflow: hidden;
	background: var(--bg-sunk);
}

/* Biased up the frame by default — these are photographs of people, and a
   centred crop lopped heads off in the tall cells. Cells that need a
   different framing override --pos inline. */
.reel__cell img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 50% var(--pos, 30%);
}

/* Opacity can be driven past 1 by the closing half — the colour is mixed to
   stay short of solid black so the wall still reads underneath. */
/* Solid ground, with the depth carried entirely by --veil, so each half of
   the headline can sit on its own level of darkness. */
.reel__veil {
	position: absolute;
	inset: 0;
	background: rgb(11, 11, 13);
	opacity: var(--veil, 0);
	pointer-events: none;
}

.reel__copy {
	position: absolute;
	inset: 0;
	display: grid;
	place-content: center;
	justify-items: center;
	margin: 0;
	padding: 0 var(--gutter);
	text-align: center;
	pointer-events: none;
}

/* Both halves live in the same spot; only one is up at a time. */
.reel__line {
	grid-area: 1 / 1;
	max-width: 16ch;
	font-family: var(--body);
	font-weight: 300;
	font-size: clamp(2.6rem, 6.4vw, 6rem);
	line-height: 81%;
	/* Tight tracking in the spirit of the hero headline. The negative tracking
	   also eats into the spaces, so the gaps between words are given back. */
	letter-spacing: -5px;
	word-spacing: .34em;
	color: #F5F5F5;
	opacity: 0;
	transform: translateY(22px);
	transition: opacity .55s ease, transform .7s cubic-bezier(.25, .6, .25, 1);
}

/* The closing half is the longer sentence — a notch down so it still fits. */
.reel__line--b {
	max-width: 19ch;
	font-size: clamp(2rem, 4.6vw, 4.4rem);
	line-height: 1.1;
}

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

/* Variant 2 squares off the hero: the photo starts the moment the hero ends,
   so the footage must not bleed past the fold any more. */
body.is-v2 .hero { overflow: hidden; }

body.is-v2 .hero__media {
	inset: 0;
	height: 100%;
}

/* The scrim lands on the reel's own colour, so the hero hands over to the
   photo wall without a band of a slightly different black between them. */
body.is-v2 .hero__scrim {
	background:
		linear-gradient(to bottom,
			rgba(18, 18, 22, .42) 0%,
			rgba(18, 18, 22, .06) 14%,
			rgba(18, 18, 22, 0) 34%,
			rgba(18, 18, 22, .55) 74%,
			rgba(18, 18, 22, .95) 100%);
}

/* Same ground under the whole variant, so no seam can show at any join. */
body.is-v2 { background: var(--bg-sunk); }

/* The wall already ended the beat, so the caption follows close behind it —
   the section brings no opening padding of its own here. */
body.is-v2 .about {
	background: var(--bg-sunk);
	padding-top: 0;
}

body.is-v2 .about__top { padding-top: calc(var(--header-h) + clamp(3rem, 7vh, 5rem)); }

/* The reel already delivers this sentence in two halves, so variant 2 drops
   the standalone headline. Variant 1 still opens the section with it. */
body.is-v2 .about__intro { display: none; }

/* Variant 1 keeps its gradient hand-off, so it has no reel to show. */
body:not(.is-v2) .reel { display: none; }

/* -------------------------------------------------------------------- about */

/* Transparent at the top so the hero footage bleeding down from above shows
   through, solid by the point the bleed ends. z-index lifts the section (and
   its text) above the positioned media layer. The huge top padding is the
   point: the headline starts deep into the screen, with the hero footage
   still dissolving in the emptiness above it. */
/* The strands sit behind everything in the section and stay faint — they are
   texture, not subject. The canvas the script injects fills this box. */
/* Sticky and one screen tall, not the full section height: the canvas then
   costs one viewport to draw instead of several thousand pixels of it. The
   negative margin keeps it out of the flow entirely. */
.about__bg {
	position: sticky;
	top: 0;
	z-index: 0;
	height: 100svh;
	margin-bottom: -100svh;
	overflow: hidden;
	opacity: .5;
	pointer-events: none;
}

.about__bg canvas {
	width: 100%;
	height: 100%;
	display: block;
}

.about > .container,
.about__stage { position: relative; z-index: 1; }

.about {
	position: relative;
	z-index: 1;
	background: linear-gradient(to bottom, rgba(18, 18, 22, 0) 0, var(--bg-sunk) 24svh);
	padding-top: clamp(14rem, 30svh, 24rem);
	padding-bottom: clamp(5rem, 10vh, 8rem);
}

/* The statement. Light weight at display size, an em-scale indent before the
   first word, and a second half that starts dim and is lit word by word as
   the visitor scrolls through it. */
.about__intro {
	max-width: 30ch;
	font-family: var(--body);
	font-weight: 300;
	font-size: clamp(2.5rem, 5.2vw, 4.9rem);
	line-height: 1.12;
	letter-spacing: -.025em;
	text-indent: clamp(3.5rem, 10vw, 9rem);
	color: #F5F5F5;
}

/* Split by script into word wrappers (line breaks stay at word boundaries)
   holding character spans. text-indent inherits, and every inline-block is
   its own block container — without the reset each one would carry the
   headline's whole indent inside itself and explode the line. */
.about__wd {
	display: inline-block;
	text-indent: 0;
	white-space: nowrap;
}

.about__c {
	display: inline-block;
	text-indent: 0;
	opacity: 0;
	transform: translateY(.35em);
	filter: blur(8px);
	transition:
		opacity .5s ease,
		transform .6s cubic-bezier(.22, .6, .25, 1),
		filter .6s ease;
}

.about__intro.is-in .about__c:not(.about__c--hi) {
	opacity: 1;
	transform: none;
	filter: none;
}

/* The wave characters: pop in white at 1.4×, then settle into the dim tone. */
.about__c--hi {
	transform: scale(1);
	filter: none;
	color: var(--text-mute);
	transition: opacity .25s ease, transform .3s cubic-bezier(.55, 0, .8, .5), color .3s ease;
}

.about__c--hi.is-flash {
	opacity: 1;
	transform: scale(1.4);
	color: #FFF;
}

.about__c--hi.is-flash.is-settle {
	transform: scale(1);
	color: var(--text-mute);
	transition: transform .4s ease, color .4s ease;
}

/* Who he is comes first in white; the venue list trails behind it in grey. */
.about__note {
	grid-column: 1;
	margin: 0;
	max-width: 46ch;
	color: var(--text-mute);
	font-size: clamp(1.05rem, 1.32vw, 1.3rem);
	line-height: 1.6;
}

.about__note-lead { color: #F5F5F5; }

/* Scroll-in reveal for whole blocks. */
[data-reveal] {
	opacity: 0;
	transform: translateY(30px);
	filter: blur(6px);
	transition:
		opacity .9s ease,
		transform .9s cubic-bezier(.25, .6, .25, 1),
		filter .9s ease;
}

[data-reveal].is-in {
	opacity: 1;
	transform: none;
	filter: none;
}

/* ---- the pinned slider, built to the reference's anatomy: a square photo
        plate top right, a hairline with a progress fill, and along the bottom
        the small "Our", the huge word, and the sentence. Scroll drives all of
        it — JS writes --fill on the live word and swaps is-active. ---- */

.about__stage {
	/* One viewport of runway per slide. */
	height: 500vh;
	margin-top: clamp(6rem, 14vh, 11rem);
}

.about__pin {
	position: sticky;
	top: 0;
	height: 100svh;
	display: grid;
	grid-template-rows: 1fr auto auto;
	padding-bottom: clamp(3rem, 10vh, 6rem);
}

/* The live row: label, the word column, and the sentence. The words already
   read stack above this line, which is why the row is anchored to the rule. */
.about__row {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr) minmax(0, 32rem);
	column-gap: clamp(1.2rem, 3.5vw, 3.5rem);
	align-items: start;
}

/* Caption left, photo right, both hanging from the top of the frame. */
.about__top {
	display: grid;
	grid-template-columns: minmax(0, 34rem) 1fr auto;
	align-items: start;
	/* Clears the fixed header, then keeps a hand's breadth of air below it. */
	padding-top: calc(var(--header-h) + clamp(3.4rem, 6vh, 4.5rem));
}

.about__shot {
	grid-column: 3;
	position: relative;
	width: clamp(190px, 22vw, 330px);
	aspect-ratio: 1;
	/* Same soft corner as the buttons, so the plate belongs to the set. */
	border-radius: var(--radius);
	overflow: hidden;
}

.about__shot-img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	border-radius: var(--radius);
	filter: brightness(.92) saturate(.95);
	transition: opacity .7s ease, transform .9s cubic-bezier(.25, .6, .25, 1);
}

.about__shot-img.is-after   { opacity: 0; transform: scale(1.08); }
.about__shot-img.is-active  { opacity: 1; transform: none; }
.about__shot-img.is-before  { opacity: 0; transform: scale(.94); }

/* Hairline across the frame; the fill tracks progress through the whole set. */
.about__rule {
	position: relative;
	height: 1px;
	/* Tuned so the air above the rule equals the air below it. */
	margin-bottom: clamp(1rem, 2.5vh, 2rem);
	background: var(--line-soft);
}

.about__rule-fill {
	position: absolute;
	inset: 0 auto 0 0;
	width: var(--progress, 0%);
	background: #F5F5F5;
}

/* The little label that rides just ahead of the live word, at its cap line. */
.about__our {
	margin-top: .35em;
	font-family: var(--body);
	font-weight: 700;
	font-size: clamp(.58rem, .74vw, .74rem);
	letter-spacing: .1em;
	text-transform: uppercase;
	color: #F5F5F5;
	white-space: nowrap;
}

/* One line tall, but the overflow above stays visible — that is where the
   words already read pile up. The track slides one line per slide. */
.about__titles {
	/* Pitch is wider than the glyphs so the stack breathes and the hairline
	   has clean air to cross in — the reference spaces them the same way.
	   Sized so the air above the rule matches the air below it. */
	--line: 1.62em;
	position: relative;
	height: var(--line);
	font-size: clamp(2.4rem, 5.6vw, 5.2rem);
	overflow: visible;
}

.about__titles-track {
	position: absolute;
	inset: 0 auto auto 0;
	transform: translateY(calc(var(--index, 0) * var(--line) * -1));
	transition: transform .7s cubic-bezier(.3, .7, .2, 1);
}

/* Two stacked copies of the word: the ghost always shows, the bright one is
   clipped to --fill so the word lights up left-to-right across its slide. */
.about__stage-title {
	display: grid;
	height: var(--line);
	margin: 0;
	font-family: var(--body);
	font-weight: 300;
	font-size: 1em;
	letter-spacing: -.03em;
	line-height: 1;
	white-space: nowrap;
	transition: opacity .5s ease;
}

.about__title-ghost,
.about__title-fill { grid-area: 1 / 1; }

.about__title-ghost { color: rgba(245, 245, 245, .26); }

.about__title-fill {
	color: #F5F5F5;
	clip-path: inset(0 calc(100% - var(--fill, 0%)) 0 0);
}

/* Words already read stay stacked above, fading with distance so the pile
   thins out well before it can reach the caption; those still to come are
   hidden outright. JS writes --dim from the distance to the live word. */
.about__stage-title { opacity: var(--dim, 0); }

/* The sentence swaps in place — only the live one is up. */
.about__col--texts {
	position: relative;
	min-height: clamp(6rem, 17vh, 9rem);
}

.about__stage-text {
	position: absolute;
	inset: 0 0 auto 0;
	margin: 0;
	max-width: 40ch;
	color: var(--text-soft);
	font-size: clamp(.92rem, 1.05vw, 1.05rem);
	line-height: 1.7;
	transition: opacity .45s ease, transform .55s cubic-bezier(.3, .7, .2, 1);
}

/* Words of the live sentence rise in one after another. */
.about__w {
	display: inline-block;
	opacity: 0;
	transform: translateY(50%);
	transition: opacity .6s cubic-bezier(.445, .05, .55, .95),
		transform .6s cubic-bezier(.445, .05, .55, .95);
}

.about__stage-text.is-active .about__w {
	opacity: 1;
	transform: none;
	transition-delay: var(--trans-delay, 0s);
}

.about__stage-text.is-after  { opacity: 0; transform: translateY(40%); }
.about__stage-text.is-active { opacity: 1; transform: none; }
.about__stage-text.is-before { opacity: 0; transform: translateY(-40%); }

/* Award plaques — placeholder tiles until they get their own design pass. */
/* Four plaques on one line. The row reaches left past the copy column — the
   word on this slide is short, so the space is there. */
.about__stage-text--awards {
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: clamp(.4rem, .8vw, .7rem);
	align-content: start;
	inset: 0 0 auto auto;
	width: min(46rem, 54vw);
	max-width: none;
}

.about__award {
	display: flex;
	flex-direction: column;
	gap: .25rem;
	padding: clamp(.6rem, 1vw, .85rem);
	border: 1px solid var(--line);
	border-radius: var(--radius);
}

.about__award-org {
	font-family: var(--mono);
	font-size: .62rem;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--brass);
}

.about__award-name {
	font-size: clamp(.72rem, .82vw, .82rem);
	color: #F5F5F5;
	line-height: 1.3;
}

.about__award-stars {
	font-size: .6rem;
	letter-spacing: .3em;
	color: var(--brass);
}

.about__award-year {
	font-family: var(--mono);
	font-size: .68rem;
	color: var(--text-mute);
}

/* Generic slider mechanics — reusable by any other slider on the page. */
.slider { position: relative; }

.slider__list {
	display: flex;
	gap: clamp(12px, 1.5vw, 22px);
	transition: transform .55s cubic-bezier(.4, 0, .2, 1);
}

.slider__item { flex: 0 0 calc((100% - 2 * clamp(12px, 1.5vw, 22px)) / 3); }

.slider__nav { display: flex; align-items: center; gap: 1.4rem; margin-top: 1.6rem; }

.slider__arrow {
	width: 44px;
	height: 44px;
	border: 1px solid var(--line);
	border-radius: 50%;
	display: grid;
	place-items: center;
	transition: border-color .3s ease, background .3s ease, color .3s ease;
}

.slider__arrow::before { content: '←'; font-size: 1rem; line-height: 1; }
.slider__arrow--next::before { content: '→'; }

.slider__arrow:hover { border-color: var(--brass); color: var(--brass); }
.slider__arrow[disabled] { opacity: .25; cursor: default; }
.slider__arrow[disabled]:hover { border-color: var(--line); color: inherit; }

.slider__count {
	font-family: var(--mono);
	font-size: .74rem;
	letter-spacing: .16em;
	color: var(--text-mute);
}

/* ----------------------------------------------------------------- services */

.services__head {
	display: flex;
	flex-direction: column;
	gap: 1.4rem;
	margin-bottom: clamp(2.5rem, 6vh, 4rem);
}

.services__list {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: clamp(1.5rem, 3vw, 2.75rem);
}

.card { display: flex; flex-direction: column; align-items: flex-start; gap: 1.1rem; }

.card__media { width: 100%; overflow: hidden; background: var(--bg-lift); }

.card__img {
	width: 100%;
	aspect-ratio: 4 / 5;
	object-fit: cover;
	filter: brightness(.8) contrast(1.06) saturate(.85);
	transition: transform .7s cubic-bezier(.4, 0, .2, 1), filter .5s ease;
}

.card:hover .card__img { transform: scale(1.03); filter: brightness(.95) contrast(1.06) saturate(.95); }

.card__title {
	font-family: var(--display);
	font-weight: 300;
	font-size: clamp(1.25rem, 1.7vw, 1.6rem);
	line-height: 1.15;
	letter-spacing: -.005em;
}

.card__text { color: var(--text-soft); max-width: 34ch; font-size: .97rem; }
.card__link { margin-top: auto; }

/* ------------------------------------------- sections not yet designed */

.section--draft { color: var(--text-mute); }
.section--draft .section__title { font-size: clamp(1.6rem, 2.6vw, 2.2rem); color: var(--text-mute); }
.section--draft img[src=''] { display: none; }
.section--draft .gallery__list,
.section--draft .clients__list,
.section--draft .videos__list,
.section--draft .reviews__list { display: grid; gap: 1rem; margin-top: 2rem; }
.section--draft .form { display: grid; gap: .75rem; max-width: 480px; margin-top: 2rem; }
.section--draft .form__input,
.section--draft .form__textarea {
	padding: .85rem 1rem;
	border-radius: var(--radius);
	border: 1px solid var(--line);
	background: none;
	color: var(--text);
	font: inherit;
}
.footer.section--draft { padding-block: 4rem; border-top: 1px solid var(--line-soft); }

/* --------------------------------------------------------------- responsive */

/* Short laptop screens: keep the headline from eating the frame. */
@media (max-height: 780px) {
	.hero__head { padding-block: clamp(1rem, 3vh, 2rem); }
	.hero__eyebrow { margin-bottom: .9rem; }
	.hero__lead { margin-top: 1.1rem; font-size: .95rem; }
	.hero__actions { margin-top: 1.4rem; }
}

/* The phone button always stays: it is the only direct action in the header.
   The location line goes first, then the nav collapses into the burger. */
@media (max-width: 1180px) {
	.header__loc { display: none; }
	.header__brand { gap: 0; }
}

@media (max-width: 960px) {
	.services__list { grid-template-columns: 1fr 1fr; }
	.slider__item { flex: 0 0 calc((100% - clamp(12px, 1.5vw, 22px)) / 2); }
}

/* About's pinned stage folds into a column on small screens: the object on
   top, the swapping title and text under it. Still scroll-driven. */
@media (max-width: 900px) {
	.about__pin {
		grid-template-columns: minmax(0, 1fr);
		grid-template-rows: 1fr auto auto auto;
		grid-template-areas:
			'shot'
			'rule'
			'title'
			'text';
		justify-items: start;
		row-gap: clamp(.6rem, 2vh, 1.2rem);
	}

	/* "Our" tucks above the word instead of beside it. */
	.about__our { display: none; }
	.about__shot { justify-self: center; width: min(62vw, 300px); }
	.about__col { height: auto; min-height: clamp(4rem, 14vh, 6rem); width: 100%; }
	.about__stage-title,
	.about__stage-text { position: absolute; }
	.about__col--titles { height: clamp(3.4rem, 11vh, 5rem); }
	.about__col--texts { height: clamp(7rem, 20vh, 9rem); }

	.about__intro { text-indent: clamp(2rem, 12vw, 4rem); }
}

@media (max-width: 900px) {
	.burger { display: flex; }
	/* Nav is out of flow here, so the button needs its own push to the right. */
	.header__side { margin-left: auto; }

	.nav {
		position: absolute;
		inset: 100% 0 auto 0;
		background: rgba(11, 11, 13, .97);
		backdrop-filter: blur(12px);
		border-block: 1px solid var(--line-soft);
		display: none;
	}

	.nav.is-open { display: block; }

	.nav__list { flex-direction: column; gap: 0; padding: 1rem var(--gutter) 1.5rem; }
	.nav__link { display: block; padding: .8rem 0; font-size: .9rem; letter-spacing: .12em; }
}

@media (max-width: 640px) {
	:root { --header-h: 64px; }

	.nav__link { font-size: 16px; }
	.btn--sm { padding: .6rem 1rem; font-size: .66rem; }

	.hero__scrim {
		background: linear-gradient(to bottom,
			rgba(11, 11, 13, .78) 0%,
			rgba(11, 11, 13, .40) 40%,
			rgba(11, 11, 13, .92) 100%);
	}

	.services__list { grid-template-columns: 1fr; }
	.slider__item { flex: 0 0 100%; }
	.about__slider .slider__item img { aspect-ratio: 3 / 2; }
}

@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	*, *::before, *::after {
		animation-duration: .01ms !important;
		transition-duration: .01ms !important;
	}
}
