/*
 * Screen layouts: fail-safe, login, dashboard shell.
 */

/* Centred single-panel screens (fail-safe + login) ---------------------- */
.xn-centered {
	display: grid;
	place-items: center;
	min-height: 100vh;
	min-height: 100dvh;
	padding: var(--xn-space-5);
	/* A single soft light source behind the panel, echoing a detailing bay
	   inspection lamp. Kept low-contrast so text stays legible. */
	background:
		radial-gradient(1100px 520px at 50% -10%, var(--xn-glow), transparent 70%),
		var(--xn-bg);
}

.xn-panel {
	width: 100%;
	max-width: 420px;
	padding: var(--xn-space-6);
	text-align: start;
}

.xn-panel .xn-logo {
	margin-block-end: var(--xn-space-5);
}

.xn-auth-foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--xn-space-3);
	margin-block-start: var(--xn-space-5);
	padding-block-start: var(--xn-space-4);
	border-block-start: 1px solid var(--xn-border);
}

/* Dashboard shell ------------------------------------------------------ */
.xn-shell {
	display: grid;
	grid-template-columns: var(--xn-sidebar-w) 1fr;
	min-height: 100vh;
	min-height: 100dvh;
	/* Clips the parked drawer rather than letting it extend the scrollable
	   width. clip, not hidden, so it creates no scroll container of its
	   own and sticky positioning inside still works. */
	overflow-x: clip;
}

.xn-shell__body {
	display: flex;
	flex-direction: column;
	min-width: 0; /* lets tables scroll instead of stretching the grid */
}

/* Sidebar -------------------------------------------------------------- */

/*
 * One column, three bands: a fixed header, a navigation list that takes the
 * remaining height and scrolls on its own, and a footer pinned to the bottom.
 * Scrolling the list rather than the whole panel is what keeps the collapse
 * control and the version line reachable however long the menu grows.
 */
.xn-sidebar {
	display: flex;
	flex-direction: column;
	gap: var(--xn-space-4);
	padding: var(--xn-space-4) var(--xn-space-3);
	background: var(--xn-surface);
	border-inline-end: 1px solid var(--xn-border);
	min-width: 0;
	/* Respects a notch or a home indicator without affecting the desktop. */
	padding-block-start: max( var(--xn-space-4), env( safe-area-inset-top ) );
	padding-block-end: max( var(--xn-space-4), env( safe-area-inset-bottom ) );
}

.xn-sidebar__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--xn-space-2);
	padding-inline: var(--xn-space-2);
	padding-block-end: var(--xn-space-4);
	border-block-end: 1px solid var(--xn-border);
	min-height: 44px;
	flex: none;
}

.xn-sidebar__controls {
	display: flex;
	align-items: center;
	gap: var(--xn-space-1);
	flex: none;
}

/* The close control belongs to the mobile drawer only. */
.xn-sidebar__close {
	display: none;
	flex: none;
}

.xn-nav {
	display: grid;
	gap: 2px;
	flex: 1 1 auto;
	align-content: start;
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	/* The scrollbar sits outside the items, so nothing jumps when it
	   appears. */
	scrollbar-width: thin;
}

/* A quiet caption, not a decorative divider. */
.xn-nav__section {
	display: flex;
	align-items: center;
	gap: var(--xn-space-2);
	margin-block-start: var(--xn-space-4);
	padding: var(--xn-space-2) var(--xn-space-3) var(--xn-space-1);
}

.xn-nav__section::after {
	content: "";
	flex: 1;
	height: 1px;
	background: var(--xn-border);
}

.xn-nav__section-label {
	color: var(--xn-text-muted);
	font-size: var(--xn-text-small);
	font-weight: var(--xn-weight-title);
	letter-spacing: 0.02em;
	white-space: nowrap;
}

.xn-nav__item {
	display: flex;
	align-items: center;
	gap: var(--xn-space-3);
	/* A comfortable target on touch without loosening the desktop rhythm. */
	min-height: 44px;
	padding-inline: var(--xn-space-3);
	padding-block: var(--xn-space-2);
	border-radius: var(--xn-radius-md);
	color: var(--xn-text-muted);
	font-weight: var(--xn-weight-title);
	font-size: var(--xn-text-body);
	white-space: nowrap;
	position: relative;
	transition: background-color var(--xn-speed) var(--xn-ease),
		color var(--xn-speed) var(--xn-ease);
}

.xn-nav__item .xn-icon {
	flex: none;
}

.xn-nav__label {
	flex: 1;
	overflow: hidden;
	text-overflow: ellipsis;
}

a.xn-nav__item:hover {
	background: var(--xn-surface-secondary);
	color: var(--xn-text);
	text-decoration: none;
}

a.xn-nav__item:focus-visible {
	outline: 2px solid var(--xn-accent);
	outline-offset: -2px;
}

.xn-nav__item.is-active {
	/*
	 * The solid brand purple in both modes, rather than a wash of it. In
	 * light mode the 20% tint was so pale that the selected item barely read
	 * as selected.
	 */
	background: var(--xn-primary);
	color: #fff;
}

.xn-nav__item.is-active .xn-icon {
	color: #fff;
}

a.xn-nav__item.is-active:hover {
	background: var(--xn-primary);
	color: #fff;
}

/*
 * The active marker is drawn as an element rather than an inset shadow so it
 * can sit on the inline-start edge in both directions and keep its rounded
 * ends; a box-shadow could not be mirrored without hard-coding a side.
 */
.xn-nav__item.is-active::before {
	content: "";
	position: absolute;
	inset-inline-start: 0;
	inset-block: 20%;
	width: 3px;
	border-radius: 0 var(--xn-radius-sm) var(--xn-radius-sm) 0;
	background: var(--xn-accent);
}

[dir="rtl"] .xn-nav__item.is-active::before {
	border-radius: var(--xn-radius-sm) 0 0 var(--xn-radius-sm);
}

.xn-nav__item.is-pending {
	cursor: default;
	opacity: 0.62;
}

.xn-sidebar__foot {
	display: flex;
	flex-direction: column;
	gap: var(--xn-space-3);
	padding-block-start: var(--xn-space-3);
	padding-inline: var(--xn-space-2);
	border-block-start: 1px solid var(--xn-border);
	color: var(--xn-text-muted);
	flex: none;
}

/* Collapsed desktop sidebar -------------------------------------------- */

/*
 * Only the width and the label visibility change. The markup, the order and
 * the active treatment stay identical, so nothing has to be re-taught when
 * the rail is narrow — and the title attribute already on every item becomes
 * the tooltip.
 */
[data-sidebar="collapsed"] {
	--xn-sidebar-w: 76px;
}

@media (min-width: 901px) {
	[data-sidebar="collapsed"] .xn-nav__label,
	[data-sidebar="collapsed"] .xn-nav__badge,
	[data-sidebar="collapsed"] .xn-nav__section-label,
	[data-sidebar="collapsed"] .xn-sidebar__version {
		/* Hidden from sight and from screen readers alike: the accessible
		   name still comes from the item's title. */
		display: none;
	}

	[data-sidebar="collapsed"] .xn-nav__item {
		justify-content: center;
		padding-inline: 0;
	}

	/* The caption collapses to the rule alone, which still separates the
	   groups without a label nobody can read. */
	[data-sidebar="collapsed"] .xn-nav__section {
		padding-inline: var(--xn-space-2);
	}

	[data-sidebar="collapsed"] .xn-sidebar__head,
	[data-sidebar="collapsed"] .xn-sidebar__foot {
		justify-content: center;
		align-items: center;
		padding-inline: 0;
	}

	[data-sidebar="collapsed"] .xn-logo {
		/* The wordmark cannot survive a 76px rail legibly, so the rail shows
		   the collapse control alone rather than a squashed logo. */
		display: none;
	}

	/* Points the other way once the rail is narrow. */
	[data-sidebar="collapsed"] .xn-sidebar__collapse .xn-icon {
		transform: scaleX(-1);
	}
}

/* Mobile drawer scrim -------------------------------------------------- */
.xn-scrim {
	position: fixed;
	inset: 0;
	z-index: 25;
	background: rgba(5, 5, 5, 0.55);
	opacity: 0;
	transition: opacity var(--xn-speed) var(--xn-ease);
}

.xn-scrim[hidden] {
	/* hidden alone is enough, but the explicit rule keeps a stray display
	   override from resurrecting an invisible tap-blocker. */
	display: none;
}

.xn-scrim.is-visible {
	opacity: 1;
}

/* Top bar -------------------------------------------------------------- */
.xn-topbar {
	display: flex;
	align-items: center;
	gap: var(--xn-space-4);
	min-height: var(--xn-topbar-h);
	padding-inline: var(--xn-space-5);
	background: var(--xn-surface);
	border-block-end: 1px solid var(--xn-border);
	position: sticky;
	inset-block-start: 0;
	z-index: 20;
	/*
	 * Keeps the menu trigger clear of a notch or a rounded corner. The
	 * fallback is the existing padding, so nothing moves on a desktop.
	 */
	padding-inline-start: max( var(--xn-space-5), env( safe-area-inset-left ) );
	padding-inline-end: max( var(--xn-space-5), env( safe-area-inset-right ) );
}

/* The trigger stays clear of the drawer it opens. */
.xn-topbar__menu {
	flex: none;
}

.xn-topbar__menu {
	display: none;
}

.xn-topbar__id {
	display: grid;
	line-height: 1.4;
}

.xn-topbar__name {
	font-weight: var(--xn-weight-title);
}

.xn-topbar__actions {
	display: flex;
	align-items: center;
	gap: var(--xn-space-3);
	margin-inline-start: auto;
}

.xn-topbar__actions form {
	margin: 0;
}

/* Main ----------------------------------------------------------------- */
.xn-main {
	padding: var(--xn-space-6) var(--xn-space-5) var(--xn-space-7);
}

.xn-page-head {
	margin-block-end: var(--xn-space-6);
}

.xn-grid {
	display: grid;
	gap: var(--xn-space-4);
	grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	align-items: start;
}

/* Responsive ----------------------------------------------------------- */
@media (max-width: 900px) {
	.xn-shell {
		grid-template-columns: 1fr;
	}

	.xn-topbar__menu {
		display: inline-flex;
	}

	/*
	 * Off-canvas drawer.
	 *
	 * It docks to the inline-START edge, which in Arabic is the right — the
	 * same side the sidebar occupies on a wide screen, and the same side the
	 * menu button sits on. inline-END is the left in Arabic, which is what
	 * previously docked the drawer against the wrong edge.
	 *
	 * The closed drawer is taken out of the layout entirely with visibility,
	 * not merely pushed past the edge. A fixed element parked beyond the
	 * viewport still counts as content the page can be scrolled to, which is
	 * what let the drawer be dragged half into view over the page.
	 */
	.xn-sidebar {
		position: fixed;
		inset-block: 0;
		inset-inline-start: 0;
		/* Never wider than the screen, and never wider than it needs to be. */
		width: min( 85vw, 320px );
		max-width: 100%;
		z-index: 30;
		gap: var(--xn-space-3);
		box-shadow: var(--xn-shadow);
		visibility: hidden;
		/*
		 * Transforms are not mirrored by direction: positive X is right in
		 * every direction. Docked against the right edge, the drawer hides by
		 * moving further right, and slides right-to-left into view.
		 */
		transform: translateX(100%);
		transition: transform var(--xn-speed) var(--xn-ease),
			visibility 0s linear var(--xn-speed);
	}

	/*
	 * In English the same inline-start edge is the left, so the drawer hides
	 * by moving left instead. Only the transform is overridden; the docking
	 * edge is logical and needs no override.
	 */
	[dir="ltr"] .xn-sidebar {
		transform: translateX(-100%);
	}

	.xn-sidebar.is-open,
	[dir="ltr"] .xn-sidebar.is-open {
		visibility: visible;
		transform: translateX(0);
		transition: transform var(--xn-speed) var(--xn-ease),
			visibility 0s;
	}

	/* The drawer scrolls within itself; the page behind it does not. */
	.xn-sidebar .xn-nav {
		overscroll-behavior: contain;
	}

	/* The drawer always carries a visible way out. */
	.xn-sidebar__close {
		display: inline-flex;
	}

	/* Collapsing is a desktop idea; the drawer is dismissed, not shrunk. */
	.xn-sidebar__collapse {
		display: none;
	}

	/* A collapsed preference must never narrow the drawer itself. */
	[data-sidebar="collapsed"] .xn-nav__label,
	[data-sidebar="collapsed"] .xn-nav__badge,
	[data-sidebar="collapsed"] .xn-nav__section-label,
	[data-sidebar="collapsed"] .xn-sidebar__version,
	[data-sidebar="collapsed"] .xn-logo {
		display: revert;
	}

	[data-sidebar="collapsed"] .xn-nav__item {
		justify-content: flex-start;
		padding-inline: var(--xn-space-3);
	}

	[data-sidebar="collapsed"] .xn-sidebar__head,
	[data-sidebar="collapsed"] .xn-sidebar__foot {
		justify-content: space-between;
		padding-inline: var(--xn-space-2);
	}

	.xn-main {
		padding: var(--xn-space-5) var(--xn-space-4) var(--xn-space-6);
	}

	.xn-title {
		font-size: 1.5rem;
	}
}

/* Scroll lock while the drawer is open, so the page behind cannot be
   scrolled or tapped by accident. */
.xn-body.xn-locked {
	overflow: hidden;
	/* Belt and braces: the page can never be dragged sideways to reveal a
	   parked drawer, on any viewport. */
	overscroll-behavior: contain;
}

.xn-body {
	overflow-x: clip;
}

/* Landscape phones: a short viewport needs the drawer to scroll. */
@media (max-width: 900px) and (max-height: 480px) {
	.xn-sidebar {
		gap: var(--xn-space-3);
		padding-block: var(--xn-space-3);
	}

	.xn-nav__item {
		padding-block: var(--xn-space-2);
	}
}

@media (max-width: 520px) {
	.xn-panel {
		padding: var(--xn-space-5);
	}

	.xn-topbar {
		padding-inline-start: max( var(--xn-space-4), env( safe-area-inset-left ) );
		padding-inline-end: max( var(--xn-space-4), env( safe-area-inset-right ) );
	}

	.xn-topbar__meta {
		display: none;
	}
}

/* External links under the authentication forms ------------------------ */
.xn-extlinks {
	display: flex;
	flex-wrap: wrap;
	gap: var(--xn-space-2);
	margin-block-start: var(--xn-space-5);
	padding-block-start: var(--xn-space-4);
	border-block-start: 1px solid var(--xn-border);
}

/* Icon-only, but still a 40px target: these are tapped on a phone. */
.xn-extlinks__item {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	color: var(--xn-text-muted);
	border: 1px solid transparent;
	border-radius: var(--xn-radius-md);
	transition: color var(--xn-speed) var(--xn-ease),
		border-color var(--xn-speed) var(--xn-ease),
		background-color var(--xn-speed) var(--xn-ease);
}

.xn-extlinks__item:hover {
	color: var(--xn-accent);
	background: var(--xn-surface-secondary);
	border-color: var(--xn-border);
	text-decoration: none;
}

/* A card that should span the full grid width when there is room. */
@media (min-width: 900px) {
	.xn-card--wide {
		grid-column: 1 / -1;
	}
}

/* Icon controls stay comfortable on small screens ---------------------- */
@media (max-width: 520px) {
	.xn-extlinks {
		/* Seven icons wrap into a tidy block instead of a cramped row. */
		justify-content: center;
		gap: var(--xn-space-1);
	}

	.xn-panel .xn-logo {
		--xn-logo-w: min(200px, 70%);
	}
}

/* Screen header with a primary action ---------------------------------- */
.xn-page-head--row {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--xn-space-4);
	flex-wrap: wrap;
}

/* Invoice document ----------------------------------------------------- */

.xn-doc {
	display: grid;
	gap: var(--xn-space-5);
	max-width: 100%;
}

.xn-doc__head {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--xn-space-4);
	padding-block-end: var(--xn-space-4);
	border-block-end: 1px solid var(--xn-border);
}

.xn-doc__logo {
	/* Sized by width so the supplied artwork keeps its own proportions. */
	width: 190px;
	max-width: 100%;
	height: auto;
}

.xn-doc__brand,
.xn-doc__meta {
	display: grid;
	gap: var(--xn-space-2);
	min-width: 0;
}

.xn-doc__meta {
	text-align: start;
}

.xn-doc__company {
	margin: 0;
	font-size: var(--xn-text-section);
	font-weight: var(--xn-weight-title);
	color: var(--xn-text);
}

.xn-doc__title {
	margin: 0;
	font-size: var(--xn-text-section);
	color: var(--xn-accent);
}

.xn-doc__subtitle {
	margin: 0 0 var(--xn-space-2);
	font-size: var(--xn-text-card);
	font-weight: var(--xn-weight-title);
	color: var(--xn-text-muted);
}

.xn-doc__ids {
	display: grid;
	gap: var(--xn-space-1);
	margin: 0;
	font-size: var(--xn-text-small);
}

.xn-doc__ids > div {
	display: flex;
	gap: var(--xn-space-2);
}

.xn-doc__ids dt {
	color: var(--xn-text-muted);
}

.xn-doc__ids dd {
	margin: 0;
}

/* Two parties side by side on a wide screen, stacked on a narrow one. */
.xn-doc__parties {
	display: grid;
	grid-template-columns: repeat( auto-fit, minmax( 260px, 1fr ) );
	gap: var(--xn-space-4);
}

.xn-doc__party {
	padding: var(--xn-space-4);
	background: var(--xn-surface-secondary);
	border-radius: var(--xn-radius-md);
	min-width: 0;
}

.xn-doc__totals {
	display: flex;
	justify-content: flex-start;
}

.xn-doc__totals .xn-deflist {
	width: min( 100%, 380px );
	padding: var(--xn-space-4);
	background: var(--xn-surface-secondary);
	border-radius: var(--xn-radius-md);
}

.xn-doc__grand {
	padding-block-start: var(--xn-space-3);
	border-block-start: 1px solid var(--xn-border);
	font-weight: var(--xn-weight-title);
	font-size: var(--xn-text-body);
}

/*
 * The two figures a reader looks for. Marked with weight and a rule rather
 * than with colour, so they survive a black-and-white printer — a highlight
 * that only exists in colour is no highlight on most office paper.
 */
.xn-doc__grand dt,
.xn-doc__grand dd,
.xn-doc__due dt,
.xn-doc__due dd {
	font-weight: var(--xn-weight-title);
	color: var(--xn-text);
}

.xn-doc__due {
	margin-block-start: var(--xn-space-2);
	padding: var(--xn-space-2) var(--xn-space-3);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-sm);
	background: var(--xn-surface-secondary);
}

.xn-doc__due dd {
	font-size: var(--xn-text-card);
}

.xn-doc__foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: var(--xn-space-4);
	padding-block-start: var(--xn-space-3);
	border-block-start: 1px solid var(--xn-border);
}

/* White behind the symbol in both modes; a scanner needs the contrast. */
.xn-doc__qr {
	line-height: 0;
	padding: var(--xn-space-2);
	background: #fff;
	border-radius: var(--xn-radius-sm);
}

.xn-doc__qr svg {
	display: block;
}

.xn-doc__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--xn-space-3);
	margin-block-start: var(--xn-space-5);
}

/*
 * Printing. The chrome of the application is not part of the document, so it
 * is dropped and the invoice is given the page in black on white — which is
 * what a customer files and an auditor reads.
 */
@media print {
	/*
	 * Paper is white whatever mode the screen was in. Redefining the tokens
	 * here rather than patching selector by selector is what makes every
	 * label legible: the muted grey is designed to sit on a dark surface, and
	 * on paper it was so pale it read as blank.
	 *
	 * The type scale is redefined with them. A screen invoice can breathe;
	 * an A4 sheet cannot, and oversized type was what pushed the QR code onto
	 * a second page. The hierarchy is kept — headings still outrank labels —
	 * but every step is smaller, so this is a re-typesetting rather than a
	 * scaling down of the whole document.
	 */
	:root,
	[data-theme="dark"],
	[data-theme="light"] {
		--xn-bg: #fff;
		--xn-surface: #fff;
		--xn-surface-secondary: #fff;
		--xn-text: #000;
		--xn-text-muted: #444;
		--xn-border: #bbb;
		--xn-shadow: none;

		--xn-text-page: 1.15rem;
		--xn-text-section: 1.05rem;
		--xn-text-card: 0.85rem;
		--xn-text-body: 0.78rem;
		--xn-text-small: 0.7rem;

		/* Tighter rhythm throughout, applied through the same scale the
		   document already uses rather than as one-off margins. */
		--xn-space-1: 2px;
		--xn-space-2: 4px;
		--xn-space-3: 6px;
		--xn-space-4: 8px;
		--xn-space-5: 10px;
		--xn-space-6: 14px;
	}

	body,
	.xn-body {
		background: #fff;
		color: #000;
		line-height: 1.35;
	}

	.xn-sidebar,
	.xn-topbar,
	.xn-scrim,
	.xn-no-print {
		display: none !important;
	}

	.xn-shell {
		display: block;
	}

	.xn-main,
	.xn-public {
		padding: 0;
		min-height: 0;
	}

	.xn-card,
	.xn-public__card,
	.xn-doc__party,
	.xn-doc__totals .xn-deflist {
		background: #fff;
		border: 0;
		box-shadow: none;
		color: #000;
		padding: 0;
	}

	/* Labels, headings and figures alike: black on white, nothing faint. */
	.xn-doc,
	.xn-doc *,
	.xn-table,
	.xn-table th,
	.xn-table td,
	.xn-deflist dt,
	.xn-deflist dd,
	.xn-doc__subtitle,
	.xn-doc__ids dt,
	.xn-doc__ids dd,
	.xn-small,
	.xn-muted {
		color: #000;
	}

	.xn-table th,
	.xn-table td {
		padding-block: 3px;
		border-color: #bbb;
	}

	.xn-deflist > div {
		border-color: #bbb;
	}

	.xn-doc {
		gap: var(--xn-space-4);
	}

	.xn-doc__logo {
		width: 150px;
	}

	/* The parties sit side by side on paper even on a narrow device. */
	.xn-doc__parties {
		grid-template-columns: 1fr 1fr;
		gap: var(--xn-space-4);
	}

	.xn-doc__party {
		border: 1px solid #bbb;
		padding: var(--xn-space-3);
	}

	.xn-doc__totals .xn-deflist {
		width: min( 100%, 300px );
	}

	/* The two figures that matter keep their emphasis in black and white. */
	.xn-doc__due {
		border: 1px solid #000;
		background: #fff;
	}

	.xn-doc__grand {
		border-block-start: 1px solid #000;
	}

	/* Keeps the brand and the code from being dropped as "background". */
	.xn-doc__logo,
	.xn-doc__qr {
		-webkit-print-color-adjust: exact;
		print-color-adjust: exact;
	}

	.xn-doc__qr {
		padding: 6px;
	}

	.xn-doc__qr svg {
		/* Large enough to scan reliably off paper, no larger. */
		width: 96px;
		height: 96px;
	}

	/* A table split across a page break is unreadable as a bill. */
	.xn-doc__lines tr {
		break-inside: avoid;
	}

	/*
	 * The whole invoice on one sheet: nothing inside it may break, and the
	 * footer that carries the code must not be orphaned onto page two.
	 */
	.xn-doc,
	.xn-doc__foot,
	.xn-doc__totals,
	.xn-doc__parties {
		break-inside: avoid;
	}

	.xn-doc__foot {
		break-before: avoid;
	}

	@page {
		size: A4;
		margin: 10mm;
	}
}

/* The customer's invoice page ------------------------------------------ */

/*
 * Stands alone: no shell, no sidebar, no navigation. Sized for a phone first,
 * because that is what scans a printed code.
 */
.xn-public {
	display: flex;
	justify-content: center;
	padding: var(--xn-space-4);
	padding-block-start: max( var(--xn-space-4), env( safe-area-inset-top ) );
	padding-block-end: max( var(--xn-space-6), env( safe-area-inset-bottom ) );
	min-height: 100vh;
	min-height: 100dvh;
	background: var(--xn-bg);
}

.xn-public__card {
	width: min( 100%, 860px );
	padding: var(--xn-space-5);
	background: var(--xn-surface);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-lg);
	min-width: 0;
}

.xn-public__card--empty {
	display: grid;
	justify-items: center;
	gap: var(--xn-space-4);
	text-align: center;
	align-content: center;
	padding-block: var(--xn-space-6);
}

.xn-public__title {
	margin: 0;
	font-size: var(--xn-text-section);
	color: var(--xn-text);
}

@media (max-width: 600px) {
	.xn-public {
		padding-inline: var(--xn-space-3);
	}

	.xn-public__card {
		padding: var(--xn-space-4);
	}

	/* The header stacks rather than squeezing the identifiers. */
	.xn-public .xn-doc__head {
		flex-direction: column;
	}
}

/* WhatsApp conversations ------------------------------------------------ */

/*
 * Two columns on a desktop, one on a phone. The list collapses above the
 * thread rather than beside it, because a 320px column holds neither well.
 */
.xn-inbox {
	display: grid;
	/* List, thread, customer. The customer column is the first to go on a
	   narrow screen, because the thread is what the work happens in. */
	grid-template-columns: minmax( 200px, 260px ) 1fr minmax( 220px, 280px );
	gap: var(--xn-space-4);
	min-height: 60vh;
	min-width: 0;
}

.xn-inbox__list,
.xn-inbox__thread {
	min-width: 0;
}

.xn-threads {
	display: grid;
	gap: var(--xn-space-1);
	margin: var(--xn-space-3) 0 0;
	padding: 0;
	list-style: none;
	max-height: 60vh;
	overflow-y: auto;
	overscroll-behavior: contain;
}

.xn-thread {
	display: grid;
	gap: 2px;
	width: 100%;
	padding: var(--xn-space-3);
	text-align: start;
	background: var(--xn-surface-secondary);
	border: 1px solid transparent;
	border-radius: var(--xn-radius-md);
	color: var(--xn-text);
	cursor: pointer;
	position: relative;
}

.xn-thread:hover {
	border-color: var(--xn-border);
}

.xn-thread.is-active {
	background: var(--xn-primary);
	color: #fff;
}

.xn-thread.is-active .xn-thread__meta {
	color: rgba(255, 255, 255, 0.75);
}

.xn-thread__name {
	font-weight: var(--xn-weight-title);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Sits on the row rather than pushing the name out of shape. */
.xn-thread__unread {
	position: absolute;
	inset-block-start: var(--xn-space-3);
	inset-inline-end: var(--xn-space-3);
	min-width: 18px;
	padding-inline: 5px;
	border-radius: 999px;
	background: var(--xn-accent);
	color: #1a1300;
	font-size: 0.6875rem;
	line-height: 18px;
	text-align: center;
}

.xn-inbox__panel {
	display: flex;
	flex-direction: column;
	gap: var(--xn-space-3);
	height: 100%;
}

.xn-inbox__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--xn-space-3);
	padding-block-end: var(--xn-space-3);
	border-block-end: 1px solid var(--xn-border);
}

.xn-inbox__empty {
	display: grid;
	place-items: center;
	min-height: 40vh;
}

.xn-messages {
	display: grid;
	gap: var(--xn-space-2);
	align-content: start;
	max-height: 48vh;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding-inline-end: var(--xn-space-2);
}

.xn-message {
	max-width: min( 82%, 520px );
	padding: var(--xn-space-2) var(--xn-space-3);
	border-radius: var(--xn-radius-md);
	background: var(--xn-surface-secondary);
	border: 1px solid var(--xn-border);
}

/* The customer's messages start the line; ours end it. */
.xn-message--in {
	justify-self: start;
}

.xn-message--out {
	justify-self: end;
	background: color-mix(in srgb, var(--xn-primary) 22%, var(--xn-surface));
}

.xn-message__body {
	margin: 0;
	white-space: pre-wrap;
	word-break: break-word;
}

.xn-message__meta {
	display: block;
	margin-block-start: 2px;
}

.xn-composer {
	display: flex;
	gap: var(--xn-space-2);
	align-items: flex-end;
}

.xn-composer textarea {
	flex: 1;
	resize: vertical;
	min-height: 44px;
}

@media (max-width: 900px) {
	.xn-threads {
		max-height: 34vh;
	}
}

.xn-inbox__customer {
	min-width: 0;
	padding: var(--xn-space-4);
	background: var(--xn-surface-secondary);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-md);
	align-self: start;
	max-height: 60vh;
	overflow-y: auto;
}

.xn-plainlist {
	margin: var(--xn-space-2) 0 0;
	padding-inline-start: var(--xn-space-4);
	display: grid;
	gap: var(--xn-space-1);
	font-size: var(--xn-text-small);
	color: var(--xn-text-muted);
}

.xn-templatebar {
	display: flex;
	justify-content: flex-start;
}

@media (max-width: 1200px) {
	.xn-inbox {
		grid-template-columns: minmax( 200px, 260px ) 1fr;
	}

	/* Below the thread rather than beside it. */
	.xn-inbox__customer {
		grid-column: 1 / -1;
		max-height: none;
	}
}

/*
 * One column on a phone. This block has to come after the one above, or the
 * wider rule wins on cascade order and forces two columns into 380px — which
 * squeezed the thread so narrowly that Arabic wrapped one character per line.
 */
@media (max-width: 900px) {
	.xn-inbox {
		grid-template-columns: 1fr;
		gap: var(--xn-space-3);
	}

	.xn-inbox__customer {
		grid-column: auto;
	}

	/* The list becomes a short, scrollable strip above the thread. */
	.xn-threads {
		max-height: 30vh;
	}

	.xn-messages {
		max-height: 52vh;
	}

	/* Bubbles may take nearly the full width; there is no second column
	   to leave room for. */
	.xn-message {
		max-width: 92%;
	}

	.xn-inbox__head {
		flex-wrap: wrap;
		gap: var(--xn-space-2);
	}

	/* The composer stays on one line, with the field taking the room. */
	.xn-composer {
		gap: var(--xn-space-2);
	}

	.xn-composer textarea {
		min-width: 0;
	}

	/* Attachment controls wrap instead of pushing the layout sideways. */
	.xn-templatebar {
		flex-wrap: wrap;
	}

	.xn-templatebar .xn-btn {
		flex: 1 1 auto;
	}

	.xn-attach {
		flex-wrap: wrap;
	}

	.xn-attach__name {
		max-width: 100%;
	}

	.xn-message__audio {
		width: 100%;
	}
}

/* WhatsApp attachments -------------------------------------------------- */

.xn-templatebar {
	flex-wrap: wrap;
	gap: var(--xn-space-2);
}

.xn-attach {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--xn-space-2);
	padding: var(--xn-space-3);
	background: var(--xn-surface-secondary);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-md);
}

/* Small enough to sit in the composer, large enough to recognise. */
.xn-attach__preview {
	width: 56px;
	height: 56px;
	object-fit: cover;
	border-radius: var(--xn-radius-sm);
}

.xn-attach__name {
	font-size: var(--xn-text-small);
	color: var(--xn-text-muted);
	max-width: 220px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.xn-attach input[type="text"] {
	flex: 1;
	min-width: 140px;
}

.xn-message__audio {
	display: block;
	margin-block-start: var(--xn-space-2);
	width: min( 260px, 100% );
}

.xn-message__image {
	display: block;
	margin-block-start: var(--xn-space-2);
	max-width: 100%;
	border-radius: var(--xn-radius-sm);
}

.xn-message__file {
	display: inline-block;
	margin-block-start: var(--xn-space-2);
	font-size: var(--xn-text-small);
	word-break: break-word;
}

/* The customer's booking page ------------------------------------------ */

/*
 * Public and phone-first: most customers arrive from a link on their own
 * handset, so the controls are large and the page is one readable column.
 */
.xn-booking {
	align-items: flex-start;
	/*
	 * No minimum height at all. Forcing a screenful here is what left a band
	 * of empty white under a short step: the wrapper kept its full height
	 * while the card shrank. The page background reaches the bottom of the
	 * screen from the document itself instead.
	 */
	min-height: 0;
}

/* The background belongs to the page, not to a box inside it. */
html:has( .xn-booking ),
body:has( .xn-booking ) {
	background: var(--xn-bg);
	min-height: 100%;
}

.xn-booking .xn-public__card {
	width: min( 100%, 560px );
	/* Only as tall as what it holds. */
	height: auto;
	align-self: flex-start;
}

.xn-booking__head {
	display: grid;
	justify-items: center;
	gap: var(--xn-space-2);
	padding-block-end: var(--xn-space-4);
	border-block-end: 1px solid var(--xn-border);
	margin-block-end: var(--xn-space-4);
}

.xn-booking__logo {
	width: 160px;
	height: auto;
}

.xn-booking__tagline {
	margin: 0;
	color: var(--xn-text-muted);
	font-size: var(--xn-text-small);
}

.xn-booking__title {
	margin: 0 0 var(--xn-space-2);
	font-size: var(--xn-text-section);
	color: var(--xn-text);
}

.xn-booking__subtitle {
	margin: 0 0 var(--xn-space-2);
	font-size: var(--xn-text-card);
	color: var(--xn-text);
}

.xn-booking__grid {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--xn-space-3);
}

.xn-booking__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--xn-space-2);
	margin-block-start: var(--xn-space-4);
}

.xn-booking__thanks {
	margin-block-start: var(--xn-space-4);
	padding: var(--xn-space-4);
	background: var(--xn-surface-secondary);
	border-radius: var(--xn-radius-md);
	text-align: center;
	line-height: 1.9;
}

/* Choices are whole tappable rows, not small boxes beside a label. */
.xn-choices {
	display: grid;
	gap: var(--xn-space-2);
}

.xn-choice {
	display: flex;
	align-items: center;
	gap: var(--xn-space-3);
	padding: var(--xn-space-3) var(--xn-space-4);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-md);
	background: var(--xn-surface-secondary);
	cursor: pointer;
	min-height: 52px;
}

.xn-choice:hover {
	border-color: var(--xn-primary);
}

.xn-choice input {
	width: 20px;
	height: 20px;
	accent-color: var(--xn-primary);
	flex: none;
}

.xn-input--lg {
	font-size: var(--xn-text-card);
	padding-block: var(--xn-space-3);
}

.xn-btn--block {
	display: flex;
	width: 100%;
	justify-content: center;
	margin-block-start: var(--xn-space-4);
	min-height: 52px;
}

@media (max-width: 600px) {
	.xn-booking__grid {
		grid-template-columns: 1fr;
	}
}

/* The customer's own bookings ------------------------------------------ */

.xn-bookings {
	display: grid;
	gap: var(--xn-space-2);
	margin-block: var(--xn-space-2) var(--xn-space-4);
}

.xn-bookingcard {
	display: grid;
	gap: var(--xn-space-1);
	padding: var(--xn-space-3);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-md);
	background: var(--xn-surface-secondary);
	justify-items: start;
}

.xn-bookingcard__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--xn-space-3);
	width: 100%;
}

/* Past bookings are there to be looked up, not to be scrolled past. */
.xn-booking__past summary {
	cursor: pointer;
	padding: var(--xn-space-2) 0;
	font-size: var(--xn-text-small);
	color: var(--xn-text-muted);
}

/* Filters above a table --------------------------------------------------- */

.xn-filters {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: var(--xn-space-3);
}

/* The live board ------------------------------------------------------- */

/*
 * Figures wrap to whatever the screen allows rather than sitting at a column
 * count that only suits a desktop. On a phone this becomes one per row
 * without any breakpoint of its own.
 */
.xn-grid--stats {
	display: grid;
	/*
	 * 210px rather than 150. A wide screen was fitting six or seven narrow
	 * tracks, so a figure like "46,600.00 ر.س" wrapped inside a card barely
	 * wider than the number itself.
	 */
	grid-template-columns: repeat( auto-fit, minmax( 210px, 1fr ) );
	gap: var(--xn-space-3);
}

/* Never more than four across: past that the cards stop reading as a set. */
@media (min-width: 1100px) {
	.xn-grid--stats {
		grid-template-columns: repeat( 4, minmax( 0, 1fr ) );
	}
}

.xn-grid--stats .xn-stat__value {
	font-size: var(--xn-text-section);
	line-height: 1.25;
	/* A long figure shrinks rather than breaking across two lines. */
	overflow-wrap: anywhere;
}

.xn-grid--stats .xn-stat__label {
	font-size: var(--xn-text-small);
	color: var(--xn-text-muted);
}

/* Equal height, so a card with a note does not tower over one without. */
.xn-grid--stats .xn-card {
	min-height: 118px;
}

.xn-grid--stats .xn-stat {
	display: grid;
	gap: var(--xn-space-1);
	align-content: start;
}

/* The one figure the board exists for, marked as such. */
.xn-stat--accent {
	border-color: color-mix(in srgb, var(--xn-primary) 45%, var(--xn-border));
}

.xn-stat--accent .xn-stat__value {
	color: var(--xn-primary);
}

.xn-stat__note {
	display: block;
	line-height: 1.5;
}

/* Booking: the returning customer ---------------------------------------- */

/*
 * A returning customer greeted by name and number — the two things they
 * recognise themselves by — rather than a sentence about data storage.
 */
.xn-welcomecard {
	display: grid;
	gap: 2px;
	padding: var(--xn-space-4);
	border: 1px solid color-mix(in srgb, var(--xn-primary) 30%, var(--xn-border));
	border-radius: var(--xn-radius-md);
	background: color-mix(in srgb, var(--xn-primary) 6%, var(--xn-surface));
	margin-block-end: var(--xn-space-4);
}

.xn-welcomecard__hello {
	font-size: var(--xn-text-small);
	color: var(--xn-text-muted);
}

.xn-welcomecard__name {
	font-size: var(--xn-text-card);
	color: var(--xn-text);
}

.xn-welcomecard__phone {
	font-size: var(--xn-text-small);
	color: var(--xn-text-muted);
	direction: ltr;
	text-align: start;
}

/* Adding a car: a real alternative, not a footnote under the list. */
.xn-addcar {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--xn-space-2);
	width: 100%;
	min-height: 52px;
	margin-block-start: var(--xn-space-2);
	padding: var(--xn-space-3);
	border: 1px dashed color-mix(in srgb, var(--xn-primary) 45%, var(--xn-border));
	border-radius: var(--xn-radius-md);
	background: transparent;
	color: var(--xn-primary);
	font-weight: var(--xn-weight-title);
	cursor: pointer;
}

.xn-addcar:hover {
	background: color-mix(in srgb, var(--xn-primary) 8%, transparent);
}

.xn-addcar__sign {
	font-size: 1.25rem;
	line-height: 1;
}

/* Forward and back, with forward taking the room. */
.xn-booking__nav {
	display: flex;
	gap: var(--xn-space-2);
	margin-block-start: var(--xn-space-4);
}

.xn-btn--grow {
	flex: 1;
	justify-content: center;
	min-height: 52px;
}

.xn-booking__nav .xn-btn {
	min-height: 52px;
}

/* Reaching a person ------------------------------------------------------ */

.xn-booking__foot {
	margin-block-start: var(--xn-space-5);
	padding-block-start: var(--xn-space-4);
	border-block-start: 1px solid var(--xn-border);
	display: grid;
	justify-items: center;
	gap: var(--xn-space-2);
	text-align: center;
}

.xn-whatsapp {
	display: inline-flex;
	align-items: center;
	gap: var(--xn-space-2);
	padding: var(--xn-space-3) var(--xn-space-5);
	border-radius: 999px;
	/* WhatsApp's own green: recognised faster than any label. */
	background: #25D366;
	color: #fff;
	font-weight: var(--xn-weight-title);
	min-height: 48px;
}

.xn-whatsapp:hover {
	background: #1EBE5A;
	color: #fff;
}

.xn-whatsapp svg {
	width: 22px;
	height: 22px;
}

/* Booking: the appointment step ----------------------------------------- */

/*
 * Three questions in three blocks rather than one long list of identical
 * rows, so a customer can see where they are and what is left.
 */
.xn-pickblock {
	padding: var(--xn-space-4);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-md);
	margin-block-end: var(--xn-space-4);
}

.xn-pickblock__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--xn-space-3);
	margin-block-end: var(--xn-space-2);
}

.xn-pickblock__title {
	margin: 0;
	font-size: var(--xn-text-card);
	color: var(--xn-text);
}

/* Answered turns the count from grey to the brand colour. */
.xn-pickblock__count {
	font-size: var(--xn-text-small);
	color: var(--xn-text-muted);
}

.xn-pickblock__count.is-chosen {
	color: var(--xn-primary);
	font-weight: var(--xn-weight-title);
}

/* A chosen row is obvious without reading its tick. */
.xn-choice:has( input:checked ) {
	border-color: var(--xn-primary);
	background: color-mix(in srgb, var(--xn-primary) 8%, var(--xn-surface));
}

/* The way out, offered only on the first step. */
.xn-booking__foot {
	margin-block-start: var(--xn-space-5);
	padding-block-start: var(--xn-space-4);
	border-block-start: 1px solid var(--xn-border);
	display: grid;
	justify-items: center;
	gap: var(--xn-space-2);
	text-align: center;
}

/* Sign in ---------------------------------------------------------------- */

/*
 * The first screen anyone sees. It gets room to breathe and a single obvious
 * action rather than a form squeezed against the top of a card.
 */
.xn-login {
	padding: var(--xn-space-4);
	background:
		radial-gradient( ellipse at top, color-mix( in srgb, var(--xn-primary) 14%, transparent ), transparent 60% ),
		var(--xn-bg);
}

.xn-login__panel {
	width: min( 100%, 420px );
	padding: var(--xn-space-6) var(--xn-space-5);
}

.xn-login__brand {
	display: grid;
	justify-items: center;
	margin-block-end: var(--xn-space-4);
}

.xn-login__title {
	text-align: center;
	margin-block-end: var(--xn-space-1);
}

.xn-login__lead {
	text-align: center;
	margin-block-end: var(--xn-space-5);
}

/* A full-width action, sized for a thumb. */
.xn-login__submit {
	min-height: 52px;
	margin-block-start: var(--xn-space-4);
}

.xn-login__foot {
	margin-block-start: var(--xn-space-5);
	padding-block-start: var(--xn-space-4);
	border-block-start: 1px solid var(--xn-border);
	text-align: center;
}

@media (max-width: 600px) {
	.xn-login__panel {
		padding: var(--xn-space-5) var(--xn-space-4);
	}
}

/* Stage buttons ---------------------------------------------------------- */

/*
 * The order of the work, readable at a glance: what can be done now stands
 * out, what is finished or not yet reachable recedes without disappearing.
 */
.xn-stage-buttons {
	display: flex;
	flex-wrap: wrap;
	gap: var(--xn-space-2);
}

.xn-stage-buttons .xn-btn {
	min-height: 46px;
}

/* Unreachable, not invisible: the shape of the workflow stays legible. */
.xn-stage-buttons .xn-btn:disabled {
	opacity: 0.45;
	cursor: not-allowed;
}

/* Taking a payment beside the balance ------------------------------------ */

.xn-payform {
	/*
	 * Its own block rather than three columns squeezed into a narrow card.
	 * Taking money is a deliberate act and deserves the room to be read
	 * before it is done.
	 */
	display: grid;
	gap: var(--xn-space-3);
	margin-block-start: var(--xn-space-4);
	padding: var(--xn-space-4);
	border: 1px solid color-mix(in srgb, var(--xn-primary) 30%, var(--xn-border));
	border-radius: var(--xn-radius-md);
	background: color-mix(in srgb, var(--xn-primary) 4%, var(--xn-surface));
}

.xn-payform__title {
	margin: 0;
	font-size: var(--xn-text-card);
	color: var(--xn-text);
}

/* The amount is the field that matters; it reads large. */
.xn-payform input[type="number"] {
	font-size: var(--xn-text-card);
	min-height: 52px;
}

.xn-payform select {
	min-height: 52px;
}

.xn-payform .xn-btn {
	min-height: 52px;
	justify-content: center;
}

@media (min-width: 700px) {
	.xn-payform__fields {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: var(--xn-space-3);
	}
}

@media (max-width: 700px) {
	.xn-payform {
		grid-template-columns: 1fr;
	}
}

/* The permission matrix -------------------------------------------------- */

/*
 * A block per module rather than a fixed three-column table: modules define
 * different actions, and the ones with their own — approving a discount,
 * administering WhatsApp — had no column to live in.
 */
.xn-permgrid {
	display: grid;
	gap: var(--xn-space-3);
	margin-block-start: var(--xn-space-3);
}

.xn-permgrid__module {
	padding: var(--xn-space-3) var(--xn-space-4);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-md);
	background: var(--xn-surface-secondary);
}

.xn-permgrid__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--xn-space-3);
	margin-block-end: var(--xn-space-2);
}

.xn-permgrid__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--xn-space-2);
}

/* A whole tappable row, not a small box beside text. */
.xn-permgrid__action {
	display: inline-flex;
	align-items: center;
	gap: var(--xn-space-2);
	padding: var(--xn-space-2) var(--xn-space-3);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-sm);
	background: var(--xn-surface);
	cursor: pointer;
	min-height: 40px;
	font-size: var(--xn-text-small);
}

.xn-permgrid__action:has( input:checked ) {
	border-color: var(--xn-primary);
	background: color-mix(in srgb, var(--xn-primary) 8%, var(--xn-surface));
}

.xn-permgrid__action input {
	width: 18px;
	height: 18px;
	accent-color: var(--xn-primary);
}

/* What this account may open --------------------------------------------- */

.xn-shortcuts {
	display: grid;
	grid-template-columns: repeat( auto-fit, minmax( 150px, 1fr ) );
	gap: var(--xn-space-2);
	margin-block-start: var(--xn-space-3);
}

.xn-shortcut {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 52px;
	padding: var(--xn-space-3);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-md);
	background: var(--xn-surface-secondary);
	color: var(--xn-text);
	font-weight: var(--xn-weight-title);
	text-align: center;
}

.xn-shortcut:hover {
	border-color: var(--xn-primary);
	color: var(--xn-primary);
}

/* Granted permissions, per module ---------------------------------------- */

.xn-permlist {
	display: grid;
	gap: var(--xn-space-2);
	margin-block-start: var(--xn-space-2);
}

.xn-permlist__row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--xn-space-3);
	padding: var(--xn-space-3);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-md);
	background: var(--xn-surface-secondary);
}

.xn-permlist__marks {
	display: flex;
	flex-wrap: wrap;
	gap: var(--xn-space-1);
}

/* The work order, read as a document ------------------------------------- */

/*
 * A fixed pair of columns. The general grid fits as many tracks as the
 * viewport allows, which on a wide screen left two cards stranded beside an
 * empty half-row.
 */
.xn-grid--profile {
	grid-template-columns: repeat(2, minmax(0, 1fr));
}

.xn-grid--profile .xn-card--wide {
	grid-column: 1 / -1;
}

@media (max-width: 900px) {
	.xn-grid--profile {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* Form sections ---------------------------------------------------------- */

/*
 * A heading per group. The services form ran name, price, duration, stages
 * and warranty together as one flat list of placeholder-only boxes, so it
 * read as a wall rather than a set of decisions.
 */
.xn-formsection {
	margin: var(--xn-space-4) 0 0;
	padding-block-start: var(--xn-space-3);
	border-block-start: 1px solid var(--xn-border);
	font-size: var(--xn-text-small);
	font-weight: var(--xn-weight-title);
	color: var(--xn-text-muted);
}

.xn-formsection:first-of-type {
	margin-block-start: var(--xn-space-2);
}

/* Columns a phone has no room for ---------------------------------------- */

/*
 * Eight columns do not fit a phone, and a table that scrolls sideways hides
 * the status — the one column a person is actually scanning for.
 */
@media (max-width: 780px) {
	.xn-col--wide {
		display: none;
	}
}

/* Period comparison ------------------------------------------------------ */

.xn-stat__delta {
	display: block;
	margin-block-start: var(--xn-space-1);
}

/*
 * Direction is carried by the arrow as well as the colour, so the reading
 * survives a monochrome print and a reader who cannot separate red from green.
 */
.xn-delta--up {
	color: var(--xn-success);
}

.xn-delta--down {
	color: var(--xn-error);
}

/* Shown on paper only ---------------------------------------------------- */

.xn-printonly {
	display: none;
}

@media print {
	.xn-printonly {
		display: block;
		margin-block-end: var(--xn-space-4);
		font-weight: var(--xn-weight-title);
	}

	/*
	 * The filter form and the print button are controls, not content; a sheet
	 * carrying a "Print" button is a sheet that wastes a line explaining
	 * nothing.
	 */
	.xn-filters,
	[data-xn-report-print] {
		display: none !important;
	}

	/* A table split across a page break is unreadable. */
	.xn-card {
		break-inside: avoid;
	}
}

/* Charts ----------------------------------------------------------------- */

/*
 * Drawn as inline SVG rather than by a library from a CDN, which would put a
 * third party between this business and its own numbers.
 */
.xn-chart {
	margin-block-start: var(--xn-space-3);
	color: var(--xn-text);
}

.xn-chart svg {
	display: block;
	overflow: visible;
}

.xn-chart--short svg {
	max-height: 280px;
}

.xn-chart__ends {
	display: flex;
	justify-content: space-between;
	margin-block-start: var(--xn-space-2);
	direction: ltr;
}

@media print {
	/* The drawing is the point of the page; it must not be split. */
	.xn-chart {
		break-inside: avoid;
	}

	[data-xn-analytics-print] {
		display: none !important;
	}
}

/* A report on paper ------------------------------------------------------ */

@media print {
	/*
	 * A printed report is read without the screen's chrome around it, so the
	 * cards stop being cards: the shadow, the radius and the fill are all
	 * screen affordances that cost ink and say nothing on a page.
	 */
	.xn-card {
		box-shadow: none !important;
		border: 1px solid #999 !important;
		border-radius: 0 !important;
		background: #fff !important;
		padding: 10px 12px !important;
		margin-block-end: 10px !important;
	}

	/* The stat cards read as a compact row rather than four tall boxes. */
	.xn-grid--stats {
		grid-template-columns: repeat(4, 1fr) !important;
		gap: 6px !important;
	}

	.xn-stat__value {
		font-size: 13pt !important;
	}

	.xn-stat__label,
	.xn-stat__note,
	.xn-stat__delta {
		font-size: 8pt !important;
	}

	/* Ruled rows: a table of figures is unreadable without them on paper. */
	.xn-table th,
	.xn-table td {
		border-block-end: 1px solid #ccc !important;
		padding: 4px 6px !important;
		font-size: 9pt !important;
	}

	.xn-table thead th {
		border-block-end: 1px solid #333 !important;
		font-weight: 700 !important;
	}

	/*
	 * A heading stranded at the foot of a page with its table overleaf is
	 * the commonest fault in a printed report.
	 */
	.xn-card__title {
		break-after: avoid;
		font-size: 11pt !important;
	}

	.xn-tablewrap {
		overflow: visible !important;
	}

	/* Columns dropped on a narrow screen belong on paper, which is wide. */
	.xn-col--wide {
		display: table-cell !important;
	}

	/*
	 * The page itself is already set for the invoice sheet at A4/10mm; a
	 * second @page here would silently reset it for every printed document.
	 */
}

/* The message as the customer will see it -------------------------------- */

.xn-preview {
	margin-block-start: var(--xn-space-2);
}

/*
 * Shaped like a WhatsApp bubble on purpose: the point is to judge what the
 * customer will see, and a plain box invites judging the words alone.
 */
.xn-preview__bubble {
	max-width: 420px;
	padding: var(--xn-space-3);
	border-radius: var(--xn-radius-md);
	background: color-mix(in srgb, var(--xn-primary) 6%, var(--xn-surface));
	border: 1px solid var(--xn-border);
}

.xn-preview__image {
	display: block;
	width: 100%;
	max-height: 200px;
	object-fit: cover;
	border-radius: var(--xn-radius-sm);
	margin-block-end: var(--xn-space-2);
}

.xn-preview__body {
	margin: 0;
	white-space: pre-wrap;
	line-height: 1.7;
}

.xn-preview__button {
	display: block;
	margin-block-start: var(--xn-space-2);
	padding-block-start: var(--xn-space-2);
	border-block-start: 1px solid var(--xn-border);
	color: var(--xn-primary);
	text-align: center;
	font-weight: var(--xn-weight-title);
}

/* ======================================================================
 * WhatsApp Settings → Offers — the offer editor.
 *
 * Every selector below is inside .xn-offer-modal or begins with .xn-offer,
 * so nothing here can reach another screen. No existing class is redefined
 * and no element selector is used bare: the rest of the operations app is
 * untouched by this block.
 *
 * Written for a phone first. The panel is a full-height sheet under 640px
 * with its own scrolling body and a footer that stays put, because the
 * editor is long and the save button must never be somewhere a person has
 * to hunt for.
 * ====================================================================== */

.xn-offer-modal .xn-offer-panel {
	max-width: 640px;
	display: flex;
	flex-direction: column;
	padding: 0;
	max-height: 92vh;
	overflow: hidden;
}

.xn-offer-modal .xn-modal__head {
	padding: var(--xn-space-5) var(--xn-space-5) var(--xn-space-3);
	border-block-end: 1px solid var(--xn-border);
	margin-block-end: 0;
	align-items: flex-start;
}

/* The state the calendar decided, said next to the title. */
.xn-offer-state {
	display: inline-block;
	margin-block-start: var(--xn-space-1);
	padding: 2px var(--xn-space-3);
	border-radius: var(--xn-radius-pill);
	border: 1px solid var(--xn-border);
	background: var(--xn-surface-secondary);
	color: var(--xn-text-muted);
	font-size: var(--xn-text-small);
	font-weight: var(--xn-weight-title);
}

.xn-offer-state[data-state="active"] {
	color: var(--xn-success);
	border-color: color-mix(in srgb, var(--xn-success) 45%, transparent);
}

.xn-offer-state[data-state="scheduled"] {
	color: var(--xn-info);
	border-color: color-mix(in srgb, var(--xn-info) 45%, transparent);
}

.xn-offer-state[data-state="expired"],
.xn-offer-state[data-state="disabled"] {
	color: var(--xn-warning);
	border-color: color-mix(in srgb, var(--xn-warning) 45%, transparent);
}

/* The form scrolls; the footer does not. */
.xn-offer-form {
	display: block;
	flex: 1 1 auto;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
	padding: var(--xn-space-4) var(--xn-space-5) 0;
	min-height: 0;
}

.xn-offer-section {
	padding-block: var(--xn-space-4);
	border-block-end: 1px solid var(--xn-border);
}

.xn-offer-section:last-of-type {
	border-block-end: 0;
}

.xn-offer-section__title {
	margin: 0 0 var(--xn-space-3);
	font-size: var(--xn-text-card);
	font-weight: var(--xn-weight-title);
	color: var(--xn-text);
}

.xn-offer-grid {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--xn-space-3) var(--xn-space-4);
}

.xn-offer-grid > .xn-field--full {
	grid-column: 1 / -1;
}

/*
 * A hidden field must actually be hidden. Any class setting display at the
 * same specificity beats [hidden], which is how a "price" box stayed on
 * screen for an offer that was only a percentage.
 */
.xn-offer-form [hidden],
.xn-offer-modal [hidden] {
	display: none !important;
}

.xn-req {
	color: var(--xn-error);
}

/* Figures ---------------------------------------------------------- */

/*
 * The number reads left-to-right inside a right-to-left form, with its unit
 * pinned to the end of the box rather than floating over the digits.
 */
.xn-offer-unit {
	position: relative;
	display: block;
}

.xn-offer-unit .xn-input {
	width: 100%;
	padding-inline-end: 3.5rem;
	text-align: start;
}

.xn-offer-unit__tag {
	position: absolute;
	inset-inline-end: var(--xn-space-3);
	inset-block-start: 50%;
	transform: translateY(-50%);
	pointer-events: none;
	color: var(--xn-text-muted);
	font-size: var(--xn-text-small);
	font-weight: var(--xn-weight-title);
}

/* Dates ------------------------------------------------------------ */

.xn-offer-when {
	display: grid;
	grid-template-columns: 1.4fr 1fr;
	gap: var(--xn-space-2);
}

/*
 * Safari on iOS lays a date input out to its own intrinsic width and then
 * overflows its container. Forcing the box to the grid cell is what keeps
 * the modal from scrolling sideways on an iPhone.
 */
.xn-offer-when .xn-input {
	width: 100%;
	min-width: 0;
	text-align: start;
}

.xn-offer-toggle {
	display: flex;
	align-items: center;
	gap: var(--xn-space-2);
	margin-block-start: var(--xn-space-3);
	font-size: var(--xn-text-small);
	color: var(--xn-text);
	cursor: pointer;
}

.xn-offer-toggle input {
	inline-size: 18px;
	block-size: 18px;
	accent-color: var(--xn-primary);
}

/* Chosen customers -------------------------------------------------- */

.xn-offer-picked__bar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--xn-space-2);
	margin-block-start: var(--xn-space-2);
	flex-wrap: wrap;
}

/* The picture ------------------------------------------------------- */

.xn-offer-image {
	display: grid;
	gap: var(--xn-space-3);
}

.xn-offer-image__empty {
	display: grid;
	place-items: center;
	padding: var(--xn-space-5);
	border: 1px dashed var(--xn-border);
	border-radius: var(--xn-radius-md);
	background: var(--xn-surface-secondary);
	text-align: center;
}

.xn-offer-image__preview {
	margin: 0;
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-md);
	overflow: hidden;
	background: var(--xn-surface-secondary);
}

.xn-offer-image__preview img {
	display: block;
	width: 100%;
	max-height: 240px;
	object-fit: cover;
}

.xn-offer-image__actions {
	display: flex;
	gap: var(--xn-space-2);
	flex-wrap: wrap;
}

/* The message ------------------------------------------------------- */

.xn-offer-body {
	min-height: 220px;
	line-height: 1.9;
	resize: vertical;
}

.xn-offer-vars {
	display: grid;
	gap: var(--xn-space-2);
	margin-block-start: var(--xn-space-3);
}

.xn-offer-vars__list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--xn-space-2);
}

.xn-offer-var {
	display: inline-flex;
	align-items: center;
	gap: var(--xn-space-2);
	padding: var(--xn-space-1) var(--xn-space-3);
	border: 1px solid var(--xn-border);
	border-radius: var(--xn-radius-pill);
	background: var(--xn-surface-secondary);
	color: var(--xn-text);
	font-family: inherit;
	font-size: var(--xn-text-small);
	cursor: pointer;
	transition: border-color var(--xn-speed) var(--xn-ease),
		background var(--xn-speed) var(--xn-ease);
}

.xn-offer-var code {
	font-family: var(--xn-font-mono);
	font-size: 0.75rem;
	color: var(--xn-primary-light);
}

.xn-offer-var:hover {
	border-color: var(--xn-primary-light);
	background: color-mix(in srgb, var(--xn-primary) 12%, transparent);
}

.xn-offer-var:focus-visible {
	outline: 2px solid var(--xn-ring);
	outline-offset: 2px;
}

/* The preview -------------------------------------------------------- */

.xn-offer-preview {
	display: grid;
	gap: var(--xn-space-2);
	padding: var(--xn-space-4);
	border-radius: var(--xn-radius-md);
	background: var(--xn-surface-secondary);
	border: 1px solid var(--xn-border);
}

.xn-offer-preview__bubble {
	position: relative;
	max-width: 90%;
	padding: var(--xn-space-3);
	padding-block-end: var(--xn-space-5);
	border-radius: var(--xn-radius-md);
	background: color-mix(in srgb, var(--xn-primary) 16%, var(--xn-surface));
	border: 1px solid color-mix(in srgb, var(--xn-primary) 30%, transparent);
}

.xn-offer-preview__image {
	display: block;
	width: 100%;
	max-height: 180px;
	object-fit: cover;
	border-radius: var(--xn-radius-sm);
	margin-block-end: var(--xn-space-3);
}

/*
 * The message exactly as written: line breaks kept, and long words broken
 * rather than allowed to widen the modal. A URL in the text used to push
 * the whole editor sideways on a phone.
 */
.xn-offer-preview__body {
	margin: 0;
	white-space: pre-wrap;
	overflow-wrap: anywhere;
	line-height: 1.9;
	font-size: var(--xn-text-body);
	color: var(--xn-text);
}

.xn-offer-preview__time {
	position: absolute;
	inset-block-end: var(--xn-space-2);
	inset-inline-start: var(--xn-space-3);
	font-size: 0.6875rem;
	color: var(--xn-text-muted);
}

/* Messages under a field ---------------------------------------------- */

.xn-offer-error {
	margin: var(--xn-space-2) 0 0;
	font-size: var(--xn-text-small);
	font-weight: var(--xn-weight-title);
	color: var(--xn-error);
}

.xn-offer-form .xn-input:focus-visible {
	outline: 2px solid var(--xn-ring);
	outline-offset: 1px;
}

/* The footer ---------------------------------------------------------- */

.xn-offer-foot {
	position: sticky;
	inset-block-end: 0;
	display: flex;
	flex-wrap: wrap;
	justify-content: flex-end;
	gap: var(--xn-space-2);
	padding: var(--xn-space-4) var(--xn-space-5);
	margin: 0 calc(-1 * var(--xn-space-5));
	background: var(--xn-surface);
	border-block-start: 1px solid var(--xn-border);
}

/* The one thing a person is here to do, so it reads as the one thing. */
.xn-offer-foot .xn-btn--primary {
	min-width: 140px;
	order: 1;
}

.xn-offer-foot .xn-btn[disabled] {
	opacity: 0.55;
	cursor: not-allowed;
}

/* Phones --------------------------------------------------------------- */

@media (max-width: 640px) {
	.xn-offer-modal {
		padding: 0;
		place-items: stretch;
	}

	.xn-offer-modal .xn-offer-panel {
		max-width: none;
		max-height: 100%;
		block-size: 100%;
		border-radius: 0;
		border: 0;
	}

	.xn-offer-modal .xn-modal__head,
	.xn-offer-form {
		padding-inline: var(--xn-space-4);
	}

	.xn-offer-grid {
		grid-template-columns: 1fr;
	}

	.xn-offer-foot {
		margin: 0 calc(-1 * var(--xn-space-4));
		padding-inline: var(--xn-space-4);
		/* Clear of the iPhone home indicator. */
		padding-block-end: calc(var(--xn-space-4) + env(safe-area-inset-bottom, 0px));
	}

	.xn-offer-foot .xn-btn {
		flex: 1 1 calc(50% - var(--xn-space-2));
	}

	.xn-offer-foot .xn-btn--primary {
		flex-basis: 100%;
	}

	.xn-offer-preview__bubble {
		max-width: 100%;
	}
}

/* Very narrow phones: 320px still has to work. */
@media (max-width: 380px) {
	.xn-offer-when {
		grid-template-columns: 1fr;
	}

	.xn-offer-foot .xn-btn {
		flex-basis: 100%;
	}
}
