body {
	font-family: var(--bs-body-font-family);
	background-color: var(--bs-body-bg);
	min-height: 100vh;
	display: flex;
	flex-direction: column;
}

main {
	flex: 1;
}

/* Footer structure; colors come from the edition */
.magfaces-footer {
	padding: 0.875rem 1.5rem;
	margin-top: auto;
}

.magfaces-row {
	word-break: break-word;
}

/* Non-centered offset rows fill the recovered space when offsets collapse
   below 1600px, so content spans full width instead of leaving a gutter.
   Applies to single- and multi-column rows alike. Use mf:row centered
   (.magfaces-row-centered) to opt into centering instead of filling. */
@media (min-width: 768px) and (max-width: 1599.98px) {
	.magfaces-row:not(.magfaces-row-centered):has(> [class*="offset-lg-"])>.magfaces-col {
		flex-grow: 1;
	}
}

/* Explicit opt-in centering (mf:row centered="true"). Centers the row's
   column(s) and neutralizes any offset on them so offset + centered do not
   double-shift. Width-independent: applies at every breakpoint. */
.magfaces-row-centered {
	justify-content: center;
}

.magfaces-row-centered>[class*="offset-lg-"] {
	margin-left: 0;
}

/* Offsets are decorative whitespace: suppress them until there is enough room.
   Bootstrap's offset-lg-* fires at 992px, but content stays cramped until ~1600px.
   Override with higher specificity (.magfaces-row >) so it beats Bootstrap. */
@media (min-width: 992px) and (max-width: 1599.98px) {
	.magfaces-row>[class*="offset-lg-"] {
		margin-left: 0;
	}
}

.magfaces-card-header-text {
	font-weight: var(--em-card-header-font-weight, bold);
}

/* The label hugs its control; the pad below owns the gap to the next field, so
   a field's vertical spacing lives in one place instead of three. */
.magfaces-form-label {
	display: block;
	font-size: var(--em-form-label-font-size);
	font-weight: var(--em-form-label-font-weight);
	color: var(--em-form-label-color);
	line-height: var(--em-control-line-height);
	margin-top: 0;
	margin-bottom: var(--em-form-label-gap);
}

/* Shared component rhythm and UX fixes (theme-independent; editions supply
   values via the --em-* contract tokens: --em-primary, --em-on-primary,
   --em-control-font-size, --em-control-radius, --em-field-padding-y,
   --em-field-padding-x, --em-field-border-width, --em-field-border,
   --em-focus-ring, --em-checked-color, --em-checked-bg, --em-checked-border) */

/* Unified control sizing: fields and buttons render at identical heights in
   every edition because both derive from the same tokens. Button padding-y
   absorbs the difference between the field border width and Bootstrap's
   1px button border. */
.form-control,
.magfaces-form-control-link {
	background-color: var(--em-field-bg);
	border: var(--em-field-border-width) solid var(--em-field-border);
	border-radius: var(--em-control-radius);
	padding: var(--em-field-padding-y) var(--em-field-padding-x);
	font-size: var(--em-control-font-size);
	line-height: var(--em-control-line-height);
}

/* Same chrome for selects, preserving Bootstrap's 2.25rem caret gutter */
.form-select {
	background-color: var(--em-field-bg);
	border: var(--em-field-border-width) solid var(--em-field-border);
	border-radius: var(--em-control-radius);
	padding: var(--em-field-padding-y) 2.25rem var(--em-field-padding-y) var(--em-field-padding-x);
	font-size: var(--em-control-font-size);
	line-height: var(--em-control-line-height);
}

/* Bootstrap's textarea.form-control ships a min-height that outranks a class */
textarea.magfaces-input-textarea {
	min-height: var(--em-textarea-min-height);
	line-height: var(--em-control-line-height);
	resize: vertical;
}

/* Bootstrap's own -sm min-height is a fractional em calculation that ignores
   the control tokens. Padding and line height already set the height. */
.form-control-sm,
.form-select-sm {
	min-height: 0;
}

/* Helper text under a field. Bootstrap leaves the line box to the inherited
   --bs-body-line-height ratio, which lands on a fraction of a pixel at every
   font size. Take the control line height so the text sits on the same rhythm
   as the field above it. */
.form-text {
	line-height: var(--em-control-line-height);
}

/* Bootstrap 5.3 declares --bs-card-color as an EMPTY placeholder (`--bs-card-color: ;`)
   and uses it for .card-body color. Because the property is defined-but-empty, a
   var() fallback never fires: the empty substitution makes `color` invalid at
   computed-value time and CSS falls back to inheriting the ancestor's color. For a
   card on the page that happens to be correct, but a card nested inside an ancestor
   that changes `color` (e.g. .magfaces-loading) inherits the wrong value. Each
   edition therefore supplies --em-card-color, which we consume here. */
.card {
	color: var(--em-card-color);
}

.card-body {
	padding: var(--em-card-body-padding);
}

/* The header already provides visual separation, so trim the body's top gap. */
.card-header+.card-body {
	padding-top: var(--em-card-body-padding-after-header);
}

.card-header {
	padding: var(--em-card-header-padding, 0.625rem 1rem);
	font-weight: var(--em-card-header-font-weight, 700);
}

/* Card-body padding handles the bottom gap; drop Bootstrap's table margin */
.card-body .table {
	margin-bottom: 0;
}

/* Button sizing comes from the same tokens as fields (see unified control
   sizing). Text wrapping: default Bootstrap behavior (wrap) so constrained
   buttons (e.g. width-full in narrow columns) never overflow; nowrap is
   applied only in contexts that handle it - justify rows wrap whole buttons,
   and the navbar wraps whole links/items. */
.btn {
	--bs-btn-border-radius: var(--em-control-radius);
	--bs-btn-font-size: var(--em-control-font-size);
	--bs-btn-padding-y: calc(var(--em-field-padding-y) + var(--em-field-border-width) - var(--bs-btn-border-width) + var(--em-btn-padding-y-delta, 0px));
}

.magfaces.btn,
.magfaces-justify-center .btn,
.magfaces-justify-right .btn,
.magfaces-justify-left .btn,
.magfaces-justify-evenly .btn,
.magfaces-justify-around .btn,
.magfaces-justify-between .btn,
.magfaces.navbar .btn,
.magfaces.navbar .nav-link {
	white-space: nowrap;
}

/* A command button row is a flex container, and a flex item defaults to
   min-width: auto, which floors it at min-content. With the nowrap above, that
   min-content is the whole unbroken label, so a button in a narrow column
   spills out of its card instead of shrinking. Let the items shrink, and let
   the label wrap once the column is narrower than the label. Wrapping only
   happens when the button would otherwise overflow. */
.magfaces-commandbuttonrow>* {
	min-width: 0;
}

.magfaces-commandbuttonrow .btn {
	white-space: normal;
}


/* Form control rows: flex row that aligns labeled controls (inputs, selects)
   with unlabeled children (buttons). Labelless children get pushed down by the
   label line-height + gap so they sit on the same control line.
   Below md the row stacks: children get a small top margin that
   matches the label margin-bottom so the vertical rhythm stays even.
   At md+ the margin is removed and flex gap takes over. */
.magfaces-form-control-row .btn {
	width: 100%;
}

.magfaces-form-control-row>*+* {
	margin-top: 6px;
}

@media (min-width: 768px) {
	.magfaces-form-control-row>*+* {
		margin-top: 0;
	}

	.magfaces-form-control-row>* {
		flex: 2 1 0;
		min-width: 0;
	}

	.magfaces-form-control-row>div:not(:has(.magfaces-form-label)) {
		padding-top: calc(var(--em-control-line-height) + var(--em-form-label-gap) - 1px);
		flex: 1 1 0;
	}
}

/* Labeled horizontal rule: heading text with a line extending to the right.
   margin-bottom fine-tunes the gap below the centered hr line to ~8px. */
.magfaces-hr {
	display: flex;
	align-items: center;
	gap: var(--em-space-2);
	margin-bottom: 0.2em;
}

/* Pull the next magfaces component up so its label sits at the same distance
   from the hr line as plain text. Cancels the component's own top pad. */
div:has(> .magfaces-pad .magfaces-hr)+div:has(> .magfaces-pad),
div:has(> .magfaces-pad>hr)+div:has(> .magfaces-pad) {
	margin-top: calc(-1 * var(--em-space-5));
}

.magfaces-hr>h5 {
	margin: 0;
	white-space: nowrap;
}

.magfaces-hr>hr {
	flex: 1;
	margin: 0;
}

/* When an mf:hr follows a <p>, pull the hr up by the <p>'s margin-bottom
   so the gap above the hr is purely its own pad. Avoids nested :has(). */
p+div:has(.magfaces-hr),
p+div:has(> .magfaces-pad>hr) {
	margin-top: calc(-1 * var(--em-space-4));
}

/* Tables: flip striping to even rows so the first data row after a dark
   header is always light. Covers both BS 5.3 (--bs-table-bg-type via
   :nth-of-type) and DataTables (.odd/.even class + box-shadow). */
.table-striped.magfaces>tbody>tr:nth-of-type(odd)>* {
	--bs-table-bg-type: initial;
}

.table-striped.magfaces>tbody>tr:nth-of-type(even)>* {
	--bs-table-bg-type: var(--bs-table-striped-bg);
}

table.dataTable.table-striped.magfaces>tbody>tr.odd>* {
	box-shadow: none;
}

table.dataTable.table-striped.magfaces>tbody>tr.even>* {
	box-shadow: var(--bs-table-striped-bg) 0px 0px 0px 9999px inset;
}



/* Navbar forms: vertically center inputs and buttons on the same baseline.
   Flex gap owns ALL spacing here; the pad system's padding utilities
   (pe-1 etc., which are !important) are neutralized because they pollute
   the gaps asymmetrically. */
.magfaces-nav-form {
	align-items: center;
	gap: 0.5rem;
}

.magfaces-nav-form .magfaces-pad {
	padding: 0 !important;
}

/* An expanded navbar centers its items with flex, so a pad's top padding
   pushes its item below center by half that padding, at any item height.
   Zeroing the vertical padding makes centering independent of both the pad
   and the item's height. Below the expand breakpoint the collapse stacks
   vertically and the pads still own that spacing, so scope this to the
   expanded state only. Horizontal pad padding is left alone. */
@media (min-width: 768px) {
	.magfaces.navbar-expand-md .magfaces-pad {
		padding-top: 0 !important;
		padding-bottom: 0 !important;
	}
}

/* Navbar alignment: the container aligns the brand and the collapse by text
   baseline, so the first row of nav-links shares a line with the brand
   regardless of the brand's font size. Baseline is applied ONLY here - inside
   the collapse, items (forms, buttons) center against the collapse height;
   pushing baseline deeper makes each form compute its own baseline and
   misalign. Nav-link rows are tightened via --bs-nav-link-padding-y for
   denser stacking when links wrap. */
.magfaces.navbar {
	--bs-nav-link-padding-y: 0;
}

.magfaces.navbar .nav-link {
	--bs-nav-link-padding-y: 0;
}

.magfaces.navbar>.container,
.magfaces.navbar>.container-fluid {
	align-items: baseline;
}

.magfaces.navbar>.container:has(.magfaces-navbar-brand-img),
.magfaces.navbar>.container-fluid:has(.magfaces-navbar-brand-img) {
	align-items: center;
}

.magfaces.navbar .navbar-collapse,
.magfaces.navbar .navbar-nav {
	align-items: center;
}

/* When links exceed the available width, wrap whole links onto a second row
   rather than wrapping text inside each label. */
.magfaces.navbar .navbar-nav {
	flex-wrap: wrap;
}

/* Spacing between adjacent navbar items (forms, nav) */
.magfaces.navbar .navbar-collapse {
	gap: 0.5rem;
}

/* Vertical rhythm: 16px between field groups, 20px before an action row, 8px
   after an hr (which already spends 16px), 16px between cards in a column. */
.card-body .magfaces-pad.pt-1 {
	padding-top: var(--em-space-5) !important;
}

.card-body .magfaces-pad.pt-2 {
	padding-top: var(--em-space-6) !important;
}

.card-body .magfaces-pad.pt-3 {
	padding-top: var(--em-space-7) !important;
}

/* The card body's own padding already spaces the first field. The wrapper must
   itself be the card body's first child: without that, this reached into every
   later form in the body and flattened its first field against whatever sat
   above it. */
.card-body> :is(form, div):first-child>div:first-child>.magfaces-pad.pt-1 {
	padding-top: 0 !important;
}

.card-body hr+div>.magfaces-pad.pt-2 {
	padding-top: var(--em-space-3) !important;
}

.magfaces-col>.magfaces-pad.pt-2 {
	padding-top: var(--em-space-5) !important;
}

.magfaces-col>.magfaces-pad.pt-3 {
	padding-top: var(--em-space-6) !important;
}

/* Focus and checked states from contract tokens */
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
	border-color: var(--em-field-focus-border);
	box-shadow: var(--em-focus-ring);
}

.form-control:focus,
.form-select:focus {
	background-color: var(--em-field-bg);
}

.form-check-input:checked {
	background-color: var(--em-primary);
	border-color: var(--em-primary);
}

.form-range::-webkit-slider-thumb {
	background-color: var(--em-primary);
}

.form-range::-moz-range-thumb {
	background-color: var(--em-primary);
}

.form-check-input {
	--bs-form-check-bg: var(--em-field-bg);
}

/* Vertically center checkbox/switch with its label. This row is flex + gap, so
   Bootstrap's float, negative margin and 1.5em/2.5em padding gutters indent
   checkbox, switch and radio by three different amounts and none of them line
   up with the fields above. */
.form-check {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding-left: 0;
	margin-bottom: 0;
	min-height: 0;
}

.form-check .form-check-input {
	float: none;
	margin-top: 0;
	margin-left: 0;
	width: var(--em-check-size);
	height: var(--em-check-size);
}

/* The switch is also an input[type=checkbox] but must stay a pill */
.form-check:not(.form-switch)>input[type="checkbox"].form-check-input {
	border-radius: var(--em-check-radius, 0.25em);
}

.form-control:disabled,
.form-select:disabled,
.magfaces-form-control-link-disabled {
	background-color: var(--em-field-disabled-bg);
	color: var(--em-field-disabled-color);
	border-color: var(--em-field-disabled-border);
	border-width: var(--em-field-disabled-border-width, var(--em-field-border-width));
	padding-top: var(--em-field-disabled-padding-y, var(--em-field-padding-y));
	padding-bottom: var(--em-field-disabled-padding-y, var(--em-field-padding-y));
	opacity: 1;
	cursor: not-allowed;
}

/* Editions that fill disabled checks from tokens set this to 1 */
.form-check-input:disabled {
	opacity: var(--em-check-disabled-opacity, 0.65);
}

/* formControlLink is a field: same fill as one, and it centres its anchor,
   which as an inline box sat a pixel high in the control. */
.magfaces-form-control-link {
	display: flex;
	align-items: center;
}

.magfaces-form-control-link:not(.magfaces-form-control-link-disabled) {
	background-color: var(--em-field-bg);
}

.magfaces-form-control-link-disabled {
	pointer-events: none;
}

/* The renderer emits a <span>, not an <a>, when the link is disabled */
.magfaces-form-control-link-disabled :is(a, span) {
	color: var(--em-field-disabled-color);
	text-decoration: none;
}

/* DataTables control bar: dataTables.bootstrap5 css loads after this sheet.
   Force the length + filter controls to share one row at all viewport widths
   (DataTables' col-sm-12 stacks them vertically on narrow screens).
   .dt-bootstrap5 bumps specificity to beat the library without !important. */
div.dataTables_wrapper.dt-bootstrap5>.row:first-child {
	flex-wrap: nowrap;
	align-items: center;
}

div.dataTables_wrapper.dt-bootstrap5>.row:first-child>div[class*="col-"] {
	flex: 0 0 auto;
	width: auto;
}

/* The filter's grid column hugs the right edge and may shrink when narrow */
div.dataTables_wrapper.dt-bootstrap5>.row:first-child>div[class*="col-"]:has(div.dataTables_filter) {
	margin-left: auto;
	flex: 0 1 auto;
	min-width: 0;
}

/* Identical label treatment so "entries" and "Search:" sit on one baseline */
div.dataTables_wrapper.dt-bootstrap5 div.dataTables_length label,
div.dataTables_wrapper.dt-bootstrap5 div.dataTables_filter label {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	margin-bottom: 0;
	white-space: nowrap;
}

div.dataTables_wrapper.dt-bootstrap5 div.dataTables_filter label {
	display: flex;
	/* lets the input flex-shrink inside the label */
}

div.dataTables_wrapper.dt-bootstrap5 div.dataTables_filter input {
	margin-left: 0;
	flex: 1 1 auto;
	width: auto;
	min-width: 0;
}

/* DataTable "Show entries" select: give the caret room. No padding shorthand
   here - it resets the gutter and clips the value behind the chevron. */
.dataTables_length select {
	padding-right: 2rem;
	min-width: 4.25rem;
}

div.dataTables_wrapper.dt-bootstrap5 :is(.dataTables_length, .dataTables_filter) :is(select, input) {
	font-size: var(--em-font-meta);
	line-height: var(--em-control-line-height);
	padding-top: var(--em-field-padding-y-sm);
	padding-bottom: var(--em-field-padding-y-sm);
}

/* Sort arrows paint inside the cell's right padding */
div.dataTables_wrapper.dt-bootstrap5 table>thead>tr>th[class*="sorting"] {
	padding-right: 1.5rem;
}

div.dataTables_wrapper.dt-bootstrap5>.row:first-child {
	margin-bottom: var(--em-space-4);
}

div.dataTables_wrapper.dt-bootstrap5>.row:last-child {
	margin-top: var(--em-space-4);
	align-items: center;
}

pre {
	display: block;
	padding: .5em;
	margin: 0 0 .5em;
	font-size: 1.0em;
	color: #333333;
	word-break: break-all;
	word-wrap: break-word;
	background-color: #eaeaea !important;
	border: 1px solid #ccc;
	border-radius: 4px;
	font-family: monospace;
}

.btn-check:checked+.btn-secondary {
	color: var(--em-checked-color);
	background-color: var(--em-checked-bg);
	border-color: var(--em-checked-border);
	box-shadow: var(--em-checked-ring, none);
}

/* Segmented control: each segment carries its own hairline and its own radius.
   A border on the group clipped the selected fill, and at fractional flex
   widths left a sliver of group background beside it.
   The group wraps when the segments do not fit, and CSS cannot select the last
   segment on a wrapped line. Radii on :first-of-type and :last-of-type
   therefore left every wrapped line square at the break, so each segment
   rounds itself instead. The gap keeps neighbouring rounded corners apart. It
   is half a space-2 step, which is 3px and a whole pixel at a 16px root. */
.magfaces-selectOneButtons {
	display: flex;
	flex-wrap: wrap;
	align-items: stretch;
	gap: calc(var(--em-space-2) / 2);
	padding: 0;
	border: none;
	font-size: var(--em-control-font-size);
}

.magfaces-selectOneButtons>.magfaces-btn-label {
	/* A segmented control is an input, so it keeps the field height, not the
	   taller action-button height. Zero the delta; the calc stays in .btn. */
	--em-btn-padding-y-delta: 0px;
	display: flex;
	align-items: center;
	justify-content: center;
	flex: 1 1 0;
	min-width: 6rem;
	border-radius: var(--em-control-radius);
}

/* The renderer hard-codes me-1, and that utility is !important */
.magfaces-selectOneButtons>.magfaces-btn-label.me-1 {
	margin: 0 !important;
}

.magfaces-selectOneRadio {
	font-size: var(--em-control-font-size);
}

.magfaces-selectOneRadio>.form-check+.form-check {
	margin-top: var(--em-space-3);
}

.magfaces-selectOneRadio .magfaces-invalid-feedback {
	margin-left: -24px;
}

/* Hidden until DataTables initializes it, to avoid a flash of unstyled table on page load */
.magfaces-table-rich {
	display: none;
}

.magfaces.alert {
	margin-top: .5rem;
	margin-bottom: .5rem;
}

.magfaces-alert-last {
	margin-bottom: 0 !important;
}

/* Real dimensions: the old scale(1.6) plus -31px/+9px correction drifted at any
   other font size and pulled the thumb out of its track. */
.magfaces .form-switch>.form-check-input {
	width: var(--em-switch-width);
	height: var(--em-switch-height);
	margin-left: 0;
	margin-top: 0;
	transform: none;
}

.magfaces .form-switch>.form-label {
	margin-left: 0;
	margin-top: 0;
}

html.magfaces-progress,
html.magfaces-progress * {
	cursor: progress !important;
	pointer-events: none !important;
}

.magfaces-loading-overlay {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background: rgba(0, 0, 0, .80);
	z-index: 2000;
	display: none;
}

.magfaces-loading {
	text-align: center;
	color: #FFF;
	position: absolute;
	top: 50%;
	left: 0;
	width: 100%;
	padding: 0 1rem;
}

.magfaces-blockui-loading-card {
	max-width: 600px;
	margin: 0 auto;
	transform: translateY(-50%);
	--bs-heading-color: #FFF;
}

.magfaces-width-full {
	width: 100%;
}

.magfaces-width-half {
	width: 50%;
}

.magfaces-width-contain {
	width: fit-content;
}

/* Button rows: wrap whole buttons to the next line before text wraps inside them */
.magfaces-justify-center,
.magfaces-justify-right,
.magfaces-justify-left,
.magfaces-justify-evenly,
.magfaces-justify-around,
.magfaces-justify-between {
	flex-wrap: wrap;
	gap: 0.5rem;
}

.magfaces-justify-center {
	justify-content: center;
}

.magfaces-justify-right {
	justify-content: flex-end;
}

.magfaces-justify-left {
	justify-content: flex-start;
}

.magfaces-justify-evenly {
	justify-content: space-evenly;
}

.magfaces-justify-around {
	justify-content: space-around;
}

.magfaces-justify-between {
	justify-content: space-between;
}

.magfaces.toast {
	padding-top: .5rem;
	padding-bottom: .5rem;
}

.magfaces.toast.info {
	color: var(--bs-info-text-emphasis);
	background: var(--bs-info-bg-subtle);
}

.magfaces.toast.warn {
	color: var(--bs-warning-text-emphasis);
	background: var(--bs-warning-bg-subtle);
}

.magfaces.toast.error {
	color: var(--bs-danger-text-emphasis);
	background: var(--bs-danger-bg-subtle);
}

.magfaces thead th {
	word-break: keep-all;
	white-space: nowrap;
}

.magfaces tbody tr td:first-child {
	word-break: initial;
}

.magfaces tbody tr {
	word-break: break-word;
}

.magfaces input::placeholder {
	opacity: .5;
}

.magfaces.modal {
	color: var(--bs-card-color);
}

.magfaces-blockui-card {
	max-width: 600px;
	margin: 0 auto;
	transform: translateY(-50%);
}

/* MagFaces Half-Column Grid Extensions */
@media (min-width : 768px) {
	.col-md-0h {
		flex: 0 0 auto;
		width: 4.16667%;
	}

	.col-md-1h {
		flex: 0 0 auto;
		width: 12.5%;
	}

	.col-md-2h {
		flex: 0 0 auto;
		width: 20.83333%;
	}

	.col-md-3h {
		flex: 0 0 auto;
		width: 29.16667%;
	}

	.col-md-4h {
		flex: 0 0 auto;
		width: 37.5%;
	}

	.col-md-5h {
		flex: 0 0 auto;
		width: 45.83333%;
	}

	.col-md-6h {
		flex: 0 0 auto;
		width: 54.16667%;
	}

	.col-md-7h {
		flex: 0 0 auto;
		width: 62.5%;
	}

	.col-md-8h {
		flex: 0 0 auto;
		width: 70.83333%;
	}

	.col-md-9h {
		flex: 0 0 auto;
		width: 79.16667%;
	}

	.col-md-10h {
		flex: 0 0 auto;
		width: 87.5%;
	}

	.col-md-11h {
		flex: 0 0 auto;
		width: 95.83333%;
	}
}

/* Half-column offsets: 1600px breakpoint to match the offset suppression above */
@media (min-width : 1600px) {
	.offset-lg-0h {
		margin-left: 4.16667%;
	}

	.offset-lg-1h {
		margin-left: 12.5%;
	}

	.offset-lg-2h {
		margin-left: 20.83333%;
	}

	.offset-lg-3h {
		margin-left: 29.16667%;
	}

	.offset-lg-4h {
		margin-left: 37.5%;
	}

	.offset-lg-5h {
		margin-left: 45.83333%;
	}

	.offset-lg-6h {
		margin-left: 54.16667%;
	}

	.offset-lg-7h {
		margin-left: 62.5%;
	}

	.offset-lg-8h {
		margin-left: 70.83333%;
	}

	.offset-lg-9h {
		margin-left: 79.16667%;
	}

	.offset-lg-10h {
		margin-left: 87.5%;
	}

	.offset-lg-11h {
		margin-left: 95.83333%;
	}
}

/* Nav list — sidebar anchor navigation (mf:navList / mf:navListItem) */
.magfaces-nav-list-title {
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	margin-bottom: 0.5rem;
}

.magfaces-nav-list .nav {
	gap: 0;
}

.magfaces-nav-list .nav-link {
	font-weight: 500;
	line-height: 1.35;
	padding: 0.2rem 0;
}

.magfaces-nav-list-ordered .nav {
	list-style-type: decimal;
	padding-left: 1.5rem;
}

.magfaces-nav-list-ordered .nav-link {
	display: inline;
	padding: 0;
}

/* Tabs — tab pane spacing (mf:tab) */
.magfaces-tab {
	padding-top: var(--bs-card-spacer-y);
}

/* Content section — anchored heading + body (mf:contentSection) */
.magfaces-content-section {
	margin-bottom: 2rem;
}

.magfaces-content-section .magfaces-content-section {
	margin-bottom: 0;
}

.magfaces-content-section:last-child {
	margin-bottom: 0;
}

.magfaces-content-section-heading {
	font-weight: 600;
	margin-bottom: 0.5rem;
}

/* Card accent (mf:card accent="true"): filled card using the edition's primary
   brand color as background, with white headings and links over dark body text.
   Works across all editions via --em-primary. */
.magfaces-card-accent {
	background-color: var(--em-primary);
	border-color: var(--em-primary);
}

.magfaces-card-accent :is(h1, h2, h3, h4, h5, h6) {
	color: #fff;
}

.magfaces-card-accent a {
	color: #fff;
	text-decoration: underline;
}

.magfaces-card-accent a:hover {
	color: rgba(255, 255, 255, 0.8);
}

.magfaces-hero-card {
	position: relative;
	overflow: hidden;
}

.magfaces-hero-image {
	display: block;
	width: 100%;
}

.magfaces-hero-overlay {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	padding: 3rem 1.5rem;
	text-align: center;
	/* inherits to the heading AND the CTA button */
}

.magfaces-hero-heading {
	margin: 0;
	font-weight: 700;
	font-size: clamp(1.75rem, 1.375rem + 2vw, 3rem);
	line-height: 1.2;
	white-space: pre-line;
}

/* Below md the image keeps its natural landscape ratio, so the overlay text
   covers most of a short strip. Crop to a portrait card instead, capped so it
   never fills the screen, and anchor the crop low where the subject usually
   sits. An authored line break is a wide-screen hint: at phone widths it only
   adds a line, so let the heading wrap on its own and balance the lines. */
@media (max-width: 767.98px) {
	.magfaces-hero-image {
		aspect-ratio: 4 / 5;
		max-height: 70vh;
		object-fit: cover;
		object-position: 50% 75%;
	}

	.magfaces-hero-overlay {
		padding: 2rem 1.25rem;
	}

	.magfaces-hero-heading {
		font-size: clamp(1.75rem, 1rem + 4vw, 2.25rem);
		white-space: normal;
		text-wrap: balance;
	}
}

.magfaces-stack-bottom-row {
	margin-top: auto;
}

.magfaces-selectOneCards {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}

/* display:flex is restated deliberately. Bootstrap's .form-check and this rule
   carry equal specificity, so relying on source order alone would be fragile. */
.magfaces-selectOneCards>.form-check {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	margin-bottom: 0;
	padding: 1rem;
	border: var(--em-field-border-width) solid var(--bs-border-color);
	border-radius: var(--em-control-radius);
	background-color: var(--em-field-bg);
	position: relative;
	cursor: pointer;
}

/* Bootstrap floats the input and pulls it left by 1.5em to sit in the padding
   gutter of a block .form-check. This component pads evenly instead, so both
   the float and the negative margin have to go. */
.magfaces-selectOneCards>.form-check>.form-check-input {
	float: none;
	margin-left: 0;
	flex-shrink: 0;
	position: relative;
	z-index: 2;
}

/* :has() is required rather than the sibling selector Bootstrap uses: the input
   is a descendant of the option container, not a sibling of it. */
.magfaces-selectOneCards>.form-check:has(> .form-check-input:checked) {
	border-color: var(--em-primary);
	background-color: var(--bs-primary-bg-subtle);
}

.magfaces-selectOneCards>.form-check>.form-check-label {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	flex: 1;
	min-width: 0;
	cursor: pointer;
}

/* Stretch the label hit-area to cover the entire card so clicks in the
   padding still toggle the radio through the label's for association.
   The ::before is absolutely positioned against .form-check (the nearest
   positioned ancestor), not the label, so it fills the full card including
   padding.  The input sits above it via z-index so its focus ring is
   unobscured. */
.magfaces-selectOneCards>.form-check>.form-check-label::before {
	content: '';
	position: absolute;
	inset: 0;
}

/* An option body authored as an mf:row is a flex child of the label, so it has to
   be told to grow. Bootstrap's negative row margin and the column padding cancel
   out against the option padding, so the content still lines up. */
.magfaces-selectOneCards>.form-check>.form-check-label>.magfaces-row {
	flex: 1;
	min-width: 0;
}

.magfaces-selectOneCards>.form-check:has(> .form-check-input:disabled) {
	cursor: default;
}

.magfaces-selectOneCards>.form-check:has(> .form-check-input:disabled)>.form-check-label {
	cursor: default;
	opacity: 0.65;
}

/* Decorative affordance pinned to the trailing edge of the option. It is a span,
   not a button, so it cannot take focus and cannot be activated on its own. */
.magfaces-selectOneCards-action {
	margin-left: auto;
	flex-shrink: 0;
	pointer-events: none;
	display: inline-grid;
}

/* The renderer emits the text of both states. Both land in the same grid cell, so the
   affordance is always as wide as the longer of the two and its width does not change
   when the selection changes. Without this the option body reflows on every toggle,
   because the affordance is a flex sibling of the body and steals width from it. */
.magfaces-selectOneCards-action>span {
	grid-area: 1 / 1;
}

.magfaces-selectOneCards-action-ghost {
	visibility: hidden;
}

/* mf:outputField — read-only display that occupies the same box model
   as .form-control inside mf:formControlRow.  On mobile all padding is
   zeroed so stacked labels read tightly.  At md+ the value gets top
   padding that matches an input's padding-top + border-top so the text
   baselines align when an outputField sits beside an inputText in a
   flex row. */
.magfaces-outputField {
	border: none;
	border-radius: 0;
	padding: 0;
	font-size: var(--em-control-font-size);
	line-height: var(--em-control-line-height);
	font-weight: 600;
	color: var(--bs-heading-color);
}

/* The label above an outputField uses the disabled-label tokens so it
   visually recedes the same way a disabled input's label does.
   margin-bottom is tightened because without a border to anchor the eye
   the standard label gap reads looser between two text lines than between
   a label and an input border.  The selector matches whether or not a
   pad wrapper is present. */
.magfaces-outputField~.magfaces-form-label,
.magfaces-form-label:has(~ .magfaces-outputField) {
	font-size: var(--em-form-label-disabled-font-size, var(--em-form-label-font-size));
	font-weight: var(--em-form-label-disabled-font-weight, var(--em-form-label-font-weight));
	color: var(--em-form-label-disabled-color, var(--em-form-label-color));
	margin-bottom: 2px;
}
