/* Bin Auf component library (§9.0).
 *
 * Every value here is a token. If you find yourself typing a hex code or a pixel
 * value, the token set is missing something - add it there, not here.
 *
 * Constraints that run through the whole file:
 *
 * 1. RTL is a layout property, not a translation task. Everything uses logical
 *    properties (inline-start, margin-inline, padding-block) so Arabic and Urdu
 *    mirror without a single [dir="rtl"] override.
 * 2. Tap targets are a size, not a style. --ba-target-min is the floor
 *    everywhere; technician and gate surfaces raise it.
 * 3. Every list has an empty state, every async thing has a skeleton, and
 *    offline is calm rather than alarming - it is normal operation in a plant
 *    room, not an error.
 * 4. Motion respects prefers-reduced-motion. Always.
 * 5. Never inherit colour. These surfaces used to extend Frappe's website
 *    template, which colours h1-h6 with its own variable on its own light/dark
 *    switch - two theme systems on one page disagree, and a heading gets
 *    painted onto a surface of the same colour. They now render standalone
 *    (binauf/templates/standalone.html), but the rules below still state colour
 *    explicitly: the library has to survive being embedded somewhere hostile.
 * 6. Glass is a surface, not a filter over unknown content. Translucency lowers
 *    contrast by definition, so every glass surface pairs with an explicit text
 *    colour and an opaque fallback where backdrop-filter is unsupported.
 */

*,
*::before,
*::after {
	box-sizing: border-box;
}

/* --------------------------------------------------------------------------
 * Page
 * ----------------------------------------------------------------------- */
/* Ambient colour behind the glass. Without something to refract, frosted panels
 * read as flat grey boxes - the blur needs a gradient to be worth its cost.
 *
 * Painted as a background-image on the element rather than a ::before overlay.
 * An overlay would have needed z-index:-1 to sit behind the content, and on
 * <body> that puts it behind the background that <body> propagates to the
 * canvas - so it renders, and is never visible. Fixed attachment keeps one
 * continuous field behind everything as the page scrolls.
 */
.ba-page {
	margin: 0;
	min-height: 100vh;
	background-color: var(--ba-bg-subtle);
	background-image:
		radial-gradient(48rem 32rem at 12% -8%, color-mix(in srgb, var(--ba-accent) 22%, transparent), transparent 70%),
		radial-gradient(40rem 30rem at 92% 4%, color-mix(in srgb, var(--ba-status-info) 16%, transparent), transparent 68%),
		radial-gradient(52rem 40rem at 68% 108%, color-mix(in srgb, var(--ba-status-success) 12%, transparent), transparent 72%);
	background-attachment: fixed;
	color: var(--ba-text);
	font-family: var(--ba-font-sans);
	font-size: var(--ba-text-base);
	line-height: var(--ba-leading-base);
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

/* The gate theme gets no ambience and no glass. Translucency costs contrast,
 * and a guard reading a tablet in direct sunlight cannot afford any of it. */
[data-theme="contrast"] .ba-page {
	background-image: none;
}

/* Colour restated for every element a host stylesheet is likely to name.
 * Inheritance is not safe when this library is a guest. */
.ba-page h1,
.ba-page h2,
.ba-page h3,
.ba-page h4,
.ba-page h5,
.ba-page h6,
.ba-page p,
.ba-page li,
.ba-page td,
.ba-page th,
.ba-page dt,
.ba-page dd,
.ba-page label,
.ba-page strong,
.ba-page code,
.ba-page figcaption {
	color: var(--ba-text);
}

.ba-page a {
	color: var(--ba-accent);
	text-decoration-thickness: 1px;
	text-underline-offset: 2px;
	transition: opacity var(--ba-duration-fast) var(--ba-ease-standard);
}

.ba-page a:hover {
	opacity: 0.75;
}

.ba-page hr {
	border: 0;
	border-top: 1px solid var(--ba-border);
	margin-block: var(--ba-space-5);
}

.ba-container {
	inline-size: min(100% - var(--ba-space-5) * 2, 72rem);
	margin-inline: auto;
	padding-block: var(--ba-space-6);
}

.ba-stack {
	display: flex;
	flex-direction: column;
	gap: var(--ba-space-4);
}

.ba-grid {
	display: grid;
	gap: var(--ba-space-4);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}

/* Wide content scrolls inside its own box. The page body must never scroll
 * sideways - on a phone that turns every vertical swipe into a fight. */
.ba-scroll-x {
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
	overscroll-behavior-x: contain;
}

/* --------------------------------------------------------------------------
 * Motion
 *
 * Entrances stagger so a list resolves as a sequence rather than a flash.
 * Nothing blocking animates: an animation that delays a gate decision or a
 * technician's job state is a cost, not a delight.
 * ----------------------------------------------------------------------- */
@keyframes ba-rise {
	from { opacity: 0; transform: translateY(12px); }
	to { opacity: 1; transform: none; }
}

@keyframes ba-fade {
	from { opacity: 0; }
	to { opacity: 1; }
}

@keyframes ba-sheet-in {
	from { transform: translateY(100%); }
	to { transform: none; }
}

@keyframes ba-pop {
	from { opacity: 0; transform: scale(0.96); }
	to { opacity: 1; transform: none; }
}

@keyframes ba-shimmer {
	to { background-position-x: -200%; }
}

@keyframes ba-spin {
	to { transform: rotate(1turn); }
}

@keyframes ba-pulse-ring {
	0% { box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 40%, transparent); }
	70% { box-shadow: 0 0 0 10px transparent; }
	100% { box-shadow: 0 0 0 0 transparent; }
}

.ba-animate-in {
	animation: ba-rise var(--ba-duration-slow) var(--ba-ease-decelerate) both;
}

/* Stagger helper: each child waits its turn. Capped at 8 so a hundred-row list
 * does not make the last row arrive a minute late. */
.ba-stagger > * {
	animation: ba-rise var(--ba-duration-slow) var(--ba-ease-decelerate) both;
}
.ba-stagger > *:nth-child(1) { animation-delay: 0ms; }
.ba-stagger > *:nth-child(2) { animation-delay: 40ms; }
.ba-stagger > *:nth-child(3) { animation-delay: 80ms; }
.ba-stagger > *:nth-child(4) { animation-delay: 120ms; }
.ba-stagger > *:nth-child(5) { animation-delay: 160ms; }
.ba-stagger > *:nth-child(6) { animation-delay: 200ms; }
.ba-stagger > *:nth-child(7) { animation-delay: 240ms; }
.ba-stagger > *:nth-child(n + 8) { animation-delay: 280ms; }

/* Not a nicety. Vestibular disorders make large motion genuinely painful, and
 * this is one media query. Opacity survives so state changes stay legible. */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 1ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 1ms !important;
		scroll-behavior: auto !important;
	}
}

/* --------------------------------------------------------------------------
 * Glass
 * ----------------------------------------------------------------------- */
.ba-glass {
	background: var(--ba-glass);
	border: 1px solid var(--ba-glass-border);
	box-shadow: var(--ba-shadow-glass);
	-webkit-backdrop-filter: blur(var(--ba-blur-md)) saturate(160%);
	backdrop-filter: blur(var(--ba-blur-md)) saturate(160%);
}

/* Without backdrop-filter the translucency alone would leave text competing
 * with whatever sits behind it. Fall back to the opaque surface. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
	.ba-glass,
	.ba-card,
	.ba-sheet,
	.ba-modal,
	.ba-topbar,
	.ba-tabs {
		background: var(--ba-surface);
	}
}

/* --------------------------------------------------------------------------
 * Typography
 * ----------------------------------------------------------------------- */
.ba-h1 {
	margin: 0 0 var(--ba-space-3);
	font-size: var(--ba-text-3xl);
	line-height: var(--ba-leading-3xl);
	font-weight: var(--ba-weight-bold);
	letter-spacing: -0.02em;
	color: var(--ba-text);
}

.ba-h2 {
	margin: 0 0 var(--ba-space-3);
	font-size: var(--ba-text-xl);
	line-height: var(--ba-leading-xl);
	font-weight: var(--ba-weight-semibold);
	letter-spacing: -0.01em;
	color: var(--ba-text);
}

/* Reserved for headline numbers, not decoration. The solid colour underneath is
 * what shows if the background-clip fails. */
.ba-gradient-text {
	color: var(--ba-accent);
	background: linear-gradient(
		100deg,
		var(--ba-accent),
		color-mix(in srgb, var(--ba-accent) 55%, var(--ba-status-success))
	);
	-webkit-background-clip: text;
	background-clip: text;
}

@supports (-webkit-background-clip: text) or (background-clip: text) {
	.ba-gradient-text {
		-webkit-text-fill-color: transparent;
		color: transparent;
	}
}

.ba-page .ba-muted {
	color: var(--ba-text-muted);
}

.ba-page .ba-label {
	display: block;
	margin-block-end: var(--ba-space-1);
	font-size: var(--ba-text-xs);
	line-height: var(--ba-leading-xs);
	font-weight: var(--ba-weight-semibold);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--ba-text-muted);
}

/* Tabular figures so a column of money lines up on the decimal. Money that
 * jitters between rows reads as untrustworthy even when it is correct. */
.ba-num {
	font-variant-numeric: tabular-nums;
	font-feature-settings: "tnum" 1;
}

.ba-glance {
	font-size: var(--ba-text-glance);
	line-height: var(--ba-leading-glance);
	font-weight: var(--ba-weight-bold);
	letter-spacing: -0.03em;
	font-variant-numeric: tabular-nums;
	color: var(--ba-text);
}

/* --------------------------------------------------------------------------
 * Top bar
 * ----------------------------------------------------------------------- */
.ba-topbar {
	position: sticky;
	top: 0;
	z-index: 20;
	display: flex;
	align-items: center;
	gap: var(--ba-space-4);
	padding: var(--ba-space-3) var(--ba-space-5);
	background: var(--ba-glass-strong);
	border-block-end: 1px solid var(--ba-glass-border);
	-webkit-backdrop-filter: blur(var(--ba-blur-lg)) saturate(180%);
	backdrop-filter: blur(var(--ba-blur-lg)) saturate(180%);
}

.ba-page .ba-topbar__title {
	margin: 0;
	font-size: var(--ba-text-lg);
	font-weight: var(--ba-weight-semibold);
	color: var(--ba-text);
}

.ba-topbar__spacer {
	margin-inline-start: auto;
}

/* --------------------------------------------------------------------------
 * Card
 * ----------------------------------------------------------------------- */
.ba-card {
	position: relative;
	padding: var(--ba-space-5);
	border-radius: var(--ba-radius-xl);
	background: var(--ba-glass);
	border: 1px solid var(--ba-glass-border);
	box-shadow: var(--ba-shadow-glass);
	color: var(--ba-text);
	-webkit-backdrop-filter: blur(var(--ba-blur-md)) saturate(160%);
	backdrop-filter: blur(var(--ba-blur-md)) saturate(160%);
	transition:
		transform var(--ba-duration-base) var(--ba-ease-standard),
		box-shadow var(--ba-duration-base) var(--ba-ease-standard),
		border-color var(--ba-duration-base) var(--ba-ease-standard);
}

/* The lit top edge. This is what separates glass from a translucent rectangle -
 * it implies a light source and gives the panel a physical edge. */
.ba-card::after {
	content: "";
	position: absolute;
	inset-block-start: 0;
	inset-inline: var(--ba-space-5);
	block-size: 1px;
	background: linear-gradient(90deg, transparent, var(--ba-glass-shine), transparent);
	pointer-events: none;
}

/* Lift only where a pointer can hover. On touch this would stick after a tap
 * and leave a card raised with nothing hovering it. */
@media (hover: hover) {
	.ba-card--interactive:hover {
		transform: translateY(-3px);
		box-shadow: var(--ba-shadow-lift);
		border-color: color-mix(in srgb, var(--ba-accent) 35%, var(--ba-glass-border));
	}
}

.ba-page .ba-card__title {
	margin: 0 0 var(--ba-space-2);
	font-size: var(--ba-text-sm);
	font-weight: var(--ba-weight-semibold);
	color: var(--ba-text-muted);
}

.ba-card--flat {
	background: var(--ba-surface);
	border-color: var(--ba-border);
	box-shadow: var(--ba-shadow-1);
	-webkit-backdrop-filter: none;
	backdrop-filter: none;
}

.ba-card--success {
	border-inline-start: 3px solid var(--ba-status-success);
}

.ba-card--danger {
	border-inline-start: 3px solid var(--ba-status-danger);
}

/* --------------------------------------------------------------------------
 * Buttons
 * ----------------------------------------------------------------------- */
.ba-btn {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--ba-space-2);
	min-block-size: var(--ba-target-min);
	padding-inline: var(--ba-space-5);
	padding-block: var(--ba-space-2);
	border: 1px solid var(--ba-border);
	border-radius: var(--ba-radius-full);
	background: var(--ba-surface);
	color: var(--ba-text);
	font: inherit;
	font-weight: var(--ba-weight-medium);
	cursor: pointer;
	overflow: hidden;
	isolation: isolate;
	transition:
		transform var(--ba-duration-fast) var(--ba-ease-spring),
		box-shadow var(--ba-duration-base) var(--ba-ease-standard),
		filter var(--ba-duration-base) var(--ba-ease-standard);
}

@media (hover: hover) {
	.ba-btn:hover {
		filter: brightness(1.06);
		box-shadow: var(--ba-shadow-2);
	}
}

/* Presses land instantly. The spring on release makes the control feel physical
 * without adding any delay to the action itself. */
.ba-btn:active {
	transform: scale(0.97);
	transition-duration: var(--ba-duration-instant);
}

/* :focus-visible only, so a mouse click leaves no ring but a keyboard user
 * never loses their place. */
.ba-btn:focus-visible,
.ba-input:focus-visible,
.ba-tab:focus-visible,
.ba-row--tappable:focus-visible {
	outline: 2px solid var(--ba-accent);
	outline-offset: 2px;
	box-shadow: var(--ba-shadow-glow);
}

.ba-btn[disabled],
.ba-btn[aria-disabled="true"] {
	opacity: 0.5;
	cursor: not-allowed;
	transform: none;
	filter: none;
}

.ba-page .ba-btn--primary {
	background: linear-gradient(
		135deg,
		var(--ba-accent),
		color-mix(in srgb, var(--ba-accent) 78%, var(--ba-brand-800))
	);
	border-color: transparent;
	color: var(--ba-accent-text);
	box-shadow: 0 6px 18px color-mix(in srgb, var(--ba-accent) 32%, transparent);
}

/* Sheen sweep on hover. Purely decorative, so it sits behind the label and
 * never intercepts a click. */
.ba-btn--primary::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background: linear-gradient(100deg, transparent 20%, var(--ba-glass-shine) 50%, transparent 80%);
	transform: translateX(-120%);
	transition: transform var(--ba-duration-slower) var(--ba-ease-standard);
}

@media (hover: hover) {
	.ba-btn--primary:hover::before {
		transform: translateX(120%);
	}
}

.ba-page .ba-btn--danger {
	background: var(--ba-status-danger);
	border-color: transparent;
	color: #ffffff;
}

.ba-btn--ghost {
	background: transparent;
	border-color: transparent;
}

.ba-btn--block {
	display: flex;
	inline-size: 100%;
}

/* A button that is doing something says so. Without this, a slow request looks
 * like a button that did not register the tap, and people press it again. */
.ba-btn[data-busy="true"] {
	pointer-events: none;
	color: transparent;
}

.ba-btn[data-busy="true"]::after {
	content: "";
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: 50%;
	inline-size: 1.1em;
	block-size: 1.1em;
	margin-block-start: -0.55em;
	margin-inline-start: -0.55em;
	border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
	border-top-color: var(--ba-accent);
	border-radius: var(--ba-radius-full);
	animation: ba-spin 700ms linear infinite;
}

/* Bigger targets where hands are gloved or a car is waiting. */
.ba-surface--technician .ba-btn {
	min-block-size: var(--ba-target-technician);
}

.ba-surface--gate .ba-btn {
	min-block-size: var(--ba-target-gate);
	font-size: var(--ba-text-lg);
}

/* --------------------------------------------------------------------------
 * Fields
 * ----------------------------------------------------------------------- */
.ba-field {
	display: flex;
	flex-direction: column;
	gap: var(--ba-space-1);
}

.ba-page .ba-field > label {
	font-size: var(--ba-text-sm);
	font-weight: var(--ba-weight-medium);
	color: var(--ba-text-muted);
}

.ba-input {
	inline-size: 100%;
	min-block-size: var(--ba-target-min);
	padding: var(--ba-space-2) var(--ba-space-3);
	border: 1px solid var(--ba-border);
	border-radius: var(--ba-radius-lg);
	background: var(--ba-surface);
	color: var(--ba-text);
	font: inherit;
	transition:
		border-color var(--ba-duration-base) var(--ba-ease-standard),
		box-shadow var(--ba-duration-base) var(--ba-ease-standard);
}

.ba-input::placeholder {
	color: var(--ba-text-muted);
	opacity: 1; /* Firefox dims placeholders further; this keeps them legible. */
}

.ba-input:focus {
	outline: none;
	border-color: var(--ba-accent);
	box-shadow: var(--ba-shadow-glow);
}

.ba-page .ba-field__hint {
	font-size: var(--ba-text-xs);
	color: var(--ba-text-muted);
}

/* Errors are announced by colour *and* text. Colour alone excludes roughly one
 * in twelve men from knowing the field failed. */
.ba-page .ba-field__error {
	font-size: var(--ba-text-xs);
	font-weight: var(--ba-weight-medium);
	color: var(--ba-status-danger);
}

.ba-field--error .ba-input {
	border-color: var(--ba-status-danger);
}

/* --------------------------------------------------------------------------
 * Pills
 * ----------------------------------------------------------------------- */
.ba-pill {
	display: inline-flex;
	align-items: center;
	gap: var(--ba-space-1);
	padding: 2px var(--ba-space-3);
	border: 1px solid transparent;
	border-radius: var(--ba-radius-full);
	font-size: var(--ba-text-xs);
	line-height: var(--ba-leading-xs);
	font-weight: var(--ba-weight-semibold);
	white-space: nowrap;
	animation: ba-pop var(--ba-duration-base) var(--ba-ease-spring) both;
}

.ba-page .ba-pill--success {
	color: var(--ba-status-success);
	background: color-mix(in srgb, var(--ba-status-success) 14%, transparent);
	border-color: color-mix(in srgb, var(--ba-status-success) 30%, transparent);
}

.ba-page .ba-pill--warning {
	color: var(--ba-status-warning);
	background: color-mix(in srgb, var(--ba-status-warning) 14%, transparent);
	border-color: color-mix(in srgb, var(--ba-status-warning) 30%, transparent);
}

.ba-page .ba-pill--danger {
	color: var(--ba-status-danger);
	background: color-mix(in srgb, var(--ba-status-danger) 14%, transparent);
	border-color: color-mix(in srgb, var(--ba-status-danger) 30%, transparent);
}

.ba-page .ba-pill--info {
	color: var(--ba-status-info);
	background: color-mix(in srgb, var(--ba-status-info) 14%, transparent);
	border-color: color-mix(in srgb, var(--ba-status-info) 30%, transparent);
}

.ba-page .ba-pill--muted {
	color: var(--ba-text-muted);
	background: color-mix(in srgb, var(--ba-text-muted) 12%, transparent);
	border-color: color-mix(in srgb, var(--ba-text-muted) 25%, transparent);
}

/* A live indicator earns its animation - it is the difference between "this is
 * current" and "this may be stale". */
.ba-pill--live::before {
	content: "";
	inline-size: 6px;
	block-size: 6px;
	border-radius: var(--ba-radius-full);
	background: currentColor;
	animation: ba-pulse-ring 2s var(--ba-ease-standard) infinite;
}

/* --------------------------------------------------------------------------
 * Rows
 * ----------------------------------------------------------------------- */
.ba-row {
	display: flex;
	align-items: center;
	gap: var(--ba-space-3);
	padding: var(--ba-space-3) var(--ba-space-4);
	border: 1px solid var(--ba-border);
	border-radius: var(--ba-radius-lg);
	background: var(--ba-surface);
	color: var(--ba-text);
}

.ba-row__body {
	min-inline-size: 0; /* Lets long titles truncate instead of pushing meta out. */
	flex: 1;
}

.ba-page .ba-row__title {
	font-weight: var(--ba-weight-medium);
	color: var(--ba-text);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.ba-page .ba-row__meta {
	font-size: var(--ba-text-sm);
	color: var(--ba-text-muted);
}

.ba-row--tappable {
	inline-size: 100%;
	min-block-size: var(--ba-target-min);
	text-align: start;
	font: inherit;
	cursor: pointer;
	transition:
		transform var(--ba-duration-fast) var(--ba-ease-standard),
		background-color var(--ba-duration-fast) var(--ba-ease-standard),
		border-color var(--ba-duration-fast) var(--ba-ease-standard);
}

@media (hover: hover) {
	.ba-row--tappable:hover {
		background: var(--ba-glass-strong);
		border-color: color-mix(in srgb, var(--ba-accent) 40%, var(--ba-border));
		transform: translateX(2px);
	}
}

.ba-row--tappable:active {
	transform: scale(0.995);
}

/* --------------------------------------------------------------------------
 * Tabs
 * ----------------------------------------------------------------------- */
.ba-tabs {
	display: flex;
	gap: var(--ba-space-1);
	padding: var(--ba-space-1);
	border: 1px solid var(--ba-glass-border);
	border-radius: var(--ba-radius-full);
	background: var(--ba-glass);
	overflow-x: auto;
	scrollbar-width: none;
	-webkit-backdrop-filter: blur(var(--ba-blur-sm));
	backdrop-filter: blur(var(--ba-blur-sm));
}

.ba-tabs::-webkit-scrollbar {
	display: none;
}

.ba-tab {
	flex: 0 0 auto;
	min-block-size: var(--ba-target-min);
	padding-inline: var(--ba-space-4);
	border: 0;
	border-radius: var(--ba-radius-full);
	background: transparent;
	color: var(--ba-text-muted);
	font: inherit;
	font-weight: var(--ba-weight-medium);
	white-space: nowrap;
	cursor: pointer;
	transition:
		background-color var(--ba-duration-base) var(--ba-ease-standard),
		color var(--ba-duration-base) var(--ba-ease-standard);
}

/* aria-current is the selector, so the styling cannot drift from what a screen
 * reader announces - they are the same fact. */
.ba-page .ba-tab[aria-current="true"] {
	background: var(--ba-surface);
	color: var(--ba-text);
	box-shadow: var(--ba-shadow-1);
}

/* --------------------------------------------------------------------------
 * Tables
 * ----------------------------------------------------------------------- */
.ba-table {
	inline-size: 100%;
	border-collapse: collapse;
	font-size: var(--ba-text-sm);
}

.ba-page .ba-table th {
	position: sticky;
	top: 0;
	padding: var(--ba-space-2) var(--ba-space-3);
	text-align: start;
	font-size: var(--ba-text-xs);
	font-weight: var(--ba-weight-semibold);
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--ba-text-muted);
	background: var(--ba-glass-strong);
	border-block-end: 1px solid var(--ba-border);
	-webkit-backdrop-filter: blur(var(--ba-blur-sm));
	backdrop-filter: blur(var(--ba-blur-sm));
}

.ba-page .ba-table td {
	padding: var(--ba-space-2) var(--ba-space-3);
	border-block-end: 1px solid var(--ba-border);
	color: var(--ba-text);
}

.ba-table tbody tr {
	transition: background-color var(--ba-duration-fast) var(--ba-ease-standard);
}

@media (hover: hover) {
	.ba-table tbody tr:hover {
		background: color-mix(in srgb, var(--ba-accent) 6%, transparent);
	}
}

/* --------------------------------------------------------------------------
 * Bars
 * ----------------------------------------------------------------------- */
.ba-bar__track {
	block-size: 8px;
	border-radius: var(--ba-radius-full);
	background: color-mix(in srgb, var(--ba-text-muted) 18%, transparent);
	overflow: hidden;
}

.ba-bar__fill {
	block-size: 100%;
	border-radius: var(--ba-radius-full);
	background: linear-gradient(
		90deg,
		var(--ba-accent),
		color-mix(in srgb, var(--ba-accent) 60%, var(--ba-status-success))
	);
	transition: inline-size var(--ba-duration-slower) var(--ba-ease-decelerate);
}

.ba-bar__fill--muted {
	background: var(--ba-text-muted);
}

.ba-bar__fill--overlay {
	background: var(--ba-status-warning);
}

/* --------------------------------------------------------------------------
 * Banners
 * ----------------------------------------------------------------------- */
.ba-banner {
	display: flex;
	align-items: center;
	gap: var(--ba-space-3);
	padding: var(--ba-space-3) var(--ba-space-4);
	border: 1px solid transparent;
	border-radius: var(--ba-radius-lg);
	font-size: var(--ba-text-sm);
	font-weight: var(--ba-weight-medium);
	animation: ba-rise var(--ba-duration-base) var(--ba-ease-decelerate) both;
}

/* The offline banner once rendered white on white because its background token
 * did not exist. Both colours are stated here, together, deliberately. */
.ba-page .ba-banner--offline {
	color: #ffffff;
	background: var(--ba-status-offline);
	border-color: var(--ba-status-offline);
}

.ba-page .ba-banner--error {
	color: #ffffff;
	background: var(--ba-status-danger);
	border-color: var(--ba-status-danger);
}

/* --------------------------------------------------------------------------
 * Skeletons and empty states
 * ----------------------------------------------------------------------- */
.ba-skeleton {
	min-block-size: 1em;
	border-radius: var(--ba-radius-md);
	background: linear-gradient(
			90deg,
			color-mix(in srgb, var(--ba-text-muted) 12%, transparent) 25%,
			color-mix(in srgb, var(--ba-text-muted) 22%, transparent) 37%,
			color-mix(in srgb, var(--ba-text-muted) 12%, transparent) 63%
		)
		0 0 / 200% 100%;
	animation: ba-shimmer 1.4s linear infinite;
}

.ba-page .ba-empty {
	padding: var(--ba-space-7) var(--ba-space-5);
	text-align: center;
	color: var(--ba-text-muted);
	animation: ba-fade var(--ba-duration-slow) var(--ba-ease-standard) both;
}

.ba-page .ba-empty__title {
	margin-block-end: var(--ba-space-2);
	font-size: var(--ba-text-lg);
	font-weight: var(--ba-weight-semibold);
	color: var(--ba-text);
}

/* --------------------------------------------------------------------------
 * Overlays
 * ----------------------------------------------------------------------- */
.ba-scrim {
	position: fixed;
	inset: 0;
	z-index: 40;
	background: var(--ba-veil);
	animation: ba-fade var(--ba-duration-base) var(--ba-ease-standard) both;
	-webkit-backdrop-filter: blur(var(--ba-blur-sm));
	backdrop-filter: blur(var(--ba-blur-sm));
}

.ba-modal {
	position: fixed;
	z-index: 50;
	inset-block-start: 50%;
	inset-inline-start: 50%;
	transform: translate(-50%, -50%);
	inline-size: min(100% - var(--ba-space-5) * 2, 32rem);
	max-block-size: 85vh;
	overflow-y: auto;
	padding: var(--ba-space-5);
	border: 1px solid var(--ba-glass-border);
	border-radius: var(--ba-radius-xl);
	background: var(--ba-glass-strong);
	box-shadow: var(--ba-shadow-lift);
	color: var(--ba-text);
	-webkit-backdrop-filter: blur(var(--ba-blur-lg)) saturate(180%);
	backdrop-filter: blur(var(--ba-blur-lg)) saturate(180%);
	animation: ba-pop var(--ba-duration-slow) var(--ba-ease-spring) both;
}

/* Bottom sheet: thumbs reach the bottom of a phone, not the middle. */
.ba-sheet {
	position: fixed;
	z-index: 50;
	inset-inline: 0;
	inset-block-end: 0;
	max-block-size: 85vh;
	overflow-y: auto;
	padding: var(--ba-space-5);
	padding-block-end: calc(var(--ba-space-5) + env(safe-area-inset-bottom, 0px));
	border-block-start: 1px solid var(--ba-glass-border);
	border-start-start-radius: var(--ba-radius-xl);
	border-start-end-radius: var(--ba-radius-xl);
	background: var(--ba-glass-strong);
	box-shadow: var(--ba-shadow-lift);
	color: var(--ba-text);
	-webkit-backdrop-filter: blur(var(--ba-blur-lg)) saturate(180%);
	backdrop-filter: blur(var(--ba-blur-lg)) saturate(180%);
	animation: ba-sheet-in var(--ba-duration-slow) var(--ba-ease-decelerate) both;
}

.ba-sheet__grip {
	inline-size: 36px;
	block-size: 4px;
	margin: 0 auto var(--ba-space-4);
	border-radius: var(--ba-radius-full);
	background: var(--ba-border);
}

/* --------------------------------------------------------------------------
 * Toasts
 *
 * Anchored to the bottom on phones, where a thumb is, and where a top toast
 * would collide with the notch. Never used for anything the user must act on -
 * a message that disappears on a timer cannot carry a decision.
 * ----------------------------------------------------------------------- */
.ba-toasts {
	position: fixed;
	z-index: 60;
	inset-inline: 0;
	inset-block-end: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--ba-space-2);
	padding: var(--ba-space-4);
	padding-block-end: calc(var(--ba-space-4) + env(safe-area-inset-bottom, 0px));
	pointer-events: none;
}

.ba-toast {
	display: flex;
	align-items: center;
	gap: var(--ba-space-2);
	max-inline-size: min(100%, 26rem);
	padding: var(--ba-space-3) var(--ba-space-4);
	border: 1px solid var(--ba-glass-border);
	border-radius: var(--ba-radius-full);
	background: var(--ba-glass-strong);
	box-shadow: var(--ba-shadow-lift);
	color: var(--ba-text);
	font-size: var(--ba-text-sm);
	font-weight: var(--ba-weight-medium);
	pointer-events: auto;
	-webkit-backdrop-filter: blur(var(--ba-blur-lg)) saturate(180%);
	backdrop-filter: blur(var(--ba-blur-lg)) saturate(180%);
	animation: ba-rise var(--ba-duration-slow) var(--ba-ease-spring) both;
}

.ba-toast::before {
	content: "";
	flex: 0 0 auto;
	inline-size: 8px;
	block-size: 8px;
	border-radius: var(--ba-radius-full);
	background: var(--ba-text-muted);
}

.ba-toast--success::before { background: var(--ba-status-success); }
.ba-toast--warning::before { background: var(--ba-status-warning); }
.ba-toast--danger::before { background: var(--ba-status-danger); }

.ba-toast[data-leaving="true"] {
	animation: ba-fade var(--ba-duration-base) var(--ba-ease-exit) reverse both;
}

/* --------------------------------------------------------------------------
 * Figures
 * ----------------------------------------------------------------------- */
.ba-figure {
	margin: 0;
}

.ba-page .ba-figure figcaption {
	margin-block-start: var(--ba-space-2);
	font-size: var(--ba-text-xs);
	color: var(--ba-text-muted);
}

/* --------------------------------------------------------------------------
 * Splash
 *
 * Covers the app while it works out who you are and fetches your first screen.
 * The logo is a detailed lockup rather than a glyph, so it is given room and
 * treated as artwork: it settles into place, a sheen passes over it once, and a
 * bar reports that something is happening.
 *
 * The rule that matters is not visual. A splash that fails to leave is worse
 * than no splash at all - the app is running underneath, unreachable, and looks
 * dead. So it is removed on success, on failure, and on a timeout, and the
 * markup carries `hidden` semantics rather than opacity alone.
 * ----------------------------------------------------------------------- */
.ba-splash {
	position: fixed;
	inset: 0;
	z-index: 100;
	display: grid;
	place-items: center;
	gap: var(--ba-space-5);
	padding: var(--ba-space-6);
	background: var(--ba-bg);
	/* Fades out; `display` is not animatable, so removal is done in script. */
	transition: opacity var(--ba-duration-slow) var(--ba-ease-standard);
}
.ba-splash[data-leaving="true"] { opacity: 0; pointer-events: none; }

.ba-splash__inner { display: grid; justify-items: center; gap: var(--ba-space-5); }

.ba-splash__logo {
	position: relative;
	inline-size: min(78vw, 22rem);
	overflow: hidden;
	animation: ba-splash-settle 900ms var(--ba-ease-spring) both;
}
.ba-splash__logo img { display: block; inline-size: 100%; block-size: auto; }

/* One pass, not a loop. A sheen that repeats reads as a loading spinner and
   stops being a flourish. */
.ba-splash__logo::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(
		100deg,
		transparent 35%,
		color-mix(in srgb, #fff 55%, transparent) 50%,
		transparent 65%
	);
	transform: translateX(-120%);
	animation: ba-splash-sheen 1400ms 500ms var(--ba-ease-standard) 1 forwards;
	pointer-events: none;
}

@keyframes ba-splash-settle {
	from { opacity: 0; transform: translateY(10px) scale(0.965); }
	to { opacity: 1; transform: none; }
}
@keyframes ba-splash-sheen {
	to { transform: translateX(120%); }
}

/* An indeterminate bar: the app cannot know how far along it is, and a fake
   percentage is a lie somebody eventually notices. */
.ba-splash__bar {
	position: relative;
	inline-size: min(60vw, 13rem);
	block-size: 3px;
	border-radius: var(--ba-radius-full);
	background: color-mix(in srgb, var(--ba-text-muted) 20%, transparent);
	overflow: hidden;
}
.ba-splash__bar::after {
	content: "";
	position: absolute;
	inset-block: 0;
	inline-size: 40%;
	border-radius: inherit;
	background: var(--ba-accent);
	animation: ba-splash-slide 1200ms var(--ba-ease-standard) infinite;
}
@keyframes ba-splash-slide {
	from { inset-inline-start: -40%; }
	to { inset-inline-start: 100%; }
}

.ba-splash__note {
	font-size: var(--ba-text-sm);
	color: var(--ba-text-muted);
	animation: ba-fade var(--ba-duration-slower) 700ms var(--ba-ease-standard) both;
}

/* Reduced motion keeps the splash - it still says "wait" - and drops the
   movement. The bar would otherwise slide indefinitely in somebody's
   peripheral vision. */
@media (prefers-reduced-motion: reduce) {
	.ba-splash__logo { animation: none; }
	.ba-splash__logo::after { display: none; }
	.ba-splash__bar::after { animation: none; inline-size: 100%; opacity: 0.5; }
}
