@font-face { font-family: "Clash Grotesk"; src: url("fonts/ClashGrotesk-Extralight.otf") format("opentype"); font-weight: 200; font-style: normal; font-display: swap; }
@font-face { font-family: "Clash Grotesk"; src: url("fonts/ClashGrotesk-Light.otf")      format("opentype"); font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: "Clash Grotesk"; src: url("fonts/ClashGrotesk-Regular.otf")    format("opentype"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Clash Grotesk"; src: url("fonts/ClashGrotesk-Medium.otf")     format("opentype"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Clash Grotesk"; src: url("fonts/ClashGrotesk-Semibold.otf")   format("opentype"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "Clash Grotesk"; src: url("fonts/ClashGrotesk-Bold.otf")       format("opentype"); font-weight: 700; font-style: normal; font-display: swap; }

* { box-sizing: border-box; margin: 0; padding: 0; }

:root {
	--bg-top: #000000;
	--bg-bottom: #0a0603;
	--accent: #f6b32c;
	--ink: #f5ead3;
}

html, body {
	width: 100%;
	background: linear-gradient(180deg, var(--bg-top) 0%, #050302 45%, var(--bg-bottom) 100%);
	color: var(--ink);
	font-family: "Clash Grotesk", "Avenir Next", system-ui, sans-serif;
}

html.loading, html.loading body { overflow: hidden; height: 100%; }

#preloader {
	position: fixed;
	inset: 0;
	z-index: 50;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--bg-top);
	transition: opacity 0.3s ease;
}

/* hidden the instant the particle intro begins — the scene behind it is
   already solid black at that point (see introBgColor in main.js), so the
   swap is imperceptible; the particles do the actual "reveal" by randomly
   spawning in on top, not this div */
#preloader.hidden { opacity: 0; pointer-events: none; }

.enter-hint {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 15vh;
	z-index: 51;
	text-align: center;
	pointer-events: none;
	font-family: "DM Mono", ui-monospace, monospace;
	font-weight: 300;
	font-size: 11px;
	letter-spacing: 0.15em;
	text-transform: uppercase;
	color: var(--ink);
	opacity: 0;
	transition: opacity 0.6s ease;
}

.enter-hint.show {
	animation:
		enter-hint-in 0.9s ease 0.5s backwards,
		enter-hint-pulse 2.6s ease-in-out 1.4s infinite;
	opacity: 1;
}

/* desktop: hint rides the cursor (JS sets transform); starts centered */
.enter-hint.follow {
	left: 0;
	top: 0;
	right: auto;
	bottom: auto;
	transform: translate(50vw, 50vh);
	margin: 18px 0 0 14px;   /* sit just below-right of the pointer */
	white-space: nowrap;
	font-size: 10px;
}
.enter-hint.follow.show {
	animation: none;   /* no pulse — it already moves with the cursor; base transition fades it */
	opacity: 0.45;
}

/* desktop: "View project" label riding the cursor over clickable showcase cards
   (main.js toggles .show and sets transform) */
.card-hint {
	position: fixed;
	left: 0;
	top: 0;
	z-index: 6;
	margin: 18px 0 0 14px;   /* just below-right of the pointer, like .enter-hint.follow */
	pointer-events: none;
	white-space: nowrap;
	font-family: "DM Mono", ui-monospace, monospace;
	font-weight: 300;
	font-size: 10px;
	letter-spacing: 0.15em;
	text-transform: uppercase;
	color: var(--ink);
	opacity: 0;
	transition: opacity 0.25s ease;
}
.card-hint.show { opacity: 0.8; }

@keyframes enter-hint-in {
	from { opacity: 0; }
	to   { opacity: 0.9; }
}

@keyframes enter-hint-pulse {
	0%, 100% { opacity: 0.9; }
	50%      { opacity: 0.35; }
}

.scroll-track { height: 2105vh; }

#webgl {
	position: fixed;
	inset: 0;
	width: 100vw;
	height: 100vh;
	height: 100lvh;
	display: block;
	z-index: 1;
	touch-action: pan-y;
}

.overlay {
	position: fixed;
	inset: 0;
	z-index: 2;
	pointer-events: none;
}

.headline {
	position: fixed;
	/* all headlines share one slot, vertically centred whatever their
	   line count; +32px cancels the -32px margin below */
	top: calc(50vh + 32px);
	translate: 0 -50%;
	/* left gap matches the 21vh top gap; 12vw caps it on narrow/portrait screens */
	left: max(20px, min(21vh, 12vw));
	z-index: 2;
	/* the mask below clips to this box, and filter runs before mask — pad it
	   (cancelled by the negative margin) so the 14px intro blur isn't cropped */
	padding: 32px;
	margin: -32px;
	max-width: calc(12em + 64px);
	font-family: "Clash Grotesk", "Arial Narrow", system-ui, sans-serif;
	font-weight: 500;
	font-size: clamp(14px, 1.5vw, 25px);
	line-height: 1.06;
	letter-spacing: -0.01em;
	text-transform: uppercase;
	color: #ffffff;
	pointer-events: none;
	transition:
		opacity 0.6s ease,
		-webkit-mask-position var(--wipe, 1.8s) cubic-bezier(0.37, 0, 0.63, 1),
		mask-position var(--wipe, 1.8s) cubic-bezier(0.37, 0, 0.63, 1);   /* --wipe set by main.js: shorter on fast scrolls */
	/* diagonal wipe, like the section transition: a soft-edged, tilted opaque
	   band in a mask 14× the text's height (tall enough that the hidden states fully clear even one-line copy). Sliding it up wipes the text in from
	   the bottom (→ .is-shown), then out the same way (→ .is-passed); scrolling
	   back reverses it. No class = not reached yet (hidden). */
	-webkit-mask-image: linear-gradient(-15deg, transparent 28%, #000 36%, #000 64%, transparent 72%);
	mask-image: linear-gradient(-15deg, transparent 28%, #000 36%, #000 64%, transparent 72%);
	-webkit-mask-size: 100% 1400%;
	mask-size: 100% 1400%;
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	-webkit-mask-position: 0 0;
	mask-position: 0 0;
}
/* phones: same 20px gutter as the other mobile copy, larger type, and lifted
   into the dark wall band between the ceiling ring and the logo so the
   headline doesn't sit on top of it */
@media (max-width: 700px) {
	.headline { left: 20px; top: calc(33vh + 32px); font-size: clamp(18px, 5.2vw, 24px); }
}
.headline.is-shown  { -webkit-mask-position: 0 50%;  mask-position: 0 50%; }
.headline.is-passed { -webkit-mask-position: 0 100%; mask-position: 0 100%; }


/* held hidden until the preloader clears; main.js swaps this for .reveal */
.headline.pre-reveal { opacity: 0; filter: blur(14px); transform: scale(0.75); }

/* opacity gets a near-linear curve — the springy ease-out (kept for scale)
   jumps to ~75% opacity almost instantly, which reads as popping in at 75%. */
.headline.reveal {
	animation:
		headline-in 2.4s cubic-bezier(0.33, 0, 0.25, 1) backwards,
		headline-scale 2.4s cubic-bezier(0.16, 1, 0.3, 1) backwards;
}

@keyframes headline-in {
	from { opacity: 0; filter: blur(14px); }
	to   { opacity: 1; filter: blur(0); }
}

@keyframes headline-scale {
	from { transform: scale(0.75); }
	to   { transform: scale(1); }
}

.hero {
	position: absolute;
	top: 7vh;
	left: 0;
	right: 0;
	text-align: center;
}

.hero .word {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	font: 800 clamp(64px, 17vw, 280px)/0.9 "Clash Grotesk", sans-serif;
	letter-spacing: -0.02em;
	text-transform: uppercase;
	color: #ffffff;
	opacity: 0;
	transform: translateY(28px);
	transition: opacity 0.5s ease, transform 0.5s ease;
	text-shadow: 0 0 60px rgb(246 179 44 / 0.18);
}

.hero .word.is-active { opacity: 1; transform: translateY(0); }

.caption {
	position: absolute;
	right: clamp(20px, 5vw, 90px);
	top: 42vh;
	max-width: 22em;
	text-align: left;
	color: rgb(245 234 211 / 0.72);
	font: 500 clamp(13px, 1vw, 16px)/1.55 inherit;
	letter-spacing: 0.01em;
	opacity: 0;
	transition: opacity 0.6s ease;
}

.caption.is-active { opacity: 1; }

.scroll-hint {
	position: fixed;
	left: 50%;
	bottom: 26px;
	transform: translateX(-50%);
	z-index: 3;
	color: rgb(245 234 211 / 0.6);
	font: 600 11px/1 inherit;
	letter-spacing: 0.28em;
	text-transform: uppercase;
	transition: opacity 0.4s ease;
}

.scroll-hint::after {
	content: "";
	display: block;
	width: 1px;
	height: 26px;
	margin: 10px auto 0;
	background: linear-gradient(180deg, var(--accent), transparent);
	animation: nudge 1.8s ease-in-out infinite;
}

@keyframes nudge {
	0%, 100% { opacity: 0.2; transform: translateY(-6px); }
	50% { opacity: 1; transform: translateY(2px); }
}

.notice {
	position: fixed;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	z-index: 5;
	padding: 14px 18px;
	max-width: min(86vw, 460px);
	border: 1px solid rgb(246 179 44 / 0.35);
	border-radius: 10px;
	background: rgb(8 5 2 / 0.92);
	color: var(--ink);
	font: 13px/1.5 inherit;
	text-align: center;
	display: none;
}

.notice.show { display: block; }
.notice code { color: var(--accent); }

/* sound toggle: bars bob while audio is on; .off flattens them to a line
   (outer span scales, inner <i> keeps animating, so both directions ease) */
.audio-toggle {
	position: fixed;
	right: 20px;
	bottom: 20px;
	z-index: 6;
	display: flex;
	align-items: center;
	gap: 3px;
	height: 40px;
	padding: 0 10px;
	border: 0;
	background: none;
	cursor: pointer;
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.6s ease;
}
.audio-toggle.show { opacity: 0.85; pointer-events: auto; }
.audio-toggle.show:hover { opacity: 1; }
.audio-toggle:focus-visible { outline: 1px solid var(--ink); outline-offset: 2px; border-radius: 4px; }
.audio-toggle span {
	display: block;
	width: 2px;
	height: 16px;
	transition: transform 0.6s ease;
}
.audio-toggle.off span { transform: scaleY(0.12); }
.audio-toggle i {
	display: block;
	height: 100%;
	border-radius: 1px;
	background: var(--ink);
	animation: audio-wave 0.9s ease-in-out infinite alternate;
}
.audio-toggle span:nth-child(2) i { animation-duration: 0.7s; animation-delay: -0.3s; }
.audio-toggle span:nth-child(3) i { animation-duration: 1.1s; animation-delay: -0.6s; }
.audio-toggle span:nth-child(4) i { animation-duration: 0.8s; animation-delay: -0.2s; }
@keyframes audio-wave {
	from { transform: scaleY(0.25); }
	to   { transform: scaleY(1); }
}

#fps {
	position: fixed;
	top: 12px;
	left: 14px;
	z-index: 6;
	padding: 4px 8px;
	border-radius: 6px;
	background: rgb(8 5 2 / 0.6);
	color: var(--accent);
	font: 600 11px/1 ui-monospace, "SF Mono", Menlo, monospace;
	letter-spacing: 0.08em;
	pointer-events: none;
}

/* Debug UI only with ?debug in the URL */
html:not(.debug) #fps,
html:not(.debug) .panel { display: none; }

.panel {
	position: fixed;
	top: 16px;
	right: 16px;
	z-index: 6;
	width: 248px;
	font: 11px/1.4 inherit;
	color: var(--ink);
	user-select: none;
}

.panel-toggle {
	width: 100%;
	padding: 9px 12px;
	border: 1px solid rgb(246 179 44 / 0.4);
	border-radius: 9px;
	background: rgb(10 6 2 / 0.82);
	color: var(--accent);
	font: 600 11px/1 inherit;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	cursor: pointer;
	backdrop-filter: blur(6px);
}

.panel-body {
	margin-top: 8px;
	padding: 14px 14px 6px;
	border: 1px solid rgb(246 179 44 / 0.22);
	border-radius: 10px;
	background: rgb(8 5 2 / 0.86);
	backdrop-filter: blur(8px);
	max-height: calc(100vh - 80px);
	overflow-y: auto;
}

.panel.collapsed .panel-body { display: none; }

.ctrl { margin-bottom: 14px; }

.ctrl label {
	display: flex;
	justify-content: space-between;
	margin-bottom: 6px;
	color: rgb(245 234 211 / 0.78);
	letter-spacing: 0.04em;
}

.ctrl label .val { color: var(--accent); font-variant-numeric: tabular-nums; }

.ctrl input[type="file"] { width: 100%; font: 11px/1.3 inherit; color: rgb(245 234 211 / 0.7); cursor: pointer; }
.ctrl input[type="file"]::file-selector-button {
	margin-right: 8px;
	padding: 6px 10px;
	border: 1px solid rgb(246 179 44 / 0.4);
	border-radius: 7px;
	background: rgb(246 179 44 / 0.12);
	color: var(--accent);
	font: 600 10px/1 inherit;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	cursor: pointer;
}
.ctrl input[type="file"]::file-selector-button:hover { background: rgb(246 179 44 / 0.22); }
.upload-ctrl { padding-bottom: 12px; margin-bottom: 12px; border-bottom: 1px solid rgb(246 179 44 / 0.18); }
.upload-ctrl .hint { color: rgb(245 234 211 / 0.4); font-size: 10px; margin-top: 6px; }
.ctrl select {
	width: 100%; margin-top: 4px; padding: 7px 9px;
	border: 1px solid rgb(246 179 44 / 0.4); border-radius: 7px;
	background: rgb(10 6 2 / 0.85); color: #f6b32c;
	font: 11px/1.2 inherit; cursor: pointer;
}
.ctrl select option { background: #100a04; color: #f5ead3; }

.section-label {
	margin: 16px 0 12px;
	padding-top: 12px;
	border-top: 1px solid rgb(246 179 44 / 0.18);
	color: var(--accent);
	font: 600 10px/1 inherit;
	letter-spacing: 0.2em;
	text-transform: uppercase;
}

.copy-btn {
	width: 100%;
	margin: 2px 0 8px;
	padding: 9px 12px;
	border: 1px solid rgb(246 179 44 / 0.5);
	border-radius: 9px;
	background: rgb(246 179 44 / 0.14);
	color: var(--accent);
	font: 600 11px/1 inherit;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	cursor: pointer;
}
.copy-btn:hover { background: rgb(246 179 44 / 0.24); }

.ctrl input[type="range"] {
	-webkit-appearance: none;
	appearance: none;
	width: 100%;
	height: 3px;
	border-radius: 3px;
	background: rgb(246 179 44 / 0.25);
	outline: none;
	cursor: pointer;
}

.ctrl .check-label {
	align-items: center;
	gap: 8px;
	cursor: pointer;
}
.ctrl .check-label input { width: auto; cursor: pointer; }
.ctrl .check-label .hint { margin: 0; }

.ctrl input[type="range"]::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	width: 14px;
	height: 14px;
	border-radius: 50%;
	background: var(--accent);
	box-shadow: 0 0 8px rgb(246 179 44 / 0.7);
}

.ctrl input[type="range"]::-moz-range-thumb {
	width: 14px;
	height: 14px;
	border: none;
	border-radius: 50%;
	background: var(--accent);
	box-shadow: 0 0 8px rgb(246 179 44 / 0.7);
}

/* showcase: bottom-left description of the card at eye level; main.js fills it
   and toggles .show (see updateProjectInfo) */
.project-info {
	position: fixed;
	left: 30px;     /* mirrors the audio toggle's bars on the right */
	bottom: 30px;   /* bottom-pinned: the text's last line sits level with the audio toggle */
	z-index: 2;
	max-width: min(78vw, 340px);
	color: #fff;
	text-shadow: 0 0 6px rgb(0 0 0 / 0.45), 0 1px 14px rgb(0 0 0 / 0.55);   /* stays readable over the bright amber sky */
	pointer-events: none;
	opacity: 0;
	transition: opacity 0.25s ease;
}
.project-info.show { opacity: 1; }
.pi-meta,
.project-info p {
	font-family: "DM Mono", ui-monospace, monospace;
	font-weight: 300;
	font-size: 11px;
	letter-spacing: 0.12em;
	text-transform: uppercase;
}
.pi-meta { color: rgb(245 234 211 / 0.6); }
.pi-name {
	margin: 10px 0 14px;
	font-weight: 500;
	font-size: clamp(18px, 2vw, 32px);
	line-height: 1.06;
	letter-spacing: -0.01em;
	text-transform: uppercase;
}
.pi-title {
	margin: 0 0 12px;
	font-weight: 500;
	font-size: clamp(18px, 1.4vw, 22px);
	line-height: 1.1;
	letter-spacing: -0.01em;
	text-transform: uppercase;
}
.project-info p { margin: 0 0 10px; line-height: 1.7; letter-spacing: 0.02em; }
.project-info p:last-child { margin-bottom: 0; }

@media (max-width: 700px) {
	/* phones: hero gutter; right edge stops short of the audio toggle */
	/* lifted off the bottom edge so it sits over the card, not below it */
	.project-info { left: 20px; bottom: 18svh; max-width: calc(100vw - 90px); }
}

/* ambience section (end of the scroll): studio line + contact. main.js adds
   .show (and lifts inert) once the section has settled — see siteFooterEl */
.site-footer {
	position: fixed;
	left: max(20px, min(21vh, 12vw));   /* same gutter as the hero headlines */
	top: 50vh;
	translate: 0 -50%;
	z-index: 2;
	max-width: min(78vw, 420px);
	color: #fff;
	text-shadow: 0 1px 10px rgb(0 0 0 / 0.5);
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.8s ease;
}
.site-footer.show { opacity: 1; pointer-events: auto; }
.sf-line {
	margin: 0;
	font-family: "DM Mono", ui-monospace, monospace;
	font-weight: 300;
	font-size: 11px;
	line-height: 1.7;
	letter-spacing: 0.12em;
	text-transform: uppercase;
}
.sf-line a { color: inherit; text-underline-offset: 3px; letter-spacing: 0.02em; }
.site-footer .pi-meta { color: inherit; }

@media (max-width: 700px) {
	.site-footer { left: 20px; }
}

