/*! Strand UI v0.5.0 | MIT License | dillingerstaffing.com */

/* Alert */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-alert {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: space-between;
	width: 100%;
	padding: var(--strand-space-6);
	border-radius: var(--strand-radius-md);
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-sm);
	background: var(--strand-surface-recessed);
}

/* ── Status variants (neutral background, status color on prefix only) ── */
.strand-alert--info {
	background: var(--strand-surface-recessed);
}

.strand-alert--success {
	background: var(--strand-surface-recessed);
}

.strand-alert--warning {
	background: var(--strand-surface-recessed);
}

.strand-alert--error {
	background: var(--strand-surface-recessed);
}

/* ── Stacked layout ──
   Column layout for a multi-line alert body (e.g. a confirmation that pairs
   a status line with a peer-count line and an action) that should read
   top-to-bottom rather than the default single row. Only the axis and
   cross-alignment change; the neutral background and prefix-only status
   color (DL 11.6) are untouched. Replaces a consumer's inline
   flex-direction:column;align-items:stretch override. */
.strand-alert--stack {
	flex-direction: column;
	align-items: stretch;
	gap: var(--strand-space-3);
}

/* ── Status prefix ── */
.strand-alert__status {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-semibold);
	letter-spacing: var(--strand-tracking-wider);
	text-transform: uppercase;
	margin-right: var(--strand-space-3);
	flex-shrink: 0;
}

.strand-alert--info .strand-alert__status {
	color: var(--strand-blue-primary);
}
.strand-alert--success .strand-alert__status {
	color: var(--strand-teal-vital);
}
.strand-alert--warning .strand-alert__status {
	color: var(--strand-amber-caution);
}
.strand-alert--error .strand-alert__status {
	color: var(--strand-red-alert);
}

/* ── Content ── */
.strand-alert__content {
	flex: 1;
	min-width: 0;
}

/* ── Dismiss button ── */
.strand-alert__dismiss {
	flex-shrink: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--strand-space-6);
	height: var(--strand-space-6);
	margin-left: var(--strand-space-4);
	padding: 0;
	border: none;
	border-radius: var(--strand-radius-md);
	background: transparent;
	color: var(--strand-gray-500);
	font-size: var(--strand-text-base);
	cursor: pointer;
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart), color
		var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-alert__dismiss:hover {
	background: var(--strand-gray-100);
	color: var(--strand-gray-600);
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-alert__dismiss {
		transition: none;
	}
}

/* Avatar */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-avatar {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--strand-radius-full);
	background: var(--strand-surface-recessed);
	overflow: hidden;
	flex-shrink: 0;
	font-family: var(--strand-font-sans);
	font-weight: var(--strand-weight-medium);
	color: var(--strand-gray-600);
	user-select: none;
}

/* ── Image ── */
.strand-avatar__img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	border-radius: var(--strand-radius-full);
}

/* ── Initials ── */
.strand-avatar__initials {
	text-transform: uppercase;
	line-height: 1;
}

/* ── Sizes ── */
.strand-avatar--sm {
	width: 32px;
	height: 32px;
	font-size: var(--strand-text-xs);
}

.strand-avatar--md {
	width: 40px;
	height: 40px;
	font-size: var(--strand-text-sm);
}

.strand-avatar--lg {
	width: 48px;
	height: 48px;
	font-size: var(--strand-text-base);
}

.strand-avatar--xl {
	width: var(--strand-space-16);
	height: var(--strand-space-16);
	font-size: var(--strand-text-lg);
}

/* Badge */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Wrapper ── */
.strand-badge {
	position: relative;
	display: inline-flex;
	vertical-align: middle;
}

.strand-badge--inline {
	display: inline-flex;
}

/* ── Indicator (shared) ── */
.strand-badge__indicator {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	font-family: var(--strand-font-sans);
	font-weight: var(--strand-weight-semibold);
	color: var(--strand-on-blue-primary);
}

/* Position at top-right when wrapping children */
.strand-badge:not(.strand-badge--inline) > .strand-badge__indicator {
	position: absolute;
	top: 0;
	right: 0;
	transform: translate(50%, -50%);
	z-index: 1;
}

/* ── Dot variant ── */
.strand-badge--dot {
	width: 8px;
	height: 8px;
	border-radius: var(--strand-radius-full);
	font-size: 0;
	padding: 0;
}

/* ── Count variant ── */
.strand-badge--count {
	min-width: 20px;
	height: 20px;
	padding: 0 var(--strand-space-1);
	border-radius: var(--strand-radius-full);
	font-size: var(--strand-text-xs);
	line-height: 20px;
}

/* ── Status colors ── */
.strand-badge--default {
	background: var(--strand-gray-500);
}

.strand-badge--teal {
	background: var(--strand-teal-vital);
}

.strand-badge--blue {
	background: var(--strand-blue-primary);
}

.strand-badge--amber {
	background: var(--strand-amber-caution);
	color: var(--strand-blue-midnight);
}

.strand-badge--red {
	background: var(--strand-red-alert);
}

/* Banner */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Banner (DL Part XI.6: page-level persistent alert) ──
   Fixed to top of viewport. Pushes page content down.
   System-wide status: maintenance, announcements, warnings. */
.strand-banner {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: 101;
	padding: var(--strand-space-3) var(--strand-space-4);
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-sm);
	line-height: var(--strand-leading-normal);
	text-align: center;
	border-bottom: 1px solid transparent;
}

/* Push nav down when banner is present.
   Two cases handled here:
   - .strand-nav--glass is position:fixed, so it must be offset via top.
   - All other nav variants (default, --instrument) are in document flow,
     so they shift via margin-top instead. The fixed-positioning rule above
     wins for --glass because top is meaningful only when position:fixed,
     and margin-top has no effect on a fixed element. */
.strand-banner ~ .strand-nav--glass {
	top: var(--strand-banner-height, 0px);
}

.strand-banner ~ .strand-nav:not(.strand-nav--glass) {
	margin-top: var(--strand-banner-height, 0px);
}

/* When the page hosts a full-bleed instrument viewport AND a banner is
   present, the viewport height calculation must subtract the banner
   height as well as the nav height. The viewport is a descendant of body,
   not a sibling of .strand-banner, so :has() is required to scope this
   rule to "any document containing a banner". */
body:has(.strand-banner) .strand-instrument-viewport--full-bleed {
	height: calc(100vh - var(--strand-nav-height) - var(--strand-banner-height, 0px));
	height: calc(100dvh - var(--strand-nav-height) - var(--strand-banner-height, 0px));
}

/* JS-managed fallback for browsers without :has() */
body.strand-banner-active .strand-instrument-viewport--full-bleed {
	height: calc(100vh - var(--strand-nav-height) - var(--strand-banner-height, 0px));
	height: calc(100dvh - var(--strand-nav-height) - var(--strand-banner-height, 0px));
}

/* ── Variants ── */
.strand-banner--info {
	background: var(--strand-blue-glow);
	color: var(--strand-blue-deep);
	border-bottom-color: var(--strand-blue-indicator);
}

.strand-banner--warning {
	background: var(--strand-amber-tint);
	color: var(--strand-on-amber-tint);
	border-bottom-color: var(--strand-amber-caution);
}

.strand-banner--critical {
	background: var(--strand-red-tint);
	color: var(--strand-on-red-tint);
	border-bottom-color: var(--strand-red-alert);
}

/* ── Content ── */
.strand-banner__text {
	margin: 0;
	font-weight: var(--strand-weight-medium);
}

/* ── Dismiss button ── */
.strand-banner__dismiss {
	position: absolute;
	right: var(--strand-space-4);
	top: 50%;
	transform: translateY(-50%);
	background: none;
	border: none;
	cursor: pointer;
	color: inherit;
	opacity: 0.6;
	padding: var(--strand-space-2);
	min-width: 44px;
	min-height: 44px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--strand-radius-md);
	transition: opacity var(--strand-duration-fast) ease;
}

.strand-banner__dismiss:hover {
	opacity: 1;
}

.strand-banner__dismiss:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}

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

/* Breadcrumb */
/*! Strand UI | MIT License | dillingerstaffing.com */

.strand-breadcrumb__list {
	display: flex;
	align-items: center;
	gap: var(--strand-space-2);
	list-style: none;
	margin: 0;
	padding: 0;
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-sm);
}

.strand-breadcrumb__item {
	display: inline-flex;
	align-items: center;
	gap: var(--strand-space-2);
}

.strand-breadcrumb__separator {
	color: var(--strand-gray-400);
	user-select: none;
}

.strand-breadcrumb__link {
	color: var(--strand-gray-500);
	text-decoration: none;
	transition: color var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-breadcrumb__link:hover {
	color: var(--strand-gray-600);
}

.strand-breadcrumb__link:focus-visible {
	outline: 2px solid var(--strand-blue-primary);
	outline-offset: 2px;
}

.strand-breadcrumb__current {
	color: var(--strand-gray-600);
	font-weight: var(--strand-weight-medium);
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-breadcrumb__link {
		transition: none;
	}
}

/* Button */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-btn {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--strand-space-2);
	border: 1px solid transparent;
	border-radius: var(--strand-radius-md);
	font-family: var(--strand-font-sans);
	font-weight: var(--strand-weight-medium);
	line-height: var(--strand-leading-snug);
	white-space: nowrap;
	cursor: pointer;
	user-select: none;
	transition:
		background var(--strand-duration-fast) var(--strand-ease-out-quart),
		border-color var(--strand-duration-fast) var(--strand-ease-out-quart),
		color var(--strand-duration-fast) var(--strand-ease-out-quart),
		transform var(--strand-duration-fast) var(--strand-ease-out-expo),
		box-shadow var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-btn:active:not(:disabled) {
	transform: translateY(0);
	transition-duration: 75ms;
}

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

/* ── Sizes ── */
.strand-btn--sm {
	padding: var(--strand-space-2) var(--strand-space-5);
	font-size: var(--strand-text-sm);
	min-height: var(--strand-touch-target);
}

.strand-btn--md {
	padding: var(--strand-space-3) var(--strand-space-8);
	font-size: var(--strand-text-sm);
	min-height: var(--strand-touch-target);
}

.strand-btn--lg {
	padding: var(--strand-space-3) var(--strand-space-10);
	font-size: var(--strand-text-base);
	min-height: var(--strand-touch-target);
}

/* ── Icon-only ── */
.strand-btn--icon-only.strand-btn--sm {
	padding: var(--strand-space-1);
	min-width: var(--strand-touch-target);
}

.strand-btn--icon-only.strand-btn--md {
	padding: var(--strand-space-2);
	min-width: var(--strand-touch-target);
}

.strand-btn--icon-only.strand-btn--lg {
	padding: var(--strand-space-3);
	min-width: 48px;
}

/* ── Circular modifier (icon-only only) ──
   Combine with strand-btn--icon-only + a size modifier for a
   pill/circle button. Use for inline rating stars, avatar
   actions, or reaction chips where a square outline would
   feel harsh. */
.strand-btn--circular {
	border-radius: var(--strand-radius-full);
	aspect-ratio: 1;
}

/* ── Full width ── */
.strand-btn--full-width {
	width: 100%;
}

/* ── Primary variant ── */
.strand-btn--primary {
	background: var(--strand-blue-deep);
	color: var(--strand-on-blue-deep);
}

.strand-btn--primary:hover:not(:disabled) {
	background: var(--strand-blue-midnight);
	transform: translateY(-1px);
	box-shadow: var(--strand-hover-shadow-primary);
}

.strand-btn--primary:active:not(:disabled) {
	background: var(--strand-blue-abyss);
}

/* ── Secondary variant ── */
.strand-btn--secondary {
	background: var(--strand-surface-elevated);
	color: var(--strand-blue-midnight);
	border-color: var(--strand-gray-200);
}

.strand-btn--secondary:hover:not(:disabled) {
	background: var(--strand-blue-glow);
	border-color: var(--strand-blue-indicator);
	transform: translateY(-1px);
	box-shadow: var(--strand-elevation-1);
}

.strand-btn--secondary:active:not(:disabled) {
	background: var(--strand-blue-wash);
}

/* ── Ghost variant ──
   Uses blue-midnight rather than blue-primary because the ghost
   variant has no solid surface underneath, so its text must
   contrast against whatever page surface the button sits on.
   blue-primary (#3b8ef6) only reaches ~2.8:1 against surface-primary
   and blue-deep (#1d5ad8) still borderlines on glass-nav backdrops
   (~4.27:1 when composited over section content). blue-midnight
   (#1e3e5f) reaches ~7.5:1 against those same surfaces, clears
   WCAG 2.2 AA 4.5:1 comfortably, and preserves the blue identity. */
.strand-btn--ghost {
	background: transparent;
	color: var(--strand-blue-midnight);
}

.strand-btn--ghost:hover:not(:disabled) {
	background: var(--strand-blue-glow);
	transform: translateY(-1px);
}

.strand-btn--ghost:active:not(:disabled) {
	background: var(--strand-blue-wash);
}

/* ── Danger variant ── */
.strand-btn--danger {
	background: var(--strand-red-alert);
	color: var(--strand-on-red-alert);
}

.strand-btn--danger:hover:not(:disabled) {
	background: var(--strand-red-alert-vivid);
	transform: translateY(-1px);
	box-shadow: var(--strand-hover-shadow-danger);
}

.strand-btn--danger:active:not(:disabled) {
	background: var(--strand-red-alert-deep);
}

/* ── Loading state ── */
.strand-btn--loading {
	pointer-events: none;
}

/* When the loading spinner is active, hide the label so only
   the spinner is visible. We use visibility (not display) to
   keep the button width stable so the spinner does not jump. */
.strand-btn--loading .strand-btn__content {
	visibility: hidden;
}

.strand-btn__spinner {
	position: absolute;
	width: 16px;
	height: 16px;
	border: 2px solid currentColor;
	border-top-color: transparent;
	border-radius: var(--strand-radius-full);
	animation: strand-btn-spin 0.8s linear infinite;
	opacity: 0.8;
}

.strand-btn--lg .strand-btn__spinner {
	width: 20px;
	height: 20px;
}

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

/* ── Content wrapper ── */
.strand-btn__content {
	display: inline-flex;
	align-items: center;
	gap: var(--strand-space-2);
}

/* ── Focus ring (Part XII: Accessibility Ring) ── */
.strand-btn:focus-visible {
	outline: 2px solid var(--strand-blue-primary);
	outline-offset: 2px;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-btn {
		transition: none;
	}

	.strand-btn:hover:not(:disabled) {
		transform: none;
	}

	.strand-btn__spinner {
		animation: none;
		border-style: dotted;
	}
}

/* Card */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* CF-REF: craft-test.md [17]
   DL Principle 8 (Default Philosophy): A card at default settings must look
   like a glass instrument panel, not a blank rectangle. The base includes
   elevation, border, and padding so zero-customization output is premium.
   Craft test: place next to a Bootstrap/Material card. Must be visibly more refined. */
.strand-card {
	border-radius: var(--strand-radius-lg); /* CF-REF: radius-range.md [1] */
	background: var(--strand-surface-elevated);
	border: 1px solid var(--strand-border-subtle, rgba(148, 163, 184, 0.12));
	box-shadow: var(--strand-elevation-1); /* CF-REF: earned-elevation.md [1] */
	padding: var(--strand-space-6); /* CF-REF: padding-tiers.md [1] */
	font-family: var(--strand-font-sans);
	overflow: hidden;
	box-sizing: border-box;
	max-width: 100%;
}

/* ── Variants ── */
.strand-card--outlined {
	box-shadow: none;
	border: 1px solid var(--strand-gray-200);
}

.strand-card--flat {
	box-shadow: none;
	border: none;
}

/* ── Warm variant (DL Part 7.1b) ──
   Showcase contexts where the warm wood / controlled daylight
   spatial signature should be felt. Adds the warm shadow tint
   alongside the existing cool layers. Default cool variant
   stays the default. Use on hero/landing/gallery cards, never
   on dashboard or readout cards. */
.strand-card--warm {
	box-shadow: var(--strand-elevation-1-warm);
}

.strand-card--warm.strand-card--interactive:hover {
	box-shadow: var(--strand-elevation-2-warm);
}

/* CF-REF: motion-quality-signal.md [14]
   DL Part VI.5: Card hover uses duration-normal (250ms), not fast (150ms).
   Cards are content containers -- smoother transition than buttons.
   Framerate test: play at 30fps, must feel broken. */
.strand-card--interactive {
	cursor: pointer;
	text-decoration: none;
	color: inherit;
	display: block;
	position: relative;
	transition: transform var(--strand-duration-normal) var(--strand-ease-out-expo), box-shadow
		var(--strand-duration-normal) ease;
}

.strand-card--interactive:hover {
	transform: translateY(-2px);
	box-shadow: var(--strand-elevation-2); /* CF-REF: five-level-shadow.md [1] */
}

/* The "alive" signal for live/active cards has been through many
   iterations and each one was wrong: conic-gradient radar sweep
   (too animated), blue-indicator halo plus top edge accent (too
   loud, read as AI-workspace chrome), left-edge accent bar
   (banned, copied by every agentic coding tool as lowest-effort
   default), and finally a blue-glow tinted background (still
   too heavy, reads as "blue borders" on lab/channel grids).

   The active state has NO card-chrome treatment at all. Inline
   affordances (a "Joined" pill, a status-chip--live, a lifecycle
   overline like "Live" or "Early Access", a StatusIndicator) carry
   the entire signal. The class is kept as a no-op semantic hook
   so consumers can still mark active cards for data-attr selectors
   or future treatments, but it paints nothing by default. */
.strand-card--active {
	/* intentionally empty */
}

/* ── Padding modifiers ── */
.strand-card--pad-none {
	padding: 0;
}

.strand-card--pad-sm {
	padding: var(--strand-space-4);
}

.strand-card--pad-md {
	padding: var(--strand-space-6);
}

.strand-card--pad-lg {
	padding: var(--strand-space-8);
}

.strand-card--pad-xl {
	padding: var(--strand-space-10);
}

/* CF-REF: focus-visible.md [4] */
.strand-card--interactive:focus-visible {
	outline: 2px solid var(--strand-blue-primary);
	outline-offset: 2px;
}

/* ── Card section (BEM element, stacked rows inside a card) ──
   A horizontal row inside a composed card. Use multiple
   .strand-card__section children inside a .strand-card--pad-none
   to stack header / body / cta rows separated by horizontal rules.
   First child renders without the top border so the top edge of
   the card is clean. The --header modifier turns a section into a
   top-row header with baseline space-between layout for title
   plus trailing chrome (e.g., overline + chip). This is distinct
   from the standalone .strand-card-section molecule (DL 11.10
   section-boundary production), which is a same-named BEM block
   used outside cards. */
.strand-card__section {
	display: block;
	padding: var(--strand-space-5) var(--strand-space-6);
	border-top: 1px solid var(--strand-gray-200);
}

.strand-card__section:first-child {
	border-top: 0;
}

.strand-card__section--header {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--strand-space-4);
}

/* ── Channel grid helpers ──
   Equal-height card row with a natural top-to-bottom stack inside each
   card and the final horizontal row (the CTA) anchored to the card's
   bottom edge. Cards in the same row align to the tallest card's
   height; an uneven content mix never produces the ragged-bottom look
   where some cards end tall and others stop short. Useful for channel
   listings, membership grids, and any layout where cards carry a
   primary CTA that must read as the shared affordance across the row. */
.strand-channel-grid {
	align-items: stretch;
}
.strand-channel-grid > .strand-card {
	display: flex;
	flex-direction: column;
}
.strand-channel-grid > .strand-card > .strand-stack--vertical {
	flex: 1;
	display: flex;
	flex-direction: column;
}
.strand-channel-grid
	> .strand-card
	> .strand-stack--vertical
	> .strand-stack--horizontal:last-child {
	margin-top: auto;
}

.strand-channel-title {
	margin: 0;
	line-height: var(--strand-leading-snug);
}

.strand-channel-description {
	margin: 0;
}

.strand-channel-next {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: var(--strand-space-2);
	margin-top: var(--strand-space-1);
	padding-top: var(--strand-space-2);
	border-top: 1px dashed var(--strand-gray-200);
	font-size: var(--strand-text-xs);
	line-height: var(--strand-leading-snug);
}

.strand-channel-next__label {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	color: var(--strand-gray-500);
}

.strand-channel-next__title {
	color: var(--strand-gray-900);
	font-weight: var(--strand-weight-medium);
}

.strand-channel-next__when {
	color: var(--strand-gray-600);
	font-variant-numeric: tabular-nums;
}

.strand-channel-signin-hint {
	grid-column: 1 / -1;
}

/* CF-REF: reduced-motion.md [9] */
@media (prefers-reduced-motion: reduce) {
	.strand-card--interactive {
		transition: none;
	}

	.strand-card--interactive:hover {
		transform: none;
	}
}

/* Checkbox */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-checkbox {
	display: inline-flex;
	align-items: center;
	gap: var(--strand-space-2);
	min-height: var(--strand-touch-target);
	cursor: pointer;
	user-select: none;
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-sm);
	color: var(--strand-gray-900);
	line-height: var(--strand-leading-snug);
}

/* ── Hidden native input ── */
.strand-checkbox__native {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* ── Custom visual ── */
.strand-checkbox__control {
	display: flex;
	align-items: center;
	justify-content: center;
	width: var(--strand-control-size);
	height: var(--strand-control-size);
	border: 1px solid var(--strand-gray-200);
	border-radius: var(--strand-radius-sm);
	background: var(--strand-surface-elevated);
	color: var(--strand-on-blue-primary);
	flex-shrink: 0;
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart), border-color
		var(--strand-duration-fast) var(--strand-ease-out-quart), box-shadow var(--strand-duration-fast)
		var(--strand-ease-out-quart);
}

.strand-checkbox__icon {
	width: 14px;
	height: 14px;
}

/* ── Focus ring ── */
.strand-checkbox__native:focus-visible ~ .strand-checkbox__control {
	border-color: var(--strand-blue-primary);
	box-shadow: var(--strand-focus-ring);
}

/* ── Checked ── */
.strand-checkbox--checked .strand-checkbox__control {
	background: var(--strand-blue-primary);
	border-color: var(--strand-blue-primary);
}

/* ── Indeterminate ── */
.strand-checkbox--indeterminate .strand-checkbox__control {
	background: var(--strand-blue-primary);
	border-color: var(--strand-blue-primary);
}

/* ── Hover ── */
.strand-checkbox:hover:not(.strand-checkbox--disabled) .strand-checkbox__control {
	border-color: var(--strand-blue-indicator);
}

.strand-checkbox--checked:hover:not(.strand-checkbox--disabled) .strand-checkbox__control,
.strand-checkbox--indeterminate:hover:not(.strand-checkbox--disabled) .strand-checkbox__control {
	background: var(--strand-blue-vivid);
	border-color: var(--strand-blue-vivid);
}

/* ── Label ── */
.strand-checkbox__label {
	color: var(--strand-gray-900);
}

/* ── Disabled ── */
.strand-checkbox--disabled {
	opacity: 0.4;
	cursor: not-allowed;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-checkbox__control {
		transition: none;
	}
}

/* CodeBlock */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Layout ── */
.strand-code-block {
	position: relative;
}

/* ── Label (optional language indicator) ── */
.strand-code-block__label {
	display: block;
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	color: var(--strand-gray-600);
	margin-bottom: var(--strand-space-2);
}

/* ── Code area ──
   Both <pre> and the nested <code> get explicit color AND background
   so axe-core never has to walk inheritance or composite the
   inset box-shadow. Without explicit color/background on the code
   element, axe-core can compute a false-positive contrast violation
   by blending the inset shadow against the surface. */
.strand-code-block__pre,
.strand-code-block__pre code {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-sm);
	line-height: var(--strand-leading-relaxed);
	color: var(--strand-blue-abyss);
	background-color: var(--strand-surface-recessed);
}

.strand-code-block__pre {
	border-radius: var(--strand-radius-lg);
	padding: var(--strand-space-3) var(--strand-space-4);
	overflow-x: auto;
	white-space: pre;
	tab-size: 2;
	margin: 0;
	border: 1px solid var(--strand-gray-200);
	/* Floor the pre at touch-target so that even a theoretical empty
     code block leaves room for the copy affordance. Single-line
     snippets naturally exceed this (padding + line-height + border
     ≈ 47.66px) so this is defensive rather than active. */
	min-height: var(--strand-touch-target);
}

/* ── Copy button ──
   Injected by consumer JS (strand-copy-buttons.js in dillinger) onto any
   .strand-code-block wrapper. The button sits at the top-right of the
   wrapper. The host wrapper carries [data-strand-copy] so the inner
   <pre> can reserve right-side padding for the icon without shifting
   code blocks that have no copy button.
   Default state is low-ink so it does not compete with the code. On
   hover, focus, or parent hover it gains full contrast. Success state
   swaps the icon to a check and flips the label to "Copied" for 1.5s. */
.strand-code-block[data-strand-copy] > .strand-code-block__pre {
	padding-right: var(--strand-space-10);
}

/* Geometry derivation (why these exact values):
   - <pre> for a 1-line snippet: 12 + 21.66 + 12 + 2 = ~47.66px tall
     (padding-top + line-height + padding-bottom + 2×border).
   - Button MUST fit inside the pre or its white surface bleeds out
     the bottom. With top=8 and a 44x44 button, the bottom lands at
     54px which overflows the 47.66 pre by ~6px (the bleed the
     previous fix tried to mask).
   - Visible button is therefore 32×32 (+ 2×1 border = 34×34). With
     top=6 the box occupies y ∈ [6, 40], inside the 47.66 pre with
     ~7.66px of breathing room.
   - First code line center = padding-top (12) + ½ line-height
     (~10.83) = ~22.83px from pre top. Button center = top (6) + 17
     = 23px. Delta = 0.17px, well under the 2px test slop.
   - A `::before` pseudo-element expands the hit area to 44×44
     (WCAG 2.5.8 touch target) without affecting the visible box. */
.strand-code-block__copy {
	position: absolute;
	top: calc(var(--strand-space-2) - 2px);
	right: calc(var(--strand-space-2) - 2px);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--strand-space-8);
	height: var(--strand-space-8);
	padding: 0;
	border: 1px solid var(--strand-gray-200);
	border-radius: var(--strand-radius-md);
	background-color: var(--strand-surface-elevated);
	color: var(--strand-gray-500);
	cursor: pointer;
	opacity: 0.55;
	transition:
		opacity var(--strand-duration-fast) var(--strand-ease-out-expo),
		color var(--strand-duration-fast) ease,
		border-color var(--strand-duration-fast) ease,
		background-color var(--strand-duration-fast) ease,
		transform var(--strand-duration-fast) var(--strand-ease-out-expo);
}

/* Expanded hit area: the visible button is 32x32 so it fits in a
   1-line code block, but WCAG 2.5.8 (Target Size Minimum) prefers a
   larger touch target. This invisible pseudo-element extends the
   click area to 44x44 without shifting any pixels. The halo is
   contained within the reserved right-padding on the pre so it never
   overlaps code text. */
.strand-code-block__copy::before {
	content: "";
	position: absolute;
	inset: calc((var(--strand-touch-target) - var(--strand-space-8)) / -2);
}

.strand-code-block:hover .strand-code-block__copy,
.strand-code-block__copy:hover,
.strand-code-block__copy:focus-visible {
	opacity: 1;
	color: var(--strand-blue-primary);
	border-color: var(--strand-blue-primary);
}

.strand-code-block__copy:hover {
	transform: translateY(-1px);
}

.strand-code-block__copy:active {
	transform: translateY(0);
	transition-duration: 75ms;
}

.strand-code-block__copy:focus-visible {
	outline: 2px solid var(--strand-blue-primary);
	outline-offset: 2px;
}

/* Success state: check icon, green accent, pinned opacity. */
.strand-code-block__copy--copied,
.strand-code-block__copy--copied:hover {
	opacity: 1;
	color: var(--strand-green-positive);
	border-color: var(--strand-green-positive);
	transform: translateY(0);
}

/* Icon swap. Both icons stack in the same cell; the active one has
   display:inline, the inactive one has display:none. No opacity or
   transform animation on the swap so reduced-motion users get the
   same instant visual feedback. */
.strand-code-block__copy-icon {
	width: 16px;
	height: 16px;
	pointer-events: none;
}

.strand-code-block__copy-icon--check {
	display: none;
}

.strand-code-block__copy--copied .strand-code-block__copy-icon--clipboard {
	display: none;
}

.strand-code-block__copy--copied .strand-code-block__copy-icon--check {
	display: inline;
}

@media (prefers-reduced-motion: reduce) {
	.strand-code-block__copy {
		transition: none;
	}
	.strand-code-block__copy:hover,
	.strand-code-block__copy:active,
	.strand-code-block__copy--copied {
		transform: none;
	}
}

/* ── Inline code ── */
.strand-code-inline {
	font-family: var(--strand-font-mono);
	font-size: 0.875em;
	color: var(--strand-blue-deep);
	background: var(--strand-surface-recessed);
	padding: 0.125em 0.375em;
	border-radius: var(--strand-radius-sm);
}

/* Container */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-container {
	width: 100%;
	margin-inline: auto;
	padding-inline: clamp(1.5rem, 5vw, 4rem);
}

/* ── Sizes ── */
.strand-container--narrow {
	max-width: var(--strand-content-narrow);
}

.strand-container--default {
	max-width: var(--strand-content-default);
}

.strand-container--wide {
	max-width: var(--strand-content-wide);
}

.strand-container--full {
	max-width: var(--strand-content-full);
}

/* ContainerScale */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ══════════════════════════════════════════════════
   CONTAINER SCALE (width visualizer)
   ══════════════════════════════════════════════════
   A stacked set of horizontal bars demonstrating container
   widths (narrow / default / wide / full). Each bar's width
   is set inline per row via its consumer. */

.strand-container-scale {
	display: flex;
	flex-direction: column;
	gap: var(--strand-space-4);
}

.strand-container-scale__row {
	display: flex;
	flex-direction: column;
	gap: 6px;
}

.strand-container-scale__label {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	font-size: 11px;
}

.strand-container-scale__label code {
	font-family: var(--strand-font-mono);
	font-size: 11px;
	color: var(--strand-blue-primary);
	font-weight: var(--strand-weight-medium);
	background: transparent;
	padding: 0;
}

.strand-container-scale__caption {
	color: var(--strand-gray-500);
}

.strand-container-scale__track {
	height: 22px;
	background: var(--strand-surface-recessed);
	border-radius: var(--strand-radius-sm);
	position: relative;
	border: 1px solid var(--strand-gray-200);
}

.strand-container-scale__bar {
	height: 100%;
	background: linear-gradient(90deg, rgba(37, 99, 235, 0.14), rgba(37, 99, 235, 0.22));
	border-right: 2px solid var(--strand-blue-primary);
	display: flex;
	align-items: center;
	justify-content: flex-end;
	padding-right: var(--strand-space-2);
	transition: width 300ms var(--strand-ease-out-expo);
}

.strand-container-scale__px {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	font-weight: var(--strand-weight-medium);
	color: var(--strand-blue-primary);
}

.strand-container-scale__axis {
	display: flex;
	justify-content: space-between;
	margin-top: var(--strand-space-1);
	font-family: var(--strand-font-mono);
	font-size: 9px;
	letter-spacing: 0.06em;
	color: var(--strand-gray-400);
}

/* DataReadout */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Layout ── */
.strand-data-readout {
	display: flex;
	flex-direction: column;
	gap: var(--strand-space-1);
}

/* ── Label (overline) ── */
.strand-data-readout__label {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-ultra);
	text-transform: uppercase;
	color: var(--strand-gray-500);
	line-height: var(--strand-leading-normal);
}

/* ── Value (instrument readout) ── */
.strand-data-readout__value {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-3xl);
	font-weight: var(--strand-weight-light);
	letter-spacing: var(--strand-tracking-tighter);
	color: var(--strand-blue-midnight);
	line-height: var(--strand-leading-tight);
	font-variant-numeric: tabular-nums;
}

/* ── Size variants ── */
.strand-data-readout--sm .strand-data-readout__value {
	font-size: var(--strand-text-xl);
}

.strand-data-readout--lg .strand-data-readout__value {
	font-size: var(--strand-text-4xl);
}

.strand-data-readout--xl .strand-data-readout__value {
	font-size: clamp(4.5rem, 10vw, 7rem);
}

/* Dialog */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Backdrop ── */
.strand-dialog__backdrop {
	position: fixed;
	inset: 0;
	z-index: 1000;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--strand-backdrop);
}

/* ── Panel ── */
.strand-dialog__panel {
	position: relative;
	width: 100%;
	max-width: 560px;
	margin: var(--strand-space-4);
	padding: var(--strand-space-8);
	background: var(--strand-surface-elevated);
	border-radius: var(--strand-radius-xl);
	box-shadow: var(--strand-elevation-4);
	font-family: var(--strand-font-sans);
	outline: none;
}

/* ── Header ── */
.strand-dialog__header {
	margin-bottom: var(--strand-space-4);
	padding-right: var(--strand-space-8);
}

/* ── Title ── */
.strand-dialog__title {
	margin: 0;
	font-size: var(--strand-text-lg);
	font-weight: var(--strand-weight-semibold);
	color: var(--strand-blue-midnight);
	line-height: var(--strand-leading-snug);
}

/* ── Close button ── */
.strand-dialog__close {
	position: absolute;
	top: var(--strand-space-6);
	right: var(--strand-space-6);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	padding: 0;
	border: none;
	border-radius: var(--strand-radius-md);
	background: transparent;
	color: var(--strand-gray-500);
	font-size: var(--strand-text-lg);
	cursor: pointer;
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart), color
		var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-dialog__close:hover {
	background: var(--strand-gray-200);
	color: var(--strand-gray-900);
}

/* ── Body ── */
.strand-dialog__body {
	padding-top: var(--strand-space-6);
	color: var(--strand-gray-600);
	font-size: var(--strand-text-sm);
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-dialog__close {
		transition: none;
	}
}

/* Divider */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-divider {
	border: 0;
	margin: 0;
	padding: 0;
}

/* ── Horizontal (hr) ── */
.strand-divider--horizontal {
	width: 100%;
	border-top: 1px solid var(--strand-gray-200);
}

/* ── Vertical ── */
.strand-divider--vertical {
	align-self: stretch;
	border-left: 1px solid var(--strand-gray-200);
}

/* ── Labeled ── */
.strand-divider--labeled {
	display: flex;
	align-items: center;
	gap: var(--strand-space-3);
	border-top: 0;
}

.strand-divider__line {
	flex: 1;
	height: 0;
	border-top: 1px solid var(--strand-gray-200);
}

.strand-divider__label {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	color: var(--strand-gray-400);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	white-space: nowrap;
}

/* ── Gradient (biosynthetic separator) ── */
.strand-divider--gradient {
	border-top: none;
	height: 1px;
	background: linear-gradient(
		90deg,
		transparent 0%,
		var(--strand-blue-indicator) 50%,
		transparent 100%
	);
}

/* FormField */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-form-field {
	display: flex;
	flex-direction: column;
	gap: var(--strand-space-2);
}

/* ── Label ── */
.strand-form-field__label {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	color: var(--strand-gray-500);
	line-height: var(--strand-leading-snug);
}

/* ── Required indicator ──
   Uses blue-primary, not red, because the asterisk is a "this is required"
   accent (informational), not an error. Red is reserved for actual errors
   inside .strand-form-field__error. */
.strand-form-field__required {
	color: var(--strand-blue-primary);
	margin-left: var(--strand-space-1);
}

/* ── Control wrapper ── */
.strand-form-field__control {
	display: flex;
	flex-direction: column;
}

/* ── Hint ── */
.strand-form-field__hint {
	margin: 0;
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-xs);
	color: var(--strand-gray-500);
	line-height: var(--strand-leading-normal);
}

/* ── Error message ── */
.strand-form-field__error {
	margin: 0;
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-xs);
	color: var(--strand-red-alert);
	line-height: var(--strand-leading-normal);
}

/* Grid */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-grid {
	display: grid;
	overflow: hidden;
	max-width: 100%;
	box-sizing: border-box;
}

.strand-grid > * {
	min-width: 0;
}

/* ── Fixed column utilities ── */
.strand-grid--cols-2 {
	grid-template-columns: repeat(2, 1fr);
}
.strand-grid--cols-3 {
	grid-template-columns: repeat(3, 1fr);
}
.strand-grid--cols-4 {
	grid-template-columns: repeat(4, 1fr);
}

/* ── Responsive auto-fit (columns adjust to available width) ──
   Use the size that matches your card content width. The breakpoint
   behavior is determined entirely by the minmax() value:
   - auto-sm  (200px): 1col <410, 2col 410-640, 3col 640-870, 4col 870+
   - auto-220 (220px): 1col <450, 2col 450-700, 3col 700-940, 4col 940+
   - auto-md  (280px): 1col <570, 2col 570-880, 3col 880-1190, 4col 1190+
   - auto-260 (260px): 1col <530, 2col 530-820, 3col 820-1100, 4col 1100+
   - auto-lg  (360px): 1col <730, 2col 730-1120, 3col 1120-1500, 4col 1500+
   The auto-220 and auto-260 presets exist for card layouts that need
   4 columns at 1280 desktop, 2 columns at tablet portrait, and 1 column
   at phone widths. */
.strand-grid--auto-sm {
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.strand-grid--auto-220 {
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.strand-grid--auto-md {
	grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.strand-grid--auto-260 {
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.strand-grid--auto-lg {
	grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
}

/* ── Gap utilities ── */
.strand-grid--gap-1 {
	gap: var(--strand-space-1);
}
.strand-grid--gap-2 {
	gap: var(--strand-space-2);
}
.strand-grid--gap-3 {
	gap: var(--strand-space-3);
}
.strand-grid--gap-4 {
	gap: var(--strand-space-4);
}
.strand-grid--gap-5 {
	gap: var(--strand-space-5);
}
.strand-grid--gap-6 {
	gap: var(--strand-space-6);
}
.strand-grid--gap-8 {
	gap: var(--strand-space-8);
}

/* Input */
/*! Strand UI | MIT License | dillingerstaffing.com */

.strand-input {
	position: relative;
	display: flex;
	align-items: center;
	background: var(--strand-surface-elevated);
	border: 1px solid var(--strand-gray-200);
	border-radius: var(--strand-radius-md);
	transition: border-color var(--strand-duration-fast) var(--strand-ease-out-quart), box-shadow
		var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-input:focus-within {
	border-color: var(--strand-blue-primary);
	box-shadow: var(--strand-focus-ring);
}

/* ── Field ── */
.strand-input__field {
	flex: 1;
	width: 100%;
	padding: var(--strand-space-3) var(--strand-space-4);
	background: transparent;
	border: none;
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-base);
	color: var(--strand-gray-900);
	outline: none;
}

.strand-input__field::placeholder {
	color: var(--strand-gray-400);
}

/* ── Addons ── */
.strand-input__leading,
.strand-input__trailing {
	display: flex;
	align-items: center;
	color: var(--strand-gray-500);
	font-size: var(--strand-text-sm);
}

.strand-input__leading {
	padding-left: var(--strand-space-3);
}

.strand-input__trailing {
	padding-right: var(--strand-space-3);
}

.strand-input--has-leading .strand-input__field {
	padding-left: var(--strand-space-2);
}

.strand-input--has-trailing .strand-input__field {
	padding-right: var(--strand-space-2);
}

/* ── Error ── */
.strand-input--error {
	border-color: var(--strand-red-alert);
}

.strand-input--error:focus-within {
	border-color: var(--strand-red-alert);
	box-shadow: var(--strand-focus-ring-error);
}

/* ── Disabled ── */
.strand-input--disabled {
	opacity: 0.4;
	cursor: not-allowed;
}

.strand-input--disabled .strand-input__field {
	cursor: not-allowed;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-input {
		transition: none;
	}
}

/* InstrumentViewport */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ══════════════════════════════════════════════════
   INSTRUMENT VIEWPORT (DL Part 9.3: Dark Mode Island)
   ══════════════════════════════════════════════════
   The viewport is the only place dark backgrounds appear in the
   lab. The frame around it stays the white lab surface. Inside
   the viewport, every primitive (search bar, panel, pin, legend,
   coordinate readout, scanline, vignette) is built from the
   instrument tokens defined in tokens.css. None of these
   primitives leak the dark surface to the rest of the page.

   Three modes:
   - Default (recessed inside a card)
   - --grid (with subtle blue grid overlay)
   - --full-bleed (page-filling instrument cabinet, requires
     body to be marked .strand-body--instrument so the page
     surface is dark all the way to the edges) */

.strand-instrument-viewport {
	background: var(--strand-blue-abyss);
	color: var(--strand-gray-100);
	border-radius: var(--strand-radius-lg);
	overflow: hidden;
	position: relative;
	padding: var(--strand-space-5);
}

/* Dark-surface overrides: KV and log text needs higher contrast on #0F192A */
.strand-instrument-viewport .strand-kv__label {
	color: var(--strand-gray-400);
}
.strand-instrument-viewport .strand-kv__value {
	color: var(--strand-gray-200);
}

/* Dark-surface cascades for status chips and ghost buttons.
   The light-surface defaults of .strand-status-chip--neutral and
   .strand-btn--ghost lose contrast when placed on the dark
   instrument viewport (full-bleed body or recessed panel). Apply
   matching on-dark treatments so consumers never need inline
   style overrides. Scoped to both .strand-body--instrument (full
   page mode) and .strand-instrument-viewport (recessed panel). */
.strand-body--instrument .strand-status-chip--neutral,
.strand-instrument-viewport .strand-status-chip--neutral {
	background: color-mix(in srgb, var(--strand-gray-400) 12%, transparent);
	color: var(--strand-gray-300);
	border: 1px solid color-mix(in srgb, var(--strand-gray-400) 20%, transparent);
}

.strand-body--instrument .strand-btn--ghost,
.strand-instrument-viewport .strand-btn--ghost {
	color: var(--strand-on-blue-primary);
	border-color: rgba(255, 255, 255, 0.2);
}

/* ── Dark-context text cascade (DL Part 9.3) ──
   The generic text primitives assume the white lab surface: their default
   colors (blue-midnight headlines, gray-500 secondary/overline text) lose
   contrast on the dark viewport. DL 9.3 defines the viewport as a
   self-contained dark island where all text switches to on-dark colors, so
   a consumer places any of these primitives directly on the viewport and
   never hand-tints them. Scoped to both the full-page body mode and the
   recessed panel, matching the kv / status-chip / ghost cascades above. */
.strand-body--instrument .strand-headline,
.strand-instrument-viewport .strand-headline {
	color: var(--strand-on-blue-primary);
}

.strand-body--instrument .strand-text-secondary,
.strand-instrument-viewport .strand-text-secondary {
	color: var(--strand-gray-200);
}

/* The --xs caption tier drops one gray step so it reads as a quieter
   subordinate line. Declared after the base rule so it wins for an element
   carrying both classes (equal specificity, later source order). */
.strand-body--instrument .strand-text-secondary--xs,
.strand-instrument-viewport .strand-text-secondary--xs {
	color: var(--strand-gray-300);
}

.strand-body--instrument .strand-overline,
.strand-instrument-viewport .strand-overline {
	color: var(--strand-gray-400);
}

/* Accent overline: blue-deep is tuned for AA contrast on the white surface
   but goes muddy on the abyss. On dark, the light blue-indicator carries
   the accent. Scoped to the viewport only so the static.css light-surface
   rule keeps blue-deep everywhere else. */
.strand-body--instrument .strand-overline--accent,
.strand-instrument-viewport .strand-overline--accent {
	color: var(--strand-blue-indicator);
}

.strand-body--instrument .strand-data-readout__label,
.strand-instrument-viewport .strand-data-readout__label {
	color: var(--strand-gray-400);
}

/* The readout value's default blue-midnight is near-invisible on the abyss.
   DL 9.3 pairs the on-dark label with an on-dark value, so the full readout
   stays legible on the viewport, not just its label. */
.strand-body--instrument .strand-data-readout__value,
.strand-instrument-viewport .strand-data-readout__value {
	color: var(--strand-on-blue-primary);
}

/* A link placed directly on the dark viewport reads as instrument text:
   inherit the surrounding color and drop the light-surface gradient
   underline. The higher specificity (0,2,0) wins over the base
   .strand-link rule in Link.css regardless of bundle order. */
.strand-body--instrument .strand-link,
.strand-instrument-viewport .strand-link {
	color: inherit;
	background-image: none;
}

/* An alert placed on the dark viewport. Alert.css sets an opaque
   light-surface background (--strand-surface-recessed) and sets no color of
   its own, so on the abyss the panel stayed near-white while its text
   inherited the viewport's near-white --strand-gray-100: white on white,
   with the message effectively invisible. Give the panel the same
   translucent wash the neutral status chip uses on dark, and state the
   content color explicitly rather than leaving it to inherit. The status
   prefix keeps its semantic color (DL 11.6). */
.strand-body--instrument .strand-alert,
.strand-instrument-viewport .strand-alert {
	background: color-mix(in srgb, var(--strand-gray-400) 12%, transparent);
	color: var(--strand-on-blue-primary);
}

/* blue-primary is tuned for the white surface and goes muddy on the abyss,
   exactly as blue-deep does for the accent overline above. The lighter
   blue-indicator carries the info prefix on dark. */
.strand-body--instrument .strand-alert--info .strand-alert__status,
.strand-instrument-viewport .strand-alert--info .strand-alert__status {
	color: var(--strand-blue-indicator);
}

.strand-body--instrument .strand-alert__dismiss,
.strand-instrument-viewport .strand-alert__dismiss {
	color: var(--strand-gray-300);
}

.strand-body--instrument .strand-alert__dismiss:hover,
.strand-instrument-viewport .strand-alert__dismiss:hover {
	background: color-mix(in srgb, var(--strand-gray-400) 20%, transparent);
	color: var(--strand-on-blue-primary);
}

/* Form-control labels. These assume the white lab surface and are the last
   text primitives the cascade did not reach, which made them the worst
   offenders rather than merely low-contrast ones: switch / checkbox / radio
   labels are gray-900, i.e. near-black, so a labelled toggle placed on the
   viewport rendered dark-on-dark at about 1.2:1 and the label was invisible.
   Field labels and hints are gray-500 and landed near 2.8:1. */
.strand-body--instrument .strand-switch__label,
.strand-instrument-viewport .strand-switch__label,
.strand-body--instrument .strand-checkbox__label,
.strand-instrument-viewport .strand-checkbox__label,
.strand-body--instrument .strand-radio__label,
.strand-instrument-viewport .strand-radio__label {
	color: var(--strand-on-blue-primary);
}

.strand-body--instrument .strand-form-field__label,
.strand-instrument-viewport .strand-form-field__label,
.strand-body--instrument .strand-form-field__hint,
.strand-instrument-viewport .strand-form-field__hint {
	color: var(--strand-gray-300);
}

/* ── Light island restore (DL Part 9.6) ──
   A LIGHT surface nested inside the dark cabinet must not have its text
   tinted by the dark-context cascade above (e.g. a consumer's "Position
   Analysis" overline). Re-assert the light-surface base colors for any
   generic text primitive composed inside it. Equal specificity to the
   cascade (0,2,0); declared afterwards so it wins for a node that matches
   both selectors.

   Two selectors share these rules:

     .strand-detail-panel   the slide-in drawer component. Carries its own
                            positioning, sizing and transform.
     .strand-surface-light  a COLORS-ONLY utility for any other light panel a
                            consumer nests in a viewport: a white card, an
                            alert, a callout. It sets no background, no
                            geometry and no position, so it composes onto a
                            surface that already has its own.

   The utility exists because consumers kept reaching for .strand-detail-panel
   to get these colors and inheriting a positioned drawer with it, which
   yanks the panel out of flow and off-screen. If you want the colors, use
   .strand-surface-light; if you want the drawer, use .strand-detail-panel. */
.strand-detail-panel .strand-headline,
.strand-surface-light .strand-headline {
	color: var(--strand-blue-midnight);
}

.strand-detail-panel .strand-text-secondary,
.strand-detail-panel .strand-text-secondary--xs,
.strand-surface-light .strand-text-secondary,
.strand-surface-light .strand-text-secondary--xs {
	color: var(--strand-gray-500);
}

.strand-detail-panel .strand-overline,
.strand-surface-light .strand-overline {
	color: var(--strand-gray-500);
}

.strand-detail-panel .strand-overline--accent,
.strand-surface-light .strand-overline--accent {
	color: var(--strand-blue-deep);
}

.strand-detail-panel .strand-data-readout__label,
.strand-surface-light .strand-data-readout__label {
	color: var(--strand-gray-500);
}

.strand-detail-panel .strand-data-readout__value,
.strand-surface-light .strand-data-readout__value {
	color: var(--strand-blue-midnight);
}

.strand-detail-panel .strand-link,
.strand-surface-light .strand-link {
	color: var(--strand-blue-primary);
	background-image: linear-gradient(currentColor, currentColor);
}

/* An alert inside the light island keeps its light-surface panel and text
   rather than the on-dark wash applied by the cascade above. */
.strand-detail-panel .strand-alert,
.strand-surface-light .strand-alert {
	background: var(--strand-surface-recessed);
	color: var(--strand-gray-700);
}

/* The three cascades declared ABOVE the text block (kv, neutral status chip,
   ghost button) need restoring too, or a light island silently inherits
   on-dark treatments for them. The ghost button was the worst of these: the
   cascade gives it near-white text and a translucent white border, so a
   ghost-weight action placed on a light panel inside a viewport rendered
   white-on-light and was effectively invisible. */
.strand-detail-panel .strand-kv__label,
.strand-surface-light .strand-kv__label {
	color: var(--strand-gray-500);
}

.strand-detail-panel .strand-kv__value,
.strand-surface-light .strand-kv__value {
	color: var(--strand-gray-700);
}

.strand-detail-panel .strand-status-chip--neutral,
.strand-surface-light .strand-status-chip--neutral {
	background: var(--strand-gray-tint);
	color: var(--strand-gray-600);
	border: none;
}

.strand-detail-panel .strand-btn--ghost,
.strand-surface-light .strand-btn--ghost {
	color: var(--strand-blue-midnight);
	border-color: transparent;
}

.strand-detail-panel .strand-alert__dismiss,
.strand-surface-light .strand-alert__dismiss {
	color: var(--strand-gray-500);
}

.strand-detail-panel .strand-switch__label,
.strand-surface-light .strand-switch__label,
.strand-detail-panel .strand-checkbox__label,
.strand-surface-light .strand-checkbox__label,
.strand-detail-panel .strand-radio__label,
.strand-surface-light .strand-radio__label {
	color: var(--strand-gray-900);
}

.strand-detail-panel .strand-form-field__label,
.strand-surface-light .strand-form-field__label,
.strand-detail-panel .strand-form-field__hint,
.strand-surface-light .strand-form-field__hint {
	color: var(--strand-gray-500);
}

.strand-detail-panel .strand-alert--info .strand-alert__status {
	color: var(--strand-blue-primary);
}

.strand-instrument-viewport__label {
	color: var(--strand-gray-400);
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
}

.strand-instrument-viewport__value {
	color: var(--strand-on-blue-primary);
	font-family: var(--strand-font-mono);
	font-variant-numeric: tabular-nums;
}

/* ── Map slot (base layer inside the viewport) ──
   When the instrument viewport hosts a map (DL 9.3 explicitly
   lists maps as supported content), the consumer's map library
   container div must be given a known size. Without explicit
   dimensions a raw <div> collapses to height: 0 and libraries
   like maplibre-gl silently initialize into a zero-size canvas
   and render nothing.

   This slot primitive fills the viewport so the map canvas has
   somewhere to paint. It deliberately does NOT set z-index: a
   z-index would create a new stacking context that traps the
   map library's markers below the viewport's FUI overlays
   (scanline, vignette). Without a z-index, the markers can be
   promoted into the viewport's own stacking context via the
   library-specific marker rules below.

   NOTE on specificity: the selector is scoped via the parent
   .strand-instrument-viewport to outrank .maplibregl-map, which
   maplibre-gl.css (appended at runtime, after strand-ui.css)
   sets to position: relative. Without the descendant selector
   the single-class rules would tie on specificity, and later
   source order would hand the win to maplibre, leaving the
   container as a relatively-positioned in-flow element whose
   height: 100% can collapse to 0 in some mobile browsers when
   the parent chain's explicit heights are not yet resolved at
   paint time. Absolute positioning anchored via inset: 0
   guarantees the container always fills its containing block,
   regardless of maplibre's cascade. */
.strand-instrument-viewport .strand-instrument-viewport__map {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
}

/* ── Optional grid overlay ── */
.strand-instrument-viewport--grid::before {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(var(--strand-viewport-grid-line) 1px, transparent 1px),
		linear-gradient(90deg, var(--strand-viewport-grid-line) 1px, transparent 1px);
	background-size: var(--strand-viewport-grid-size) var(--strand-viewport-grid-size);
	pointer-events: none;
}

/* ── Full-bleed variant (page-filling instrument cabinet) ──
   The viewport occupies all the space below the nav. The body
   must be marked .strand-body--instrument so the dark surface
   reaches the edge of the screen, not just the rounded card. */
.strand-instrument-viewport--full-bleed {
	width: 100%;
	height: calc(100vh - var(--strand-nav-height));
	height: calc(100dvh - var(--strand-nav-height));
	border-radius: 0;
}

/* ── Atmospheric grid overlay (always-on for full-bleed) ── */
.strand-instrument-viewport--full-bleed::before {
	content: "";
	position: absolute;
	inset: 0;
	background-image: linear-gradient(var(--strand-viewport-grid-line) 1px, transparent 1px),
		linear-gradient(90deg, var(--strand-viewport-grid-line) 1px, transparent 1px);
	background-size: 60px 60px;
	pointer-events: none;
	z-index: 3;
}

/* ── Ambient breathing glow (DL Part 7: alive signal) ── */
.strand-instrument-viewport--full-bleed::after {
	content: "";
	position: absolute;
	inset: 0;
	background: radial-gradient(
		ellipse 60% 50% at 50% 40%,
		var(--strand-instrument-glow-soft) 0%,
		transparent 70%
	);
	pointer-events: none;
	z-index: 2;
	animation: strand-instrument-breathe 8s var(--strand-ease-in-out-sine) infinite;
}

@keyframes strand-instrument-breathe {
	0%,
	100% {
		opacity: 0.5;
	}
	50% {
		opacity: 1;
	}
}

/* ──────────────────────────────────────────────
   BODY MODE: full-bleed dark surface
   ──────────────────────────────────────────────
   When a page hosts a full-bleed instrument viewport, the body
   itself must be dark all the way to the edges. The nav also
   needs to switch to its instrument variant. Add
   .strand-body--instrument to <body> to enable this mode. */

.strand-body--instrument {
	background: var(--strand-instrument-bg);
	overflow: hidden;
}

/* ──────────────────────────────────────────────
   NAV: instrument variant (dark glassmorphic)
   ──────────────────────────────────────────────
   When the body is in instrument mode, the nav must also be
   dark + glassmorphic to match the cabinet aesthetic. Apply
   .strand-nav--instrument alongside .strand-nav for a dark
   blurred bar with blue-tinted hairline.

   The selectors compose .strand-nav with .strand-nav--instrument
   so they outweigh the bare .strand-nav rules even though Nav.css
   is bundled later than InstrumentViewport.css. Without the
   compound selector, the white surface from Nav.css would win
   on source order. */

.strand-nav.strand-nav--instrument {
	background: var(--strand-instrument-bg-translucent);
	-webkit-backdrop-filter: blur(16px);
	backdrop-filter: blur(16px);
	border-bottom: 1px solid var(--strand-instrument-border);
}

.strand-nav.strand-nav--instrument .strand-nav__logo {
	color: var(--strand-blue-primary);
	text-shadow: 0 0 12px rgba(59, 142, 246, 0.3);
}

.strand-nav.strand-nav--instrument .strand-nav__link {
	color: var(--strand-instrument-text-secondary);
}

.strand-nav.strand-nav--instrument .strand-nav__link:hover {
	color: var(--strand-instrument-text-primary);
}

/* ──────────────────────────────────────────────
   NAV ACCESSORIES (mono titles + ghost CTAs)
   ──────────────────────────────────────────────
   Lab pages render a small mono title between the logo and the
   primary CTA. The accessories live in the nav__inner container,
   between logo and actions. */

.strand-nav__title {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-sm);
	font-weight: var(--strand-weight-semibold);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	color: var(--strand-instrument-text-primary);
	text-shadow: 0 0 16px rgba(59, 142, 246, 0.15);
	margin-right: auto;
}

.strand-nav__title-tag {
	font-weight: var(--strand-weight-regular);
	color: var(--strand-gray-400);
	margin-left: var(--strand-space-1);
}

/* ──────────────────────────────────────────────
   FUI: SCANLINE
   ──────────────────────────────────────────────
   A horizontal pulse that sweeps the viewport from top to bottom
   on every scan trigger. Uses Principle 7 (alive signal) at the
   highest visibility tier: an event the user can perceive when
   the system is actively processing. Combine with .scanline-
   ambient for the slow continuous baseline pulse. */

.strand-scanline {
	position: absolute;
	left: 0;
	right: 0;
	height: 2px;
	top: -2px;
	background: linear-gradient(
		90deg,
		transparent 0%,
		rgba(59, 142, 246, 0.2) 15%,
		var(--strand-instrument-glow-strong) 50%,
		rgba(59, 142, 246, 0.2) 85%,
		transparent 100%
	);
	box-shadow: 0 0 16px rgba(59, 142, 246, 0.3), 0 0 48px rgba(59, 142, 246, 0.1), 0 1px 0
		var(--strand-instrument-border);
	pointer-events: none;
	z-index: 5;
	opacity: 0;
}

.strand-scanline--active {
	animation: strand-scan 2s var(--strand-ease-out-quart) forwards;
}

@keyframes strand-scan {
	0% {
		top: -2px;
		opacity: 0;
	}
	5% {
		opacity: 0.5;
	}
	90% {
		opacity: 0.5;
	}
	100% {
		top: 100%;
		opacity: 0;
	}
}

/* ── Ambient continuous scan (slow breathing baseline) ── */
.strand-scanline--ambient {
	position: absolute;
	left: 0;
	right: 0;
	height: 1px;
	top: -1px;
	background: linear-gradient(
		90deg,
		transparent 0%,
		rgba(59, 142, 246, 0.08) 30%,
		var(--strand-instrument-glow-medium) 50%,
		rgba(59, 142, 246, 0.08) 70%,
		transparent 100%
	);
	pointer-events: none;
	z-index: 5;
	opacity: 1;
	animation: strand-scan-slow 6s linear infinite;
}

@keyframes strand-scan-slow {
	0% {
		top: -1px;
	}
	100% {
		top: 100%;
	}
}

/* ──────────────────────────────────────────────
   FUI: VIEWPORT VIGNETTE (cursor-following spotlight)
   ──────────────────────────────────────────────
   A radial gradient that follows the user's cursor (via JS that
   sets --mouse-x / --mouse-y custom properties on the viewport),
   darkening the edges so the user's eye is drawn toward whatever
   they are pointing at. The "instrument focus" effect from
   cinematic FUI work. */

.strand-viewport-vignette {
	position: absolute;
	inset: 0;
	pointer-events: none;
	z-index: 4;
	background: radial-gradient(
		ellipse 70% 60% at var(--mouse-x, 50%) var(--mouse-y, 50%),
		transparent 45%,
		rgba(15, 25, 42, 0.35) 100%
	);
	transition: background-position 0.15s ease;
}

/* ──────────────────────────────────────────────
   FUI: COORDINATE READOUT
   ──────────────────────────────────────────────
   A small mono panel in the bottom-left corner that displays the
   instrument's current coordinate (latitude/longitude, or any
   tabular numeric pair). The blinking cursor before the values
   is the alive signal -- the system is reading position right
   now. */

.strand-coord-readout {
	position: absolute;
	bottom: var(--strand-space-4);
	left: var(--strand-space-4);
	z-index: 10;
	font-family: var(--strand-font-mono);
	font-size: 13px;
	font-variant-numeric: tabular-nums;
	letter-spacing: var(--strand-tracking-widest);
	color: rgba(148, 163, 184, 0.65);
	display: flex;
	align-items: center;
	gap: var(--strand-space-4);
	background: var(--strand-instrument-bg-overlay);
	border: 1px solid var(--strand-instrument-border);
	border-radius: var(--strand-radius-sm);
	padding: var(--strand-space-2) var(--strand-space-3);
	box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.3);
}

.strand-coord-readout::before {
	content: "";
	display: inline-block;
	width: 2px;
	height: 12px;
	background: var(--strand-instrument-glow-strong);
	border-radius: var(--strand-radius-sm);
	animation: strand-coord-blink 1.2s steps(2) infinite;
}

@keyframes strand-coord-blink {
	0%,
	49% {
		opacity: 1;
	}
	50%,
	100% {
		opacity: 0;
	}
}

.strand-coord-readout__lat,
.strand-coord-readout__lng {
	display: inline-block;
}

/* ──────────────────────────────────────────────
   FUI: MAP LEGEND (sector classification HUD)
   ──────────────────────────────────────────────
   A small HUD panel listing the sector classifications used by
   the instrument. The colored dots are the connector between
   the legend and the map pins. Hovering an item dims unrelated
   pins so the user can scan one sector at a time. */

.strand-map-legend {
	position: absolute;
	bottom: calc(var(--strand-space-6) + 6rem);
	right: var(--strand-space-4);
	z-index: 10;
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	letter-spacing: var(--strand-tracking-wide);
	color: rgba(148, 163, 184, 0.6);
	display: flex;
	flex-direction: column;
	gap: var(--strand-space-2);
	background: rgba(15, 25, 42, 0.7);
	border: 1px solid var(--strand-instrument-border);
	border-radius: var(--strand-radius-md);
	padding: var(--strand-space-3) var(--strand-space-4);
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
	box-shadow: var(--strand-instrument-shadow-inset), var(--strand-instrument-shadow-soft);
}

.strand-map-legend__title {
	font-weight: var(--strand-weight-semibold);
	text-transform: uppercase;
	letter-spacing: var(--strand-tracking-widest);
	color: rgba(148, 163, 184, 0.4);
	margin-bottom: var(--strand-space-1);
	display: flex;
	align-items: center;
	gap: var(--strand-space-2);
}

.strand-map-legend__title::before {
	content: "";
	width: 4px;
	height: 4px;
	background: var(--strand-instrument-glow-strong);
	border-radius: var(--strand-radius-full);
	animation: strand-instrument-breathe 3s var(--strand-ease-in-out-sine) infinite;
}

.strand-map-legend__item {
	display: flex;
	align-items: center;
	gap: var(--strand-space-3);
	padding: 2px 0;
	cursor: pointer;
	transition: color var(--strand-duration-fast) ease;
}

.strand-map-legend__item:hover {
	color: rgba(226, 232, 240, 0.9);
}

.strand-map-legend__item:hover .strand-map-legend__dot {
	transform: scale(1.4);
	filter: brightness(1.3);
}

.strand-map-legend__dot {
	width: 8px;
	height: 8px;
	border-radius: var(--strand-radius-full);
	transition: transform var(--strand-duration-fast) var(--strand-ease-out-expo), filter
		var(--strand-duration-fast) ease;
}

.strand-map-legend__dot--tech {
	background: var(--strand-sector-tech);
	box-shadow: 0 0 8px rgba(59, 142, 246, 0.5);
}

.strand-map-legend__dot--health {
	background: var(--strand-sector-health);
	box-shadow: 0 0 8px rgba(20, 184, 166, 0.5);
}

.strand-map-legend__dot--trades {
	background: var(--strand-sector-trades);
	box-shadow: 0 0 8px rgba(34, 211, 238, 0.5);
}

.strand-map-legend__dot--finance {
	background: var(--strand-sector-finance);
	box-shadow: 0 0 8px rgba(139, 92, 246, 0.5);
}

/* ──────────────────────────────────────────────
   SEARCH BAR (floating glassmorphic, instrument)
   ──────────────────────────────────────────────
   A floating search bar centered above the viewport. Glass-
   morphic dark surface, biosynthetic blue focus glow. This is
   the input device the user reaches for first inside an
   instrument viewport. */

.strand-search-bar {
	position: absolute;
	top: var(--strand-space-4);
	left: 50%;
	transform: translateX(-50%);
	z-index: 10;
	width: min(520px, calc(100% - var(--strand-space-8)));
	transition: left var(--strand-duration-normal) var(--strand-ease-out-expo), transform
		var(--strand-duration-normal) var(--strand-ease-out-expo), width var(--strand-duration-normal)
		var(--strand-ease-out-expo);
}

.strand-search-bar--shifted {
	left: calc(340px + var(--strand-space-4) + var(--strand-space-4));
	transform: none;
	width: min(480px, calc(100% - 340px - var(--strand-space-4) * 3));
}

.strand-search-bar__inner {
	display: flex;
	align-items: center;
	gap: var(--strand-space-2);
	background: linear-gradient(135deg, rgba(15, 25, 42, 0.88) 0%, rgba(15, 23, 42, 0.85) 100%);
	-webkit-backdrop-filter: blur(20px);
	backdrop-filter: blur(20px);
	border: 1px solid rgba(59, 142, 246, 0.15);
	border-radius: var(--strand-radius-lg);
	padding: var(--strand-space-2) var(--strand-space-4);
	box-shadow: var(--strand-instrument-shadow-deep);
	transition: border-color var(--strand-duration-normal) ease, box-shadow
		var(--strand-duration-normal) ease;
}

.strand-search-bar__inner:focus-within {
	border-color: var(--strand-instrument-border-strong);
	box-shadow: var(--strand-instrument-shadow-deep), inset 0 0 24px rgba(59, 142, 246, 0.06);
}

.strand-search-bar__input {
	flex: 1;
	background: transparent;
	border: none;
	outline: none;
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-base);
	color: var(--strand-instrument-text-primary);
	padding: var(--strand-space-2) 0;
}

.strand-search-bar__input::placeholder {
	color: var(--strand-gray-500);
}

.strand-search-bar__action {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 36px;
	height: 36px;
	border: 1px solid rgba(59, 142, 246, 0.2);
	border-radius: var(--strand-radius-md);
	background: transparent;
	color: var(--strand-gray-400);
	cursor: pointer;
	transition: color var(--strand-duration-fast) ease, border-color var(--strand-duration-fast) ease,
		box-shadow var(--strand-duration-fast) ease;
}

.strand-search-bar__action:hover {
	color: var(--strand-blue-primary);
	border-color: var(--strand-blue-primary);
	box-shadow: 0 0 8px rgba(59, 142, 246, 0.2);
}

.strand-search-bar__action:hover svg {
	filter: drop-shadow(0 0 3px rgba(59, 142, 246, 0.5));
}

.strand-search-bar__action:active {
	transform: scale(0.95);
}

.strand-search-bar__action--danger {
	border-color: rgba(239, 68, 68, 0.2);
}

.strand-search-bar__action--danger:hover {
	color: var(--strand-red-alert);
	border-color: var(--strand-red-alert);
	box-shadow: 0 0 8px rgba(239, 68, 68, 0.2);
}

.strand-search-bar__action[hidden] {
	display: none;
}

.strand-search-bar__action--locating {
	color: var(--strand-blue-primary);
	border-color: var(--strand-blue-primary);
	animation: strand-search-bar-pulse 1s var(--strand-ease-in-out-sine) infinite;
}

@keyframes strand-search-bar-pulse {
	0%,
	100% {
		border-color: var(--strand-blue-primary);
	}
	50% {
		border-color: rgba(59, 142, 246, 0.3);
	}
}

/* ──────────────────────────────────────────────
   RESULTS PANEL (instrument left sidebar)
   ──────────────────────────────────────────────
   The list of items returned by an instrument query. Glass-
   morphic dark surface, fixed left side on desktop, bottom
   sheet on mobile. Holds count, scrollable items, empty/error
   states. */

.strand-results-panel {
	position: absolute;
	top: var(--strand-space-4);
	left: var(--strand-space-4);
	width: 340px;
	max-height: calc(100% - var(--strand-space-8));
	z-index: 10;
	display: flex;
	flex-direction: column;
	background: var(--strand-instrument-bg-glass);
	-webkit-backdrop-filter: blur(16px);
	backdrop-filter: blur(16px);
	border: 1px solid rgba(59, 142, 246, 0.12);
	border-radius: var(--strand-radius-lg);
	overflow: hidden;
	box-shadow: var(--strand-instrument-shadow-medium);
}

.strand-results-panel[hidden] {
	display: none;
}

.strand-results-panel__count {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	color: var(--strand-instrument-text-tertiary);
	padding: var(--strand-space-3) var(--strand-space-4);
	border-bottom: 1px solid rgba(59, 142, 246, 0.08);
}

.strand-results-panel__items {
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
	max-height: calc(100vh - 240px);
	overscroll-behavior: contain;
}

.strand-results-panel__state {
	padding: var(--strand-space-6) var(--strand-space-4);
	text-align: center;
}

.strand-results-panel__state-title {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	letter-spacing: var(--strand-tracking-wide);
	color: var(--strand-instrument-text-tertiary);
}

.strand-results-panel__state-hint {
	font-size: var(--strand-text-xs);
	color: var(--strand-instrument-text-quiet);
	margin-top: var(--strand-space-2);
}

.strand-results-panel__error-link {
	display: inline-block;
	margin-top: var(--strand-space-3);
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	color: var(--strand-blue-primary);
	text-decoration: none;
	border: 1px solid rgba(59, 142, 246, 0.3);
	border-radius: var(--strand-radius-sm);
	padding: var(--strand-space-2) var(--strand-space-3);
	transition: background var(--strand-duration-fast) ease;
}

.strand-results-panel__error-link:hover {
	background: rgba(59, 142, 246, 0.1);
}

/* ── Result card ── */
.strand-result-card {
	display: block;
	padding: var(--strand-space-3) var(--strand-space-4);
	border-bottom: 1px solid rgba(59, 142, 246, 0.06);
	cursor: pointer;
	transition: background var(--strand-duration-fast) ease;
}

.strand-result-card:hover {
	background: rgba(59, 142, 246, 0.08);
}

/* Selection state for a master-detail list row. Uses background
   tint only. No left-edge accent bar (banned pattern, see
   Card.css comment above .strand-card--active). */
.strand-result-card.is-active,
.strand-result-card--active {
	background: rgba(59, 142, 246, 0.12);
}

.strand-result-card__title {
	font-size: var(--strand-text-sm);
	font-weight: var(--strand-weight-medium);
	color: var(--strand-instrument-text-primary);
	margin-bottom: 2px;
	line-height: 1.3;
}

.strand-result-card__company {
	font-size: var(--strand-text-xs);
	color: rgba(148, 163, 184, 0.7);
	margin-bottom: 4px;
}

.strand-result-card__meta {
	display: flex;
	align-items: center;
	gap: var(--strand-space-2);
	flex-wrap: wrap;
}

.strand-result-card__location {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	letter-spacing: var(--strand-tracking-wide);
	color: var(--strand-instrument-text-tertiary);
}

.strand-result-card__salary {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	color: var(--strand-blue-indicator);
}

.strand-result-card__badge {
	font-family: var(--strand-font-mono);
	font-size: 9px;
	letter-spacing: var(--strand-tracking-wide);
	text-transform: uppercase;
	padding: 1px 6px;
	border-radius: var(--strand-radius-sm);
}

.strand-result-card__badge--remote {
	color: var(--strand-teal-vital);
	background: rgba(20, 184, 166, 0.12);
}

.strand-result-card__badge--source {
	color: rgba(148, 163, 184, 0.4);
	background: rgba(148, 163, 184, 0.06);
}

/* ──────────────────────────────────────────────
   DETAIL PANEL (instrument right slide-over)
   ──────────────────────────────────────────────
   The expanded detail surface that slides in from the right
   side of the viewport when the user opens an item. Light
   surface, white frame inside the dark cabinet. Animated
   reveal with staggered children. */

.strand-detail-panel {
	position: absolute;
	top: 0;
	right: 0;
	width: 360px;
	max-width: 100%;
	height: 100%;
	z-index: 20;
	background: linear-gradient(135deg, rgba(248, 250, 252, 0.98) 0%, rgba(241, 245, 249, 0.97) 100%);
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
	border-left: 1px solid rgba(59, 142, 246, 0.1);
	box-shadow: -4px 0 32px rgba(0, 0, 0, 0.25), inset 1px 0 0 rgba(255, 255, 255, 0.4);
	padding: 120px var(--strand-space-6) var(--strand-space-8);
	transform: translateX(100%);
	transition: transform var(--strand-duration-slow) var(--strand-ease-out-expo);
	overflow-y: auto;
}

.strand-detail-panel[hidden] {
	display: none;
}

.strand-detail-panel.is-open,
.strand-detail-panel--open,
.strand-detail-panel.open {
	transform: translateX(0);
}

/* ── Staggered content reveal (Principle 7: alive signal) ──
   The panel and its children share three equivalent state hooks:
   .is-open (BEM-state), --open (BEM-modifier), and .open (legacy
   alias kept for downstream consumers that already use it). All
   three trigger the same reveal sequence. */
.strand-detail-panel.is-open > .strand-overline,
.strand-detail-panel--open > .strand-overline,
.strand-detail-panel.open > .strand-overline {
	animation: strand-detail-fade var(--strand-duration-slow) var(--strand-ease-out-expo) 0.1s both;
}

.strand-detail-panel.is-open .strand-detail-panel__title,
.strand-detail-panel--open .strand-detail-panel__title,
.strand-detail-panel.open .strand-detail-panel__title {
	animation: strand-detail-fade var(--strand-duration-slow) var(--strand-ease-out-expo) 0.18s both;
}

.strand-detail-panel.is-open .strand-detail-panel__meta,
.strand-detail-panel--open .strand-detail-panel__meta,
.strand-detail-panel.open .strand-detail-panel__meta {
	animation: strand-detail-fade var(--strand-duration-slow) var(--strand-ease-out-expo) 0.26s both;
}

.strand-detail-panel.is-open .strand-detail-panel__salary,
.strand-detail-panel--open .strand-detail-panel__salary,
.strand-detail-panel.open .strand-detail-panel__salary {
	animation: strand-detail-fade var(--strand-duration-slow) var(--strand-ease-out-expo) 0.34s both;
}

.strand-detail-panel.is-open .strand-detail-panel__cta,
.strand-detail-panel--open .strand-detail-panel__cta,
.strand-detail-panel.open .strand-detail-panel__cta {
	animation: strand-detail-fade var(--strand-duration-slow) var(--strand-ease-out-expo) 0.42s both;
}

.strand-detail-panel.is-open .strand-detail-panel__source,
.strand-detail-panel--open .strand-detail-panel__source,
.strand-detail-panel.open .strand-detail-panel__source {
	animation: strand-detail-fade var(--strand-duration-slow) var(--strand-ease-out-expo) 0.48s both;
}

@keyframes strand-detail-fade {
	from {
		opacity: 0;
		transform: translateY(10px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

.strand-detail-panel__close {
	position: absolute;
	top: 80px;
	right: var(--strand-space-4);
	width: 32px;
	height: 32px;
	display: flex;
	align-items: center;
	justify-content: center;
	background: transparent;
	border: 1px solid var(--strand-gray-200);
	border-radius: var(--strand-radius-md);
	font-size: var(--strand-text-lg);
	color: var(--strand-gray-500);
	cursor: pointer;
	transition: color var(--strand-duration-fast) ease, border-color var(--strand-duration-fast) ease,
		background var(--strand-duration-fast) ease;
}

.strand-detail-panel__close:hover {
	color: var(--strand-blue-primary);
	border-color: rgba(59, 142, 246, 0.3);
	background: rgba(59, 142, 246, 0.05);
}

.strand-detail-panel__close-text {
	display: none;
}

.strand-detail-panel__close-icon {
	display: inline;
}

.strand-detail-panel__title {
	font-size: 1.375rem;
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-tight);
	color: var(--strand-gray-900);
	margin: 0 0 var(--strand-space-4);
	line-height: 1.3;
}

.strand-detail-panel__meta {
	display: flex;
	flex-direction: column;
	gap: var(--strand-space-1);
	padding-bottom: var(--strand-space-4);
	border-bottom: 1px solid rgba(59, 142, 246, 0.08);
	margin-bottom: var(--strand-space-5);
}

.strand-detail-panel__company,
.strand-detail-panel__location {
	font-size: var(--strand-text-sm);
	color: var(--strand-gray-700);
	line-height: 1.4;
}

.strand-detail-panel__salary {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-lg);
	font-weight: var(--strand-weight-light);
	letter-spacing: var(--strand-tracking-tight);
	color: var(--strand-blue-midnight);
	margin-bottom: var(--strand-space-6);
}

.strand-detail-panel__cta {
	display: block;
	text-align: center;
	text-decoration: none;
	margin-bottom: var(--strand-space-3);
	transition: box-shadow var(--strand-duration-fast) ease, transform var(--strand-duration-fast)
		var(--strand-ease-out-expo);
}

.strand-detail-panel__cta:hover {
	box-shadow: 0 8px 24px rgba(29, 90, 216, 0.25);
}

.strand-detail-panel__cta:active {
	transform: translateY(0) scale(0.98);
}

.strand-detail-panel__source {
	display: block;
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	letter-spacing: var(--strand-tracking-wide);
	color: var(--strand-gray-400);
	text-align: center;
}

/* ──────────────────────────────────────────────
   MAP LOADING (instrument startup screen)
   ──────────────────────────────────────────────
   The full-bleed loading screen that hides the instrument while
   it boots. Centered spinner, mono caption, sweeping bar.
   Fades out via opacity transition. */

.strand-map-loading {
	position: absolute;
	inset: 0;
	z-index: 30;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--strand-space-6);
	background: var(--strand-instrument-bg);
	transition: opacity 0.6s ease;
}

.strand-map-loading.is-hidden,
.strand-map-loading--hidden,
.strand-map-loading.hidden {
	display: none;
}

.strand-map-loading__spinner {
	width: 40px;
	height: 40px;
	border: 1.5px solid rgba(59, 142, 246, 0.15);
	border-top-color: rgba(59, 142, 246, 0.7);
	border-radius: var(--strand-radius-full);
	animation: strand-map-loading-spin 1s linear infinite;
}

@keyframes strand-map-loading-spin {
	to {
		transform: rotate(360deg);
	}
}

.strand-map-loading__text {
	font-family: var(--strand-font-mono);
	font-size: 11px;
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	color: var(--strand-instrument-text-tertiary);
}

.strand-map-loading__bar {
	width: 160px;
	height: 1px;
	background: rgba(59, 142, 246, 0.1);
	border-radius: var(--strand-radius-sm);
	overflow: hidden;
}

.strand-map-loading__bar::after {
	content: "";
	display: block;
	height: 100%;
	width: 40%;
	background: linear-gradient(90deg, transparent, rgba(59, 142, 246, 0.6), transparent);
	animation: strand-map-loading-sweep 1.4s var(--strand-ease-in-out-sine) infinite;
}

@keyframes strand-map-loading-sweep {
	0% {
		transform: translateX(-200%);
	}
	100% {
		transform: translateX(500%);
	}
}

/* ──────────────────────────────────────────────
   MAP PINS + CLUSTER MARKERS
   ──────────────────────────────────────────────
   Pins are the data points the instrument plots on the map.
   Each pin gets a sector color (tech/health/trades/finance) and
   carries a continuous breathing ring + an initial beacon ring
   on spawn. Cluster markers are the metro-level aggregates
   shown when no query is active. */

.strand-map-pin {
	width: 24px;
	height: 24px;
	border-radius: var(--strand-radius-full);
	border: 2.5px solid rgba(255, 255, 255, 0.85);
	cursor: pointer;
	animation: strand-map-pin-spawn var(--strand-duration-slow) var(--strand-ease-spring) both;
	transition: scale var(--strand-duration-fast) var(--strand-ease-out-expo), background
		var(--strand-duration-fast) var(--strand-ease-out-quart);
	box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.15), 0 0 12px currentColor, 0 0 28px currentColor, 0 0
		56px currentColor;
	position: relative;
}

.strand-map-pin::before {
	content: "";
	position: absolute;
	inset: -8px;
	border-radius: var(--strand-radius-full);
	border: 1.5px solid currentColor;
	animation: strand-map-pin-breathe var(--strand-duration-cinematic-slow)
		var(--strand-ease-in-out-sine) infinite;
	animation-delay: inherit;
	pointer-events: none;
}

.strand-map-pin::after {
	content: "";
	position: absolute;
	inset: -4px;
	border-radius: var(--strand-radius-full);
	border: 2px solid rgba(255, 255, 255, 0.5);
	opacity: 0;
	animation: strand-map-pin-beacon var(--strand-duration-cinematic) var(--strand-ease-out-quart)
		forwards;
	animation-delay: inherit;
	pointer-events: none;
}

.strand-map-pin:hover {
	scale: 1.6;
	box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.3), 0 0 16px currentColor, 0 0 40px currentColor, 0 0
		72px currentColor;
}

.strand-map-pin:hover::before {
	animation: none;
	opacity: 0;
}

.strand-map-pin:hover::after {
	animation: strand-map-pin-pulse 1s var(--strand-ease-out-expo) infinite;
}

.strand-map-pin--tech {
	background: var(--strand-sector-tech);
	color: var(--strand-sector-tech);
}

.strand-map-pin--health {
	background: var(--strand-sector-health);
	border-color: rgba(20, 184, 166, 0.7);
	color: var(--strand-sector-health);
}

.strand-map-pin--trades {
	background: var(--strand-sector-trades);
	border-color: rgba(34, 211, 238, 0.7);
	color: var(--strand-sector-trades);
}

.strand-map-pin--finance {
	background: var(--strand-sector-finance);
	border-color: rgba(139, 92, 246, 0.7);
	color: var(--strand-sector-finance);
}

.strand-map-pin.is-highlighted,
.strand-map-pin--highlighted {
	scale: 1.6;
	box-shadow: 0 0 16px currentColor, 0 0 36px currentColor, 0 0 64px rgba(59, 142, 246, 0.4);
	z-index: 10;
}

.strand-map-pin.is-dimmed,
.strand-map-pin--dimmed {
	opacity: 0.15;
	transition: opacity var(--strand-duration-fast) ease;
}

@keyframes strand-map-pin-pulse {
	0% {
		box-shadow: 0 0 0 0 rgba(59, 142, 246, 0.6);
	}
	100% {
		box-shadow: 0 0 0 18px rgba(59, 142, 246, 0);
	}
}

@keyframes strand-map-pin-spawn {
	0% {
		scale: 0;
		opacity: 0;
	}
	50% {
		opacity: 1;
	}
	100% {
		scale: 1;
	}
}

@keyframes strand-map-pin-beacon {
	0% {
		scale: 1;
		opacity: 0.7;
	}
	100% {
		scale: 3;
		opacity: 0;
	}
}

@keyframes strand-map-pin-breathe {
	0%,
	100% {
		opacity: 0.25;
		scale: 1;
	}
	50% {
		opacity: 0.55;
		scale: 1.5;
	}
}

/* ── Cluster marker (metro-level aggregate) ── */
.strand-cluster-marker {
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--strand-radius-full);
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-semibold);
	color: var(--strand-instrument-text-primary);
	border: 2px solid rgba(59, 142, 246, 0.6);
	box-shadow: 0 0 20px rgba(59, 142, 246, 0.4), 0 0 40px rgba(59, 142, 246, 0.15);
	cursor: pointer;
	transition: transform var(--strand-duration-fast) var(--strand-ease-out-expo), box-shadow
		var(--strand-duration-fast) ease;
}

.strand-cluster-marker:hover {
	transform: scale(1.15);
	box-shadow: 0 0 28px rgba(59, 142, 246, 0.6), 0 0 56px rgba(59, 142, 246, 0.25);
}

/* ──────────────────────────────────────────────
   MAPLIBRE COMPATIBILITY OVERRIDES
   ──────────────────────────────────────────────
   When MapLibre GL is rendered inside an instrument viewport,
   its default chrome must be muted and re-themed to fit the
   dark cabinet. These overrides should ONLY take effect inside
   .strand-instrument-viewport so they do not pollute lighter
   maps elsewhere on the site. */

.strand-instrument-viewport .maplibregl-marker {
	z-index: 6 !important;
}

.strand-instrument-viewport .maplibregl-ctrl-bottom-left {
	opacity: 0.25;
	transition: opacity var(--strand-duration-fast) ease;
}

.strand-instrument-viewport .maplibregl-ctrl-bottom-left:hover {
	opacity: 0.7;
}

.strand-instrument-viewport .maplibregl-ctrl-bottom-right {
	bottom: 40px;
	right: 12px;
}

.strand-instrument-viewport .maplibregl-ctrl-bottom-right .maplibregl-ctrl-group {
	background: rgba(15, 25, 42, 0.85);
	border: 1px solid rgba(59, 142, 246, 0.2);
	border-radius: var(--strand-radius-md);
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
}

.strand-instrument-viewport .maplibregl-ctrl-bottom-right .maplibregl-ctrl-group button {
	width: 36px;
	height: 36px;
	background: transparent;
	border: none;
	color: var(--strand-gray-300);
	cursor: pointer;
}

.strand-instrument-viewport .maplibregl-ctrl-bottom-right .maplibregl-ctrl-group button:hover {
	color: var(--strand-blue-primary);
}

.strand-instrument-viewport .maplibregl-ctrl-bottom-right .maplibregl-ctrl-group button + button {
	border-top: 1px solid rgba(59, 142, 246, 0.15);
}

.strand-instrument-viewport .maplibregl-popup-content {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	background: var(--strand-instrument-bg-translucent);
	color: var(--strand-instrument-text-primary);
	border: 1px solid rgba(59, 142, 246, 0.2);
	border-radius: var(--strand-radius-md);
	padding: var(--strand-space-2) var(--strand-space-3);
	box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
}

.strand-instrument-viewport .maplibregl-popup-tip {
	border-top-color: var(--strand-instrument-bg-translucent);
}

/* ──────────────────────────────────────────────
   RESPONSIVE
   ──────────────────────────────────────────────
   On mobile, the results panel becomes a bottom sheet, the
   detail panel slides up from the bottom, the search bar
   takes the full width, and the FUI overlays (legend, coord
   readout) hide because they would crowd small screens. */

@media (max-width: 768px) {
	.strand-results-panel {
		top: auto;
		bottom: 0;
		left: 0;
		width: 100%;
		max-height: 40vh;
		border-radius: var(--strand-radius-lg) var(--strand-radius-lg) 0 0;
		overflow-y: auto;
		-webkit-overflow-scrolling: touch;
		overscroll-behavior: contain;
	}

	.strand-results-panel__items {
		max-height: none;
		overflow-y: visible;
	}

	.strand-search-bar,
	.strand-search-bar--shifted {
		top: var(--strand-space-3);
		left: var(--strand-space-4) !important;
		width: calc(100% - (var(--strand-space-4) * 2)) !important;
		transform: none !important;
		transition: none !important;
	}

	.strand-search-bar__action {
		width: 44px;
		height: 44px;
	}

	.strand-map-pin {
		width: 24px;
		height: 24px;
	}
}

@media (max-width: 640px) {
	.strand-detail-panel {
		width: 100%;
		height: auto;
		max-height: 70vh;
		top: auto;
		bottom: 0;
		border-radius: var(--strand-radius-xl) var(--strand-radius-xl) 0 0;
		padding: 0 var(--strand-space-4) var(--strand-space-6);
		transform: translateY(100%);
		box-shadow: 0 -4px 32px rgba(0, 0, 0, 0.2);
	}

	.strand-detail-panel.is-open,
	.strand-detail-panel--open,
	.strand-detail-panel.open {
		transform: translateY(0);
	}

	.strand-detail-panel__close {
		position: relative;
		top: 0;
		left: 0;
		right: 0;
		width: 100%;
		height: 48px;
		border-radius: 0;
		border: none;
		border-bottom: 1px solid var(--strand-gray-200);
		background: transparent;
		font-family: var(--strand-font-mono);
		font-size: var(--strand-text-sm);
		font-weight: var(--strand-weight-medium);
		letter-spacing: var(--strand-tracking-wider);
		color: var(--strand-blue-primary);
		margin-bottom: var(--strand-space-4);
	}

	.strand-detail-panel__close:hover {
		background: var(--strand-blue-glow);
	}

	.strand-detail-panel__close-icon {
		display: none;
	}

	.strand-detail-panel__close-text {
		display: inline;
	}

	.strand-detail-panel > .strand-overline {
		margin-bottom: var(--strand-space-2);
	}

	.strand-map-legend,
	.strand-coord-readout {
		display: none;
	}

	.strand-search-bar__input {
		font-size: 16px;
	}
}

/* ──────────────────────────────────────────────
   REDUCED MOTION
   ──────────────────────────────────────────────
   Strip every animation and transition from the FUI primitives
   when the user has prefers-reduced-motion. The instrument is
   still readable, just static. */

@media (prefers-reduced-motion: reduce) {
	.strand-scanline,
	.strand-scanline--ambient {
		display: none;
	}

	.strand-instrument-viewport--full-bleed::after {
		animation: none;
	}

	.strand-map-pin {
		animation: none;
	}

	.strand-map-pin::before,
	.strand-map-pin::after {
		animation: none;
	}

	.strand-map-pin:hover {
		animation: none;
	}

	.strand-map-pin:hover::before,
	.strand-map-pin:hover::after {
		animation: none;
	}

	.strand-detail-panel {
		transition: none;
	}

	.strand-detail-panel.is-open > .strand-overline,
	.strand-detail-panel--open > .strand-overline,
	.strand-detail-panel.open > .strand-overline,
	.strand-detail-panel.is-open .strand-detail-panel__title,
	.strand-detail-panel--open .strand-detail-panel__title,
	.strand-detail-panel.open .strand-detail-panel__title,
	.strand-detail-panel.is-open .strand-detail-panel__meta,
	.strand-detail-panel--open .strand-detail-panel__meta,
	.strand-detail-panel.open .strand-detail-panel__meta,
	.strand-detail-panel.is-open .strand-detail-panel__salary,
	.strand-detail-panel--open .strand-detail-panel__salary,
	.strand-detail-panel.open .strand-detail-panel__salary,
	.strand-detail-panel.is-open .strand-detail-panel__cta,
	.strand-detail-panel--open .strand-detail-panel__cta,
	.strand-detail-panel.open .strand-detail-panel__cta,
	.strand-detail-panel.is-open .strand-detail-panel__source,
	.strand-detail-panel--open .strand-detail-panel__source,
	.strand-detail-panel.open .strand-detail-panel__source {
		animation: none;
	}

	.strand-map-legend__title::before {
		animation: none;
	}

	.strand-coord-readout::before {
		animation: none;
	}

	.strand-map-loading__spinner {
		animation: none;
		border-color: rgba(59, 142, 246, 0.4);
	}

	.strand-search-bar__action--locating {
		animation: none;
	}
}

/* LabFrame */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ══════════════════════════════════════════════════
   LAB FRAME (fake in-page window hosting a modal demo)
   ══════════════════════════════════════════════════
   Doc-specific framing primitive. Real Strand Dialog steals
   the whole viewport; this frame hosts a dialog-style demo
   inline so docs can show the open state without replacing
   the page. Uses macOS-style traffic-light chrome with dot
   colors set inline per dot. */

.strand-ref-frame {
	background: var(--strand-surface-recessed);
	border-radius: var(--strand-radius-lg);
	overflow: hidden;
}

.strand-ref-frame__chrome {
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 10px 14px;
	background: var(--strand-surface-subtle);
	border-bottom: 1px solid var(--strand-gray-200);
}

.strand-ref-frame__dot {
	width: 10px;
	height: 10px;
	border-radius: 50%;
	display: inline-block;
	flex: none;
}

.strand-ref-frame__title {
	margin-left: var(--strand-space-3);
	font-family: var(--strand-font-mono);
	font-size: 11px;
	letter-spacing: 0.04em;
	color: var(--strand-gray-600);
}

.strand-ref-frame__body {
	position: relative;
	min-height: 280px;
	background: var(--strand-surface-elevated);
}

.strand-ref-frame__content {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--strand-space-5);
	padding: var(--strand-space-8) 36px;
	transition: filter 180ms var(--strand-ease-out-quart), opacity 180ms var(--strand-ease-out-quart);
}

.strand-ref-frame__content[aria-hidden="true"] {
	filter: blur(2px);
	opacity: 0.55;
	pointer-events: none;
}

.strand-ref-frame__content-head {
	/* container for overline + heading; no explicit styles */
}

.strand-ref-frame__actions {
	display: flex;
	gap: 10px;
}

.strand-ref-frame__overlay {
	position: absolute;
	inset: 0;
	background: rgba(15, 25, 42, 0.45);
	display: flex;
	align-items: center;
	justify-content: center;
	animation: strand-ref-frame-overlay-in 160ms var(--strand-ease-out-quart) forwards;
	padding: var(--strand-space-6);
}

@keyframes strand-ref-frame-overlay-in {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}

.strand-ref-frame__panel {
	background: var(--strand-surface-elevated);
	border-radius: var(--strand-radius-lg);
	box-shadow: 0 20px 50px rgba(15, 25, 42, 0.25), 0 2px 6px rgba(15, 25, 42, 0.12);
	width: min(440px, 100%);
	overflow: hidden;
	animation: strand-ref-frame-panel-in 180ms var(--strand-ease-out-expo) forwards;
	transform-origin: center;
}

@keyframes strand-ref-frame-panel-in {
	from {
		opacity: 0;
		transform: translateY(8px) scale(0.98);
	}
	to {
		opacity: 1;
		transform: translateY(0) scale(1);
	}
}

.strand-ref-frame__panel-header {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	padding: var(--strand-space-5) 22px 14px;
	gap: var(--strand-space-4);
}

.strand-ref-frame__panel-title {
	margin: 4px 0 0;
	font-family: var(--strand-font-sans);
	font-size: 18px;
	font-weight: var(--strand-weight-medium);
	line-height: 1.3;
	color: var(--strand-blue-midnight);
	letter-spacing: -0.01em;
}

.strand-ref-frame__panel-close {
	background: transparent;
	border: 0;
	width: 28px;
	height: 28px;
	border-radius: var(--strand-radius-md);
	display: grid;
	place-items: center;
	font-size: 18px;
	line-height: 1;
	color: var(--strand-gray-500);
	cursor: pointer;
	flex: none;
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart), color
		var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-ref-frame__panel-close:hover {
	background: var(--strand-surface-recessed);
	color: var(--strand-blue-midnight);
}

.strand-ref-frame__panel-close:focus-visible {
	outline: 2px solid var(--strand-blue-primary);
	outline-offset: 2px;
}

.strand-ref-frame__panel-body {
	padding: 0 22px var(--strand-space-5);
	font-size: 14px;
	line-height: 1.55;
	color: var(--strand-gray-700);
}

.strand-ref-frame__panel-footer {
	display: flex;
	justify-content: flex-end;
	gap: 10px;
	padding: var(--strand-space-4) 22px var(--strand-space-5);
	border-top: 1px solid var(--strand-gray-100);
	background: var(--strand-surface-recessed);
}

@media (prefers-reduced-motion: reduce) {
	.strand-ref-frame__overlay,
	.strand-ref-frame__panel {
		animation: none;
	}

	.strand-ref-frame__content {
		transition: none;
	}
}

/* LabGlassStage */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ══════════════════════════════════════════════════
   LAB GLASS STAGE (frosted-glass specimen container)
   ══════════════════════════════════════════════════
   A layered blue-gradient backdrop with an embedded glass
   panel on top, used by docs pages to demonstrate frosted
   glass surfaces against a cinematic backdrop. */

.strand-ref-glass-stage {
	position: relative;
	padding: var(--strand-space-10) var(--strand-space-8);
	border-radius: var(--strand-radius-lg);
	overflow: hidden;
	background: radial-gradient(circle at 20% 30%, rgba(59, 142, 246, 0.45) 0%, transparent 55%),
		radial-gradient(circle at 80% 70%, rgba(20, 184, 166, 0.35) 0%, transparent 55%),
		linear-gradient(135deg, var(--strand-blue-midnight) 0%, var(--strand-blue-abyss) 100%);
}

.strand-ref-glass-panel {
	position: relative;
	padding: 22px var(--strand-space-6);
	border-radius: var(--strand-radius-lg);
	background: rgba(15, 25, 42, 0.42);
	-webkit-backdrop-filter: blur(14px) saturate(1.2);
	backdrop-filter: blur(14px) saturate(1.2);
	border: 1px solid rgba(255, 255, 255, 0.16);
	box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.12);
	color: var(--strand-on-blue-primary);
}

/* LabRevealStage */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ══════════════════════════════════════════════════
   LAB REVEAL STAGE (staggered entry animation specimen)
   ══════════════════════════════════════════════════
   Demonstrates the Principle 7 alive-signal staggered reveal
   pattern. Four lines animate in at 0 / 180 / 360 / 540 ms.
   Consumers remount the stage (via a key/ref) to replay. */

.strand-ref-reveal-stage {
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding: var(--strand-space-5) 0;
}

.strand-ref-reveal-line {
	opacity: 0;
	transform: translateY(8px);
	animation: strand-ref-reveal-in 2400ms var(--strand-ease-out-quart) forwards;
	font-family: var(--strand-font-mono);
	font-size: 12px;
	letter-spacing: 0.04em;
	color: var(--strand-blue-midnight);
	padding: 10px 14px;
	background: var(--strand-surface-elevated);
	border-left: 2px solid var(--strand-blue-primary);
}

.strand-ref-reveal-line:nth-child(1) {
	animation-delay: 0ms;
}
.strand-ref-reveal-line:nth-child(2) {
	animation-delay: 180ms;
}
.strand-ref-reveal-line:nth-child(3) {
	animation-delay: 360ms;
}
.strand-ref-reveal-line:nth-child(4) {
	animation-delay: 540ms;
}

@keyframes strand-ref-reveal-in {
	0% {
		opacity: 0;
		transform: translateY(8px);
	}
	25% {
		opacity: 1;
		transform: translateY(0);
	}
	85% {
		opacity: 1;
		transform: translateY(0);
	}
	100% {
		opacity: 1;
		transform: translateY(0);
	}
}

@media (prefers-reduced-motion: reduce) {
	.strand-ref-reveal-line {
		animation: none;
		opacity: 1;
		transform: none;
	}
}

/* LabShell */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ══════════════════════════════════════════════════
   LAB SHELL / COMPONENT-REFERENCE PAGE CHROME
   ══════════════════════════════════════════════════
   A named primitive family for a component-reference page
   layout: sticky sidebar + scrollable main + header + section
   + example. Used by the Strand docs site itself, by any
   consumer building a component showcase, and by the DS Strand
   lab. 40+ classes map 1:1 to the handoff's showcase-* chrome.

   Computed styles match the baseline at viewport 1440 per
   `.strand-handoff-render/ledger/focused.json`.

   The sidebar sticky-top offset is set via the CSS custom
   property `--strand-ref-sticky-top` (default 0) so consumers
   can push the sidebar below fixed page chrome (e.g. a glass
   nav) without hardcoding any DS-specific value. Set on the
   shell root: `<div class="strand-ref-shell"
   style="--strand-ref-sticky-top: var(--strand-nav-height);">`.
   ══════════════════════════════════════════════════ */

/* Shell root: two-column grid, sidebar left, main right. */
.strand-ref-shell {
	display: grid;
	/* minmax(0, 1fr) on the main track, not a bare 1fr. A bare 1fr keeps an
     implicit min-content floor, so a wide child (a long heading, the metrics
     row) forces the track wider than the viewport. Paired with the shell's
     overflow-x clip below, that surplus is cut off on the right while the
     left gutter renders correctly: asymmetric clipping on narrow viewports.
     minmax(0, ...) lets the track shrink to the available width so content
     reflows instead. Boundary Integrity: the container owns its width. */
	grid-template-columns: 256px minmax(0, 1fr);
	min-height: 100vh;
}

/* Sidebar: sticky column. Height fills viewport from sticky-top to
   bottom so its bottom never falls below the viewport edge. dvh tracks
   mobile browser-chrome changes; vh fallback for older browsers. */
.strand-ref-shell__sidebar {
	position: sticky;
	top: var(--strand-ref-sticky-top, 0);
	height: calc(100vh - var(--strand-ref-sticky-top, 0px));
	height: calc(100dvh - var(--strand-ref-sticky-top, 0px));
	max-height: calc(100vh - var(--strand-ref-sticky-top, 0px));
	max-height: calc(100dvh - var(--strand-ref-sticky-top, 0px));
	background: var(--strand-surface-elevated);
	border-right: 1px solid var(--strand-gray-200);
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

/* Sidebar head: fixed top block with brand and hairline bottom. */
.strand-ref-shell__sidebar-head {
	padding: var(--strand-space-6) var(--strand-space-5) 18px;
	border-bottom: 1px solid var(--strand-gray-100);
	flex: none;
	background: var(--strand-surface-elevated);
}

/* Sidebar scroll region: groups of nav links, overflow-y auto. */
.strand-ref-shell__sidebar-scroll {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	overflow-anchor: none;
	overscroll-behavior: contain;
	padding: var(--strand-space-4) var(--strand-space-5) var(--strand-space-6);
	display: flex;
	flex-direction: column;
	gap: 18px;
}

/* Brand row: mark + title + sub stacked horizontally. */
.strand-ref-shell__brand {
	display: flex;
	align-items: center;
	gap: var(--strand-space-3);
}

.strand-ref-shell__brand-mark {
	width: 36px;
	height: 36px;
	border-radius: var(--strand-radius-md);
	background: var(--strand-blue-midnight);
	color: var(--strand-on-blue-primary);
	display: grid;
	place-items: center;
	font-family: var(--strand-font-mono);
	font-weight: var(--strand-weight-semibold);
	font-size: 13px;
	letter-spacing: 0.04em;
}

.strand-ref-shell__brand-title {
	font-family: var(--strand-font-mono);
	font-weight: var(--strand-weight-medium);
	color: var(--strand-blue-midnight);
	font-size: 15px;
}

.strand-ref-shell__brand-sub {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	text-transform: uppercase;
	letter-spacing: var(--strand-tracking-ultra);
	color: var(--strand-gray-500);
}

/* Sidebar nav group: divider between groups, label + list. */
.strand-ref-shell__group {
	display: flex;
	flex-direction: column;
	gap: var(--strand-space-1);
	padding-top: 10px;
	border-top: 1px solid var(--strand-gray-100);
}

.strand-ref-shell__group:first-of-type {
	border-top: 0;
	padding-top: 0;
}

.strand-ref-shell__group-label {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--strand-blue-midnight);
	font-weight: var(--strand-weight-semibold);
	padding: 6px 10px 8px;
}

.strand-ref-shell__group-list {
	display: flex;
	flex-direction: column;
	gap: 1px;
}

.strand-ref-shell__group-link {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 7px 10px;
	border-radius: var(--strand-radius-md);
	font-family: var(--strand-font-sans);
	font-size: 13px;
	color: var(--strand-gray-700);
	text-decoration: none;
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart), color
		var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-ref-shell__group-link:hover {
	background: var(--strand-surface-recessed);
	color: var(--strand-blue-midnight);
}

.strand-ref-shell__group-dot {
	width: 4px;
	height: 4px;
	border-radius: 50%;
	background: var(--strand-gray-300);
	flex: none;
}

.strand-ref-shell__group-link:hover .strand-ref-shell__group-dot {
	background: var(--strand-blue-primary);
}

/* Main: scrolling column, padding 56px 64px 96px, clamped width. */
.strand-ref-shell__main {
	padding: 56px var(--strand-space-16) 96px;
	max-width: 1120px;
	/* min-width: 0 lets this grid item shrink below its content's min-content
     width so content reflows within the track rather than overflowing the
     viewport and being clipped on the right (Boundary Integrity). */
	min-width: 0;
}

/* Header: h1 + lead + metrics row, bottom hairline. */
.strand-ref-header {
	padding-bottom: var(--strand-space-10);
	border-bottom: 1px solid var(--strand-gray-200);
	margin-bottom: var(--strand-space-8);
}

.strand-ref-header__title {
	font-family: var(--strand-font-sans);
	font-weight: var(--strand-weight-light);
	letter-spacing: var(--strand-tracking-tight);
	color: var(--strand-blue-midnight);
	font-size: 40px;
	margin: var(--strand-space-3) 0 0;
}

.strand-ref-header__lead {
	max-width: 620px;
	margin: var(--strand-space-4) 0 0;
	font-family: var(--strand-font-sans);
	font-size: 20px;
	color: var(--strand-gray-500);
	line-height: 1.5;
}

.strand-ref-header__meta {
	display: grid;
	grid-template-columns: repeat(4, minmax(0, auto));
	gap: 48px;
	margin: var(--strand-space-8) 0 0;
	justify-content: start;
}

.strand-ref-header__meta-item {
	display: flex;
	flex-direction: column;
	gap: var(--strand-space-2);
	margin: 0;
}

.strand-ref-header__meta-label {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--strand-gray-500);
	margin: 0;
}

.strand-ref-header__meta-value {
	font-family: var(--strand-font-sans);
	font-size: 22px;
	font-weight: var(--strand-weight-light);
	letter-spacing: -0.01em;
	color: var(--strand-blue-midnight);
	margin: 0;
	line-height: 1.2;
}

/* Taxonomy explainer block: overline + definition list of terms. */
.strand-ref-taxonomy {
	margin-top: var(--strand-space-6);
	padding: var(--strand-space-5) 22px;
	background: var(--strand-surface-recessed);
	border: 1px solid var(--strand-gray-200);
	border-radius: var(--strand-radius-lg);
	max-width: 720px;
}

.strand-ref-taxonomy__title {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--strand-blue-midnight);
	font-weight: var(--strand-weight-semibold);
	margin-bottom: 10px;
}

.strand-ref-taxonomy__list {
	margin: 0;
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--strand-space-2);
}

.strand-ref-taxonomy__list > div {
	display: grid;
	grid-template-columns: 160px 1fr;
	gap: 14px;
	align-items: baseline;
}

.strand-ref-taxonomy__list dt {
	font-family: var(--strand-font-mono);
	font-size: 11px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--strand-blue-primary);
	font-weight: var(--strand-weight-semibold);
	margin: 0;
}

.strand-ref-taxonomy__list dd {
	margin: 0;
	font-size: 13px;
	color: var(--strand-gray-700);
	line-height: 1.5;
}

.strand-ref-taxonomy__list em {
	font-style: italic;
	color: var(--strand-blue-midnight);
}

/* Section: vertical block with hairline bottom. */
.strand-ref-section {
	padding: var(--strand-space-12) 0;
	border-bottom: 1px solid var(--strand-gray-200);
	scroll-margin-top: var(--strand-space-4);
}

.strand-ref-section:last-child {
	border-bottom: 0;
}

.strand-ref-section__head {
	margin-bottom: var(--strand-space-8);
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--strand-space-6);
	flex-wrap: wrap;
}

.strand-ref-section__head h2 {
	margin: var(--strand-space-2) 0 0;
	font-weight: var(--strand-weight-light);
	letter-spacing: var(--strand-tracking-tight);
	color: var(--strand-blue-midnight);
	font-size: 28px;
}

.strand-ref-section__head-note {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	letter-spacing: var(--strand-tracking-ultra);
	text-transform: uppercase;
	color: var(--strand-gray-500);
}

.strand-ref-section__body {
	display: flex;
	flex-direction: column;
	gap: var(--strand-space-8);
}

/* Example: a single component specimen with meta column + demo panel.
   minmax(0, 1fr) + min-width: 0 on children: a code block or table inside
   the demo (or a consumer-composed wrapper) must not set a min-content
   floor wider than the viewport (Boundary Integrity; same regression
   class as the shell main track). */
.strand-ref-example {
	display: grid;
	grid-template-columns: 200px minmax(0, 1fr);
	gap: 28px;
	align-items: start;
}

.strand-ref-example > * {
	min-width: 0;
}

.strand-ref-example__meta {
	padding-top: 6px;
}

.strand-ref-example__label {
	font-family: var(--strand-font-sans);
	font-size: 13px;
	font-weight: var(--strand-weight-medium);
	color: var(--strand-blue-midnight);
	margin-bottom: 6px;
}

.strand-ref-example__code {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	letter-spacing: 0.04em;
	color: var(--strand-gray-500);
	display: block;
	line-height: 1.5;
}

.strand-ref-example__demo {
	background: var(--strand-surface-elevated);
	border: 1px solid var(--strand-gray-200);
	border-radius: var(--strand-radius-lg);
	/* 24px 28px: token 24px (space-6) + literal 28px chrome value (no token match) */
	padding: var(--strand-space-6) 28px;
	box-shadow: 0 1px 2px rgba(15, 23, 42, 0.03);
}

.strand-ref-example__demo--pad-none {
	padding: 0;
	overflow: hidden;
}

.strand-ref-example__demo--recessed {
	background: var(--strand-surface-recessed);
}

.strand-ref-example__caption {
	margin-top: var(--strand-space-2);
	font-size: 12px;
	line-height: 1.55;
	color: var(--strand-gray-500);
	max-width: 560px;
}

/* Mobile-trigger FAB: hidden by default; shown only inside the responsive
   breakpoint. Floats bottom-right and toggles the mobile drawer. The
   consumer pairs this with toggling `.is-nav-open` on the shell mount
   point. The button itself uses standard strand-btn primitives plus
   this class for FAB positioning. */
.strand-ref-mobile-trigger {
	display: none;
}

.strand-ref-shell__nav-scrim {
	display: none;
}

/* Responsive: at <=1040px the sidebar becomes a slide-over drawer.
   Consumer toggles `.is-nav-open` on the shell mount point to open the
   drawer. The trigger button (.strand-ref-mobile-trigger) sits as a FAB
   bottom-right, and the scrim (.strand-ref-shell__nav-scrim) closes it
   on tap. */
@media (max-width: 1040px) {
	.strand-ref-shell {
		/* Single column at mobile, but still minmax(0, ...) so the lone track can
       shrink below its content's min-content width (see the base rule). */
		grid-template-columns: minmax(0, 1fr);
	}

	.strand-ref-shell__sidebar {
		position: fixed;
		top: var(--strand-ref-sticky-top, 0);
		left: 0;
		right: 0;
		bottom: 0;
		height: auto;
		max-height: none;
		width: 100%;
		z-index: 80;
		background: var(--strand-surface-elevated);
		box-shadow: var(--strand-elevation-3, 0 8px 28px rgba(15, 23, 42, 0.18));
		transform: translateX(-100%);
		transition: transform var(--strand-duration-normal, 200ms)
			var(--strand-ease-out-quart, ease-out);
		visibility: hidden;
		border-right: 0;
		border-bottom: 0;
	}

	.strand-ref-shell.is-nav-open .strand-ref-shell__sidebar {
		transform: translateX(0);
		visibility: visible;
	}

	.strand-ref-shell__nav-scrim {
		position: fixed;
		top: var(--strand-ref-sticky-top, 0);
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 75;
		background: rgba(15, 23, 42, 0.4);
	}

	.strand-ref-shell.is-nav-open .strand-ref-shell__nav-scrim {
		display: block;
	}

	.strand-ref-mobile-trigger {
		display: inline-flex;
		position: fixed;
		bottom: calc(env(safe-area-inset-bottom, 0px) + 16px);
		right: 16px;
		z-index: 85;
		box-shadow: var(--strand-elevation-3, 0 8px 24px rgba(15, 23, 42, 0.2));
		border-radius: var(--strand-radius-full, 9999px);
	}

	.strand-ref-shell__main {
		padding: var(--strand-space-8) var(--strand-space-6) var(--strand-space-16);
	}

	.strand-ref-example {
		grid-template-columns: minmax(0, 1fr);
	}

	.strand-ref-header__meta {
		grid-template-columns: 1fr 1fr;
		gap: var(--strand-space-6) var(--strand-space-8);
	}
}

@media (max-width: 560px) {
	.strand-ref-header__meta {
		grid-template-columns: 1fr;
		gap: 18px;
	}
}

/* LabTip */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ══════════════════════════════════════════════════
   LAB TIP (pure-CSS tooltip specimen)
   ══════════════════════════════════════════════════
   A hover/focus-driven tooltip for QA specimens on docs
   pages. This is NOT the production Strand Tooltip (which is
   JS-driven with collision detection); it is a static
   specimen showing four fixed positions simultaneously.

   Use the --pinned modifier to show the bubble without
   hover/focus (useful for before/after screenshots). */

.strand-ref-tip {
	position: relative;
	display: inline-flex;
}

.strand-ref-tip__bubble {
	position: absolute;
	z-index: 2;
	padding: 6px 10px;
	border-radius: var(--strand-radius-sm);
	background: var(--strand-blue-midnight);
	color: var(--strand-on-blue-primary);
	font-family: var(--strand-font-mono);
	font-size: 11px;
	letter-spacing: 0.04em;
	white-space: nowrap;
	opacity: 0;
	pointer-events: none;
	transition: opacity var(--strand-duration-fast) var(--strand-ease-out-quart), transform
		var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-ref-tip:hover .strand-ref-tip__bubble,
.strand-ref-tip:focus-within .strand-ref-tip__bubble,
.strand-ref-tip--pinned .strand-ref-tip__bubble {
	opacity: 1;
}

.strand-ref-tip__bubble--top {
	left: 50%;
	bottom: calc(100% + 8px);
	transform: translateX(-50%) translateY(4px);
}

.strand-ref-tip__bubble--bottom {
	left: 50%;
	top: calc(100% + 8px);
	transform: translateX(-50%) translateY(-4px);
}

.strand-ref-tip__bubble--left {
	right: calc(100% + 8px);
	top: 50%;
	transform: translateX(4px) translateY(-50%);
}

.strand-ref-tip__bubble--right {
	left: calc(100% + 8px);
	top: 50%;
	transform: translateX(-4px) translateY(-50%);
}

.strand-ref-tip:hover .strand-ref-tip__bubble--top,
.strand-ref-tip--pinned .strand-ref-tip__bubble--top,
.strand-ref-tip:focus-within .strand-ref-tip__bubble--top {
	transform: translateX(-50%) translateY(0);
}

.strand-ref-tip:hover .strand-ref-tip__bubble--bottom,
.strand-ref-tip--pinned .strand-ref-tip__bubble--bottom,
.strand-ref-tip:focus-within .strand-ref-tip__bubble--bottom {
	transform: translateX(-50%) translateY(0);
}

.strand-ref-tip:hover .strand-ref-tip__bubble--left,
.strand-ref-tip--pinned .strand-ref-tip__bubble--left,
.strand-ref-tip:focus-within .strand-ref-tip__bubble--left {
	transform: translateX(0) translateY(-50%);
}

.strand-ref-tip:hover .strand-ref-tip__bubble--right,
.strand-ref-tip--pinned .strand-ref-tip__bubble--right,
.strand-ref-tip:focus-within .strand-ref-tip__bubble--right {
	transform: translateX(0) translateY(-50%);
}

@media (prefers-reduced-motion: reduce) {
	.strand-ref-tip__bubble {
		transition: none;
	}
}

/* LabUtilCell */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ══════════════════════════════════════════════════
   LAB UTIL CELL (utility-class demo tile)
   ══════════════════════════════════════════════════
   Demo tile that shows a single utility class in action
   (e.g. .strand-mt-4, .strand-mx-auto). The row container
   arranges cells horizontally; each cell has a code label
   + demo area + optional caption. */

.strand-ref-util-row {
	display: flex;
	align-items: stretch;
	gap: 18px;
	flex-wrap: wrap;
}

.strand-ref-util-cell {
	display: flex;
	flex-direction: column;
	gap: 10px;
	/* 14px 16px: specimen chrome literal, no token match for 14px */
	padding: 14px var(--strand-space-4);
	background: var(--strand-surface-recessed);
	border: 1px solid var(--strand-gray-200);
	border-radius: var(--strand-radius-md);
	min-width: 180px;
	flex: 1;
}

.strand-ref-util-cell__code {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	letter-spacing: 0.06em;
	color: var(--strand-blue-primary);
	font-weight: var(--strand-weight-medium);
}

.strand-ref-util-cell__caption {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	letter-spacing: 0.04em;
	color: var(--strand-gray-500);
}

.strand-ref-util-cell__demo {
	background: var(--strand-surface-elevated);
	border: 1px dashed var(--strand-gray-300);
	border-radius: var(--strand-radius-sm);
	padding: 0;
	display: flex;
	flex-direction: column;
}

.strand-ref-util-cell__block {
	background: var(--strand-blue-midnight);
	color: var(--strand-on-blue-primary);
	padding: 6px 10px;
	font-family: var(--strand-font-mono);
	font-size: 11px;
	letter-spacing: 0.04em;
	display: inline-block;
	align-self: flex-start;
}

/* Link */
/*! Strand UI | MIT License | dillingerstaffing.com */

.strand-link {
	color: var(--strand-blue-primary);
	text-decoration: none;
	font-family: var(--strand-font-sans);
	background-image: linear-gradient(currentColor, currentColor);
	background-position: 0% 100%;
	background-repeat: no-repeat;
	background-size: 0% 1px;
	transition: background-size var(--strand-duration-normal) var(--strand-ease-out-expo);
	cursor: pointer;
}

.strand-link:hover {
	background-size: 100% 1px;
}

/* ── Focus ring ── */
.strand-link:focus-visible {
	outline: 2px solid var(--strand-blue-primary);
	outline-offset: 2px;
}

/* ── CTA link variant ── */
.strand-link--cta {
	display: inline-flex;
	align-items: center;
	min-height: var(--strand-touch-target);
	padding-block: var(--strand-space-2);
	font-size: var(--strand-text-sm);
}

.strand-link--cta:hover {
	color: var(--strand-blue-vivid);
}

/* ── Monospace link variant (footer, overline-style) ── */
.strand-link--mono {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	letter-spacing: var(--strand-tracking-wider);
	color: var(--strand-gray-400);
	background-image: none;
}

.strand-link--mono:hover {
	color: var(--strand-blue-primary);
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-link {
		transition: none;
	}
}

/* Nav */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Nav bar ── */
.strand-nav {
	position: relative;
	width: 100%;
	height: var(--strand-nav-height);
	background: var(--strand-surface-elevated);
	border-bottom: 1px solid var(--strand-gray-200);
	font-family: var(--strand-font-sans);
}

/* ── Inner layout ──
   min-height: inherit picks up the nav's own min-height (the
   --strand-nav-height token). On mobile the nav switches to
   height: auto + min-height: var(--strand-nav-height); without this
   inherit the inner only takes its content height (~40px from the
   hamburger), and the nav's extra ~24px appears as empty space below,
   which reads as "logo squished upward". With min-height: inherit the
   inner is always at least token-tall and align-items: center
   distributes content vertically through that full height. */
.strand-nav__inner {
	display: flex;
	align-items: center;
	height: 100%;
	min-height: inherit;
	padding: 0 var(--strand-space-6);
	max-width: 1280px;
	margin: 0 auto;
}

/* ── Logo ──
   DL Part IV.5 brand identifier: mono uppercase wide letter-spacing,
   semibold weight, blue-midnight color. The logo is the only typographic
   identity on the page, so it carries the brand even at this small size. */
.strand-nav__logo {
	flex-shrink: 0;
	display: inline-flex;
	align-items: center;
	margin-right: var(--strand-space-8);
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-sm);
	font-weight: var(--strand-weight-semibold);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	color: var(--strand-blue-midnight);
	text-decoration: none;
	position: relative;
}

/* ── Logo with animated pulse underline (DL Principle 7: alive signal) ──
   A 2px blue underline that pulses in slow rhythm. Use on the brand
   identifier for the active site to signal "this is the live system." */
.strand-nav__logo--pulse::after {
	content: "";
	position: absolute;
	bottom: -4px;
	left: 0;
	right: 0;
	height: 2px;
	background: var(--strand-blue-primary);
	border-radius: var(--strand-radius-sm);
	animation: strand-nav-logo-pulse 2s var(--strand-ease-in-out-sine) infinite;
}

@keyframes strand-nav-logo-pulse {
	0%,
	100% {
		opacity: 1;
	}
	50% {
		opacity: 0.4;
	}
}

@media (prefers-reduced-motion: reduce) {
	.strand-nav__logo--pulse::after {
		animation: none;
		opacity: 0.6;
	}
}

/* ── Desktop items ──
   Pushed to the right edge so logo and primary nav have visual breathing
   room. The old (pre-v0.5) "flex: 1" caused items to cluster against the
   logo, leaving the right edge empty. margin-left auto restores the
   space-between layout most navbars expect. */
.strand-nav__items {
	display: flex;
	align-items: center;
	gap: var(--strand-space-6);
	margin-left: auto;
	list-style: none;
}

/* ── Nav slot ──
   A content slot that lives in the nav. Typical uses: an account/auth
   affordance, a utility button, a search field. Position behavior is
   context-aware:
   - If the slot follows .strand-nav__items (which already absorbs free
     space via margin-left: auto), the slot sits 24px to its right so
     it does not visually crash into the last nav link.
   - If the slot stands alone (no items list before it, as on simpler
     nav bars), the slot pushes itself to the right edge with
     margin-left: auto.
   This keeps a single class usable across both long and short navs
   without modifiers. */
.strand-nav__slot {
	display: flex;
	align-items: center;
	margin-left: auto;
}

.strand-nav__items + .strand-nav__slot {
	margin-left: var(--strand-space-8);
}

/* ── Reserved slot ──
   A slot whose contents swap identity at runtime carries a hidden cost.
   An auth affordance is the common case: signed out it is one short
   button, signed in it is an avatar plus a sign-out button and often a
   profile link, and the two states are nowhere near the same width. When
   the swap happens the slot resizes, which moves the slot's own box and
   redistributes the free space every auto-margined sibling in the nav is
   sharing. The result is a header that visibly jumps on load, on sign-in,
   and on sign-out, on a surface that is otherwise completely static.

   --reserve pins the slot's inline size so the swap cannot change it.
   Neither the slot nor its siblings move. Contents align to the end so
   the affordance stays pinned to the nav's right edge at every width and
   in every state.

   Set --strand-nav-slot-reserve to the widest state you actually render.
   The defaults fit an avatar plus two short controls, and drop at the
   mobile breakpoint where the nav has far less room to give. Reserving
   less than the widest state reintroduces the shift; reserving more only
   costs empty space. */
.strand-nav__slot--reserve {
	min-width: var(--strand-nav-slot-reserve, 14rem);
	justify-content: flex-end;
}

/* ── Links ── */
.strand-nav__link {
	color: var(--strand-gray-600);
	text-decoration: none;
	font-size: var(--strand-text-sm);
	font-weight: var(--strand-weight-medium);
	transition: color var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-nav__link:hover {
	color: var(--strand-gray-900);
}

.strand-nav__link:focus-visible {
	outline: 2px solid var(--strand-blue-primary);
	outline-offset: 2px;
}

.strand-nav__link--active {
	color: var(--strand-blue-primary);
	font-weight: var(--strand-weight-medium);
}

/* ── Actions ── */
.strand-nav__actions {
	display: flex;
	align-items: center;
	gap: var(--strand-space-3);
	margin-left: auto;
}

/* ── Hamburger ── */
.strand-nav__hamburger {
	display: none;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	margin-left: auto;
	padding: 0;
	border: none;
	border-radius: var(--strand-radius-md);
	background: transparent;
	color: var(--strand-gray-600);
	cursor: pointer;
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-nav__hamburger:hover {
	background: var(--strand-gray-200);
}

.strand-nav__hamburger:focus-visible {
	outline: 2px solid var(--strand-blue-primary);
	outline-offset: 2px;
}

.strand-nav__hamburger-icon {
	display: block;
	width: 20px;
	height: 2px;
	background: currentColor;
	position: relative;
}

.strand-nav__hamburger-icon::before,
.strand-nav__hamburger-icon::after {
	content: "";
	position: absolute;
	left: 0;
	width: 100%;
	height: 2px;
	background: currentColor;
}

.strand-nav__hamburger-icon::before {
	top: -6px;
}

.strand-nav__hamburger-icon::after {
	top: 6px;
}

/* ── Mobile menu ──
   Docks immediately below its parent .strand-nav as an overlay (the
   parent has position: relative, so absolute positions against it).
   For .strand-nav--glass the parent is position: fixed, so the menu
   still anchors right under the fixed bar at viewport top. For inline
   specimens or any non-glass placement the menu drops below the nav
   in flow rather than jumping to viewport top. Hidden by default;
   show via JS by adding strand-nav__mobile-menu--open. */
.strand-nav__mobile-menu {
	display: none;
	position: absolute;
	top: 100%;
	left: 0;
	right: 0;
	flex-direction: column;
	width: 100%;
	padding: var(--strand-space-4) var(--strand-space-6);
	background: var(--strand-surface-elevated);
	border-bottom: 1px solid var(--strand-gray-200);
	box-shadow: var(--strand-elevation-2);
	z-index: 99;
}

.strand-nav__mobile-menu--open {
	display: flex;
}

.strand-nav__mobile-link {
	display: block;
	padding: var(--strand-space-3) 0;
	color: var(--strand-gray-600);
	text-decoration: none;
	font-size: var(--strand-text-sm);
	font-weight: var(--strand-weight-medium);
	transition: color var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-nav__mobile-link:hover {
	color: var(--strand-gray-900);
}

.strand-nav__mobile-link--active {
	color: var(--strand-blue-primary);
	font-weight: var(--strand-weight-semibold);
}

/* ── Responsive ──
   At mobile widths the desktop items + actions hide and the
   hamburger button shows. The mobile menu stays display:none until
   JS toggles strand-nav__mobile-menu--open on click. */
@media (max-width: 767px) {
	.strand-nav__items {
		display: none;
	}

	.strand-nav__actions {
		display: none;
	}

	/* The slot remains visible on mobile because it typically carries the
     account affordance (avatar + sign-in/out) which should stay reachable
     from every viewport. Its margin-left collapses on mobile because
     the only remaining siblings are the logo (left) and the hamburger
     (right), and margin-left:auto on one of them handles the layout. */
	.strand-nav__slot {
		margin-left: auto;
	}

	/* A reserved slot drops to a tighter default here. The desktop
	   reservation would crowd the logo and any back affordance at phone
	   widths, and the mobile nav hides .strand-nav__actions anyway, so the
	   slot's widest state is correspondingly smaller. */
	.strand-nav__slot--reserve {
		min-width: var(--strand-nav-slot-reserve-sm, 11.75rem);
	}

	.strand-nav__hamburger {
		display: inline-flex;
	}

	.strand-nav {
		height: auto;
		min-height: var(--strand-nav-height);
	}
}

/* ── Glassmorphic variant (DL 11.5: "white or glassmorphic") ──
   The glass nav's rendered height is the token. Previous versions set
   `height: auto; padding: var(--strand-space-4) 0` which produced a
   ~77px render (token is 64px), so `body:has(.strand-nav--glass) {
   padding-top: var(--strand-nav-height) }` left content behind the nav.
   Pinning height to the token keeps the body offset rule honest across
   every consumer. */
.strand-nav--glass {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: 100;
	height: var(--strand-nav-height);
	background: var(--strand-glass-bg);
	-webkit-backdrop-filter: blur(var(--strand-glass-blur));
	backdrop-filter: blur(var(--strand-glass-blur));
	border-bottom: 1px solid var(--strand-glass-border);
}

/* ── Scrolled state (DL Part XIX.1) ──
   Apply via JS scroll listener. Adds shadow + blue-shifted border. */
.strand-nav--scrolled {
	box-shadow: var(--strand-elevation-1);
	border-bottom-color: var(--strand-nav-scrolled-border);
}

.strand-nav--glass {
	transition: box-shadow var(--strand-duration-normal) ease, border-color
		var(--strand-duration-normal) ease;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-nav__link,
	.strand-nav__mobile-link,
	.strand-nav__hamburger,
	.strand-nav--glass {
		transition: none;
	}
}

/* Progress */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Bar base ── */
.strand-progress--bar {
	width: 100%;
	background: var(--strand-gray-200);
	border-radius: var(--strand-radius-full);
	overflow: hidden;
}

/* ── Bar sizes ── */
.strand-progress--bar.strand-progress--sm {
	height: 4px;
}

.strand-progress--bar.strand-progress--md {
	height: 8px;
}

.strand-progress--bar.strand-progress--lg {
	height: 12px;
}

/* ── Bar fill ── */
.strand-progress--bar .strand-progress__fill {
	height: 100%;
	background: var(--strand-blue-primary);
	border-radius: var(--strand-radius-full);
	transition: width var(--strand-duration-normal) var(--strand-ease-out-quart);
}

/* ── Bar indeterminate ── */
.strand-progress--bar.strand-progress--indeterminate .strand-progress__fill {
	width: 40%;
	animation: strand-progress-shimmer var(--strand-duration-cinematic) var(--strand-ease-in-out-sine)
		infinite;
}

@keyframes strand-progress-shimmer {
	0% {
		transform: translateX(-100%);
	}
	100% {
		transform: translateX(350%);
	}
}

/* ── Ring base ── */
.strand-progress--ring {
	display: inline-flex;
	align-items: center;
	justify-content: center;
}

/* ── Ring track and fill ── */
.strand-progress__track {
	stroke: var(--strand-gray-200);
}

.strand-progress__fill {
	stroke: var(--strand-blue-primary);
}

.strand-progress--ring .strand-progress__ring {
	transition: stroke-dashoffset var(--strand-duration-normal) var(--strand-ease-out-quart);
}

/* ── Ring indeterminate ── */
.strand-progress--ring.strand-progress--indeterminate .strand-progress__ring {
	animation: strand-progress-rotate 1.2s linear infinite;
}

@keyframes strand-progress-rotate {
	to {
		transform: rotate(360deg);
	}
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-progress--bar .strand-progress__fill {
		transition: none;
	}

	.strand-progress--bar.strand-progress--indeterminate .strand-progress__fill {
		animation: none;
		width: 100%;
		opacity: 0.4;
	}

	.strand-progress--ring.strand-progress--indeterminate .strand-progress__ring {
		animation: none;
	}
}

/* Radio */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-radio {
	display: inline-flex;
	align-items: center;
	gap: var(--strand-space-2);
	min-height: var(--strand-touch-target);
	cursor: pointer;
	user-select: none;
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-sm);
	color: var(--strand-gray-900);
	line-height: var(--strand-leading-snug);
}

/* ── Hidden native input ── */
.strand-radio__native {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* ── Custom visual ── */
.strand-radio__control {
	display: flex;
	align-items: center;
	justify-content: center;
	width: var(--strand-control-size);
	height: var(--strand-control-size);
	border: 1px solid var(--strand-gray-200);
	border-radius: var(--strand-radius-full);
	background: var(--strand-surface-elevated);
	flex-shrink: 0;
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart), border-color
		var(--strand-duration-fast) var(--strand-ease-out-quart), box-shadow var(--strand-duration-fast)
		var(--strand-ease-out-quart);
}

.strand-radio__dot {
	width: 8px;
	height: 8px;
	border-radius: var(--strand-radius-full);
	background: var(--strand-on-blue-primary);
	transform: scale(0);
	transition: transform var(--strand-duration-fast) var(--strand-ease-out-expo);
}

/* ── Focus ring ── */
.strand-radio__native:focus-visible ~ .strand-radio__control {
	border-color: var(--strand-blue-primary);
	box-shadow: var(--strand-focus-ring);
}

/* ── Checked ── */
.strand-radio--checked .strand-radio__control {
	background: var(--strand-blue-primary);
	border-color: var(--strand-blue-primary);
}

.strand-radio--checked .strand-radio__dot {
	transform: scale(1);
}

/* ── Hover ── */
.strand-radio:hover:not(.strand-radio--disabled) .strand-radio__control {
	border-color: var(--strand-blue-indicator);
}

.strand-radio--checked:hover:not(.strand-radio--disabled) .strand-radio__control {
	background: var(--strand-blue-vivid);
	border-color: var(--strand-blue-vivid);
}

/* ── Label ── */
.strand-radio__label {
	color: var(--strand-gray-900);
}

/* ── Disabled ── */
.strand-radio--disabled {
	opacity: 0.4;
	cursor: not-allowed;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-radio__control,
	.strand-radio__dot {
		transition: none;
	}
}

/* ScrollReveal */
/*! Strand UI | MIT License | dillingerstaffing.com */

.strand-reveal {
	opacity: 0;
	transform: translateY(24px);
	transition: opacity var(--strand-duration-glacial) var(--strand-ease-out-expo), transform
		var(--strand-duration-glacial) var(--strand-ease-out-expo);
}

.strand-reveal--visible {
	opacity: 1;
	transform: translateY(0);
}

/* Stagger children */
.strand-reveal-group > .strand-reveal:nth-child(1) {
	transition-delay: 0ms;
}
.strand-reveal-group > .strand-reveal:nth-child(2) {
	transition-delay: var(--strand-stagger-delay);
}
.strand-reveal-group > .strand-reveal:nth-child(3) {
	transition-delay: calc(var(--strand-stagger-delay) * 2);
}
.strand-reveal-group > .strand-reveal:nth-child(4) {
	transition-delay: calc(var(--strand-stagger-delay) * 3);
}
.strand-reveal-group > .strand-reveal:nth-child(5) {
	transition-delay: calc(var(--strand-stagger-delay) * 4);
}
.strand-reveal-group > .strand-reveal:nth-child(6) {
	transition-delay: var(--strand-duration-slow);
}

/* ── CSS-only scroll-driven animation (no JS required) ── */
@supports (animation-timeline: view()) {
	.strand-reveal {
		opacity: 0;
		transform: translateY(24px);
		transition: none;
		animation: strand-reveal-up linear both;
		animation-timeline: view();
		animation-range: entry 0% entry 100%;
	}

	.strand-reveal-group > .strand-reveal {
		animation: strand-reveal-up linear both;
		animation-timeline: view();
		animation-range: entry 0% entry 100%;
	}

	@keyframes strand-reveal-up {
		from {
			opacity: 0;
			transform: translateY(24px);
		}
		to {
			opacity: 1;
			transform: translateY(0);
		}
	}
}

/* Manual / programmatic reveal. Opts out of the scroll-driven view-timeline so a
   consumer can drive the reveal by toggling .strand-reveal--visible: replay on
   click, reveal on mount, reveal on state change. Uses the same transition as the
   no-JS fallback, so it stays replayable even where animation-timeline is supported.
   Add .strand-reveal--manual per element, or .strand-reveal-group--manual on the group. */
.strand-reveal--manual,
.strand-reveal-group--manual > .strand-reveal {
	animation: none;
	animation-timeline: auto;
	transition: opacity var(--strand-duration-glacial) var(--strand-ease-out-expo), transform
		var(--strand-duration-glacial) var(--strand-ease-out-expo);
}

/* A manual reveal toggled visible must win over the @supports view-timeline
   base rule above, which re-declares opacity:0 / transform on .strand-reveal at
   equal specificity but later in source order. With the animation removed (above)
   nothing else drives opacity, so without this compound (higher-specificity)
   rule the element stays at opacity 0 and toggling --visible has no effect:
   the reveal never appears and replay does nothing. */
.strand-reveal--manual.strand-reveal--visible,
.strand-reveal-group--manual > .strand-reveal--visible {
	opacity: 1;
	transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
	.strand-reveal {
		opacity: 1;
		transform: none;
		transition: none;
		animation: none;
	}
}

/* Section */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-section {
	width: 100%;
}

/* ── Variants ── */
.strand-section--standard {
	padding-block: clamp(4rem, 8vw, 8rem);
}

.strand-section--hero {
	padding-block: clamp(6rem, 12vw, 12rem);
	text-align: center;
	position: relative;
	overflow: hidden;
}

/* ── Compact hero ──
   A tighter hero variant for pages where a secondary CTA (install tabs,
   pricing, signup) sits directly below the hero and must be at least
   partially visible in the default viewport on desktop. Keeps the hero
   treatment (center text, positioning, overflow handling) but trims the
   vertical padding so the fold sits later in the page. */
.strand-section--hero-compact {
	padding-block: clamp(3.5rem, 6vw, 6rem);
	text-align: center;
	position: relative;
	overflow: hidden;
}

.strand-section--hero-xl {
	padding-block: clamp(8rem, 15vw, 14rem);
	text-align: center;
	position: relative;
	overflow: hidden;
}

/* ── Backgrounds ── */
.strand-section--bg-primary {
	background-color: var(--strand-surface-primary);
}

.strand-section--bg-elevated {
	background-color: var(--strand-surface-elevated);
}

.strand-section--bg-recessed {
	background-color: var(--strand-surface-recessed);
}

/* ── Compact variant ── */
.strand-section--compact {
	padding-block: var(--strand-space-12);
}

/* ── Border variant ── */
.strand-section--border-top {
	border-top: 1px solid var(--strand-gray-200);
}

/* ── Scroll target (anchored section with nav offset) ──
   Global scroll-padding-top on html already offsets anchor scrolls
   by the nav+banner stack height (see packages/tokens/css/base.css).
   This modifier is retained as an explicit per-section opt-in for
   consumers who want to reinforce the contract locally, and it
   tracks the same tokens so a nav-height change propagates without
   hunting. */
.strand-section--scroll-target {
	scroll-margin-top: calc(var(--strand-nav-height) + var(--strand-banner-height, 0px));
}

/* Select */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-select {
	position: relative;
	display: inline-flex;
	align-items: center;
	background: var(--strand-surface-elevated);
	border: 1px solid var(--strand-gray-200);
	border-radius: var(--strand-radius-md);
	transition: border-color var(--strand-duration-fast) var(--strand-ease-out-quart), box-shadow
		var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-select:focus-within {
	border-color: var(--strand-blue-primary);
	box-shadow: var(--strand-focus-ring);
}

/* ── Field ── */
.strand-select__field {
	flex: 1;
	width: 100%;
	padding: var(--strand-space-3) var(--strand-space-8) var(--strand-space-3) var(--strand-space-4);
	background: transparent;
	border: none;
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-base);
	color: var(--strand-gray-900);
	outline: none;
	appearance: none;
	cursor: pointer;
}

/* ── Arrow indicator (auto via ::after, no markup needed) ──
   Renders a chevron-down on the right edge of every strand-select
   wrapper, sized to align with the field text. Consumers can also
   place an explicit <span class="strand-select__arrow"> for control. */
.strand-select::after {
	content: "";
	position: absolute;
	right: var(--strand-space-3);
	top: 50%;
	width: 0;
	height: 0;
	margin-top: -2px;
	border-left: 5px solid transparent;
	border-right: 5px solid transparent;
	border-top: 5px solid var(--strand-gray-500);
	pointer-events: none;
}

.strand-select__arrow {
	position: absolute;
	right: var(--strand-space-3);
	top: 50%;
	transform: translateY(-50%);
	width: 0;
	height: 0;
	border-left: 5px solid transparent;
	border-right: 5px solid transparent;
	border-top: 5px solid var(--strand-gray-500);
	pointer-events: none;
}

/* ── Error ── */
.strand-select--error {
	border-color: var(--strand-red-alert);
}

.strand-select--error:focus-within {
	border-color: var(--strand-red-alert);
	box-shadow: var(--strand-focus-ring-error);
}

/* ── Disabled ── */
.strand-select--disabled {
	opacity: 0.4;
	cursor: not-allowed;
}

.strand-select--disabled .strand-select__field {
	cursor: not-allowed;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-select {
		transition: none;
	}
}

/* Skeleton */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-skeleton {
	display: block;
	background: var(--strand-gray-100);
}

/* ── Shimmer ── */
.strand-skeleton--shimmer {
	background: linear-gradient(
		90deg,
		var(--strand-gray-100) 25%,
		var(--strand-gray-50) 50%,
		var(--strand-gray-100) 75%
	);
	background-size: 200% 100%;
	animation: strand-skeleton-shimmer 1.8s var(--strand-ease-in-out-sine) infinite;
}

@keyframes strand-skeleton-shimmer {
	0% {
		background-position: 200% 0;
	}
	100% {
		background-position: -200% 0;
	}
}

/* ── Text variant ── */
.strand-skeleton--text {
	height: 1em;
	border-radius: var(--strand-radius-sm);
}

/* ── Rectangle variant ── */
.strand-skeleton--rectangle {
	border-radius: var(--strand-radius-md);
}

/* ── Circle variant ── */
.strand-skeleton--circle {
	border-radius: var(--strand-radius-full);
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-skeleton--shimmer {
		animation: none;
		background: var(--strand-gray-100);
	}
}

/* Slider */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-slider {
	position: relative;
	display: flex;
	align-items: center;
	width: 100%;
}

/* ── Field ── */
.strand-slider__field {
	/* The field itself is a 44px-tall transparent hit area so the
     full WCAG 2.5.5 touch target is draggable. The visible track
     is painted via the ::before-equivalent (we cannot use
     pseudo-elements on form controls, so the track tint is set
     directly on the field with a small height plus a vertical
     gradient that fades the bottom). */
	width: 100%;
	min-height: var(--strand-touch-target);
	height: var(--strand-touch-target);
	appearance: none;
	background: transparent;
	outline: none;
	cursor: pointer;
}

/* ── Track: Webkit ── */
.strand-slider__field::-webkit-slider-runnable-track {
	height: 6px;
	background: var(--strand-gray-200);
	border-radius: var(--strand-radius-full);
}

/* ── Thumb: Webkit ──
   The earlier approach of width: 20px + border: 12px solid transparent +
   background-clip: padding-box collapses to a hollow ring in webkit
   because the slider thumb pseudo-element does not honor the
   padding-box clip the way regular elements do. The painted box
   shows only the (currently empty) padding region while the
   transparent border consumes the visible area. The fix is to use
   a flat 20x20 thumb with NO transparent border. The 44px hit area
   is provided by the field height above, not by an oversized
   thumb. */
.strand-slider__field::-webkit-slider-thumb {
	appearance: none;
	width: 20px;
	height: 20px;
	/* Vertically center the thumb on the 6px track inside the
     44px field: (44 - 20) / 2 = 12 from the top, but the track is
     also vertically centered, so the thumb sits on the track when
     margin-top equals (track-height - thumb-height) / 2 = -7px. */
	margin-top: -7px;
	background: var(--strand-blue-primary);
	border: none;
	border-radius: var(--strand-radius-full);
	cursor: pointer;
	box-shadow: var(--strand-elevation-1);
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart), transform
		var(--strand-duration-fast) var(--strand-ease-out-expo);
}

.strand-slider__field:hover:not(:disabled)::-webkit-slider-thumb {
	background: var(--strand-blue-vivid);
	transform: scale(1.15);
}

.strand-slider__field:active:not(:disabled)::-webkit-slider-thumb {
	background: var(--strand-blue-deep);
	transform: scale(1.05);
}

/* ── Thumb: Firefox ──
   Firefox does not need the manual margin offset because
   ::-moz-range-thumb is centered on the track automatically. */
.strand-slider__field::-moz-range-thumb {
	width: 20px;
	height: 20px;
	background: var(--strand-blue-primary);
	border: none;
	border-radius: var(--strand-radius-full);
	cursor: pointer;
	box-shadow: var(--strand-elevation-1);
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart), transform
		var(--strand-duration-fast) var(--strand-ease-out-expo);
}

.strand-slider__field:hover:not(:disabled)::-moz-range-thumb {
	background: var(--strand-blue-vivid);
	transform: scale(1.15);
}

.strand-slider__field:active:not(:disabled)::-moz-range-thumb {
	background: var(--strand-blue-deep);
	transform: scale(1.05);
}

/* ── Track: Firefox ── */
.strand-slider__field::-moz-range-track {
	height: 6px;
	background: var(--strand-gray-200);
	border-radius: var(--strand-radius-full);
}

/* ── Focus ── */
.strand-slider__field:focus-visible::-webkit-slider-thumb {
	box-shadow: var(--strand-focus-ring);
}

.strand-slider__field:focus-visible::-moz-range-thumb {
	box-shadow: var(--strand-focus-ring);
}

/* ── Disabled ── */
.strand-slider--disabled {
	opacity: 0.4;
	cursor: not-allowed;
}

.strand-slider--disabled .strand-slider__field {
	cursor: not-allowed;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-slider__field::-webkit-slider-thumb {
		transition: none;
	}

	.strand-slider__field::-moz-range-thumb {
		transition: none;
	}
}

/* Spinner */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-spinner {
	display: inline-flex;
	align-items: center;
	justify-content: center;
}

/* ── Ring ── */
.strand-spinner__ring {
	display: block;
	border: 2px solid var(--strand-gray-200);
	border-top-color: var(--strand-blue-primary);
	border-radius: var(--strand-radius-full);
	animation: strand-spinner-rotate 0.8s linear infinite;
}

/* ── Sizes ── */
.strand-spinner--sm .strand-spinner__ring {
	width: 16px;
	height: 16px;
}

.strand-spinner--md .strand-spinner__ring {
	width: 20px;
	height: 20px;
}

.strand-spinner--lg .strand-spinner__ring {
	width: 32px;
	height: 32px;
}

/* ── Animation ── */
@keyframes strand-spinner-rotate {
	to {
		transform: rotate(360deg);
	}
}

/* ── Screen reader only text ── */
.strand-spinner__sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-spinner__ring {
		animation: none;
		border-style: dotted;
	}
}

/* Stack */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-stack {
	display: flex;
	max-width: 100%;
	box-sizing: border-box;
}

.strand-stack > * {
	min-width: 0;
}

/* ── Direction ── */
.strand-stack--vertical {
	flex-direction: column;
}

.strand-stack--horizontal {
	flex-direction: row;
}

/* ── Alignment ── */
.strand-stack--align-start {
	align-items: flex-start;
}

.strand-stack--align-center {
	align-items: center;
}

.strand-stack--align-end {
	align-items: flex-end;
}

/* Default stretch handled by flexbox default */

/* ── Justification ── */
.strand-stack--justify-start {
	justify-content: flex-start;
}

.strand-stack--justify-center {
	justify-content: center;
}

.strand-stack--justify-end {
	justify-content: flex-end;
}

.strand-stack--justify-between {
	justify-content: space-between;
}

.strand-stack--justify-around {
	justify-content: space-around;
}

/* ── Wrap ── */
.strand-stack--wrap {
	flex-wrap: wrap;
}

/* ── Responsive direction (DL Part XV: small-screen reflow) ──
   Combine with strand-stack--horizontal: at <=768px the stack
   collapses to a vertical column, alignment recenters, and
   children are allowed to grow to a comfortable max-width via
   strand-stack__item--responsive (sets max-width 280px width 100%). */
@media (max-width: 768px) {
	.strand-stack--horizontal.strand-stack--responsive {
		flex-direction: column;
		align-items: center;
	}

	.strand-stack--responsive > .strand-stack__item--full-mobile,
	.strand-stack--responsive > .strand-btn {
		width: 100%;
		max-width: 280px;
	}
}

/* ── Gap utilities ── */
.strand-stack--gap-0 {
	gap: 0;
}
.strand-stack--gap-1 {
	gap: var(--strand-space-1);
}
.strand-stack--gap-2 {
	gap: var(--strand-space-2);
}
.strand-stack--gap-3 {
	gap: var(--strand-space-3);
}
.strand-stack--gap-4 {
	gap: var(--strand-space-4);
}
.strand-stack--gap-5 {
	gap: var(--strand-space-5);
}
.strand-stack--gap-6 {
	gap: var(--strand-space-6);
}
.strand-stack--gap-8 {
	gap: var(--strand-space-8);
}

/* StarRating */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-star-rating {
	display: inline-flex;
	align-items: center;
	gap: var(--strand-space-1);
	color: var(--strand-gray-900);
	line-height: 1;
}

/* ── Star button ── */
.strand-star-rating__star {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background: transparent;
	border: 0;
	padding: 0;
	margin: 0;
	cursor: pointer;
	color: var(--strand-gray-200);
	border-radius: var(--strand-radius-sm);
	transition: color var(--strand-duration-fast) var(--strand-ease-out-quart), transform
		var(--strand-duration-fast) var(--strand-ease-out-quart);
	min-width: var(--strand-touch-target);
	min-height: var(--strand-touch-target);
	font-family: inherit;
}

.strand-star-rating__star:focus-visible {
	outline: none;
	box-shadow: var(--strand-focus-ring);
}

.strand-star-rating__star--active {
	color: var(--strand-amber-caution);
}

.strand-star-rating__star:not(:disabled):hover .strand-star-rating__glyph,
.strand-star-rating__star:not(:disabled):focus-visible .strand-star-rating__glyph {
	transform: scale(1.08);
}

/* ── Glyph ── */
.strand-star-rating__glyph {
	display: inline-block;
	font-size: inherit;
	line-height: 1;
	transition: transform var(--strand-duration-fast) var(--strand-ease-out-quart);
}

/* ── Sizes ── */
.strand-star-rating--sm {
	font-size: 1rem;
}

.strand-star-rating--sm .strand-star-rating__star {
	min-width: 32px;
	min-height: 32px;
}

.strand-star-rating--md {
	font-size: 1.5rem;
}

.strand-star-rating--md .strand-star-rating__star {
	min-width: var(--strand-touch-target);
	min-height: var(--strand-touch-target);
}

.strand-star-rating--lg {
	font-size: 2.25rem;
}

.strand-star-rating--lg .strand-star-rating__star {
	min-width: 56px;
	min-height: 56px;
}

/* ── Read-only ── */
.strand-star-rating--readonly .strand-star-rating__star {
	cursor: default;
	pointer-events: none;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-star-rating__star,
	.strand-star-rating__glyph {
		transition: none;
	}

	.strand-star-rating__star:not(:disabled):hover .strand-star-rating__glyph,
	.strand-star-rating__star:not(:disabled):focus-visible .strand-star-rating__glyph {
		transform: none;
	}
}

/* Swatch */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ══════════════════════════════════════════════════
   SWATCH (color specimen for docs surfaces)
   ══════════════════════════════════════════════════
   A single color tile rendered in a grid of swatches on a
   design-token documentation page. Background and text colors
   are applied inline via the consumer (because tone varies per
   swatch). Base chrome (border, min-height, internal layout)
   is CSS-driven. Grid container is a sibling primitive. */

.strand-swatch-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
	gap: 10px;
}

.strand-swatch {
	display: flex;
	flex-direction: column;
	gap: var(--strand-space-1);
	/* 14px 14px 12px: specimen chrome literal, no token scale match */
	padding: 14px 14px 12px;
	border-radius: var(--strand-radius-md);
	border: 1px solid var(--strand-gray-200);
	min-height: 92px;
}

.strand-swatch__name {
	font-family: var(--strand-font-mono);
	font-size: 10px;
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	font-weight: var(--strand-weight-medium);
}

.strand-swatch__hex {
	font-family: var(--strand-font-mono);
	font-size: 11px;
	opacity: 0.75;
	margin-top: auto;
}

/* Switch */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-switch {
	display: inline-flex;
	align-items: center;
	gap: var(--strand-space-2);
	min-height: var(--strand-touch-target);
	cursor: pointer;
	user-select: none;
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-sm);
	color: var(--strand-gray-900);
	line-height: var(--strand-leading-snug);
}

/* ── Track ── */
.strand-switch__track {
	position: relative;
	display: inline-flex;
	align-items: center;
	width: 40px;
	height: 22px;
	padding: 0;
	border: 1px solid var(--strand-gray-200);
	border-radius: var(--strand-radius-full);
	background: var(--strand-gray-200);
	cursor: pointer;
	flex-shrink: 0;
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart), border-color
		var(--strand-duration-fast) var(--strand-ease-out-quart), box-shadow var(--strand-duration-fast)
		var(--strand-ease-out-quart);
}

.strand-switch__track:focus-visible {
	border-color: var(--strand-blue-primary);
	box-shadow: var(--strand-focus-ring);
	outline: none;
}

/* ── Thumb ── */
.strand-switch__thumb {
	position: absolute;
	left: 2px;
	width: 16px;
	height: 16px;
	border-radius: var(--strand-radius-full);
	background: var(--strand-surface-elevated);
	box-shadow: var(--strand-elevation-1);
	transition: transform var(--strand-duration-fast) var(--strand-ease-out-expo);
}

/* ── Checked ── */
.strand-switch--checked .strand-switch__track {
	background: var(--strand-blue-primary);
	border-color: var(--strand-blue-primary);
}

.strand-switch--checked .strand-switch__thumb {
	transform: translateX(18px);
}

/* ── Hover ── */
.strand-switch:hover:not(.strand-switch--disabled) .strand-switch__track {
	border-color: var(--strand-blue-indicator);
}

.strand-switch--checked:hover:not(.strand-switch--disabled) .strand-switch__track {
	background: var(--strand-blue-vivid);
	border-color: var(--strand-blue-vivid);
}

/* ── Label ── */
.strand-switch__label {
	color: var(--strand-gray-900);
}

/* ── Disabled ── */
.strand-switch--disabled {
	opacity: 0.4;
	cursor: not-allowed;
}

.strand-switch--disabled .strand-switch__track {
	cursor: not-allowed;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-switch__track,
	.strand-switch__thumb {
		transition: none;
	}
}

/* Table */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Wrapper (responsive scroll) ── */
.strand-table-wrapper {
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
}

/* ── Table ── */
.strand-table {
	width: 100%;
	border-collapse: collapse;
	border-spacing: 0;
}

/* ── Header ── */
.strand-table__th {
	padding: var(--strand-space-3) var(--strand-space-4);
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	color: var(--strand-gray-500);
	text-align: left;
	border-bottom: 1px solid var(--strand-gray-200);
	white-space: nowrap;
}

/* ── Sort button ── */
.strand-table__sort-btn {
	display: inline-flex;
	align-items: center;
	gap: var(--strand-space-1);
	padding: 0;
	border: none;
	background: none;
	font: inherit;
	color: inherit;
	letter-spacing: inherit;
	text-transform: inherit;
	cursor: pointer;
	white-space: nowrap;
}

.strand-table__sort-btn:hover {
	color: var(--strand-gray-600);
}

.strand-table__sort-btn:focus-visible {
	outline: 2px solid var(--strand-blue-primary);
	outline-offset: 2px;
}

.strand-table__sort-indicator {
	font-size: var(--strand-text-xs);
	opacity: 0.6;
}

/* ── Body ── */
.strand-table__td {
	padding: var(--strand-space-3) var(--strand-space-4);
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-sm);
	color: var(--strand-gray-600);
	border-bottom: 1px solid var(--strand-gray-200);
}

/* ── Row hover ── */
.strand-table__row {
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-table__row:hover {
	background: var(--strand-blue-glow);
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-table__row {
		transition: none;
	}
}

/* Tabs */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Tab list ──
   Horizontally scroll the tablist when the combined tab widths exceed
   the container. Without this, a tablist wider than the viewport
   pushes the document horizontally and every other section inherits
   the scrollbar. The tablist keeps its own overflow so the overflow
   stays local, keyboard users still focus every tab in order, and
   scroll-snap preserves the tactile feel of a control panel. */
.strand-tabs [role="tablist"] {
	display: flex;
	gap: var(--strand-space-1);
	border-bottom: 1px solid var(--strand-gray-200);
	overflow-x: auto;
	scrollbar-width: none;
	-ms-overflow-style: none;
	scroll-snap-type: x proximity;
	/* Let flex children keep their intrinsic width so they don't shrink
     past their text before we allow scrolling. */
	flex-wrap: nowrap;
}

.strand-tabs [role="tablist"]::-webkit-scrollbar {
	display: none;
}

/* ── Tab button ──
   flex-shrink: 0 keeps tabs at their intrinsic text width so the
   tablist scrolls instead of squeezing tab labels. scroll-snap-align
   pins each tab's start edge when the user flicks, preserving the
   control-panel tactile feel. */
.strand-tabs__tab {
	position: relative;
	flex-shrink: 0;
	scroll-snap-align: start;
	padding: var(--strand-space-2) var(--strand-space-4);
	border: none;
	border-bottom: 2px solid transparent;
	background: none;
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-sm);
	font-weight: var(--strand-weight-medium);
	color: var(--strand-gray-500);
	cursor: pointer;
	transition: color var(--strand-duration-fast) var(--strand-ease-out-quart), border-color
		var(--strand-duration-fast) var(--strand-ease-out-expo);
}

.strand-tabs__tab:hover {
	color: var(--strand-gray-600);
}

.strand-tabs__tab:focus-visible {
	outline: 2px solid var(--strand-blue-primary);
	outline-offset: 2px;
}

.strand-tabs__tab--active {
	color: var(--strand-blue-primary);
	border-bottom-color: var(--strand-blue-primary);
}

/* ── Instrument variant (mono uppercase, control-panel feel) ──
   Use for state switches like "For Talent / For Employers" where
   the tab acts like a toggle on a control panel rather than a
   content navigation. Pairs with strand-tabs__panel--reveal. */
.strand-tabs--instrument [role="tablist"] {
	/* Use `safe center` so centering only applies when the content fits.
     When the tablist overflows, flex will clip the near edge, hiding
     the first tab from users who scrolled to the start. `safe center`
     degrades to `start` on overflow, which keeps every tab reachable
     via scroll. */
	justify-content: safe center;
	gap: 0;
	border-bottom: 1px solid var(--strand-surface-subtle);
}

.strand-tabs--instrument .strand-tabs__tab {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	color: var(--strand-gray-600);
	padding: var(--strand-space-3) var(--strand-space-6);
}

.strand-tabs--instrument .strand-tabs__tab--active {
	color: var(--strand-blue-midnight);
	border-bottom-color: var(--strand-blue-primary);
}

/* ── Panel ── */
.strand-tabs [role="tabpanel"] {
	padding: var(--strand-space-4) 0;
}

/* ── Panel reveal animation (DL Part VI motion: subtle entry) ──
   Apply to the active tab panel when switching tabs. Fades in
   with a small upward slide. JS toggles strand-tabs__panel--reveal
   on the panel that becomes active. */
@keyframes strand-tabs-reveal {
	from {
		opacity: 0;
		transform: translateY(8px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

.strand-tabs__panel--reveal {
	animation: strand-tabs-reveal var(--strand-duration-normal) var(--strand-ease-out-expo) both;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-tabs__tab {
		transition: none;
	}
	.strand-tabs__panel--reveal {
		animation: none;
	}
}

/* Tag */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-tag {
	display: inline-flex;
	align-items: center;
	gap: var(--strand-space-1);
	padding: var(--strand-space-1) var(--strand-space-2);
	border-radius: var(--strand-radius-sm);
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	line-height: var(--strand-leading-snug);
	white-space: nowrap;
	border: 1px solid transparent;
}

/* ── Solid variant ── */
.strand-tag--solid.strand-tag--default {
	background: var(--strand-surface-recessed);
	color: var(--strand-gray-600);
}

.strand-tag--solid.strand-tag--teal {
	background: var(--strand-surface-recessed);
	color: var(--strand-on-teal-tint);
}

.strand-tag--solid.strand-tag--blue {
	background: var(--strand-blue-glow);
	color: var(--strand-blue-deep);
}

.strand-tag--solid.strand-tag--amber {
	background: var(--strand-surface-recessed);
	color: var(--strand-on-amber-tint);
}

.strand-tag--solid.strand-tag--red {
	background: var(--strand-surface-recessed);
	color: var(--strand-on-red-tint);
}

/* ── Outlined variant ── */
.strand-tag--outlined {
	background: transparent;
}

.strand-tag--outlined.strand-tag--default {
	border-color: var(--strand-gray-200);
	color: var(--strand-gray-600);
}

.strand-tag--outlined.strand-tag--teal {
	border-color: var(--strand-teal-vital);
	color: var(--strand-on-teal-tint);
}

.strand-tag--outlined.strand-tag--blue {
	border-color: var(--strand-blue-primary);
	color: var(--strand-blue-deep);
}

.strand-tag--outlined.strand-tag--amber {
	border-color: var(--strand-amber-caution);
	color: var(--strand-on-amber-tint);
}

.strand-tag--outlined.strand-tag--red {
	border-color: var(--strand-red-alert);
	color: var(--strand-on-red-tint);
}

/* ── Remove button ── */
.strand-tag__remove {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	border: none;
	background: none;
	color: currentColor;
	opacity: 0.6;
	cursor: pointer;
	border-radius: var(--strand-radius-sm);
	transition: opacity var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-tag__remove:hover {
	opacity: 1;
}

/* ── Joined chip (interactive pill) ──
   A compact pill the user taps to toggle membership off. Used
   for "joined channel" style affordances where the in-content
   pill carries the active signal and the parent card chrome
   stays quiet. Designed to live inside a card header row. */
.strand-chip--joined {
	display: inline-flex;
	align-items: center;
	gap: var(--strand-space-1);
	flex-shrink: 0;
	padding: 2px var(--strand-space-2);
	background: var(--strand-blue-glow);
	color: var(--strand-blue-deep);
	border: 1px solid var(--strand-blue-indicator);
	border-radius: var(--strand-radius-full);
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-semibold);
	line-height: 1;
	letter-spacing: var(--strand-tracking-tight);
	cursor: pointer;
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart), border-color
		var(--strand-duration-fast) var(--strand-ease-out-quart), color var(--strand-duration-fast)
		var(--strand-ease-out-quart);
}

.strand-chip--joined:hover:not(:disabled) {
	background: var(--strand-blue-tint);
	border-color: var(--strand-blue-deep);
}

.strand-chip--joined:focus-visible {
	outline: 2px solid var(--strand-blue-primary);
	outline-offset: 2px;
}

.strand-chip--joined:disabled {
	opacity: 0.5;
	cursor: not-allowed;
}

.strand-chip--joined__check {
	font-size: 0.875em;
	line-height: 1;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-tag__remove {
		transition: none;
	}

	.strand-chip--joined {
		transition: none;
	}
}

/* Textarea */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Base ── */
.strand-textarea {
	position: relative;
	display: flex;
	flex-direction: column;
	background: var(--strand-surface-elevated);
	border: 1px solid var(--strand-gray-200);
	border-radius: var(--strand-radius-md);
	transition: border-color var(--strand-duration-fast) var(--strand-ease-out-quart), box-shadow
		var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-textarea:focus-within {
	border-color: var(--strand-blue-primary);
	box-shadow: var(--strand-focus-ring);
}

/* ── Field ── */
.strand-textarea__field {
	width: 100%;
	min-height: 80px;
	padding: var(--strand-space-3) var(--strand-space-4);
	background: transparent;
	border: none;
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-base);
	color: var(--strand-gray-900);
	outline: none;
	resize: vertical;
	line-height: var(--strand-leading-normal);
}

.strand-textarea__field::placeholder {
	color: var(--strand-gray-400);
}

/* ── Auto-resize ── */
.strand-textarea--auto-resize .strand-textarea__field {
	resize: none;
	overflow: hidden;
}

/* ── Character count ── */
.strand-textarea__count {
	align-self: flex-end;
	padding: var(--strand-space-1) var(--strand-space-3) var(--strand-space-2);
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-xs);
	color: var(--strand-gray-500);
}

/* ── Error ── */
.strand-textarea--error {
	border-color: var(--strand-red-alert);
}

.strand-textarea--error:focus-within {
	border-color: var(--strand-red-alert);
	box-shadow: var(--strand-focus-ring-error);
}

/* ── Disabled ── */
.strand-textarea--disabled {
	opacity: 0.4;
	cursor: not-allowed;
}

.strand-textarea--disabled .strand-textarea__field {
	cursor: not-allowed;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-textarea {
		transition: none;
	}
}

/* Toast */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Container ── */
.strand-toast__container {
	position: fixed;
	right: var(--strand-space-6);
	bottom: var(--strand-space-6);
	z-index: 1100;
	display: flex;
	flex-direction: column-reverse;
	gap: var(--strand-space-3);
	pointer-events: none;
}

/* ── Toast ── */
.strand-toast {
	display: flex;
	align-items: center;
	justify-content: space-between;
	min-width: 280px;
	max-width: 420px;
	padding: var(--strand-space-4) var(--strand-space-5);
	background: var(--strand-surface-elevated);
	border-radius: var(--strand-radius-lg);
	box-shadow: var(--strand-elevation-3);
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-sm);
	pointer-events: auto;
	animation: strand-toast-in var(--strand-duration-normal) var(--strand-ease-out-expo);
}

/* ── Status prefix ── */
.strand-toast__status {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-semibold);
	letter-spacing: var(--strand-tracking-wider);
	text-transform: uppercase;
	margin-right: var(--strand-space-3);
	flex-shrink: 0;
}

.strand-toast--info .strand-toast__status {
	color: var(--strand-blue-primary);
}
.strand-toast--success .strand-toast__status {
	color: var(--strand-teal-vital);
}
.strand-toast--warning .strand-toast__status {
	color: var(--strand-amber-caution);
}
.strand-toast--error .strand-toast__status {
	color: var(--strand-red-alert);
}

/* ── Message ── */
.strand-toast__message {
	flex: 1;
	min-width: 0;
	color: var(--strand-gray-900);
}

/* ── Dismiss button ── */
.strand-toast__dismiss {
	flex-shrink: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--strand-space-6);
	height: var(--strand-space-6);
	margin-left: var(--strand-space-3);
	padding: 0;
	border: none;
	border-radius: var(--strand-radius-md);
	background: transparent;
	color: var(--strand-gray-500);
	font-size: var(--strand-text-base);
	cursor: pointer;
	transition: background var(--strand-duration-fast) var(--strand-ease-out-quart), color
		var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-toast__dismiss:hover {
	background: var(--strand-gray-200);
	color: var(--strand-gray-600);
}

/* ── Animations ── */
@keyframes strand-toast-in {
	from {
		opacity: 0;
		transform: translateY(8px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-toast {
		animation: none;
	}

	.strand-toast__dismiss {
		transition: none;
	}
}

/* TokenSpecimen */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ══════════════════════════════════════════════════
   TOKEN SPECIMEN (spacing / radius / shadow tiles)
   ══════════════════════════════════════════════════
   Each specimen pairs a visual (spacer bar, radius box, or
   shadow tile) with a mono label and value. Visuals set their
   pixel dimensions and shadow/radius inline (per-token variance
   is the whole point of the spec). */

.strand-token-specimen-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
	gap: var(--strand-space-5);
}

.strand-token-specimen {
	display: flex;
	flex-direction: column;
	gap: 10px;
	align-items: flex-start;
	font-family: var(--strand-font-mono);
	font-size: 10px;
	letter-spacing: 0.04em;
	color: var(--strand-gray-500);
	line-height: 1.5;
}

.strand-token-specimen b {
	display: block;
	font-weight: var(--strand-weight-medium);
	color: var(--strand-blue-midnight);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	font-size: 10px;
}

.strand-token-specimen > span {
	display: block;
	line-height: 1.5;
}

.strand-token-specimen__spacer {
	display: block;
	height: 16px;
	background: var(--strand-blue-primary);
	border-radius: var(--strand-radius-sm);
	box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.15);
}

.strand-token-specimen__box {
	width: 64px;
	height: 64px;
	background: var(--strand-surface-elevated);
	border: 1px solid var(--strand-gray-200);
}

/* Tooltip */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Wrapper ── */
.strand-tooltip__wrapper {
	position: relative;
	display: inline-flex;
	/* Scope tooltip popup layout to this wrapper so an absolutely
     positioned, nowrap tooltip cannot bloat an ancestor's scrollWidth
     on narrow viewports. Without this, a long tooltip next to a
     viewport-edge trigger overflows the trigger's containing card and
     fails overflow-budget tests like "card does not overflow 375px".
     See packages/strand-ui/src/components/Tooltip/Tooltip.tsx for the
     rendered DOM; the popup uses `position: absolute` + nowrap, which
     is enough to stop layout-flow contribution but NOT scroll-extent
     contribution without `contain: layout`. */
	contain: layout;
}

/* ── Tooltip ── */
.strand-tooltip {
	position: absolute;
	z-index: 1200;
	padding: var(--strand-space-1) var(--strand-space-2);
	background: var(--strand-gray-900);
	color: var(--strand-on-blue-primary);
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-xs);
	border-radius: var(--strand-radius-md);
	white-space: nowrap;
	pointer-events: none;
	opacity: 0;
	transition: opacity var(--strand-duration-fast) var(--strand-ease-out-quart);
}

.strand-tooltip--visible {
	opacity: 1;
}

/* ── Positions ── */
.strand-tooltip--top {
	bottom: 100%;
	left: 50%;
	transform: translateX(-50%);
	margin-bottom: var(--strand-space-2);
}

.strand-tooltip--bottom {
	top: 100%;
	left: 50%;
	transform: translateX(-50%);
	margin-top: var(--strand-space-2);
}

.strand-tooltip--left {
	right: 100%;
	top: 50%;
	transform: translateY(-50%);
	margin-right: var(--strand-space-2);
}

.strand-tooltip--right {
	left: 100%;
	top: 50%;
	transform: translateY(-50%);
	margin-left: var(--strand-space-2);
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
	.strand-tooltip {
		transition: none;
	}
}

/* TypeSpecimen */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ══════════════════════════════════════════════════
   TYPE SPECIMEN (docs-site typography tile)
   ══════════════════════════════════════════════════
   A stacked specimen: large display text on top, mono meta
   line below. Use inside a section body on a typography
   documentation page. Dashed divider between siblings. */

.strand-type-specimen {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--strand-space-3);
	padding: var(--strand-space-5) 0;
	border-top: 1px dashed var(--strand-gray-200);
}

.strand-type-specimen:first-child {
	border-top: 0;
	padding-top: 0;
}

.strand-type-specimen__meta {
	display: block;
	font-family: var(--strand-font-mono);
	font-size: 10px;
	letter-spacing: 0.06em;
	color: var(--strand-gray-500);
	line-height: 1.5;
}

.strand-type-specimen__meta b {
	color: var(--strand-blue-primary);
	font-weight: var(--strand-weight-medium);
}

/* Static */
/*! Strand UI | MIT License | dillingerstaffing.com */

/* ── Presentation mode ──
   Add .strand-static to a parent element to render components
   at full visual fidelity without interaction.
   Use for documentation, showcases, and screenshots. */

.strand-static {
	pointer-events: none;
}

.strand-static [disabled],
.strand-static [aria-disabled="true"] {
	opacity: 1;
	cursor: default;
}

.strand-static .strand-btn:disabled {
	opacity: 1;
	cursor: default;
}

.strand-static .strand-toast {
	position: static;
}

.strand-static .strand-tooltip {
	position: static;
}

.strand-static *,
.strand-static *::before,
.strand-static *::after {
	transition: none !important;
	animation: none !important;
}

/* ── Recessed viewport ──
   The instrument viewport sits below the card surface.
   Use for component previews, showcases, and documentation. */

.strand-viewport {
	background: var(--strand-surface-recessed);
	box-shadow: var(--strand-shadow-inset);
	border-radius: var(--strand-radius-lg);
	padding: var(--strand-space-6);
}

/* ── Frosted viewport (DL Part 9.4) ──
   Light data-dense context with frosted-glass character. The
   "frosted instrument panel embedded in the lab bench" feel,
   without going dark. Backdrop-filter requires content behind it. */
.strand-viewport--frosted {
	background: var(--strand-glass-bg);
	-webkit-backdrop-filter: blur(var(--strand-glass-blur));
	backdrop-filter: blur(var(--strand-glass-blur));
	border: 1px solid var(--strand-glass-border);
	border-radius: var(--strand-radius-lg);
	box-shadow: var(--strand-shadow-inset);
	padding: var(--strand-space-6);
}

/* ── Glass surface utility (DL Part 9.5) ──
   Frosted-glass treatment for any element that wants the
   "glass walls with controlled daylight" character. Apply to
   nav, sticky toolbars, modal headers, surfaces above content.
   backdrop-filter only renders meaningfully when there is
   something behind the element. */
.strand-glass-surface {
	background: var(--strand-glass-bg);
	-webkit-backdrop-filter: blur(var(--strand-glass-blur));
	backdrop-filter: blur(var(--strand-glass-blur));
	border: 1px solid var(--strand-glass-border);
}

/* ── Overline (specimen label pattern, DL Part IV.5) ── */
.strand-overline {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-ultra);
	text-transform: uppercase;
	color: var(--strand-gray-500);
	line-height: var(--strand-leading-normal);
}

/* ── Overline accent variant (blue instead of gray) ──
   Uses blue-deep so the small uppercase text meets WCAG AA 4.5:1
   contrast on the default white surface. blue-primary (#3b82f6)
   only scores 3.28:1 at 10.4px mono. blue-deep stays vibrant
   without violating contrast. */
.strand-overline--accent {
	color: var(--strand-blue-deep);
}

/* ── Overline with pulse dot (DL Part 7: alive signal) ──
   Adds an animated dot prefix that pulses in a slow rhythm. Use on
   the primary hero overline to signal "this is a living system."
   Combine with strand-overline--accent for the blue brand color.
   The dot uses blue-primary; on dark backgrounds use teal-vital.

   The display: inline-block + relative positioning shrinks the
   overline to its text width so the absolutely-positioned dot
   sits immediately to the LEFT of the text -- not to the left of
   a full-width parent container. */
.strand-overline--pulse {
	position: relative;
	padding-left: var(--strand-space-4);
	display: inline-block !important;
}

.strand-overline--pulse::before {
	content: "";
	position: absolute;
	left: 0;
	top: 50%;
	width: 6px;
	height: 6px;
	margin-top: -3px;
	border-radius: var(--strand-radius-full);
	background: var(--strand-blue-primary);
	animation: strand-overline-pulse 2s var(--strand-ease-in-out-sine) infinite;
}

@keyframes strand-overline-pulse {
	0%,
	100% {
		opacity: 1;
	}
	50% {
		opacity: 0.4;
	}
}

@media (prefers-reduced-motion: reduce) {
	.strand-overline--pulse::before {
		animation: none;
	}
}

/* DL Part XVII.2: Contrast-safe pairing on recessed surfaces.
   gray-500 on surface-recessed fails 4.5:1 for small text.
   Auto-darken to gray-600 when inside a recessed context. */
.strand-section--bg-recessed .strand-overline {
	color: var(--strand-gray-600);
}

.strand-section--bg-recessed .strand-overline--accent {
	color: var(--strand-blue-deep);
}

.strand-section--bg-recessed .strand-lead {
	color: var(--strand-gray-600);
}

.strand-section--bg-recessed .strand-text-secondary {
	color: var(--strand-gray-600);
}

.strand-section--bg-recessed .strand-step-indicator {
	color: var(--strand-blue-deep);
}

/* All small text on recessed surfaces needs darker colors for WCAG 4.5:1 */
.strand-section--bg-recessed .strand-tabs__tab,
.strand-section--bg-recessed .strand-form-field__label,
.strand-section--bg-recessed .strand-form-field__hint,
.strand-section--bg-recessed .strand-badge--default {
	color: var(--strand-gray-600);
}

.strand-section--bg-recessed .strand-tabs__tab--active {
	color: var(--strand-blue-deep);
	border-bottom-color: var(--strand-blue-deep);
}

/* ── Headline (display heading, DL Part IV.5) ── */
.strand-headline {
	font-family: var(--strand-font-mono);
	font-weight: var(--strand-weight-light);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	color: var(--strand-blue-midnight);
	line-height: var(--strand-leading-tight);
}

.strand-headline--xl {
	font-size: clamp(2.5rem, 5vw + 1rem, 5rem);
	letter-spacing: 0.35em;
}

.strand-headline--lg {
	font-size: clamp(1.5rem, 3vw + 0.5rem, 2.5rem);
	letter-spacing: var(--strand-tracking-tighter);
}

/* ── Gradient text effect (premium hero headlines) ── */
.strand-headline--gradient {
	background: linear-gradient(180deg, var(--strand-gray-900) 0%, var(--strand-blue-midnight) 100%);
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: transparent;
}

/* ── Title (human voice display, DL Part IV.7) ── */
.strand-title {
	font-family: var(--strand-font-sans);
	font-weight: var(--strand-weight-light);
	letter-spacing: var(--strand-tracking-tighter);
	color: var(--strand-blue-midnight);
	font-size: clamp(1.5rem, 3vw + 0.5rem, 2.5rem);
	line-height: var(--strand-leading-snug);
}

/* ── Lead (intro paragraph, DL Part IV.6) ── */
.strand-lead {
	font-size: var(--strand-text-lg);
	color: var(--strand-gray-500);
	max-width: 50ch;
	line-height: var(--strand-leading-relaxed);
}

/* ── Secondary text (caption/description, DL Part III.8 Color Roles) ── */
.strand-text-secondary {
	font-size: var(--strand-text-sm);
	color: var(--strand-gray-500);
	line-height: var(--strand-leading-relaxed);
}

.strand-text-secondary strong {
	color: var(--strand-gray-700);
}

.strand-text-secondary a {
	color: var(--strand-blue-primary);
	text-decoration: none;
}

.strand-text-secondary a:hover {
	color: var(--strand-blue-vivid);
}

.strand-text-secondary--xs {
	font-size: var(--strand-text-xs);
}

/* ── Typographic utilities ──
   Pure single-property utilities for composing text styles when
   the full .strand-overline / .strand-kv / .strand-data-readout
   component semantics are too heavy. Use on inline meta rows,
   cadence lines, and data-adjacent body copy that should read
   as instrument output without inheriting the overline's letter
   spacing and weight.
   The size utilities map 1:1 onto the type scale tokens and carry
   no color. That is the difference from .strand-text-secondary,
   which also recolors to gray-500: use a size utility to shrink a
   value (a long URL, code, or id) in place while it stays primary
   colored; use .strand-text-secondary to demote text to metadata. */
.strand-font-mono {
	font-family: var(--strand-font-mono);
}

.strand-text-sm {
	font-size: var(--strand-text-sm);
}

.strand-text-xs {
	font-size: var(--strand-text-xs);
}

/* Break a long unbreakable string (URL, hash, id, token) so it wraps
   inside a constrained box instead of overflowing. overflow-wrap:
   anywhere also lets a flex item shrink below the string's intrinsic
   width, which is what a readonly URL field needs in order to wrap. */
.strand-break-anywhere {
	overflow-wrap: anywhere;
}

/* Keep a short data atom (a date, an amount, an id) on one line so a
   squeezed table column or flex row wraps its prose neighbors
   instead. The inverse of .strand-break-anywhere: compose nowrap onto
   the cells that must never break mid-figure and break-anywhere onto
   the column that should absorb the wrapping. */
.strand-nowrap {
	white-space: nowrap;
}

/* ── Value tone (financial sign) ──
   Color a numeric readout by its financial meaning: money coming in
   (a credit, a surplus, money kept back) reads positive/green; money
   going out (a debit, an overage, a shortfall) reads negative/red.
   The deep shades are chosen for WCAG AA contrast as text on light
   surfaces (the pure accents are too light to read as small figures).
   font-variant-numeric keeps figures aligned in tables and readouts.
   Compose onto any text node - a table cell, a data-readout value, a
   kv value: the utility sets only color + numeric alignment, so it
   layers on top of the host component's own typography. The color
   carries !important because the contract is that the tone wins
   wherever it is composed: component rules such as
   .strand-kv--editorial .strand-kv__value set color at higher
   specificity and later source order, and without the marker the
   composed tone silently loses the cascade (Gap #44). */
.strand-value {
	font-variant-numeric: tabular-nums;
}

.strand-value--positive {
	color: var(--strand-green-positive-deep) !important;
}

.strand-value--negative {
	color: var(--strand-red-alert-deep) !important;
}

/* ══════════════════════════════════════════════════
   COMPOSITION MOLECULES (DL Part XI-B Grammar)
   Named derivations of DL composition primitives.
   Each molecule is a convenience class for a common
   derivation chain. See DL 11.10 Productions for the
   grammar rules; DL 11.12 Derivation for verification.
   ══════════════════════════════════════════════════ */

/* ── Card Section ──
   Derivation: section-boundary production (DL 11.10).
   OVERLINE + border-bottom + margin-bottom + padding-bottom */
.strand-card-section {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: var(--strand-space-3);
	padding-bottom: var(--strand-space-2);
	border-bottom: 1px solid var(--strand-gray-200);
}

/* ── Key-Value Row ──
   Derivation: inline-pair + ranked-sequence (DL 11.10).
   identifier(OVERLINE) + quantifier(MONO_VALUE) + RANK_BORDER */
.strand-kv {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: var(--strand-space-2) var(--strand-space-4);
}

.strand-kv + .strand-kv {
	border-top: 1px solid var(--strand-border-subtle);
}

.strand-kv__label {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-wider);
	text-transform: uppercase;
	color: var(--strand-gray-500);
}

.strand-kv__value {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	color: var(--strand-gray-700);
	font-variant-numeric: tabular-nums;
	text-align: right;
}

.strand-kv__value--status {
	color: var(--strand-teal-vital);
}

/* ── Key-Value Row: editorial modifier ──
   Additive modifier on .strand-kv for card-metadata contexts
   (composed cards, editorial specimens) where values should
   read as soft sans-serif Blue-midnight rather than the default
   mono-gray instrument readout. Dashed divider between rows.
   Use inside .strand-card__section content blocks. The base
   .strand-kv default stays mono-tabular for instrument surfaces. */
.strand-kv--editorial {
	padding: 6px 0;
	font-size: var(--strand-text-sm);
	line-height: 1.4;
}

.strand-kv--editorial + .strand-kv--editorial {
	border-top: 1px dashed var(--strand-gray-200);
}

.strand-kv--editorial .strand-kv__label {
	letter-spacing: var(--strand-tracking-ultra);
	flex: none;
	white-space: nowrap;
}

.strand-kv--editorial .strand-kv__value {
	font-family: var(--strand-font-sans);
	font-size: var(--strand-text-sm);
	color: var(--strand-blue-midnight);
	font-weight: var(--strand-weight-medium);
	font-variant-numeric: normal;
}

/* ── Diagnostic Log Entry ──
   Derivation: inline-sequence + ranked-sequence (DL 11.10).
   time(OVERLINE) + status(OVERLINE+COLOR_SEMANTIC) + text(SECONDARY) + RANK_BORDER */
.strand-log {
	display: flex;
	align-items: baseline;
	gap: var(--strand-space-3);
	padding: var(--strand-space-2) var(--strand-space-4);
}

.strand-log + .strand-log {
	border-top: 1px solid var(--strand-border-subtle);
}

.strand-log__time {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-wider);
	text-transform: uppercase;
	color: var(--strand-gray-400);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

.strand-log__status {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-semibold);
	letter-spacing: var(--strand-tracking-wider);
	text-transform: uppercase;
	white-space: nowrap;
}

.strand-log__status--complete {
	color: var(--strand-teal-vital);
}
.strand-log__status--process {
	color: var(--strand-blue-primary);
}
.strand-log__status--warning {
	color: var(--strand-amber-caution);
}
.strand-log__status--error {
	color: var(--strand-red-alert);
}

.strand-log__text {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	color: var(--strand-gray-300);
	line-height: var(--strand-leading-normal);
}

.strand-log__text strong {
	font-weight: var(--strand-weight-semibold);
	color: var(--strand-gray-100);
}

/* ── Metric Row ──
   Derivation: centered-group (DL 11.10).
   self-contained(atom)* with center distribution + responsive gap */
.strand-metric-row {
	display: flex;
	justify-content: center;
	gap: clamp(2rem, 5vw, 4rem);
	text-align: center;
}

/* ── Bar Chart ──
   Derivation: column-array (DL 11.10).
   column(amount? + bar + label)* with equal flex + flex-end alignment.
   Blue Discipline: one color (--strand-blue-indicator). Part XIII viz rules. */
.strand-bar-chart {
	display: flex;
	gap: var(--strand-space-2);
	align-items: flex-end;
	height: var(--strand-space-40);
	padding: var(--strand-space-4);
}

.strand-bar-chart__col {
	flex: 1;
	display: grid;
	grid-template-rows: auto 1fr auto;
	align-items: end;
	justify-items: center;
	gap: var(--strand-space-1);
	height: 100%;
}

.strand-bar-chart__bar {
	width: 100%;
	background: var(--strand-blue-indicator);
	border-radius: var(--strand-radius-sm) var(--strand-radius-sm) 0 0;
	min-height: var(--strand-space-1);
}

.strand-bar-chart__amount {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-wider);
	text-transform: uppercase;
	color: var(--strand-gray-300);
	font-variant-numeric: tabular-nums;
}

.strand-bar-chart__label {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-wider);
	text-transform: uppercase;
	color: var(--strand-gray-400);
}

/* ── Bar chart size modifiers ── */
.strand-bar-chart--sm {
	height: var(--strand-space-24);
}
.strand-bar-chart--lg {
	height: var(--strand-space-48);
}

/* ── Nav offset for glass (fixed) nav ── */
.strand-nav-offset {
	padding-top: var(--strand-space-16);
}

/* ══════════════════════════════════════════════════
   UTILITIES
   ══════════════════════════════════════════════════ */

/* ── Screen reader only ── */
.strand-sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* ── Section header ──
   Derivation: section-boundary production (DL 11.10).
   Centered heading group with bottom margin. */
.strand-section-header {
	margin-bottom: clamp(2rem, 4vw, 4rem);
}

/* ── Step indicator ──
   Numbered position indicator for sequential steps. */
.strand-step-indicator {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--strand-space-8);
	height: var(--strand-space-8);
	border-radius: var(--strand-radius-full);
	background: var(--strand-blue-glow);
	color: var(--strand-blue-primary);
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-sm);
	font-weight: var(--strand-weight-semibold);
	margin-bottom: var(--strand-space-3);
}

/* ── Text center ── */
.strand-text-center {
	text-align: center;
}

/* ── Code name (mono identifier heading) ── */
.strand-code-name {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-base);
	font-weight: var(--strand-weight-semibold);
	color: var(--strand-blue-midnight);
	margin: 0;
}

/* ── Heading small (section heading inside cards) ── */
.strand-heading--sm {
	font-size: var(--strand-text-lg);
	font-weight: var(--strand-weight-medium);
	color: var(--strand-gray-800);
	margin: 0;
}

/* ═══ HEADLINE MONO VARIANT (DL Part IV.5) ═══
   Mono-family headline with medium weight, tight tracking,
   and sentence case (NOT uppercase). Use for product/lab
   titles where the brand identity is typed in lowercase or
   mixed case ("trust" vs "TRUST"). Composes with
   strand-headline--sm/md/lg for size. */
.strand-headline--mono {
	font-family: var(--strand-font-mono);
	font-weight: var(--strand-weight-medium);
	text-transform: none;
	letter-spacing: var(--strand-tracking-tight);
}

.strand-headline--md {
	font-size: clamp(1.25rem, 2vw + 0.5rem, 1.75rem);
	letter-spacing: var(--strand-tracking-tight);
}

/* ═══ STATUS CHIP (DL Part XVII: small classification label) ═══
   Inline pill for lab/product status. Mono uppercase, small tint.
   Pair with strand-card or header rows. */
.strand-status-chip {
	display: inline-block;
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-wide);
	text-transform: uppercase;
	padding: var(--strand-space-1) var(--strand-space-2);
	border-radius: var(--strand-radius-sm);
	line-height: var(--strand-leading-normal);
}

.strand-status-chip--live {
	color: var(--strand-on-teal-tint);
	background: var(--strand-teal-tint);
}

.strand-status-chip--neutral {
	color: var(--strand-gray-600);
	background: var(--strand-gray-tint);
}

.strand-status-chip--accent {
	color: var(--strand-blue-deep);
	background: var(--strand-blue-tint);
}

.strand-status-chip--caution {
	color: var(--strand-on-amber-tint);
	background: var(--strand-amber-tint);
}

/* COMMITTED variant: post-RSVP confirmation on dark instrument
   viewports. Vital teal on translucent fill and 30% alpha border
   signals earned state (DL Principle 5: Earned Elevation) without
   stealing focus from the event title. The translucent composition
   lets the chip sit cleanly on both dark and light surfaces. */
.strand-status-chip--committed {
	color: var(--strand-teal-vital);
	background: color-mix(in srgb, var(--strand-teal-vital) 16%, transparent);
	border: 1px solid color-mix(in srgb, var(--strand-teal-vital) 30%, transparent);
}

/* ── Viewport flex modifiers (component showcase layout) ── */
.strand-viewport--flex {
	display: flex;
	align-items: center;
	gap: var(--strand-space-4);
	flex-wrap: wrap;
}

.strand-viewport--flex-col {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--strand-space-4);
}

/* ═══ CONNECTED STEPS (DL Part XI-B: connected-sequence production) ═══
   Sequential process steps with a visual connector line between cards.
   Use with a grid of strand-card elements inside. */
.strand-steps-connected {
	position: relative;
}

.strand-steps-connected::before {
	content: "";
	position: absolute;
	top: 50%;
	left: var(--strand-space-8);
	right: var(--strand-space-8);
	height: 1px;
	background: linear-gradient(
		90deg,
		transparent 0%,
		var(--strand-blue-indicator) 20%,
		var(--strand-blue-indicator) 80%,
		transparent 100%
	);
	z-index: 0;
	pointer-events: none;
}

.strand-steps-connected > * {
	position: relative;
	z-index: 1;
}

@media (max-width: 767px) {
	.strand-steps-connected::before {
		display: none;
	}
}

/* ═══ FOOTER (DL Part XIX.5) ═══
   Compact closing panel. Border-top, centered, quiet. */
.strand-footer {
	padding: var(--strand-space-12) 0;
	border-top: 1px solid var(--strand-gray-200);
	text-align: center;
}

.strand-footer__nav {
	display: flex;
	justify-content: center;
	gap: var(--strand-space-6);
	flex-wrap: wrap;
	margin-bottom: var(--strand-space-6);
}

.strand-footer__link {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	letter-spacing: var(--strand-tracking-wider);
	color: var(--strand-gray-400);
	text-decoration: none;
	transition: color var(--strand-duration-fast) ease;
}

.strand-footer__link:hover {
	color: var(--strand-blue-primary);
}

.strand-footer__copy {
	font-size: var(--strand-text-xs);
	color: var(--strand-gray-400);
}

/* ═══ FORM LAYOUT (DL Part XVIII.1: Specimen Collection Room) ═══ */
.strand-form-grid {
	display: grid;
	gap: var(--strand-space-6);
}

.strand-form-row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--strand-space-6);
}

@media (max-width: 639px) {
	.strand-form-row {
		grid-template-columns: 1fr;
	}
}

/* ═══ HONEYPOT (DL Part XI.1: form security) ═══
   Hidden field that bots fill but humans never see.
   NOT display:none (some bots skip those).
   Positioned offscreen with aria-hidden and tabindex=-1. */
.strand-honeypot {
	position: absolute;
	left: -9999px;
	top: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
	opacity: 0;
	pointer-events: none;
}

/* ═══ HERO BACKGROUND (DL Part IX.4: hero surface treatment) ═══
   Full-bleed container behind hero content. Holds animated SVG,
   WebGL canvas, gradient, or any hero visual. */
.strand-hero-bg {
	position: absolute;
	inset: 0;
	overflow: hidden;
	pointer-events: none;
	z-index: 0;
}

.strand-hero-bg > * {
	width: 100%;
	height: 100%;
}

/* ═══ HERO NODE GRID (DL Part IX.4 + Principle 7: alive signal) ═══
   Animated SVG background showing a field of pulsing connection
   points. Use inside strand-hero-bg with an inline <svg>. Nodes and
   lines inherit color via currentColor; wire animation delays with
   the .strand-hero-grid__line--N modifiers (1-18). */
.strand-hero-grid {
	color: var(--strand-blue-primary);
}

.strand-hero-grid svg {
	width: 100%;
	height: 100%;
}

.strand-hero-grid__nodes {
	fill: currentColor;
	opacity: 0.2;
}

.strand-hero-grid__lines {
	stroke: currentColor;
	fill: none;
	stroke-width: 0.5;
	opacity: 0.08;
}

.strand-hero-grid__line {
	animation: strand-hero-grid-glow 12s var(--strand-ease-in-out-sine) infinite;
}

.strand-hero-grid__line--1 {
	animation-delay: 0s;
}
.strand-hero-grid__line--2 {
	animation-delay: 1s;
}
.strand-hero-grid__line--3 {
	animation-delay: 2s;
}
.strand-hero-grid__line--4 {
	animation-delay: 0.5s;
}
.strand-hero-grid__line--5 {
	animation-delay: 3s;
}
.strand-hero-grid__line--6 {
	animation-delay: 1.5s;
}
.strand-hero-grid__line--7 {
	animation-delay: 4s;
}
.strand-hero-grid__line--8 {
	animation-delay: 2.5s;
}
.strand-hero-grid__line--9 {
	animation-delay: 5s;
}
.strand-hero-grid__line--10 {
	animation-delay: 3.5s;
}
.strand-hero-grid__line--11 {
	animation-delay: 6s;
}
.strand-hero-grid__line--12 {
	animation-delay: 4.5s;
}
.strand-hero-grid__line--13 {
	animation-delay: 7s;
}
.strand-hero-grid__line--14 {
	animation-delay: 5.5s;
}
.strand-hero-grid__line--15 {
	animation-delay: 8s;
}
.strand-hero-grid__line--16 {
	animation-delay: 6.5s;
}
.strand-hero-grid__line--17 {
	animation-delay: 9s;
}
.strand-hero-grid__line--18 {
	animation-delay: 7.5s;
}

@keyframes strand-hero-grid-glow {
	0%,
	100% {
		stroke-opacity: 0.08;
	}
	50% {
		stroke-opacity: 0.4;
	}
}

/* ═══ PULSE INDICATOR (DL Principle 7: alive signal) ═══
   Small pulsing dot. Indicates "this element is live/active."
   Same concept as strand-card--active but as a point indicator.
   Use as ::after on logos, status dots, live badges. */
.strand-pulse {
	position: relative;
}

.strand-pulse::after {
	content: "";
	position: absolute;
	top: 0;
	right: -6px;
	width: 6px;
	height: 6px;
	border-radius: var(--strand-radius-full);
	background: var(--strand-teal-vital);
	animation: strand-pulse-glow 3s var(--strand-ease-in-out-sine) infinite;
}

@keyframes strand-pulse-glow {
	0%,
	100% {
		opacity: 0.4;
		transform: scale(1);
	}
	50% {
		opacity: 1;
		transform: scale(1.2);
	}
}

/* ═══ AUTH INDICATOR (nav utility) ═══
   Small text indicator for signed-in state in nav. */
.strand-auth-indicator {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	color: var(--strand-gray-400);
	letter-spacing: var(--strand-tracking-wide);
	white-space: nowrap;
}

.strand-auth-avatar {
	width: 28px;
	height: 28px;
	border-radius: var(--strand-radius-full);
	background: var(--strand-blue-glow);
	color: var(--strand-blue-primary);
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-semibold);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	text-decoration: none;
}

/* ═══ EMPTY STATES (DL Part XXI) ═══
   When an instrument has no data, it does not disappear.
   It shows an idle state. The label remains, the value
   becomes a placeholder. The user understands the
   instrument exists and will activate when data arrives. */

/* ── Idle Readout (DL Part 21.1) ──
   A DataReadout with no value. Shows -- in the value
   position, with the label still visible. Apply alongside
   .strand-data-readout. */
.strand-idle-readout .strand-data-readout__value::before {
	content: "--";
	color: var(--strand-gray-300);
	font-variant-numeric: tabular-nums;
}

.strand-idle-readout .strand-data-readout__value > * {
	display: none;
}

/* ── Empty Collection (DL Part 21.2) ──
   A list or grid with no items. Centered message in
   instrument voice. No illustration. The absence of data
   IS the visual. */
.strand-empty-collection {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: var(--strand-space-16) var(--strand-space-6);
	text-align: center;
	color: var(--strand-gray-400);
	font-family: var(--strand-font-sans);
}

.strand-empty-collection__message {
	font-size: var(--strand-text-base);
	color: var(--strand-gray-500);
	margin: 0;
}

.strand-empty-collection__action {
	margin-top: var(--strand-space-4);
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-xs);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-widest);
	text-transform: uppercase;
	color: var(--strand-blue-primary);
	text-decoration: none;
	background-image: linear-gradient(var(--strand-blue-primary), var(--strand-blue-primary));
	background-size: 0% 1px;
	background-position: left bottom;
	background-repeat: no-repeat;
	transition: background-size var(--strand-duration-normal) var(--strand-ease-out-expo);
}

.strand-empty-collection__action:hover {
	background-size: 100% 1px;
}

/* ── Empty Search (DL Part 21.3) ──
   "0 matches detected." plus suggestion in secondary text.
   The search instrument ran and returned nothing. */
.strand-empty-search {
	display: flex;
	flex-direction: column;
	align-items: center;
	padding: var(--strand-space-12) var(--strand-space-6);
	text-align: center;
}

.strand-empty-search__count {
	font-family: var(--strand-font-mono);
	font-size: var(--strand-text-sm);
	font-weight: var(--strand-weight-medium);
	letter-spacing: var(--strand-tracking-wider);
	text-transform: uppercase;
	color: var(--strand-gray-500);
	margin-bottom: var(--strand-space-2);
}

.strand-empty-search__suggestion {
	font-size: var(--strand-text-sm);
	color: var(--strand-gray-400);
	line-height: var(--strand-leading-relaxed);
	max-width: 50ch;
}

/* ═══ UTILITY CLASSES ══════════════════════════════
   Atomic, single-property helpers. Token-only values.
   Use sparingly: prefer named primitives and molecules
   when a coherent concept exists. These exist to absorb
   the long tail of one-off layout glue without forcing
   consumers to ship inline styles or per-page CSS files.
   ═══════════════════════════════════════════════════ */

/* ── Display ── */
.strand-block {
	display: block;
}

/* ── Flex / sizing ── */
.strand-flex-1 {
	flex: 1;
}
.strand-min-w-0 {
	min-width: 0;
}
.strand-full-width {
	width: 100%;
}

/* ── Margin top (token scale 1-8) ── */
.strand-mt-1 {
	margin-top: var(--strand-space-1);
}
.strand-mt-2 {
	margin-top: var(--strand-space-2);
}
.strand-mt-3 {
	margin-top: var(--strand-space-3);
}
.strand-mt-4 {
	margin-top: var(--strand-space-4);
}
.strand-mt-5 {
	margin-top: var(--strand-space-5);
}
.strand-mt-6 {
	margin-top: var(--strand-space-6);
}
.strand-mt-8 {
	margin-top: var(--strand-space-8);
}

/* ── Margin bottom (token scale 1-8) ── */
.strand-mb-1 {
	margin-bottom: var(--strand-space-1);
}
.strand-mb-2 {
	margin-bottom: var(--strand-space-2);
}
.strand-mb-3 {
	margin-bottom: var(--strand-space-3);
}
.strand-mb-4 {
	margin-bottom: var(--strand-space-4);
}
.strand-mb-5 {
	margin-bottom: var(--strand-space-5);
}
.strand-mb-6 {
	margin-bottom: var(--strand-space-6);
}
.strand-mb-8 {
	margin-bottom: var(--strand-space-8);
}

/* ── Margin auto (centering) ── */
.strand-mx-auto {
	margin-inline: auto;
}

/* ── Margin zero ──
   Strip a browser default margin (e.g. a <p> whose parent stack already owns
   the gap) so a semantic element can sit flush without an inline style. The
   zero endpoint of the margin scale. */
.strand-m-0 {
	margin: 0;
}

/* ── Padding (token scale 1-8) ──
   Mirror of the margin utilities. Added so consumers never inline
   `padding: var(--strand-space-N)` for list rows, section bands, or
   border-offset spacing. */
.strand-pt-1 {
	padding-top: var(--strand-space-1);
}
.strand-pt-2 {
	padding-top: var(--strand-space-2);
}
.strand-pt-3 {
	padding-top: var(--strand-space-3);
}
.strand-pt-4 {
	padding-top: var(--strand-space-4);
}
.strand-pt-5 {
	padding-top: var(--strand-space-5);
}
.strand-pt-6 {
	padding-top: var(--strand-space-6);
}
.strand-pt-8 {
	padding-top: var(--strand-space-8);
}
.strand-pb-1 {
	padding-bottom: var(--strand-space-1);
}
.strand-pb-2 {
	padding-bottom: var(--strand-space-2);
}
.strand-pb-3 {
	padding-bottom: var(--strand-space-3);
}
.strand-pb-4 {
	padding-bottom: var(--strand-space-4);
}
.strand-pb-5 {
	padding-bottom: var(--strand-space-5);
}
.strand-pb-6 {
	padding-bottom: var(--strand-space-6);
}
.strand-pb-8 {
	padding-bottom: var(--strand-space-8);
}
.strand-py-1 {
	padding-block: var(--strand-space-1);
}
.strand-py-2 {
	padding-block: var(--strand-space-2);
}
.strand-py-3 {
	padding-block: var(--strand-space-3);
}
.strand-py-4 {
	padding-block: var(--strand-space-4);
}
.strand-py-5 {
	padding-block: var(--strand-space-5);
}
.strand-py-6 {
	padding-block: var(--strand-space-6);
}
.strand-py-8 {
	padding-block: var(--strand-space-8);
}

/* ── Inline flex ──
   Row layout for a small cluster (icon + label, nav slot) that should
   size to content rather than fill like strand-stack. */
.strand-inline-flex {
	display: inline-flex;
	align-items: center;
	gap: var(--strand-space-2);
}

/* ── Italic text ──
   Emphasis for a single run (e.g. an "(edited)" marker). */
.strand-italic {
	font-style: italic;
}

/* ── List reset ──
   Strip default list chrome so a semantic <ul>/<ol> can carry
   strand-stack layout without browser bullets/indent. Replaces the
   repeated inline `list-style:none;margin:0;padding:0`. */
.strand-list-reset {
	list-style: none;
	margin: 0;
	padding: 0;
}

/* ── Disabled state ──
   Non-interactive visual for a control whose action is unavailable.
   Pairs with aria-disabled/disabled; the utility owns only the look. */
.strand-is-disabled {
	opacity: 0.35;
	pointer-events: none;
}

/* ── Link: inherit color ──
   A link that should read as body/heading text, not the accent link
   color (e.g. a whole card title wrapped in <a>). Replaces inline
   `color:inherit;background-image:none`. */
.strand-link--inherit {
	color: inherit;
	background-image: none;
}

/* ── Responsive 16:9 embed ──
   Aspect-ratio media box for an iframe (video, map). The child fills the
   box absolutely so no intrinsic-size inline styles are needed. */
.strand-embed-16x9 {
	position: relative;
	width: 100%;
	aspect-ratio: 16 / 9;
}
.strand-embed-16x9 > iframe,
.strand-embed-16x9 > * {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
}

/* ── Centered page ──
   Full-viewport single-card layout (token/confirmation pages: rate via
   email, unsubscribe). Centers one card with no bespoke page CSS. */
.strand-page--centered {
	min-height: 100vh;
	display: flex;
	align-items: center;
	justify-content: center;
}

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

	.strand-pulse::after {
		animation: none;
		opacity: 0.6;
	}

	.strand-hero-bg {
		display: none;
	}
}
