/* ====================================================
   DIVERGENCE CHART — charts.css
   Colours/fonts reference variables from style.css only.
   ==================================================== */

/* --- Header --- */
.lab-frozen-article .chart-header {
	margin-bottom: 10px;
}
.lab-frozen-article .chart-title {
	margin-top: 5px;
	margin-bottom: 0;
}

/* --- Legend (placed below title, above chart) --- */
.lab-frozen-article .chart-legend-wrapper {
	display: flex;
	gap: 24px;
	margin: 10px 0 16px;
	flex-wrap: wrap;
	align-items: center;
}
.lab-frozen-article .chart-legend-item {
	display: flex;
	align-items: center;
	gap: 8px;
	font-family: 'Inter', 'Arial', sans-serif;
	font-size: 14px;
	font-weight: 500;
	color: var(--white);
}
.lab-frozen-article .chart-legend-line {
	width: 28px;
	height: 2.5px;
	background: var(--white);
	flex-shrink: 0;
	border-radius: 2px;
}
.lab-frozen-article .chart-legend-line.sat {
	background: repeating-linear-gradient(
		90deg,
		var(--grey-25) 0,
		var(--grey-25) 6px,
		transparent 6px,
		transparent 10px
	);
}

/* --- SVG wrapper — enables floating overlays, full width --- */
.lab-frozen-article .chart-svg-wrapper {
	position: relative;
	width: 100%;
}

/* --- Base SVG --- */
.lab-frozen-article .divergence-chart {
	width: 100%;
	display: block;
}

/* --- Grid & axes --- */
.lab-frozen-article .chart-gridline {
	stroke: var(--grey-75);
	stroke-width: 1;
	fill: none;
}
.lab-frozen-article .chart-axis {
	stroke: var(--grey-50);
	stroke-width: 1.5;
	fill: none;
}

/* --- SVG text labels (all white) --- */
.lab-frozen-article .chart-label {
	fill: var(--white);
	font-family: 'Inter', 'Arial', sans-serif;
	font-size: 15px;
	font-weight: 500;
}
.lab-frozen-article .chart-label-right  { text-anchor: end; }
.lab-frozen-article .chart-label-center { text-anchor: middle; }
.lab-frozen-article .chart-label-left   { text-anchor: start; }

/* --- Gap fill --- */
.lab-frozen-article .chart-gap-fill {
	fill: rgba(255, 255, 255, 0.07);
	stroke: none;
	animation: chart-fade-in 1.5s ease-out 1.5s both;
}

/* --- Attacker line: draws in --- */
.lab-frozen-article .chart-line-attacker {
	stroke: var(--white);
	stroke-width: 2.5;
	fill: none;
	stroke-linecap: round;
	stroke-linejoin: round;
	stroke-dasharray: 1;
	stroke-dashoffset: 1;
	animation: chart-draw 2s ease-out 0.3s forwards;
}

/* --- SAT line: fades in after attacker finishes --- */
.lab-frozen-article .chart-line-sat {
	stroke: var(--grey-25);
	stroke-width: 2;
	fill: none;
	stroke-dasharray: 6 4;
	stroke-linecap: round;
	opacity: 0;
	animation: chart-fade-in 0.8s ease-out 2.2s forwards;
}

/* --- Dots --- */
.lab-frozen-article .chart-dot,
.lab-frozen-article .chart-dot-sat {
	transform-box: fill-box;
	transform-origin: center;
}
.lab-frozen-article .chart-dot     { fill: var(--white); }
.lab-frozen-article .chart-dot-sat { fill: var(--grey-25); }

/* staggered appearance matching line draw timing */
.lab-frozen-article .chart-dot-d1     { animation: chart-dot-pulse 0.45s ease-out 1.1s both; }
.lab-frozen-article .chart-dot-d2     { animation: chart-dot-pulse 0.45s ease-out 1.55s both; }
.lab-frozen-article .chart-dot-d3     { animation: chart-dot-pulse 0.45s ease-out 2.0s both; }
.lab-frozen-article .chart-dot-sat-d1 { animation: chart-dot-pulse 0.45s ease-out 2.35s both; }

/* --- ChatGPT annotation --- */
.lab-frozen-article .chart-annotation-line {
	stroke: var(--grey-50);
	stroke-width: 1;
	stroke-dasharray: 4 3;
	opacity: 0;
	animation: chart-fade-in 0.5s ease-out 1.1s forwards;
}
.lab-frozen-article .chart-annotation-text {
	fill: var(--white);
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 15px;
	font-weight: 700;
	text-anchor: start;
	opacity: 0;
	animation: chart-fade-in 0.5s ease-out 1.1s forwards;
}

/* --- 2023 inline annotation (connector + text in gap area) --- */
.lab-frozen-article .chart-callout-line {
	stroke: var(--grey-50);
	stroke-width: 1;
	fill: none;
	opacity: 0;
	animation: chart-fade-in 0.5s ease-out 1.65s forwards;
}
.lab-frozen-article .chart-inline-annotation {
	fill: var(--white);
	font-family: 'Inter', 'Arial', sans-serif;
	/* Was 10px -- unreadable sitting inline against the chart's own
	   gridlines/gap-fill mesh (Finn, 14 Sep 2026). Bumped toward the chart's
	   other label sizes (11-14px) and to 600 weight for contrast. */
	font-size: 15px;
	font-weight: 600;
	text-anchor: middle;
	opacity: 0;
	animation: chart-fade-in 0.5s ease-out 1.65s forwards;
}

/* --- Floating HTML callout labels (overlaid on SVG) --- */
.lab-frozen-article .chart-float {
	position: absolute;
	pointer-events: none;
	opacity: 0;
	animation: chart-fade-in 0.6s ease-out 2.3s forwards;
	text-align: right;
}
.lab-frozen-article .chart-float-attacker {
	top: 2%;
	right: 2%;
	max-width: 390px;
}
.lab-frozen-article .chart-float-sat {
	top: 70%;
	right: 2%;
	max-width: 390px;
}
.lab-frozen-article .chart-float-title {
	margin: 0 0 4px;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 17px;
	font-weight: 700;
	color: var(--white);
	line-height: 1.2em;
}
/* Body copy was 11px/grey-25 -- too faint sitting over the chart's own
   converging lines on the right-hand side (Finn, 14 Sep 2026). Bumped
   size for legibility; kept grey-25 (rgb 126,126,126) rather than grey-50
   (rgb 78,78,78, noticeably dimmer) -- grey-50 reads fine on light panels
   but is close to invisible against this section's black background, per
   the same calibration already used elsewhere in Threat Context. */
/* Raised again to 15px at 75% white (Frank, 1 Oct 2026): no tiny grey
   text on any chart. */
.lab-frozen-article .chart-float-body {
	margin: 0;
	font-family: 'Inter', 'Arial', sans-serif;
	font-size: 15px;
	font-weight: 500;
	color: rgba(255, 255, 255, 0.75);
	line-height: 1.5em;
}

/* --- Mobile: bottom annotation text (in SVG) --- */
.lab-frozen-article .chart-bottom-label {
	fill: var(--white);
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 15px;
	font-weight: 700;
	text-anchor: start;
}
.lab-frozen-article .chart-bottom-label-sat {
	fill: rgba(255, 255, 255, 0.75);
}
.lab-frozen-article .chart-bottom-text {
	fill: rgba(255, 255, 255, 0.75);
	font-family: 'Inter', 'Arial', sans-serif;
	font-size: 14px;
	font-weight: 400;
	text-anchor: start;
}

/* --- Source note ---
   14px at 70% white, up from 12px grey-50: at the old size and contrast
   the note was close to unreadable on the black Threat Context panels,
   the only place it's used (Finn, 30 Sep 2026; same fix as VM's
   .vm-footnote). */
/* Ruled footnote, as on the VM charts (Finn, 2 Oct 2026). */
.lab-frozen-article .chart-source-note {
	max-width: 74ch;
	margin-top: 30px;
	padding-top: 20px;
	border-top: 1px solid rgba(255, 255, 255, 0.2);
	font-family: 'Inter', 'Arial', sans-serif;
	font-size: 14px;
	line-height: 1.6em;
	color: rgba(255, 255, 255, 0.7);
}

/* Ranked rows: label, a to-scale bar on a 0 to 100% track, and the value.
   The lead row is white and largest; the rest sit back at 75% white
   (Finn, 2 Oct 2026, after the VM coverage chart). Each row sets --v. */
.lab-frozen-article .chart-rows {
	margin: 0;
	padding: 0;
	list-style: none;
}
.lab-frozen-article .chart-row {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr) 110px;
	align-items: center;
	gap: 12px 40px;
	padding: 28px 0;
	border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.lab-frozen-article .chart-row:first-child {
	padding-top: 0;
	border-top: 0;
}
.lab-frozen-article .chart-row-label h4 {
	margin: 0 0 6px;
	font-size: 1.1em;
	line-height: 1.25;
	color: rgba(255, 255, 255, 0.85);
}
.lab-frozen-article .chart-row-label p {
	margin: 0;
	font-size: 0.8em;
	line-height: 1.55;
	color: rgba(255, 255, 255, 0.75);
}
.lab-frozen-article .chart-row-track {
	height: 14px;
	border-radius: 7px;
	background: rgba(255, 255, 255, 0.08);
	overflow: hidden;
}
.lab-frozen-article .chart-row-fill {
	display: block;
	width: calc(var(--v) * 1%);
	height: 100%;
	border-radius: 7px;
	background: rgba(255, 255, 255, 0.3);
}
.lab-frozen-article .chart-row-val {
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 1.5em;
	font-weight: 800;
	line-height: 1;
	text-align: right;
	color: rgba(255, 255, 255, 0.75);
}
.lab-frozen-article .chart-row.is-hero h4 {
	color: var(--white);
}
.lab-frozen-article .chart-row.is-hero .chart-row-fill {
	background: var(--white);
}
.lab-frozen-article .chart-row.is-hero .chart-row-val {
	font-size: 2.25em;
	color: var(--white);
}
@media (max-width: 768px) {
	.lab-frozen-article .chart-row {
		grid-template-columns: minmax(0, 1fr) 84px;
		gap: 14px 16px;
	}
	.lab-frozen-article .chart-row.is-hero .chart-row-val {
		font-size: 1.75em;
	}
	.lab-frozen-article .chart-row-label {
		grid-column: 1 / -1;
	}
}

/* --- Desktop / Mobile visibility --- */
.lab-frozen-article .chart-desktop { display: block; }
.lab-frozen-article .chart-mobile  { display: none; }

/* --- Keyframe animations --- */
@keyframes chart-draw {
	to { stroke-dashoffset: 0; }
}
@keyframes chart-fade-in {
	from { opacity: 0; }
	to   { opacity: 1; }
}
@keyframes chart-dot-pulse {
	0%   { opacity: 0; transform: scale(0); }
	60%  { opacity: 1; transform: scale(1.7); }
	100% { opacity: 1; transform: scale(1); }
}

/* --- Mobile breakpoint --- */
@media (max-width: 768px) {
	.lab-frozen-article .chart-desktop        { display: none; }
	.lab-frozen-article .chart-mobile         { display: block; }
	.lab-frozen-article .chart-float          { display: none; }
	.lab-frozen-article .chart-legend-wrapper { margin-bottom: 10px; }
}

/* ====================================================
   DISCLOSURE TIMELINE — axis + event nodes
   ==================================================== */

.lab-frozen-article .disclosure-timeline {
	--dt-axis-color: rgba(255, 255, 255, 0.18);
	--dt-axis-accent: rgba(255, 255, 255, 0.4);
	--dt-node-color: var(--white);
	--dt-drop-color: rgba(255, 255, 255, 0.22);
	--dt-label-color: var(--grey-25);
	--dt-title-color: var(--white);
	--dt-body-color: var(--grey-25);
	--dt-caption-w: 240px;
	--dt-drop-h: 32px;
	--dt-caption-gap: 14px;

	position: relative;
	margin: 8px 0 0;
	padding: 0;
	/* Raised from 360px for the audited captions, which run longer (2 Oct 2026). */
	min-height: 500px;
}

/* --- Axis --- */
.lab-frozen-article .disclosure-timeline__axis {
	position: absolute;
	left: 0;
	right: 0;
	top: 50%;
	height: 1px;
	background: linear-gradient(
		to right,
		transparent 0,
		var(--dt-axis-color) 12px,
		var(--dt-axis-color) calc(100% - 12px),
		transparent 100%
	);
}
/* Axis caps: small labels anchored to the very ends of the axis */
.lab-frozen-article .disclosure-timeline__axis-cap {
	position: absolute;
	top: 14px;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 13px;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, 0.75);
	white-space: nowrap;
	line-height: 1;
}
.lab-frozen-article .disclosure-timeline__axis-cap--start { left: 0; }
.lab-frozen-article .disclosure-timeline__axis-cap--end { right: 0; }

/* "Two patch bypasses" chevron annotation */
.lab-frozen-article .disclosure-timeline__bypass {
	position: absolute;
	top: -16px;
	transform: translateX(-50%);
	display: flex;
	flex-direction: column;
	align-items: center;
	color: rgba(255, 255, 255, 0.55);
	pointer-events: none;
}
.lab-frozen-article .disclosure-timeline__bypass svg {
	display: block;
}
.lab-frozen-article .disclosure-timeline__bypass-label {
	margin-top: 4px;
	font-family: 'Inter', 'Arial', sans-serif;
	font-size: 14px;
	font-weight: 500;
	font-style: italic;
	letter-spacing: 0.01em;
	color: rgba(255, 255, 255, 0.75);
	white-space: nowrap;
	line-height: 1;
}

/* --- Events --- */
.lab-frozen-article .disclosure-timeline__events {
	position: absolute;
	inset: 0;
	list-style: none;
	margin: 0;
	padding: 0;
}
.lab-frozen-article .disclosure-timeline__event {
	position: absolute;
	left: var(--at);
	top: 0;
	bottom: 0;
	width: 0;
}

/* Node: ring + dot — sits on the axis */
.lab-frozen-article .disclosure-timeline__node {
	position: absolute;
	top: 50%;
	left: 0;
	width: 9px;
	height: 9px;
	border-radius: 50%;
	background: var(--dt-node-color);
	transform: translate(-50%, -50%);
	box-shadow: 0 0 0 4px var(--grey-100), 0 0 0 5px rgba(255, 255, 255, 0.35);
	z-index: 2;
}
.lab-frozen-article .disclosure-timeline__node--open {
	background: var(--grey-100);
	box-shadow: 0 0 0 4px var(--grey-100), 0 0 0 5px var(--white);
}

/* Drop hairline from axis to caption */
.lab-frozen-article .disclosure-timeline__event::before {
	content: '';
	position: absolute;
	left: 0;
	width: 1px;
	height: var(--dt-drop-h);
	background: var(--dt-drop-color);
	transform: translateX(-0.5px);
}
.lab-frozen-article .disclosure-timeline__event--above::before {
	bottom: calc(50% + 6px);
}
.lab-frozen-article .disclosure-timeline__event--below::before {
	top: calc(50% + 6px);
}

/* Caption: absolutely anchored to the event x-position; centered by default */
.lab-frozen-article .disclosure-timeline__caption {
	position: absolute;
	left: 0;
	width: var(--dt-caption-w);
	transform: translateX(-50%);
	color: var(--dt-body-color);
}
.lab-frozen-article .disclosure-timeline__event--above .disclosure-timeline__caption {
	bottom: calc(50% + 6px + var(--dt-drop-h) + var(--dt-caption-gap));
	text-align: center;
}
.lab-frozen-article .disclosure-timeline__event--below .disclosure-timeline__caption {
	top: calc(50% + 6px + var(--dt-drop-h) + var(--dt-caption-gap));
	text-align: center;
}

/* Edge alignment so captions don't overflow the panel */
.lab-frozen-article .disclosure-timeline__event[style*="--at: 0%"] .disclosure-timeline__caption {
	transform: translateX(0);
	text-align: left;
}
.lab-frozen-article .disclosure-timeline__event[style*="--at: 100%"] .disclosure-timeline__caption {
	transform: translateX(-100%);
	text-align: right;
}

.lab-frozen-article .disclosure-timeline__date {
	display: block;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 14px;
	font-weight: 800;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--white);
	line-height: 1;
	margin-bottom: 10px;
}
.lab-frozen-article .disclosure-timeline__vendor {
	display: block;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 16px;
	font-weight: 700;
	letter-spacing: -0.015em;
	color: var(--dt-title-color);
	line-height: 1.2em;
	margin-bottom: 6px;
}
.lab-frozen-article .disclosure-timeline__body {
	margin: 0;
	font-family: 'Inter', 'Arial', sans-serif;
	font-size: 15px;
	font-weight: 400;
	color: rgba(255, 255, 255, 0.75);
	line-height: 1.55em;
	letter-spacing: 0.005em;
}

/* --- Mobile: vertical timeline --- */
@media (max-width: 768px) {
	.lab-frozen-article .disclosure-timeline {
		min-height: 0;
		padding: 4px 0 4px 32px;
	}
	.lab-frozen-article .disclosure-timeline__axis {
		position: absolute;
		top: 10px;
		bottom: 10px;
		left: 6px;
		right: auto;
		width: 1px;
		height: auto;
		background: linear-gradient(
			to bottom,
			transparent 0,
			var(--dt-axis-color) 10px,
			var(--dt-axis-color) calc(100% - 10px),
			transparent 100%
		);
	}
	.lab-frozen-article .disclosure-timeline__axis-cap,
	.lab-frozen-article .disclosure-timeline__bypass {
		display: none;
	}
	.lab-frozen-article .disclosure-timeline__events {
		position: static;
		inset: auto;
		display: flex;
		flex-direction: column;
		gap: 26px;
	}
	.lab-frozen-article .disclosure-timeline__event,
	.lab-frozen-article .disclosure-timeline__event--above,
	.lab-frozen-article .disclosure-timeline__event--below {
		position: relative;
		left: 0;
		top: auto;
		bottom: auto;
		width: 100%;
		min-height: 0;
	}
	.lab-frozen-article .disclosure-timeline__event::before {
		display: none;
	}
	.lab-frozen-article .disclosure-timeline__node {
		position: absolute;
		top: 10px;
		left: -26px;
		transform: none;
		width: 9px;
		height: 9px;
		box-shadow: 0 0 0 4px var(--grey-100), 0 0 0 5px rgba(255, 255, 255, 0.35);
	}
	.lab-frozen-article .disclosure-timeline__caption,
	.lab-frozen-article .disclosure-timeline__event--above .disclosure-timeline__caption,
	.lab-frozen-article .disclosure-timeline__event--below .disclosure-timeline__caption,
	.lab-frozen-article .disclosure-timeline__event[style*="--at: 0%"] .disclosure-timeline__caption,
	.lab-frozen-article .disclosure-timeline__event[style*="--at: 100%"] .disclosure-timeline__caption {
		position: relative;
		left: auto;
		right: auto;
		top: auto;
		bottom: auto;
		width: 100%;
		max-width: 100%;
		text-align: left;
		transform: none;
	}
	.lab-frozen-article .disclosure-timeline__date {
		margin-bottom: 8px;
	}
	.lab-frozen-article .disclosure-timeline__vendor {
		font-size: 17px;
		margin-bottom: 4px;
	}
	.lab-frozen-article .disclosure-timeline__body {
		font-size: 15px;
	}
}

/* ====================================================
   REGULATION INFOGRAPHIC
   ==================================================== */

.lab-frozen-article .reg-subtitle {
	margin: 6px 0 24px;
	font-size: 0.9em;
	line-height: 1.4em;
	color: var(--grey-25);
}
.lab-frozen-article .reg-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 10px;
	margin-bottom: 10px;
}
.lab-frozen-article .reg-card {
	background: var(--grey-75);
	border-radius: 20px;
	padding: 30px;
}
.lab-frozen-article .reg-eyebrow {
	display: block;
	margin-bottom: 10px;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 0.7em; /* 14px */
	font-weight: 900;
	letter-spacing: 0.05em;
	line-height: 1em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, 0.75);
}
.lab-frozen-article .reg-stat {
	display: block;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 3em;
	font-weight: 800;
	line-height: 0.95em;
	letter-spacing: -0.04em;
	color: var(--white);
	margin-bottom: 16px;
}
.lab-frozen-article .reg-copy {
	font-size: 0.85em;
	line-height: 1.5em;
	color: var(--white);
	margin: 0 0 10px;
}
.lab-frozen-article .reg-source {
	font-size: 0.65em;
	line-height: 1.4em;
	color: var(--grey-50);
	margin: 0;
}
/* grey-50 above is calibrated for the light outcome panels (same issue as
   .vef-h3-year) -- reg-source only ever actually sits on the dark
   panel-grey-100 reg-card/reg-precedent surfaces, where it was reading as
   near-invisible (Finn, 11 Sep 2026). Override to the same light grey the
   rest of this dark context already uses. */
.lab-frozen-article .reg-card .reg-source,
.lab-frozen-article .reg-precedent .reg-source {
	color: rgba(255, 255, 255, 0.6);
}
.lab-frozen-article .reg-card .reg-source:hover,
.lab-frozen-article .reg-precedent .reg-source:hover {
	color: rgba(255, 255, 255, 0.85);
}

/* Precedent bar */
.lab-frozen-article .reg-precedent {
	background: var(--grey-75);
	border-radius: 20px;
	padding: 30px;
	display: flex;
	align-items: center;
	gap: 30px;
}
.lab-frozen-article .reg-precedent-stat {
	flex-shrink: 0;
	min-width: 120px;
}
.lab-frozen-article .reg-precedent-stat .reg-stat {
	font-size: 3.5em;
	margin-bottom: 0;
}
.lab-frozen-article .reg-precedent-content {
	flex: 1;
}
.lab-frozen-article .reg-precedent-content .reg-copy {
	margin-bottom: 6px;
}

/* 3-column grid at lg and above */
@media (min-width: 992px) {
	.lab-frozen-article .reg-grid {
		grid-template-columns: repeat(3, 1fr);
	}
}

/* Stack precedent bar on smaller screens */
@media (max-width: 991px) {
	.lab-frozen-article .reg-precedent {
		flex-direction: column;
		align-items: flex-start;
		gap: 12px;
	}
	.lab-frozen-article .reg-precedent-stat {
		min-width: unset;
	}
	.lab-frozen-article .reg-precedent-stat .reg-stat {
		font-size: 3em;
	}
}

/* --- Product Landscape intro type-scale + methodology reposition
   (density pass, 11 Sep 2026) ---
   Body text is 500-weight system-wide, not 400 -- semibold (600) promotes
   this above plain body copy without reaching the lede's bold (700); white
   (not a muted grey) matches on-dark surfaces, since a grey token
   calibrated for light panels reads as too low-contrast here. */
.lab-frozen-article .vef-intro-support {
	margin-top: var(--space-30);
	margin-bottom: 0;
	font-family: 'Inter', 'Arial', sans-serif;
	font-size: 1em;
	font-weight: 600;
	color: var(--white);
	letter-spacing: normal;
}
/* Sits directly under the h2, which already carries its own pad-b-50 --
   stacking .section-intro's usual space-30 on top would double the gap. */
.lab-frozen-article h2.pad-b-50 + .vef-intro-support {
	margin-top: 0;
}
.lab-frozen-article .vef-intro-support .text-white {
	font-weight: 700;
}
/* Trims a .section-intro down to the same 1.5em weight as the outcome-card
   h4 labels (Efficacy etc.) below it, rather than the default 2em -- used
   where the intro is making a denser, more argumentative point than a
   simple lead-in question. */
.lab-frozen-article .vef-intro-compact {
	font-size: 1.5em;
}

/* Methodology footnote below the continuum -- fine print, not a second
   lede. rgba(255,255,255,0.6) is the existing on-black muted-text value
   this codebase already uses elsewhere, rather than a grey-* token, which
   the rule above already flags as too low-contrast on this dark surface. */
.lab-frozen-article .vef-methodology-note {
	margin: 0;
	font-family: 'Inter', 'Arial', sans-serif;
	font-size: 0.8em;
	font-weight: 400;
	line-height: 1.5;
	color: rgba(255, 255, 255, 0.6);
	letter-spacing: normal;
}
.lab-frozen-article .vef-methodology-note strong {
	color: rgba(255, 255, 255, 0.85);
	font-weight: 600;
}

/* --- Collapsible regulatory cards (SAT density pass, 11 Sep 2026) ---
   Cards collapse to eyebrow + stat by default; expanding reveals the
   subcopy. .reg-eyebrow/.reg-stat are already display:block, so wrapping
   them in <summary> keeps the existing vertical stack; the toggle icon is
   positioned absolutely so it doesn't force them onto one row. */
.lab-frozen-article .reg-card,
.lab-frozen-article .reg-precedent {
	position: relative;
}
.lab-frozen-article .vef-reg-toggle summary {
	cursor: pointer;
	list-style: none;
}
.lab-frozen-article .vef-reg-toggle summary::-webkit-details-marker {
	display: none;
}
.lab-frozen-article .vef-reg-toggle-icon {
	position: absolute;
	top: 25px;
	right: 25px;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 20px;
	font-weight: 400;
	color: var(--grey-25);
}
.lab-frozen-article .vef-reg-toggle-icon::before {
	content: '+';
}
.lab-frozen-article .vef-reg-toggle[open] .vef-reg-toggle-icon::before {
	content: '\2212';
}
.lab-frozen-article .vef-reg-toggle:not([open]) .reg-copy,
.lab-frozen-article .vef-reg-toggle:not([open]) .reg-source,
.lab-frozen-article .vef-reg-toggle:not([open]) .reg-precedent-content {
	display: none;
}

/* Precedent cards: .reg-precedent is a flex row pairing .reg-precedent-stat
   with .reg-precedent-content -- fine when both are always visible, but the
   content is now inside <details> and hidden by default, so the row moves
   onto <summary> (stat + toggle icon) and the content stacks below when
   opened, rather than forcing an empty flex track. Scoped to :has() the
   toggle variant specifically -- the bare selector here previously matched
   EVERY .reg-precedent site-wide, including Email's plain (non-toggle)
   precedent cards, which broke their flex layout entirely and collapsed
   the gap between stat and copy to zero (Finn, 11 Sep 2026). */
.lab-frozen-article .reg-precedent:has(.vef-reg-toggle--precedent) {
	display: block;
}
.lab-frozen-article .vef-reg-toggle--precedent summary {
	display: flex;
	align-items: center;
	gap: 30px;
}
.lab-frozen-article .vef-reg-toggle--precedent .reg-precedent-content {
	margin-top: 16px;
}
/* The site's own CSS gives .reg-grid a margin-bottom but never gave two
   stacked .reg-precedent cards any spacing between each other -- with a
   second precedent card added here (Oxfam), they sat flush together
   (Finn, 11 Sep 2026). Matches the 10px rhythm .reg-grid already uses. */
.lab-frozen-article .reg-precedent + .reg-precedent {
	margin-top: 10px;
}

/* Stat cards unboxed onto top rules, matching the VM charts (Finn, 2 Oct
   2026): grey boxes inside a grey panel read as filler. Each stat now sits
   under a hairline, with the gap doing the separating. */
.lab-frozen-article .reg-grid {
	gap: 40px;
	margin-bottom: 40px;
}
.lab-frozen-article .reg-card,
.lab-frozen-article .reg-precedent {
	padding: 24px 0 0;
	background: none;
	border-radius: 0;
	border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.lab-frozen-article .reg-precedent + .reg-precedent {
	margin-top: 40px;
}
/* A long word stat ("Untested") is wider than a narrow card at 3em, so
   the figure caps at its card width. */
.lab-frozen-article .reg-card {
	container-type: inline-size;
}
.lab-frozen-article .reg-card .reg-stat {
	font-size: min(3em, 22cqi);
}
.lab-frozen-article .vef-reg-toggle-icon {
	top: 22px;
	right: 0;
}

/* Two verdict lines bookending the regulatory grid, pulled out of the
   paragraph flow into a standalone, ruled statement. */
.lab-frozen-article .vef-reg-stamp {
	border-top: 2px solid rgba(255, 255, 255, 0.25);
	border-bottom: 2px solid rgba(255, 255, 255, 0.25);
	padding: 20px 0;
	margin: 0;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 1.3em;
	font-weight: 700;
	letter-spacing: -0.01em;
	line-height: 1.3;
}

/* --- Threat Context density pass: two-column editorial prose (Finn, 14 Sep 2026) ---
   The section was prose-dominant: ~12 body paragraphs against 2 infographic
   panels, all sharing the same 800px left edge, so the graphics read as
   slightly-wider text rows rather than set-pieces. Flowing the three pure-prose
   blocks into two columns roughly halves their height and reads as premium
   editorial, while the widened panels below out-dent the text on both edges.
   Applied ONLY to the pure multi-paragraph blocks -- NOT the ruled
   .vef-reg-stamp statements or the stat panels (multi-column would split their
   borders/grids). 30px gap = the design system's space-30.
   Gated at 992px: below it the prose band is already full-width and stacks; a
   2-col split there would collapse to ~150px columns (unreadable). */
@media (min-width: 992px) {
	.lab-frozen-article .threat-prose-2col {
		column-count: 2;
		column-gap: 50px; /* space-50 -- wider gutter so the two columns read as distinct */
		/* space-20 above and below the denser 2-col block. Block padding only:
		   the old shorthand zeroed the column's 12px gutter, so the block sat
		   12px left of the headings around it (Finn, 6 Oct 2026). */
		padding-top: 20px;
		padding-bottom: 20px;
	}
	.lab-frozen-article .threat-prose-2col > h3 {
		column-span: all; /* sub-head spans both columns, prose flows beneath */
		/* 20px read as too tight under a 60px h3 -- the heading's own weight
		   needs more breathing room before the lighter 17px column text
		   starts (Finn, 14 Sep 2026). */
		margin-bottom: 50px; /* space-50 */
	}
	.lab-frozen-article .threat-prose-2col > p:not(.section-quote):not(.section-intro) {
		break-inside: avoid; /* never split a paragraph across the column gap */
		/* Drop to the infographic body-copy size (17px, matching .bar-content /
		   .reg-copy) so the two-column editorial prose reads lighter than the
		   20px single-column body. Scoped to > p so the em-based h3 keeps 60px. */
		font-size: 0.85em; /* 17px */
		line-height: 1.6em; /* 27.2px -- comfortable measure in a ~375px column */
	}
	.lab-frozen-article .threat-prose-2col > p:first-of-type {
		margin-top: 0;
	}
}

/* The two-column prose outside Threat Context (SAT Foreword and Conclusion
   first, Finn, 6 Oct 2026): the section heading and the Foreword's author
   line span both columns, like the h3. */
@media (min-width: 992px) {
	.lab-frozen-article .threat-prose-2col > h2,
	.lab-frozen-article .threat-prose-2col > .section-quote,
	.lab-frozen-article .threat-prose-2col > .section-intro,
	.lab-frozen-article .threat-prose-2col > .author-details {
		column-span: all;
	}
	.lab-frozen-article .threat-prose-2col > .section-quote,
	.lab-frozen-article .threat-prose-2col > .section-intro {
		margin-bottom: 50px; /* space-50, as under the h3 */
	}
	.lab-frozen-article .threat-prose-2col > ul,
	.lab-frozen-article .threat-prose-2col > ol {
		break-inside: avoid;
	}
	.lab-frozen-article .threat-prose-2col > h2 {
		margin-bottom: 30px; /* space-30 */
	}
}

/* Threat Context's closing summary, set in the stamp statements' type
   (.vef-reg-stamp: Inter Tight, 26px, bold) without their rules, so the
   section ends on a statement rather than a footnote-sized line (Finn,
   6 Oct 2026). */
.lab-frozen-article .threat-summary p {
	margin: 0;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 1.3em;
	font-weight: 700;
	letter-spacing: -0.01em;
	line-height: 1.3;
}

/* Hero'd infographic panels break wider than the 800px prose band (markup:
   col-xxl-10 offset-xxl-1, ~1333px, out-denting the text both sides). Give the
   larger footprint room to breathe: 50px pad = space-50, up from the base 30px.
   Panel's own responsive padding at narrow widths is left to the base rules. */
@media (min-width: 992px) {
	.lab-frozen-article .threat-hero-panel .panel {
		padding: 50px; /* space-50 */
	}
}

/* Single-column Threat Context prose, sized to match the infographic body
   copy (.bar-content / .reg-copy, both 17px) rather than the 20px article
   default -- keeps the whole dense Threat Context section reading as one
   consistent voice instead of jumping between 20px and 17px block to block.
   Unconditional (not gated to >=992px): the infographics themselves are 17px
   at every width, so this stays consistent on mobile too (Finn, 14 Sep 2026). */
.lab-frozen-article .threat-prose-tight p {
	font-size: 0.85em; /* 17px */
	line-height: 1.6em; /* 27.2px */
}

/* Wide Threat Context (SAT first, Finn, 6 Oct 2026). The section's text
   column now matches the embedded videos and the lead-gen close
   (col-xl-10 col-xxl-8), so headings and the two-column prose get room.
   A lone paragraph would run past a comfortable measure at that width,
   so single-column prose keeps one; the two-column blocks need no cap. */
.lab-frozen-article #section-threat .col-xl-10:not(.threat-prose-2col) > p:not(.section-intro),
.lab-frozen-article #section-threat .threat-prose-tight p {
	max-width: 40em;
}

/* .threat-split (an explicit flex 2-column pairing) was tried here for
   "Where legacy filters fall short" and reverted (14 Sep 2026): its
   align-items:center floated the shorter column mid-way down, unaligned
   with the top of its partner, which read as a layout bug rather than an
   intentional pairing. CSS multi-column (.threat-prose-2col, used
   throughout this section) is the right tool instead -- its columns are
   always top-aligned by construction (there is no way to centre one), and
   it auto-balances by rendered height regardless of paragraph count, so it
   scales to any domain's Threat Context copy without per-block manual
   authoring. Rolled out on that basis: every 2+ paragraph prose block in
   Threat Context uses .threat-prose-2col; a lone paragraph uses
   .threat-prose-tight (nothing to split). See email/browser/SAT Threat
   Context for the applied pattern. */

/* Small-source-size year, sat inline in an h3 rather than as a sup. grey-25,
   not grey-50 -- grey-50 is calibrated for text on the light outcome panels
   and is nearly invisible against this section's black background; grey-25
   matches .text-grey already used elsewhere in this dark section. */
.lab-frozen-article .vef-h3-year {
	font-size: 0.4em;
	font-weight: 400;
	color: var(--grey-25);
	letter-spacing: normal;
}

/* --- Collapsible Security Outcomes cards (SAT density pass, 11 Sep 2026) ---
   Outcome boxes collapse to just the number + heading; expanding reveals
   the associated outcomes as a dot-point list. */
/* Bootstrap's .row is a flex container with the default align-items:
   stretch, so a collapsed card sitting next to an expanded one on the same
   line gets stretched to match its taller neighbour, leaving dead space
   below the collapsed heading (Finn, 11 Sep 2026). Default to flex-start
   so a card's height always matches its own content. */
.lab-frozen-article .vef-outcome-row {
	align-items: flex-start;
}
/* ...but when both cards in the row are in the SAME state (both open or
   both collapsed), restore the stretch so full expansion doesn't leave a
   ragged bottom edge when the two cards hold a different number of bullets
   (Finn, 11 Sep 2026). Markup pairs exactly two cards per .vef-outcome-row,
   so :has() here always resolves per-pair, never across the whole grid. */
.lab-frozen-article .vef-outcome-row:not(:has(details:not([open]))),
.lab-frozen-article .vef-outcome-row:not(:has(details[open])) {
	align-items: stretch;
}
.lab-frozen-article .vef-outcome-toggle summary {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 10px;
	cursor: pointer;
	list-style: none;
}
.lab-frozen-article .vef-outcome-toggle summary::-webkit-details-marker {
	display: none;
}
.lab-frozen-article .vef-outcome-toggle summary h4 {
	margin-bottom: 0;
}
/* The base h4 margin-bottom fix doesn't reach here: this h4 sits inside
   <summary> (margin-bottom zeroed above so the toggle icon aligns cleanly on
   the flex row), and the revealed content in the "Where legacy filters fall
   short" pattern cards is a plain <p>, not a .vef-outcome-list <ul> -- so it
   never picked up that list's own margin-top: 12px. Without this, expanding
   one of those cards (e.g. "Mid-Campaign Defence Response") left the
   paragraph sitting flush under the heading (Finn, 14 Sep 2026). */
.lab-frozen-article .vef-outcome-toggle summary + p {
	margin-top: 12px;
}
/* Same author-origin override that breaks native details-closed hiding for
   .vef-outcome-list (see below) also hides the plain-<p> reveal case here --
   confirmed live on the already-shipped four-pattern cards, the paragraph
   stayed visible even with the details closed. Force it explicitly, same as
   the list case. */
.lab-frozen-article .vef-outcome-toggle:not([open]) summary + p {
	display: none;
}
.lab-frozen-article .vef-outcome-toggle[open] summary + p {
	display: block;
}
/* The Addendum FAQ reuses .vef-outcome-toggle bare (no card grid, no
   .panel gutter around it), so its items had no spacing of their own --
   sat flush against each other and against "Disclaimers" below (Finn,
   14 Sep 2026). Grid-based uses (Security Outcomes, four-patterns) don't
   need this: Bootstrap's row/col gutters already separate those.
   20px, the site's --space-20 step, not an arbitrary value. */
.lab-frozen-article #section-addendum .vef-outcome-toggle {
	margin-bottom: 20px;
}
/* Design-system review (14 Sep 2026): h4 (30px/700, the base rule above)
   is the site's only sub-heading size, so reusing it unchanged for each
   FAQ question collapsed two hierarchy levels: "Further questions" (a
   section label, peer of Scope/Feature Granularity Model/Disclaimers)
   read identically to the individual questions nested under it. Demotes
   the question text to body size (20px, still bold) so it reads as
   content within "Further questions" rather than another section label.
   (Note: the sibling body paragraphs were 20px when this was written; they
   were subsequently dropped to 17px below -- the question text stays at
   20px so it still outranks its own 17px answer.) Card-grid uses of
   .vef-outcome-toggle (Security Outcomes, four-patterns) keep the full h4
   size -- those really are card headings, not nested list items. */
.lab-frozen-article #section-addendum .vef-outcome-toggle summary h4 {
	font-size: 20px;
}

/* Addendum density pass (Finn, 14 Sep 2026): the "Email Security category"
   block (Scope / Feature Granularity Model / Further questions / Disclaimers)
   had no separation between one sub-section's closing paragraph and the next
   h4 label -- both relied on the paragraph's own 16px margin-bottom (h4's own
   margin-top is 0), so it read as one continuous flow with no sub-section
   boundary. margin-top on the h4 itself gives each sub-section a clean break
   from the one above, on the space-50 step. */
.lab-frozen-article #section-addendum .addendum-category > h4 {
	margin-top: 50px;
}
/* Same pass: body copy (Scope/FGM/Disclaimers paragraphs, and especially the
   FAQ answers) drops from the 20px article default to 17px, matching the
   infographic body-copy size used elsewhere (.bar-content/.reg-copy) and the
   Threat Context prose. The FAQ question h4 above stays at 20px, so it still
   outranks its own answer -- same "label out-ranks its content" principle as
   the h3/h4 nest one level up. */
.lab-frozen-article #section-addendum .addendum-category p {
	font-size: 0.85em; /* 17px */
	line-height: 1.6em;
}
.lab-frozen-article .vef-outcome-toggle-icon {
	flex-shrink: 0;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 20px;
	font-weight: 400;
	color: var(--grey-50);
}
/* grey-50 above is calibrated for the light outcome panels -- this same icon
   is reused bare in the Addendum FAQ, which sits directly on bkg-black,
   where it drops under WCAG's 3:1 non-text contrast floor. Same fix as the
   reg-source override above: translucent white for the dark context. */
.lab-frozen-article #section-addendum .vef-outcome-toggle-icon {
	color: rgba(255, 255, 255, 0.6);
}
.lab-frozen-article .vef-outcome-toggle-icon::before {
	content: '+';
}
.lab-frozen-article .vef-outcome-toggle[open] .vef-outcome-toggle-icon::before {
	content: '\2212';
}
.lab-frozen-article .vef-outcome-list {
	margin: 12px 0 0;
	padding-left: 18px;
}
/* Body size, not fine print: the expanded points are the content of the
   card (Frank, 1 Oct 2026). */
.lab-frozen-article .vef-outcome-list li {
	font-size: 1em;
	line-height: 1.6;
	color: var(--black);
	margin-bottom: 6px;
}
.lab-frozen-article .vef-outcome-list li:last-child {
	margin-bottom: 0;
}
/* Something upstream overrides the browser's native details-closed hiding
   (an author-origin rule always beats the UA stylesheet's
   details:not([open]) > * rule, regardless of specificity), so the
   closed/open state is forced explicitly here instead of relying on the
   native behaviour. */
.lab-frozen-article .vef-outcome-toggle:not([open]) .vef-outcome-list {
	display: none;
}
.lab-frozen-article .vef-outcome-toggle[open] .vef-outcome-list {
	display: block;
}

/* ====================================================
   VENDOR ENQUIRY FEATURE — ported from the proven prototype
   (live-article-proof-sat.html, confirmed working against the real
   HubSpot form 4 Sep 2026) into production for SAT, 14 Sep 2026.
   Only the genuinely new rules from that prototype are here -- it also
   contained an inline <style> block re-listing several .vef-* rules
   (.vef-reg-stamp, .vef-outcome-toggle, .vef-intro-support,
   .vef-methodology-note, .vef-h3-year, .vef-reg-toggle) that ALREADY exist
   above, refined further since the prototype was frozen -- skipped
   deliberately, not overwritten.
   ==================================================== */

/* Vendor selection affordance -- desktop/tablet rows (.prod-list li) and
   mobile tiles (the liquidGlass continuum icons), both toggled by the same
   JS (see the vendor-enquiry <script> block at the end of this article's
   own HTML). Vendor identity is read from a li's own text content / the
   matching tile's icon src -- no data-vendor attribute needed, so no
   markup change to the vendor list itself. */
.lab-frozen-article .prod-list li {
	cursor: pointer;
	transition: opacity 0.12s ease;
}
.lab-frozen-article .prod-list li:hover {
	opacity: 0.75;
}
.lab-frozen-article .prod-list li.vef-selected {
	background: var(--grey-10);
	color: var(--black);
	border-radius: 9999px;
	padding: 0 14px;
}
.lab-frozen-article .prod-list li.vef-selected .prod-list-icon {
	background: transparent;
}
.lab-frozen-article .prod-list li.vef-selected .prod-list-icon img {
	filter: invert(1);
}
.lab-frozen-article .liquidGlass-wrapper.prod-icon {
	cursor: pointer;
	transition: opacity 0.12s ease;
}
.lab-frozen-article .liquidGlass-wrapper.prod-icon:hover {
	opacity: 0.85;
}
.lab-frozen-article .liquidGlass-wrapper.prod-icon.vef-selected {
	outline: 2px solid var(--white);
	outline-offset: 2px;
}
.lab-frozen-article .liquidGlass-wrapper.prod-icon.vef-selected .liquidGlass-content img {
	filter: invert(1);
}

/* "Make an Enquiry" capture modal -- dark palette regardless of the
   article's own section theming, since it's a fixed overlay above
   everything. Not scoped to .lab-frozen-article: it's a fixed-position
   scrim that sits above the whole page, same reasoning as not scoping
   any other fixed/overlay component in this codebase. */
.vef-modal-scrim {
	position: fixed;
	inset: 0;
	z-index: 2000;
	background: rgba(0, 0, 0, 0.55);
	backdrop-filter: blur(4px);
	display: none;
	align-items: center;
	justify-content: center;
	padding: 20px;
}
.vef-modal-scrim.vef-modal-open {
	display: flex;
}
.vef-modal {
	position: relative;
	width: 100%;
	max-width: 420px;
	max-height: calc(100vh - 40px);
	max-height: calc(100dvh - 40px);
	overflow-y: auto;
	overscroll-behavior: contain;
	background: #141414;
	border-radius: 20px;
	padding: 28px 26px;
	box-shadow: 0 30px 60px rgba(0, 0, 0, 0.45);
}
.vef-modal-close {
	position: absolute;
	top: 16px;
	right: 16px;
	width: 26px;
	height: 26px;
	border-radius: 9999px;
	background: var(--black);
	color: var(--white);
	border: none;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 14px;
	font-weight: 700;
	cursor: pointer;
	padding: 0;
	transition: opacity 0.12s ease;
}
.vef-modal-close:hover {
	opacity: 0.65;
}
/* The right padding keeps the pills clear of the close button. */
.vef-modal-pills {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin: 0 0 18px;
	padding-right: 34px;
}
.vef-modal-pills .vef-sl-pill {
	background: var(--grey-10);
	color: var(--black);
}
/* Scoped under .vef-modal because the modal sits inside
   .lab-frozen-article, whose h3 rule (60px, 40px on mobile) otherwise
   outranks a bare class and sets these headings at article size. */
.vef-modal .vef-modal-heading {
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-weight: 700;
	font-size: 32px;
	line-height: 1.05;
	letter-spacing: -0.03em;
	text-wrap: balance;
	color: var(--white);
	margin: 0 0 12px;
}
.vef-modal #vef-modal-confirm-heading {
	font-size: 28px;
}
@media (max-width: 575px) {
	.vef-modal .vef-modal-heading {
		font-size: 28px;
	}
	.vef-modal #vef-modal-confirm-heading {
		font-size: 26px;
	}
}
.vef-modal-copy {
	color: #d0d2d5;
	font-size: 14px;
	line-height: 1.55;
	margin: 0 0 16px;
}
.vef-modal-copy strong {
	color: var(--white);
}
.vef-modal-form {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
}
.vef-modal-input {
	flex: 1 1 150px;
	background: #2a2a2a;
	border: 1px solid transparent;
	border-radius: 9999px;
	padding: 12px 16px;
	color: var(--white);
	font-size: 14px;
	line-height: 20px;
	font-family: 'Inter', 'Arial', sans-serif;
	min-width: 0;
}
.vef-modal-input::placeholder {
	color: var(--grey-25);
}
.vef-modal-input.vef-input-error {
	border-color: var(--white);
	border-width: 2px;
}
.vef-modal-input:disabled {
	opacity: 0.6;
}
/* Number of employees. A native select, styled as the other pill inputs,
   with its own chevron. Until a band is chosen the required select is
   :invalid and reads in the placeholder grey. */
.vef-modal-select {
	appearance: none;
	-webkit-appearance: none;
	padding-right: 40px;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 16px center;
	cursor: pointer;
}
.vef-modal-select:invalid {
	color: var(--grey-25);
}
.vef-modal-select option {
	background: #2a2a2a;
	color: var(--white);
}
.vef-modal-submit {
	flex: 1 1 150px;
	background: var(--white);
	color: var(--black);
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-weight: 700;
	font-size: 14px;
	line-height: 20px;
	border: none;
	border-radius: 9999px;
	padding: 12px 18px;
	cursor: pointer;
}
.vef-modal-submit:disabled {
	background: #3a3a3a;
	color: #8a8a8a;
	cursor: default;
}
.vef-modal-error {
	display: flex;
	align-items: center;
	gap: 10px;
	color: var(--white);
	font-size: 12.5px;
	margin: 10px 0 0;
}
.vef-modal-error-icon {
	flex: 0 0 auto;
}
.vef-modal-error[hidden] {
	/* The class above sets display:flex, which outranks the user agent's
	   [hidden] { display: none } on specificity. Without this the empty
	   error row, and its icon, sit under every form all the time. */
	display: none;
}
.vef-modal .vef-modal-reassure,
.vef-modal .vef-modal-notice {
	color: #d0d2d5;
	font-size: 13px;
	line-height: 1.5;
	margin: 12px 0 0;
}
/* The consent notice for research emails (2 Oct 2026). It must stay
   readable and next to the button, so it is not fine print. */
.vef-modal .vef-modal-notice {
	margin-top: 6px;
}
.vef-modal .vef-modal-notice a {
	color: var(--white);
	text-decoration: underline;
	text-underline-offset: 2px;
}
.vef-modal-success-icon {
	width: 36px;
	height: 36px;
	border-radius: 9999px;
	background: var(--white);
	color: var(--black);
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 800;
	font-size: 16px;
	margin: 0 0 14px;
}
/* Make an enquiry: a spinner while sending, then white with a check and
   "Enquiry sent" once Form 1 is accepted. The sent state overrides the
   grey :disabled look, since the button stays disabled until the window
   moves on. Motion is only added for readers who have not asked for less
   of it; for everyone else the states simply swap. */
.vef-modal-submit.vef-submit-sending,
.vef-modal-submit.vef-submit-sent {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
}
.vef-modal-submit.vef-submit-sent,
.vef-modal-submit.vef-submit-sent:disabled {
	background: var(--white);
	color: var(--black);
}
.vef-submit-spinner {
	width: 14px;
	height: 14px;
	border-radius: 9999px;
	border: 2px solid currentColor;
	border-top-color: transparent;
	flex: 0 0 auto;
}
.vef-submit-check {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 20px;
	height: 20px;
	border-radius: 9999px;
	background: var(--black);
	color: var(--white);
	flex: 0 0 auto;
}
@media (prefers-reduced-motion: reduce) {
	.vef-submit-spinner {
		display: none;
	}
}
@media (prefers-reduced-motion: no-preference) {
	.vef-modal-submit {
		transition: background-color 0.2s ease, color 0.2s ease;
	}
	.vef-submit-spinner {
		animation: vef-spin 0.8s linear infinite;
	}
	.vef-submit-check,
	.vef-modal-step .vef-modal-success-icon {
		animation: vef-pop 0.28s cubic-bezier(0.2, 0.9, 0.3, 1.3) both;
	}
	.vef-modal-step:not([hidden]) {
		animation: vef-step-in 0.2s ease both;
	}
}
@keyframes vef-spin {
	to { transform: rotate(360deg); }
}
@keyframes vef-pop {
	from { transform: scale(0.6); opacity: 0; }
	to { transform: scale(1); opacity: 1; }
}
@keyframes vef-step-in {
	from { opacity: 0; }
	to { opacity: 1; }
}
/* Each view takes focus when it appears so a screen reader reads it
   from the top. The view itself is not interactive, so it draws no ring. */
.vef-modal-step:focus {
	outline: none;
}
/* Findings / Build-a-shortlist toggle -- two DOM copies exist on the page
   (above the Cyber Continuum, and inside the Consultation CTA card
   further down); both stay in sync via the shared [data-vef-seg]
   attribute the JS selects on, not a shared id. */
.vef-split-pill {
	display: flex;
	align-items: stretch;
	justify-content: center;
	background: rgba(0, 0, 0, 0.06);
	border-radius: 9999px;
	padding: 6px;
	gap: 6px;
	max-width: 480px;
	margin: 0 auto 20px;
}
.vef-split-seg {
	-webkit-appearance: none;
	appearance: none;
	flex: 1;
	border: none;
	background: transparent;
	color: var(--black);
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-weight: 700;
	font-size: 15px;
	border-radius: 9999px;
	padding: 14px 18px;
	line-height: 1.1;
	cursor: pointer;
	text-align: center;
	transition: background 0.15s ease, color 0.15s ease;
}
.vef-split-seg.vef-split-active {
	background: var(--black);
	color: var(--white);
}
/* Dark-mode variant, for the copy of this toggle sitting on a bkg-black
   section (the base rule's rgba(0,0,0,0.06) fill and black text are built
   for a light/bkg-grey ground and disappear on black). */
.vef-split-pill--dark {
	background: rgba(255, 255, 255, 0.12);
}
.vef-split-pill--dark .vef-split-seg {
	color: var(--white);
}
.vef-split-pill--dark .vef-split-seg.vef-split-active {
	background: var(--white);
	color: var(--black);
}
/* Sits inline in the Product Landscape intro column, left-aligned with the
   paragraphs above it, not centred like the base component's own margin. */
#vef-split-pill-top {
	margin: 30px 0 0; /* space-30 */
	max-width: 360px;
}
/* Consultation CTA's own copy of the toggle: moved above the heading and
   left-aligned to the card, same reasoning as #vef-split-pill-top above --
   the base .vef-split-pill's centred margin read as floating, disconnected
   from the card's own left-aligned heading/copy/button (Finn, 14 Sep 2026). */
.vef-split-cta .vef-split-pill {
	margin: 0 0 20px;
	max-width: 360px;
}

/* Shortlist bar content -- replaces the sticky-cta's usual subscribe form
   while a shortlist is active, same flex rhythm and pill dimensions as
   the rest of the sticky bar. */
.lab-frozen-article .vef-shortlist-content {
	display: none;
	padding: 6px 6px 6px 16px;
	align-items: center;
	gap: 6px;
	justify-content: flex-end;
}
/* Production's own @media (max-width: 992px) switches .sticky-cta to
   full-width/flush-left with no centering transform -- matching that exact
   breakpoint avoids fighting it; below 992px the bar just inherits
   width:100%/left:0, which is the right call anyway (more room for the
   pill row on tablet/mobile). */
@media (min-width: 993px) {
	.lab-frozen-article .sticky-cta.vef-shortlist-mode {
		max-width: 480px;
	}
}
.lab-frozen-article .sticky-cta.vef-shortlist-mode .sticky-cta-content {
	display: none;
}
.lab-frozen-article .sticky-cta.vef-shortlist-mode .vef-shortlist-content {
	display: flex;
}
.vef-sl-clear {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 22px;
	height: 22px;
	background: var(--black);
	border: none;
	border-radius: 9999px;
	color: var(--white);
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 13px;
	font-weight: 700;
	line-height: 1;
	cursor: pointer;
	padding: 0;
	transition: opacity 0.12s ease;
}
.vef-sl-clear:hover {
	opacity: 0.65;
}
.vef-sl-names {
	flex: 1;
	min-width: 0;
	display: flex;
	align-items: center;
	gap: 6px;
	overflow-x: auto;
	scrollbar-width: none;
	-ms-overflow-style: none;
	cursor: grab;
}
.vef-sl-names::-webkit-scrollbar {
	display: none;
}
.vef-sl-names.vef-dragging {
	cursor: grabbing;
}
.vef-sl-pill {
	flex: 0 0 auto;
	background: var(--grey-10);
	color: var(--black);
	border-radius: 9999px;
	padding: 0 12px;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 13px;
	font-weight: 600;
	line-height: 28px;
	white-space: nowrap;
}
/* Removable shortlist pill. There is no removable-chip pattern in the
   Tekspace design system, so this is a small extension, not a settled
   component. Deliberately minimal: a bare x in the pill's own text colour,
   quieter at rest and full strength on hover, in a 20px box to sit with
   the 22px Clear (x) on the same bar. Clear stays as the fallback for
   emptying the shortlist in one go. */
.vef-sl-pill-removable {
	display: inline-flex;
	align-items: center;
	gap: 2px;
	padding: 0 4px 0 12px;
}
.vef-sl-pill-remove {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 20px;
	height: 20px;
	padding: 0;
	border: none;
	border-radius: 9999px;
	background: transparent;
	color: inherit;
	cursor: pointer;
	opacity: 0.45;
	transition: opacity 0.12s ease;
}
.vef-sl-pill-x {
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 15px;
	font-weight: 500;
	line-height: 1;
}
.vef-sl-pill-remove:hover,
.vef-sl-pill-remove:focus-visible {
	opacity: 1;
}
.vef-sl-pill-remove:focus-visible {
	outline: 2px solid var(--black);
	outline-offset: 0;
}
/* Limit reached: a one-second ring on each pill, pointing at the remove
   buttons. Inset, because .vef-sl-names scrolls and would clip an outer
   ring, and an inset shadow cannot shift layout. No animation at all under
   reduced motion; the limit message carries it alone. */
@media (prefers-reduced-motion: no-preference) {
	.vef-sl-names.vef-sl-pulse .vef-sl-pill {
		animation: vef-sl-pill-pulse 1s ease-out;
	}
}
@keyframes vef-sl-pill-pulse {
	0% { box-shadow: inset 0 0 0 0 transparent; }
	25% { box-shadow: inset 0 0 0 2px var(--black); }
	100% { box-shadow: inset 0 0 0 2px transparent; }
}
/* Screen-reader-only text, for the shortlist live regions. */
.vef-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;
}
.vef-sl-empty {
	flex: 0 0 auto;
	color: var(--grey-50);
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 14px;
	font-weight: 600;
	white-space: nowrap;
}
.vef-sl-send {
	flex: 0 0 auto;
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 14px;
	font-weight: 700;
	line-height: 40px;
	padding: 0 18px;
	color: var(--white);
	background: var(--black);
	border: none;
	border-radius: 9999px;
	text-decoration: none;
	display: inline-block;
	transition: opacity 0.12s ease;
}
.vef-sl-send.vef-disabled {
	opacity: 0.4;
	pointer-events: none;
}

/* Staging-only test strip (vendor-enquiry.js renderStagingStrip). Never
   rendered on tekspace.com.au. Dashed, so it reads as scaffolding rather
   than part of the article. 1 Oct 2026. */
.lab-frozen-article .vef-staging-strip {
	margin: 30px 0 0; /* space-30 */
	max-width: 480px;
	padding: 20px; /* space-20 */
	border: 1px dashed rgba(255, 255, 255, 0.5);
	border-radius: 10px; /* radius-md */
}
.lab-frozen-article .vef-staging-title {
	font-weight: 700;
	font-size: 15px;
	margin: 0 0 10px; /* space-10 */
	color: var(--white);
}
.lab-frozen-article .vef-staging-copy {
	font-size: 15px;
	margin: 0 0 20px; /* space-20 */
	color: rgba(255, 255, 255, 0.7);
}
.lab-frozen-article .vef-staging-copy strong {
	color: var(--white);
}

/* ---------- ONE-SCROLL TYPE SCALE (SAT preview, Finn, 6 Oct 2026) ----------
   One set of type steps for the whole article, so the Foreword, Threat
   Context, Outcomes, Landscape, Conclusion and Addendum read as one scroll
   instead of each module carrying its own sizes. Opt-in per article with
   .lab-type-scale on the wrapper; SAT first.

   Step        desktop   mobile   used for
   Display     170px     64px     section titles (h2), unchanged
   Heading     60px      40px     h3, h2.small, .section-quote, unchanged
   Intro       40px      30px     .section-intro
   Title       30px      24px     h4, chart titles
   Statement   26px      22px     stamps, the Threat Context summary, the
                                  Outcomes framing line
   Lead        20px      19px     the Landscape intro paragraph
   Body        17px      17px     all article prose
   Small       15px      15px     lists, notes, chart call-outs
   Micro       14px      14px     source notes, card labels, unchanged */
.lab-frozen-article.lab-type-scale section[id^="section-"] p:not([class]),
.lab-frozen-article.lab-type-scale .threat-prose-tight p,
.lab-frozen-article.lab-type-scale .threat-prose-2col > p:not(.section-quote):not(.section-intro) {
	font-size: 0.85em; /* Body 17px */
	line-height: 1.6em;
}
.lab-frozen-article.lab-type-scale .col-xl-10 > ul:not([class]) > li,
.lab-frozen-article.lab-type-scale .col-xl-10 > ol:not([class]) > li {
	font-size: 0.85em; /* Body 17px, as the prose around the list */
	line-height: 1.6em;
}
.lab-frozen-article.lab-type-scale .vef-intro-support {
	font-size: 1em; /* Lead 20px */
	font-weight: 500;
	line-height: 1.6em;
}
.lab-frozen-article.lab-type-scale .vef-reg-stamp,
.lab-frozen-article.lab-type-scale section[id^="section-"] .threat-summary p,
.lab-frozen-article.lab-type-scale .section-intro.vef-intro-compact {
	font-family: 'Inter Tight', 'Arial', sans-serif;
	font-size: 1.3em; /* Statement 26px */
	font-weight: 700;
	letter-spacing: -0.01em;
	line-height: 1.3;
}
.lab-frozen-article.lab-type-scale #section-addendum .small-ul,
.lab-frozen-article.lab-type-scale #section-addendum .small-ol,
.lab-frozen-article.lab-type-scale .vef-methodology-note {
	font-size: 0.75em; /* Small 15px */
	font-weight: 500;
	line-height: 1.6em;
}
.lab-frozen-article.lab-type-scale #section-addendum .small-ul li,
.lab-frozen-article.lab-type-scale #section-addendum .small-ol li {
	font-size: 1em;
	line-height: 1.6em;
}
/* One text column for every section: the wide column Threat Context, the
   videos and the lead-gen close use, with single-column prose and lists held
   to a reading measure of about 800px. */
.lab-frozen-article.lab-type-scale .col-xl-10:not(.threat-prose-2col) > p:not([class]) {
	max-width: 47em; /* ~800px at Body 17px */
}
.lab-frozen-article.lab-type-scale .col-xl-10 > p.vef-intro-support {
	max-width: 40em; /* ~800px at Lead 20px */
}
.lab-frozen-article.lab-type-scale #section-addendum .small-ul,
.lab-frozen-article.lab-type-scale #section-addendum .small-ol {
	max-width: 53em;
}
@media (max-width: 991.98px) {
	.lab-frozen-article.lab-type-scale .section-intro:not(.vef-intro-compact) {
		font-size: 1.5em; /* Intro 30px, under the 40px heading */
	}
	.lab-frozen-article.lab-type-scale h4:not(#section-addendum details h4) {
		font-size: 1.2em; /* Title 24px */
	}
	.lab-frozen-article.lab-type-scale .vef-reg-stamp,
	.lab-frozen-article.lab-type-scale section[id^="section-"] .threat-summary p,
	.lab-frozen-article.lab-type-scale .section-intro.vef-intro-compact {
		font-size: 1.1em; /* Statement 22px */
	}
	.lab-frozen-article.lab-type-scale .vef-intro-support {
		font-size: 0.95em; /* Lead 19px */
	}
}
