The governed definitions this build depends on. Read them first if a term is unfamiliar.
- PrimarySpin-OffA spin-off separates a business by distributing shares or entitlement in another security to eligible holders.
- PrerequisiteCorporate ActionA corporate action is an issuer or security event that changes rights, quantities, cash flows, identity, or trading terms.
- ImportantAdjusted PriceAdjusted price is a transformed price expressed on a declared comparison basis after applying specified corporate-action or other adjustment factors.
- ImportantCorporate Action Effective DateA corporate action effective date is the market or legal date from which the declared event treatment applies.
- ImportantHolding Period ReturnHolding period return measures the economic gain or loss relative to starting value over a specified ownership interval.
- ImportantPayment DatePayment date is the scheduled or actual date on which a corporate-action entitlement is delivered to eligible holders.
- ImportantRecord DateRecord date is the date used under event terms to identify holders of record for a corporate-action entitlement.
- ImportantReturn BaseReturn base is the declared starting value, unit, and economic basis against which a return is measured.
- ImportantStable Security IdentifierA stable security identifier is a governed key intended to preserve instrument identity across changes in mutable labels such as ticker or name.
- ImportantTotal ReturnTotal return measures price change together with eligible distributions over a declared holding period.
A spin-off does not simply make value disappear from the parent. It gives an eligible holder a continuing parent position plus one or more child positions. The adjustment problem is to preserve that economic bridge without claiming that a formula predicted the market.
What this lets you do
By the end, you can build a parent-only adjusted series, audit each child valuation, and separately calculate a parent-plus-child return. You will also know when the evidence is too weak to publish a factor.
Start with the question, not the formula
Three common outputs answer different questions:
| Output | Question |
|---|---|
| Parent-only adjusted price | What would the earlier parent price look like on the post-event parent-only basis? |
| Combined-position return | What happened to a holder who kept the parent and received the child? |
| Tax-basis allocation | How does an applicable tax regime assign cost basis? |
This topic calculates the first two. It does not calculate tax basis.
A precise data contract
Each event revision has availableAt, and the calculator selects only the latest revision available by asOf. Each price and FX observation has both observedAt and availableAt. That prevents a corrected child price published tomorrow from entering yesterday's result.
For every child, record:
- a stable child security identifier;
- positive integer child and parent entitlement bases;
- whether the price is
when_issuedorregular_way; - observation and availability timestamps;
- price currency and source;
- causal FX evidence when currencies differ.
The parent cum price must describe a regular-way parent line that still includes the distribution entitlement. A separately supplied ex-parent price must describe the parent without that entitlement.
The guard comes before arithmetic. An unavailable child is not worth zero merely because a data field is empty.
The parent-only adjustment
If child distributes shares for parent shares, its entitlement ratio is:
Let be the sourced child price and the parent quote currency per child-currency unit. The value delivered per parent share is:
For all children:
Given a parent cum-distribution reference :
The backward factor is:
Multiply parent-only prices strictly before the event by . The anchor is the post-event parent-only basis. Keep full precision through the calculation. For parity, both implementations convert each finite IEEE-754 output to its shortest round-trippable decimal string, then round that decimal half-up to six places; ties move away from zero.
The star on matters: it is a theoretical reference under declared inputs, not the observed first ex-distribution trade.
Synthetic multi-child example
Suppose a synthetic parent has a USD 120 cum price and distributes:
- Child A: 1 child for 4 parents, priced at EUR 40 when-issued, with USD 1.10 per EUR;
- Child B: 1 child for 10 parents, priced at USD 25 regular-way.
Child A contributes per parent. Child B contributes . Total distributed value is USD 13.50.
A USD 96 pre-event parent-only observation becomes USD 85.20 on the post-event parent-only basis.
Total return is a different bridge
If the sourced observed ex-parent price is , gross combined return is:
The synthetic ex-parent observation is USD 108, so combined value is USD 121.50 and gross return is 1.25%. With USD 0.20 fees and USD 0.30 taxes per parent, net combined value is USD 121 and net return is 0.833333%.
This return does not replace the 0.8875 parent-only factor. The factor removes distributed value from the earlier parent history. The return keeps distributed value with the holder.
Fractions and cash in lieu
For 23 parent shares, Child A entitlement is 5.75 shares and Child B entitlement is 2.3 shares. The reference values of the fractions are USD 33 and USD 7.50.
Those are diagnostics, not actual cash proceeds. A distribution agent may aggregate fractions, choose a sale time and broker, deduct costs, withhold tax, and distribute net cash later. Store actual cash as a separate transaction when it becomes available.
Real historical event: GE Vernova, 2024
GE's issuer announcement and SEC-filed information statement establish the useful timeline:
- one GE Vernova share for every four GE shares;
- record date: 2024-03-19;
- when-issued GEV trading expected around 2024-03-27;
- separate GE regular-way and GE WI ex-distribution markets before completion;
- distribution effective: 2024-04-02 at 12:10 a.m. New York time;
- GEV regular-way trading began 2024-04-02;
- only whole GEV shares were distributed, with fractional interests sold for net cash proceeds.
The example is historically valuable because it makes entitlement, price discovery, legal distribution, market-line identity, and cash-in-lieu timing visible.
It is not a historical numeric adjustment in this article. The issuer and SEC event documents do not themselves supply every exact market observation under one reproducible methodology and cutoff. A researched factor still requires the exact GE cum-distribution line and timestamp, a GEV when-issued or regular-way price and timestamp, source and revision availability, and the selected methodology. No provider value is invented or silently substituted.
What authoritative methodologies teach
S&P DJI's March 2026 policy generally adds a spin-off security to relevant market-cap indices at zero price before the ex-date, with no divisor adjustment, although it documents exceptions and delayed-price handling. It calculates a combined parent-plus-child return for the ex-date in its constituent return files.
FTSE Russell's June 2026 guide documents a valuation hierarchy that prefers when-issued prices, then exchange, company, broker, and terms-based estimates. It also documents treatment when a child or parent does not trade and guards against negative assigned values.
These rules prove that price source and purpose are part of the algorithm. They do not prove one universal adjustment convention. The reference implementation here is intentionally narrower: accept sourced when-issued or regular-way prices, otherwise return pending_child_price or reject.
Explore the states
Open the guided spin-off lab. Step through the canonical two-child bridge, then compare a delayed child price and an invalid distributed value. Reset is deterministic; Back reverses one teaching state; Play uses the same transition as Step.
Implementation and tests
The Python and TypeScript implementations consume one shared fixture. Tests cover:
- future revisions and cancellations;
- missing child prices with pending and reject policies;
- ratio denominators and duplicate children;
- cross-currency evidence and future FX;
- when-issued versus regular-way timing;
- zero, negative, and impossible values;
- optional total-return inputs;
- fractions, immutability, and exact parity.
Failure modes worth catching
- Do not treat zero-price index mechanics as zero economic value.
- Do not mix a when-issued child close with an unrelated parent open without declaring the convention.
- Do not sum child ratios before valuing currencies separately.
- Do not use a later corrected event revision in an earlier backtest.
- Do not force a factor when the child has not traded or valuation evidence is missing.
- Do not use fractional reference value as actual cash in lieu.
- Order concurrent splits, dividends, identifier changes, and spin-offs explicitly.
- Reject ; it signals incompatible inputs or methodology.
Summary
A trustworthy spin-off adjustment names the child entitlement, price type, currency, timestamps, evidence, factor direction, and output purpose. Parent-only price adjustment removes child value. Holder total return keeps it. Missing evidence produces a visible pending state, not a plausible-looking number.
Continue with Stock-Dividend Adjustment to compare a distribution that changes the share basis without creating an independent child company.
Primary references
- S&P DJI Equity Indices Policies & Practices, March 2026
- FTSE Russell Corporate Actions and Events Guide, v6.9, June 2026
- GE issuer announcement, 2024-02-29
- GE Vernova information statement filed with the SEC
- GE Form 8-K reporting completion
Source roles and limitations are recorded in ../REFERENCES.md.
Asset map
| Asset | Placement | Video scene | Source | Status |
|---|---|---|---|---|
| Parent/child outcome | Opening | 2 | visuals/static/article-hero.svg | Ready |
| Multi-child value bridge | Worked example | 5 | visuals/static/worked-example.svg | Ready |
| Evidence guard | Failure modes | 8 | visuals/static/failure-guard.svg | Ready |
| Guided lab | Exploration | 4–8 | visuals/animated/playground.html | Ready |
Rendered from the canonical Mermaid sources linked by this article.
Evidence-to-output calculation flow
This flow separates point-in-time evidence, child valuation, price-only adjustment, and total return.
Takeaway: one evidence set can support a price factor, a total-return bridge, both, or neither; the output must say which.
Spin-off event and market-line lifecycle
This lifecycle keeps entitlement, conditional trading, legal distribution, and regular-way trading distinct.
Takeaway: a record date does not by itself identify the price line, valuation timestamp, or time when cash in lieu is known.
References5 primary sources and evidence notesExpand the source trail, evidence role, and limitations behind the engineering choices.
Expand the source trail, evidence role, and limitations behind the engineering choices.
Accessed 2026-07-22. Primary sources define event facts and methodology; none is treated as a universal vendor convention.
R1 — S&P DJI Equity Indices Policies & Practices
- Organization: S&P Dow Jones Indices
- Source type: Official index methodology
- Publication date/version: March 2026
- URL: https://www.spglobal.com/spdji/en/documents/methodologies/methodology-sp-equity-indices-policies-practices.pdf
- Jurisdiction/applicability: S&P DJI equity indices; not a universal security-master rule
- Supports: spin-off definitions; zero-price child treatment; possible non-zero parent adjustment; delayed/unknown-price treatment; when-issued and regular-way definitions; combined parent-plus-child return treatment on the ex-date
- Limitations: Index implementation can differ from a vendor's adjusted-price history, a holder return, accounting, or tax basis.
R2 — FTSE Russell Corporate Actions and Events Guide
- Organization: FTSE Russell
- Source type: Official index methodology
- Publication date/version: June 2026, v6.9
- URL: https://www.lseg.com/content/dam/ftse-russell/en_us/documents/policy-documents/corporate-actions-and-events-guide.pdf
- Jurisdiction/applicability: FTSE Russell market-capitalisation weighted indices
- Supports: eligibility treatment; delayed trading; taxation; valuation hierarchy preferring when-issued prices, then exchange/company/broker/terms estimates; negative-value guards; parent price adjustment as capital repayment
- Limitations: The reference implementation intentionally does not reproduce FTSE Russell's hierarchy or arbitrary-value fallback. It accepts only sourced when-issued or regular-way observations.
R3 — GE Board approves GE Vernova spin-off
- Organization: General Electric Company
- Source type: Issuer announcement
- Publication date: 2024-02-29
- URL: https://www.ge.com/news/press-releases/ge-board-of-directors-approves-spin-off-of-ge-vernova-ge-vernova-and-ge-aerospace-to
- Jurisdiction/applicability: GE/GE Vernova event facts
- Supports: one GEV share for every four GE shares; 2024-03-19 record date; expected 2024-04-02 distribution and regular-way GEV trading; expected GEV WI and GE/GE WI markets
- Limitations: Forward-looking at publication; supplies no price observation used by this package.
R4 — GE Vernova Information Statement, SEC exhibit 99.1
- Organization: GE Vernova Inc.; filed with the U.S. Securities and Exchange Commission
- Source type: Issuer information statement in SEC filing
- Filing date: 2024-03-08
- URL: https://www.sec.gov/Archives/edgar/data/1996810/000119312524059354/d542465dex991.htm
- Jurisdiction/applicability: U.S. event, holder, and tax disclosures
- Supports: 1-for-4 entitlement; record date; 2024-04-02 12:10 a.m. New York effective time; whole-share distribution; fraction aggregation and net cash proceeds; when-issued/ex-distribution market distinctions; basis allocation by relative fair values for U.S. federal income-tax discussion
- Limitations: Not tax advice, not an index methodology, and not a public market-price dataset.
R5 — GE Form 8-K reporting completion
- Organization: General Electric Company; filed with the U.S. Securities and Exchange Commission
- Source type: SEC Form 8-K
- Filing date: 2024-04-02
- URL: https://www.sec.gov/Archives/edgar/data/40545/000119312524084038/d792336d8k.htm
- Jurisdiction/applicability: GE/GE Vernova completion facts
- Supports: completed pro-rata distribution; 1-for-4 ratio; cash in lieu of fractions; GEV regular-way trading began 2024-04-02
- Limitations: Does not supply the parent cum price, child reference price, FX, or actual holder cash proceeds needed for a numeric adjustment.
Evidence decision
The GE Vernova event is included only as a historical timeline and evidence checklist. A historical numeric factor is deferred. R3–R5 establish terms and timing, but they do not establish every required market observation under one declared source/cutoff convention. The package does not fill that gap with an unsourced web quote, a vendor value, or hindsight. All numerical teaching data are explicitly synthetic.
Full dependency-light reference implementations in both supported languages.
/** Point-in-time spin-off price-adjustment reference implementation. */
const PRICE_TYPES = new Set(["when_issued", "regular_way"]);
const textValue = (value: unknown, name: string): string => {
if (typeof value !== "string" || !value.trim()) throw new TypeError(`${name} must be a non-empty string`);
return value;
};
const numberValue = (value: unknown, name: string, minimum?: number): number => {
if (typeof value !== "number" || !Number.isFinite(value)) throw new TypeError(`${name} must be a finite number`);
if (minimum !== undefined && value < minimum) throw new RangeError(`${name} must be at least ${minimum}`);
return value;
};
const positive = (value: unknown, name: string): number => {
const result = numberValue(value, name); if (result <= 0) throw new RangeError(`${name} must be positive`); return result;
};
const positiveInteger = (value: unknown, name: string): number => {
const result = positive(value, name); if (!Number.isInteger(result)) throw new RangeError(`${name} must be a positive integer`); return result;
};
const instant = (value: unknown, name: string): number => {
const source = textValue(value, name);
if (!/(?:Z|[+-]\d{2}:\d{2})$/.test(source) || Number.isNaN(Date.parse(source))) throw new RangeError(`${name} must be an ISO 8601 instant with a UTC offset`);
return Date.parse(source);
};
/** Round the shortest round-trippable decimal form half away from zero at 6dp. */
export const roundHalfUp6 = (value: number): number => {
if (!Number.isFinite(value)) throw new TypeError("rounding input must be finite");
const negative = value < 0;
const [coefficient, exponentText = "0"] = Math.abs(value).toString().toLowerCase().split("e");
const [whole, fraction = ""] = coefficient.split(".");
const exponent = Number(exponentText);
const digits = (whole + fraction).replace(/^0+(?=\d)/, "") || "0";
const decimalPlaces = fraction.length - exponent;
const shift = 6 - decimalPlaces;
let scaled: bigint;
if (shift >= 0) {
scaled = BigInt(digits) * (10n ** BigInt(shift));
} else {
const divisor = 10n ** BigInt(-shift);
const numerator = BigInt(digits);
scaled = numerator / divisor;
if ((numerator % divisor) * 2n >= divisor) scaled += 1n;
}
if (scaled === 0n) return 0;
const padded = scaled.toString().padStart(7, "0");
const decimal = `${negative ? "-" : ""}${padded.slice(0, -6)}.${padded.slice(-6)}`;
return Number(decimal);
};
const round6 = roundHalfUp6;
type RecordValue = Record<string, any>;
function selectRevision(revisions: unknown, asOf: number): RecordValue {
if (!Array.isArray(revisions) || !revisions.length) throw new TypeError("revisions must be a non-empty list");
let eventId: string | null = null; const seen = new Set<string>(); const candidates: Array<[number, number, RecordValue]> = [];
revisions.forEach((revision) => {
if (!revision || typeof revision !== "object" || Array.isArray(revision)) throw new TypeError("each revision must be an object");
const item = revision as RecordValue; const current = textValue(item.eventId, "eventId");
if (eventId === null) eventId = current; else if (eventId !== current) throw new RangeError("all revisions must describe the same eventId");
if (!Number.isInteger(item.revisionSequence) || item.revisionSequence < 1) throw new RangeError("revisionSequence must be a positive integer");
const identity = `${textValue(item.revisionId, "revisionId")}\u0000${item.revisionSequence}`;
if (seen.has(identity)) throw new RangeError("duplicate revision identity"); seen.add(identity);
const available = instant(item.availableAt, "availableAt"); if (available <= asOf) candidates.push([available, item.revisionSequence, item]);
});
if (!candidates.length) throw new RangeError("no event revision was available at asOf");
candidates.sort((a, b) => a[0] - b[0] || a[1] - b[1]); return candidates[candidates.length - 1][2];
}
function validateTimeline(event: RecordValue): [number, number, number] {
const times = [instant(event.recordAt,"recordAt"), instant(event.whenIssuedStartAt,"whenIssuedStartAt"), instant(event.distributionAt,"distributionAt"), instant(event.parentExAt,"parentExAt"), instant(event.childRegularWayAt,"childRegularWayAt")];
if (!(times[0] <= times[1] && times[1] <= times[2] && times[2] <= times[3] && times[3] <= times[4])) throw new RangeError("timeline must satisfy record <= when-issued <= distribution <= parent ex <= child regular-way");
return [times[1], times[3], times[4]];
}
function observation(value: unknown, name: string, asOf: number, currency: string): [number, number] {
if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${name} must be an object`);
const item = value as RecordValue; if (textValue(item.currency, `${name}.currency`) !== currency) throw new RangeError(`${name}.currency must equal quoteCurrency`);
const price = positive(item.price, `${name}.price`), observed = instant(item.observedAt, `${name}.observedAt`), available = instant(item.availableAt, `${name}.availableAt`);
if (observed > available) throw new RangeError(`${name}.observedAt must not be later than availableAt`);
if (available > asOf) throw new RangeError(`${name} was not available at asOf`); textValue(item.sourceId, `${name}.sourceId`); return [price, observed];
}
export function calculate(input: RecordValue): RecordValue {
if (!input || typeof input !== "object" || Array.isArray(input)) throw new TypeError("data must be an object");
const value = structuredClone(input), asOf = instant(value.asOf, "asOf"), event = selectRevision(value.revisions, asOf), status = textValue(event.status, "status");
if (status === "cancelled") throw new RangeError("selected event revision is cancelled"); if (status !== "confirmed") throw new RangeError("status must be confirmed or cancelled");
const [whenIssuedAt, exAt, childRegularWayAt] = validateTimeline(event), quoteCurrency = textValue(event.quoteCurrency, "quoteCurrency");
const [parentCum, parentCumObserved] = observation(event.parentCumObservation, "parentCumObservation", asOf, quoteCurrency);
if (textValue(event.parentCumObservation.priceType,"parentCumObservation.priceType") !== "regular_way_cum_distribution") throw new RangeError("parentCumObservation.priceType must be regular_way_cum_distribution");
if (parentCumObserved >= exAt) throw new RangeError("parent cum observation must precede parentExAt");
if (!Array.isArray(event.children) || !event.children.length) throw new TypeError("children must be a non-empty list");
const holding = event.holdingParentShares; if (holding !== undefined && (!Number.isInteger(holding) || holding < 0)) throw new RangeError("holdingParentShares must be a non-negative integer");
const ids = new Set<string>(), components: RecordValue[] = [], componentValues: Array<[string,number]> = [], missing: string[] = [];
event.children.forEach((child: RecordValue) => {
if (!child || typeof child !== "object" || Array.isArray(child)) throw new TypeError("each child must be an object");
const securityId = textValue(child.securityId,"securityId"); if (ids.has(securityId)) throw new RangeError("child securityId values must be unique"); ids.add(securityId);
const childShares = positiveInteger(child.childShares,"childShares"), parentShares = positiveInteger(child.parentShares,"parentShares"), ratio = childShares / parentShares;
if (!child.reference || typeof child.reference !== "object" || Array.isArray(child.reference)) throw new TypeError("child reference must be an object");
const reference = child.reference, referenceStatus = textValue(reference.status,"reference.status");
if (referenceStatus === "unavailable") { missing.push(securityId); return; }
if (referenceStatus !== "available") throw new RangeError("reference.status must be available or unavailable");
const priceType = textValue(reference.priceType,"reference.priceType"); if (!PRICE_TYPES.has(priceType)) throw new RangeError("reference.priceType must be when_issued or regular_way");
const childCurrency = textValue(reference.currency,"reference.currency"), childPrice = positive(reference.price,"reference.price"), observedAt = instant(reference.observedAt,"reference.observedAt"), availableAt = instant(reference.availableAt,"reference.availableAt");
if (observedAt > availableAt) throw new RangeError("reference.observedAt must not be later than reference.availableAt"); if (availableAt > asOf) throw new RangeError("child price evidence was not available at asOf"); if (priceType === "when_issued" && !(whenIssuedAt <= observedAt && observedAt <= exAt)) throw new RangeError("when-issued reference must be observed between whenIssuedStartAt and parentExAt"); if (priceType === "regular_way" && observedAt < childRegularWayAt) throw new RangeError("regular-way reference cannot precede childRegularWayAt");
textValue(reference.sourceId,"reference.sourceId"); const fx = positive(reference.fxQuotePerChildCurrency ?? 1,"fxQuotePerChildCurrency");
if (childCurrency === quoteCurrency) { if (Math.abs(fx-1)>1e-12) throw new RangeError("same-currency child requires fxQuotePerChildCurrency = 1"); }
else { const fxObserved=instant(reference.fxObservedAt,"fxObservedAt"),fxAvailable=instant(reference.fxAvailableAt,"fxAvailableAt"); if(fxObserved>fxAvailable)throw new RangeError("fxObservedAt must not be later than fxAvailableAt"); if (fxAvailable > asOf) throw new RangeError("FX evidence was not available at asOf"); textValue(reference.fxSourceId,"fxSourceId"); }
const priceQuote = childPrice * fx, valuePerParent = ratio * priceQuote;
const component: RecordValue = {securityId,entitlementRatio:round6(ratio),priceType,priceQuote:round6(priceQuote),valuePerParent:round6(valuePerParent)};
if (holding !== undefined) { const exact=holding*ratio, whole=Math.floor(exact); component.holderEntitlement={exactChildShares:round6(exact),wholeChildShares:whole,fractionalChildShare:round6(exact-whole),fractionalReferenceValue:round6((exact-whole)*priceQuote),actualCashInLieuKnown:false}; }
components.push(component); componentValues.push([securityId,valuePerParent]);
});
if (missing.length) { const policy=textValue(event.unavailableChildPricePolicy ?? "pending","unavailableChildPricePolicy"); if(policy==="reject")throw new RangeError("one or more child reference prices are unavailable"); if(policy!=="pending")throw new RangeError("unavailableChildPricePolicy must be pending or reject"); return {eventId:event.eventId,revisionId:event.revisionId,status:"pending_child_price",missingChildSecurityIds:missing,adjustmentFactor:null,theoreticalParentExReference:null,referenceIsTradedParentExFact:false}; }
const distributed=componentValues.reduce((sum,item)=>sum+item[1],0), theoreticalEx=parentCum-distributed; if(theoreticalEx<=0)throw new RangeError("distributed child value must remain below parentCumPrice");
const allocation={parentWeight:round6(theoreticalEx/parentCum),children:componentValues.map(item=>({securityId:item[0],weight:round6(item[1]/parentCum)}))};
let totalReturn=null; if(event.parentExObservation!==undefined){ const [parentEx,parentExObserved]=observation(event.parentExObservation,"parentExObservation",asOf,quoteCurrency); if(textValue(event.parentExObservation.priceType,"parentExObservation.priceType")!=="regular_way_ex_distribution")throw new RangeError("parentExObservation.priceType must be regular_way_ex_distribution"); if(parentExObserved<exAt)throw new RangeError("parent ex observation cannot precede parentExAt"); const fees=numberValue(event.feesPerParentQuote??0,"feesPerParentQuote",0),taxes=numberValue(event.taxesPerParentQuote??0,"taxesPerParentQuote",0),gross=parentEx+distributed,net=gross-fees-taxes; if(net<0)throw new RangeError("fees and taxes cannot make net combined value negative"); totalReturn={parentExObserved:round6(parentEx),grossCombinedValue:round6(gross),grossReturn:round6(gross/parentCum-1),netCombinedValue:round6(net),netReturn:round6(net/parentCum-1),feesPerParentQuote:round6(fees),taxesPerParentQuote:round6(taxes)}; }
return {eventId:event.eventId,revisionId:event.revisionId,availableAt:event.availableAt,status:"ready",quoteCurrency,parentCumPrice:round6(parentCum),distributedValuePerParent:round6(distributed),theoreticalParentExReference:round6(theoreticalEx),backwardAdjustmentFactor:round6(theoreticalEx/parentCum),factorDirection:"multiply_pre_event_parent_prices",factorAnchor:"post_event_parent_only_basis",rounding:"shortest_decimal_half_up_6_on_output_only",components,economicValueAllocation:allocation,totalReturnBridge:totalReturn,referenceIsTradedParentExFact:false};
}
The embedded lab now expands to its full document height, keeping the article as the only scroll surface.