/*
 * The operator's own page, outside the app: plain CSS, not the app's Tailwind theme. The app is
 * styled from a hotel's tokens (apps/web/src/lib/design/app.css) and the operator never appears in
 * it; these are the product's own colours instead, navy, gold and white (docs/brand/README.md).
 *
 * The page is daybreak: a dark body rising past the foot of the frame, lit along its upper rim, with
 * the bloom standing over it. Nothing here is an image — the whole field is gradients and one inset
 * shadow, because the page's CSP lets it load no script and no third-party byte at all.
 */

/* Jost, self-hosted: the display line is the page, and a platform sans is not a display voice. One
   variable file covers 300 to 600. Licensed SIL OFL 1.1 — see OFL.txt beside this file.
   One file covering latin and latin-ext, subset from the variable original, so Turkish's ğ, ş, İ
   and ı are in the same file as everything else. No unicode-range anywhere: Google serves those two
   ranges as two files, and the second of them never loaded in the browser this was built against,
   which is a fallback nobody would see. With one file there is no range for a glyph to fall
   through. */
@font-face {
	font-family: 'Jost';
	font-style: normal;
	font-weight: 300 600;
	font-display: swap;
	src: url('/jost.woff2') format('woff2');
}

:root {
	color-scheme: dark;

	/* The product's own colours (docs/brand/README.md). */
	--gold: #e2b15c;

	/* The field is night, and it is darkest behind the body: what is below the rim is not sky but the
	   space the body stands in, so lifting it toward the brand navy lights the two bottom corners and
	   turns a lit body into a dark disc cut out of a bright ground — tried, and reverted. The navy
	   itself is on screen in the body's own lit face (#12293f, three points a channel off #143C61
	   taken down), which is where a dawn puts it. */
	--field-high: #04080e;
	--field-mid: #060d17;
	--field-low: #0a1826;

	/* The rim, hot where the light breaks and gold a pixel below it. */
	--rim-hot: #fff3dc;
	--rim: #f0c77e;

	--ink: #ffffff;
	/* Measured on the darkest ground this page has: 9.5:1 and 5.6:1. */
	--ink-quiet: rgb(255 255 255 / 74%);
	--ink-faint: rgb(255 255 255 / 58%);

	--gutter: clamp(1.25rem, 4vw, 3.5rem);

	/* The top of the body, which is the horizon everything else is placed against, and the drop
	   below it that the type has to start at. Measured, not guessed: white on the lit rim came back
	   at 2.02:1, so the type is anchored under the light rather than centred across it. The clear is
	   a fraction of the body because that is what sets how fast the arc falls away. */
	--rim-line: 34vh;
	--orb: max(70vh, 92vw);
	--rim-clear: min(calc(var(--orb) * 0.185), 14vh);

	/* The masthead's own height, so the stage can be placed against the rim rather than against
	   whatever is above it. */
	--mast-pad: clamp(1.5rem, 3.5vh, 2.75rem);
	--mark-w: clamp(6.5rem, 9vw, 10rem);
	--mast-h: calc(var(--mast-pad) * 2 + var(--mark-w) * 88.5 / 504);

	/* Both glows are sized off their own width so they stay wider than they are tall. Sized in
	   percentages of the box instead, a tall phone turns the halo into a searchlight. */
	--bloom-core: min(42vw, 460px);
	--bloom-sky: min(118vw, 1500px);

	--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

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

html {
	/* iOS enlarges text in landscape unless told not to. */
	-webkit-text-size-adjust: 100%;
	scrollbar-color: rgb(255 255 255 / 22%) transparent;
}

body {
	margin: 0;
	min-height: 100svh;
	display: grid;
	grid-template-rows: auto 1fr auto;
	background: var(--field-high);
	color: var(--ink);
	font-family:
		'Jost',
		system-ui,
		-apple-system,
		'Segoe UI',
		Roboto,
		sans-serif;
	font-weight: 400;
	line-height: 1.6;
}

::selection {
	background: var(--gold);
	color: #08121e;
}

/* ---- The field ------------------------------------------------------------------------------ */

.field {
	position: fixed;
	inset: 0;
	z-index: 0;
	overflow: hidden;
	background: linear-gradient(
		180deg,
		var(--field-high) 0%,
		var(--field-mid) 46%,
		var(--field-low) 100%
	);
}

/* Stars, thinning to nothing before they reach the bloom, the way they do at that hour. */
.stars {
	position: absolute;
	inset: 0;
	background-repeat: no-repeat;
	background-image:
		radial-gradient(1.7px 1.7px at 11% 14%, rgb(255 255 255 / 58%), transparent),
		radial-gradient(1.2px 1.2px at 19% 31%, rgb(255 255 255 / 34%), transparent),
		radial-gradient(1.4px 1.4px at 27% 8%, rgb(255 255 255 / 46%), transparent),
		radial-gradient(1px 1px at 34% 22%, rgb(255 255 255 / 28%), transparent),
		radial-gradient(1.6px 1.6px at 43% 12%, rgb(255 255 255 / 40%), transparent),
		radial-gradient(1.1px 1.1px at 52% 5%, rgb(255 255 255 / 30%), transparent),
		radial-gradient(1.3px 1.3px at 61% 18%, rgb(255 255 255 / 36%), transparent),
		radial-gradient(1.8px 1.8px at 70% 9%, rgb(255 255 255 / 52%), transparent),
		radial-gradient(1px 1px at 77% 26%, rgb(255 255 255 / 26%), transparent),
		radial-gradient(1.5px 1.5px at 85% 15%, rgb(255 255 255 / 44%), transparent),
		radial-gradient(1.2px 1.2px at 92% 30%, rgb(255 255 255 / 32%), transparent),
		radial-gradient(1.1px 1.1px at 6% 27%, rgb(255 255 255 / 30%), transparent),
		radial-gradient(1.3px 1.3px at 33% 3%, rgb(255 255 255 / 34%), transparent),
		radial-gradient(1px 1px at 65% 33%, rgb(255 255 255 / 24%), transparent),
		radial-gradient(1.4px 1.4px at 95% 6%, rgb(255 255 255 / 38%), transparent),
		radial-gradient(1px 1px at 38% 35%, rgb(255 255 255 / 22%), transparent);
	-webkit-mask-image: linear-gradient(180deg, #000 0%, #000 40%, transparent 66%);
	mask-image: linear-gradient(180deg, #000 0%, #000 40%, transparent 66%);
	animation: star-in 2.6s 0.15s var(--ease-out) backwards;
}

/* The bloom is a halo centred on the rim, not a glow sitting above it: a gradient whose centre lands
   on the edge of its own box gets cut across at its brightest, which draws a hard line clean across
   the frame wherever the body is narrower than the glow. Centred, it falls off in every direction
   and the body — painted after this, so over it — takes away the half that is behind the limb. */
.bloom {
	position: absolute;
	left: 50%;
	top: var(--rim-line);
	width: calc(var(--bloom-sky) * 2);
	height: calc(var(--bloom-sky) * 0.68);
	transform: translate(-50%, -50%);
	background:
		radial-gradient(
			ellipse var(--bloom-core) calc(var(--bloom-core) * 0.58) at 50% 50%,
			rgb(255 232 187 / 40%) 0%,
			rgb(240 199 126 / 15%) 42%,
			rgb(226 177 92 / 0%) 78%
		),
		radial-gradient(
			ellipse var(--bloom-sky) calc(var(--bloom-sky) * 0.34) at 50% 50%,
			rgb(226 177 92 / 15%) 0%,
			rgb(226 177 92 / 5%) 40%,
			rgb(226 177 92 / 0%) 74%
		);
	animation:
		bloom-in 2.4s 0.1s var(--ease-out) backwards,
		breathe 24s 2.5s ease-in-out infinite;
}

/* The body itself. Its fill is the field one shade lifted, so the limb reads against the sky even
   where no light reaches it. */
.orb {
	position: absolute;
	left: 50%;
	top: var(--rim-line);
	width: var(--orb);
	height: var(--orb);
	transform: translateX(-50%);
	border-radius: 50%;
	background: radial-gradient(
		100% 100% at 50% 0%,
		#12293f 0%,
		#0b1a2a 26%,
		#060e18 62%,
		#040a11 100%
	);
	animation: orb-in 2.4s var(--ease-out) backwards;
}

/* The rim is four stacked inset shadows on an overlay of its own, masked to the quarter the light
   actually reaches. An unmasked inset shadow runs the whole circumference at full strength, which
   turns a lit body into a glowing ring and drags the light down behind the display line. */
.orb::before {
	content: '';
	position: absolute;
	inset: 0;
	border-radius: 50%;
	box-shadow:
		inset 0 1px 0 rgb(255 243 220 / 95%),
		inset 0 6px 14px -4px rgb(240 199 126 / 92%),
		inset 0 26px 50px -26px rgb(226 177 92 / 45%),
		inset 0 110px 150px -100px rgb(226 177 92 / 14%);
	-webkit-mask-image: radial-gradient(
		ellipse 46% 26% at 50% 0%,
		#000 0%,
		#000 34%,
		rgb(0 0 0 / 0%) 84%
	);
	mask-image: radial-gradient(ellipse 46% 26% at 50% 0%, #000 0%, #000 34%, rgb(0 0 0 / 0%) 84%);
}

/* ---- The page -------------------------------------------------------------------------------- */

.mast,
.stage,
.foot {
	position: relative;
	z-index: 1;
	padding-inline: var(--gutter);
}

.mast {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding-block: var(--mast-pad);
	animation: lift 1.4s 0.5s var(--ease-out) backwards;
}

/* The other language. Quiet enough to leave the display line the only thing that speaks first, and
   still the second thing the eye finds, because for half the people this page is for it is the
   first thing they need. */
.lang {
	display: inline-flex;
	align-items: center;
	flex: none;
	padding: 0.5rem 0.25rem;
	/* Not the foot's weight: on a phone held sideways the masthead sits over the rim, where faint
	   white measured 3.82:1 against the light. This is the one control half the people this page is
	   for need first, so it is quiet, not faint. */
	color: var(--ink-quiet);
	font-size: 0.75rem;
	font-weight: 400;
	line-height: 1;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	text-indent: 0.14em;
	text-decoration: none;
	-webkit-tap-highlight-color: transparent;
	transition: color 0.4s var(--ease-out);
}

.lang:hover,
.lang:focus-visible {
	color: var(--ink);
}

.lang:focus-visible {
	outline: 2px solid var(--rim);
	outline-offset: 4px;
	border-radius: 4px;
}

/* The logo's own proportions, so the space it takes is known before it loads. height:auto is what
   lets the ratio govern: with a height left standing the box keeps it and the drawing letterboxes
   itself inside, which looks right and measures wrong. */
.mark {
	display: block;
	width: var(--mark-w);
	height: auto;
}

.stage {
	display: grid;
	justify-items: center;
	align-content: start;
	text-align: center;
	/* Placed against the rim rather than centred in what is left: the type starts one clear below
	   the light, so no glyph ever sits on the lit arc at any width. */
	padding-top: max(0px, calc(var(--rim-line) + var(--rim-clear) - var(--mast-h)));
	padding-bottom: clamp(0.75rem, 2.5vh, 1.75rem);
}

.display {
	margin: 0;
	/* Bounded by height as well as width: on a phone held sideways the width-only clamp gives a
	   display line taller than the room left for everything under it. */
	font-size: min(clamp(1.6rem, 0.6rem + 4.6vw, 5rem), 9vh);
	font-weight: 300;
	line-height: 1.1;
	letter-spacing: 0.42em;
	text-transform: uppercase;
	/* Letter-spacing trails the last letter too, so a centred line hangs half a space left of
	   centre; pulling the box in by one space puts the glyphs back on the axis. */
	margin-right: -0.42em;
	animation: lift 1.5s 0.75s var(--ease-out) backwards;
}

.lede {
	margin: clamp(1.5rem, 3.5vh, 2.25rem) 0 0;
	max-width: 42ch;
	font-size: clamp(0.95rem, 0.88rem + 0.35vw, 1.125rem);
	font-weight: 300;
	line-height: 1.65;
	letter-spacing: 0.01em;
	color: var(--ink-quiet);
	text-wrap: balance;
	animation: lift 1.5s 0.95s var(--ease-out) backwards;
}

.write {
	margin-top: clamp(2rem, 5vh, 3.25rem);
	display: inline-flex;
	align-items: center;
	padding: 0.95em 2.1em;
	border: 1px solid rgb(226 177 92 / 38%);
	border-radius: 999px;
	color: var(--gold);
	font-size: clamp(0.7rem, 0.66rem + 0.2vw, 0.8rem);
	font-weight: 500;
	line-height: 1;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	text-decoration: none;
	text-indent: 0.2em;
	background: rgb(226 177 92 / 0%);
	-webkit-tap-highlight-color: transparent;
	transition:
		border-color 0.45s var(--ease-out),
		background-color 0.45s var(--ease-out),
		box-shadow 0.45s var(--ease-out),
		color 0.45s var(--ease-out);
	animation: lift 1.5s 1.15s var(--ease-out) backwards;
}

.write:hover,
.write:focus-visible {
	border-color: rgb(226 177 92 / 78%);
	background: rgb(226 177 92 / 9%);
	color: var(--rim-hot);
	box-shadow: 0 8px 30px -10px rgb(226 177 92 / 38%);
}

.write:focus-visible {
	outline: 2px solid var(--rim);
	outline-offset: 4px;
}

.write:active {
	background: rgb(226 177 92 / 14%);
}

.foot {
	padding-block: clamp(1rem, 2vh, 1.75rem);
	text-align: center;
	animation: lift 1.4s 1.35s var(--ease-out) backwards;
}

.foot p {
	margin: 0;
	font-size: 0.75rem;
	font-weight: 400;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ink-faint);
}

/* The waitlist. One row on a wide frame, stacked on a narrow one, both halves sharing the pill's
   shape so the form reads as one object rather than a field with a button next to it. */
.join {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.6rem;
	width: 100%;
	max-width: 30rem;
	margin-top: clamp(1.5rem, 3.5vh, 2.5rem);
	animation: lift 1.5s 1.15s var(--ease-out) backwards;
}

.join-field {
	flex: 1 1 15rem;
	min-width: 0;
	padding: 0.95em 1.4em;
	border: 1px solid rgb(255 255 255 / 16%);
	border-radius: 999px;
	background: rgb(255 255 255 / 4%);
	color: var(--ink);
	font-family: inherit;
	font-size: clamp(0.85rem, 0.8rem + 0.2vw, 0.95rem);
	line-height: 1;
	/* The caret is the one place the gold appears inside a control, and it is the thing the eye is
	   looking for the moment the field is focused. */
	caret-color: var(--gold);
	transition:
		border-color 0.45s var(--ease-out),
		background-color 0.45s var(--ease-out);
}

.join-field::placeholder {
	/* Measured on the ground it sits on rather than dimmed by eye: a placeholder is text. */
	color: rgb(255 255 255 / 62%);
}

.join-field:hover {
	border-color: rgb(255 255 255 / 28%);
}

.join-field:focus {
	border-color: rgb(226 177 92 / 70%);
	background: rgb(255 255 255 / 7%);
	outline: 2px solid var(--rim);
	outline-offset: 2px;
}

/* The browser marks a bad address only once it has been asked to submit one, so nothing goes red
   while the address is still being typed. */
.join-field:user-invalid {
	border-color: rgb(226 177 92 / 70%);
}

.join-go {
	flex: 0 0 auto;
	padding: 0.95em 1.9em;
	border: 1px solid rgb(226 177 92 / 38%);
	border-radius: 999px;
	background: rgb(226 177 92 / 0%);
	color: var(--gold);
	font-family: inherit;
	font-size: clamp(0.7rem, 0.66rem + 0.2vw, 0.8rem);
	font-weight: 500;
	line-height: 1;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	text-indent: 0.2em;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
	transition:
		border-color 0.45s var(--ease-out),
		background-color 0.45s var(--ease-out),
		box-shadow 0.45s var(--ease-out),
		color 0.45s var(--ease-out);
}

.join-go:hover,
.join-go:focus-visible {
	border-color: rgb(226 177 92 / 78%);
	background: rgb(226 177 92 / 9%);
	color: var(--rim-hot);
	box-shadow: 0 8px 30px -10px rgb(226 177 92 / 38%);
}

.join-go:focus-visible {
	outline: 2px solid var(--rim);
	outline-offset: 4px;
}

.join-go:active {
	background: rgb(226 177 92 / 14%);
}

/* What the address is used for, said where it is asked for. */
.note {
	margin: clamp(0.7rem, 1.5vh, 1rem) 0 0;
	max-width: 34rem;
	font-size: 0.8rem;
	font-weight: 300;
	line-height: 1.55;
	color: var(--ink-faint);
	text-wrap: balance;
}

.aside {
	margin: clamp(0.55rem, 1.1vh, 0.85rem) 0 0;
	font-size: 0.8rem;
	font-weight: 300;
	color: var(--ink-faint);
}

.write-link {
	color: var(--ink-quiet);
	text-decoration-color: rgb(226 177 92 / 45%);
	text-underline-offset: 0.25em;
	transition: color 0.4s var(--ease-out);
}

.write-link:hover,
.write-link:focus-visible {
	color: var(--gold);
}

.write-link:focus-visible {
	outline: 2px solid var(--rim);
	outline-offset: 3px;
	border-radius: 3px;
}

/* The label a screen reader reads and a sighted reader does not need, the placeholder having said
   it already. Not display:none, which would take it out of the accessibility tree with it. */
.offscreen {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* The trap. Hidden from sight and from the tab order and from a screen reader, so the only thing
   that ever fills it is something filling every field it can find. */
.trap {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* ---- Daybreak -------------------------------------------------------------------------------- */

@keyframes orb-in {
	from {
		transform: translateX(-50%) translateY(4.5rem);
	}
}

@keyframes bloom-in {
	from {
		opacity: 0;
		transform: translate(-50%, -50%) translateY(3rem);
	}
}

@keyframes star-in {
	from {
		opacity: 0;
	}
}

/* The page's type settles from above while the body rises from below, and the two meet. It is
   written that way round for a reason: a transform counts toward the scrollable overflow, so
   entering from *below* hangs the foot of the page past the bottom of the frame and the browser
   shows a scrollbar for as long as the animation runs — about two and a half seconds of a bar
   sliding away on a page that does not scroll. Content above the top edge is clipped and never
   scrollable, so entering from above costs nothing. The offset stays under the masthead's own
   padding so the mark never clips at the top on the shortest frames. */
@keyframes lift {
	from {
		opacity: 0;
		transform: translateY(-0.7rem);
	}
}

/* The one thing that keeps moving, at the pace of actual weather. */
@keyframes breathe {
	50% {
		opacity: 0.84;
	}
}

/* ---- Small and short frames ------------------------------------------------------------------ */

/* The small phones still in use: at 320 the sentence takes a third line and the full rhythm runs
   51px past the foot of the frame. The scene keeps its proportions; the spacing gives way. */
@media (max-height: 640px) and (orientation: portrait) {
	:root {
		--rim-clear: calc(var(--orb) * 0.115);
		--mast-pad: 1.1rem;
	}

	.lede {
		margin-top: 1rem;
	}

	.write {
		margin-top: 1.5rem;
	}

	.foot {
		padding-block: 0.75rem;
	}
}

/* A phone held sideways has no room for a horizon a third of the way down. */
@media (max-height: 560px) and (orientation: landscape) {
	:root {
		--rim-line: 17vh;
		--rim-clear: calc(var(--orb) * 0.1);
		--mast-pad: 0.85rem;
	}

	.display {
		letter-spacing: 0.32em;
		margin-right: -0.32em;
	}

	.lede {
		margin-top: 0.7rem;
	}

	.write {
		margin-top: 1rem;
	}

	.foot {
		padding-block: 0.25rem;
	}

	.stage {
		padding-bottom: 0.5rem;
	}
}

/* A browser does not print backgrounds unless it is asked to, so without this the page prints as
   white type on white paper — a blank sheet. Printed, it is the words on paper: the field goes, the
   ink turns over, and brightness(0) makes the white drawing a black one without a second file.
   Paper cannot be clicked, so the address is spelled out beside the label. */
@media print {
	.field {
		display: none;
	}

	body {
		background: #fff;
		color: #000;
	}

	.mark {
		filter: brightness(0);
	}

	.lang {
		display: none;
	}

	.display {
		color: #000;
	}

	.lede,
	.foot p {
		color: #333;
	}

	.write,
	.join-go,
	.join-field {
		color: #000;
		border-color: #000;
		box-shadow: none;
	}

	/* Paper takes no addresses. The note stays, because what it says is still true. */
	.join {
		display: none;
	}

	.note,
	.aside {
		color: #333;
	}

	.write::after {
		content: ' · ' attr(data-address);
		text-transform: none;
	}

	@page {
		margin: 18mm;
	}
}

/* The page's one control, under a thumb. WCAG 2.2 AA asks for 24px and the pill already clears it
   at 35, but a finger is not a pointer and this is the only thing on the page to press. Scoped to a
   coarse pointer so a mouse keeps the finer pill the drawing asks for. */
@media (pointer: coarse) {
	.write,
	.lang,
	.join-field,
	.join-go {
		min-height: 44px;
	}
}

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation: none !important;
		transition-duration: 0.01ms !important;
	}
}
