/**
 * The chat box, on the front of the site.
 *
 * Black and white, deliberately. Two themes that are inversions of one
 * another: light is black on white, dark is white on black, and the same
 * handful of variables carry both. Nothing here introduces a color, so the
 * box takes its character from the site around it rather than competing with
 * it, and it reads on any background.
 *
 * It inherits the theme's own typeface. Override any variable below in the
 * theme and nothing in this file has to be touched.
 */

.dsag-chat {
	--dsag-paper: #ffffff;
	--dsag-ink: #000000;
	--dsag-line: #d8d8d8;
	--dsag-quiet: #6b6b6b;
	--dsag-theirs: #f2f2f2;
	--dsag-mine: #000000;
	--dsag-mine-ink: #ffffff;
	--dsag-radius: 10px;

	/* The one color on the box, and by default it is not ours: --ds-accent is
	   the theme's own, set on the Theme Settings screen, so the button matches
	   the site without the same color being stored twice and drifting. Black
	   is the fallback for a site that does not set it. */
	--dsag-key: var(--ds-accent, #000000);
	--dsag-key-ink: #ffffff;
}

.dsag-chat.key-black {
	--dsag-key: #000000;
	--dsag-key-ink: #ffffff;
}

.dsag-chat.key-white {
	--dsag-key: #ffffff;
	--dsag-key-ink: #000000;
}

/* The inversion. Same roles, opposite ends. */
.dsag-chat.is-dark {
	--dsag-paper: #000000;
	--dsag-ink: #ffffff;
	--dsag-line: #333333;
	--dsag-quiet: #9a9a9a;
	--dsag-theirs: #1a1a1a;
	--dsag-mine: #ffffff;
	--dsag-mine-ink: #000000;
}

.dsag-chat-panel {
	display: flex;
	flex-direction: column;
	min-height: 0;
	background: var(--dsag-paper);
	border: 1px solid var(--dsag-line);
	border-radius: var(--dsag-radius);
	color: var(--dsag-ink);
	font-size: 15px;
	line-height: 1.5;
	overflow: hidden;
}

.dsag-chat-panel[hidden] {
	display: none;
}

.dsag-chat-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 12px 16px;
	border-bottom: 1px solid var(--dsag-line);
}

.dsag-chat-title {
	font-weight: 600;
}

/* Start again, and leave. Small and quiet: they are always there but they are
   not what the panel is for. */
.dsag-chat-tools {
	display: flex;
	align-items: center;
	gap: 6px;
	flex: 0 0 auto;
}

.dsag-chat .dsag-chat-mini {
	padding: 4px 10px;
	border: 1px solid var(--dsag-line);
	border-radius: 999px;
	background: none;
	color: var(--dsag-quiet);
	font: inherit;
	font-size: 12px;
	line-height: 1.4;
	cursor: pointer;
	white-space: nowrap;
}

.dsag-chat .dsag-chat-mini:hover,
.dsag-chat .dsag-chat-mini:focus {
	background: var(--dsag-paper);
	border-color: var(--dsag-ink);
	color: var(--dsag-ink);
}

/* A height, not a ceiling.
 *
 * With only a maximum, the panel was as tall as whatever was in it: short on
 * the greeting, then jumping taller the moment a question and an answer
 * arrived, and settling once it hit the limit. A box that resizes under the
 * pointer while somebody is reading it reads as broken. It takes its full
 * height from the start and scrolls inside that instead. */
.dsag-chat-log {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	min-height: 0;
	height: 60vh;
	padding: 16px;
	overflow-y: auto;
	overscroll-behavior: contain;
}

/* The conversation sits on the bottom of the log, against the box somebody is
 * about to type into, and grows upwards into the room above it.
 *
 * Held at the top instead, a panel with a fixed height opens as a greeting with
 * a half-empty box beneath it, which reads as the panel having failed to shrink
 * rather than as a conversation waiting to happen. Pushed down, the same slack
 * sits above the first line and reads as the top of a scroll.
 *
 * An auto margin rather than justify-content: flex-end, which looks the same
 * until the conversation is longer than the box and then puts the earliest
 * lines above the scroll area, where they cannot be scrolled back to. */
.dsag-chat-log > :first-child {
	margin-top: auto;
}

/* Each line keeps the height its own words need. A column of flex items will
   otherwise squeeze them all to fit once the conversation is longer than the
   box, rather than letting it scroll. */
.dsag-chat-log > * {
	flex: 0 0 auto;
}

.dsag-chat-line + .dsag-chat-line {
	margin-top: 12px;
}

.dsag-chat-who {
	display: block;
	margin-bottom: 2px;
	font-size: 12px;
	color: var(--dsag-quiet);
}

.dsag-chat-who:empty {
	display: none;
}

.dsag-chat-text {
	margin: 0;
	padding: 8px 12px;
	border-radius: var(--dsag-radius);
	background: var(--dsag-theirs);
	color: var(--dsag-ink);
	/* A model writes paragraphs. They are kept rather than run together. */
	white-space: pre-wrap;
	overflow-wrap: anywhere;
}

.dsag-chat-line.is-me {
	/* The visitor's own words sit to the right, as they do everywhere else. */
	margin-left: auto;
	max-width: 85%;
}

.dsag-chat-line.is-me .dsag-chat-text {
	background: var(--dsag-mine);
	color: var(--dsag-mine-ink);
}

.dsag-chat-line.is-waiting .dsag-chat-text {
	color: var(--dsag-quiet);
}

/* The way through when she cannot answer. Marked out from the conversation,
   because it is the site talking rather than the assistant. */
.dsag-chat-offer .dsag-chat-text {
	background: none;
	padding: 8px 0 0;
	border-top: 1px solid var(--dsag-line);
	border-radius: 0;
	font-size: 14px;
	color: var(--dsag-quiet);
}

.dsag-chat-link {
	color: inherit;
	font-weight: 600;
	text-decoration: underline;
}

/* A link in the visitor's own bubble is on the inverted background, so it
   takes that bubble's ink rather than the panel's. */
.dsag-chat-line.is-me .dsag-chat-link {
	color: var(--dsag-mine-ink);
}

/* A link takes its own line rather than sitting in the middle of a sentence.
   An address is one unbroken run of characters, so inline it either pushes the
   bubble wider than the panel or breaks across a line mid word; on its own
   line it reads as the thing to click. */
.dsag-chat-text .dsag-chat-link {
	display: block;
	margin: 6px 0 0;
	overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------- *
 * Typing, and sending
 * ---------------------------------------------------------------- */

/* The box above, the button beneath it and across the whole width. A button
   beside the box competes with it for room and shrinks the thing being typed
   into; underneath, both get the full width. */
.dsag-chat-form {
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: 12px 16px;
	border-top: 1px solid var(--dsag-line);
}

.dsag-chat .dsag-chat-box {
	display: block;
	width: 100%;
	box-sizing: border-box;
	height: 44px;
	padding: 10px 12px;
	border: 1px solid var(--dsag-line);
	border-radius: var(--dsag-radius);
	background: var(--dsag-paper);
	color: var(--dsag-ink);
	font: inherit;
}

.dsag-chat .dsag-chat-box::placeholder {
	color: var(--dsag-quiet);
}

.dsag-chat .dsag-chat-send {
	display: block;
	width: 100%;
	box-sizing: border-box;
	padding: 12px 16px;
	border: 1px solid var(--dsag-key);
	border-radius: var(--dsag-radius);
	background: var(--dsag-key);
	color: var(--dsag-key-ink);
	font: inherit;
	font-weight: 600;
	cursor: pointer;
}

/* Named through the wrapper, and every color stated.
 *
 * This said background:transparent and left the text to a variable, which is
 * fine until the site's own stylesheet has a button:hover rule of its own.
 * Theirs set the text to white, mine set the background to nothing, and the
 * result on a white panel was a button with no text on it. A hover state has
 * to state its own foreground and background together or it is at the mercy of
 * whatever else on the page has an opinion about buttons. */
/* Filled, not outlined. This inverted the wrong way round: it took the panel's
   own background and drew a border, so on a white box the hover was a white
   button with a thin black outline, which reads as the button being switched
   off rather than pointed at. The same inversion as the chat button: black on
   a white panel, white on a black one, the text the opposite of whichever it
   lands on. */
.dsag-chat .dsag-chat-send:hover,
.dsag-chat .dsag-chat-send:focus {
	background: var(--dsag-ink);
	color: var(--dsag-paper);
	border-color: var(--dsag-ink);
}

.dsag-chat .dsag-chat-send[disabled] {
	opacity: .5;
	cursor: default;
}

.dsag-chat .dsag-chat-send[disabled]:hover,
.dsag-chat .dsag-chat-send[disabled]:focus {
	background: var(--dsag-key);
	color: var(--dsag-key-ink);
	border-color: var(--dsag-key);
}

/* ---------------------------------------------------------------- *
 * In the corner of every page
 * ---------------------------------------------------------------- */

/* The page's own gutter, so the button lines up with the edge of the content
   rather than hanging in the margin beside it. 50px on a desktop, and the
   phone rule further down drops it to the 20px the layout uses there. */
.dsag-chat-float {
	position: fixed;
	right: 50px;
	bottom: 50px;
	z-index: 9999;
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	gap: 10px;
}

.dsag-chat-float .dsag-chat-panel {
	width: min(380px, calc(100vw - 40px));

	/* One height, and it is the height of what is in it when it opens.
	 *
	 * Measured off the real panel in a headless browser rather than chosen:
	 *
	 *   intro 157 + its 16 margin + greeting 59 = 232
	 *   the log's own padding                   =  32
	 *   head 52, the box and button below 126, borders 2
	 *
	 * which is 444, and that is the figure. Shorter than that and the log scrolls before a
	 * word has been typed, and the first thing to go is the top of her head.
	 * Taller than that and the surplus shows as a band of nothing above the
	 * photo, because the conversation is held against the bottom of the log.
	 * There is no range to pick from: one number is both limits at once, and
	 * every figure tried before this - 560, 480, 500 - was somewhere in the
	 * gap above her head.
	 *
	 * The window term counts the 50px the panel sits off the bottom, which is
	 * the room it actually has. It only comes into play below about a 500px
	 * window, and there the log does scroll, because there is nowhere else for
	 * the pixels to come from. */
	height: min(444px, calc(100svh - 50px));

	box-shadow: 0 10px 40px rgba(0, 0, 0, .18);
}

/* The panel sets the height here, so the log takes what is left of it rather
   than carrying a figure of its own. */
.dsag-chat-float .dsag-chat-log {
	height: auto;
}

/* On a short window the open panel takes the bottom gutter back.
 *
 * Measured on a 503px window, which is what a 1080p screen at 175% scaling
 * leaves: the panel is 450 because that is what her photo, her name, the line
 * under it and the greeting need, and 450 plus the 50 underneath is 500 of the
 * 503, so the box opened three pixels from the top of the screen. There is
 * nothing to take from the panel without the log scrolling, so it comes from
 * the gap underneath instead, and the box clears the top of the window by
 * thirty.
 *
 * Only while it is open, and only the panel: :has() keeps the closed button on
 * the 50px the rest of the page uses. */
@media (max-height: 560px) {
	.dsag-chat-float:has(.dsag-chat-panel:not([hidden])) {
		bottom: 20px;
	}
}

/* The button that opens it. Its own color, set apart from the panel's
   theme, because it sits on the site's background rather than on the panel
   and the two are not always the same shade. */
.dsag-chat .dsag-chat-tab {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 12px 20px;
	border: 1px solid transparent;
	border-radius: 999px;
	font: inherit;
	font-weight: 600;
	box-shadow: 0 6px 20px rgba(0, 0, 0, .22);
	cursor: pointer;
}

.dsag-chat .dsag-chat-tab {
	background: var(--dsag-key);
	color: var(--dsag-key-ink);
}

/* Bordered, or a white button disappears on a white page. */
.dsag-chat.key-white .dsag-chat-tab {
	border-color: #000000;
}

/* Hovered, it drops the accent and goes to the panel's own opposite: black
   against a white box, white against a black one. That is --dsag-ink in both
   cases, which is the whole reason the themes are built as an inversion of one
   another rather than as two sets of colors.
 *
 * Stated in full, foreground and background together, for the same reason as
 * the send button: a theme with its own button:hover must not be able to
 * repaint half of this one. */
.dsag-chat .dsag-chat-tab:hover,
.dsag-chat .dsag-chat-tab:focus {
	background: var(--dsag-ink);
	border-color: var(--dsag-ink);
	color: var(--dsag-paper);
	transform: translateY(-1px);
}

.dsag-chat-tab[hidden] {
	display: none;
}

/* Drawn in the current text color, so it inverts with the button. */
.dsag-chat-icon {
	display: block;
	width: 20px;
	height: 20px;
	flex: 0 0 auto;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

@media (prefers-reduced-motion: reduce) {
	.dsag-chat-tab:hover,
	.dsag-chat-tab:focus {
		transform: none;
	}
}

/* On a phone the panel takes the width of the window, less the page gutter.
 *
 * A 380px card with a margin down each side is a desktop shape on a desktop
 * screen; on a phone it leaves a strip of page either side of a conversation
 * that is already short of room. Full width, with the same 20px the rest of
 * the page keeps at the edges, so it sits in the layout rather than on top of
 * it. The button takes that gutter too, from the container rather than from a
 * margin of its own.
 *
 * 600px rather than 480: a panel that wide against a 540px window still leaves
 * the same strip, so the breakpoint is where the layout stops working rather
 * than at a particular device. */
@media (max-width: 600px) {
	.dsag-chat-float {
		right: 20px;
		bottom: 20px;
		left: 20px;
		align-items: stretch;
	}

	.dsag-chat-float .dsag-chat-panel {
		width: 100%;
		/* svh, not vh: vh on a phone is the window with the browser chrome
		   hidden, so a panel sized to it is taller than the screen. The gutter
		   is counted twice, for the top and the bottom. A height rather than a
		   ceiling, so it does not grow as the conversation does. */
		height: calc(100svh - 40px);
	}

	.dsag-chat-float .dsag-chat-log {
		/* Whatever is left once the head and the form have taken theirs. */
		height: auto;
	}

	/* Back to its corner rather than stretched across the row, and no margin
	   of its own: the container's inset is already the gutter. */
	.dsag-chat-tab {
		align-self: flex-end;
		margin: 0;
	}
}

/* ---------------------------------------------------------------- *
 * Her face
 * ---------------------------------------------------------------- */

/* Once, at the top, at a size worth looking at. */
.dsag-chat-intro {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	/* No padding of its own at the top. It is the first thing in the log and
	   the log already insets it; four more read as the panel having left a gap
	   above her photo rather than as the card breathing. */
	padding: 0 0 16px;
	margin-bottom: 16px;
	border-bottom: 1px solid var(--dsag-line);
	text-align: center;
}

.dsag-chat-face {
	display: block;
	width: 72px;
	height: 72px;
	border-radius: 50%;
	object-fit: cover;
	/* The photo is the one colorful thing in a black and white panel, so it
	   gets a ring in the panel's own ink rather than an accent. */
	border: 1px solid var(--dsag-line);
}

.dsag-chat-introname {
	font-weight: 600;
}

.dsag-chat-bio {
	margin: 0;
	max-width: 30ch;
	font-size: 13px;
	line-height: 1.45;
	color: var(--dsag-quiet);
}

/* Or small, beside each of her lines, for whoever prefers it that way. The
   bubble keeps its own left edge so the text still lines up down the column. */
.dsag-chat-line.has-face {
	display: grid;
	grid-template-columns: 28px minmax(0, 1fr);
	grid-template-rows: auto auto;
	column-gap: 8px;
	align-items: start;
}

.dsag-chat-avatar {
	grid-row: 1 / 3;
	width: 28px;
	height: 28px;
	margin-top: 2px;
	border-radius: 50%;
	object-fit: cover;
}

/* Only where the theme's lightbox is actually listening, so the cursor never
   promises something no handler will answer. */
.dsag-chat-face.can-open {
	cursor: zoom-in;
}
