@font-face {
	font-family: 'Nunito';
	src: url('/assets/fonts/nunito-latin-wght-normal.woff2') format('woff2');
	font-weight: 200 1000;
	font-style: normal;
	font-display: swap;
}

:root {
	color-scheme: light;
	--ink: #2b1b4f;
	--ink-soft: #4b3f6e;
	--line: #b9aed6;
	--sky-top: #e2d6fa;
	--sky-right: #cfe6fc;
	--sky-bottom: #f6def0;
	--card: #ffffff;
	--dark-btn: #1e1d26;
	--font: 'Nunito', ui-rounded, system-ui, -apple-system, 'Segoe UI', sans-serif;
}

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

html {
	height: 100%;
}

body {
	margin: 0;
	min-height: 100vh;
	min-height: 100dvh;
	overflow-x: hidden;
	font-family: var(--font);
	color: var(--ink);
	background:
		radial-gradient(120% 70% at 100% 0%, var(--sky-right), transparent 70%),
		radial-gradient(90% 60% at 50% 110%, var(--sky-bottom), transparent 75%),
		var(--sky-top);
	-webkit-font-smoothing: antialiased;
}

/* The founder's painted sky. A fixed layer (not background-attachment: fixed, which iOS ignores);
   the gradient above stays underneath while the picture loads. */
/* the sky picture: a real element (depth.js moves it; a pseudo-element cannot be reached from script) */
.skypic {
	position: fixed;
	inset: -24px; /* bleed, so the camera never shows an edge */
	z-index: -1;
	pointer-events: none;
	background-image: url('/assets/sky/desktop/sky.webp');
	background-size: cover;
	background-position: center bottom;
}
@media (max-aspect-ratio: 4/5) {
	.skypic {
		background-image: url('/assets/sky/mobile/sky.webp');
	}
}

/* ---------- The door: logo, words and buttons ---------- */

.door {
	position: relative;
	z-index: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	width: min(100%, 440px);
	min-height: 100vh;
	min-height: 100dvh;
	margin-inline: auto;
	padding:
		calc(env(safe-area-inset-top) + 13vh)
		calc(env(safe-area-inset-right) + 24px)
		calc(env(safe-area-inset-bottom) + 18px)
		calc(env(safe-area-inset-left) + 24px);
	padding:
		calc(env(safe-area-inset-top) + 13dvh)
		calc(env(safe-area-inset-right) + 24px)
		calc(env(safe-area-inset-bottom) + 18px)
		calc(env(safe-area-inset-left) + 24px);
	text-align: center;
}

.brand {
	margin: 0;
	line-height: 0;
}
.logo {
	width: min(84vw, 400px);
	height: auto;
	filter: drop-shadow(0 8px 18px rgb(90 60 160 / 0.18));
}

.words {
	margin-top: clamp(24px, 8vh, 90px);
	margin-top: clamp(24px, 8dvh, 90px);
}
.title {
	margin: 0;
	font-size: clamp(30px, 8.6vw, 40px);
	font-weight: 900;
	letter-spacing: -0.01em;
	line-height: 1.1;
}
.line {
	margin: 8px 0 0;
	font-size: clamp(17px, 4.6vw, 21px);
	font-weight: 600;
	color: var(--ink-soft);
}

.actions {
	width: 100%;
	margin-top: clamp(24px, 5vh, 44px);
	margin-top: clamp(24px, 5dvh, 44px);
}

.btn {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 12px;
	width: 100%;
	min-height: 58px;
	padding: 0 24px;
	border: 0;
	border-radius: 999px;
	font: 800 18px/1 var(--font);
	cursor: pointer;
	transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.btn:active {
	transform: scale(0.98);
}
.btn:focus-visible {
	outline: 3px solid #7b5cff;
	outline-offset: 3px;
}
.google {
	background: var(--dark-btn);
	color: #fff;
	box-shadow: 0 10px 24px rgb(30 20 60 / 0.25);
}
.google .g {
	flex: none;
	width: 22px;
	height: 22px;
	padding: 0;
}
.email {
	background: var(--card);
	color: var(--ink);
	box-shadow: 0 8px 22px rgb(120 90 200 / 0.16);
}
@media (hover: hover) {
	.btn:hover {
		transform: translateY(-1px);
	}
}

.or {
	display: flex;
	align-items: center;
	gap: 14px;
	margin: 18px 4px;
	font-size: 16px;
	font-weight: 600;
	color: var(--ink-soft);
}
.or::before,
.or::after {
	content: '';
	flex: 1;
	height: 1px;
	background: var(--line);
}

.foot {
	margin: auto 0 0;
	padding-top: 24px;
	font-size: 13px;
	font-weight: 600;
	color: var(--ink-soft);
	opacity: 0.8;
}

/* ---------- "Opening soon" ---------- */

dialog:not([open]) {
	display: none;
}
dialog[open] {
	/* centred also where <dialog> is not native */
	position: fixed;
	inset: 0;
	margin: auto;
	height: fit-content;
	z-index: 10;
}
dialog {
	width: min(88vw, 340px);
	padding: 28px 24px 24px;
	border: 0;
	border-radius: 28px;
	background: var(--card);
	color: var(--ink);
	text-align: center;
	box-shadow: 0 24px 60px rgb(40 20 90 / 0.3);
}
dialog::backdrop {
	background: rgb(60 40 110 / 0.35);
	backdrop-filter: blur(3px);
}
dialog img {
	width: 160px;
	height: auto;
}
dialog h2 {
	margin: 10px 0 6px;
	font-size: 24px;
	font-weight: 900;
}
dialog p {
	margin: 0 0 20px;
	font-size: 16px;
	font-weight: 600;
	color: var(--ink-soft);
}
dialog .btn {
	min-height: 50px;
	border: 2px solid #d9cff3;
	box-shadow: none;
}
.x {
	position: absolute;
	top: 10px;
	right: 12px;
	width: 40px;
	height: 40px;
	border: 0;
	border-radius: 50%;
	background: transparent;
	color: var(--ink-soft);
	font: 700 26px/1 var(--font);
	cursor: pointer;
}
.x:focus-visible {
	outline: 3px solid #7b5cff;
}

/* ---------- Wide screens ---------- */

@media (min-width: 860px) {
	.door { padding-top: 14vh; }
}

/* ---------- Floating collectibles (layout from tools/layout-study.py + layout-assets.py; every number is the model's) ---------- */

:root {
	/* three sizes only, as a share of the phone's width with a ceiling */
	--S: min(14.4vw, 60px);
	--M: min(18.5vw, 80px);
	--L: min(22.6vw, 96px);
	/* where the page's own layout puts things (the same formulas as .door, .logo, .words, .actions, .foot) */
	--logo-top: calc(13vh + env(safe-area-inset-top, 0px));
	--logo-top: calc(13dvh + env(safe-area-inset-top, 0px));
	--foot-up: calc(59.6px + env(safe-area-inset-bottom, 0px));
	--email-bottom: calc(var(--logo-top) + 0.3896 * min(84vw, 400px) + clamp(24px, 8vh, 90px) + 1.1 * clamp(30px, 8.6vw, 40px) + 8px + 1.382 * clamp(17px, 4.6vw, 21px) + clamp(24px, 5vh, 44px) + 173.6px);
	--email-bottom: calc(var(--logo-top) + 0.3896 * min(84vw, 400px) + clamp(24px, 8dvh, 90px) + 1.1 * clamp(30px, 8.6vw, 40px) + 8px + 1.382 * clamp(17px, 4.6vw, 21px) + clamp(24px, 5dvh, 44px) + 173.6px);
	/* room for a tilted silhouette: 24 px from content + 4 px float on that side, 8 px from the frame + 4 px float
	   (the top band is measured below the status-bar inset, never into it); 60 px from a button */
	--band-top: calc(13vh - 40px);
	--band-top: calc(13dvh - 40px);
	--band-bottom: calc(100vh - var(--foot-up) - var(--email-bottom) - 92px);
	--band-bottom: calc(100dvh - var(--foot-up) - var(--email-bottom) - 92px);
	/* what each piece may be: its size tier, capped by the band above/below and by the room between the 24 px
	   side margin and the clear centre axis (factors = rotated height/width per px of size) */
	--fit-p1: min(var(--L), var(--band-top) / 0.6115, (50vw - 40px) / 1.0421);
	--fit-p2: min(var(--L), var(--band-top) / 0.779, (50vw - 40px) / 1.0567);
	--fit-p3: min(var(--S), var(--band-bottom) / 1.0096, (50vw - 40px) / 1.0769);
	--fit-p4: min(var(--L), var(--band-bottom) / 1.0422, (50vw - 40px) / 0.7082);
	/* a pair shows together or not at all: below 45 px either one would be a smudge, so both collapse to 0 */
	--pair-top: calc((min(var(--fit-p1), var(--fit-p2)) - 45px) * 1000);
	--pair-bottom: calc((min(var(--fit-p3), var(--fit-p4)) - 45px) * 1000);
}

.sky {
	position: fixed;
	inset: 0;
	z-index: 0;
	overflow: hidden;
	pointer-events: none;
	/* moved by depth.js at depth 18 */
	animation: enter 1.4s cubic-bezier(.2, .8, .2, 1) 0.4s both;
}
.sky picture {
	display: contents;
}
.float {
	position: absolute;
	height: auto;
	filter: drop-shadow(0 6px 10px rgb(80 50 140 / 0.16));
	animation: float var(--t, 5s) ease-in-out var(--d, 0s) infinite alternate;
}
@keyframes float {
	from { transform: translateY(-4px); }
	to { transform: translateY(4px); }
}
@media (prefers-reduced-motion: reduce) {
	.float { animation: none; }
}

/* phones: the car and the idiom piece above the logo, the bike and the robot above the footer.
   Each piece's outer edge sits on the 24 px side margin (plus the sliver its tilt adds), so the two on a side share one edge line. */
.f-car    { --s: clamp(0px, var(--pair-top), var(--fit-p1)); width: var(--s); left: calc(24px + var(--s) * 0.0211); top: calc(var(--logo-top) - 28px - var(--s) * 0.5689); rotate: -5deg; --t: 4.9s; --d: -2.2s; }
.f-idiom  { --s: clamp(0px, var(--pair-top), var(--fit-p2)); width: var(--s); right: calc(24px + var(--s) * 0.0284); top: calc(var(--logo-top) - 28px - var(--s) * 0.7367); rotate: 5deg; --t: 5.2s; --d: -1.3s; }
.f-bike   { --s: clamp(0px, var(--pair-bottom), var(--fit-p3)); width: var(--s); left: calc(24px + var(--s) * 0.0385); bottom: calc(var(--foot-up) + 28px + var(--s) * 0.0418); rotate: 5deg; --t: 4.7s; --d: -3.1s; }
.f-gundam { --s: clamp(0px, var(--pair-bottom), var(--fit-p4)); height: var(--s); width: auto; right: calc(24px + var(--s) * 0.0341); bottom: calc(var(--foot-up) + 28px + var(--s) * 0.0211); rotate: -4deg; --t: 5.5s; --d: -0.6s; }
.f-guitar, .f-arttoy { display: none; }

/* wide screens: six, anchored to the column (96 px off its edge, the middle pair 224 px), so the picture holds at any width */
@media (min-width: 860px) {
	.float { left: auto; right: auto; top: auto; bottom: auto; }
	.f-car    { width: 120px; right: calc(50% + 296px); top: calc(14vh + 46.4px); rotate: -5deg; }
	.f-gundam { height: 120px; width: auto; left: calc(50% + 296px); top: calc(14vh + 18px); rotate: -4deg; }
	.f-guitar { display: block; height: 96px; width: auto; right: calc(50% + 424px); top: calc(22vh + 130px); rotate: -8deg; --t: 5s; --d: -4s; }
	.f-bike   { width: 72px; left: calc(50% + 424px); top: calc(22vh + 144.7px); rotate: -5deg; }
	.f-arttoy { display: block; height: 96px; width: auto; right: calc(50% + 296px); bottom: 30.3px; rotate: 4deg; --t: 5.3s; --d: -1.8s; }
	.f-idiom  { width: 120px; left: calc(50% + 296px); bottom: 33.1px; rotate: 5deg; }
}
/* narrower than 1044 px the middle pair would leave the frame */
@media (min-width: 860px) and (max-width: 1043px) {
	.f-guitar, .f-bike { display: none; }
}
/* shorter than 640 px the column and footer no longer fit the window: the page scrolls, so nothing floats over it */
@media (min-width: 860px) and (max-height: 639px) {
	.float { display: none; }
}

/* ---------- Entrance: the sky settles first (sky.css), then the column rises in, line by line ---------- */
.door > * {
	animation: rise 0.9s cubic-bezier(.2, .8, .2, 1) both;
}
.door > :nth-child(1) { animation-delay: 0.15s; }
.door > :nth-child(2) { animation-delay: 0.3s; }
.door > :nth-child(3) { animation-delay: 0.42s; }
.door > :nth-child(4) { animation-delay: 0.6s; }
@keyframes rise {
	from { opacity: 0; translate: 0 14px; }
}
@media (prefers-reduced-motion: reduce) {
	.door > *, .sky { animation: none; }
	.sky, .skypic { transform: none; }
}

/* ---------- Performance (10-10, see docs FRONT-DOOR-PERFORMANCE-PLAN) ---------- */
/* after the entrance (depth.js adds .settled at 2.6 s) its animation state is dropped */
.settled .door > *, .settled .sky { animation: none; }
/* while the "opening soon" dialog is open the scene holds still (founder 10-10) */
.paused .cloud, .paused .char, .paused .float { animation-play-state: paused; }

/* the pause control for the moving scene (WCAG 2.2.2; Vercel's interface guidelines) */
.motion-toggle {
	position: fixed;
	right: calc(env(safe-area-inset-right) + 14px);
	bottom: calc(env(safe-area-inset-bottom) + 14px);
	z-index: 2;
	display: grid;
	place-items: center;
	width: 36px;
	height: 36px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: rgb(255 255 255 / 0.72);
	color: var(--ink-soft);
	box-shadow: 0 4px 12px rgb(60 40 110 / 0.16);
	cursor: pointer;
}
.motion-toggle svg { fill: currentColor; }
.motion-toggle .i-play { display: none; }
.still .motion-toggle .i-pause { display: none; }
.still .motion-toggle .i-play { display: block; }
.motion-toggle:focus-visible { outline: 3px solid #7b5cff; outline-offset: 2px; }
@media (hover: hover) { .motion-toggle:hover { background: #fff; } }
@media (prefers-reduced-motion: reduce) { .motion-toggle { display: none; } }

/* paused by the visitor: everything holds still */
.still .cloud, .still .char, .still .float { animation-play-state: paused; }

/* the light tier (weak devices, depth.js): no glow, no dialog blur; the camera is off in script */
.lite .glow { display: none; }
.lite dialog::backdrop { backdrop-filter: none; background: rgb(60 40 110 / 0.45); }

/* ---------- Phones: the founder's picture (10-10). Drifting clouds, the logo with its case behind it (the case
   wiggles now and then), a boy and a girl either side of "Welcome to Tuu", a few stars. Nothing else floats. ---------- */

.case, .pals, .words .pal { display: none; }
@media (max-width: 859px) {
	.sky { display: none; }

	/* the lockup rebuilt from two pictures, placed where the founder's Photoshop lockup has them (tools/brand-mobile.py) */
	.brand { position: relative; width: min(84vw, 400px); aspect-ratio: 1797 / 700; }
	.brand .logo { position: absolute; z-index: 1; left: 0; top: 10.43%; width: 69.73%; }
	.case {
		display: block;
		position: absolute;
		left: 55.82%;
		top: -15.33%;
		width: 52.5%;
		height: auto;
		transform-origin: 50% 77%; /* the bottom of the case: it rocks on its base */
		animation: wiggle 4s ease-in-out 1.6s infinite;
	}

	.pals {
		display: block;
		position: fixed;
		inset: 0;
		z-index: 0;
		pointer-events: none;
		animation: rise 0.9s cubic-bezier(.2, .8, .2, 1) 0.75s both;
		/* the stars over the logo: centred in the band above the case (14 px off the frame, 16 px clear of the case) */
		--band: calc(var(--logo-top) - 30px - env(safe-area-inset-top, 0px));
		--s1-fit: min(32px, var(--band) - 6px);
		--s1: clamp(0px, (var(--s1-fit) - 18px) * 1000, var(--s1-fit));
		--s2: calc(var(--s1) * 0.7);
		--mid: calc(env(safe-area-inset-top, 0px) + 14px + var(--band) / 2);
		--lw: min(84vw, 400px);
	}
	.pal { position: absolute; height: auto; } /* each picture's soft shadow is baked in (cut-objects.py) */
	/* sizes and offsets are per the figure inside each picture (chars/manifest.json "art" vs "px") */
	.s1 { width: calc(var(--s1) * 1.7561); left: calc(50% - var(--lw) * 0.36 - var(--s1) * 0.878); top: calc(var(--mid) - 3px - var(--s1) * 0.744); --t: 4.6s; --dl: -1.2s; }
	.s2 { width: calc(var(--s2) * 1.775); left: calc(50% + var(--lw) * 0.4 - var(--s2) * 0.8875); top: calc(var(--mid) + 3px - var(--s2) * 0.7375); --t: 5.3s; --dl: -3s; }

	/* the boy stands on the title's baseline, 10 px to the left of "Welcome". His height (the figure, not the
	   picture) fits the room left of the title (12 px off the frame) and the room under the wordmark (14 px clear);
	   the title is 7.72 font-sizes wide in Nunito 900 (measured). */
	.words {
		position: relative;
		--fs: clamp(30px, 8.6vw, 40px);
		--lw: min(84vw, 400px);
		--kid-fit: min(120px, ((100vw - 7.72 * var(--fs)) / 2 - 22px) / 0.521, 0.0412 * var(--lw) + clamp(24px, 8vh, 90px) + 0.879 * var(--fs) - 14px);
		--kid-fit: min(120px, ((100vw - 7.72 * var(--fs)) / 2 - 22px) / 0.521, 0.0412 * var(--lw) + clamp(24px, 8dvh, 90px) + 0.879 * var(--fs) - 14px);
		--s3: min(18px, var(--kid) * 0.2);
		/* the girl, on the same baseline right of "Tuu": the case sits above her side, so her room under it is
		   12 px clear of the case's base (it rocks a couple of px); she is often a little shorter than the boy */
		--girl-fit: min(120px, ((100vw - 7.72 * var(--fs)) / 2 - 22px) / 0.5475, clamp(24px, 8vh, 90px) + 0.879 * var(--fs) - 12px);
		--girl-fit: min(120px, ((100vw - 7.72 * var(--fs)) / 2 - 22px) / 0.5475, clamp(24px, 8dvh, 90px) + 0.879 * var(--fs) - 12px);
		/* they come as a pair: if either would be under 56 px (a smudge), both step out */
		--pair: calc((min(var(--kid-fit), var(--girl-fit)) - 56px) * 1000);
		--kid: clamp(0px, var(--pair), var(--kid-fit));
		--girl: clamp(0px, var(--pair), var(--girl-fit));
	}
	.words .pal { display: block; }
	.kid { width: auto; height: calc(var(--kid) * 1.1886); right: calc(100% + 10px - var(--kid) * 0.0928); top: calc(0.879 * var(--fs) - var(--kid) * 1.0569); }
	.girl { width: auto; height: calc(var(--girl) * 1.1925); left: calc(100% + 10px - var(--girl) * 0.095); top: calc(0.879 * var(--fs) - var(--girl) * 1.06); }
	/* a small one by the boy's head, as if he has just spotted it */
	.s3 { width: calc(var(--s3) * 1.7561); left: calc(-2px - var(--s3) * 0.378); top: calc(0.879 * var(--fs) - var(--kid) * 0.86 - var(--s3) * 0.744); --t: 3.9s; --dl: -2.1s; }
	.star { animation: twinkle var(--t) ease-in-out var(--dl) infinite alternate; }
}

/* the case: still for a moment, then a short wiggle, like something inside is excited */
@keyframes wiggle {
	0%, 55%, 100% { rotate: 0deg; translate: 0 0; }
	62% { rotate: -2.6deg; translate: 0 -2px; }
	70% { rotate: 2.2deg; translate: 0 -1px; }
	78% { rotate: -1.5deg; translate: 0 -1px; }
	86% { rotate: 0.8deg; translate: 0 0; }
	93% { rotate: 0deg; }
}
@keyframes twinkle {
	from { translate: 0 -3px; rotate: -5deg; }
	to { translate: 0 3px; rotate: 5deg; }
}
.settled .pals { animation: none; }
.paused .case, .paused .pal, .still .case, .still .pal { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
	.case, .pals, .pal { animation: none; }
}
