/* The default boots.
 *
 * Every selector here is an element name.  There is not one class or id in this
 * file, and there is none in the markup either -- so a skin can be written by
 * anyone who can read a tag name, and any rule here can be overridden by the
 * same selector without reaching for !important (a skin is linked after this
 * file, so it simply wins).
 *
 * The variables below are the shortest path to a different look.  Redefine the
 * six of them on :root and everything follows.
 */

:root {
	--paper: #10131a;
	--panel: #171b24;
	--ink: #c9d2dc;
	--dim: #6b7785;
	--rule: #262c38;
	--accent: #7fd1b9;
	--warn: #e0715f;
	--font: 13px/1.55 ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
	color-scheme: dark;
}

@media (prefers-color-scheme: light) {
	:root {
		--paper: #f6f4ee;
		--panel: #eae7dd;
		--ink: #23262b;
		--dim: #77746b;
		--rule: #d5d1c5;
		--accent: #0f7a63;
		--warn: #a83a25;
		color-scheme: light;
	}
}

* {
	box-sizing: border-box;
}

html,
body {
	height: 100%;
}

body {
	margin: 0;
	background: var(--paper);
	color: var(--ink);
	font: var(--font);
}

button,
input,
select {
	font: inherit;
	color: inherit;
	background: var(--panel);
	border: 1px solid var(--rule);
	padding: 0.25em 0.6em;
}

button {
	cursor: pointer;
}

button:hover {
	border-color: var(--accent);
	color: var(--accent);
}

input:disabled {
	opacity: 0.45;
	cursor: not-allowed;
}

/* ---------------------------------------------------------------- structure */

rainboots-app {
	display: grid;
	grid-template:
		'header header' auto
		'sidebar main' 1fr / minmax(9rem, 15rem) 1fr;
	height: 100dvh;
}

app-header {
	grid-area: header;
	display: flex;
	align-items: center;
	gap: 1em;
	padding: 0.4em 0.8em;
	border-bottom: 1px solid var(--rule);
	background: var(--panel);
}

app-title {
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: lowercase;
	color: var(--accent);
}

my-identity {
	margin-left: auto;
	color: var(--dim);
}

app-sidebar {
	grid-area: sidebar;
	display: flex;
	flex-direction: column;
	gap: 0.2em;
	overflow-y: auto;
	padding: 0.5em 0;
	border-right: 1px solid var(--rule);
}

sidebar-heading {
	display: block;
	padding: 0.6em 0.9em 0.2em;
	color: var(--dim);
	text-transform: uppercase;
	letter-spacing: 0.16em;
	font-size: 0.82em;
}

puddle-list,
dm-list {
	display: flex;
	flex-direction: column;
}

/* Only rendered where DMs cannot work at all. */
dm-unsupported {
	display: block;
	padding: 0.2em 0.9em 0.6em;
	color: var(--dim);
	font-size: 0.9em;
}

puddle-entry,
dm-entry {
	display: flex;
	align-items: baseline;
	gap: 0.5em;
	padding: 0.3em 0.9em;
	cursor: pointer;
	border-left: 2px solid transparent;
}

puddle-entry:hover,
dm-entry:hover {
	background: var(--panel);
}

puddle-entry[current],
dm-entry[current] {
	border-left-color: var(--accent);
	background: var(--panel);
}

puddle-entry[state='connecting'],
puddle-entry[state='closed'] {
	color: var(--dim);
}

puddle-domain {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

peer-count,
dm-state {
	margin-left: auto;
	color: var(--dim);
	font-size: 0.85em;
}

/* Unread ------------------------------------------------------------------ */

unread-count {
	display: inline-block;
	min-width: 1.6em;
	margin-left: 0.4em;
	padding: 0 0.35em;
	border-radius: 1em;
	background: var(--accent);
	color: var(--paper);
	font-size: 0.8em;
	font-weight: 700;
	text-align: center;
}

puddle-entry[unread] puddle-domain,
dm-entry[unread] peer-nick {
	font-weight: 700;
}

/* Nothing is unread in the room you are standing in, so a badge on the current
 * entry would only ever be a stale one. */
puddle-entry[unread] peer-count,
dm-entry[unread] dm-state {
	margin-left: auto;
}

dm-entry[state='declined'] dm-state,
dm-entry[state='failed'] dm-state {
	color: var(--warn);
}

app-main {
	grid-area: main;
	min-width: 0;
	display: grid;
}

app-main > * {
	grid-area: 1 / 1;
}

app-main > *:not([current]) {
	display: none;
}

/* -------------------------------------------------------------------- rooms */

puddle-pane {
	min-height: 0;
	display: grid;
	grid-template:
		'header header' auto
		'log roster' 1fr
		'composer composer' auto / 1fr minmax(8rem, 13rem);
}

pane-header,
panel-header {
	grid-area: header;
	display: flex;
	align-items: baseline;
	gap: 0.8em;
	padding: 0.45em 0.8em;
	border-bottom: 1px solid var(--rule);
}

pane-header puddle-domain {
	color: var(--accent);
}

puddle-state,
my-nick {
	color: var(--dim);
}

my-nick {
	margin-left: auto;
}

message-log {
	grid-area: log;
	overflow-y: auto;
	padding: 0.6em 0.8em;
	min-height: 0;
}

chat-message,
system-notice {
	display: block;
	padding: 0.08em 0;
	overflow-wrap: anywhere;
}

system-notice {
	color: var(--dim);
	font-style: italic;
}

message-when {
	color: var(--dim);
	margin-right: 0.6em;
	font-size: 0.85em;
}

message-text {
	margin-left: 0.5em;
}

peer-roster {
	grid-area: roster;
	overflow-y: auto;
	border-left: 1px solid var(--rule);
	padding: 0.6em 0.5em;
	display: flex;
	flex-direction: column;
	gap: 0.15em;
}

peer-entry {
	display: flex;
	align-items: baseline;
	gap: 0.4em;
	padding: 0.1em 0.3em;
}

/* The child combinator matters: a nick's hint trigger is a <button> too, and a
 * descendant selector here would hide every name in the roster. */
peer-entry > button {
	margin-left: auto;
	visibility: hidden;
	padding: 0 0.4em;
	font-size: 0.85em;
}

peer-entry:hover > button {
	visibility: visible;
}

peer-entry[you] peer-nick {
	color: var(--accent);
}

message-composer {
	grid-area: composer;
	border-top: 1px solid var(--rule);
	padding: 0.5em 0.8em;
}

message-composer form {
	display: flex;
	gap: 0.5em;
}

message-composer input {
	flex: 1;
	min-width: 0;
}

/* -------------------------------------------------------------------- nicks */

/* Hovering any nick shows the peer id behind it, because a puddle invents
 * nicks and could give yours to somebody else.  Keeping them looking casual is
 * part of the point: nothing here is a verified name. */
peer-nick {
	color: var(--ink);
	font-weight: 600;
}

peer-nick[anon] {
	font-weight: 400;
	font-style: italic;
	color: var(--dim);
}

/* --------------------------------------------------------------- hover hints

 * A hint's trigger has to be a <button> -- that is what `interestfor` accepts --
 * and none of the ones that stand in for text should look like one.  Restyle
 * `hover-hint` to change every tooltip in the app at once; it is one element. */
peer-nick button,
local-candidate button {
	font: inherit;
	color: inherit;
	background: none;
	border: 0;
	padding: 0;
	cursor: help;
	text-decoration: underline dotted currentColor;
	text-underline-offset: 0.2em;
}

peer-nick button:hover,
local-candidate button:hover {
	color: inherit;
}

hover-hint {
	/* The trigger is the implicit anchor, so neither end needs a name.  The UA
	 * pins a popover to all four insets; undo that before anchoring it.
	 *
	 * `position-area` rather than `top`/`left: anchor(center)`: the latter
	 * leaves the box sizing itself against whatever room is left to the right of
	 * the anchor, so a hint on a nick near the window edge wraps to one word per
	 * line.  Spanning the inline axis gives it the whole width to size against,
	 * and anchor-center puts it back under the trigger. */
	inset: auto;
	margin: unset;
	position-area: block-end span-all;
	justify-self: anchor-center;
	margin-block-start: 0.45em;
	position-try-fallbacks: flip-block;

	width: max-content;
	max-width: 34ch;
	padding: 0.45em 0.65em;
	font: var(--font);
	color: var(--ink);
	background: var(--panel);
	border: 1px solid var(--rule);
	box-shadow: 0 0.3em 1em #0005;
	overflow-wrap: anywhere;
}

/* Anything whose hint is about an address being visible. */
hover-hint[kind='exposure'] {
	color: var(--warn);
	border-color: var(--warn);
}

hover-hint[kind='identity'] {
	font-weight: 400;
	font-style: normal;
}

peer-id {
	overflow-wrap: anywhere;
	color: var(--dim);
}

/* ---------------------------------------------------------------------- dms */

dm-panel {
	min-height: 0;
	display: grid;
	grid-template:
		'header' auto
		'transport' auto
		'log' 1fr
		'composer' auto;
}

panel-header > button {
	margin-left: auto;
}

dm-peer {
	font-weight: 700;
}

peer-identities {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5em;
	color: var(--dim);
}

peer-identity[unknown] {
	font-style: italic;
}

dm-state {
	margin-left: 0;
}

dm-transport {
	grid-area: transport;
	display: flex;
	align-items: center;
	gap: 0.8em;
	padding: 0.4em 0.8em;
	border-bottom: 1px solid var(--rule);
	background: var(--panel);
	font-size: 0.9em;
	flex-wrap: wrap;
}

candidate-pair {
	display: flex;
	align-items: center;
	gap: 0.5em;
	color: var(--dim);
}

candidate-pair[empty] {
	font-style: italic;
}

local-candidate,
remote-candidate {
	color: var(--ink);
}

/* Not relayed: our address is in the packets they receive.  Marked, and the
 * tooltip says so.  A relay candidate gets no reassuring mark in return -- a
 * stateless relay can encode an IPv4 address into the IPv6 it hands out, so
 * "relay" is not a promise that nothing leaked. */
local-candidate[exposed] {
	color: var(--warn);
}

local-candidate[exposed] button {
	text-decoration: underline wavy var(--warn);
}

dm-transport > button {
	margin-left: auto;
}

/* Already unrelayed, and there is no way back on a live connection.  It stays
 * focusable (aria-disabled, not disabled) so it can still explain itself. */
dm-transport > button[spent],
dm-transport > button[spent]:hover {
	cursor: help;
	color: var(--warn);
	border-color: var(--warn);
}

/* ------------------------------------------------------------------ invites */

invite-tray {
	position: fixed;
	right: 1em;
	bottom: 1em;
	display: flex;
	flex-direction: column;
	gap: 0.6em;
	z-index: 10;
}

dm-invite,
update-notice {
	display: none;
}

dm-invite[open],
update-notice[open] {
	display: flex;
	flex-direction: column;
	gap: 0.4em;
	padding: 0.7em 0.9em;
	background: var(--panel);
	border: 1px solid var(--accent);
	box-shadow: 0 0.4em 1.6em #0006;
	max-width: 22em;
}

invite-title {
	color: var(--accent);
	text-transform: uppercase;
	letter-spacing: 0.14em;
	font-size: 0.8em;
}

invite-actions {
	display: flex;
	gap: 0.5em;
	margin-top: 0.2em;
}

/* ----------------------------------------------------------------- settings */

/* Settings shares the `main` grid area with the panes rather than replacing
 * them, so it has to be opaque and it has to cover the whole cell -- a
 * max-width here would leave the pane showing through beside it, with the
 * message log reading straight through the text.  The width limit belongs on
 * the contents instead. */
settings-panel {
	grid-area: main;
	display: none;
	overflow-y: auto;
	padding: 1.2em;
	gap: 1.6em;
	flex-direction: column;
	align-items: start;
	background: var(--paper);
}

settings-panel[open] {
	display: flex;
}

settings-panel > * {
	width: 100%;
	max-width: 44em;
}

settings-panel form {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5em;
	margin-top: 0.5em;
}

form-title {
	display: block;
	color: var(--accent);
	text-transform: uppercase;
	letter-spacing: 0.14em;
	font-size: 0.82em;
}

form-note {
	display: block;
	color: var(--dim);
	margin-top: 0.35em;
	max-width: 60ch;
}

puddle-known {
	display: flex;
	flex-direction: column;
	gap: 0.25em;
	margin-top: 0.7em;
}

puddle-known-entry {
	display: flex;
	align-items: baseline;
	gap: 0.6em;
}

puddle-known-entry button {
	margin-left: auto;
}

/* ------------------------------------------------------------------ narrow */

@media (max-width: 42em) {
	rainboots-app {
		grid-template:
			'header' auto
			'sidebar' auto
			'main' 1fr / 1fr;
	}

	app-sidebar {
		flex-direction: row;
		flex-wrap: wrap;
		border-right: 0;
		border-bottom: 1px solid var(--rule);
	}

	puddle-list,
	dm-list {
		flex-direction: row;
		flex-wrap: wrap;
	}

	puddle-pane {
		grid-template:
			'header' auto
			'log' 1fr
			'roster' auto
			'composer' auto;
	}

	peer-roster {
		flex-direction: row;
		flex-wrap: wrap;
		border-left: 0;
		border-top: 1px solid var(--rule);
		max-height: 6em;
	}

	peer-entry > button {
		visibility: visible;
	}
}
