/* Intranet Colewood brand — single scheme (no multi-brand map) */

:root {
	--intranet-colewood: #ff7700;
	--intranet-colewood-navy: #2e3548;
	--admin-colewood-theme: var(--intranet-colewood);
}

body.contrast-colewood {
	--admin-brand: var(--intranet-colewood);
	--bs-primary: var(--intranet-colewood);
	--bs-primary-rgb: 255, 119, 0;
	--bs-secondary: var(--intranet-colewood-navy);
	--bs-secondary-rgb: 46, 53, 72;

	&.admin-auth-page .auth-brand-panel {
		background: var(--intranet-colewood-navy);
	}
}

/* Page-body contrast helpers (BS5 tables / forms on themed surfaces) */
#page-content {
	table.table {
		--bs-table-bg: transparent;
		--bs-table-color: var(--admin-text-default);
		--bs-table-border-color: var(--admin-surface-3);
		color: var(--admin-text-default);
	}

	.form-control,
	.form-select {
		background-color: var(--bs-body-bg);
		color: var(--bs-body-color);
		border-color: var(--bs-border-color);
	}
}

#admin-sidebar {
	.intranet-nav .badge {
		font-weight: 500;
		font-size: 0.7rem;
	}
}

/* Dark-mode contrast for chrome + migrated surfaces */
:root[data-bs-theme="dark"] {
	#intranet-toast-container {
		.toast {
			background-color: var(--admin-surface-1);
			color: var(--admin-text-default);
			border-color: var(--admin-surface-3);
		}

		.toast-header {
			background-color: var(--admin-surface-2);
			color: var(--admin-text-strong);
			border-bottom-color: var(--admin-surface-3);
		}
	}

	.modal-content {
		background-color: var(--admin-surface-1);
		color: var(--admin-text-default);
		border-color: var(--admin-surface-3);
	}

	.modal-header,
	.modal-footer {
		border-color: var(--admin-surface-3);
	}

	.dropdown-menu {
		--bs-dropdown-bg: var(--admin-surface-1);
		--bs-dropdown-color: var(--admin-text-default);
		--bs-dropdown-border-color: var(--admin-surface-3);
		--bs-dropdown-link-color: var(--admin-text-default);
		--bs-dropdown-link-hover-bg: var(--admin-surface-2);
		--bs-dropdown-link-hover-color: var(--admin-text-strong);
		--bs-dropdown-divider-bg: var(--admin-surface-3);
		--bs-dropdown-header-color: var(--admin-text-muted);
	}

	/* Soften primary on dark so orange stays readable on navy surfaces */
	body.contrast-colewood {
		--bs-link-color: #ff9a3d;
		--bs-link-hover-color: #ffb366;
	}
}

/* Hotdesk floor plan */
.hotdesk-floor-legend .hotdesk-swatch {
	display: inline-block;
	width: 0.85rem;
	height: 0.85rem;
	border-radius: 50%;
	vertical-align: -0.1rem;
	margin-right: 0.25rem;
}

.hotdesk-floor-plan svg .desk {
	cursor: default;

	/* Desk state is set as a class by hotdeskbookings.js; the fill comes from the
	   same --hotdesk-* tokens as the legend, so the two cannot drift apart.
	   Only modified desks are painted — a bare .desk keeps the SVG's own fill. */
	&.desk--available { --desk-fill: var(--hotdesk-available); --desk-ink: var(--intranet-colewood-navy); }
	&.desk--booked    { --desk-fill: var(--hotdesk-booked);    --desk-ink: #fff; }
	&.desk--half-day  { --desk-fill: var(--hotdesk-half-day);  --desk-ink: #fff; }
	&.desk--unused    { --desk-fill: var(--hotdesk-unused); }

	&[class*="desk--"] {
		rect { fill: var(--desk-fill); }
		path { fill: var(--desk-ink, currentColor); }
	}
}

/*
 * PPC / AdWords — days since last change severity.
 * Green → red ramp (legacy intent); theme-friendly surfaces for the legend.
 */
:root {
	--days-inactive-extremely-low: #37cf41;
	--days-inactive-very-low: #8bc34a;
	--days-inactive-low: #c0ca33;
	--days-inactive-mid: #f9a825;
	--days-inactive-high: #fb8c00;
	--days-inactive-very-high: #f57c00;
	--days-inactive-extremely-high: #e53935;
}

:root[data-bs-theme="dark"] {
	--days-inactive-extremely-low: #66bb6a;
	--days-inactive-very-low: #9ccc65;
	--days-inactive-low: #d4e157;
	--days-inactive-mid: #ffca28;
	--days-inactive-high: #ffa726;
	--days-inactive-very-high: #ff9800;
	--days-inactive-extremely-high: #ef5350;
}

.days-inactive--extremely-low { color: var(--days-inactive-extremely-low); }
.days-inactive--very-low { color: var(--days-inactive-very-low); }
.days-inactive--low { color: var(--days-inactive-low); }
.days-inactive--mid { color: var(--days-inactive-mid); }
.days-inactive--high { color: var(--days-inactive-high); }
.days-inactive--very-high { color: var(--days-inactive-very-high); }
.days-inactive--extremely-high { color: var(--days-inactive-extremely-high); }

.days-unchanged-legend {
	.days-unchanged-bucket {
		display: inline-flex;
		align-items: center;
		gap: 0.4rem;
		border: 1px solid var(--bs-border-color);
		background: var(--admin-surface-1, var(--bs-body-bg));
		color: var(--admin-text-default, var(--bs-body-color));
		font-weight: 500;

		&:hover,
		&:focus-visible {
			border-color: var(--admin-brand, var(--bs-primary));
			background: var(--admin-surface-2, var(--bs-secondary-bg));
		}

		&[aria-pressed="true"] {
			border-color: var(--admin-brand, var(--bs-primary));
			box-shadow: 0 0 0 0.15rem color-mix(in srgb, var(--admin-brand, var(--bs-primary)) 35%, transparent);
		}

		.days-unchanged-swatch {
			display: inline-block;
			width: 0.85rem;
			height: 0.85rem;
			border-radius: 0.2rem;
			flex-shrink: 0;
			border: 1px solid color-mix(in srgb, currentColor 40%, var(--bs-border-color));
		}

		&.days-inactive--extremely-low .days-unchanged-swatch { background-color: var(--days-inactive-extremely-low); }
		&.days-inactive--very-low .days-unchanged-swatch { background-color: var(--days-inactive-very-low); }
		&.days-inactive--low .days-unchanged-swatch { background-color: var(--days-inactive-low); }
		&.days-inactive--mid .days-unchanged-swatch { background-color: var(--days-inactive-mid); }
		&.days-inactive--high .days-unchanged-swatch { background-color: var(--days-inactive-high); }
		&.days-inactive--very-high .days-unchanged-swatch { background-color: var(--days-inactive-very-high); }
		&.days-inactive--extremely-high .days-unchanged-swatch { background-color: var(--days-inactive-extremely-high); }

		.days-unchanged-count {
			font-variant-numeric: tabular-nums;
			min-width: 1.5rem;
		}

		.days-unchanged-pct:empty {
			display: none;
		}
	}
}

/* Contact card (full) + contact chip (compact) — shared customisable border */
.contact-card,
.contact-chip {
	--contact-border: var(--bs-border-color);
	--contact-bg: var(--admin-surface-1, var(--bs-body-bg));
	--contact-fg: var(--admin-text-default, var(--bs-body-color));

	border: var(--contact-border-width, 3px) solid transparent;
	color: var(--contact-fg);
	background:
		linear-gradient(var(--contact-bg), var(--contact-bg)) padding-box,
		var(--contact-border) border-box;
	text-decoration: none;

	&:hover,
	&:focus-visible {
		color: var(--contact-fg);
		text-decoration: none;
	}

	&:focus-visible {
		outline: 2px solid var(--admin-brand, var(--bs-primary));
		outline-offset: 2px;
	}
}

.contact-card {
	--contact-border-width: 3px;
	--contact-avatar: 2.75rem;

	display: flex;
	flex-direction: column;
	height: 100%;
	padding: 0.85rem 1rem;
	border-radius: var(--bs-border-radius, 0.375rem);
	box-shadow: var(--bs-box-shadow-sm);
	font-size: 0.9375rem;
	line-height: 1.3;

	&.contact-card--compact {
		--contact-avatar: 2.25rem;
		padding: 0.55rem 0.75rem;
		font-size: 0.875rem;
	}

	/* Denser cards inside 4-col who’s where (e.g. Directors company-wide) */
	.whos-where--cols-4 &.contact-card--compact {
		--contact-avatar: 2rem;
		padding: 0.45rem 0.6rem;
		font-size: 0.8125rem;
	}

	.contact-card__row {
		display: flex;
		justify-content: space-between;
		align-items: flex-start;
		gap: 0.5rem;
		/* Wrap rather than squeeze: on a narrow card the status badge drops
		   under the name instead of fighting it for width. Kicks in exactly
		   when the identity's minimum below cannot be honoured, so wide cards
		   stay one line — no media query, and it tracks the card's own width
		   rather than the viewport's. */
		flex-wrap: wrap;
	}

	.contact-card__identity {
		display: flex;
		align-items: center;
		gap: 0.65rem;
		/* The floor that makes wrapping happen: names get at least this much
		   before the badge is pushed to its own line. min() keeps a card
		   narrower than the floor itself from overflowing. */
		min-width: min(12rem, 100%);
	}

	.contact-card__avatar {
		width: var(--contact-avatar);
		height: var(--contact-avatar);
		border-radius: 50%;
		object-fit: cover;
		flex-shrink: 0;
	}

	.contact-card__avatar--initials {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		background: var(--admin-surface-2, var(--bs-secondary-bg));
		color: var(--admin-text-muted, var(--bs-secondary-color));
		font-weight: 600;
		font-size: calc(var(--contact-avatar) * 0.36);
		user-select: none;
	}

	.contact-card__name {
		font-weight: 600;
	}

	.contact-card__subtitle {
		line-height: 1.25;
	}

	.contact-card__badge {
		align-self: flex-start;
	}
}

/* Bootstrap ships no min-width utility, but a flex child needs min-width: 0
   before text-truncate can bite — without it the child refuses to shrink, the
   row overflows, and the status badge rides over the name. whos_where.js and
   the contact-card partial have been using this class all along; now it
   exists. */
.min-w-0 {
	min-width: 0;
}

/* Select2 single selects wear Bootstrap's chevron rather than select2's own
   CSS-border triangle, so a select2 and a plain .form-select beside it look
   like the same control.

   The glyph is Bootstrap's --bs-form-select-bg-img, which it redefines under
   [data-bs-theme=dark] — but that variable is declared on .form-select
   itself, and select2 renders its container as a sibling of the (hidden)
   select rather than inside it, so the value does not inherit. Restated on
   :root here, where select2 can see it, while .form-select keeps setting its
   own identical value locally. */
:root {
	--intranet-select-chevron: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}

[data-bs-theme="dark"] {
	--intranet-select-chevron: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}

.select2-container--default .select2-selection--single .select2-selection__arrow {
	/* Bootstrap's own metrics: 16x12 glyph, 0.75rem from the right edge. */
	inline-size: 2.25rem;
	background-image: var(--intranet-select-chevron);
	background-repeat: no-repeat;
	background-position: right 0.75rem center;
	background-size: 16px 12px;

	/* select2 draws the triangle with borders on a <b>; the chevron replaces it. */
	b {
		display: none;
	}
}

/* Call notes keep the whitespace they were written with — two thirds carry
   newlines, and collapsing those turns a structured update into a wall.
   Wrapping is forced so a pasted URL cannot widen the modal. */
.call-note__body {
	display: block;
	white-space: pre-wrap;
	overflow-wrap: anywhere;
}

/* PPC dashboard target line — the figure plus whether it was met. Colour is
   not the only signal: each state carries its own icon and the word. */
.ppc-target {
	display: inline-block;
	margin-top: 0.15rem;
	font-size: 0.75rem;
	font-weight: 600;
	white-space: nowrap;
	color: var(--bs-secondary-color);
}

.ppc-target--achieved { color: #15803d; }
.ppc-target--on { color: #b45309; }
.ppc-target--missed { color: #b91c1c; }

/* Navbar timesheet recorder.

   One element, two states — the idle button and the live chip are both in the
   markup and CSS picks which is shown, so starting and stopping needs no
   re-render. Live, the chip is deliberately loud on dark headers; on the white
   daytime bar the idle control inverts to dark chrome. */
.timesheet-recorder .timesheet-recorder__chip,
.timesheet-recorder--live .timesheet-recorder__start {
	display: none;
}

.timesheet-recorder--live .timesheet-recorder__chip {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.2rem 0.35rem 0.2rem 0.7rem;
	border-radius: 2rem;
	background: #fff;
	color: #1f2430;
	box-shadow: 0 0 0 0 rgb(255 255 255 / 70%);
	animation: recorder-pulse 2s ease-out infinite;
}

.timesheet-recorder__start {
	border: 1px solid rgb(255 255 255 / 55%);
	border-radius: 2rem;
	color: #fff;
	padding: 0.25rem 0.7rem;
	white-space: nowrap;

	&:hover,
	&:focus-visible {
		background: rgb(255 255 255 / 15%);
		border-color: #fff;
		color: #fff;
	}
}

html[data-time-header-tone="light"] .timesheet-recorder__start {
	border-color: rgb(15 23 42 / 18%);
	color: var(--admin-text-default, #334155);

	&:hover,
	&:focus-visible {
		background: rgb(15 23 42 / 6%);
		border-color: rgb(15 23 42 / 28%);
		color: var(--admin-text-strong, #0f172a);
	}
}

/* Only shown once the recording has outlived its own day; the amber reads
   against the white chip without competing with the red stop button. */
/* Two classes deep on purpose. Font Awesome is loaded after this file and
   .fa-solid sets its own display, so a single-class rule here loses on
   source order and the triangle showed on every recording regardless of the
   attribute. */
.timesheet-recorder .timesheet-recorder__warning {
	display: none;
	color: #b45309;
	font-size: 0.85rem;
	cursor: help;
}

.timesheet-recorder[data-overrunning="1"] .timesheet-recorder__warning {
	display: inline-block;
}

.timesheet-recorder__dot {
	inline-size: 0.55rem;
	block-size: 0.55rem;
	border-radius: 50%;
	background: #c4314b;
	flex-shrink: 0;
	animation: recorder-dot 2s ease-in-out infinite;
}

.timesheet-recorder__client {
	max-inline-size: 12rem;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-weight: 600;
	font-size: 0.875rem;
}

.timesheet-recorder__elapsed {
	font-variant-numeric: tabular-nums;
	font-weight: 700;
	font-size: 0.875rem;
	min-inline-size: 3.2rem;
	text-align: right;
}

.timesheet-recorder__stop {
	--bs-btn-padding-y: 0.1rem;
	--bs-btn-padding-x: 0.45rem;
	border-radius: 2rem;
	background: #c4314b;
	color: #fff;

	&:hover,
	&:focus-visible {
		background: #a52840;
		color: #fff;
	}
}

/* The halo says "this is still running" from the corner of the eye. */
@keyframes recorder-pulse {
	0% { box-shadow: 0 0 0 0 rgb(255 255 255 / 70%); }
	70% { box-shadow: 0 0 0 0.6rem rgb(255 255 255 / 0%); }
	100% { box-shadow: 0 0 0 0 rgb(255 255 255 / 0%); }
}

@keyframes recorder-dot {
	0%, 100% { opacity: 1; }
	50% { opacity: 0.25; }
}

@media (prefers-reduced-motion: reduce) {
	.timesheet-recorder--live .timesheet-recorder__chip,
	.timesheet-recorder__dot {
		animation: none;
	}
}

/* Navbar next meeting — Granola-style text line beside the recorder. */
.nav-calendar-next {
	align-items: center;
	gap: 0.45rem;
	max-inline-size: min(20rem, 34vw);
	padding: 0;
	color: var(--navbar-fg);
	cursor: default;
	user-select: none;
	font-size: 0.8125rem;
	font-weight: 500;
	line-height: 1.2;
	text-decoration: none;
	white-space: nowrap;

	&:hover,
	&:focus-visible {
		color: var(--navbar-fg);
		text-decoration: none;
		opacity: 0.92;
	}

	&__icon {
		flex-shrink: 0;
		font-size: 0.75rem;
		opacity: 0.88;
	}

	&__label {
		display: inline-flex;
		align-items: baseline;
		gap: 0.35rem;
		min-inline-size: 0;
		overflow: hidden;
	}

	&__subject {
		overflow: hidden;
		text-overflow: ellipsis;
		min-inline-size: 0;
	}

	&__sep {
		flex-shrink: 0;
		opacity: 0.55;
		font-weight: 400;
	}

	&__countdown {
		flex-shrink: 0;
		font-variant-numeric: tabular-nums;
		font-weight: 500;
	}

	/* Status text, not editable copy — keep the arrow even when it is a join link. */
	a& {
		cursor: default;
	}
}

/* Department dashboard stat tiles — same brand-tinted icon chip language as
   the personal dashboard's shortcuts, at the size a headline figure wants. */
.dept-tile__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	inline-size: 2.75rem;
	block-size: 2.75rem;
	border-radius: 0.7rem;
	background: color-mix(in srgb, var(--bs-primary) 14%, transparent);
	color: var(--bs-primary);
	font-size: 1.05rem;
}

/* Dashboard shortcut tiles. Horizontal — icon chip, label, caption — because
   a lone icon floating over a five-word label reads as an empty card. Hover
   answers with the brand colour and a slight lift, matching how the rest of
   the app signals "this goes somewhere". */
.quick-link {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 0.65rem 0.85rem;
	border: var(--bs-border-width) solid var(--bs-border-color);
	border-radius: var(--bs-border-radius);
	background: var(--admin-surface-1, var(--bs-body-bg));
	color: var(--bs-body-color);
	text-decoration: none;
	transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;

	&:hover,
	&:focus-visible {
		color: var(--bs-body-color);
		border-color: var(--bs-primary);
		box-shadow: var(--bs-box-shadow-sm);
		transform: translateY(-1px);
	}

	.quick-link__icon {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		flex-shrink: 0;
		width: 2.5rem;
		height: 2.5rem;
		border-radius: 0.6rem;
		/* bg-primary-subtle is un-themed (navy against the orange primary),
		   so tint the brand colour directly instead. */
		background: color-mix(in srgb, var(--bs-primary) 14%, transparent);
		color: var(--bs-primary);
	}

	&:hover .quick-link__icon {
		background: color-mix(in srgb, var(--bs-primary) 22%, transparent);
	}
}

/* Standalone circular avatar wearing the contact-card ring — the dashboard
   hero. Same transparent-border + two-layer background as .contact-chip, so
   solid and gradient customisations both paint. */
.contact-avatar-ring {
	--contact-border: var(--bs-border-color);
	--contact-avatar: 4.5rem;

	display: inline-flex;
	flex-shrink: 0;
	border-radius: 50%;
	border: 3px solid transparent;
	background:
		linear-gradient(var(--admin-surface-1, var(--bs-body-bg)), var(--admin-surface-1, var(--bs-body-bg))) padding-box,
		var(--contact-border) border-box;

	img,
	.contact-avatar-ring__initials {
		width: var(--contact-avatar);
		height: var(--contact-avatar);
		border-radius: 50%;
		object-fit: cover;
	}

	.contact-avatar-ring__initials {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		background: var(--admin-surface-2, var(--bs-secondary-bg));
		color: var(--admin-text-muted, var(--bs-secondary-color));
		font-weight: 600;
		font-size: 1.5rem;
	}
}

/* Profile-page hero: the same ring, sized to be the page's main image. Font size
   is restated rather than changed on the base rule, which the dashboard hero
   shares and is tuned for. */
.contact-avatar-ring--xl {
	--contact-avatar: min(11rem, 60vw);

	border-width: 5px;

	.contact-avatar-ring__initials {
		font-size: calc(var(--contact-avatar) * 0.36);
	}
}

/* Profile hero — time-of-day sky + the member's ring/border customisation. */
.profile-hero {
	--profile-hero-accent: var(--contact-border, var(--admin-brand, var(--bs-primary)));
	--profile-hero-pad: clamp(1.25rem, 3vw, 2rem);

	position: relative;
	border-radius: 0.75rem;
	overflow: hidden;
}

.profile-hero__backdrop {
	position: absolute;
	inset: 0;
	background: linear-gradient(
		180deg,
		var(--time-header-sky, var(--admin-surface-2, #f1f5f9)) 0%,
		var(--time-header-horizon, var(--admin-surface-1, #fff)) 58%,
		color-mix(in oklch, var(--time-header-horizon, var(--admin-surface-1, #fff)) 40%, var(--admin-body-bg, #f8fafc)) 100%
	);
	transition:
		background 0.75s ease,
		--time-header-sky 0.75s ease,
		--time-header-horizon 0.75s ease;
}

html.time-header--instant .profile-hero__backdrop {
	transition: none;
}

.profile-hero__content {
	position: relative;
	padding: var(--profile-hero-pad);
}

.profile-hero__crumbs {
	margin-bottom: 1rem;

	.breadcrumb {
		--bs-breadcrumb-divider-color: var(--profile-hero-muted, var(--bs-secondary-color));
	}

	.breadcrumb-item,
	.breadcrumb-item a {
		color: var(--profile-hero-muted, var(--bs-secondary-color));
	}

	.breadcrumb-item.active {
		color: var(--profile-hero-fg, var(--bs-body-color));
	}
}

.profile-hero__main {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: 1.25rem 1.5rem;
}

.profile-hero__identity {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1rem 1.25rem;
	min-width: min(100%, 16rem);
}

.profile-hero__avatar.contact-avatar-ring {
	background:
		linear-gradient(rgb(255 255 255 / 94%), rgb(255 255 255 / 94%)) padding-box,
		var(--contact-border) border-box;
	box-shadow: 0 8px 28px rgb(15 23 42 / 12%);
}

.profile-hero__name {
	color: var(--profile-hero-fg, var(--bs-body-color));
}

.profile-hero__subtitle {
	color: var(--profile-hero-muted, var(--bs-secondary-color));
}

.profile-hero__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
}

.profile-hero__accent {
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	height: 4px;
	background: var(--profile-hero-accent);
	opacity: 0.9;
}

.profile-nameplate {
	line-height: 1.12;
}

.profile-nameplate--dm-sans {
	font-family: 'DM Sans', system-ui, sans-serif;
	font-weight: 700;
}

.profile-nameplate--fraunces {
	font-family: 'Fraunces', Georgia, serif;
	font-weight: 650;
	font-variation-settings: 'SOFT' 50, 'WONK' 1;
}

.profile-nameplate--playfair {
	font-family: 'Playfair Display', Georgia, serif;
	font-weight: 700;
}

.profile-nameplate--bebas {
	font-family: 'Bebas Neue', sans-serif;
	font-weight: 400;
	font-size: 1.35em;
	letter-spacing: 0.05em;
	text-transform: uppercase;
}

.profile-nameplate--space-grotesk {
	font-family: 'Space Grotesk', system-ui, sans-serif;
	font-weight: 600;
}

.profile-nameplate--libre-baskerville {
	font-family: 'Libre Baskerville', Georgia, serif;
	font-weight: 700;
}

.profile-nameplate--outfit {
	font-family: 'Outfit', system-ui, sans-serif;
	font-weight: 600;
}

.profile-nameplate--permanent-marker {
	font-family: 'Permanent Marker', cursive;
	font-weight: 400;
	letter-spacing: 0.02em;
}

.profile-nameplate--weight-normal {
	font-weight: 500;
}

.profile-nameplate--weight-bold {
	font-weight: 700;
}

.profile-nameplate--weight-display {
	font-weight: 800;
	font-size: 1.08em;
	letter-spacing: -0.02em;
}

.profile-hero__pronouns,
.profile-hero__status {
	color: var(--profile-hero-muted, var(--bs-secondary-color));
}

.profile-hero--backdrop-border .profile-hero__backdrop {
	background: linear-gradient(
		180deg,
		color-mix(in oklch, var(--contact-border, var(--admin-brand, var(--bs-primary))) 18%, var(--admin-surface-2, #f1f5f9)) 0%,
		color-mix(in oklch, var(--contact-border, var(--admin-brand, var(--bs-primary))) 8%, var(--admin-surface-1, #fff)) 100%
	);
}

.profile-hero--backdrop-neutral .profile-hero__backdrop {
	background: linear-gradient(
		180deg,
		var(--admin-surface-2, #f1f5f9) 0%,
		var(--admin-surface-1, #fff) 100%
	);
}

.profile-hero--ring-thick .profile-hero__avatar.contact-avatar-ring {
	border-width: 5px;
}

.profile-hero--ring-double .profile-hero__avatar.contact-avatar-ring {
	border-width: 3px;
	box-shadow:
		0 0 0 3px color-mix(in srgb, var(--contact-border, var(--admin-brand, var(--bs-primary))) 35%, transparent),
		0 8px 28px rgb(15 23 42 / 12%);
}

.profile-hero-preview__hero {
	border-radius: 0.75rem;
}

.profile-edit-nav .btn {
	scroll-margin-top: 5rem;
}

.contact-chip__text {
	line-height: 1.15;
	max-width: 100%;
}

.contact-chip__nick {
	font-size: 0.72em;
	line-height: 1.1;
}

.profile-nameplate-option {
	cursor: pointer;

	.profile-nameplate-option__frame {
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 0.35rem;
		min-width: 4.75rem;
		padding: 0.55rem 0.65rem;
		border: 2px solid var(--bs-border-color);
		border-radius: 0.65rem;
		background: var(--admin-surface-1, var(--bs-body-bg));
		transition: border-color 0.15s ease, box-shadow 0.15s ease;
	}

	.profile-nameplate-option__sample {
		font-size: 1.35rem;
		line-height: 1;
	}

	&:hover .profile-nameplate-option__frame {
		border-color: color-mix(in srgb, var(--admin-brand, var(--bs-primary)) 45%, var(--bs-border-color));
	}

	&:has(input:checked) .profile-nameplate-option__frame {
		border-color: var(--admin-brand, var(--bs-primary));
		box-shadow: 0 0 0 0.15rem color-mix(in srgb, var(--admin-brand, var(--bs-primary)) 28%, transparent);
	}

	&:has(input:focus-visible) .profile-nameplate-option__frame {
		outline: 2px solid var(--admin-brand, var(--bs-primary));
		outline-offset: 2px;
	}
}

.profile-nameplate-preview {
	background: linear-gradient(
		180deg,
		var(--time-header-sky, var(--admin-surface-2, #f1f5f9)) 0%,
		var(--time-header-horizon, var(--admin-surface-1, #fff)) 100%
	);
}

html[data-time-header-tone="dark"] .profile-hero {
	--profile-hero-fg: #f8fafc;
	--profile-hero-muted: rgb(248 250 252 / 78%);

	.profile-hero__avatar.contact-avatar-ring {
		background:
			linear-gradient(rgb(255 255 255 / 92%), rgb(255 255 255 / 92%)) padding-box,
			var(--contact-border) border-box;
		box-shadow: 0 10px 32px rgb(0 0 0 / 28%);
	}

	.profile-hero__btn-outline {
		--bs-btn-color: #f8fafc;
		--bs-btn-border-color: rgb(248 250 252 / 55%);
		--bs-btn-hover-color: #0f172a;
		--bs-btn-hover-bg: #f8fafc;
		--bs-btn-hover-border-color: #f8fafc;
	}

	.badge.text-bg-secondary {
		--bs-badge-color: #fff;
	}
}

:root[data-bs-theme="dark"] .profile-hero__backdrop {
	background: linear-gradient(
		180deg,
		var(--admin-surface-2, #1e293b) 0%,
		var(--admin-surface-1, #0f172a) 100%
	);
}

:root[data-bs-theme="dark"] .profile-hero {
	--profile-hero-fg: var(--admin-text-strong, #f8fafc);
	--profile-hero-muted: var(--admin-text-soft, #cbd5e1);
}

.contact-chip {
	--contact-border-width: 3px;
	--contact-pad-y: 0.2rem;
	--contact-pad-x: 0.55rem;
	--contact-gap: 0.45rem;
	--contact-avatar: 2.5rem;
	--contact-font: 0.875rem;

	display: inline-flex;
	align-items: center;
	gap: var(--contact-gap);
	max-width: 100%;
	padding: var(--contact-pad-y) var(--contact-pad-x) var(--contact-pad-y) var(--contact-pad-y);
	border-radius: 999px;
	font-size: var(--contact-font);
	line-height: 1.2;
	vertical-align: middle;

	&.dropdown-toggle::after {
		display: none;
	}

	.contact-chip__avatar {
		width: var(--contact-avatar);
		height: var(--contact-avatar);
		border-radius: 50%;
		object-fit: cover;
		flex-shrink: 0;
	}

	.contact-chip__avatar--initials {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		background: var(--admin-surface-2, var(--bs-secondary-bg));
		color: var(--admin-text-muted, var(--bs-secondary-color));
		font-weight: 600;
		font-size: calc(var(--contact-avatar) * 0.36);
		user-select: none;
	}

	.contact-chip__name {
		min-width: 0;
		font-weight: 600;
		padding-right: 0.15rem;
	}

	.contact-chip__status {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		width: 1.5rem;
		height: 1.5rem;
		border-radius: 50%;
		flex-shrink: 0;
		font-size: 0.75rem;
		background: var(--admin-surface-2, var(--bs-secondary-bg));
		color: var(--admin-text-muted, var(--bs-secondary-color));

		&.contact-chip__status--office {
			background: color-mix(in srgb, var(--bs-primary) 18%, transparent);
			color: var(--bs-primary);
		}

		&.contact-chip__status--wfh {
			background: color-mix(in srgb, var(--bs-secondary) 18%, transparent);
			color: var(--bs-secondary);
		}

		/* Nothing booked covering that moment. Muted rather than absent, so the
		   marker still reads as a deliberate state and not a rendering gap. */
		&.contact-chip__status--not_booked {
			background: color-mix(in srgb, var(--bs-secondary-color, #64748b) 14%, transparent);
			color: var(--bs-secondary-color, #64748b);
		}
	}

	&.contact-chip--sm {
		--contact-avatar: 1.75rem;
		--contact-pad-y: 0.15rem;
		--contact-pad-x: 0.45rem;
		--contact-gap: 0.35rem;
		--contact-border-width: 2px;
		--contact-font: 0.8125rem;

		.contact-chip__status {
			width: 1.25rem;
			height: 1.25rem;
			font-size: 0.65rem;
		}
	}

	&.contact-chip--lg {
		--contact-avatar: 3.25rem;
		--contact-pad-y: 0.3rem;
		--contact-pad-x: 0.75rem;
		--contact-gap: 0.55rem;
		--contact-border-width: 3px;
		--contact-font: 1rem;

		.contact-chip__status {
			width: 1.75rem;
			height: 1.75rem;
			font-size: 0.85rem;
		}
	}

	/* Compact pill for the 56px brand navbar */
	&.contact-chip--nav {
		--contact-avatar: 1.5rem; /* 24px */
		--contact-pad-y: 0.2rem;
		--contact-pad-x: 0.6rem;
		--contact-gap: 0.45rem;
		--contact-border-width: 2px;
		--contact-font: 0.8125rem;
		--contact-bg: rgba(255, 255, 255, 0.96);
		--contact-fg: var(--admin-text-default, #212529);

		max-height: 36px;
		max-width: 14rem;
		/* Explicit gap — theme `.dropdown-toggle { gap: 0 }` is more specific than `.contact-chip` */
		gap: var(--contact-gap);
		box-shadow: 0 1px 2px rgba(0, 0, 0, 0.14);
		transition: box-shadow 0.15s ease, filter 0.15s ease;

		.contact-chip__name {
			max-width: 7.5rem;
			padding-right: 0.1rem;
		}

		.contact-chip__status {
			width: 1.15rem;
			height: 1.15rem;
			font-size: 0.6rem;
			background: var(--admin-surface-2, #e9ecef);
			color: var(--admin-text-default, #212529);
			box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);

			&.contact-chip__status--office {
				background: var(--bs-primary);
				color: #fff;
				box-shadow: none;
			}

			&.contact-chip__status--wfh {
				background: var(--bs-secondary);
				color: #fff;
				box-shadow: none;
			}
		}

		&:hover,
		&:focus-visible {
			filter: brightness(0.98);
			box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
		}

		&:focus-visible {
			outline: 2px solid var(--admin-brand, var(--bs-primary));
			outline-offset: 2px;
		}
	}
}

html[data-time-header-tone="dark"] header .navbar .contact-chip--nav:focus-visible,
:root[data-bs-theme="dark"] header .navbar .contact-chip--nav:focus-visible {
	outline-color: #fff;
}

header .navbar .user-menu .contact-chip--nav {
	align-self: center;
	margin: 0;
	/* Beat .navbar a / legacy dropdown-toggle nav chrome on the time-of-day bar */
	color: var(--contact-fg);
	text-decoration: none;
	height: auto;
	max-height: 36px;
	gap: var(--contact-gap);
	padding: var(--contact-pad-y) var(--contact-pad-x) var(--contact-pad-y) var(--contact-pad-y);

	&:hover,
	&:focus-visible {
		color: var(--contact-fg);
		text-decoration: none;
		background-color: transparent;
	}
}

:root[data-bs-theme="dark"] {
	.contact-chip.contact-chip--nav {
		--contact-bg: color-mix(in srgb, var(--admin-surface-1, #1e1e1e) 92%, #fff);
		--contact-fg: var(--admin-text-default, #e9ecef);
		box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);

		.contact-chip__status {
			box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);

			&.contact-chip__status--office,
			&.contact-chip__status--wfh {
				box-shadow: none;
			}
		}
	}
}

@media (max-width: 575.98px) {
	.navbar .contact-chip--nav .contact-chip__name,
	.navbar .contact-chip--sm .contact-chip__name {
		display: none;
	}
}

.contact-chip-swatch {
	/* Swatch colour/gradient string comes from the server as --swatch-bg. */
	background: var(--swatch-bg, var(--bs-secondary-bg));
	width: 2rem;
	height: 2rem;
	border-radius: 50%;
	border: 2px solid var(--bs-border-color);
	padding: 0;
	cursor: pointer;
	background-clip: padding-box;
	flex-shrink: 0;

	&:hover,
	&:focus-visible {
		border-color: var(--admin-brand, var(--bs-primary));
	}

	&[aria-pressed="true"] {
		outline: 2px solid var(--admin-brand, var(--bs-primary));
		outline-offset: 2px;
	}
}

.contact-chip-swatch-input {
	position: absolute;
	opacity: 0;
	pointer-events: none;

	&:checked + .contact-chip-swatch {
		outline: 2px solid var(--admin-brand, var(--bs-primary));
		outline-offset: 2px;
		border-color: var(--admin-brand, var(--bs-primary));
	}

	&:focus-visible + .contact-chip-swatch {
		outline: 2px solid var(--admin-brand, var(--bs-primary));
		outline-offset: 2px;
	}
}

/* Staff benefits */
.staff-benefits {
	.staff-benefits__icon {
		width: 2.5rem;
		height: 2.5rem;
		font-size: 1rem;
	}

	.staff-benefits__logo-link {
		max-width: 100%;

		&:hover .staff-benefits__logo,
		&:focus-visible .staff-benefits__logo {
			opacity: 0.85;
		}
	}

	.staff-benefits__logo {
		display: block;
		max-width: 11.25rem;
		max-height: 3.75rem;
		width: auto;
		height: auto;
		margin-inline: auto;
		object-fit: contain;
		transition: opacity 0.15s ease;

		&.staff-benefits__logo--rac {
			max-width: 10rem;
		}
	}

	.staff-benefits__code {
		color: var(--admin-text-strong, var(--bs-body-color));
		font-weight: 600;
		letter-spacing: 0.02em;
	}

	.staff-benefits__discount {
		.card-body {
			display: flex;
			flex-direction: column;
			align-items: center;
		}
	}
}

:root[data-bs-theme="dark"] {
	.staff-benefits {
		.staff-benefits__logo {
			filter: brightness(1.05);
		}
	}
}

/* Timesheet calendar */
.timesheet {
	.fc-daygrid-body,
	.fc-scrollgrid-sync-table {
		min-height: 14rem;
	}

	.fc-toolbar-chunk {
		margin-bottom: 0.625rem;
	}
}

/* WFH block booking — inline multi-select calendar */
.wfh-calendar {
	.flatpickr-calendar {
		box-shadow: none;
		width: 100%;
		max-width: 22rem;
	}

	.flatpickr-day {
		&.selected,
		&.selected:hover {
			background: var(--bs-primary);
			border-color: var(--bs-primary);
			color: #fff;
		}

		&.flatpickr-disabled,
		&.flatpickr-disabled:hover {
			color: var(--admin-text-muted, var(--bs-secondary-color));
			opacity: 0.45;
		}
	}
}

/* Clients index */
.clients-index {
	.clients-index__summary {
		appearance: none;
		background: var(--bs-body-bg);
		cursor: pointer;
		transition: border-color 0.15s ease, box-shadow 0.15s ease;

		&:hover,
		&:focus-visible {
			border-color: var(--bs-primary) !important;
			box-shadow: 0 0 0 0.2rem rgb(var(--bs-primary-rgb) / 18%);
			outline: none;
		}

		&.is-active {
			border-color: var(--bs-primary) !important;
			box-shadow: 0 0 0 0.15rem rgb(var(--bs-primary-rgb) / 22%);
		}
	}

	.clients-index__toolbar {
		.form-label {
			margin-bottom: 0.35rem;
		}

		.select2-container {
			max-width: 100%;
		}
	}

	#clients-table tbody td {
		vertical-align: middle;
	}
}

/* Stat cards — shared by the clients report and directors dashboard */
.stat-cards {
	.stat-cards__icon {
		width: 2.75rem;
		height: 2.75rem;
		font-size: 1.1rem;
	}

	.stat-cards__icon--sm {
		width: 2.25rem;
		height: 2.25rem;
		font-size: 0.9rem;
	}

	.stat-cards__bar {
		height: 0.4rem;
		background-color: var(--bs-tertiary-bg);
	}

	.stat-cards__row {
		transition: background-color 0.15s ease;

		&:hover {
			background-color: var(--bs-tertiary-bg);
		}
	}
}

/* Client services list */
.client-services-list {
	.client-services-list__summary {
		appearance: none;
		background: var(--bs-body-bg);
		cursor: pointer;
		transition: border-color 0.15s ease, box-shadow 0.15s ease;

		&:hover,
		&:focus-visible {
			border-color: var(--bs-primary) !important;
			box-shadow: 0 0 0 0.2rem rgb(var(--bs-primary-rgb) / 18%);
			outline: none;
		}

		&.is-active {
			border-color: var(--bs-primary) !important;
			box-shadow: 0 0 0 0.15rem rgb(var(--bs-primary-rgb) / 22%);
		}
	}

	.client-services-list__toolbar {
		.form-label {
			margin-bottom: 0.35rem;
		}
	}

	#clients_services_table tbody td {
		vertical-align: middle;
	}
}

/* Client overview (manager sample + report) */
.client-overview-index {
	.client-overview-index__card {
		.card-body {
			min-height: 11rem;
		}
	}
}

.client-overview-report {
	.stats {
		tr.pending td {
			background-color: #ffa726 !important;
			color: #fff;
			font-weight: 600;
		}

		tr.outstanding td {
			background-color: #ee4a47 !important;
			color: #fff;
			font-weight: 600;
		}

		.tab-content table {
			width: 100% !important;
		}

		td.month {
			max-width: 6rem;
			font-weight: 600;
		}

		td.calls,
		td.target-hours {
			max-width: 6rem;
			font-weight: 600;
			font-size: 1.25rem;
		}

		td.managed-hours {
			font-size: 1.125rem;
		}

		td.notes {
			min-width: 18rem;
		}
	}

	.client-overview-report__note {
		border-bottom: 1px solid var(--bs-border-color);
		padding-bottom: 0.75rem;

		&:last-child {
			border-bottom: 0;
			padding-bottom: 0;
			margin-bottom: 0 !important;
		}
	}

	.small-dates {
		display: inline-block;
		margin-right: 0.35rem;
	}
}

/* Meeting rooms */
.meeting-rooms {
	.meeting-rooms__swatch {
		display: inline-block;
		width: 0.75rem;
		height: 0.75rem;
		border-radius: 50%;
		background: var(--room-colour, var(--bs-secondary));
		box-shadow: inset 0 0 0 1px rgb(0 0 0 / 12%);
	}

	.meeting-rooms__availability {
		.list-group-item {
			padding-block: 0.75rem;
		}
	}
}

/* Meeting event modal (calendar item -> IntranetUI.dialog) */
.meeting-event-modal {
	.meeting-rooms__swatch {
		display: inline-block;
		width: 0.75rem;
		height: 0.75rem;
		border-radius: 50%;
		background: var(--room-colour, var(--bs-secondary));
		box-shadow: inset 0 0 0 1px rgb(0 0 0 / 12%);
	}

	dt {
		font-weight: 400;
	}
}

/* Holidays (Xero leave calendar) */
.holidays-page {
	.holiday-calendar {
		min-height: 28rem;
	}

	#holiday-book-form fieldset:disabled {
		opacity: 0.72;
	}
}

/* Dashboard metric helpers (were in purged Flatty custom.css).
   These are applied by string concatenation in the AdWords/KPI DataTables
   render callbacks, so they cannot be replaced with BS5 utilities without
   rewriting those callbacks.

   NOTE: `.metric-small` was previously `.small`, which shadowed Bootstrap's own
   `.small` (.875em) app-wide with an absolute 0.75rem. Do not reintroduce a
   bare `.small` rule here. */
.bold { font-weight: 700; }
.large { font-size: 1.125rem; }
.metric-small { font-size: 0.75rem; }

.grey {
	&,
	& a { color: var(--admin-text-muted, #898989) !important; }
}

.black { color: var(--admin-text-default, #333) !important; }
.red { color: var(--days-inactive-extremely-high) !important; }
.orange { color: var(--days-inactive-high) !important; }
.green { color: var(--days-inactive-extremely-low) !important; }

/* Time allocation report */
#time_allocation {
	tbody tr.time-allocation-row {
		cursor: pointer;
	}

	td.time-allocation-toggle {
		width: 2.5rem;
	}
}

.time-allocation-child {
	background: var(--admin-surface-1, var(--bs-tertiary-bg));
	border-top: 1px solid var(--admin-surface-3, var(--bs-border-color));
}

/* Hours-logged report — accordion rows that carry timesheet data are brand-highlighted.
   Replaces hardcoded #ec6b41 / #21333f inline styles set from hours_logged.js. */
.accordion-button.hours-logged-button--active {
	background: var(--intranet-colewood);
	color: var(--bs-white);

	&:not(.collapsed) {
		background: var(--intranet-colewood-navy);
		color: var(--bs-white);
	}

	&:hover {
		background: var(--intranet-colewood-navy);
		color: var(--bs-white);
	}

	&::after {
		filter: brightness(0) invert(1);
	}
}

/* Wide DataTables (directory) — was three duplicated inline <style>
   blocks. The overflow rule targeted .dataTables_wrapper, which DataTables 2
   no longer emits (it is .dt-container), so it had stopped applying. */
#data_table {
	tbody {
		td { vertical-align: middle; }
		tr { line-height: var(--data-table-line-height, 1.4); }
	}

	.contact-chip { max-width: 16rem; }
}

#data_table.data-table--roomy tbody tr { --data-table-line-height: 2; }

.dt-container { overflow: visible; }

/* ---------------------------------------------------------------------------
   NOTE: native CSS nesting does NOT concatenate like Sass — `&--modifier` is
   invalid and is silently dropped. Write the full compound `&.block--modifier`.

   Components that take a value from the server.
   Twig sets a custom property; the rule below consumes it. Replaces inline
   style="" attributes, following the pattern already used by timeline.css.
   --------------------------------------------------------------------------- */

/* Progress bars driven by a computed percentage. */
.progress-bar--metered {
	inline-size: var(--bar-pct, 0%);
}

/* Leader-board trophies. Tokens so the ramp is defined once, not per template. */
:root {
	--trophy-gold: #f8a326;
	--trophy-silver: #999;
	--trophy-bronze: #924e31;
}

.trophy {
	color: var(--trophy-colour, var(--trophy-gold));

	&.trophy--gold   { --trophy-colour: var(--trophy-gold); }
	&.trophy--silver { --trophy-colour: var(--trophy-silver); }
	&.trophy--bronze { --trophy-colour: var(--trophy-bronze); }

	&.trophy--podium {
		font-size: 300%;
	}
}

.podium-step {
	block-size: 100px;
}

/* Hot-desk floor-plan legend. The same tokens drive the SVG desk fills, so the
   legend and the plan cannot drift apart. */
:root {
	--hotdesk-available: #f7d9ac;
	--hotdesk-booked: #e02020;
	--hotdesk-half-day: #00acec;
	--hotdesk-unused: var(--bs-secondary-color, grey);
}

.hotdesk-swatch {
	background: var(--hotdesk-swatch-colour, transparent);

	&.hotdesk-swatch--available { --hotdesk-swatch-colour: var(--hotdesk-available); }
	&.hotdesk-swatch--booked    { --hotdesk-swatch-colour: var(--hotdesk-booked); }
	&.hotdesk-swatch--half-day  { --hotdesk-swatch-colour: var(--hotdesk-half-day); }
	&.hotdesk-swatch--unused    { --hotdesk-swatch-colour: var(--hotdesk-unused); }
}

/* Team badge — background and its contrast colour both come from the server
   (Model_Teamcolours already classifies light vs dark), passed as custom
   properties instead of a full inline declaration.
   NB: the old .colour-coded-box class this replaces had no CSS at all. */
.team-badge {
	background-color: var(--team-colour, var(--bs-secondary));
	color: var(--team-text, var(--bs-white));
}

/* Chart containers sized by the view. */
.chart {
	block-size: var(--chart-height, 350px);
}

/* Sticky sidebars in the user create/edit forms. */
.sticky-offset {
	top: var(--sticky-offset, 1rem);
}

/* Square staff avatar placeholder (create/edit/view). */
.staff-avatar {
	aspect-ratio: 1;
	max-inline-size: var(--staff-avatar-size, 230px);
}

/* Hero monogram on the profile screen — the large stand-in for the photo.
   Its own class rather than .staff-avatar + a Bootstrap .bg-* utility, because
   those carry !important and would beat the monogram styles below. */
.avatar-hero-mono {
	aspect-ratio: 1;
	max-inline-size: var(--staff-avatar-size, 230px);
	background: var(--admin-surface-2, var(--bs-secondary-bg));
	color: var(--admin-text-muted, var(--bs-secondary-color));
	user-select: none;
}

/* ---- Initials monogram ----
   The avatar for anyone not using a photo or Gravatar. Layered onto the existing
   contact-card__avatar--initials / contact-chip__avatar--initials spans, so the
   sizing stays with the card and only the paint job lives here.

   --avatar-hue is stamped per person from a hash of their name (Model_Avatar::hue,
   mirrored in contact_chip.js) so the colour is stable and needs nothing stored.

   Every style paints explicitly, `classic` included. It would be tempting to let
   classic inherit the host element's own grey, but then any host that sets its
   own background at equal specificity silently beats the other four styles. */
.avatar-mono {
	--avatar-accent: hsl(var(--avatar-hue, 210) 58% 45%);

	/* The muted default the intranet has always used, and still the fallback for
	   anyone who never picks a style. */
	&.avatar-mono--classic {
		background: var(--admin-surface-2, var(--bs-secondary-bg));
		color: var(--admin-text-muted, var(--bs-secondary-color));
	}

	&.avatar-mono--filled {
		background: var(--avatar-accent);
		color: #fff;
	}

	/* Text is darkened past the accent rather than reusing it: on a 20% tint of
	   itself the accent is only ~2.4:1, which is hard to read even for two letters. */
	&.avatar-mono--soft {
		background: color-mix(in srgb, var(--avatar-accent) 20%, var(--bs-body-bg));
		color: color-mix(in srgb, var(--avatar-accent) 75%, var(--bs-body-color));
	}

	&.avatar-mono--outline {
		background: transparent;
		border: 2px solid var(--avatar-accent);
		color: var(--avatar-accent);
	}

	/* Follows the contact card border, gradients included — the one style that
	   ignores the name hash, because the point of it is to match the ring. */
	&.avatar-mono--brand {
		background: var(--contact-border, var(--bs-primary));
		color: #fff;
	}
}

/* Mid-tone accents go muddy against a dark surface; lift them rather than
   restating every rule. The lifted accent is light enough that filled needs dark
   text — `brand` is left alone, because its fill is the user's own border colour
   and stays a saturated mid-tone in both themes. */
:root[data-bs-theme="dark"] .avatar-mono {
	--avatar-accent: hsl(var(--avatar-hue, 210) 62% 62%);

	&.avatar-mono--filled {
		color: var(--admin-slate-900, #0f172a);
	}
}

/* Table column sizing — replaces per-<th> inline widths. */
.col-w-10 { inline-size: 10%; }
.col-minw-sm { min-inline-size: 8rem; }
.col-minw-md { min-inline-size: 12rem; }

/* Bounded controls / media — replaces one-off inline sizing. */
.input-group--date { max-inline-size: 22rem; }
.form-narrow { max-inline-size: 32rem; }
.select-auto { inline-size: auto; min-inline-size: 11rem; }
.thumb-sm { max-block-size: 31px; }
.thumb-lg { max-block-size: 200px; }

/* Column widths for tables that need a floor so cells don't collapse. */
.col-minw-xs { min-inline-size: 150px; }
.col-minw-lg { min-inline-size: 180px; }
.col-minw-xl { min-inline-size: 190px; }
.col-w-toggle { inline-size: 2.5rem; }
.col-w-actions { inline-size: 225px; }

/* Thin progress track — drives Bootstrap's own height variable. */
.progress--thin { --bs-progress-height: 0.5rem; }

/* Small round colour dot (meeting-room list, dashboards). */
.colour-dot {
	display: inline-block;
	inline-size: 0.6rem;
	block-size: 0.6rem;
	border-radius: 50%;
	background-color: var(--dot-colour, var(--bs-secondary));
}

/* Toast container must clear modals; tracks Bootstrap's own z-index scale. */
#intranet-toast-container {
	z-index: var(--bs-toast-zindex, 1090);
}

/* Holiday balance severity — 5 steps from ample (green) to critical (red).
   Was raw CSS colour keywords in an inline style, which did not adapt to dark
   mode; these tokens do. */
:root {
	--holiday-balance-ample: var(--days-inactive-extremely-low);
	--holiday-balance-good: var(--days-inactive-low);
	--holiday-balance-fair: var(--days-inactive-mid);
	--holiday-balance-low: var(--days-inactive-high);
	--holiday-balance-critical: var(--days-inactive-extremely-high);
}

.holiday-balance {
	color: var(--holiday-balance-colour, inherit);

	&.holiday-balance--ample    { --holiday-balance-colour: var(--holiday-balance-ample); }
	&.holiday-balance--good     { --holiday-balance-colour: var(--holiday-balance-good); }
	&.holiday-balance--fair     { --holiday-balance-colour: var(--holiday-balance-fair); }
	&.holiday-balance--low      { --holiday-balance-colour: var(--holiday-balance-low); }
	&.holiday-balance--critical { --holiday-balance-colour: var(--holiday-balance-critical); }
}

/* Search-team KPI report — was an inline <style> in reports/search_team_kpi.twig. */
.kpi-report {
	li {
		list-style: none;
		text-align: start;
	}

	.no-border tr > td {
		border: 0;
		padding: 0.125rem 0.5rem;
	}

	.no-border tr:first-of-type > td {
		padding-block-start: 0;
	}

	.no-border tr > td:nth-of-type(2) {
		font-weight: 700;
	}

	tfoot {
		border-top: 2px ridge var(--bs-border-color);
	}
}

/* PPC dashboard — was an inline <style> in adwords/dashboards/final.twig.
   px -> rem, and the ID selector dropped so no !important is needed. */
.ppc-dashboard {
	font-size: 0.6875rem;

	+ .row { display: none; }

	td.hours { min-inline-size: 10.625rem; }
}

.week-link { cursor: pointer; }

/* Toggled by jQuery .toggle() in adwords_dashboard.js — must stay a plain
   display rule, not .d-none, or the toggle cannot override it. */
.weekly { display: none; }

/* Sales leader board — was an inline <style> in sales/report/leader_board.twig. */
@keyframes dropText {
	0%   { opacity: 1; margin: 0; animation-timing-function: ease-in; }
	100% { opacity: 0; margin: 150px auto -150px; }
}

@keyframes addText {
	0%   { opacity: 0; margin: -150px auto 150px; animation-timing-function: ease-in; }
	100% { opacity: 1; margin: 0; }
}

.drop_text,
.add_text {
	display: block;
}

.drop_text { animation: dropText 3s linear 1 0s; }
.add_text  { animation: addText 1s linear 1 0s; }

@media (prefers-reduced-motion: reduce) {
	.drop_text,
	.add_text {
		animation: none;
	}
}

.leader-statistic,
#podium p.name {
	overflow: hidden;
}

/* Department chip — Enum_Department::chip() emits this.
   Colour arrives as --dept-colour; the tint is mixed against the body
   background so it reads correctly in both light and dark mode. Generalised
   from .timeline-dept-badge, which used the same treatment. */
.dept-chip {
	--dept-colour: var(--bs-secondary-color, #64748b);

	/* Contrast note: the obvious treatment — an 18% tint with the brand colour at
	   full strength as the text — fails WCAG AA on over half the palette
	   (Design lands at 2.85:1 against the 4.5 needed for text this size).
	   Lightening the tint and darkening the text takes the worst case to 5.3:1
	   while keeping each department recognisably its own colour. */
	background-color: color-mix(in srgb, var(--dept-colour) 15%, var(--bs-body-bg, #fff));
	color: color-mix(in srgb, var(--dept-colour) 70%, #000);
	border: 1px solid color-mix(in srgb, var(--dept-colour) 35%, transparent);
	font-weight: 500;

	/* Abbreviated chips carry a title; show it's hoverable without a link cursor. */
	&[title] {
		cursor: help;
	}
}

:root[data-bs-theme="dark"] .dept-chip {
	/* Mid-dark brand fills go muddy against a dark surface — lift the text and
	   thin the tint so the chip stays legible. */
	background-color: color-mix(in srgb, var(--dept-colour) 28%, var(--bs-body-bg, #0b1220));
	color: color-mix(in srgb, var(--dept-colour) 45%, #ffffff);
	border-color: color-mix(in srgb, var(--dept-colour) 50%, transparent);
}

/* Two-factor type chip — Enum_Twofactor_Type::chip() emits this.
   Same treatment as .dept-chip so the two read alike in a table row; the
   palette is fixed and small, but the tint/text mix is kept identical rather
   than hand-tuned so it stays legible if a type is ever recoloured. */
.tfa-chip {
	--tfa-colour: var(--bs-secondary-color, #64748b);

	background-color: color-mix(in srgb, var(--tfa-colour) 15%, var(--bs-body-bg, #fff));
	color: color-mix(in srgb, var(--tfa-colour) 70%, #000);
	border: 1px solid color-mix(in srgb, var(--tfa-colour) 35%, transparent);
	font-weight: 500;

	&[title] {
		cursor: help;
	}
}

:root[data-bs-theme="dark"] .tfa-chip {
	background-color: color-mix(in srgb, var(--tfa-colour) 28%, var(--bs-body-bg, #0b1220));
	color: color-mix(in srgb, var(--tfa-colour) 45%, #ffffff);
	border-color: color-mix(in srgb, var(--tfa-colour) 50%, transparent);
}

/* Verification code countdown ring (password view screen).
   --totp-progress is set by the script, 1 down to 0 across the period. The ring
   is a conic-gradient rather than an SVG so there is no extra markup and it
   inherits the theme colours. */
.totp-countdown {
	--totp-progress: 1;

	display: inline-grid;
	place-items: center;
	inline-size: 2.25rem;
	block-size: 2.25rem;
	border-radius: 50%;
	font-size: 0.75rem;
	font-variant-numeric: tabular-nums;
	font-weight: 600;
	color: var(--bs-body-color);
	background:
		conic-gradient(var(--bs-primary) calc(var(--totp-progress) * 360deg), transparent 0),
		radial-gradient(closest-side, var(--bs-body-bg) 74%, transparent 75%);
	/* The radial layer masks the middle; without it the whole disc fills. */
	background-blend-mode: normal;
	position: relative;

	&::after {
		content: "";
		position: absolute;
		inset: 4px;
		border-radius: 50%;
		background: var(--bs-body-bg);
	}

	> * {
		position: relative;
		z-index: 1;
	}

	&.totp-countdown--low {
		background:
			conic-gradient(var(--bs-danger) calc(var(--totp-progress) * 360deg), transparent 0),
			radial-gradient(closest-side, var(--bs-body-bg) 74%, transparent 75%);
		color: var(--bs-danger);
	}
}

/* Generated QR codes (Service_Qrcode renders SVG with class .qr-code).
   Sized by the wrapper so the same markup works on the card, the admin list and
   the edit screen without regenerating at a different scale. */
.wifi-qr {
	inline-size: 8rem;
	max-inline-size: 100%;

	/* Target the root svg, not .qr-code: the library stamps its cssClass onto
	   every <path> as well, so styling that class would size each module. */
	> svg {
		display: block;
		inline-size: 100%;
		block-size: auto;
		/* The quiet zone is part of the code, so the white has to reach the
		   edge — a coloured background bleeding in can stop it scanning. */
		background: #fff;
		border-radius: var(--bs-border-radius-sm);
		border: 1px solid var(--bs-border-color);
	}
}

.wifi-qr--sm { inline-size: 4rem; }
.wifi-qr--lg { inline-size: 12rem; }

/* Builder preview and list thumbnails. Same treatment as .wifi-qr — the white
   has to reach the edge or the quiet zone is lost and the code may not scan. */
.qr-preview,
.qr-thumb {
	inline-size: 12rem;
	max-inline-size: 100%;

	> svg {
		display: block;
		inline-size: 100%;
		block-size: auto;
		background: #fff;
		border-radius: var(--bs-border-radius-sm);
		border: 1px solid var(--bs-border-color);
	}
}

.qr-thumb { inline-size: 4rem; }

.qr-preview:empty::after {
	content: "Enter some content to see the code";
	display: grid;
	place-items: center;
	aspect-ratio: 1;
	padding: 1rem;
	text-align: center;
	font-size: 0.85rem;
	color: var(--bs-secondary-color);
	border: 1px dashed var(--bs-border-color);
	border-radius: var(--bs-border-radius);
}

/* Colour chip on the preset buttons. */
.qr-swatch {
	display: inline-block;
	inline-size: 0.75rem;
	block-size: 0.75rem;
	border-radius: 50%;
	vertical-align: -0.05rem;
	margin-inline-end: 0.15rem;
	border: 1px solid rgb(0 0 0 / 15%);
}

/* Setup walkthrough in the verification-code modal. Numbered markers sit in
   their own gutter so wrapped lines stay aligned under the text, not the digit. */
.totp-steps {
	counter-reset: totp-step;
	list-style: none;
	padding-left: 0;
	margin-bottom: 0;

	> li {
		counter-increment: totp-step;
		position: relative;
		padding-left: 2.5rem;
		margin-bottom: 0.85rem;

		&::before {
			content: counter(totp-step);
			position: absolute;
			left: 0;
			top: 0;
			display: grid;
			place-items: center;
			inline-size: 1.75rem;
			block-size: 1.75rem;
			border-radius: 50%;
			background: var(--bs-primary-bg-subtle);
			color: var(--bs-primary-text-emphasis);
			font-size: 0.8rem;
			font-weight: 600;
		}

		&:last-child {
			margin-bottom: 0;
		}
	}
}

/* A refresh failed — the code shown may no longer be current. */
.totp--stale [data-totp-code] {
	opacity: 0.5;
	text-decoration: line-through;
}

@media (prefers-reduced-motion: reduce) {
	.totp-countdown {
		transition: none;
	}
}

/* A two-factor row on the password form. Nothing custom beyond a hover cue —
   the layout is Bootstrap utilities. */
.tfa-row {
	transition: border-color 0.15s ease-in-out;

	&:hover {
		border-color: var(--bs-primary-border-subtle);
	}
}

/* Drop zones: password attachments, and the profile avatar upload.
   A <label> wrapping a visually-hidden file input, so click-to-browse and
   keyboard activation come from the native control rather than JS. */
.password-dropzone,
.avatar-dropzone {
	border: 2px dashed var(--bs-border-color);
	border-radius: var(--bs-border-radius);
	background: var(--bs-tertiary-bg);
	cursor: pointer;
	transition: border-color .15s ease-in-out, background-color .15s ease-in-out;

	&:hover {
		border-color: var(--admin-brand, var(--bs-primary));
		background: var(--bs-secondary-bg);
	}

	/* Set while a file is dragged over — see passwords_view.js. */
	&.is-dragover {
		border-color: var(--admin-brand, var(--bs-primary));
		background: color-mix(in srgb, var(--admin-brand, var(--bs-primary)) 10%, var(--bs-body-bg));
	}

	/* The hidden input still receives focus; surface it on the visible box. */
	&:has(input:focus-visible) {
		outline: 2px solid var(--admin-brand, var(--bs-primary));
		outline-offset: 2px;
	}
}

@media (prefers-reduced-motion: reduce) {
	.password-dropzone,
	.avatar-dropzone {
		transition: none;
	}
}

/* ---- Profile avatar picker ----
   Source tiles behave like radio cards: the whole tile is the label, and the
   checked state is driven by :has() rather than a JS class toggle. */
.avatar-upload-or {
	position: relative;
	line-height: 1;

	&::before,
	&::after {
		content: '';
		position: absolute;
		top: 50%;
		width: calc(50% - 1.5rem);
		height: 1px;
		background: var(--bs-border-color);
	}

	&::before {
		left: 0;
	}

	&::after {
		right: 0;
	}
}

.avatar-upload-section {
	border: 2px solid var(--bs-border-color);
	border-radius: var(--bs-border-radius);
	background: var(--bs-body-bg);
}

.avatar-source {
	cursor: pointer;

	& .avatar-source__tile {
		border: 2px solid var(--bs-border-color);
		border-radius: var(--bs-border-radius);
		transition: border-color .15s ease-in-out, background-color .15s ease-in-out;
	}

	&:hover .avatar-source__tile {
		border-color: var(--admin-brand, var(--bs-primary));
	}

	&:has(input:checked) .avatar-source__tile {
		border-color: var(--admin-brand, var(--bs-primary));
		background: color-mix(in srgb, var(--admin-brand, var(--bs-primary)) 8%, var(--bs-body-bg));
	}

	&:has(input:focus-visible) .avatar-source__tile {
		outline: 2px solid var(--admin-brand, var(--bs-primary));
		outline-offset: 2px;
	}
}

/* Monogram style swatches: a live 44px sample of each treatment. */
.avatar-mono-option {
	cursor: pointer;

	/* Shape and type only — the paint comes from the .avatar-mono style the
	   swatch is demonstrating. */
	& .avatar-mono-sample {
		inline-size: 2.75rem;
		block-size: 2.75rem;
		border-radius: 50%;
		display: inline-flex;
		align-items: center;
		justify-content: center;
		font-weight: 600;
		font-size: 0.95rem;
		user-select: none;
	}

	& .avatar-mono-frame {
		border: 2px solid transparent;
		border-radius: var(--bs-border-radius);
	}

	&:has(input:checked) .avatar-mono-frame {
		border-color: var(--admin-brand, var(--bs-primary));
	}

	&:has(input:focus-visible) .avatar-mono-frame {
		outline: 2px solid var(--admin-brand, var(--bs-primary));
		outline-offset: 2px;
	}
}

@media (prefers-reduced-motion: reduce) {
	.avatar-source .avatar-source__tile {
		transition: none;
	}
}

/* ---- View password screen ---- */

/* Key glyph above the credential name. */
.password-glyph {
	inline-size: 3.25rem;
	block-size: 3.25rem;
	border-radius: 50%;
	background: color-mix(in srgb, var(--admin-brand, var(--bs-primary)) 15%, var(--bs-body-bg));
	color: var(--admin-brand, var(--bs-primary));
	font-size: 1.35rem;
}

/* The panel holding the username / password pair. */
.password-reveal {
	background: var(--bs-tertiary-bg);
	border: 1px solid var(--bs-border-color);

	code {
		color: var(--bs-body-color);
	}
}

/* The password detail column. Centred and capped rather than running the full
   width of the window — the content is a handful of short values and a note,
   and a 2000px-wide card leaves them stranded at either edge. */
.password-detail {
	max-inline-size: 68rem;
	margin-inline: auto;
}

/* Copy control on the password view: the icon alone, sitting directly after
   the value it copies rather than pushed to the far edge — with a long
   password the button ended up nowhere near the text it belonged to. */
.password-copy {
	border: 0;
	background: none;
	padding: 0.25rem;
	line-height: 1;
	color: var(--bs-secondary-color);
	border-radius: var(--bs-border-radius-sm);
	cursor: pointer;
	transition: color 0.15s ease-in-out;

	&:hover,
	&:focus-visible {
		color: var(--bs-primary);
	}

	/* Held briefly after a successful copy; see flashCopied(). */
	&.is-copied {
		color: var(--bs-success);
	}
}

@media (prefers-reduced-motion: reduce) {
	.password-copy {
		transition: none;
	}
}


/* Notes keep the line breaks and spacing they were typed with. Using CSS
   rather than nl2br means indentation and blank lines survive too, and the
   value stays plain escaped text with nothing marked HTML-safe. */
/* Per-service availability — one brand icon per service, coloured by state.

   Colours are Microsoft's presence palette, so the states read the same here
   as in Teams. Colour is never the only signal: the title and a
   visually-hidden span carry "Microsoft Teams — Busy (In a meeting)" in
   words. Replaces the single Teams dot-and-label; the icon is the brand, the
   colour is the state. */
.service-presence-set {
	display: inline-flex;
	align-items: center;
	gap: 0.65rem;
}

.service-presence {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 1.6rem;
	block-size: 1.6rem;
	border-radius: 50%;
	color: var(--presence-colour, var(--bs-secondary-color));
	background: color-mix(in srgb, var(--presence-colour, #8a8886) 14%, transparent);
	font-size: 0.8rem;
	cursor: help;
}

/* The breathing ring for genuinely-available states. Painted on ::after so
   the glyph holds still, and switched off for reduced-motion users. */
.service-presence--pulse::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: 50%;
	box-shadow: 0 0 0 0 color-mix(in srgb, var(--presence-colour, #6bb700) 55%, transparent);
	animation: presence-pulse 2.4s ease-out infinite;
}

@keyframes presence-pulse {
	0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--presence-colour, #6bb700) 55%, transparent); }
	70% { box-shadow: 0 0 0 0.55rem transparent; }
	100% { box-shadow: 0 0 0 0 transparent; }
}

@media (prefers-reduced-motion: reduce) {
	.service-presence--pulse::after {
		animation: none;
	}
}

/* A note, dressed as one — see partials/_note_card.twig.

   A pinned index card: warm paper tint, a brand-orange spine down the left,
   ruled lines behind the text and a turned-up corner. The ruling is a repeating
   gradient locked to the same line-height as the text, so the words sit on the
   lines rather than drifting across them as the note gets longer.

   NB the compound class is written out in full — native CSS nesting cannot
   concatenate `&__heading`, and doing so silently drops the rule. */
.note-card {
	--note-line-height: 1.6rem;
	--note-paper: #fffdf6;
	--note-rule: rgb(13 110 253 / 9%);

	position: relative;
	padding: 1.5rem 1.75rem 1.75rem;
	border: 1px solid var(--bs-border-color);
	border-left: 4px solid var(--intranet-colewood, #ff7700);
	border-radius: 0.5rem;
	background-color: var(--note-paper);
	box-shadow: 0 1px 2px rgb(0 0 0 / 6%), 0 6px 16px -8px rgb(0 0 0 / 18%);
	text-align: left;

	/* Turned-up corner: two triangles, one the page and one its shadow. */
	&::after {
		content: "";
		position: absolute;
		right: 0;
		bottom: 0;
		border-width: 0 0 1.4rem 1.4rem;
		border-style: solid;
		border-color: transparent transparent var(--bs-body-bg) transparent;
		border-bottom-right-radius: 0.5rem;
		filter: drop-shadow(-1px -1px 1px rgb(0 0 0 / 12%));
	}

	.note-card__heading {
		display: flex;
		align-items: center;
		gap: 0.4rem;
		margin-bottom: 0.75rem;
		font-size: 0.75rem;
		letter-spacing: 0.1em;
		text-transform: uppercase;
		font-weight: 600;
		/* Brand orange straight is 2.6:1 on this paper — the same shortfall the
		   department chips hit. Darkened to clear AA while still reading orange;
		   the spine beside it carries the colour at full strength, where it is
		   a graphic rather than text. */
		color: color-mix(in srgb, var(--intranet-colewood, #ff7700) 70%, #000);
	}

	.note-card__heading i {
		/* Pinned at a slight angle, the way one actually would be. */
		rotate: -20deg;
	}
}

:root[data-bs-theme="dark"] .note-card {
	--note-paper: color-mix(in srgb, var(--bs-body-bg) 88%, #ffcc66);
	--note-rule: rgb(255 255 255 / 7%);

	box-shadow: 0 1px 2px rgb(0 0 0 / 30%), 0 6px 16px -8px rgb(0 0 0 / 50%);

	/* Full-strength orange lands at 4.34:1 on this paper — just under AA for
	   text this size. Lifted with a little white rather than darkened, which is
	   the wrong direction against a dark background. The spine keeps the pure
	   colour: it is a graphic, where 3:1 is the bar and 4.34 clears it. */
	.note-card__heading {
		color: color-mix(in srgb, var(--intranet-colewood, #ff7700) 85%, #fff);
	}
}

.note-card__body {
	/* pre-wrap keeps the line breaks, spacing and indentation as typed — the
	   reason this is CSS rather than nl2br. */
	white-space: pre-wrap;
	overflow-wrap: anywhere;
	line-height: var(--note-line-height, 1.6rem);
	/* Ruled lines sit behind the text, aligned to the line box. */
	background-image: repeating-linear-gradient(
		to bottom,
		transparent 0,
		transparent calc(var(--note-line-height, 1.6rem) - 1px),
		var(--note-rule, transparent) calc(var(--note-line-height, 1.6rem) - 1px),
		var(--note-rule, transparent) var(--note-line-height, 1.6rem)
	);
}

/* Ruled lines are decoration: dropped on request, and where the caller has
   asked for a plain card. */
.note-card--plain .note-card__body,
.note-card--plain {
	background-image: none;
}

@media (prefers-contrast: more) {
	.note-card__body {
		background-image: none;
	}
}

/* Create / edit form: actions stay reachable at the foot of a long form. */
.password-form__actions {
	position: sticky;
	bottom: 0;
	z-index: 2;
	background: linear-gradient(to top, var(--bs-body-bg) 65%, transparent);
}

@media (prefers-reduced-motion: no-preference) {
	.password-form .form-control,
	.password-form .form-select {
		transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
	}
}
