/* New Rubrik (Newlyn) — licensed 2026-07-07, order #260061 (annual subscription,
   valid through 2027-07-07). Only the 3 weights actually used are loaded; one
   @font-face per weight since this is a static family, not a variable font. */
@font-face {
	font-family: "New Rubrik";
	src: url("/fonts/NewRubrik-Regular.woff2") format("woff2");
	font-weight: 400;
	font-display: block;
}
@font-face {
	font-family: "New Rubrik";
	src: url("/fonts/NewRubrik-Bold.woff2") format("woff2");
	font-weight: 700;
	font-display: block;
}
@font-face {
	font-family: "New Rubrik";
	src: url("/fonts/NewRubrik-ExtraBold.woff2") format("woff2");
	font-weight: 800;
	font-display: block;
}

/* Design tokens. Background gradient and event colour are ported from the app. */
:root {
	color-scheme: light dark;
	overflow-x: clip;

	--grad-top: oklch(0.6700 0.2969 345.00);
	--grad-middle: oklch(0.5234 0.2969 330.04);
	--grad-bottom: oklch(0.5050 0.2969 301.00);
	/* Zero-alpha twins; `transparent` would grey the fade. */
	--grad-top-0: oklch(from var(--grad-top) l c h / 0);
	--grad-middle-0: oklch(from var(--grad-middle) l c h / 0);
	--grad-bottom-0: oklch(from var(--grad-bottom) l c h / 0);

	/* Edges resolve to --grad-middle so the iOS safe-area insets blend. */
	--background:
		linear-gradient(to bottom, var(--grad-middle) 0%, var(--grad-middle) 7%, var(--grad-middle-0) 22%, var(--grad-middle-0) 78%, var(--grad-middle) 93%, var(--grad-middle) 100%),
		radial-gradient(120% 82% at 24% 34%, var(--grad-top), var(--grad-top-0) 84%),
		radial-gradient(120% 82% at 76% 66%, var(--grad-bottom), var(--grad-bottom-0) 84%);
	--text: oklch(100% 0 0);

	/* Shared by the clock beads, the work-hours band, and the signup pill. */
	--event-l: 0.700007;
	--event-c: 0.296876;
	--event-h: 180.04;

	--clock-max: 400px;
	--clock-pad: 1.12;

	--pad: clamp(2rem, 5vw, 4rem);

	/* clock.js reads these for the event-bead shadow tint. */
	--shadow-transmit: 0.6;
	--shadow-cap: 0.2;

	@media (prefers-color-scheme: dark) {
		--grad-top: oklch(0.5360 0.2969 345.00);
		--grad-middle: oklch(0.4188 0.2969 330.04);
		--grad-bottom: oklch(0.4040 0.2969 301.00);
	}
}

* {
	box-sizing: border-box;
}

body {
	margin: 0;
	min-height: 100dvh;
	overflow-x: clip;
	/* Flex column, not grid: .site-footer-logo is a real flow sibling of <main> (see
	   below) so it can enforce a guaranteed minimum gap. A grid's place-items: center
	   just overflows past its padding once content doesn't fit — it can't reserve
	   space for an element it doesn't control the position of. */
	display: flex;
	flex-direction: column;
	align-items: center;
	/* Each side clears the notch/home-indicator. */
	padding:
		max(var(--pad), env(safe-area-inset-top))
		max(var(--pad), env(safe-area-inset-right))
		max(var(--pad), env(safe-area-inset-bottom))
		max(var(--pad), env(safe-area-inset-left));
	background-color: var(--grad-middle);   /* sampled for the safe-area insets */
	background-image: var(--background);
	color: var(--text);
	font-family: "New Rubrik", system-ui, sans-serif;
	font-optical-sizing: auto;
}

/* Single sizing control for the whole block; tune scale here. */
main {
	--w-min: 15rem;
	--w-max: 36rem;
	--main-gap: 24px;
	transform: translateY(-5vh);
	width: clamp(var(--w-min), 100%, var(--w-max));
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--main-gap);
	/* Centers main in whatever space body's flex column has left over — same visual
	   effect as the old grid place-items: center, but as a real flex item it can be
	   pushed down by .site-footer-logo's own margin instead of just overlapping it. */
	margin-block: auto;
}

/* WebGL clock (progressive enhancement); clock.js mounts a canvas here. */
#daywatch-clock {
	position: relative;
	isolation: isolate;
	z-index: 2;           /* clock + its ground shadow sit above the form */
	touch-action: none;   /* drag tilts the clock instead of scrolling */
	width: calc((100% + 16px) * var(--clock-pad));
	max-width: calc(var(--clock-max) * var(--clock-pad));
	aspect-ratio: 1;
	/* Hidden until clock.js adds .clock-live on its first frame, then fades in. No placeholder image: it caused an iOS wide-gamut re-composite flash. */

	/* Faked ground shadow (Safari won't drop-shadow a WebGL canvas). */
	&::before {
		content: "";
		position: absolute;
		top: 50%;
		left: 50%;
		width: 88%;
		aspect-ratio: 1;
		/* Foreshorten with the live tilt (--tilt-x/y from clock.js). */
		transform: translate(-50%, -50%) scale(calc(1 - max(var(--tilt-y, 0), var(--tilt-y, 0) * -1) * 0.18), calc(1 - max(var(--tilt-x, 0), var(--tilt-x, 0) * -1) * 0.18));
		z-index: -1;
		border-radius: 50%;
		opacity: 0;
		transition: opacity 1s ease;
		box-shadow:
			1.5px 4.5px 5px rgba(0, 0, 0, 0.22),
			5px 14px 11px rgba(0, 0, 0, 0.20),
			12px 33px 20px rgba(0, 0, 0, 0.18),
			24px 66px 24px rgba(0, 0, 0, 0.22),
			40px 111px 35px rgba(0, 0, 0, 0.20),
			60px 165px 47px rgba(0, 0, 0, 0.18);
	}
	&.clock-live::before { opacity: 1; }
	& canvas {
		position: absolute;   /* keep canvas height out of the aspect-ratio box */
		inset: 0;
		z-index: 0;
		display: block;
		width: 100%;
		height: 100%;
		opacity: 0;
		transition: opacity 1s ease;
	}
	&.clock-live canvas { opacity: 1; }
}

/* Text block + form, grouped so they share one gap distinct from the clock's. */
.content {
	width: 100%;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 2rem;
}

h1 {
	width: 100%;
	margin: 0;
	font-size: 2rem;
	font-weight: 800;
	line-height: 1;
	text-align: center;
	/* Balances line lengths when it must wrap; stays on one line once the
	   container's wide enough to fit it without wrapping at all. */
	text-wrap: balance;
}

/* Post-signup pages (thanks/confirmed): icon + name + message, no clock. Ported 1:1 from Figma
   (node 19:126, "Thank you"). */
.post-signup {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 48px;
}

.post-signup-identity {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;

	/* Overrides the site-wide h1 (2rem/800, tuned for the homepage headline) with the
	   smaller, dimmer wordmark this design uses. Explicit stacking context: the icon's
	   drop-shadow reaches 165px down, well into this text 4px below it. */
	& h1 {
		width: auto;
		position: relative;
		z-index: 1;
		font-size: 20px;
		line-height: 32px;
		opacity: 0.8;
	}

	& .app-icon {
		width: 128px;
		height: 128px;
		/* A background-image, not an <img> — inset box-shadow paints below an element's own
		   content, so it can never show up over a real <img> inside it (the image always
		   covers its own inset shadow, even on a wrapper). Moving the icon to a background
		   means there's no competing content, so one element can carry the rounding and
		   every shadow layer, outer and inset alike. Source PNG (app-icon-squared.png) is a
		   plain square with no rounding baked in, so this radius is the real, exact shape
		   now instead of an approximation (matches Figma's icon node exactly). box-shadow
		   (not drop-shadow) since it respects border-radius directly and is cheap to render —
		   drop-shadow used to reanalyze the whole alpha channel on every paint, which is what
		   made Safari take up to 20s on this. Outer layers are the homepage clock's ground
		   shadow (#daywatch-clock::before); inset layers are the icon's own highlight/glow
		   from Figma. */
		background-image: url("/images/app-icon-squared.png");
		background-size: cover;
		border-radius: 34px;
		box-shadow:
			1.5px 4.5px 5px rgba(0, 0, 0, 0.22),
			5px 14px 11px rgba(0, 0, 0, 0.20),
			12px 33px 20px rgba(0, 0, 0, 0.18),
			24px 66px 24px rgba(0, 0, 0, 0.22),
			40px 111px 35px rgba(0, 0, 0, 0.20),
			60px 165px 47px rgba(0, 0, 0, 0.18),
			inset 0 2px 2px rgba(255, 255, 255, 0.4),
			inset 0 0 4px rgba(255, 77, 209, 0.75);
	}
}

.post-signup-message {
	margin: 0;
	max-width: 320px;
	font-size: 16px;
	font-weight: 400;
	line-height: normal;
	text-align: center;
	text-wrap: balance;
}

@keyframes signup-spin {
	to { transform: rotate(360deg); }
}

/* Signup field: a groove carved into the gradient, with the pill seated in it. */
.signup {
	position: relative;
	min-height: 3.3rem;
	display: flex;
	align-items: center;
	width: 100%;
	max-width: 480px;
	padding: 4px;
	border-radius: 999px;
	background: linear-gradient(to bottom, rgba(0, 0, 0, 0.20) 0%, rgba(0, 0, 0, 0.32) 26%, rgba(0, 0, 0, 0.17) 58%, rgba(0, 0, 0, 0.04) 85%, rgba(255, 255, 255, 0.08) 100%);
	box-shadow:
		inset 0 -1.5px 0.5px color-mix(in oklch, var(--grad-top), transparent 45%),
		inset 0 1px 0.5px rgba(255, 255, 255, 0.10),
		inset 0 -1px 0.5px rgba(255, 255, 255, 0.13),
		inset 0 0 0 1px rgba(0, 0, 0, 0.10),
		inset 4px 12px 6px oklch(0 0 0 / 0.25);

	/* Lit lower lip. */
	&::after {
		content: "";
		position: absolute;
		inset: 0;
		z-index: 0;
		border-radius: inherit;
		pointer-events: none;
		background: linear-gradient(100deg, rgba(255, 255, 255, 0.26) 0%, rgba(255, 255, 255, 0.08) 32%, rgba(255, 255, 255, 0) 58%);
		-webkit-mask: linear-gradient(to top, #000 0%, transparent 40%);
		        mask: linear-gradient(to top, #000 0%, transparent 40%);
	}

	/* Focus ring — only when the email input is focused (the button rings itself). */
	&:has(input:focus) {
		outline: 3px solid rgba(255, 255, 255, 0.75);
		outline-offset: 3px;
	}

	& .signup-label {
		flex: none;
		position: relative;
		z-index: 1;
		padding-left: 1.1rem;
		font: inherit;
		font-size: 1rem;
		font-weight: 700;
		color: #fff;
		user-select: none;
	}

	& input {
		flex: 1;
		min-width: 0;
		position: relative;
		z-index: 1;
		padding: 0.5rem 4px 0.5rem 0.7rem;
		font: inherit;
		font-size: 1rem;
		color: #fff;
		background: transparent;
		border: none;

		&::placeholder { color: rgba(255, 255, 255, 0.55); }
		&:focus { outline: none; }   /* the groove shows the ring instead */
	}

	/* Sign up pill: a glass green button. Colour tracks --event-*. */
	& button {
		flex: none;
		align-self: stretch;
		position: relative;
		z-index: 1;                   /* stacking context for the specular ::before */
		padding: 0.5rem 1.35rem;
		font: inherit;
		font-size: 0.95rem;
		font-weight: 700;
		color: #000;                  /* black on green — WCAG AA */
		border: none;
		border-radius: 999px;
		cursor: pointer;
		-webkit-appearance: none;
		appearance: none;
		white-space: nowrap;
		--sc: calc(var(--event-c) * 0.9);   /* button-only: 10% less saturated than the clock/band */
		background: oklch(100% var(--sc) var(--event-h) / 0.5);
		--sl: var(--event-l);   /* inner-shadow lightness; --drop scales the drop shadow */
		box-shadow:
			inset 0 0 4px 1px oklch(var(--sl) var(--sc) var(--event-h) / 0.6),
			inset -4px -10px 6px oklch(calc(var(--sl) * 0.55) var(--sc) var(--event-h) / 0.6),
			inset 4px 16px 16px oklch(100% 0.1 var(--event-h) / 0.6),
			inset 0 0 24px 12px oklch(var(--sl) var(--sc) var(--event-h) / 0.8),
			calc(4px * var(--drop, 1)) calc(4px * var(--drop, 1)) calc(4px * var(--drop, 1)) oklch(0% 0 0 / 0.25);
		transition: box-shadow 0.5s ease, margin 0.5s ease, padding 0.5s ease;

		/* Specular streak; hover lifts its opacity to 1. */
		&::before {
			content: "";
			position: absolute;
			top: 20%;
			left: 16%;
			right: 20%;
			height: 12%;
			z-index: -1;
			border-radius: 999px;
			pointer-events: none;
			background: linear-gradient(to right, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0.435));
			opacity: 0.92;
			filter: blur(2.5px);
		}
		/* Hover swells the pill; press snaps back instantly. No transform/opacity animation
		   here — either would promote a GPU layer that flickers above the clock shadow.
		   Pointer-only so a tap can't stick. */
		@media (hover: hover) {
			&:hover { --drop: 1.5; --sl: 0.9; margin: -1px; padding-inline: calc(1.35rem + 1px); }
			&:hover::before { opacity: 1; }
		}
		&:active { --drop: 1; margin: 0; padding-inline: 1.35rem; transition: none; }
		&:focus-visible { outline: 3px solid rgba(255, 255, 255, 0.75); outline-offset: 3px; }
	}

	/* iOS-style spinner (see index.njk): hidden until JS adds .is-submitting on the real
	   form submit, then swaps in for the label. Discrete steps(8), not linear — a smooth
	   spin reads as a generic web spinner; the native indicator jumps between fixed
	   positions. */
	& .ios-spinner {
		display: none;
	}
	&.is-submitting button {
		pointer-events: none;

		& .btn-label { visibility: hidden; }
		& .ios-spinner {
			display: block;
			position: absolute;
			top: 50%;
			left: 50%;
			margin: -10px 0 0 -10px;
			animation: signup-spin 1s steps(8) infinite;
		}
	}

	/* Faked cast shadow behind the pill. Tune with --psh-*. */
	&::before {
		content: "";
		position: absolute;
		z-index: 0;
		top: 50%;
		right: 4px;
		width: var(--psh-w, 100px);
		height: calc(var(--psh-len, 44px) * var(--cast, 1));
		transition: height 0.5s ease;
		border-radius: 0 0 var(--psh-round-r, 32px) var(--psh-round-l, 20px);
		background: rgb(0 0 0 / var(--psh-dark, 0.2));
		transform-origin: top center;
		transform: skewX(var(--psh-skew, 20deg));
		filter: blur(var(--psh-blur, 4px));
		pointer-events: none;
		-webkit-mask: linear-gradient(to bottom, #000 var(--psh-fade, 35%), transparent 100%);
		        mask: linear-gradient(to bottom, #000 var(--psh-fade, 35%), transparent 100%);
	}
	/* Cast shadow follows the button's state; :has() reaches it since it's a sibling pseudo. */
	@media (hover: hover) {
		&:has(button:hover)::before { --cast: 1.25; }
	}
	&:has(button:active)::before { --cast: 1; transition: none; }
}

/* Studio wordmark, every page (set in base.njk) — pinned to the viewport, not the document,
   so it stays put at the bottom regardless of page height or scroll. */
.site-footer-logo {
	/* In normal flow (a flex sibling of <main>, not fixed) so this gap is a real,
	   guaranteed minimum — not a position that can overlap main once content stops
	   fitting the viewport. When there's extra room, main's margin-block: auto pushes
	   this down near the bottom, same look as the old fixed-48px pin; when there
	   isn't, the page grows past 100dvh and scrolls instead of overlapping. */
	margin-top: 64px;
	margin-bottom: 48px;
	opacity: 0.9;
}
