/* ==========================================================================
   Solinity public landing page. Deep Light, marketing surface.

   REDESIGNED 11-08-2026, then cut down the same day. The page is one screen:
   a background, a glass call frame, two lines and a link. The screenshot shelf
   that briefly lived below it is gone, and with it the only reason to scroll.

   Tokens mirror solinity-desktop/src/app.css name-for-name, so the app and this
   page read as one brand.

   HARD RULE: the reserved signal.capture-true (#f5443b) is the sacred open-mic
   signal and must NEVER appear on a marketing surface. It is deliberately not
   defined here, so it cannot be referenced even by accident. The rose the live
   dock uses is --state-error's family, which is a different meaning and a
   different value.

   Self-contained on purpose: no external fonts, scripts or network of any
   kind. media/ is local, and the call frame is a simulation that transmits and
   stores nothing.
   ========================================================================== */
:root {
	color-scheme: dark;

	/* 1.1 Neutral base ladder (the room) */
	--base-ink: #090a0f;
	--base-abyss: #0c0e15;
	--base-dusk: #12141c;
	--base-graphite: #191c26;
	--base-slate: #232735;
	--base-fog: #313646;

	/* 1.2 Temperature ramp (the one expressive colour, applied only as light) */
	--temp-far: #7c99be;
	--temp-away: #9fb6cf;
	--temp-silver: #c4d2e2;
	--temp-rest: #3a4358;
	--temp-attend: #c9885a;
	--temp-present: #e6a15c;
	--temp-emphasis: #f2c089;

	/* 1.3 Surfaced type and apparatus ink */
	--ink-paper: #f0eadf;
	--ink-paper-soft: #d6cfc1;
	--ink-mist: #b9c2ce;
	--ink-mist-mute: #8a94a2;
	--ink-mist-faint: #5c6472;

	/* 1.4 Functional colour (meaning only) */
	--state-error: #c77b6a;

	/* 4.1 Spacing scale (4px base) */
	--space-1: 4px;
	--space-2: 8px;
	--space-3: 12px;
	--space-4: 16px;
	--space-5: 24px;
	--space-6: 32px;
	--space-7: 48px;
	--space-8: 64px;
	--space-9: 96px;
	--space-10: 128px;

	/* 4.2 Radius */
	--radius-xs: 4px;
	--radius-sm: 8px;
	--radius-md: 12px;
	--radius-lg: 20px;
	--radius-xl: 28px;
	--radius-pill: 999px;

	/* 3.1 Font stacks */
	--font-surface: 'Newsreader', 'Iowan Old Style', 'Palatino Linotype', Palatino, 'Book Antiqua', Georgia, serif;
	--font-apparatus: 'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, 'Helvetica Neue', Arial, sans-serif;

	/* 3.2 Type scale (ratio 1.25, base 16px) */
	--type-caption: 0.8125rem;
	--type-ui: 1rem;
	--type-body: 1.25rem;
	--type-lead: 1.5625rem;
	--type-title: 1.9375rem;
	--type-display: 2.4375rem;
	--type-hero: 3.0625rem;

	/* 3.3 Weight, leading, tracking */
	--weight-regular: 400;
	--weight-medium: 500;
	--weight-semibold: 600;
	--leading-body: 1.55;
	--leading-lead: 1.4;
	--leading-title: 1.12;
	--leading-apparatus: 1.35;
	--tracking-display: -0.01em;
	--tracking-label: 0.16em;

	/* 5.1 House easing (postural settle) and durations */
	--ease-settle: cubic-bezier(0.16, 0.84, 0.3, 1);
	--ease-breathe: cubic-bezier(0.37, 0, 0.63, 1);
	--dur-settle: 750ms;
	--dur-reveal: 950ms;
	--dur-breathe: 6500ms;

	/* 10.1 The glass frame. Thin on purpose (12-08-2026): the water is the
	   picture and the pane is a window onto it, not a card sitting over it.
	   These three numbers are the whole material - a faint cool tint, an edge
	   to make it an object, and just enough blur to keep type legible over
	   moving water. Nothing here tints warm, and nothing here saturates: a
	   colour difference between inside and outside the pane is the exact
	   defect the product lead spotted.

	   MEASURED, NOT GUESSED. Almost all of the pane's lightness is the fill,
	   not the blur: at blur 16px the pane read +11.6 luma over the water, and
	   removing the fill alone took that to +1.4. So transparency is bought here
	   in the tint and the gradient below, and the blur is free to be whatever
	   keeps type legible over moving water. */
	--glass-tint: rgba(226, 232, 240, 0.014);
	--glass-edge: rgba(255, 255, 255, 0.15);
	--glass-blur: 20px;
}

* { box-sizing: border-box; }

html, body {
	margin: 0;
	padding: 0;
	max-width: 100%;
	overflow-x: hidden;
}

body {
	background: var(--base-ink);
	color: var(--ink-mist);
	font-family: var(--font-apparatus);
	font-size: var(--type-ui);
	line-height: var(--leading-apparatus);
	font-weight: var(--weight-regular);
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

a { color: var(--temp-away); text-decoration: none; }
a:hover { color: var(--temp-silver); }

:focus-visible {
	outline: 2px solid var(--temp-present);
	outline-offset: 3px;
	border-radius: var(--radius-sm);
}

::selection {
	background: rgba(230, 161, 92, 0.28);
	color: var(--ink-paper);
}

img { max-width: 100%; height: auto; display: block; }

.slot-link {
	color: var(--ink-mist-mute);
	border-bottom: 1px dashed currentColor;
}

/* ==========================================================================
   1. THE SKY
   Fixed behind everything, the height of the viewport. The .sky's own
   background is the poster, which is frame 0 of the clip, so there is
   something in place before a video exists and something left when one is
   refused. Two videos sit on top of it and hand the picture between
   themselves; see index.html for why there are two.
   ========================================================================== */
.sky {
	position: fixed;
	inset: 0;
	z-index: -1;
	background: var(--base-ink) url('media/water-poster.jpg') center / cover no-repeat;
}

/* The wrapper carries the one-off arrival fade only. Keep it off the videos:
   the loop crossfade writes their opacity inline every frame, and an inline
   value beats any class this would set. */
.sky-motion {
	position: absolute;
	inset: 0;
	opacity: 0;
	transition: opacity 1400ms var(--ease-settle);
}

.sky-motion.is-on { opacity: 1; }

/* Two of them, stacked. Script owns z-index and the top one's opacity; the
   one underneath stays opaque and is simply covered. */
.sky-motion video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	will-change: opacity;
}

/* Three jobs at once: lift the middle so the frame has something to sit on,
   drop the corners so nothing competes with it, and warm the whole thing very
   slightly towards the brand's one expressive colour. */
.sky-scrim {
	position: absolute;
	inset: 0;
	background:
		radial-gradient(120% 90% at 50% 45%, rgba(9, 10, 15, 0) 0%, rgba(9, 10, 15, 0.42) 58%, rgba(9, 10, 15, 0.82) 100%),
		linear-gradient(180deg, rgba(9, 10, 15, 0.55) 0%, rgba(9, 10, 15, 0.18) 30%, rgba(9, 10, 15, 0.35) 72%, rgba(9, 10, 15, 0.9) 100%),
		linear-gradient(180deg, rgba(201, 136, 90, 0.07) 0%, rgba(124, 153, 190, 0.05) 100%);
}

/* ==========================================================================
   2. THE TOP BAR
   The mark, and nothing else. The artwork is black on transparent, so it is
   inverted here rather than shipped twice.
   ========================================================================== */
.topbar {
	position: absolute;
	top: var(--space-6);
	left: var(--space-7);
	z-index: 3;
}

.mark {
	display: block;
	line-height: 0;
	opacity: 0.72;
	transition: opacity var(--dur-settle) var(--ease-settle);
}

.mark:hover { opacity: 1; }

.mark img {
	width: 34px;
	height: 34px;
	filter: invert(1);
}

/* ==========================================================================
   3. THE ONE SCREEN
   ========================================================================== */
.hero {
	position: relative;
	min-height: 100svh;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: var(--space-9) var(--space-5) var(--space-10);
	text-align: center;
}

/* ---------- 3.1 The glass ------------------------------------------------ */
.glass {
	position: relative;
	width: min(370px, 88vw);
	height: min(540px, 62svh);
	min-height: 420px;
	display: flex;
	flex-direction: column;
	padding: var(--space-5) var(--space-5) var(--space-6);
	border-radius: var(--radius-xl);
	border: 1px solid var(--glass-edge);
	/* Neutral greys only, and no saturate(): the pane must not be a different
	   colour from the water it is over. It was blur(26px) saturate(1.35) with
	   about 14% white in it until 12-08-2026, which read as a milky card. */
	background:
		linear-gradient(165deg, rgba(255, 255, 255, 0.032) 0%, rgba(255, 255, 255, 0.006) 45%, rgba(255, 255, 255, 0.018) 100%),
		var(--glass-tint);
	backdrop-filter: blur(var(--glass-blur));
	-webkit-backdrop-filter: blur(var(--glass-blur));
	/* The shadow does the work the fill used to do. A pane this thin needs the
	   drop shadow and the edge to be read as an object at all. */
	box-shadow:
		0 48px 110px rgba(0, 0, 0, 0.6),
		0 8px 28px rgba(0, 0, 0, 0.38),
		inset 0 1px 0 rgba(255, 255, 255, 0.15),
		inset 0 -1px 0 rgba(255, 255, 255, 0.035);
	overflow: hidden;
	animation: rise var(--dur-reveal) var(--ease-settle) both;
}

/* Browsers without backdrop-filter get a solid pane rather than a hole in the
   page. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
	.glass { background: rgba(12, 14, 21, 0.86); }
}

@keyframes rise {
	from { opacity: 0; transform: translateY(18px) scale(0.985); }
	to { opacity: 1; transform: none; }
}

/* The one moving highlight on the pane: a slow diagonal sheen, so the glass
   reads as glass rather than as a hole.

   NEUTRAL, NOT AMBER (12-08-2026). It used to lead with
   rgba(242, 192, 137, 0.10), and measuring it found a warm band crossing the
   pane centre every 14 seconds - a 7.2 swing on the warm-cold axis, which is
   the moving colour change the product lead saw. A specular highlight is the
   colour of the light it reflects, and the light here is a cold overcast sky,
   so white is also the more correct answer. Quieter too, because a thinner
   pane should carry a thinner highlight. */
.glass-sheen {
	position: absolute;
	inset: -40% -60%;
	pointer-events: none;
	background: linear-gradient(115deg, transparent 40%, rgba(255, 255, 255, 0.045) 50%, rgba(255, 255, 255, 0.055) 53%, transparent 62%);
	animation: sheen 14s var(--ease-breathe) infinite;
}

@keyframes sheen {
	0%, 100% { transform: translate3d(-16%, -6%, 0); opacity: 0.5; }
	50% { transform: translate3d(16%, 6%, 0); opacity: 0.95; }
}

/* ---------- 3.2 The frame's head ---------------------------------------- */
.frame-head {
	position: relative;
	z-index: 1;
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: var(--space-3);
}

.frame-slot { display: block; }
.frame-slot-end { display: flex; justify-content: flex-end; }

/* Both of these gained a shadow with the pane's fill (12-08-2026): what sits
   behind them is water now, not frosted glass, and a wave crest is bright. */
.wordmark {
	/* The system stack, matching solinity-desktop's own --font token
	   (shared/design/tokens.css) rather than --font-surface: the wordmark is
	   the one word that has to read as the same brand mark on both shells,
	   and Newsreader made it read as a different product's logotype. */
	font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, 'Segoe UI', sans-serif;
	font-size: var(--type-body);
	font-weight: var(--weight-medium);
	letter-spacing: 0.01em;
	color: var(--ink-paper);
	text-shadow: 0 1px 3px rgba(9, 10, 15, 0.9), 0 0 16px rgba(9, 10, 15, 0.7);
}

/* 36px and one flat fill, no border - the account/settings affordance,
   sized and coloured to match solinity-desktop's own header button
   (.icon-button.sm there; shared/design/tokens.css's --action-sm is the same
   36px, and --button-fill is this same 0.14 white, the one round-button fill
   that system uses everywhere and never edges with a border). */
.chip {
	display: grid;
	place-items: center;
	width: 36px;
	height: 36px;
	border-radius: var(--radius-pill);
	background: rgba(255, 255, 255, 0.14);
	color: var(--ink-paper-soft);
}

/* icons.js's glyphs are drawn at a fixed 24px regardless of the button
   they sit in - solinity-desktop never scales a glyph to its circle - so
   this stays 24px whatever the button's own diameter is. */
.chip svg { width: 24px; height: 24px; }

/* ---------- 3.3 The void ------------------------------------------------- */
.frame-void {
	position: relative;
	z-index: 1;
	flex: 1;
	display: grid;
	place-items: center;
}

/* THERE IS NOTHING IN THE MIDDLE OF THE PANE, ON ANY STATE. A warm radial
   glow lived here until 12-08-2026: dim at rest, brighter on connecting,
   breathing through a call. The product lead cut it in every state - the pane
   is glass, the water behind it is the picture, and this space is held for
   whatever the product later shows in it.

   So a call is now told apart entirely by the dock: the action button changes
   shape and colour, the timer appears, the mute control appears. That was
   already true of the words; it is now true of the light as well. */

/* BRIGHTER THAN IT WAS, AND CARRYING ITS OWN SHADOW (12-08-2026). This used to
   be --ink-mist-mute, which was safe against a pane with 14% white in it. The
   pane now has under 2%, so the ground behind these words is moving water, and
   a mid grey fell to about 2.2:1 over a bright crest. Paper ink plus a dark
   glow holds regardless of what the water is doing underneath. */
.frame-status {
	position: relative;
	margin: 0;
	font-size: var(--type-caption);
	letter-spacing: var(--tracking-label);
	text-transform: uppercase;
	color: var(--ink-paper-soft);
	text-shadow: 0 1px 3px rgba(9, 10, 15, 0.95), 0 0 14px rgba(9, 10, 15, 0.8);
	min-height: 1.2em;
}

/* ---------- 3.4 The dock ------------------------------------------------- */
.frame-dock {
	position: relative;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
}

.dock-action,
.dock-mute {
	appearance: none;
	border: 0;
	cursor: pointer;
	display: grid;
	place-items: center;
	border-radius: var(--radius-pill);
	transition:
		width var(--dur-settle) var(--ease-settle),
		height var(--dur-settle) var(--ease-settle),
		background var(--dur-settle) var(--ease-settle),
		border-color var(--dur-settle) var(--ease-settle),
		color var(--dur-settle) var(--ease-settle),
		opacity 380ms var(--ease-settle),
		margin var(--dur-settle) var(--ease-settle),
		transform var(--dur-settle) var(--ease-settle);
}

/* 60px, one flat fill, no coloured gradient - sized and coloured to match
   solinity-desktop's own idle call button (.icon-button.lg.filled there;
   shared/design/tokens.css's --action-lg is the same 60px, and --button-fill
   is this same 0.14 white). It used to be 72px with an amber gradient, which
   read as this shell's own invention rather than the same control. */
.dock-action {
	position: relative;
	width: 60px;
	height: 60px;
	border: 1px solid transparent;
	background: rgba(255, 255, 255, 0.14);
	color: var(--ink-paper);
	box-shadow: none;
}

.dock-action:hover { transform: translateY(-1px); }
.dock-action:active { background: rgba(255, 255, 255, 0.22); transform: translateY(0) scale(0.97); }

/* icons.js's glyphs are drawn at a fixed 24px regardless of the button
   they sit in, so this stays 24px at every dock-action size. */
.dock-action svg { width: 24px; height: 24px; }
.glyph-end, .glyph-muted { display: none; }

/* Connecting: one ring leaving the button, at the pace of a breath. Neutral
   now, not amber: the ring is motion, not a second brand colour. */
.dock-action::after {
	content: '';
	position: absolute;
	inset: -1px;
	border-radius: inherit;
	border: 1px solid rgba(240, 234, 223, 0.5);
	opacity: 0;
	pointer-events: none;
}

[data-state="connecting"] .dock-action::after { animation: ring 1800ms var(--ease-settle) infinite; }

@keyframes ring {
	0% { opacity: 0.7; transform: scale(1); }
	100% { opacity: 0; transform: scale(1.5); }
}

/* Live and muted: the same button, a different function, and a shape that
   says so before the label does. 48px and this same rose tint are
   solinity-desktop's .icon-button.md.danger (tokens.css's --action-md and
   --danger/--danger-rgb at 0.14/0.34) - the one variant in that system that
   keeps its own tint and hairline rather than the neutral fill every other
   button shares, because the hang-up glyph needs the extra contrast. It used
   to stay 60px here, which is why "the close call button is too big" - the
   shrink on answer is the tell that its function changed, same as desktop. */
[data-state="live"] .dock-action,
[data-state="muted"] .dock-action {
	width: 48px;
	height: 48px;
	background: rgba(230, 178, 165, 0.14);
	border-color: rgba(230, 178, 165, 0.34);
	color: #e6b2a5;
	box-shadow: none;
}

[data-state="live"] .dock-action:active,
[data-state="muted"] .dock-action:active {
	background: rgba(230, 178, 165, 0.22);
}

[data-state="live"] .glyph-start,
[data-state="muted"] .glyph-start { display: none; }

[data-state="live"] .glyph-end,
[data-state="muted"] .glyph-end { display: block; }

/* The timer and the mute control do not exist outside a call, so they take up
   no room until there is one. */
.dock-timer {
	width: 0;
	overflow: hidden;
	opacity: 0;
	font-size: var(--type-lead);
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.01em;
	color: var(--ink-paper);
	text-shadow: 0 1px 3px rgba(9, 10, 15, 0.9), 0 0 16px rgba(9, 10, 15, 0.7);
	transition:
		width var(--dur-settle) var(--ease-settle),
		margin var(--dur-settle) var(--ease-settle),
		opacity 380ms var(--ease-settle);
}

/* 48px at rest and the same 0.14 white fill the other buttons carry
   (solinity-desktop's .icon-button.md.quiet) - it used to be 52px at 0.1. */
.dock-mute {
	width: 0;
	height: 48px;
	flex: 0 0 auto;
	overflow: hidden;
	opacity: 0;
	pointer-events: none;
	border: 1px solid transparent;
	background: rgba(255, 255, 255, 0.14);
	color: var(--ink-paper);
	transform: scale(0.7);
}

.dock-mute svg { width: 24px; height: 24px; }

[data-state="live"] .dock-timer,
[data-state="muted"] .dock-timer {
	width: 78px;
	margin: 0 var(--space-3);
	opacity: 1;
}

[data-state="live"] .dock-mute,
[data-state="muted"] .dock-mute {
	width: 48px;
	opacity: 1;
	pointer-events: auto;
	transform: none;
}

[data-state="muted"] .dock-mute {
	background: rgba(240, 234, 223, 0.9);
	color: var(--base-ink);
}

[data-state="muted"] .glyph-mic { display: none; }
[data-state="live"] .glyph-muted { display: none; }
.glyph-muted { display: block; }

/* ---------- 3.5 The two lines that survived ------------------------------ */
.whisper {
	margin: var(--space-7) 0 0;
	font-family: var(--font-surface);
	font-size: var(--type-lead);
	line-height: var(--leading-lead);
	letter-spacing: var(--tracking-display);
	color: var(--ink-paper);
	text-shadow: 0 1px 24px rgba(9, 10, 15, 0.75);
	animation: rise var(--dur-reveal) var(--ease-settle) 200ms both;
}

.fineprint {
	margin: var(--space-3) 0 0;
	max-width: 34ch;
	font-size: var(--type-caption);
	/* mist-mute, not mist-faint: this is the page's one honest disclosure and
	   it clears 4.5:1 on the scrim. faint does not, at this size. */
	color: var(--ink-mist-mute);
	animation: rise var(--dur-reveal) var(--ease-settle) 320ms both;
}

/* ==========================================================================
   4. THE FOOTLINE
   What is left of a footer, pinned to the bottom of the one screen. absolute,
   not fixed: on a viewport too short for the frame the page may still scroll,
   and this should go with it rather than sit over it.
   ========================================================================== */
.footline {
	position: absolute;
	bottom: var(--space-6);
	left: 0;
	right: 0;
	margin: 0;
	padding: 0 var(--space-5);
	text-align: center;
	font-size: var(--type-caption);
	letter-spacing: var(--tracking-label);
	text-transform: uppercase;
	color: var(--ink-mist-mute);
	animation: rise var(--dur-reveal) var(--ease-settle) 440ms both;
}

/* ==========================================================================
   5. NARROWER, AND QUIETER
   ========================================================================== */
@media (max-width: 900px) {
	.topbar { top: var(--space-5); left: var(--space-5); }
}

@media (max-width: 560px) {
	:root { --type-lead: 1.375rem; --type-body: 1.125rem; }
	.hero { padding: var(--space-9) var(--space-4) var(--space-10); }
	.glass { height: min(500px, 58svh); padding: var(--space-4) var(--space-4) var(--space-5); }
	.whisper { margin-top: var(--space-6); }
	/* The placeholder is the longest this line will ever be, and at 390px it
	   reaches the edges. Tighter tracking rather than a smaller face. */
	.footline { bottom: var(--space-5); font-size: 0.75rem; letter-spacing: 0.08em; }
}

/* Under reduced motion the page keeps every state and loses every loop: no
   video, no sheen, no connecting ring. The arrivals become arrivals. */
@media (prefers-reduced-motion: reduce) {
	.glass-sheen,
	.dock-action::after { animation: none !important; }

	.glass,
	.whisper,
	.fineprint,
	.footline { animation: none; }

	*, *::before, *::after {
		transition-duration: 1ms !important;
		animation-duration: 1ms !important;
		animation-iteration-count: 1 !important;
	}
}
