/* BoatLink - boat telemetry UI for boatlink.kybetube.com
   Loaded after variables.css / global.css / components.css: re-tints the shared
   design tokens to a marine palette, then adds the boat-specific components. */

:root {
	/* Marine palette - deep water navy with a channel-marker cyan accent */
	--color-primary: #0ea5e9;
	--color-primary-hover: #0284c7;

	--bg-primary: #071620;
	--bg-secondary: #0d2430;
	--bg-tertiary: #163444;
	--bg-card: #0d2430;

	--text-primary: #eef7fb;
	--text-secondary: #9db6c4;
	--text-muted: #6b8798;

	--border-color: #1c4053;
	--border-focus: #0ea5e9;

	/* Status colours, shared by badges, dots and alert rows */
	--status-online: #10b981;
	--status-late: #f59e0b;
	--status-silent: #ef4444;
	--status-unknown: #6b8798;
}

body {
	background-image:
		radial-gradient(1100px 500px at 15% -10%, rgba(14, 165, 233, 0.10), transparent 60%),
		radial-gradient(900px 500px at 100% 0%, rgba(16, 185, 129, 0.06), transparent 55%);
	background-attachment: fixed;
}

/* ============ SHELL ============ */

.sidebar.collapsed .sidebar-header::before {
	content: '⚓';
}

.bl-logo {
	display: flex;
	align-items: center;
	gap: var(--spacing-sm);
	font-size: var(--font-size-xl);
	font-weight: 700;
	color: var(--text-primary);
	text-decoration: none;
	letter-spacing: -0.02em;
}

.bl-logo:hover {
	color: var(--text-primary);
}

.bl-logo-mark {
	width: 28px;
	height: 28px;
	flex-shrink: 0;
	color: var(--color-primary);
}

.bl-logo-accent {
	color: var(--color-primary);
}

.bl-logo-sub {
	display: block;
	margin-top: 2px;
	font-size: 0.7rem;
	font-weight: 500;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--text-muted);
}

.sidebar.collapsed .bl-logo-sub {
	display: none;
}

/* Nav badge for the open-alert count */
.nav-item-badge {
	margin-left: auto;
	min-width: 20px;
	padding: 0 6px;
	border-radius: var(--radius-full);
	background: var(--color-danger);
	color: #fff;
	font-size: 0.7rem;
	font-weight: 700;
	line-height: 18px;
	text-align: center;
}

.nav-item-badge:empty,
.nav-item-badge[data-count="0"] {
	display: none;
}

.sidebar.collapsed .nav-item-badge {
	display: none;
}

/* ============ PAGE TRANSITIONS (shared with the kybetube user SPA) ============ */

.content-container {
	transition: opacity 150ms ease;
}

.content-container.fade-out { opacity: 0; }
.content-container.fade-in { opacity: 1; }

.page-loading {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 40vh;
}

.spinner {
	width: 32px;
	height: 32px;
	border: 3px solid var(--border-color);
	border-top-color: var(--color-primary);
	border-radius: 50%;
	animation: bl-spin 0.8s linear infinite;
}

@keyframes bl-spin {
	to { transform: rotate(360deg); }
}

/* ============ STATUS ============ */

.bl-status {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 3px 10px 3px 8px;
	border-radius: var(--radius-full);
	font-size: var(--font-size-sm);
	font-weight: 600;
	white-space: nowrap;
}

.bl-dot {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	flex-shrink: 0;
	background: var(--status-unknown);
}

.bl-status-online { background: rgba(16, 185, 129, 0.12); color: var(--status-online); }
.bl-status-online .bl-dot {
	background: var(--status-online);
	box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.6);
	animation: bl-pulse 2.4s ease-out infinite;
}

.bl-status-late { background: rgba(245, 158, 11, 0.12); color: var(--status-late); }
.bl-status-late .bl-dot { background: var(--status-late); }

.bl-status-silent { background: rgba(239, 68, 68, 0.12); color: var(--status-silent); }
.bl-status-silent .bl-dot { background: var(--status-silent); }

/* The gateway is in contact and cannot reach this device. Read as a problem,
   like silent - but its own colour, because the answer is different: silent
   means go and look, this means it is switched off or something else is holding
   its connection. */
.bl-status-unreachable { background: rgba(245, 158, 11, 0.14); color: var(--status-late); }
.bl-status-unreachable .bl-dot { background: var(--status-late); }

.bl-status-no-data,
.bl-status-no-devices { background: rgba(107, 135, 152, 0.14); color: var(--status-unknown); }

/* Switched off on purpose. Grey rather than amber or red: nothing is wrong,
   and a warning colour on a deliberate choice is what made "not reachable"
   read as a fault in the first place. */
.bl-status-off { background: rgba(107, 135, 152, 0.14); color: var(--status-unknown); }
.bl-status-off .bl-dot { background: var(--status-unknown); }

@keyframes bl-pulse {
	0%   { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.5); }
	70%  { box-shadow: 0 0 0 7px rgba(16, 185, 129, 0); }
	100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}

@media (prefers-reduced-motion: reduce) {
	.bl-status-online .bl-dot { animation: none; }
	.spinner { animation-duration: 2s; }
	.content-container { transition: none; }
}

/* ============ FLEET ============ */

.bl-fleet-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
	gap: var(--spacing-lg);
}

.bl-boat-card {
	display: block;
	background: var(--bg-card);
	border: 1px solid var(--border-color);
	border-radius: var(--radius-lg);
	padding: var(--spacing-lg);
	color: inherit;
	text-decoration: none;
	position: relative;
	overflow: hidden;
	transition: transform var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.bl-boat-card::before {
	content: '';
	position: absolute;
	inset: 0 auto 0 0;
	width: 3px;
	background: var(--status-unknown);
}

.bl-boat-card[data-status="online"]::before { background: var(--status-online); }
.bl-boat-card[data-status="late"]::before   { background: var(--status-late); }
.bl-boat-card[data-status="silent"]::before { background: var(--status-silent); }

.bl-boat-card:hover {
	transform: translateY(-2px);
	border-color: var(--color-primary);
	box-shadow: 0 12px 28px rgba(0, 0, 0, 0.35);
	color: inherit;
}

.bl-boat-card-head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--spacing-sm);
	margin-bottom: var(--spacing-xs);
}

.bl-boat-name {
	font-size: var(--font-size-lg);
	font-weight: 600;
	color: var(--text-primary);
}

.bl-boat-harbour {
	display: flex;
	align-items: center;
	gap: 5px;
	font-size: var(--font-size-sm);
	color: var(--text-muted);
	margin-bottom: var(--spacing-md);
}

.bl-boat-harbour svg {
	width: 14px;
	height: 14px;
	flex-shrink: 0;
}

/* Headline metric row on a fleet card */
.bl-metrics {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--spacing-sm);
	padding: var(--spacing-md) 0;
	border-top: 1px solid var(--border-color);
	border-bottom: 1px solid var(--border-color);
	margin-bottom: var(--spacing-md);
}

.bl-metric-label {
	display: block;
	font-size: 0.7rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--text-muted);
	margin-bottom: 2px;
}

.bl-metric-value {
	font-size: var(--font-size-xl);
	font-weight: 600;
	color: var(--text-primary);
	font-variant-numeric: tabular-nums;
	line-height: 1.2;
}

.bl-metric-unit {
	font-size: var(--font-size-sm);
	font-weight: 500;
	color: var(--text-secondary);
	margin-left: 1px;
}

.bl-metric-value.is-cold  { color: #60a5fa; }
.bl-metric-value.is-alarm { color: var(--color-danger); }
.bl-metric-value.is-warn  { color: var(--color-warning); }
.bl-metric-value.is-empty { color: var(--text-muted); font-size: var(--font-size-lg); }

.bl-boat-foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--spacing-sm);
	font-size: var(--font-size-sm);
	color: var(--text-muted);
}

/* ============ ALERTS ============ */

.bl-alert-list {
	display: flex;
	flex-direction: column;
	gap: var(--spacing-sm);
}

.bl-alert {
	display: flex;
	align-items: flex-start;
	gap: var(--spacing-md);
	padding: var(--spacing-md);
	border-radius: var(--radius-md);
	border: 1px solid var(--border-color);
	background: var(--bg-secondary);
	border-left-width: 3px;
}

.bl-alert-critical { border-left-color: var(--color-danger); }
.bl-alert-warning  { border-left-color: var(--color-warning); }
.bl-alert-info     { border-left-color: var(--text-muted); }

.bl-alert-icon {
	width: 20px;
	height: 20px;
	flex-shrink: 0;
	margin-top: 2px;
}

.bl-alert-critical .bl-alert-icon { color: var(--color-danger); }
.bl-alert-warning .bl-alert-icon  { color: var(--color-warning); }
.bl-alert-info .bl-alert-icon     { color: var(--text-muted); }

.bl-alert-body { flex: 1; min-width: 0; }

.bl-alert-title {
	font-weight: 600;
	color: var(--text-primary);
	margin-bottom: 2px;
}

.bl-alert-detail {
	font-size: var(--font-size-sm);
	color: var(--text-secondary);
	overflow-wrap: anywhere;
}

.bl-alert-boat {
	font-size: var(--font-size-sm);
	color: var(--color-primary);
	white-space: nowrap;
	text-decoration: none;
}

/* Compact banner used at the top of a boat page */
.bl-alert-strip {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-sm);
	margin-bottom: var(--spacing-lg);
}

/* ============ BOAT DETAIL ============ */

.bl-boat-header {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--spacing-md);
	margin-bottom: var(--spacing-xl);
}

.bl-back {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	font-size: var(--font-size-sm);
	color: var(--text-secondary);
	text-decoration: none;
	margin-bottom: var(--spacing-sm);
}

.bl-back:hover { color: var(--color-primary); }

.bl-back svg { width: 16px; height: 16px; }

.bl-compartment {
	margin-bottom: var(--spacing-xl);
}

.bl-compartment-head {
	display: flex;
	align-items: baseline;
	gap: var(--spacing-sm);
	margin-bottom: var(--spacing-md);
	padding-bottom: var(--spacing-sm);
	border-bottom: 1px solid var(--border-color);
}

.bl-compartment-name {
	font-size: var(--font-size-lg);
	font-weight: 600;
	color: var(--text-primary);
}

.bl-compartment-meta {
	font-size: var(--font-size-sm);
	color: var(--text-muted);
}

.bl-sensor-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
	gap: var(--spacing-md);
}

.bl-sensor-card {
	background: var(--bg-card);
	border: 1px solid var(--border-color);
	border-radius: var(--radius-lg);
	padding: var(--spacing-md);
}

.bl-sensor-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--spacing-sm);
	margin-bottom: var(--spacing-md);
}

.bl-sensor-name {
	font-weight: 600;
	color: var(--text-primary);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.bl-reading-row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--spacing-sm);
	padding: 6px 0;
}

.bl-reading-row + .bl-reading-row {
	border-top: 1px solid rgba(255, 255, 255, 0.04);
}

.bl-reading-name {
	display: flex;
	align-items: center;
	gap: 6px;
	font-size: var(--font-size-sm);
	color: var(--text-secondary);
}

.bl-reading-name svg {
	width: 14px;
	height: 14px;
	opacity: 0.7;
	flex-shrink: 0;
}

.bl-reading-value {
	font-weight: 600;
	color: var(--text-primary);
	font-variant-numeric: tabular-nums;
}

/* A latching alarm reads as a word, coloured by which word it is: "Water"
   should catch the eye across a cabin, "Dry" should reassure without shouting. */
.bl-value-alarm {
	color: var(--danger, #f87171);
}

.bl-value-ok {
	color: var(--success, #34d399);
}

/* The history line under a bilge reading: when it last had water, how long. */
.bl-reading-note {
	padding: 0 0 6px;
	font-size: var(--font-size-sm);
	color: var(--text-muted);
}

.bl-note-alarm {
	color: var(--danger, #f87171);
}

.bl-sensor-foot {
	margin-top: var(--spacing-sm);
	padding-top: var(--spacing-sm);
	border-top: 1px solid var(--border-color);
	font-size: var(--font-size-sm);
	color: var(--text-muted);
}

/* Battery strength bar */
.bl-batt {
	display: inline-flex;
	align-items: center;
	gap: 6px;
}

.bl-batt-track {
	width: 34px;
	height: 6px;
	border-radius: var(--radius-full);
	background: var(--bg-tertiary);
	overflow: hidden;
}

.bl-batt-fill {
	height: 100%;
	border-radius: var(--radius-full);
	background: var(--status-online);
	transition: width var(--transition-base);
}

.bl-batt-fill.is-low  { background: var(--color-danger); }
.bl-batt-fill.is-warn { background: var(--color-warning); }

/* ============ CHART ============ */

.bl-chart-toolbar {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-sm);
	align-items: center;
}

.bl-range-group {
	display: inline-flex;
	background: var(--bg-tertiary);
	border-radius: var(--radius-md);
	padding: 2px;
	gap: 2px;
}

.bl-range-btn {
	padding: 4px 12px;
	border: none;
	background: transparent;
	color: var(--text-secondary);
	border-radius: var(--radius-sm);
	font-size: var(--font-size-sm);
	font-weight: 500;
	transition: all var(--transition-fast);
}

.bl-range-btn:hover { color: var(--text-primary); }

.bl-range-btn.active {
	background: var(--color-primary);
	color: #fff;
}

.form-select,
.form-select-sm {
	padding: var(--spacing-xs) var(--spacing-sm);
	background-color: var(--bg-tertiary);
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md);
	color: var(--text-primary);
}

.form-select:focus {
	outline: none;
	border-color: var(--border-focus);
}

.bl-chart-container {
	position: relative;
	height: 340px;
}

/* Window paging under the chart */
.bl-chart-nav {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--spacing-sm);
	margin: var(--spacing-sm) 0 var(--spacing-xs);
}

.bl-nav-btn {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	padding: 6px 10px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md);
	background: var(--bg-tertiary);
	color: var(--text-secondary);
	font-size: var(--font-size-sm);
	font-weight: 500;
	transition: color var(--transition-fast), border-color var(--transition-fast);
}

.bl-nav-btn svg {
	width: 16px;
	height: 16px;
}

.bl-nav-btn:hover:not(:disabled) {
	color: var(--text-primary);
	border-color: var(--color-primary);
}

.bl-nav-btn:disabled {
	opacity: 0.4;
	cursor: not-allowed;
}

.bl-chart-window {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 2px;
	min-width: 0;
	font-size: var(--font-size-sm);
	color: var(--text-muted);
	text-align: center;
	font-variant-numeric: tabular-nums;
}

.bl-now-btn {
	padding: 0;
	border: none;
	background: none;
	color: var(--color-primary);
	font-size: var(--font-size-sm);
	font-weight: 600;
}

.bl-now-btn[hidden] { display: none; }

/* ============ EMPTY STATES ============ */

.empty-state {
	text-align: center;
	padding: var(--spacing-2xl) var(--spacing-lg);
	color: var(--text-secondary);
}

.empty-state svg {
	width: 48px;
	height: 48px;
	color: var(--text-muted);
	opacity: 0.5;
	margin-bottom: var(--spacing-md);
}

.empty-state h3 {
	font-size: var(--font-size-lg);
	color: var(--text-primary);
	margin-bottom: var(--spacing-xs);
	font-weight: 600;
}

.empty-state p {
	max-width: 46ch;
	margin: 0 auto;
	font-size: var(--font-size-sm);
}

/* ============ SETTINGS ============ */

.bl-settings-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
	gap: var(--spacing-lg);
	align-items: start;
}

/* ============ PHONE SHELL ============

   On phones the fixed sidebar is replaced by an app bar and a bottom tab bar,
   so nav sits under the thumb and the full screen width goes to the data.
   Both are inert above 768px; the sidebar is the navigation there. */

.bl-appbar,
.bl-tabbar,
.bl-only-mobile {
	display: none;
}

.bl-appbar-back,
.bl-appbar-brand,
.bl-appbar-title,
.bl-appbar-user {
	display: none;
}

/* ============ RESPONSIVE ============ */

@media (max-width: 768px) {
	.sidebar { display: none; }

	/* App bar: brand on the top-level pages, back + boat name on a boat page. */
	.bl-appbar {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		z-index: 90;
		display: flex;
		align-items: center;
		gap: var(--spacing-sm);
		height: calc(56px + env(safe-area-inset-top));
		padding: env(safe-area-inset-top) var(--spacing-md) 0;
		background: rgba(7, 22, 32, 0.92);
		backdrop-filter: blur(10px);
		-webkit-backdrop-filter: blur(10px);
		border-bottom: 1px solid var(--border-color);
	}

	.bl-appbar-brand {
		display: flex;
		align-items: center;
		gap: var(--spacing-sm);
		flex: 1;
		min-width: 0;
		font-size: var(--font-size-lg);
		font-weight: 700;
		color: var(--text-primary);
		text-decoration: none;
		letter-spacing: -0.02em;
	}

	.bl-appbar-brand .bl-logo-mark {
		width: 24px;
		height: 24px;
	}

	.bl-appbar.is-detail .bl-appbar-brand { display: none; }

	.bl-appbar.is-detail .bl-appbar-back,
	.bl-appbar.is-detail .bl-appbar-title {
		display: flex;
	}

	.bl-appbar-back {
		align-items: center;
		justify-content: center;
		width: 40px;
		height: 40px;
		margin-left: calc(var(--spacing-sm) * -1);
		flex-shrink: 0;
		background: none;
		border: none;
		border-radius: var(--radius-full);
		color: var(--text-primary);
		-webkit-tap-highlight-color: transparent;
	}

	.bl-appbar-back:active { background: rgba(255, 255, 255, 0.08); }

	.bl-appbar-back svg {
		width: 24px;
		height: 24px;
	}

	.bl-appbar-title {
		flex: 1;
		min-width: 0;
		align-items: center;
		font-size: var(--font-size-lg);
		font-weight: 600;
		color: var(--text-primary);
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}

	.bl-appbar-user {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 34px;
		height: 34px;
		flex-shrink: 0;
		border-radius: var(--radius-full);
		background: var(--color-primary);
		color: #fff;
		font-weight: 600;
		text-decoration: none;
		-webkit-tap-highlight-color: transparent;
	}

	/* Tab bar */
	.bl-tabbar {
		position: fixed;
		bottom: 0;
		left: 0;
		right: 0;
		z-index: 90;
		display: grid;
		grid-template-columns: repeat(3, 1fr);
		background: rgba(13, 36, 48, 0.96);
		backdrop-filter: blur(12px);
		-webkit-backdrop-filter: blur(12px);
		border-top: 1px solid var(--border-color);
		padding-bottom: env(safe-area-inset-bottom);
	}

	.bl-tab {
		position: relative;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: 3px;
		min-height: 58px;
		padding: var(--spacing-sm) var(--spacing-xs);
		color: var(--text-muted);
		text-decoration: none;
		font-size: 0.7rem;
		font-weight: 600;
		letter-spacing: 0.01em;
		-webkit-tap-highlight-color: transparent;
		transition: color var(--transition-fast);
	}

	.bl-tab svg {
		width: 24px;
		height: 24px;
	}

	.bl-tab.active { color: var(--color-primary); }
	.bl-tab:active { background: rgba(255, 255, 255, 0.05); }

	.bl-tab-badge {
		position: absolute;
		top: 6px;
		left: 50%;
		margin-left: 4px;
		min-width: 18px;
		padding: 0 5px;
		border-radius: var(--radius-full);
		background: var(--color-danger);
		color: #fff;
		font-size: 0.65rem;
		font-weight: 700;
		line-height: 18px;
		text-align: center;
	}

	.bl-tab-badge:empty,
	.bl-tab-badge[data-count="0"] {
		display: none;
	}

	/* Content sits between the two bars, clear of the notch and home indicator. */
	.main-content,
	body.sidebar-collapsed .main-content {
		margin-left: 0;
		padding:
			calc(56px + env(safe-area-inset-top) + var(--spacing-md))
			var(--spacing-md)
			calc(58px + env(safe-area-inset-bottom) + var(--spacing-lg));
	}

	.bl-only-mobile { display: block; }

	/* The app bar owns the back affordance on a boat page. */
	.bl-back { display: none; }

	/* ---- Density ---- */

	.page-header { margin-bottom: var(--spacing-lg); }
	.page-title { font-size: var(--font-size-xl); }
	.page-subtitle { font-size: var(--font-size-sm); }

	.stats-grid {
		grid-template-columns: repeat(2, 1fr);
		gap: var(--spacing-sm);
		margin-bottom: var(--spacing-lg);
	}

	.stat-card { padding: var(--spacing-md); }
	.stat-label { font-size: 0.75rem; }
	.stat-value { font-size: var(--font-size-xl); }

	.bl-fleet-grid,
	.bl-sensor-grid,
	.bl-settings-grid {
		grid-template-columns: 1fr;
		gap: var(--spacing-md);
	}

	.bl-boat-card { padding: var(--spacing-md) var(--spacing-md) var(--spacing-md) var(--spacing-lg); }
	.bl-metrics { padding: var(--spacing-sm) 0; }

	.bl-boat-header { margin-bottom: var(--spacing-lg); }
	.bl-compartment { margin-bottom: var(--spacing-lg); }
	.bl-compartment-head { flex-wrap: wrap; }

	.bl-alert { padding: var(--spacing-sm) var(--spacing-md); }

	/* ---- Chart ---- */

	.card-header {
		flex-direction: column;
		align-items: flex-start;
		gap: var(--spacing-sm);
	}

	.bl-chart-toolbar { width: 100%; }

	.bl-chart-toolbar .form-select-sm {
		flex: 1 1 100%;
		width: 100%;
	}

	.bl-range-group {
		display: flex;
		width: 100%;
	}

	.bl-range-btn {
		flex: 1;
		padding: 10px 0;
		font-size: var(--font-size-base);
	}

	.bl-chart-container { height: 240px; }

	.bl-nav-btn {
		min-height: 40px;
		padding: 6px 12px;
	}

	/* Labels would crowd the window text out on a narrow screen; the arrows
	   carry the meaning on their own. */
	.bl-nav-btn span { display: none; }

	/* ---- Touch targets ---- */

	/* 16px inputs: anything smaller makes iOS Safari zoom the page on focus. */
	.form-input,
	.form-select,
	.form-select-sm {
		font-size: 16px;
		padding: var(--spacing-sm) var(--spacing-md);
	}

	.btn {
		min-height: 44px;
		padding: var(--spacing-sm) var(--spacing-lg);
	}

	.bl-settings-grid .btn,
	.bl-only-mobile .btn {
		width: 100%;
	}

	/* ---- Toasts clear the tab bar ---- */

	.toast-container {
		top: auto;
		left: var(--spacing-md);
		right: var(--spacing-md);
		bottom: calc(58px + env(safe-area-inset-bottom) + var(--spacing-sm));
	}

	.toast {
		min-width: 0;
		max-width: none;
	}

	/* ---- Login ---- */

	.login-card { padding: var(--spacing-xl) var(--spacing-lg); }
	.login-btn { min-height: 48px; }
}

/* Hover lift is a mouse affordance; on touch it sticks after a tap. */
@media (hover: none) {
	.bl-boat-card:hover {
		transform: none;
		box-shadow: none;
		border-color: var(--border-color);
	}

	.bl-boat-card:active {
		transform: scale(0.985);
		border-color: var(--color-primary);
	}
}

/* Small phones (iPhone SE and similar) */
@media (max-width: 380px) {
	.bl-metric-value { font-size: var(--font-size-lg); }
	.bl-metric-label { font-size: 0.65rem; }
	.stat-value { font-size: var(--font-size-lg); }
}

/* Landscape: give the chart what little height there is */
@media (max-height: 480px) and (orientation: landscape) {
	.bl-chart-container { height: 200px; }
}

/* ---- Switch panel control ------------------------------------------- */

.bl-switch-card { margin-bottom: var(--spacing-xl); }

.bl-switch-status {
	font-size: var(--font-size-sm);
	color: var(--text-muted);
	display: inline-flex;
	align-items: center;
	gap: 6px;
}

/* A live socket is worth showing, because the alternative - a queued change
   that lands half an hour later - looks identical otherwise. */
.bl-live-dot {
	width: 8px; height: 8px; border-radius: 50%;
	background: var(--success, #34d399);
	box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.6);
	animation: bl-live-pulse 2s infinite;
}

@keyframes bl-live-pulse {
	70%  { box-shadow: 0 0 0 8px rgba(52, 211, 153, 0); }
	100% { box-shadow: 0 0 0 0 rgba(52, 211, 153, 0); }
}

.bl-switch-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
	gap: 4px var(--spacing-lg);
}

.bl-switch-row {
	display: flex;
	align-items: center;
	gap: var(--spacing-sm);
	padding: 8px 0;
	border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}

.bl-switch-name { flex: 1; font-size: var(--font-size-sm); color: var(--text-secondary); }

.bl-switch-state {
	min-width: 4.5em;
	text-align: right;
	font-size: var(--font-size-sm);
	color: var(--text-muted);
	font-variant-numeric: tabular-nums;
}

.bl-switch {
	position: relative;
	width: 44px; height: 24px;
	border-radius: 12px;
	border: 1px solid var(--border-color);
	background: rgba(255, 255, 255, 0.08);
	cursor: pointer;
	padding: 0;
	transition: background 0.15s ease;
	flex-shrink: 0;
}

.bl-switch-knob {
	position: absolute;
	top: 2px; left: 2px;
	width: 18px; height: 18px;
	border-radius: 50%;
	background: var(--text-secondary);
	transition: transform 0.15s ease, background 0.15s ease;
}

.bl-switch.on { background: var(--success, #34d399); }
.bl-switch.on .bl-switch-knob { transform: translateX(20px); background: #fff; }

/* Pending is deliberately distinct from both on and off: the switch has been
   asked to move and has not yet confirmed. */
.bl-switch.pending { opacity: 0.6; }
.bl-switch.pending .bl-switch-knob { animation: bl-blink 0.8s infinite; }

@keyframes bl-blink { 50% { opacity: 0.35; } }

.bl-switch:disabled { opacity: 0.35; cursor: not-allowed; }

/* A reading that just arrived over the socket. Brief, so a value changing
   while someone is looking at it does not change silently. */
.bl-value-fresh { animation: bl-fresh 1.2s ease-out; }

@keyframes bl-fresh {
	0%   { color: var(--color-primary); }
	100% { color: inherit; }
}

@media (prefers-reduced-motion: reduce) {
	.bl-value-fresh { animation: none; }
}

/* Why a switch did not move. Usually the owner's own phone app holding the
   panel, which allows one connection at a time. */
.bl-switch-error {
	margin: 0 0 var(--spacing-sm);
	padding: 8px 10px;
	border-radius: var(--radius-md);
	background: rgba(239, 68, 68, 0.12);
	color: var(--status-silent);
	font-size: var(--font-size-sm);
}

.bl-switch-card .bl-sensor-foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--spacing-sm);
}

.bl-switch-refresh {
	background: none;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md);
	padding: 4px 10px;
	color: var(--text-secondary);
	font-size: var(--font-size-sm);
	cursor: pointer;
}

.bl-switch-refresh:hover:not(:disabled) {
	border-color: var(--color-primary);
	color: var(--text-primary);
}

.bl-switch-refresh:disabled { opacity: 0.4; cursor: not-allowed; }

/* Undo a switch the boat has not collected yet. Only offered while the
   command is still queued - once it has been handed over it may already
   have happened. */
.bl-switch-cancel {
	margin-left: 8px;
	background: none;
	border: none;
	padding: 0;
	color: var(--color-primary);
	font-size: var(--font-size-sm);
	text-decoration: underline;
	cursor: pointer;
}

.bl-switch-cancel:hover { color: var(--text-primary); }

/* Gateway polling mode. A fast poll costs battery, so the choice is the
   owner's rather than something the system decides silently. */
.bl-mode-card { margin-bottom: var(--spacing-lg); }

.bl-mode-row {
	display: flex;
	align-items: center;
	gap: var(--spacing-md);
	flex-wrap: wrap;
}

.bl-mode-group {
	display: inline-flex;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md);
	overflow: hidden;
}

.bl-mode-btn {
	background: none;
	border: none;
	padding: 6px 14px;
	color: var(--text-secondary);
	font-size: var(--font-size-sm);
	cursor: pointer;
}

.bl-mode-btn + .bl-mode-btn { border-left: 1px solid var(--border-color); }
.bl-mode-btn:hover:not(.active) { color: var(--text-primary); }

.bl-mode-btn.active {
	background: var(--color-primary);
	color: #fff;
	font-weight: 600;
}

.bl-mode-hint {
	font-size: var(--font-size-sm);
	color: var(--text-muted);
}

/* Time since the gateway last checked in, counting up between polls. Makes
   the cadence visible instead of something you infer from things changing. */
.bl-poll-age {
	margin-left: auto;
	font-size: var(--font-size-sm);
	color: var(--text-muted);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

/* Lost the server. Everything in the card is now history, so it should not
   look like live state. */
.bl-stale { opacity: 0.55; }

/* ---- Diesel-heater control card ---------------------------------------- */
.bl-switch-status.on { color: var(--status-online); }
.bl-heater-controls {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-sm);
	align-items: center;
	margin: var(--spacing-sm) 0;
}
.bl-heater-power,
.bl-heater-vent,
.bl-heater-pump {
	padding: 8px 14px;
	border-radius: var(--radius-md, 8px);
	border: 1px solid var(--border-color);
	background: var(--bg-tertiary);
	color: var(--text-primary);
	cursor: pointer;
	font-weight: 600;
}
.bl-heater-power.on { background: var(--status-online); border-color: var(--status-online); color: #04120b; }
.bl-heater-vent.on { background: var(--color-primary); border-color: var(--color-primary); color: #04121a; }
.bl-heater-power:disabled,
.bl-heater-vent:disabled,
.bl-heater-pump:disabled,
.bl-heater-slider:disabled { opacity: 0.45; cursor: not-allowed; }
/* Setpoint sliders: each takes a full row under the toggle buttons. */
.bl-heater-slider-row {
	flex: 1 1 100%;
	display: flex;
	flex-direction: column;
	gap: 4px;
}
.bl-heater-slider-row label {
	display: flex;
	align-items: center;
	gap: var(--spacing-sm);
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.85rem);
}
.bl-heater-slider-val {
	color: var(--text-primary);
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	min-width: 3ch;
}
.bl-heater-slider-row.pending .bl-heater-slider-val { color: var(--color-primary); }
.bl-heater-setting {
	display: inline-flex;
	align-items: center;
	color: var(--color-primary);
	font-size: var(--font-size-xs, 0.75rem);
}
.bl-heater-slider {
	width: 100%;
	height: 24px;
	cursor: pointer;
	accent-color: var(--color-primary);
}
.bl-heater-metrics {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--spacing-sm);
	margin-top: var(--spacing-sm);
}
.bl-heater-metrics div {
	display: flex;
	flex-direction: column;
	padding: 6px 8px;
	background: var(--bg-tertiary);
	border-radius: var(--radius-md, 8px);
}
.bl-heater-metrics span { color: var(--text-muted); font-size: var(--font-size-xs, 0.75rem); }
.bl-heater-metrics b { color: var(--text-primary); }

/* ---- Heater control feedback (in-flight) ------------------------------- */
/* Reuses the existing bl-spin keyframe (defined near the top). */
.bl-spin {
	display: inline-block;
	width: 12px; height: 12px;
	margin-left: 6px;
	vertical-align: -1px;
	border: 2px solid currentColor;
	border-right-color: transparent;
	border-radius: 50%;
	animation: bl-spin 0.7s linear infinite;
}
/* Two classes beats :disabled at equal specificity via source order, so the
   in-flight look wins over the dimmed disabled state. */
.bl-heater-power.pending,
.bl-heater-vent.pending,
.bl-heater-pump.pending {
	opacity: 1;
	border-color: var(--color-primary);
	box-shadow: 0 0 0 2px rgba(14, 165, 233, 0.25);
	cursor: progress;
}
.bl-heater-slider-row.pending .bl-heater-slider-val {
	animation: bl-heater-pulse 1s ease-in-out infinite;
}
/* Own name, not the shared bl-pulse (which drives the status dot's box-shadow). */
@keyframes bl-heater-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }

@media (prefers-reduced-motion: reduce) {
	.bl-spin { animation-duration: 2s; }
	.bl-heater-slider-row.pending .bl-heater-slider-val { animation: none; }
}

/* Last-known values being re-confirmed after a page load: the read-outs dim and
   the header shows "refreshing…" until the device's own report lands. Controls
   (sliders, toggles, buttons) stay full-strength - they work off last-known. */
.bl-refreshing .bl-heater-metrics,
.bl-refreshing .bl-heater-slider-val,
.bl-refreshing .bl-switch-state { opacity: 0.5; }
.bl-refreshing .bl-switch-status { color: var(--text-muted); }

/* Passive (power-save) read-only view: setpoints shown, no controls. */
.bl-heater-setpoints {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-md);
	margin: var(--spacing-sm) 0;
}
.bl-heater-setpoints div { display: flex; flex-direction: column; }
.bl-heater-setpoints span { color: var(--text-muted); font-size: var(--font-size-xs, 0.75rem); }
.bl-heater-setpoints b { color: var(--text-primary); font-size: var(--font-size-lg, 1.05rem); }
.bl-heater-savenote {
	margin-top: var(--spacing-sm);
	padding: 8px 10px;
	border-radius: var(--radius-md, 8px);
	background: rgba(245, 158, 11, 0.10);
	color: var(--status-late);
	font-size: var(--font-size-sm, 0.85rem);
}

/* Remote gateway restart control */
.bl-mode-foot {
	display: flex;
	align-items: center;
	gap: var(--spacing-sm);
	margin-top: var(--spacing-sm);
	padding-top: var(--spacing-sm);
	border-top: 1px solid var(--border-color);
}
.bl-gw-reboot {
	padding: 6px 12px;
	border-radius: var(--radius-md, 8px);
	border: 1px solid var(--border-color);
	background: var(--bg-tertiary);
	color: var(--text-secondary);
	cursor: pointer;
	font-weight: 600;
}
.bl-gw-reboot:hover:not(:disabled) { border-color: var(--status-late); color: var(--text-primary); }
.bl-gw-reboot:disabled { opacity: 0.45; cursor: not-allowed; }
.bl-gw-reboot.pending { color: var(--status-late); cursor: progress; opacity: 1; }

/* ---- Unreachable device / gateway-offline states ---------------------- */
/* Device the gateway can't reach right now: read-outs greyed, controls locked. */
.bl-unreach-note {
	margin-top: var(--spacing-sm);
	padding: 8px 10px;
	border-radius: var(--radius-md, 8px);
	background: rgba(239, 68, 68, 0.10);
	color: var(--status-silent);
	font-size: var(--font-size-sm, 0.85rem);
	cursor: help;
}
.bl-boat-banner {
	margin-bottom: var(--spacing-md);
	padding: 10px 14px;
	border-radius: var(--radius-md, 8px);
	background: rgba(239, 68, 68, 0.12);
	border: 1px solid rgba(239, 68, 68, 0.30);
	color: var(--status-silent);
	font-weight: 600;
}

/* This page losing its socket is not the boat losing contact. Said quietly,
   because it is our problem and it fixes itself. */
.bl-boat-banner-soft {
	background: rgba(148, 163, 184, 0.12);
	border-color: rgba(148, 163, 184, 0.28);
	color: var(--text-secondary);
	font-weight: 400;
}

#bl-reboot-timer { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--status-late); }

/* ── Renaming ────────────────────────────────────────────────────────────────
   A name the user can change in place. The pencil is invisible until the row
   is hovered (or the control focused) so a boat page full of sensors does not
   read as a page full of buttons; on touch, where there is no hover, it stays
   faintly visible instead of being unreachable. */
/* The name itself, inside a renameable thing. Truncated rather than wrapped:
   a long name must not push the pencil off the end of the row. */
.bl-name-text {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.bl-nameable {
	display: inline-flex;
	align-items: center;
	gap: var(--spacing-xs, 4px);
	min-width: 0;
	max-width: 100%;
}

.bl-rename-btn {
	flex: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 22px;
	height: 22px;
	padding: 0;
	border: 0;
	border-radius: var(--radius-sm, 4px);
	background: transparent;
	color: var(--text-muted);
	cursor: pointer;
	opacity: 0;
	transition: opacity 0.12s ease, color 0.12s ease, background 0.12s ease;
}

.bl-rename-btn svg {
	width: 13px;
	height: 13px;
}

.bl-nameable:hover .bl-rename-btn,
.bl-rename-btn:focus-visible {
	opacity: 1;
}

.bl-rename-btn:hover {
	color: var(--color-primary);
	background: rgba(14, 165, 233, 0.12);
}

/* No hover to reveal it, so it is always there - just quiet. */
@media (hover: none) {
	.bl-rename-btn { opacity: 0.45; }
}

.bl-rename-input {
	min-width: 6em;
	max-width: 100%;
	padding: 2px 6px;
	border: 1px solid var(--border-focus);
	border-radius: var(--radius-sm, 4px);
	background: var(--bg-primary);
	/* Inherit so the box matches the heading it replaces rather than snapping
	   the layout to a form-control size mid-edit. */
	color: var(--text-primary);
	font: inherit;
	line-height: inherit;
}

.bl-rename-input:focus {
	outline: none;
	box-shadow: 0 0 0 2px rgba(14, 165, 233, 0.25);
}

/* The switch rows are a tight grid; keep the name column from growing when
   its pencil appears. */
.bl-switch-name .bl-nameable { max-width: 100%; }

/* ── Compartment management ──────────────────────────────────────────────── */
.bl-compartment-add {
	display: flex;
	justify-content: center;
	margin: var(--spacing-lg, 20px) 0 var(--spacing-xl, 28px);
}

.bl-add-compartment {
	display: inline-flex;
	align-items: center;
	gap: var(--spacing-xs, 4px);
	padding: 8px 16px;
	border: 1px dashed var(--border-color);
	border-radius: var(--radius-md, 8px);
	background: transparent;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.85rem);
	cursor: pointer;
	transition: border-color 0.12s ease, color 0.12s ease;
}

.bl-add-compartment:hover {
	border-color: var(--color-primary);
	color: var(--color-primary);
}

.bl-add-compartment svg { width: 15px; height: 15px; }

/* Same quiet-until-hovered treatment as the rename pencil, so a boat page is
   not a wall of destructive buttons. */
.bl-compartment-remove {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	margin-left: var(--spacing-xs, 4px);
	padding: 0;
	border: 0;
	border-radius: var(--radius-sm, 4px);
	background: transparent;
	color: var(--text-muted);
	cursor: pointer;
	opacity: 0;
	transition: opacity 0.12s ease, color 0.12s ease, background 0.12s ease;
}

.bl-compartment-remove svg { width: 14px; height: 14px; }

.bl-compartment-head:hover .bl-compartment-remove,
.bl-compartment-remove:focus-visible { opacity: 1; }

.bl-compartment-remove:hover {
	color: var(--status-silent);
	background: rgba(239, 68, 68, 0.12);
}

@media (hover: none) {
	.bl-compartment-remove { opacity: 0.45; }
}

.bl-compartment-empty {
	color: var(--text-muted);
	font-size: var(--font-size-sm, 0.85rem);
	padding: var(--spacing-md, 12px) 0;
}

/* Sits in the sensor card footer, which is a flex row. */
.bl-move-device {
	flex: none;
	max-width: 11em;
	padding: 2px 6px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 4px);
	background: var(--bg-primary);
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.8rem);
}

.bl-sensor-foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--spacing-sm, 8px);
}

.bl-new-compartment {
	min-width: 14em;
	text-align: center;
}

/* ── Adding a boat ───────────────────────────────────────────────────────── */
.bl-add-boat {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--spacing-sm, 8px);
	min-height: 140px;
	padding: var(--spacing-lg, 20px);
	border: 1px dashed var(--border-color);
	border-radius: var(--radius-lg, 12px);
	background: transparent;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.85rem);
	cursor: pointer;
	transition: border-color 0.12s ease, color 0.12s ease;
}

.bl-add-boat:hover {
	border-color: var(--color-primary);
	color: var(--color-primary);
}

.bl-add-boat svg { width: 22px; height: 22px; }

.bl-new-boat {
	align-self: center;
	min-width: 12em;
	text-align: center;
}

/* ── Choosing which readings a card shows ────────────────────────────────── */
.bl-device-config {
	flex: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	padding: 0;
	border: 0;
	border-radius: var(--radius-sm, 4px);
	background: transparent;
	color: var(--text-muted);
	cursor: pointer;
	opacity: 0;
	transition: opacity 0.12s ease, color 0.12s ease, background 0.12s ease;
}

.bl-device-config svg { width: 16px; height: 16px; }

.bl-sensor-card:hover .bl-device-config,
.bl-device-config:focus-visible { opacity: 1; }

.bl-device-config:hover {
	color: var(--color-primary);
	background: rgba(14, 165, 233, 0.12);
}

@media (hover: none) {
	.bl-device-config { opacity: 0.45; }
}

.bl-setting-head,
.bl-reading-setting {
	display: grid;
	grid-template-columns: 1fr 3.2em 4.2em;
	align-items: center;
	gap: var(--spacing-sm, 8px);
	font-size: var(--font-size-sm, 0.85rem);
}

.bl-setting-head {
	padding-bottom: 4px;
	border-bottom: 1px solid var(--border-color);
	color: var(--text-muted);
	font-size: 0.72rem;
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.bl-setting-head span:not(:first-child),
.bl-setting-box { justify-self: center; }

.bl-reading-setting {
	padding: 6px 0;
	color: var(--text-secondary);
}

.bl-setting-name {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.bl-setting-box { cursor: pointer; }
.bl-reading-setting input[type="checkbox"] { accent-color: var(--color-primary); }

/* Limits sit under their row, spanning all three columns. */
.bl-limit-row {
	grid-column: 1 / -1;
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--spacing-sm, 8px);
	padding: 2px 0 6px;
}

.bl-limit {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	color: var(--text-muted);
}

.bl-limit input {
	width: 5.5em;
	padding: 2px 6px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 4px);
	background: var(--bg-primary);
	color: var(--text-primary);
	font: inherit;
}

.bl-limit-hint {
	color: var(--text-muted);
	font-size: 0.72rem;
}

/* ── Device settings modal ───────────────────────────────────────────────── */
.bl-modal-overlay {
	position: fixed;
	inset: 0;
	z-index: 60;
	display: flex;
	align-items: flex-end;
	justify-content: center;
	padding: 0;
	background: rgba(4, 16, 24, 0.72);
	overflow-y: auto;
	overscroll-behavior: contain;
}

.bl-modal-overlay[hidden] { display: none; }

.bl-modal {
	width: 100%;
	max-width: 480px;
	max-height: 92vh;
	display: flex;
	flex-direction: column;
	border: 1px solid var(--border-color);
	border-bottom: 0;
	border-radius: var(--radius-lg, 12px) var(--radius-lg, 12px) 0 0;
	background: var(--bg-card);
	box-shadow: 0 -8px 40px rgba(0, 0, 0, 0.45);
}

/* On a phone it is a sheet from the bottom, which is where a thumb is.
   With room, it centres. */
@media (min-width: 640px) {
	.bl-modal-overlay { align-items: center; padding: var(--spacing-lg, 20px); }
	.bl-modal { border-bottom: 1px solid var(--border-color); border-radius: var(--radius-lg, 12px); }
}

.bl-modal-head {
	display: flex;
	align-items: center;
	gap: var(--spacing-sm, 8px);
	padding: var(--spacing-md, 14px) var(--spacing-lg, 18px);
	border-bottom: 1px solid var(--border-color);
}

.bl-modal-title {
	flex: 1;
	min-width: 0;
	margin: 0;
	font-size: var(--font-size-lg, 1.05rem);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.bl-modal-close {
	flex: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	padding: 0;
	border: 0;
	border-radius: var(--radius-sm, 4px);
	background: transparent;
	color: var(--text-secondary);
	cursor: pointer;
}

.bl-modal-close svg { width: 18px; height: 18px; }
.bl-modal-close:hover { color: var(--text-primary); background: var(--bg-tertiary); }

.bl-modal-body {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	padding: var(--spacing-lg, 18px);
	-webkit-overflow-scrolling: touch;
}

.bl-modal-section + .bl-modal-section {
	margin-top: var(--spacing-lg, 20px);
	padding-top: var(--spacing-lg, 20px);
	border-top: 1px solid var(--border-color);
}

.bl-modal-heading {
	margin: 0 0 var(--spacing-sm, 8px);
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--text-muted);
}

.bl-modal-name {
	font-size: var(--font-size-lg, 1.05rem);
	font-weight: 600;
	color: var(--text-primary);
}

.bl-modal-hint {
	margin: var(--spacing-xs, 4px) 0 var(--spacing-sm, 8px);
	font-size: var(--font-size-sm, 0.8rem);
	color: var(--text-muted);
	line-height: 1.45;
}

.bl-facts {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 2px var(--spacing-md, 12px);
	margin: var(--spacing-sm, 8px) 0 0;
	font-size: var(--font-size-sm, 0.85rem);
}

.bl-facts dt { color: var(--text-muted); }
.bl-facts dd { margin: 0; color: var(--text-secondary); overflow-wrap: anywhere; }

.bl-modal-switch {
	display: flex;
	align-items: flex-start;
	gap: var(--spacing-sm, 10px);
	padding: var(--spacing-sm, 8px) 0;
	cursor: pointer;
}

.bl-modal-switch input {
	flex: none;
	margin-top: 3px;
	accent-color: var(--color-primary);
}

.bl-modal-switch strong {
	display: block;
	font-size: var(--font-size-sm, 0.9rem);
	font-weight: 600;
	color: var(--text-primary);
}

.bl-modal-switch em {
	display: block;
	margin-top: 2px;
	font-style: normal;
	font-size: var(--font-size-sm, 0.78rem);
	color: var(--text-muted);
	line-height: 1.4;
}

.bl-modal-field {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--spacing-sm, 8px);
	padding: var(--spacing-sm, 8px) 0;
	font-size: var(--font-size-sm, 0.85rem);
	color: var(--text-secondary);
}

.bl-modal-chart {
	position: relative;
	height: 180px;
}

/* ── A sensor kept off the cards ─────────────────────────────────────────── */
.bl-hidden-row {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-xs, 6px);
	margin-top: var(--spacing-sm, 8px);
}

.bl-hidden-chip {
	padding: 3px 10px;
	border: 1px dashed var(--border-color);
	border-radius: var(--radius-full, 9999px);
	background: transparent;
	color: var(--text-muted);
	font-size: var(--font-size-sm, 0.78rem);
	cursor: pointer;
}

.bl-hidden-chip:hover { border-color: var(--color-primary); color: var(--color-primary); }

/* Said instead of a status pill: its quiet is deliberate, not a fault. */
.bl-sensor-off {
	flex: none;
	padding: 2px 8px;
	border-radius: var(--radius-full, 9999px);
	background: var(--bg-tertiary);
	color: var(--text-muted);
	font-size: 0.72rem;
	white-space: nowrap;
}

/* A device the catalogue does not recognise: heard, never decoded. */
.bl-modal-warn {
	margin: var(--spacing-sm, 8px) 0;
	padding: 10px 12px;
	border-radius: var(--radius-md, 8px);
	background: rgba(239, 68, 68, 0.12);
	border: 1px solid rgba(239, 68, 68, 0.30);
	color: var(--status-silent);
	font-size: var(--font-size-sm, 0.8rem);
	line-height: 1.45;
}

/* ── Archived boats ──────────────────────────────────────────────────────── */
.bl-archived {
	grid-column: 1 / -1;
	margin-top: var(--spacing-md, 12px);
	padding: var(--spacing-sm, 10px) var(--spacing-md, 14px);
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 8px);
	background: var(--bg-secondary);
}

.bl-archived > summary {
	cursor: pointer;
	list-style: none;
	font-size: var(--font-size-sm, 0.85rem);
	color: var(--text-muted);
}

.bl-archived > summary::-webkit-details-marker { display: none; }

.bl-archived-note {
	margin: var(--spacing-sm, 8px) 0;
	font-size: var(--font-size-sm, 0.8rem);
	color: var(--text-muted);
	line-height: 1.45;
}

.bl-archived-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--spacing-sm, 8px);
	padding: 8px 0;
	border-top: 1px solid var(--border-color);
}

.bl-archived-name {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	color: var(--text-secondary);
}

.bl-archived-actions {
	flex: none;
	display: flex;
	align-items: center;
	gap: var(--spacing-sm, 10px);
}

.bl-archived-open { color: var(--color-primary); font-size: var(--font-size-sm, 0.85rem); }

.bl-archived-restore {
	padding: 4px 10px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 4px);
	background: transparent;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.8rem);
	cursor: pointer;
}

.bl-archived-restore:hover { border-color: var(--color-primary); color: var(--color-primary); }

.bl-archived-banner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: var(--spacing-sm, 10px);
	margin-bottom: var(--spacing-md, 14px);
	padding: 10px 14px;
	border: 1px solid rgba(148, 163, 184, 0.3);
	border-radius: var(--radius-md, 8px);
	background: rgba(148, 163, 184, 0.10);
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.85rem);
}

/* Destructive-ish, so it sits at the end of the page and stays quiet. */
.bl-boat-archive {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--spacing-sm, 10px);
	margin: var(--spacing-xl, 28px) 0 var(--spacing-lg, 20px);
	padding-top: var(--spacing-lg, 18px);
	border-top: 1px solid var(--border-color);
}

.bl-archive-boat {
	flex: none;
	padding: 6px 14px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 8px);
	background: transparent;
	color: var(--text-muted);
	font-size: var(--font-size-sm, 0.85rem);
	cursor: pointer;
}

.bl-archive-boat:hover { border-color: var(--status-silent); color: var(--status-silent); }

.bl-archive-note {
	flex: 1;
	min-width: 12em;
	font-size: var(--font-size-sm, 0.78rem);
	color: var(--text-muted);
	line-height: 1.45;
}

/* ── What state the boat is in ───────────────────────────────────────────── */
.bl-mode-bar {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--spacing-sm, 10px);
	margin-bottom: var(--spacing-md, 14px);
	padding: 10px 14px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 8px);
	background: var(--bg-secondary);
}

.bl-mode-label {
	flex: none;
	font-size: var(--font-size-sm, 0.85rem);
	color: var(--text-secondary);
}

.bl-mode-select {
	flex: none;
	padding: 4px 10px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 4px);
	background: var(--bg-primary);
	color: var(--text-primary);
	font: inherit;
	font-size: var(--font-size-sm, 0.85rem);
	font-weight: 600;
}

.bl-mode-hint {
	flex: 1;
	min-width: 12em;
	font-size: var(--font-size-sm, 0.78rem);
	color: var(--text-muted);
	line-height: 1.4;
}

/* Deleting is not archiving, so it does not look like it. */
.bl-archive-boat.is-delete { color: var(--status-silent); border-color: rgba(239, 68, 68, 0.35); }
.bl-archive-boat.is-delete:hover { background: rgba(239, 68, 68, 0.12); }

/* ── People ──────────────────────────────────────────────────────────────── */
.bl-people-card { margin-bottom: var(--spacing-lg, 20px); }

.bl-person {
	display: flex;
	align-items: center;
	gap: var(--spacing-sm, 8px);
	padding: 8px 0;
	border-bottom: 1px solid var(--border-color);
}

.bl-person:last-of-type { border-bottom: 0; }

.bl-person-who { flex: 1; min-width: 0; color: var(--text-primary); font-size: var(--font-size-sm, 0.9rem); }
.bl-person-meta { color: var(--text-muted); font-size: var(--font-size-sm, 0.8rem); }

.bl-person-tag {
	padding: 1px 7px;
	border-radius: var(--radius-full, 9999px);
	background: var(--bg-tertiary);
	color: var(--text-muted);
	font-size: 0.68rem;
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.bl-person-level { flex: none; max-width: 12rem; padding: 4px 8px; font-size: var(--font-size-sm, 0.8rem); }

.bl-person-remove {
	flex: none;
	padding: 4px 10px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 4px);
	background: transparent;
	color: var(--text-muted);
	font-size: var(--font-size-sm, 0.8rem);
	cursor: pointer;
}

.bl-person-remove:hover { border-color: var(--status-silent); color: var(--status-silent); }

/* Beside a name still waiting on an invitation: another way in for somebody
   whose email never arrived. */
.bl-person-link {
	flex: none;
	padding: 4px 10px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 4px);
	background: transparent;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.8rem);
	cursor: pointer;
}

.bl-person-link:hover { border-color: var(--color-primary); color: var(--color-primary); }
.bl-person-link:disabled { opacity: 0.6; cursor: default; }

/* The invitation link, shown once after inviting somebody. Only the hash of
   the token is stored, so this is the only moment it exists in readable form -
   which is why it gets a box of its own rather than a toast that fades. */
.bl-invite-link {
	margin: var(--spacing-md, 12px) 0 0;
	padding: var(--spacing-md, 12px);
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 10px);
	background: var(--bg-tertiary);
}

.bl-invite-link-label {
	display: block;
	margin-bottom: 6px;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.82rem);
}

.bl-invite-link-row {
	display: flex;
	gap: var(--spacing-sm, 8px);
}

/* A link is long and nobody reads it - it is there to be copied, so it may
   scroll rather than push the button off the row. */
.bl-invite-link-row .form-input {
	flex: 1;
	min-width: 0;
	font-family: var(--font-mono, monospace);
	font-size: var(--font-size-sm, 0.8rem);
}

/* A secondary button is bg-tertiary, and so is this box - so without an edge
   the one control that matters here reads as a word rather than a button. */
.bl-invite-link-row .btn {
	border: 1px solid var(--border-color);
	flex: none;
}

.bl-invite-link .form-hint { margin: 8px 0 0; }

.bl-invite-row {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-sm, 8px);
	margin-top: var(--spacing-md, 14px);
	padding-top: var(--spacing-md, 14px);
	border-top: 1px solid var(--border-color);
}

.bl-invite-email { flex: 1; min-width: 14rem; }
.bl-invite-level { flex: none; max-width: 12rem; }

/* ── A control card that cannot be operated ──────────────────────────────────
   One state for the whole card rather than each control dimming itself. A card
   that is partly bright and partly dim reads as broken; a card that is plainly
   grey, with one line saying why, reads as locked. */
.bl-locked {
	position: relative;
	border-color: var(--border-color);
	background: var(--bg-secondary);
}

/* Everything except the reason and the card title, which have to stay legible
   for the card to explain itself. */
.bl-locked .bl-switch-grid,
.bl-locked .bl-heater-controls,
.bl-locked .bl-heater-metrics,
.bl-locked .bl-heater-setpoints,
.bl-locked .bl-sensor-foot,
.bl-locked .bl-switch-status {
	opacity: 0.4;
	filter: grayscale(1);
	pointer-events: none;
	user-select: none;
}

.bl-locked .card-title { color: var(--text-secondary); }

.bl-locked-note {
	margin-top: var(--spacing-sm, 8px);
	padding: 8px 12px;
	border-radius: var(--radius-md, 8px);
	background: rgba(148, 163, 184, 0.12);
	border: 1px solid rgba(148, 163, 184, 0.28);
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.82rem);
	line-height: 1.45;
}

/* The refresh button stays usable: asking the boat to look again is exactly
   what somebody wants when a panel is unreachable. */
.bl-locked .bl-switch-refresh {
	opacity: 1;
	filter: none;
	pointer-events: auto;
}

/* The answer before the controls: what this boat watches, and what is wrong
   with it now. A page that opens with twenty-five checkboxes has buried it. */
.bl-alert-summary {
	margin: 0 0 var(--spacing-md, 14px);
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.85rem);
	line-height: 1.6;
}

.bl-alert-summary b { color: var(--text-primary); }

.bl-alert-firing-now {
	display: inline-block;
	margin: 0 4px;
	padding: 1px 8px;
	border-radius: 999px;
	background: rgba(239, 68, 68, 0.14);
	color: var(--status-silent);
	font-weight: 600;
}

/* A sensor folds away. Twenty-five readings laid flat is a wall; the one you
   came to change is behind its own name. */
.bl-alert-device { border-top: 1px solid var(--border-color); }
.bl-alert-device[open] { padding-bottom: var(--spacing-sm, 10px); }

.bl-alert-device-head {
	display: flex;
	align-items: baseline;
	gap: 8px;
	flex-wrap: wrap;
	padding: 10px 0;
	cursor: pointer;
	list-style: none;
}

.bl-alert-device-head::-webkit-details-marker { display: none; }

/* A disclosure triangle that turns, rather than the browser's own. */
.bl-alert-device-head::before {
	content: '';
	flex: none;
	width: 0;
	height: 0;
	border-left: 5px solid currentColor;
	border-top: 4px solid transparent;
	border-bottom: 4px solid transparent;
	color: var(--text-secondary);
	transition: transform 0.15s ease;
}

.bl-alert-device[open] > .bl-alert-device-head::before { transform: rotate(90deg); }
.bl-alert-device-head:hover { color: var(--text-primary); }

.bl-alert-device-count {
	margin-left: auto;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.78rem);
}

/* What this sensor is raising right now, so this page and the other tab are
   plainly about the same boat. */
.bl-alert-firing {
	padding: 1px 8px;
	border-radius: 999px;
	background: rgba(239, 68, 68, 0.14);
	color: var(--status-silent);
	font-size: var(--font-size-sm, 0.75rem);
	font-weight: 600;
}

/* Where a boat's alerts go. Sits above the rules, because "what counts as an
   alert" is only worth setting if one can reach somebody. */
.bl-alert-delivery {
	margin: 0 0 var(--spacing-md, 14px);
	padding: var(--spacing-sm, 10px) var(--spacing-md, 14px);
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 8px);
	background: var(--bg-secondary);
}

.bl-alert-delivery-head {
	margin-bottom: 6px;
	font-size: var(--font-size-sm, 0.82rem);
	font-weight: 600;
	letter-spacing: 0.03em;
	text-transform: uppercase;
	color: var(--text-secondary);
}

.bl-alert-channel {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
	padding: 6px 0;
}

.bl-alert-channel-name { flex: 1; font-size: var(--font-size-sm, 0.9rem); }

.bl-alert-channel select {
	padding: 4px 8px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 6px);
	background: var(--bg-card);
	color: var(--text-primary);
	font-size: var(--font-size-sm, 0.8rem);
}

.bl-alert-channel-remove,
.bl-alert-connect {
	padding: 5px 10px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 6px);
	background: none;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.8rem);
	cursor: pointer;
}

.bl-alert-connect { margin-top: 6px; border-style: dashed; width: 100%; }
.bl-alert-channel-remove:hover,
.bl-alert-connect:hover { color: var(--text-primary); background: var(--bg-tertiary); border-style: solid; }

.bl-alert-link { margin-top: 8px; font-size: var(--font-size-sm, 0.82rem); }
.bl-alert-link[hidden] { display: none; }

/* The code is meant to be typed into a phone, so it is big enough to read
   across a desk. */
.bl-alert-code {
	display: inline-block;
	margin: 4px 0;
	padding: 6px 12px;
	border-radius: var(--radius-sm, 6px);
	background: var(--bg-tertiary);
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 1rem;
	letter-spacing: 0.08em;
}

/* ── Where a boat is ─────────────────────────────────────────────────────────
   Folded away by default: the position matters when you are wondering about it
   and takes a screenful when you are not. */
/* ── Sub header: which view of this page you are on ──────────────────────────
   One component, two users - the boat's views and the alerts page's two lists.
   Written as one rule rather than two lookalikes, because a sub header that
   drifts between pages is worse than one that was never shared. */
.bl-subnav,
.bl-alert-tabs {
	display: flex;
	gap: 4px;
	margin-bottom: var(--spacing-lg, 20px);
	border-bottom: 1px solid var(--border-color);
	overflow-x: auto;
	scrollbar-width: none;
}

.bl-subnav::-webkit-scrollbar,
.bl-alert-tabs::-webkit-scrollbar { display: none; }

.bl-subnav-item,
.bl-alert-tab {
	flex: none;
	padding: 10px 14px;
	border: 0;
	border-bottom: 2px solid transparent;
	margin-bottom: -1px;
	background: none;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.9rem);
	font-weight: 500;
	white-space: nowrap;
	cursor: pointer;
}

.bl-subnav-item:hover,
.bl-alert-tab:hover { color: var(--text-primary); }

.bl-subnav-item.active,
.bl-alert-tab.active {
	color: var(--text-primary);
	border-bottom-color: var(--accent, #10b981);
}

.bl-subnav-item:focus-visible {
	outline: 2px solid var(--accent, #10b981);
	outline-offset: 3px;
	border-radius: var(--radius-sm, 6px);
}

/* A hidden view has to be gone, not merely invisible: Leaflet measures zero in
   a container that is still laid out, and draws one tile in the corner. */
.bl-tab-panel[hidden] { display: none; }

.bl-map-card { margin-bottom: var(--spacing-lg, 20px); padding: 0; overflow: hidden; }

/* The map is a page of its own, so it runs to the bottom of the screen rather
   than sitting in a card with room left over. The height below is only what it
   uses before the exact one is measured - see sizeMap() in boat.js. */
#boat-tab-map { margin-bottom: 0; }
#boat-tab-map .bl-map-card { margin-bottom: 0; }
#boat-tab-map .bl-map { height: clamp(320px, 60vh, 640px); }

/* Something not built yet, said plainly. */
.bl-soon { padding: var(--spacing-xl, 26px); max-width: 62ch; }

.bl-soon p { margin: 0 0 10px; color: var(--text-secondary); line-height: 1.6; }

.bl-soon .bl-soon-note { margin-bottom: 0; color: var(--text-muted); font-size: var(--font-size-sm, 0.85rem); }

/* ── The anchor watch ────────────────────────────────────────────────────────
   A bar above the map, because the circle it sets is drawn on the map right
   below it. Quiet until it has something to say, then loud. */
.bl-anchor {
	display: flex;
	align-items: center;
	padding: 10px var(--spacing-lg, 18px);
	border-bottom: 1px solid var(--border-color);
	background: var(--bg-secondary);
}

.bl-anchor-row {
	display: flex;
	align-items: center;
	gap: var(--spacing-md, 14px);
	flex-wrap: wrap;
	width: 100%;
	font-size: var(--font-size-sm, 0.85rem);
}

.bl-anchor-state { font-weight: 600; color: var(--text-secondary); }
.bl-anchor-state.is-set { color: var(--accent, #10b981); }
.bl-anchor-state.is-warn { color: var(--status-late, #f59e0b); }
.bl-anchor-state.is-alarm { color: var(--color-danger, #ef4444); }

.bl-anchor-note { color: var(--text-muted); }

.bl-anchor-distance { color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.bl-anchor-distance.is-out { color: var(--color-danger, #ef4444); font-weight: 600; }

.bl-anchor-radius { display: flex; align-items: center; gap: 6px; color: var(--text-muted); }

.bl-anchor-radius input {
	width: 64px;
	padding: 4px 8px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 6px);
	background: var(--bg-card);
	color: var(--text-primary);
	font: inherit;
	font-size: var(--font-size-sm, 0.85rem);
}

.bl-anchor-drop { margin-left: auto; }

/* The three actions on a watch that already exists. Links rather than buttons:
   one primary action per bar, and this bar's was spent dropping it. */
.bl-anchor-link {
	border: 0;
	background: none;
	padding: 0;
	color: var(--accent, #10b981);
	font: inherit;
	font-size: var(--font-size-sm, 0.85rem);
	cursor: pointer;
}

.bl-anchor-link:hover { text-decoration: underline; }
.bl-anchor-link.is-quiet { color: var(--text-muted); }
.bl-anchor-link.is-quiet:hover { color: var(--color-danger, #ef4444); }

.bl-anchor-pin {
	display: block;
	font-size: 15px;
	line-height: 18px;
	text-align: center;
	text-shadow: 0 0 3px rgba(0, 0, 0, 0.9), 0 0 6px rgba(0, 0, 0, 0.6);
}

@media (max-width: 640px) {
	.bl-anchor-drop { margin-left: 0; }
}

/* ── Passages, under the map ─────────────────────────────────────────────────
   A column of trips: when, where to where, how far, how long. Rows rather than
   cards - the eye runs down a list of dates far better than it scans a grid,
   and this is a thing you skim to find one. */
.bl-passage-head {
	display: flex;
	align-items: baseline;
	gap: 8px;
	margin: var(--spacing-lg, 18px) 0 8px;
}

.bl-passage-title { font-weight: 600; color: var(--text-primary); }

.bl-passage-count {
	color: var(--text-muted);
	font-size: var(--font-size-sm, 0.82rem);
	font-variant-numeric: tabular-nums;
}

.bl-passage-list {
	display: flex;
	flex-direction: column;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 10px);
	overflow: hidden;
	background: var(--bg-card);
}

.bl-passage-row {
	display: grid;
	grid-template-columns: 76px 1fr auto auto;
	align-items: baseline;
	gap: var(--spacing-md, 14px);
	width: 100%;
	padding: 10px var(--spacing-lg, 16px);
	border: 0;
	border-bottom: 1px solid var(--border-color);
	background: none;
	color: var(--text-secondary);
	font: inherit;
	font-size: var(--font-size-sm, 0.86rem);
	text-align: left;
	cursor: pointer;
	transition: background var(--transition-fast);
}

.bl-passage-row:last-child { border-bottom: 0; }
.bl-passage-row:hover { background: var(--bg-tertiary); }

/* The one on the map. Marked on the left edge rather than by colouring the
   whole row, which at this density reads as an alert. */
.bl-passage-row.active {
	background: var(--bg-tertiary);
	box-shadow: inset 3px 0 0 var(--accent, #10b981);
}

.bl-passage-when { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.bl-passage-where { color: var(--text-primary); min-width: 0; }

.bl-passage-live {
	margin-left: 8px;
	padding: 1px 6px;
	border-radius: 999px;
	background: rgba(16, 185, 129, 0.14);
	color: var(--accent, #10b981);
	font-size: 0.7rem;
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.bl-passage-far,
.bl-passage-took { font-variant-numeric: tabular-nums; white-space: nowrap; }
.bl-passage-took { color: var(--text-muted); }

.bl-passage-none {
	margin-top: var(--spacing-lg, 18px);
	padding: var(--spacing-md, 14px) var(--spacing-lg, 16px);
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 10px);
	color: var(--text-muted);
	font-size: var(--font-size-sm, 0.85rem);
	line-height: 1.5;
}

@media (max-width: 640px) {
	/* Four columns will not fit a phone: the distance and the time drop under
	   the route rather than being squeezed into slivers. */
	.bl-passage-row {
		grid-template-columns: 64px 1fr auto;
		row-gap: 2px;
	}
	.bl-passage-took { grid-column: 2 / 4; color: var(--text-muted); }
}

/* ── The logbook ─────────────────────────────────────────────────────────────
   Read as a column of dated facts, so the date and what kind of thing it was
   sit in their own gutter and the eye can run down it without reading. */
.bl-log-head {
	display: flex;
	align-items: center;
	gap: var(--spacing-md, 14px);
	flex-wrap: wrap;
	margin-bottom: var(--spacing-lg, 18px);
}

.bl-log-total {
	color: var(--text-muted);
	font-size: var(--font-size-sm, 0.82rem);
	font-variant-numeric: tabular-nums;
}

.bl-log-add { margin-left: auto; }

.bl-log-entry {
	display: flex;
	gap: var(--spacing-lg, 18px);
	padding: var(--spacing-md, 14px) 0;
	border-bottom: 1px solid var(--border-color);
}

.bl-log-entry:last-child { border-bottom: 0; }

.bl-log-when {
	display: flex;
	flex-direction: column;
	gap: 4px;
	flex: none;
	width: 108px;
}

.bl-log-date {
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.82rem);
	font-variant-numeric: tabular-nums;
}

/* The kind, as a quiet label rather than a badge: on a page that is mostly
   repairs, six coloured pills would be decoration, not information. */
.bl-log-kind {
	color: var(--text-muted);
	font-size: 0.7rem;
	letter-spacing: 0.04em;
	text-transform: uppercase;
}

.bl-log-kind[data-kind="fault"] { color: var(--color-danger, #ef4444); }
.bl-log-kind[data-kind="repair"] { color: var(--accent, #10b981); }

.bl-log-main { flex: 1; min-width: 0; }

.bl-log-title {
	display: flex;
	align-items: baseline;
	gap: var(--spacing-sm, 10px);
	font-weight: 600;
	color: var(--text-primary);
}

.bl-log-cost {
	margin-left: auto;
	flex: none;
	color: var(--text-secondary);
	font-weight: 500;
	font-variant-numeric: tabular-nums;
}

/* Kept as typed: line breaks are how somebody writes down three things they
   did, and running them together loses the list. */
.bl-log-body {
	margin: 6px 0 0;
	color: var(--text-secondary);
	line-height: 1.55;
	white-space: pre-wrap;
}

.bl-log-facts {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
	margin-top: 8px;
	color: var(--text-muted);
	font-size: var(--font-size-sm, 0.8rem);
}

.bl-log-dot { opacity: 0.5; }

.bl-log-remove {
	flex: none;
	align-self: flex-start;
	width: 26px;
	height: 26px;
	border: 0;
	border-radius: var(--radius-sm, 6px);
	background: none;
	color: var(--text-muted);
	font-size: 1.1rem;
	line-height: 1;
	cursor: pointer;
	opacity: 0;
	transition: opacity var(--transition-fast), color var(--transition-fast);
}

.bl-log-entry:hover .bl-log-remove,
.bl-log-remove:focus-visible { opacity: 1; }

.bl-log-remove:hover { color: var(--color-danger, #ef4444); }

.bl-log-form {
	display: flex;
	flex-direction: column;
	gap: var(--spacing-md, 14px);
	padding: var(--spacing-lg, 18px);
	margin-bottom: var(--spacing-lg, 18px);
}

.bl-log-form label {
	display: flex;
	flex-direction: column;
	gap: 5px;
	flex: 1;
	min-width: 160px;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.82rem);
}

.bl-log-form-row {
	display: flex;
	gap: var(--spacing-md, 14px);
	flex-wrap: wrap;
}

.bl-log-form .form-input,
.bl-log-form .form-select {
	width: 100%;
	padding: var(--spacing-xs, 6px) var(--spacing-sm, 10px);
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 8px);
	background: var(--bg-tertiary);
	color: var(--text-primary);
	font: inherit;
	font-size: var(--font-size-sm, 0.88rem);
}

.bl-log-form textarea.form-input { resize: vertical; }

.bl-log-form-actions { display: flex; gap: var(--spacing-sm, 10px); }

@media (max-width: 640px) {
	/* The date gutter costs a third of a phone screen, so on one the date
	   leads the entry instead of standing beside it. */
	.bl-log-entry { flex-direction: column; gap: 6px; }
	.bl-log-when { flex-direction: row; align-items: baseline; gap: 10px; width: auto; }
	.bl-log-remove { opacity: 1; position: absolute; right: 0; }
	.bl-log-entry { position: relative; }
	.bl-log-add { margin-left: 0; }
}

.bl-map-head {
	display: flex;
	align-items: baseline;
	gap: 10px;
	flex-wrap: wrap;
	padding: var(--spacing-md, 14px) var(--spacing-lg, 18px);
	list-style: none;
}

.bl-map-head::-webkit-details-marker { display: none; }

/* Only the fleet map still folds away. The boat's map has a tab to itself, so
   its head is a title rather than a control - and must not wear a disclosure
   triangle that does nothing. */
details.bl-map-card > .bl-map-head { cursor: pointer; }

details.bl-map-card > .bl-map-head::before {
	content: '';
	flex: none;
	width: 0;
	height: 0;
	border-left: 5px solid currentColor;
	border-top: 4px solid transparent;
	border-bottom: 4px solid transparent;
	color: var(--text-secondary);
	transition: transform 0.15s ease;
}

.bl-map-card[open] > .bl-map-head::before { transform: rotate(90deg); }

.bl-map-title { font-weight: 600; }

/* The place name, and the coordinates behind it. "Espoo" is the answer; the
   numbers are the evidence. */
.bl-map-place {
	margin-left: auto;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.82rem);
	font-variant-numeric: tabular-nums;
}

.bl-map {
	height: 340px;
	width: 100%;
	background: var(--bg-secondary);
}

/* Leaflet paints its own white, which in a slate-dark app is a hole in the
   page. Everything below puts the map inside the design rather than on top of
   it. */
.leaflet-container {
	background: var(--bg-primary);
	font: inherit;
	color: var(--text-primary);
}

/* The basemap, darkened.
   Standard OSM tiles rather than a ready-made dark basemap: the dark ones are
   all somebody's commercial tier behind an API key, and that licensing question
   is not worth answering for a colour scheme. Inverting and rotating the hue
   turns the map dark while keeping water blue-ish and land grey. The result is
   approximated rather than designed - the honest cost of not taking on a
   provider. Only this pane is filtered, so the seamarks above it stay their own
   colours. */
.leaflet-basemap-pane {
	/* Dimmed rather than inverted. Inverting turns forest into army green and
	   the whole map reads as night vision - it looks processed, not dark. This
	   keeps the map's own colours and takes the glare off, which is what a dark
	   page actually needs from it. */
	filter: brightness(0.52) saturate(0.55) contrast(1.12);
}

/* Controls, in the app's own materials rather than Leaflet's white chrome. */
.leaflet-bar,
.leaflet-control-layers,
.leaflet-control-attribution {
	background: var(--bg-card) !important;
	color: var(--text-secondary) !important;
	border: 1px solid var(--border-color) !important;
	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35) !important;
}

.leaflet-bar a,
.leaflet-bar a:hover {
	background: var(--bg-card);
	color: var(--text-primary);
	border-bottom-color: var(--border-color);
}

.leaflet-bar a:hover { background: var(--bg-tertiary); }

/* Full screen: the map is given the whole display, so it has to fill it. The
   height set for the page layout would otherwise leave it letterboxed in the
   middle of a black screen. */
.bl-map:fullscreen,
.bl-map-card:fullscreen {
	width: 100%;
	height: 100%;
	max-height: none;
	border-radius: 0;
}

.bl-map-card:fullscreen .bl-map { height: 100%; }
.bl-map-card:fullscreen .bl-map-head,
.bl-map-card:fullscreen .bl-map-foot,
.bl-map-card:fullscreen .bl-anchor { display: none; }

/* Buttons added to the map - "centre on the boat" - in Leaflet's own bar so
   they sit with the zoom controls instead of beside them looking foreign. */
.bl-map-btn a {
	display: flex !important;
	align-items: center;
	justify-content: center;
}

.bl-map-btn a svg { display: block; }

.leaflet-control-attribution { font-size: 0.68rem; padding: 2px 6px; }
.leaflet-control-attribution a { color: var(--text-secondary); }

.leaflet-control-scale-line {
	background: rgba(15, 23, 42, 0.7);
	border-color: var(--border-color);
	color: var(--text-secondary);
}

/* A boat, as a dot that says how it is doing - the same colours as its status
   pill, so green here and green there mean the same thing. */
.bl-pin {
	display: block;
	border-radius: 50%;
	background: var(--pin, #10b981);
	border: 2px solid rgba(15, 23, 42, 0.85);
	box-shadow: 0 0 0 1px var(--pin, #10b981), 0 2px 6px rgba(0, 0, 0, 0.5);
}

/* Only the boat's current position pulses. A track's start is a fact, not a
   thing that is still happening. */
.bl-pin-live { animation: bl-pin-pulse 2.6s ease-out infinite; }

@keyframes bl-pin-pulse {
	0%   { box-shadow: 0 0 0 1px var(--pin), 0 0 0 0 color-mix(in srgb, var(--pin) 55%, transparent); }
	70%  { box-shadow: 0 0 0 1px var(--pin), 0 0 0 14px transparent; }
	100% { box-shadow: 0 0 0 1px var(--pin), 0 0 0 0 transparent; }
}

@media (prefers-reduced-motion: reduce) {
	.bl-pin-live { animation: none; }
}

/* Popups, in the app's card material. */
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
	background: var(--bg-card);
	color: var(--text-primary);
	border: 1px solid var(--border-color);
	box-shadow: 0 8px 30px rgba(0, 0, 0, 0.45);
}

.leaflet-popup-content { font: inherit; line-height: 1.5; margin: 10px 14px; }
.leaflet-popup-close-button { color: var(--text-secondary) !important; }

.bl-pop { min-width: 130px; }
.bl-pop-place { color: var(--text-secondary); }
/* The number somebody clicked the water to find out. */
.bl-pop-depth {
	margin: 2px 0 1px;
	color: var(--text-primary);
	font-size: 1.05rem;
	font-weight: 600;
	font-variant-numeric: tabular-nums;
}

.bl-pop-note, .bl-pop-when {
	margin-top: 4px;
	color: var(--text-muted);
	font-size: var(--font-size-sm, 0.78rem);
}
.bl-pop-alert {
	margin-top: 4px;
	color: var(--status-silent, #ef4444);
	font-weight: 600;
	font-size: var(--font-size-sm, 0.8rem);
}
.bl-pop-link { margin-top: 6px; }
.bl-pop-link a { color: var(--color-primary, #4f46e5); font-weight: 600; }

/* The layer toggle, which is otherwise white-on-white. */
.leaflet-control-layers-expanded { padding: 8px 10px; color: var(--text-primary) !important; }
.leaflet-control-layers-selector { margin-right: 6px; }

.bl-map-foot {
	display: flex;
	align-items: center;
	gap: var(--spacing-md, 14px);
	flex-wrap: wrap;
	padding: 10px var(--spacing-lg, 18px);
	border-top: 1px solid var(--border-color);
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.82rem);
}

.bl-map-foot-label {
	color: var(--text-muted);
	font-size: var(--font-size-sm, 0.82rem);
}

.bl-map-foot select {
	padding: 4px 8px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 6px);
	background: var(--bg-card);
	color: var(--text-primary);
	font-size: var(--font-size-sm, 0.8rem);
}

.bl-map-note { margin-left: auto; }

.bl-map-none {
	padding: var(--spacing-lg, 18px);
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.85rem);
}

@media (max-width: 640px) {
	.bl-map { height: 260px; }

	/* The foot is a row on a desktop and a stack on a phone. Left as a row it
	   pushes "Position from GPS" off the right edge, which is where the sentence
	   explaining the position ends up being invisible on the device most likely
	   to be asking where the boat is. */
	.bl-map-foot { align-items: flex-start; flex-direction: column; gap: 6px; }
	.bl-map-note { margin-left: 0; }

	/* Nothing in a card may make the page scroll sideways. */
	.bl-map-card { max-width: 100%; }
}

/* ── Choosing what a fleet card shows ────────────────────────────────────────
   The card itself is a link to the boat, so the picker lives beside it rather
   than inside it: a control within a link is one you cannot press. */
.bl-boat-cell {
	display: flex;
	flex-direction: column;
}

.bl-boat-cell > .bl-boat-card { flex: 1; }

.bl-metrics-none {
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.82rem);
}

.bl-headline-toggle {
	width: 100%;
	margin-top: 6px;
	padding: 6px 10px;
	border: 1px dashed var(--border-color);
	border-radius: var(--radius-md, 8px);
	background: none;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.82rem);
	cursor: pointer;
}

.bl-headline-toggle:hover {
	color: var(--text-primary);
	border-style: solid;
	background: var(--bg-tertiary);
}

.bl-headline-panel {
	margin-top: 6px;
	padding: var(--spacing-sm, 10px);
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 8px);
	background: var(--bg-secondary);
}

.bl-headline-panel[hidden] { display: none; }

.bl-headline-hint {
	margin: 0 0 8px;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.8rem);
}

/* Which sensor a number came from, under the number. The reason a reading is
   chosen per sensor and not per kind: two thermometers on one card are only
   useful if you can tell which is which. */
.bl-metric-from {
	display: block;
	margin-top: 2px;
	color: var(--text-secondary);
	font-size: 0.68rem;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* The picker lists every reading aboard, under the sensor that produces it. */
.bl-headline-group + .bl-headline-group {
	margin-top: 6px;
	padding-top: 6px;
	border-top: 1px solid var(--border-color);
}

.bl-headline-sensor {
	padding: 2px 8px;
	color: var(--text-primary);
	font-size: var(--font-size-sm, 0.78rem);
	font-weight: 600;
}

.bl-headline-where { color: var(--text-secondary); font-weight: 400; }

/* A boat can report a great many readings; the panel scrolls rather than
   running off the bottom of the card. */
.bl-headline-panel {
	max-height: 60vh;
	overflow-y: auto;
}

.bl-headline-choice {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 6px 8px;
	border-radius: var(--radius-sm, 6px);
	cursor: pointer;
}

.bl-headline-choice:hover { background: var(--bg-tertiary); }
.bl-headline-choice.is-on { background: var(--bg-tertiary); }
.bl-headline-name { flex: 1; font-size: var(--font-size-sm, 0.85rem); }

/* What that reading says right now, so the choice is made against real numbers
   rather than a list of words. */
.bl-headline-now {
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.8rem);
	font-variant-numeric: tabular-nums;
}

/* A reading is shown however old it is - the last thing a sensor said beats a
   blank - but an old value looks exactly like a current one, and that is the
   one somebody would act on. */
.bl-sensor-stale {
	margin: 0 0 8px;
	padding: 6px 10px;
	border-radius: var(--radius-sm, 6px);
	background: rgba(245, 158, 11, 0.1);
	border: 1px solid rgba(245, 158, 11, 0.25);
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.78rem);
	line-height: 1.45;
}

/* ── The alerts page: what happened, and what you get told about ──────────── */
.bl-alert-tabs {
	display: flex;
	gap: 4px;
	margin-bottom: var(--spacing-lg, 20px);
	border-bottom: 1px solid var(--border-color);
}

.bl-alert-tab {
	padding: 10px 14px;
	border: 0;
	border-bottom: 2px solid transparent;
	background: none;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.9rem);
	font-weight: 500;
	cursor: pointer;
}

.bl-alert-tab:hover { color: var(--text-primary); }

.bl-alert-tab.active {
	color: var(--text-primary);
	border-bottom-color: var(--accent, #10b981);
}

.bl-alert-boat + .bl-alert-boat { margin-top: var(--spacing-lg, 20px); }

.bl-alert-readonly {
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.8rem);
}

.bl-alert-mode,
.bl-alert-none {
	margin: 0 0 var(--spacing-md, 14px);
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.82rem);
	line-height: 1.5;
}

.bl-alert-device + .bl-alert-device {
	margin-top: var(--spacing-md, 14px);
	padding-top: var(--spacing-md, 14px);
	border-top: 1px solid var(--border-color);
}

.bl-alert-device-name { font-weight: 600; }
.bl-alert-where { color: var(--text-secondary); font-weight: 400; }

.bl-alert-row {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 8px;
	padding: 8px 0;
	cursor: pointer;
}

.bl-alert-row input[type="checkbox"] { cursor: pointer; }
.bl-alert-row input:disabled { cursor: default; }

.bl-alert-name { font-size: var(--font-size-sm, 0.9rem); }

.bl-alert-detail,
.bl-alert-now {
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.8rem);
}

.bl-alert-now { margin-left: auto; font-variant-numeric: tabular-nums; }

/* Off a dashboard is not off an alarm. Marked, because somebody scanning this
   page has to know the row is still live. */
.bl-alert-hidden {
	margin-left: 6px;
	padding: 1px 6px;
	border-radius: 999px;
	background: var(--bg-tertiary);
	color: var(--text-secondary);
	font-size: 0.7rem;
}

.bl-alert-reading.is-off .bl-alert-name { color: var(--text-secondary); }

.bl-alert-limits {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-md, 14px);
	padding: 0 0 8px 26px;
}

.bl-alert-limit {
	display: flex;
	align-items: center;
	gap: 6px;
	font-size: var(--font-size-sm, 0.8rem);
	color: var(--text-secondary);
}

.bl-alert-limit input {
	width: 90px;
	padding: 5px 8px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 6px);
	background: var(--bg-secondary);
	color: var(--text-primary);
	font-variant-numeric: tabular-nums;
}

/* The duration, the code direction, and which state of an on/off reading is
   worth a message - all of them sat unstyled next to styled number boxes. */
.bl-alert-limit select {
	padding: 5px 8px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 6px);
	background: var(--bg-secondary);
	color: var(--text-primary);
	font-size: var(--font-size-sm, 0.8rem);
}

.bl-alert-unit { min-width: 2ch; }

/* ── Managing a boat's states ────────────────────────────────────────────── */
.bl-mode-manage {
	flex: none;
	padding: 4px 12px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 4px);
	background: transparent;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.8rem);
	cursor: pointer;
}

.bl-mode-manage:hover { border-color: var(--color-primary); color: var(--color-primary); }

.bl-mode-manager {
	margin: 0 0 var(--spacing-md, 14px);
	padding: var(--spacing-md, 14px);
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 8px);
	background: var(--bg-secondary);
}

.bl-mode-manager-note {
	margin: 0 0 var(--spacing-sm, 10px);
	font-size: var(--font-size-sm, 0.8rem);
	color: var(--text-muted);
	line-height: 1.45;
}

.bl-mode-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--spacing-sm, 8px);
	padding: 7px 0;
	border-bottom: 1px solid var(--border-color);
}

.bl-mode-row:last-of-type { border-bottom: 0; }

.bl-mode-row-name { color: var(--text-primary); font-size: var(--font-size-sm, 0.88rem); }

.bl-mode-remove {
	flex: none;
	padding: 3px 10px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-sm, 4px);
	background: transparent;
	color: var(--text-muted);
	font-size: var(--font-size-sm, 0.78rem);
	cursor: pointer;
}

.bl-mode-remove:hover { border-color: var(--status-silent); color: var(--status-silent); }

.bl-mode-add {
	display: flex;
	gap: var(--spacing-sm, 8px);
	margin-top: var(--spacing-md, 12px);
	padding-top: var(--spacing-md, 12px);
	border-top: 1px solid var(--border-color);
}

.bl-mode-name { flex: 1; min-width: 0; }

/* ============ ADDING A DEVICE ============
   One button in the device area, and a modal list behind it. Kept quiet: adding
   a device is a deliberate act, not something the page nags about. */
.bl-add-device-row {
	display: flex;
	justify-content: center;
	margin: var(--spacing-lg, 20px) 0 var(--spacing-sm, 8px);
}

.bl-add-device {
	display: inline-flex;
	align-items: center;
	gap: var(--spacing-xs, 6px);
	padding: 9px 18px;
	border: 1px solid var(--color-primary);
	border-radius: var(--radius-md, 8px);
	background: transparent;
	color: var(--color-primary);
	font-size: var(--font-size-sm, 0.85rem);
	font-weight: 600;
	cursor: pointer;
	transition: background 0.12s ease, color 0.12s ease;
}

.bl-add-device:hover { background: var(--color-primary); color: #fff; }

.bl-add-device svg { width: 16px; height: 16px; }

/* The candidate list and its loading state live inside the modal now. */
.bl-detected-list { margin-top: var(--spacing-sm, 8px); }

.bl-modal-loading {
	display: flex;
	justify-content: center;
	padding: var(--spacing-xl, 28px);
}

.bl-detected-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--spacing-md, 12px);
	padding: var(--spacing-sm, 10px) 0;
	flex-wrap: wrap;
}

.bl-detected-row + .bl-detected-row {
	border-top: 1px solid var(--border-color);
}

.bl-detected-info {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.bl-detected-mac {
	font-family: var(--font-mono, ui-monospace, monospace);
	font-size: var(--font-size-sm, 0.85rem);
	color: var(--text-primary);
}

.bl-detected-reports {
	font-size: var(--font-size-xs, 0.78rem);
	color: var(--text-muted);
}

.bl-detected-seen {
	font-size: var(--font-size-xs, 0.78rem);
	color: var(--text-muted);
	opacity: 0.85;
}

.bl-detected-actions {
	display: flex;
	gap: var(--spacing-sm, 8px);
	flex-shrink: 0;
}

.bl-detected-add,
.bl-adopt-submit {
	padding: 8px 16px;
	border: none;
	border-radius: var(--radius-md, 8px);
	background: var(--color-primary);
	color: #fff;
	font-size: var(--font-size-sm, 0.85rem);
	font-weight: 600;
	cursor: pointer;
	transition: opacity 0.12s ease;
}

.bl-detected-add:hover,
.bl-adopt-submit:hover { opacity: 0.9; }

.bl-detected-dismiss {
	padding: 8px 14px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 8px);
	background: transparent;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.85rem);
	cursor: pointer;
	transition: border-color 0.12s ease, color 0.12s ease;
}

.bl-detected-dismiss:hover {
	border-color: var(--text-secondary);
	color: var(--text-primary);
}

/* The add form: labels stacked over their controls, the submit full width so it
   is an obvious end to the short flow. */
.bl-adopt-form { display: flex; flex-direction: column; }

.bl-adopt-label {
	margin: var(--spacing-md, 12px) 0 var(--spacing-xs, 4px);
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--text-muted);
}

.bl-adopt-label:first-child { margin-top: 0; }

.bl-adopt-actions {
	display: flex;
	gap: var(--spacing-sm, 8px);
	margin-top: var(--spacing-lg, 18px);
}

.bl-adopt-submit { flex: 1; }

.bl-adopt-back {
	padding: 8px 16px;
	border: 1px solid var(--border-color);
	border-radius: var(--radius-md, 8px);
	background: transparent;
	color: var(--text-secondary);
	font-size: var(--font-size-sm, 0.85rem);
	cursor: pointer;
}

.bl-adopt-back:hover { color: var(--text-primary); border-color: var(--text-secondary); }
