/**
 * Demand AI chatbot — launcher bubble, docked panel, branded transcript.
 *
 * The chatbot team's stylesheet (chat-lib_v1.css) docks .chat-widget as a full-height
 * column down the right edge, has no launcher, and leaves several of its own
 * hooks unstyled. This file re-docks it as a bottom-right panel behind a chat bubble and
 * dresses the whole thing in the brand palette.
 *
 * Load AFTER chat-lib_v1.css. Pairs with dai-chatbot-init.js.
 *
 * Colour comes from the Demand AI Visual Identity Guidelines (2026) ten-colour palette.
 * The one exception is --daiChat-teal-light: the guidelines' accent teal is #69a7a7, but
 * the site already ships #4fb0b0 as BRAND_LIGHT (locations/app.js, and the direction in
 * this repo's CLAUDE.md), so the chatbot matches the site rather than the book.
 */

/* Registered so the conic angle animates. Without @property a custom property is a
   plain string and the gradient jumps rather than sweeps. Unsupported browsers keep the
   ring, just without the travel. */
@property --daiChat-angle {
	syntax: "<angle>";
	inherits: false;
	initial-value: 0deg;
}

.daiChat {
	/* --- brand palette ----------------------------------------------------- */
	--daiChat-teal-900: #154b4c;
	--daiChat-teal-700: #056e6e;
	--daiChat-teal-600: #207d7f;
	--daiChat-teal-300: #90bfc0;
	--daiChat-teal-100: #ebf3f3;
	--daiChat-teal-50: #f7fafa;
	--daiChat-grey-700: #47494b;
	--daiChat-teal-light: #4fb0b0;

	/* --- semantic ---------------------------------------------------------- */
	--daiChat-brand: var(--daiChat-teal-700);
	--daiChat-brand-strong: var(--daiChat-teal-900);
	--daiChat-surface: #fff;
	--daiChat-text: var(--daiChat-grey-700);
	--daiChat-on-brand: #fff;
	--daiChat-border: rgba(21, 75, 76, .12);
	--daiChat-border-strong: rgba(21, 75, 76, .22);
	--daiChat-ring: 0 0 0 3px rgba(79, 176, 176, .35);
	--daiChat-shadow-panel: 0 24px 60px rgba(21, 75, 76, .20), 0 4px 12px rgba(21, 75, 76, .08);
	--daiChat-shadow-launcher: 0 10px 28px rgba(5, 110, 110, .34), 0 2px 6px rgba(21, 75, 76, .14);
	--daiChat-shadow-launcher-hover: 0 16px 36px rgba(5, 110, 110, .42), 0 3px 8px rgba(21, 75, 76, .16);

	/* Referenced by chat-lib_v1.css but never defined there. */
	--color-theme: var(--daiChat-brand);
	--color-thumb-active: var(--daiChat-teal-100);

	/* --- metrics ----------------------------------------------------------- */
	--daiChat-gap: 24px;
	--daiChat-size: 60px;
	/* Height of the site's fixed header. Measured at runtime by dai-chatbot-init.js
	   because it changes with viewport width (93px at 768, 113px at 1280); this value
	   is only the fallback used before that first measurement. */
	--daiChat-nav-h: 96px;
	/* clearance kept between the header's underside and the top of the panel */
	--daiChat-nav-clear: 16px;
	--daiChat-radius: 22px;
	--daiChat-bubble-radius: 16px;
	/* Clearance for the input row. Tuned so the stand-in sits where the real first
	   message lands, making the hand-off at 10s invisible. The offset measured the
	   same on both breakpoints, so there is no mobile override. */
	--daiChat-transcript-bottom: 91px;

	--daiChat-ease: cubic-bezier(.2, .7, .3, 1);
}

/* ---------------------------------------------------------------- launcher */

/* Scoped with .daiChat throughout. The theme's style.css carries
   `button:focus, button:active { background: var(--wp--preset--color--primary) }`
   — one class plus an element, which outranks a bare `.daiChat__launcher`, so the
   moment the bubble was focused or pressed WordPress repainted it #4cbb17 kelly
   green. Two classes outrank that, and the states are declared explicitly below. */
.daiChat .daiChat__launcher {
	position: fixed;
	right: var(--daiChat-gap);
	bottom: var(--daiChat-gap);
	/* Deliberately low: the chatbot sits UNDER the site header so the nav's dropdowns
	   always open on top of it. The header is z-index 50 on the homepage and 1000
	   everywhere else, and its submenus cannot rise above their header's layer, so
	   the chatbot has to stay below 50. Still above page content (10 and under) and
	   below form dropdowns (.dai-form__comboList, 30) and the research gate dialog
	   (200), both of which should cover it. Panel is one below the launcher. */
	z-index: 26;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	width: auto;
	height: var(--daiChat-size);
	padding: 0 22px 0 15px;
	border: 0;
	border-radius: 999px;
	cursor: pointer;
	color: var(--daiChat-on-brand);
	background: linear-gradient(135deg, var(--daiChat-teal-light), var(--daiChat-brand) 62%, var(--daiChat-brand-strong));
	box-shadow: var(--daiChat-shadow-launcher);
	transition: transform .22s var(--daiChat-ease), box-shadow .22s ease,
		width .26s var(--daiChat-ease), padding .26s var(--daiChat-ease),
		gap .26s var(--daiChat-ease);
}

/* Logomark + label. Open, the pill collapses back to a circular close button. */
.daiChat .daiChat__mark {
	flex: 0 0 auto;
	width: 30px;
	height: auto;
	transition: opacity .18s ease, width .26s var(--daiChat-ease);
}

.daiChat .daiChat__label {
	max-width: 240px;
	overflow: hidden;
	font-size: 15px;
	font-weight: 600;
	line-height: 1;
	white-space: nowrap;
	transition: opacity .16s ease, max-width .26s var(--daiChat-ease);
}

.daiChat.is-open .daiChat__launcher {
	width: var(--daiChat-size);
	padding: 0;
	gap: 0;
}

.daiChat.is-open .daiChat__mark {
	width: 0;
	opacity: 0;
}

.daiChat.is-open .daiChat__label {
	max-width: 0;
	opacity: 0;
}

.daiChat .daiChat__launcher:hover {
	transform: translateY(-2px) scale(1.04);
	box-shadow: var(--daiChat-shadow-launcher-hover);
}

.daiChat .daiChat__launcher:active {
	transform: translateY(0) scale(.97);
}

/* Re-declared for :focus and :active specifically — this is the pair the theme targets. */
.daiChat .daiChat__launcher:focus,
.daiChat .daiChat__launcher:active {
	background: linear-gradient(135deg, var(--daiChat-teal-light), var(--daiChat-brand) 62%, var(--daiChat-brand-strong));
	color: var(--daiChat-on-brand);
}

.daiChat .daiChat__launcher:focus-visible {
	outline: 3px solid var(--daiChat-teal-light);
	outline-offset: 3px;
}

.daiChat .daiChat__icon--close {
	position: absolute;
	width: 24px;
	height: 24px;
	opacity: 0;
	transform: rotate(-90deg) scale(.6);
	transition: opacity .18s ease, transform .18s ease;
}

.daiChat.is-open .daiChat__icon--close {
	opacity: 1;
	transform: none;
}

/* A white hairline around the pill with one brighter segment travelling round it.
   Conic gradient masked down to the border only; the sweep animates the angle, which
   is why --daiChat-angle is registered with @property above (an unregistered custom
   property is just a string, and the gradient would jump rather than travel). */
.daiChat .daiChat__launcher::before {
	content: "";
	position: absolute;
	inset: -1px;
	z-index: 0;
	padding: 1px;
	border-radius: inherit;
	pointer-events: none;
	background: conic-gradient(from var(--daiChat-angle),
			rgba(255, 255, 255, .55) 0deg,
			rgba(255, 255, 255, .55) 58deg,
			#fff 90deg,
			rgba(255, 255, 255, .55) 122deg,
			rgba(255, 255, 255, .55) 360deg);
	-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	-webkit-mask-composite: xor;
	mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	mask-composite: exclude;
	/* drop-shadow applies AFTER the mask, so the glow hugs the visible hairline rather
	   than the pseudo-element's box — and it rides along with the bright streak. A
	   box-shadow here would be clipped away by the same mask that makes the ring. */
	filter: drop-shadow(0 0 4px rgba(255, 255, 255, .85))
		drop-shadow(0 0 10px rgba(255, 255, 255, .45));
	animation: daiChatSweep 2.4s linear infinite;
}

@keyframes daiChatSweep {
	to { --daiChat-angle: 360deg; }
}

/* Keep the contents above the ring. */
.daiChat .daiChat__mark,
.daiChat .daiChat__label {
	position: relative;
	z-index: 1;
}

.daiChat .daiChat__icon--close {
	z-index: 1;
}

/* ------------------------------------------------------------------- panel */

.daiChat__panel {
	/* Shown/hidden with visibility + opacity rather than display, so the widget keeps a
	   real box to measure and scroll while the panel is closed. */
	visibility: hidden;
	opacity: 0;
	pointer-events: none;
	transform: translateY(12px) scale(.97);
	transition: opacity .24s var(--daiChat-ease),
		transform .24s var(--daiChat-ease),
		visibility 0s linear .24s;
	position: fixed;
	right: var(--daiChat-gap);
	bottom: calc(var(--daiChat-gap) + var(--daiChat-size) + 16px);
	/* Below the site header and its dropdowns — see the launcher's z-index. */
	z-index: 25;
	width: min(384px, calc(100vw - 32px));
	/* Never taller than the gap between the fixed header and the launcher, so the panel
	   can never ride up over the nav — it just gets shorter on shorter screens.
	   Declared twice: the vh line is the fallback for browsers without dvh, which is
	   dropped at parse time where dvh is understood. dvh matters on mobile, where the
	   browser's own chrome shrinks the visible viewport. */
	height: min(560px, calc(100vh - var(--daiChat-nav-h) - var(--daiChat-gap) - var(--daiChat-size) - var(--daiChat-nav-clear) - 16px));
	height: min(560px, calc(100dvh - var(--daiChat-nav-h) - var(--daiChat-gap) - var(--daiChat-size) - var(--daiChat-nav-clear) - 16px));
	border: 1px solid var(--daiChat-border);
	border-radius: var(--daiChat-radius);
	background: var(--daiChat-surface);
	box-shadow: var(--daiChat-shadow-panel);
	overflow: hidden;
	transform-origin: bottom right;
}

.daiChat.is-open .daiChat__panel {
	visibility: visible;
	opacity: 1;
	pointer-events: auto;
	transform: none;
	transition: opacity .24s var(--daiChat-ease),
		transform .24s var(--daiChat-ease),
		visibility 0s;
}

/* Make the vendor widget fill the panel instead of docking itself to the viewport.
   chat-lib_v1.css re-pins position/width/inset at five breakpoints — several with
   `!important` — so these have to be forced. */
.daiChat .chat-widget {
	position: absolute !important;
	inset: 0 !important;
	width: auto !important;
	height: auto !important;
	max-height: none;
	padding-top: 0;
	padding-inline: 16px;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	background: var(--daiChat-surface);
	/* chat-lib_v1.css raises this to z-index 99 under 768px, which would bury the
	   in-panel close button. `auto`, NOT 0: a positioned element with z-index 0 forms
	   a stacking context, which would trap the sticky question inside it and leave it
	   stranded beneath .daiChat__qmask however high its own z-index went. With auto
	   there is no context, so question (2), mask (1) and messages all stack together
	   in the panel. */
	z-index: auto;
}

/* ---------------------------------------------------------- in-panel close */

/* Shown on mobile only: there the sheet covers the corner and the launcher is
   hidden, so this is the only way out. On desktop the launcher itself is the
   toggle and sits right below the panel. */
.daiChat .daiChat__close {
	display: none;
	position: absolute;
	top: 10px;
	right: 12px;
	z-index: 5;
	width: 34px;
	height: 34px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	cursor: pointer;
	color: var(--daiChat-brand);
	background: var(--daiChat-teal-100);
	transition: background-color .18s ease;
}

.daiChat .daiChat__close:hover,
.daiChat .daiChat__close:focus,
.daiChat .daiChat__close:active {
	color: var(--daiChat-brand);
	background: var(--daiChat-teal-300);
}

.daiChat .daiChat__close svg {
	width: 15px;
	height: 15px;
}

.daiChat .daiChat__close:focus-visible {
	outline: 3px solid var(--daiChat-teal-light);
	outline-offset: 2px;
}

/* -------------------------------------------------------------- transcript */

.daiChat .chat-messages {
	flex: 1;
	padding-inline: 2px;
	padding-block: 14px 0;
	/* offsetParent for the messages, so dai-chatbot-init.js can read a message's
	   natural offsetTop while sticky is momentarily suspended. */
	position: relative;
}

/* Chat transcripts read bottom-up: with only a few messages they should sit just above
   the input, not stranded at the top. A zero-height flex item that soaks up the spare
   space does it. When the transcript overflows there is no spare space, so it collapses
   to nothing and normal scrolling is unaffected.
   Not `justify-content: flex-end` — that puts overflowing content above the scroll
   origin where it cannot be reached. Not `margin-top: auto` on `:first-child` either:
   the first child is often the greeting hidden by the .is-conversing rule below, and a
   display:none element is not a flex item, so the margin lands on nothing. */
.daiChat .chat-messages::before {
	content: "";
	flex: 1 0 auto;
}

/* The vendor sets 12px/1.7 on both senders — small and loose for a 384px panel. */
.daiChat .message {
	margin-bottom: 12px;
	max-width: 88%;
}

.daiChat .message.bubble {
	padding: 11px 15px;
	border-radius: var(--daiChat-bubble-radius);
	font-size: 14px;
	line-height: 1.55;
}

.daiChat .message.bot,
.daiChat .message.bot.bubble {
	color: var(--daiChat-text);
	font-size: 14px;
	line-height: 1.55;
}

.daiChat .message.bot.bubble {
	background-color: var(--daiChat-teal-100);
	border-bottom-left-radius: 4px;
}

.daiChat .message.bot.bubble strong {
	color: var(--daiChat-teal-900);
}

.daiChat .message.user,
.daiChat .message.user.bubble {
	color: var(--daiChat-on-brand);
	font-size: 14px;
	line-height: 1.55;
}

.daiChat .message.user.bubble {
	background-color: var(--daiChat-brand);
	border-bottom-right-radius: 4px;
	/* Stickiness is applied inline by dai-chatbot-init.js, to EXACTLY ONE question at
	   a time — whichever has scrolled past the top. Doing it here in CSS made every
	   question sticky at once, so they piled up on each other at top: 0. */
}

/* One opaque strip across the whole transcript width, sitting behind the stuck
   question and in front of the messages, so the answer is simply not drawn where it
   passes the question — no patchwork of rectangles around the bubble, and therefore
   no seams between them. It lives outside .chat-messages so the transcript's own
   overflow cannot clip it, and dai-chatbot-init.js sets its height from the question
   actually on screen. */
.daiChat__qmask {
	position: absolute;
	top: 0;
	right: 0;
	left: 0;
	z-index: 1;
	height: var(--daiChat-qmask-h, 0px);
	background: var(--daiChat-surface);
	pointer-events: none;
}

/* Answers covering two or more topics come back with markdown headings ("# Our
   Services"), which picked up the site's page-heading sizes — a 50px+ h1 inside a
   384px panel. Bring them down to bubble scale: a step above the 14px body text,
   enough to mark a section and no more.
   !important throughout because the theme's style.css forces all three with it:
   Roboto and 700 on h1–h5 everywhere, and `font-size: 25px; line-height: 32px` on
   h1–h5 below 768px. The font is reclaimed to match the bubble's own text. */
.daiChat .message.bubble h1,
.daiChat .message.bubble h2,
.daiChat .message.bubble h3,
.daiChat .message.bubble h4,
.daiChat .message.bubble h5,
.daiChat .message.bubble h6 {
	margin: 14px 0 6px;
	padding: 0;
	font-family: inherit !important;
	font-weight: 700 !important;
	line-height: 1.35 !important;
	letter-spacing: normal;
	text-transform: none;
	color: var(--daiChat-teal-900);
}

.daiChat .message.bubble h1,
.daiChat .message.bubble h2 {
	font-size: 16px !important;
}

.daiChat .message.bubble h3 {
	font-size: 15px !important;
}

.daiChat .message.bubble h4,
.daiChat .message.bubble h5,
.daiChat .message.bubble h6 {
	font-size: 14px !important;
}

/* No extra gap when a bubble opens with a heading. */
.daiChat .message.bubble .out > :first-child {
	margin-top: 0;
}

/* Trailing margin on the last block inside a bubble doubles the gap. */
.daiChat .message.bubble .out > :last-child,
.daiChat .message.bot.bubble div:last-child {
	margin-bottom: 0;
}

/* Typing indicator — vendor ships #888 text and #333 dots. */
.daiChat .message.typing .loading-text {
	color: var(--daiChat-teal-600);
	font-size: 13px;
	opacity: .85;
}

.daiChat .message.typing .dot {
	background-color: var(--daiChat-teal-light);
}

/* ------------------------------------------------------------------- input */

/* The vendor input is sized for a much wider rail; let it flex instead of
   fixing it at 70%, or the two buttons wrap out of the pill. */
.daiChat .chat-input input {
	flex: 1 1 auto;
	width: auto;
	height: auto;
	padding: 15px 18px;
	font-size: 15px;
	/* The theme styles every input[type=text] with a 1px grey border and a 5px
	   radius (style.css), which drew a second box inside the vendor's pill.
	   The pill is the control now, so the field itself is bare. */
	border: 0;
	border-radius: 50px;
	background: transparent;
	box-shadow: none;
	color: var(--daiChat-teal-900);
}

.daiChat .chat-input input::placeholder {
	color: rgba(71, 73, 75, .55);
}

/* ...and the theme's matching :focus outline drew that inner box again on focus.
   Move the focus ring onto the pill so keyboard users still get one. */
.daiChat .chat-input input:focus,
.daiChat .chat-input input:focus-visible {
	outline: 0;
}

.daiChat .chat-input {
	gap: 6px;
	margin-block: 14px 16px;
	padding-right: 6px;
	border-color: var(--daiChat-border-strong);
	transition: box-shadow .16s ease, border-color .16s ease;
}

.daiChat .chat-input:focus-within {
	border-color: var(--daiChat-brand);
	box-shadow: var(--daiChat-ring);
}

/* chat-lib_v1.css has no rule at all for .input-actions, the div the bundle wraps the
   two buttons in, so they stack vertically and burst out of the input pill. */
.daiChat .input-actions {
	display: flex;
	flex: 0 0 auto;
	flex-wrap: nowrap;
	align-items: center;
	gap: 6px;
}

/* chat-lib_v1.css styles .chat-input button for an <img> child, but the bundle
   renders empty <span class="send-icon"> / <span class="speech-icon">, so both
   buttons come out blank. Masks below supply the missing glyphs. */
.daiChat .chat-input button {
	display: grid;
	place-items: center;
	flex: 0 0 auto;
	width: 38px;
	height: 38px;
	right: 0;
	background: var(--daiChat-brand);
	transition: background-color .18s ease, transform .18s ease;
}

.daiChat .chat-input button:hover {
	background: var(--daiChat-teal-light);
	transform: scale(1.06);
}

.daiChat .chat-input button:focus-visible {
	outline: 3px solid var(--daiChat-teal-light);
	outline-offset: 2px;
}

.daiChat .send-icon,
.daiChat .speech-icon {
	display: block;
	width: 19px;
	height: 19px;
	background-color: var(--daiChat-on-brand);
}

.daiChat .send-icon {
	-webkit-mask: var(--daiChat-send) center / contain no-repeat;
	mask: var(--daiChat-send) center / contain no-repeat;
}

.daiChat .speech-icon {
	-webkit-mask: var(--daiChat-mic) center / contain no-repeat;
	mask: var(--daiChat-mic) center / contain no-repeat;
}

/* While a reply is being written, the send arrow becomes a stop square, as in
   ChatGPT. chat-lib disables its input for exactly that span — from the moment a
   question is sent until the reply's `done` (or `error`) event — so the disabled
   input is the signal; no script needed.
   The vendor widget has no way to cancel a reply, and ignores the send button
   while one is in flight, so the button cannot act as "stop" yet: it shows the
   state without inviting a click (no pointer, no hover lift). */
.daiChat .chat-input:has(input:disabled) .send-icon {
	width: 14px;
	height: 14px;
	-webkit-mask-image: var(--daiChat-stop);
	mask-image: var(--daiChat-stop);
}

.daiChat .chat-input:has(input:disabled) .send-button {
	cursor: default;
}

.daiChat .chat-input:has(input:disabled) .send-button:hover {
	background: var(--daiChat-brand);
	transform: none;
}

.daiChat {
	--daiChat-stop: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black"><rect x="2" y="2" width="20" height="20" rx="4"/></svg>');
	--daiChat-send: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black"><path d="M2.3 21.5 23 12 2.3 2.5 2.3 10l14.8 2L2.3 14z"/></svg>');
	--daiChat-mic: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black"><path d="M12 15a3.5 3.5 0 0 0 3.5-3.5v-6a3.5 3.5 0 1 0-7 0v6A3.5 3.5 0 0 0 12 15z"/><path d="M18.5 11.5a.9.9 0 1 0-1.8 0 4.7 4.7 0 0 1-9.4 0 .9.9 0 1 0-1.8 0 6.5 6.5 0 0 0 5.6 6.4v2.2h-2a.9.9 0 1 0 0 1.8h5.8a.9.9 0 1 0 0-1.8h-2v-2.2a6.5 6.5 0 0 0 5.6-6.4z"/></svg>');
}

/* ---------------------------------------------------- stand-in greeting */

/* chat-lib.iife_v3.js withholds its opening message behind a hard-coded 10s
   setTimeout, so the transcript is genuinely empty for the first 10 seconds
   after page load. dai-chatbot-init.js drops this stand-in in until the real
   one arrives. It carries the vendor's own `message bot bubble` classes, so it
   inherits their exact styling; only the placement is ours.
   Scoped with .daiChat so it outweighs the vendor's `.message.bubble`, which sets
   position: relative — at one class it lost, and the bubble sat in normal flow and
   overflowed the panel. Anchored to the bottom so it sits where the real first
   message will, just above the input. */
.daiChat .daiChat__greeting {
	position: absolute;
	top: auto;
	bottom: var(--daiChat-transcript-bottom);
	left: 16px;
	/* shrink-to-fit and cap where the real bot bubble caps (88% of the transcript
	   box), so swapping one for the other does not change the bubble's shape */
	right: auto;
	max-width: 80%;
	z-index: 3;
	margin: 0;
}

/* chat-lib's greeting is on a 10s timer that ignores whether the conversation has
   already started, so it can drop in ABOVE the visitor's first message. Once they
   have said something it is stale — hide it. It is always the first child when
   present, and a real transcript always opens with the visitor's own message. */
.daiChat.is-conversing .chat-messages > .message.bot.bubble:first-child {
	display: none;
}

/* ------------------------------------------------- touch devices (tablet) */

/* Above the mobile breakpoint a tablet still gets the docked panel, but shorter:
   the on-screen keyboard claims a chunk of the viewport as soon as the visitor
   taps the field, and a 560px panel leaves nothing above it. */
@media (min-width: 769px) and (pointer: coarse) {
	.daiChat__panel {
		max-height: min(460px, calc(100vh - var(--daiChat-nav-h) - var(--daiChat-gap) - var(--daiChat-size) - var(--daiChat-nav-clear) - 16px));
		max-height: min(460px, calc(100dvh - var(--daiChat-nav-h) - var(--daiChat-gap) - var(--daiChat-size) - var(--daiChat-nav-clear) - 16px));
	}
}

/* ------------------------------------------------------------------ mobile */

@media (max-width: 768px) {
	.daiChat {
		--daiChat-gap: 16px;
		--daiChat-size: 54px;
	}

	.daiChat__panel {
		right: 0;
		left: 0;
		bottom: 0;
		width: 100%;
		/* An explicit height: with height:auto the messages area collapses and the
		   sheet shrinks to just the input row. Capped so the sheet stops below the
		   header however short the screen is (landscape phones especially). The
		   launcher is hidden while the sheet is open, so it needs no allowance. */
		max-height: min(62vh, calc(100vh - var(--daiChat-nav-h) - var(--daiChat-nav-clear)));
		max-height: min(62dvh, calc(100dvh - var(--daiChat-nav-h) - var(--daiChat-nav-clear)));
		border-radius: var(--daiChat-radius) var(--daiChat-radius) 0 0;
		border-inline: 0;
		border-bottom: 0;
		transform-origin: bottom center;
	}

	/* The sheet covers the corner, so the bubble would sit on top of it. */
	.daiChat.is-open .daiChat__launcher {
		display: none;
	}

	.daiChat .daiChat__close {
		display: grid;
		place-items: center;
	}

	/* room for the floating close button */
	.daiChat .chat-widget {
		padding-top: 44px;
	}

	.daiChat .message {
		max-width: 92%;
	}
}

/* ------------------------------------------- touch: grow with the conversation */

/* On phone and tablet the panel opens only as tall as it needs to be and grows as
   messages arrive, up to the ceilings set above. dai-chatbot-init.js measures the
   transcript and writes --daiChat-panel-h; the 100% fallback means that before that
   first measurement (or if the script fails) the panel simply fills to max-height,
   which is the old behaviour. The vendor widget is absolutely positioned inside the
   panel, so it contributes no height of its own — hence measuring in JS rather than
   letting the box size itself. */
@media (pointer: coarse) {
	.daiChat__panel {
		height: var(--daiChat-panel-h, 100%);
		transition: opacity .24s var(--daiChat-ease),
			transform .24s var(--daiChat-ease),
			visibility 0s linear .24s,
			height .3s var(--daiChat-ease);
	}

	.daiChat.is-open .daiChat__panel {
		transition: opacity .24s var(--daiChat-ease),
			transform .24s var(--daiChat-ease),
			visibility 0s,
			height .3s var(--daiChat-ease);
	}
}

/* --------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {

	.daiChat .daiChat__launcher,
	.daiChat .daiChat__icon--close,
	.daiChat .daiChat__mark,
	.daiChat .daiChat__label,
	.daiChat .daiChat__close,
	.daiChat .chat-input,
	.daiChat .chat-input button {
		transition: none;
	}

	.daiChat .daiChat__launcher:hover,
	.daiChat .daiChat__launcher:active,
	.daiChat .chat-input button:hover {
		transform: none;
	}

	/* Border stays — it is the CTA's edge, not decoration. Just stop it travelling. */
	.daiChat .daiChat__launcher::before {
		animation: none;
	}

	.daiChat__panel,
	.daiChat.is-open .daiChat__panel {
		transition: none;
		transform: none;
	}
}
