/*
 * SaveBar.
 *
 * Plain CSS with no preprocessor variables, on purpose. The rules this replaces
 * lived in a 1300-line shared stylesheet, in twenty blocks between line 38 and
 * line 1237, depending on seventeen SCSS variables from a sibling file - so
 * moving the component meant hunting the rules, and changing the component
 * meant hoping nothing else used them.
 *
 * Everything tunable is a custom property with a fallback, so a host can theme
 * it from one declaration and needs no build step to consume it.
 */

.savebar,
.savebar-list {
	--savebar-accent: #4f46e5;
	--savebar-accent-hover: #4338ca;
	--savebar-radius: 16px;
	--savebar-surface: rgba( 255, 255, 255, 0.85 );
	--savebar-ring: rgba( 15, 23, 42, 0.08 );
	--savebar-shadow: 0 18px 45px -14px rgba( 15, 23, 42, 0.4 ), 0 2px 8px -3px rgba( 15, 23, 42, 0.15 );
	--savebar-text: #0f172a;
	--savebar-muted: #475569;
	--savebar-font: inherit;
}

.savebar *,
.savebar-list * {
	box-sizing: border-box;
}

/* ------------------------------------------------------------------ island -- */

.savebar {
	position: sticky;
	bottom: 16px;
	z-index: 20;
	box-sizing: border-box;

	/* Hugs its content. A message about a temporary state is not a toolbar, so
	   it is not the width of one. */
	display: flex;
	align-items: center;
	gap: 8px;
	width: fit-content;
	max-width: 100%;
	min-height: 52px;
	margin: 24px 0 0;
	padding: 8px 8px 8px 16px;
	border: 0;
	border-radius: var( --savebar-radius );

	/* Frosted, so content stays legible as it scrolls underneath. */
	background: var( --savebar-surface );
	backdrop-filter: saturate( 180% ) blur( 20px );
	box-shadow: 0 0 0 1px var( --savebar-ring ), var( --savebar-shadow );
	font-family: var( --savebar-font );
	animation: savebar-in 200ms cubic-bezier( 0.22, 1, 0.36, 1 );
}

/* Centred against the column it sits in, not the viewport, so it stays inside
   the host's own frame. */
.savebar--center {
	margin-left: auto;
	margin-right: auto;
}

.savebar--end {
	margin-left: auto;
}

.savebar.is-leaving {
	opacity: 0;
	transform: translateY( 8px );
	transition: opacity 320ms ease, transform 320ms ease;
}

@keyframes savebar-in {

	from {
		opacity: 0;
		transform: translateY( 8px );
	}

	to {
		opacity: 1;
		transform: translateY( 0 );
	}
}

/* --------------------------------------------------------------------- dot -- */

.savebar__dot {
	position: relative;
	display: flex;
	flex: 0 0 auto;
	width: 8px;
	height: 8px;
}

.savebar__ping {
	position: absolute;
	inset: 0;
	border-radius: 999px;
	background: var( --savebar-accent );
	opacity: 0.55;
	animation: savebar-ping 1600ms cubic-bezier( 0, 0, 0.2, 1 ) infinite;
}

.savebar__core {
	position: relative;
	width: 8px;
	height: 8px;
	border-radius: 999px;
	background: var( --savebar-accent );
}

@keyframes savebar-ping {

	75%,
	100% {
		transform: scale( 2.2 );
		opacity: 0;
	}
}

/* ---------------------------------------------------------------- controls -- */

.savebar__count,
.savebar__discard,
.savebar__save {
	/* Nothing may shrink below its own text: a flex item is allowed to, and the
	   result is a label wrapping onto three lines inside a pill. */
	flex: 0 0 auto;
	white-space: nowrap;
	font-family: inherit;
	cursor: pointer;
}

.savebar__count {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 8px 6px;
	border: 0;
	border-radius: 8px;
	background: transparent;
	font-size: 14px;
	font-weight: 600;
	line-height: 20px;
	color: var( --savebar-text );
	transition: background-color 150ms ease;
}

.savebar__count:hover,
.savebar__count:focus-visible {
	background: rgba( 15, 23, 42, 0.05 );
}

.savebar__count:focus-visible,
.savebar__discard:focus-visible,
.savebar__save:focus-visible {
	outline: 2px solid var( --savebar-accent );
	outline-offset: 1px;
}

/* Points right when closed, down when open: the one convention a reader does
   not have to be taught. The 1px is the nudge every labelled icon needs - a
   line box holds its letters low, so a centred glyph reads high beside them. */
.savebar__chevron {
	flex: 0 0 auto;
	transform: translateY( 1px );
	color: #94a3b8;
	transition: transform 200ms ease;
}

.savebar__chevron.is-open {
	transform: translateY( 1px ) rotate( 180deg );
}

.savebar__sep {
	flex: 0 0 auto;
	width: 1px;
	height: 20px;
	margin: 0 2px;
	background: #e2e8f0;
}

.savebar__discard {
	padding: 9px 12px;
	border: 0;
	border-radius: 10px;
	background: transparent;
	font-size: 13px;
	font-weight: 500;
	color: var( --savebar-muted );
	transition: background-color 150ms ease, color 150ms ease;
}

.savebar__discard:hover {
	background: rgba( 15, 23, 42, 0.06 );
	color: var( --savebar-text );
}

.savebar__save {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	padding: 10px 16px;
	border: 0;
	border-radius: 10px;
	background: var( --savebar-accent );
	font-size: 13px;
	font-weight: 600;
	color: #fff;
	transition: background-color 150ms ease, transform 150ms ease;
}

.savebar__save:hover {
	background: var( --savebar-accent-hover );
	color: #fff;
	transform: translateY( -1px );
}

.savebar__save:active {
	transform: translateY( 0 );
}

/* Disabled is the saving state, so it stays readable: it is on screen while
   somebody waits, and an unreadable button during a wait reads as a failure. */
.savebar__save:disabled,
.savebar__discard:disabled {
	opacity: 0.65;
	cursor: not-allowed;
	transform: none;
}

.savebar__save svg {
	transform: translateY( 1px );
}

.savebar__spin {
	animation: savebar-spin 700ms linear infinite;
}

@keyframes savebar-spin {

	to {
		transform: translateY( 1px ) rotate( 360deg );
	}
}

/* ------------------------------------------------------------ confirmation -- */

/*
 * Saving and discarding end in different words and different colours. The one
 * thing a confirmation must never be is ambiguous about which of them just
 * happened.
 */
.savebar__flash {
	display: flex;
	align-items: center;
	gap: 9px;
	margin: 0;
	padding: 6px 4px;
	font-size: 14px;
	font-weight: 600;
	line-height: 20px;
	color: var( --savebar-text );
}

.savebar__badge {
	display: grid;
	place-items: center;
	flex: 0 0 auto;
	width: 22px;
	height: 22px;
	border-radius: 7px;
}

.savebar__badge.is-saved {
	background: #ecfdf3;
	color: #067647;
	box-shadow: inset 0 0 0 1px #abefc6;
}

.savebar__badge.is-reverted {
	background: #f1f5f9;
	color: #475569;
	box-shadow: inset 0 0 0 1px #e2e8f0;
}

/* --------------------------------------------------------------- the list -- */

.savebar-list {
	position: sticky;
	bottom: 82px;
	z-index: 21;
	box-sizing: border-box;
	width: fit-content;
	min-width: 280px;
	max-width: min( 560px, 100% );
	max-height: 210px;

	/* Pulled back over the island so the two touch rather than stacking. */
	margin: 0 0 -68px;
	padding: 12px 14px;
	overflow-y: auto;
	border-radius: var( --savebar-radius );
	background: rgba( 255, 255, 255, 0.92 );
	backdrop-filter: saturate( 180% ) blur( 20px );
	box-shadow: 0 0 0 1px var( --savebar-ring ), var( --savebar-shadow );
	font-family: var( --savebar-font );
	animation: savebar-in 200ms cubic-bezier( 0.22, 1, 0.36, 1 );
}

.savebar-list.savebar--center {
	margin-left: auto;
	margin-right: auto;
}

.savebar-list.savebar--end {
	margin-left: auto;
}

.savebar-list ul {
	display: flex;
	flex-direction: column;
	gap: 9px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.savebar-list li {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 7px;
	margin: 0;
	font-size: 12.5px;
	line-height: 17px;
}

.savebar-list__field {
	font-weight: 600;
	color: #1e293b;
}

/* Struck through, because the row is a before and an after and only one of them
   is still true. */
.savebar-list__from {
	color: #94a3b8;
	text-decoration: line-through;
}

.savebar-list__arrow {
	color: #cbd5e1;
}

.savebar-list__to {
	font-weight: 600;
	color: var( --savebar-accent );
}

/* ------------------------------------------------------------- adaptations -- */

/*
 * Touch targets. The resting heights suit a dense admin read with a mouse and
 * clear WCAG 2.2 AA, whose 2.5.8 minimum is 24px. They do not clear the 44px
 * 2.5.5 and the platform guidelines ask for.
 *
 * Keyed on pointer first and width second, because the question is what
 * somebody is touching the screen with, not how wide the screen is.
 */
@media ( pointer: coarse ), ( max-width: 782px ) {

	.savebar__count,
	.savebar__discard,
	.savebar__save {
		min-height: 44px;
	}
}

/* Below this the content cannot fit beside the page gutters, so the island
   stops hugging and spans the column. Still a pill, just a wider one. */
@media ( max-width: 600px ) {

	.savebar {
		width: auto;
		margin-left: 0;
		margin-right: 0;
		padding-left: 12px;
	}

	.savebar-list {
		width: auto;
		min-width: 0;
		margin-left: 0;
		margin-right: 0;
	}
}

@media ( prefers-reduced-motion: reduce ) {

	.savebar,
	.savebar-list,
	.savebar__ping,
	.savebar__spin,
	.savebar__chevron,
	.savebar__count,
	.savebar__discard,
	.savebar__save {
		animation: none;
		transition: none;
	}

	.savebar.is-leaving {
		transition: none;
	}
}
