## Government Ownership Dashboard 

This tool tracks every deal announced by the US government to acquire an equity stake in a private US company, including the terms of each deal and primary source documents, if any. The dashboard will be updated as new transactions are announced or new information on existing deals becomes available.

---

#fed-stakes {
color-scheme: light;
/* Keep the centered layout steady when the grid adds a scrollbar. */
scrollbar-gutter: stable;
--foreground: #18181b;
--muted: #71717a;
--subtle: #fafafa;
/* Controls share the cards' border, which matches the site's accordion. */
--border: var(--bs-border-color, rgb(26 23 20 / 0.3));
--radius: 0;
--interface-radius: 0;
--badge-inset: 4px;
--space: 8px;
--secondary: #52525b;
--type-title: 28px;
--type-heading: 18px;
--type-emphasis: 15px;
--type-body: 14px;
--type-label: 13px;
--type-note: 12px;
--icon-arrow: 27px;
--icon-arrow-sm: 22px;
--weight-normal: 400;
--weight-medium: 500;
--weight-strong: 600;
}
#fed-stakes,
#fed-stakes * {
box-sizing: border-box;
}
#fed-stakes [hidden] {
display: none !important;
}
/* Hovering and clicking the chart shouldn't highlight its counts and sector
labels. Text everywhere else stays selectable for copying. */
#fed-stakes #chart {
-webkit-user-select: none;
user-select: none;
}
/* Hold everything back until the chart and cards are built, then show it at once.
The chart keeps its height so the page below doesn't jump. If the script never
finishes, the content still appears after 8 seconds. */
#fed-stakes.is-loading.fed-stakes-layout {
visibility: hidden;
animation: fed-stakes-failsafe 0s 8s forwards;
}
#fed-stakes.is-loading #chart {
min-height: 436px;
}
@keyframes fed-stakes-failsafe {
to {
visibility: visible;
}
}
@keyframes fed-stakes-fade-in {
from {
opacity: 0;
}
}
#fed-stakes:not(.is-loading).fed-stakes-layout {
animation: fed-stakes-fade-in 200ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
#fed-stakes:not(.is-loading).fed-stakes-layout {
animation: none;
}
}
#fed-stakes {
/* Fill the host column even when Drupal's container is flex or grid. */
width: 100%;
margin: 0;
padding: 0;
background: #fff;
color: var(--foreground);
font-family:
'Inter',
ui-sans-serif,
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
sans-serif;
-webkit-font-smoothing: antialiased;
}
/* Fill the Drupal column; Drupal sets the max width and side gutters. */
#fed-stakes.fed-stakes-layout {
width: 100%;
margin: 0;
padding: 32px 0;
}
#fed-stakes h1,
#fed-stakes h2,
#fed-stakes h3,
#fed-stakes p {
margin: 0;
}
#fed-stakes h1 {
font-size: var(--type-title);
line-height: 1.15;
letter-spacing: -0.035em;
font-weight: var(--weight-strong);
}
#fed-stakes.hero {
display: flex;
justify-content: space-between;
align-items: center;
gap: 4px;
margin-bottom: 24px;
}
#fed-stakes.hero-copy {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
}
#fed-stakes.intro {
margin-top: 8px;
color: var(--secondary);
font-size: var(--type-body);
line-height: 1.6;
}
/* One sentence per line: the date range, then how to read the marks. */
#fed-stakes.intro > span {
display: block;
}
/* A neutral stand-in for the chart's marks; color belongs to sectors. */
#fed-stakes.intro-square {
display: inline-block;
width: 11px;
height: 11px;
margin: 0 3px;
background: var(--secondary);
vertical-align: -0.5px;
}
#fed-stakes.card {
padding: 0;
}
#fed-stakes.chart-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
#fed-stakes button,
#fed-stakes input {
font: inherit;
}
#fed-stakes button {
cursor: pointer;
}
#fed-stakes button:focus-visible,
#fed-stakes summary:focus-visible,
#fed-stakes input:focus-visible,
#fed-stakes #chart:focus-visible {
outline: 2px solid #52525b;
outline-offset: 3px;
}
#fed-stakes.segmented {
display: inline-flex;
padding: 3px;
border: 1px solid var(--border);
border-radius: var(--interface-radius);
}
#fed-stakes.segmented button {
padding: 7px 14px;
border: 0;
border-radius: var(--interface-radius);
background: transparent;
color: var(--muted);
font-size: var(--type-body);
font-weight: var(--weight-medium);
}
#fed-stakes.segmented button.active {
background: var(--foreground);
color: #fff;
}
#fed-stakes.company-search {
position: relative;
width: 240px;
}
#fed-stakes.chart-meta {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px 20px;
margin-top: 20px;
color: var(--secondary);
font-size: var(--type-label);
}
#fed-stakes.legend {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 10px 14px;
}
#fed-stakes.key {
display: inline-flex;
align-items: center;
gap: 6px;
}
#fed-stakes.key i {
width: 9px;
height: 9px;
border-radius: 1px;
flex-shrink: 0;
}

#fed-stakes #chart {
min-height: 470px;
margin-top: 8px;
overflow-x: auto;
scrollbar-width: thin;
scrollbar-color: #a1a1aa #f4f4f5;
overscroll-behavior-x: contain;
}
#fed-stakes :is(#chart, #chart-export) svg {
display: block;
font-family: inherit;
font-variant-numeric: tabular-nums;
}
#fed-stakes :is(#chart, #chart-export) svg text {
pointer-events: none;
}
#fed-stakes.company-mark {
cursor: pointer;
outline: none;
}
#fed-stakes.mark-focus-ring {
fill: none;
stroke: #18181b;
stroke-width: 2px;
pointer-events: none;
}
#fed-stakes.keyboard-hint rect {
fill: var(--foreground);
}
#fed-stakes.keyboard-hint text {
font-size: var(--type-note);
font-weight: var(--weight-medium);
fill: #fff;
}
#fed-stakes.company-detail {
border-top: 1px solid var(--border);
padding-top: 16px;
}
#fed-stakes #detail-placeholder {
color: var(--muted);
font-size: var(--type-label);
font-weight: var(--weight-normal);
line-height: 1.5;
padding-top: 4px;
}
#fed-stakes.detail-header {
display: flex;
align-items: baseline;
gap: 12px;
}
#fed-stakes.detail-category {
color: var(--muted);
font-size: var(--type-note);
margin-bottom: 4px;
}
#fed-stakes h3 {
font-size: var(--type-heading);
letter-spacing: -0.015em;
font-weight: var(--weight-strong);
}
#fed-stakes.detail-facts {
display: flex;
flex-wrap: wrap;
gap: 12px 28px;
margin: 12px 0 10px;
font-size: var(--type-label);
}
#fed-stakes.detail-facts dt {
color: var(--muted);
font-size: var(--type-note);
margin-bottom: 3px;
}
#fed-stakes.detail-facts dd {
margin: 0;
}
#fed-stakes.detail-summary {
max-width: 900px;
font-size: var(--type-body);
line-height: 1.5;
}
#fed-stakes.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;
}
@media (max-width: 650px) {
#fed-stakes.hero {
display: flex;
gap: 4px;
margin-bottom: 20px;
}
#fed-stakes.chart-toolbar {
flex-wrap: wrap;
gap: 12px;
}
#fed-stakes.company-search {
width: 100%;
}
#fed-stakes.chart-meta {
gap: 12px;
margin-top: 16px;
}
#fed-stakes.legend {
gap: 10px 12px;
}

#fed-stakes.detail-facts {
gap: 12px 20px;
}
}
@media (prefers-reduced-motion: reduce) {
#fed-stakes * {
scroll-behavior: auto !important;
}
}

/* Chart text shares the interface type scale; importance follows the active view. */
#fed-stakes :is(#chart, #chart-export).tick text {
font-size: var(--type-note);
font-weight: var(--weight-normal);
fill: var(--muted);
}
#fed-stakes :is(#chart, #chart-export).top-labels text,
#fed-stakes :is(#chart, #chart-export).addition-labels text {
font-size: var(--type-note);
font-weight: var(--weight-strong);
fill: var(--foreground);
}
#fed-stakes
:is(#chart, #chart-export)
svg[data-mode='cumulative']
.addition-labels
text {
font-weight: var(--weight-medium);
fill: var(--secondary);
}
#fed-stakes :is(#chart, #chart-export).final-label.count {
font-size: var(--type-label);
font-weight: var(--weight-strong);
fill: var(--foreground);
}
#fed-stakes :is(#chart, #chart-export).final-label.unit {
font-size: var(--type-note);
font-weight: var(--weight-normal);
fill: var(--secondary);
}
#fed-stakes :is(#chart, #chart-export).selection-labels text {
font-size: var(--type-note);
font-weight: var(--weight-strong);
fill: var(--foreground);
}

#fed-stakes :is(#chart, #chart-export).total-sector-labels text {
font-size: var(--type-note);
font-weight: 600;
}

#fed-stakes
:is(#chart, #chart-export)
.total-sector-labels
.sector-count {
font-size: var(--type-note);
font-weight: var(--weight-normal);
fill: var(--muted);
}

/* Monthly contribution equation, matching the demo. */
#fed-stakes :is(#chart, #chart-export).final-label.count {
font-size: var(--type-note);
font-weight: var(--weight-strong);
}
#fed-stakes
:is(#chart, #chart-export)
svg[data-mode='cumulative']
.addition-labels
text {
font-size: var(--type-note);
font-weight: var(--weight-normal);
fill: var(--secondary);
}
#fed-stakes :is(#chart, #chart-export).tick.summary-tick text {
font-weight: var(--weight-strong);
}
#fed-stakes :is(#chart, #chart-export).equals-label {
font-size: var(--type-note);
font-weight: var(--weight-strong);
fill: var(--foreground);
}
#fed-stakes
:is(#chart, #chart-export)
svg.arithmetic-labels
.tick.summary-tick
text,
#fed-stakes
:is(#chart, #chart-export)
svg.arithmetic-labels
.equals-label {
font-weight: var(--weight-normal);
}
#fed-stakes
:is(#chart, #chart-export)
svg.arithmetic-labels
.equals-label,
#fed-stakes
:is(#chart, #chart-export)
svg.arithmetic-labels
.final-label
.count {
font-size: var(--type-note);
font-weight: var(--weight-normal);
fill: var(--secondary);
}

#fed-stakes {
--company-card-height: 230px;
}
/* Styles for the company-card exploration copy. */
#fed-stakes.company-directory {
border-top: 0;
padding-top: 20px;
padding-bottom: 0;
}
#fed-stakes.directory-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
#fed-stakes.directory-heading h2 {
font-weight: var(--weight-strong);
font-size: var(--type-heading);
letter-spacing: -0.02em;
}
#fed-stakes.directory-heading {
margin-bottom: 20px;
}
#fed-stakes.company-card {
scroll-margin-block: 24px;
cursor: pointer;
position: relative;
min-height: var(--company-card-height);
/* Matches the site's Methodology accordion, which uses Bootstrap's border
color. The fallback approximates it where Bootstrap isn't loaded. */
border: var(--bs-border-width, 1px) solid
var(--bs-border-color, rgb(26 23 20 / 0.3));
/* Square, like the accordion; the site's own radius variable is rounded. */
border-radius: 0;
padding: 16px;
background: #fff;
transition:
background-color 120ms,
box-shadow 120ms;
}
/* Previewed from the chart: tint only, a step below the selected card's edge. */
#fed-stakes.company-card.is-active {
background: #fafafa;
}
#fed-stakes.company-card h3 button {
border: 0;
padding: 0;
background: transparent;
color: inherit;
text-align: left;
font: inherit;
}
#fed-stakes.company-card-header {
display: flex;
align-items: baseline;
justify-content: flex-start;
flex-direction: row;
flex-wrap: wrap;
gap: 4px 8px;
min-height: 38px;
}
#fed-stakes.company-card-header h3 {
flex: 1 1 125px;
min-width: 0;
overflow-wrap: break-word;
font-size: var(--type-heading);
line-height: 1.3;
}
#fed-stakes.company-card.detail-category {
flex: 0 1 auto;
max-width: 100%;
margin: 0;
font-weight: var(--weight-medium);
}
@media (prefers-reduced-motion: reduce) {
#fed-stakes.company-card {
transition: none;
}
}

#fed-stakes.chart-panel {
min-width: 0;
background: #fff;
}
#fed-stakes.search-actions {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
}
#fed-stakes.chart-explainer,
#fed-stakes.selection-context {
font-size: var(--type-label);
line-height: 1.5;
color: var(--secondary);
margin-top: 12px;
}
#fed-stakes.selection-context {
margin-top: 4px;
min-height: 36px;
}
#fed-stakes.company-count {
color: var(--secondary);
font-size: var(--type-label);
}
#fed-stakes.company-card.detail-facts {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 10px;
margin: 8px 0 0;
}
#fed-stakes.fact-stage dd {
font-size: var(--type-body);
font-weight: 600;
line-height: 1.4;
}
#fed-stakes.fact-amount dd {
font-size: var(--type-emphasis);
font-weight: 600;
}
#fed-stakes.fact-date {
grid-column: 1 / -1;
}
#fed-stakes.fact-date dt,
#fed-stakes.fact-date dd {
display: inline;
}
#fed-stakes.fact-date dt {
margin-right: 6px;
}
#fed-stakes.selected-label {
display: inline;
margin-left: 8px;
font-size: var(--type-note);
font-weight: 600;
color: #3f3f46;
}
#fed-stakes.selected-label[hidden] {
display: none;
}
#fed-stakes.deal-disclosure {
margin-top: 16px;
}
#fed-stakes.deal-disclosure summary {
cursor: pointer;
font-size: var(--type-label);
color: #3f3f46;
}
#fed-stakes.deal-disclosure.detail-summary {
margin-top: 10px;
}
@media (max-width: 650px) {
#fed-stakes.search-actions {
width: 100%;
}
#fed-stakes.search-actions.company-search {
flex: 1;
min-width: 0;
}
}

#fed-stakes.sector-tabs {
position: relative;
display: flex;
align-items: center;
gap: 6px;
min-width: 0;
padding: 3px 2px;
}
#fed-stakes.sector-tabs select {
min-width: 0;
width: 260px;
max-width: 100%;
flex: 0 1 260px;
min-height: 36px;
border: 1px solid var(--border);
border-radius: var(--interface-radius);
background: white;
padding: 8px 10px;
font: inherit;
font-size: var(--type-label);
color: var(--foreground);
}
#fed-stakes.directory-toolbar button:focus-visible,
#fed-stakes.directory-toolbar select:focus-visible {
outline: 2px solid #71717a;
outline-offset: 3px;
}
#fed-stakes.sector-tab-divider {
flex: 0 0 1px;
align-self: stretch;
margin: 5px 6px;
background: var(--border);
}
#fed-stakes.sector-tabs button {
flex: 0 0 auto;
border: 1px solid var(--border);
border-radius: var(--interface-radius);
background: white;
padding: 8px 10px;
font-size: var(--type-label);
color: var(--secondary);
}
#fed-stakes.sector-tabs button[aria-pressed='true'] {
color: var(--foreground);
border-color: #d4d4d8;
background: #f4f4f5;
font-weight: 600;
}
#fed-stakes.company-carousel {
display: flex;
gap: 16px;
overflow-x: auto;
padding: 4px var(--carousel-end-space, 0px) 4px
var(--carousel-start-space, 0px);
scroll-snap-type: x mandatory;
overscroll-behavior-x: contain;
scrollbar-width: none;
align-items: flex-start;
overflow-y: hidden;
}
#fed-stakes.company-carousel.company-card {
flex: 0 0 var(--carousel-card-width, 256px);
min-width: 0;
scroll-snap-align: start;
}
#fed-stakes.company-carousel.is-centered.company-card {
scroll-snap-align: center;
}
#fed-stakes.company-carousel:focus-visible {
outline: 2px solid #71717a;
outline-offset: 3px;
}
#fed-stakes.company-carousel::-webkit-scrollbar {
display: none;
}
#fed-stakes.carousel-frame {
position: relative;
padding-inline: 0;
}
#fed-stakes.carousel-frame.is-expanded {
padding-inline: 0;
}
#fed-stakes.carousel-arrow {
position: absolute;
top: var(--carousel-arrow-top);
transform: translateY(-50%);
z-index: 2;
display: grid;
place-items: center;
width: 36px;
height: 36px;
padding: 0 0 3px;
border: 1px solid #d4d4d8;
border-radius: var(--interface-radius);
background: #fff;
box-shadow: 0 1px 2px #18181b0d;
color: #27272a;
font-size: var(--icon-arrow);
line-height: 1;
}
#fed-stakes.carousel-arrow:not(:disabled):hover {
background: #f4f4f5;
}
#fed-stakes.carousel-frame.carousel-arrow:disabled {
opacity: 0.35;
cursor: default;
}
#fed-stakes.carousel-frame.carousel-arrow:focus-visible {
outline: 2px solid #71717a;
outline-offset: 3px;
}
#fed-stakes #carousel-previous {
left: 4px;
}
#fed-stakes #carousel-next {
right: 4px;
}
#fed-stakes.company-card.detail-facts dd {
overflow-wrap: anywhere;
}
@media (max-width: 650px) {
#fed-stakes.company-card-header {
min-height: 0;
}
#fed-stakes.company-card.detail-facts {
margin-top: 16px;
}
}

/* Directory review refinements. */
#fed-stakes.chart-takeaway {
margin-top: 12px;
font-size: var(--type-emphasis);
font-weight: 600;
}
#fed-stakes.methodology {
margin-top: 12px;
color: var(--secondary);
font-size: var(--type-label);
line-height: 1.6;
}
#fed-stakes.methodology summary {
cursor: pointer;
width: fit-content;
text-decoration: underline;
text-underline-offset: 3px;
}
#fed-stakes.methodology p {
margin-top: 8px;
max-width: 72ch;
}
#fed-stakes.selection-context {
min-height: 18px;
}
#fed-stakes.chart-meta[hidden] {
display: none;
}
#fed-stakes.directory-heading {
flex-wrap: wrap;
gap: 12px;
margin-bottom: 14px;
}
#fed-stakes.directory-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px 24px;
margin-bottom: 12px;
}
#fed-stakes.directory-controls {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
#fed-stakes #carousel-position {
color: var(--secondary);
font-size: var(--type-label);
font-variant-numeric: tabular-nums;
}
#fed-stakes.directory-controls.carousel-arrow {
position: static;
transform: none;
width: 36px;
height: 36px;
box-shadow: none;
border-radius: var(--interface-radius);
font-size: var(--icon-arrow-sm);
}
#fed-stakes.directory-controls.carousel-arrow:disabled {
opacity: 0.35;
cursor: default;
}
#fed-stakes #directory-layout {
padding: 6px 8px;
border: 1px solid var(--border);
border-radius: var(--interface-radius);
background: white;
font-size: var(--type-label);
}
#fed-stakes.directory-toolbar button,
#fed-stakes.directory-toolbar select {
height: 36px;
border: 1px solid var(--border);
border-radius: var(--interface-radius);
background: #fff;
color: var(--foreground);
font-family: inherit;
}
#fed-stakes.directory-toolbar #directory-layout {
height: 36px;
padding: 0 10px;
border-radius: var(--interface-radius);
}
#fed-stakes.directory-toolbar button:not(:disabled):hover,
#fed-stakes.directory-toolbar select:hover {
background: #f4f4f5;
}
#fed-stakes.directory-context {
margin: 0 0 12px;
color: var(--secondary);
font-size: var(--type-label);
line-height: 1.5;
}

#fed-stakes.company-carousel.is-expanded {
display: grid;
align-items: stretch;
grid-template-columns: repeat(3, minmax(0, 1fr));
padding: 4px 0;
overflow: visible;
}
/* Selected: a tinted card with a mid-gray edge; the chart carries the emphasis. */
#fed-stakes.company-card.is-selected {
border-color: #a1a1aa;
background: #fafafa;
}
#fed-stakes.detail-sources {
font-size: var(--type-label);
color: var(--secondary);
margin-top: 12px;
}
#fed-stakes.detail-sources a {
color: inherit;
text-underline-offset: 3px;
display: inline-block;
margin-right: 8px;
}
#fed-stakes.selected-company-link {
font-size: var(--type-label);
margin-top: 8px;
}
#fed-stakes.selected-company-link button {
background: none;
border: 0;
padding: 0;
text-decoration: underline;
text-underline-offset: 3px;
}
@media (max-width: 750px) {
#fed-stakes.company-carousel.is-expanded {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 520px) {
#fed-stakes.company-carousel.is-expanded {
grid-template-columns: minmax(0, 1fr);
}
}

#fed-stakes.directory-sector-label {
font-weight: 600;
white-space: nowrap;
}

#fed-stakes :is(#chart, #chart-export).final-label.count {
font-size: var(--type-note);
font-weight: var(--weight-strong);
}

#fed-stakes
:is(#chart, #chart-export)
svg[data-mode='cumulative']
.addition-labels
text {
font-size: var(--type-note);
font-weight: var(--weight-normal);
fill: var(--secondary);
}
#fed-stakes :is(#chart, #chart-export).tick.summary-tick text {
font-weight: var(--weight-strong);
}

#fed-stakes
:is(#chart, #chart-export)
.total-sector-labels
.sector-count {
font-size: var(--type-note);
font-weight: var(--weight-normal);
fill: var(--muted);
/* Counts don't align with other numbers, so tabular 1s only look loose. */
font-variant-numeric: proportional-nums;
}

#fed-stakes :is(#chart, #chart-export).equals-label {
font-size: var(--type-note);
font-weight: var(--weight-strong);
fill: var(--foreground);
}

#fed-stakes
:is(#chart, #chart-export)
svg.arithmetic-labels
.tick.summary-tick
text,
#fed-stakes
:is(#chart, #chart-export)
svg.arithmetic-labels
.equals-label {
font-weight: var(--weight-normal);
}
#fed-stakes
:is(#chart, #chart-export)
svg.arithmetic-labels
.equals-label,
#fed-stakes
:is(#chart, #chart-export)
svg.arithmetic-labels
.final-label
.count {
font-size: var(--type-note);
font-weight: var(--weight-normal);
fill: var(--secondary);
}

/* Monthly totals step back so the final total leads. */
#fed-stakes
:is(#chart, #chart-export)
svg[data-mode='cumulative']
.top-labels
text {
font-weight: var(--weight-normal);
}
#fed-stakes
:is(#chart, #chart-export)
svg[data-mode='cumulative']
.final-label
.count {
font-weight: var(--weight-strong);
}

/* Bring the chart closer to search and soften the headline scale. */
#fed-stakes.hero h1 {
font-size: var(--type-title);
}
#fed-stakes #chart {
margin-top: 4px;
min-height: 458px;
}

#fed-stakes.chart-panel >.exploration-toolbar {
justify-content: flex-start;
}
#fed-stakes.exploration-toolbar {
gap: 12px 32px;
}
#fed-stakes.exploration-filters {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px 24px;
flex: 1 1 100%;
width: 100%;
min-width: 0;
}
#fed-stakes.exploration-toolbar.search-actions {
flex: 1 1 0;
width: auto;
min-width: 0;
}
#fed-stakes.exploration-toolbar.sector-tabs {
flex: 1 1 230px;
}
#fed-stakes.exploration-toolbar.sector-tabs select {
width: 180px;
flex: 1 1 180px;
}
#fed-stakes.sector-option-swatch {
width: 9px;
height: 9px;
flex: none;
}
#fed-stakes.sector-option-count {
margin-left: auto;
color: var(--secondary);
}
#fed-stakes.filter-label {
display: block;
margin-bottom: 6px;
font-size: var(--type-label);
font-weight: 500;
color: var(--secondary);
}
#fed-stakes.sector-field {
flex: 1 1 0;
min-width: 0;
}
#fed-stakes.exploration-filters {
align-items: flex-end;
}
#fed-stakes.exploration-toolbar.search-actions {
align-items: flex-end;
}
#fed-stakes.carousel-navigation {
display: flex;
align-items: center;
gap: 8px;
white-space: nowrap;
}
#fed-stakes.exploration-toolbar.directory-controls {
gap: 20px;
flex: 0 0 auto;
flex-wrap: nowrap;
margin-left: auto;
}
@media (max-width: 650px) {
#fed-stakes.exploration-filters {
flex-basis: 100%;
flex-grow: 1;
}
#fed-stakes.exploration-toolbar.directory-controls {
margin-left: 0;
}
}
#fed-stakes.exploration-toolbar.company-search {
flex: 1;
min-width: 0;
}
#fed-stakes.exploration-toolbar.sector-tabs {
padding: 0;
}
/* Compact reading cards: full details, natural height, no action footer. */
#fed-stakes.company-card {
display: flex;
flex-direction: column;
min-height: 0;
padding: 16px;
cursor: pointer;
}
#fed-stakes.company-card-header {
flex-shrink: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
min-height: 0;
}
#fed-stakes.company-card-header h3 {
flex: 1 1 auto;
display: inline-flex;
align-items: flex-start;
gap: 4px;
min-width: 0;
max-width: 100%;
width: auto;
font-size: var(--type-heading);
line-height: 1.3;
}
#fed-stakes.company-card-header.company-select {
flex: 0 1 auto;
min-width: 0;
overflow-wrap: anywhere;
line-height: inherit;
border-radius: var(--interface-radius);
text-decoration-thickness: 1px;
text-underline-offset: 3px;
}
#fed-stakes.company-select:hover,
#fed-stakes.company-select:focus-visible {
text-decoration-line: underline;
}
#fed-stakes.company-select:focus-visible {
outline: 2px solid #52525b;
outline-offset: 3px;
}
#fed-stakes.company-card-date {
flex: none;
white-space: nowrap;
font-size: var(--type-label);
line-height: 1.5;
color: var(--secondary);
}
#fed-stakes.company-card.detail-category {
margin: 0;
color: var(--secondary);
font-size: var(--type-label);
font-weight: 400;
line-height: 1.5;
overflow-wrap: anywhere;
}
#fed-stakes.company-card.detail-summary {
margin-top: 10px;
font-size: var(--type-body);
line-height: 1.5;
}
#fed-stakes.company-announcement {
display: inline-flex;
align-items: center;
justify-content: center;
position: relative;
margin-left: 4px;
width: 12px;
height: 12px;
vertical-align: middle;
color: var(--muted);
border-radius: var(--interface-radius);
cursor: pointer;
text-decoration: none;
}
/* Expand the hit area without adding height to the description's line box.
Start at the gap's edge so the target does not cover the preceding word. */
#fed-stakes.company-announcement::before {
content: '';
position: absolute;
left: -4px;
top: -10px;
width: 32px;
height: 32px;
border-radius: var(--interface-radius);
}
#fed-stakes.company-announcement:hover,
#fed-stakes.company-announcement:focus-visible {
color: var(--foreground);
background: #f4f4f5;
}
#fed-stakes.company-announcement:focus-visible {
outline: 2px solid #52525b;
outline-offset: 3px;
}
#fed-stakes.company-announcement svg {
width: 12px;
height: 12px;
flex: none;
}
#fed-stakes.company-announcement-ending {
white-space: nowrap;
}
#fed-stakes.company-card.detail-facts {
margin: 4px 0 0;
gap: 4px;
align-content: start;
flex-shrink: 0;
padding-top: 0;
}
#fed-stakes.company-card.detail-facts > div {
display: block;
}
/* Card details share one size and one gray, so the name reads as the heading. */
#fed-stakes.company-card.detail-facts dd {
display: inline;
font-size: var(--type-label);
line-height: 1.5;
font-weight: 400;
color: var(--secondary);
}
#fed-stakes.company-card.detail-facts >.metadata-line {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 2px 0;
/* Shift the row left and clip it, so a hairline that starts a wrapped
line falls outside the card and the date lines up flush left. */
margin-left: -21px;
clip-path: inset(0 0 0 21px);
}
#fed-stakes.company-card.metadata-line > div {
position: relative;
margin-left: 21px;
}
/* A short hairline separates sector and date; it drops away if they wrap. */
#fed-stakes.company-card.metadata-date::before {
content: '';
position: absolute;
left: -11px;
top: 50%;
width: 1px;
height: 0.9em;
margin-top: -0.45em;
background: #d4d4d8;
}
#fed-stakes.company-sector-square {
display: inline-block;
width: 10px;
height: 10px;
margin-right: 6px;
vertical-align: -0.5px;
}
#fed-stakes.company-carousel {
align-items: flex-start;
}
#fed-stakes.company-directory {
padding-top: 0;
padding-bottom: 0;
}
#fed-stakes #chart {
min-height: 0;
}

/* Chart first; compact filters sit between the chart and the cards. */
#fed-stakes.hero {
margin-bottom: 4px;
}
#fed-stakes.chart-panel {
display: flex;
flex-direction: column;
}
#fed-stakes.chart-panel >.exploration-toolbar {
order: 1;
margin: 12px 0 24px;
}
#fed-stakes.rfe-controls.combobox > input[role='combobox'] {
height: 30px;
font-size: var(--type-label);
}
/* A committed sector keeps its legend swatch in the field. */
#fed-stakes.rfe-controls.combobox.has-swatch {
position: relative;
}
#fed-stakes.rfe-controls.combobox.has-swatch::before {
content: '';
position: absolute;
top: 50%;
left: 11px;
transform: translateY(-50%);
width: 9px;
height: 9px;
background: var(--swatch-color);
pointer-events: none;
z-index: 1;
}
#fed-stakes
.rfe-controls
.combobox.has-swatch
> input[role='combobox'] {
padding-inline-start: 28px;
}
/* Filters share the directory toolbar row with pagination. */
#fed-stakes.company-directory {
margin-top: 28px;
}
#fed-stakes.directory-heading > h2 {
flex: 1 1 100%;
margin: 0;
}
/* Inputs shrink toward 170px before the pager wraps to its own line. */
#fed-stakes.directory-filters {
flex: 1 1 352px;
max-width: 532px;
min-width: 0;
}
#fed-stakes.directory-filters.exploration-filters {
flex-wrap: nowrap;
gap: 12px;
width: 100%;
}
#fed-stakes.directory-filters.sector-field,
#fed-stakes.directory-filters.search-actions {
flex: 1 1 0;
width: auto;
max-width: 260px;
}
/* space-between keeps the pager right on a shared row and left once it wraps. */
#fed-stakes.directory-heading.directory-controls {
margin-left: 0;
}
/* Breakpoints follow the embed's column, not the window beside Drupal's sidebar. */
#fed-stakes.company-directory {
container-type: inline-size;
}
@container (max-width: 640px) {
#fed-stakes.directory-filters {
flex-basis: 100%;
max-width: none;
}
#fed-stakes.directory-filters.sector-field,
#fed-stakes.directory-filters.search-actions {
max-width: none;
}
}
/* Wide layouts tuck the title into the chart's empty upper-left corner. */
#fed-stakes.fed-stakes-layout {
container-type: inline-size;
}
@container (min-width: 750px) {
#fed-stakes.hero {
position: relative;
z-index: 1;
max-width: 46%;
margin-bottom: -96px;
}
#fed-stakes.hero h1 {
white-space: nowrap;
}
}
/* Medium layouts keep the title above but tuck the subtitle and key into the
chart's empty corner when placeIntro() finds room; the date range wraps. */
#fed-stakes.intro-inset.hero {
position: relative;
z-index: 1;
}
#fed-stakes.intro-inset.intro {
position: absolute;
top: 100%;
left: 0;
}
#fed-stakes.intro-inset.subtitle-range {
display: block;
}
/* On their own row, the date range and key run on as two sentences. */
#fed-stakes.intro-inline.intro > span {
display: inline;
}
#fed-stakes.intro-inline.intro > span::after {
content: '.';
}
/* The dashboard page supplies its own heading, so the title block drops away. */
#fed-stakes.is-dashboard.hero {
display: none;
}
/* Drupal already spaces the page intro from the embed, so ours drops to zero. */
#fed-stakes.is-dashboard.fed-stakes-layout {
padding-top: 0;
}
/* The dashboard is grid-only, so the count and layout toggle drop away too. */
#fed-stakes.is-dashboard.directory-controls {
display: none;
}
@container (max-width: 460px) {
#fed-stakes.directory-filters.exploration-filters {
flex-wrap: wrap;
}
#fed-stakes.directory-filters.sector-field,
#fed-stakes.directory-filters.search-actions {
flex-basis: 100%;
}
}

#fed-stakes.rfe-controls.combobox {
--font-sans:
'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
--font-mono:
'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
--color-black: #000;
--color-white: #fff;
--spacing: 0.25rem;
--container-xs: 20rem;
--container-sm: 24rem;
--container-md: 28rem;
--container-lg: 32rem;
--text-xs: var(--type-label);
--text-xs--line-height: calc(1 / 0.75);
--text-sm: var(--type-body);
--text-sm--line-height: calc(1.25 / 0.875);
--text-base: 1rem;
--text-base--line-height: calc(1.5 / 1);
--text-lg: 1.125rem;
--text-lg--line-height: calc(1.75 / 1.125);
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--tracking-tight: -0.025em;
--tracking-widest: 0.1em;
--leading-snug: 1.375;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--radius-2xl: 1rem;
--radius-4xl: 2rem;
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
--blur-xs: 4px;
--aspect-video: 16 / 9;
--default-transition-duration: 0.15s;
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
--default-font-family: var(--font-sans);
--default-mono-font-family: var(--font-mono);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--radius: 0.625rem;
--background: oklch(100% 0 0);
--foreground: oklch(14.5% 0 0);
--card: oklch(100% 0 0);
--card-foreground: oklch(14.5% 0 0);
--popover: oklch(100% 0 0);
--popover-foreground: oklch(14.5% 0 0);
--primary: oklch(20.5% 0 0);
--primary-foreground: oklch(98.5% 0 0);
--secondary: oklch(97% 0 0);
--secondary-foreground: oklch(20.5% 0 0);
--muted: oklch(97% 0 0);
--muted-foreground: oklch(55.6% 0 0);
--accent: oklch(97% 0 0);
--accent-foreground: oklch(20.5% 0 0);
--destructive: oklch(57.7% 0.245 27.325);
--border: oklch(92.2% 0 0);
--input: oklch(92.2% 0 0);
--ring: oklch(70.8% 0 0);
--chart-1: #93c5fd;
--chart-2: #3b82f6;
--chart-3: #2563eb;
--chart-4: #1d4ed8;
--chart-5: #1e40af;
--sidebar: oklch(98.5% 0 0);
--sidebar-foreground: oklch(14.5% 0 0);
--sidebar-primary: oklch(20.5% 0 0);
--sidebar-primary-foreground: oklch(98.5% 0 0);
--sidebar-accent: oklch(97% 0 0);
--sidebar-accent-foreground: oklch(20.5% 0 0);
--sidebar-border: oklch(92.2% 0 0);
--sidebar-ring: oklch(70.8% 0 0);
--sidebar-width: 16rem;
--sidebar-mobile-width: 18rem;
--scrollbar-track: transparent;
--scrollbar-thumb: var(--border);
--scrollbar-width: 0.625rem;
--scrollbar-sm-width: 0.375rem;
--scrollbar-radius: 9999px;
--chevron-down-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.556 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/>');
--chevron-down-icon-50: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.556 0 0 / 0.5)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/>');
--check-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.556 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/>');
}
#fed-stakes
.rfe-controls
.button-group:has(
> :is(.dropdown-menu,.popover,.select)
> [data-popover][aria-hidden='false']
) {
z-index: 50;
position: relative;
}
#fed-stakes.rfe-controls.combobox {
flex-direction: column;
width: fit-content;
display: inline-flex;
position: relative;
}
#fed-stakes.rfe-controls.combobox > input[role='combobox'] {
--tw-outline-style: none;
outline-style: none;
width: 100%;
min-width: 0;
}
@media (forced-colors: active) {
#fed-stakes.rfe-controls.combobox > input[role='combobox'] {
outline-offset: 2px;
outline: 2px solid #0000;
}
}
#fed-stakes
.rfe-controls
.combobox
> input[role='combobox']::placeholder {
color: var(--color-muted-foreground);
}
#fed-stakes.rfe-controls.combobox > input[role='combobox']:disabled {
pointer-events: none;
cursor: not-allowed;
opacity: 0.5;
}
#fed-stakes.rfe-controls.combobox.input-group input[role='combobox'],
#fed-stakes
.rfe-controls
.combobox
[data-popover]
input[role='combobox'] {
--tw-outline-style: none;
outline-style: none;
min-width: 0;
}
@media (forced-colors: active) {
#fed-stakes
.rfe-controls
.combobox
.input-group
input[role='combobox'],
#fed-stakes
.rfe-controls
.combobox
[data-popover]
input[role='combobox'] {
outline-offset: 2px;
outline: 2px solid #0000;
}
}
#fed-stakes
.rfe-controls
:is(
.combobox.input-group input[role='combobox'],
.combobox [data-popover] input[role='combobox']
)::placeholder {
color: var(--color-muted-foreground);
}
#fed-stakes
.rfe-controls
:is(
.combobox.input-group input[role='combobox'],
.combobox [data-popover] input[role='combobox']
):disabled {
pointer-events: none;
cursor: not-allowed;
opacity: 0.5;
}
#fed-stakes.rfe-controls.combobox > svg,
#fed-stakes.rfe-controls.combobox-trigger-icon {
pointer-events: none;
flex-shrink: 0;
position: absolute;
}
#fed-stakes
.rfe-controls
.combobox:has(>.combobox-chips)
>.combobox-trigger-icon {
display: none;
}
#fed-stakes.rfe-controls.combobox [data-clear] {
width: calc(var(--spacing) * 6);
height: calc(var(--spacing) * 6);
cursor: default;
appearance: none;
border-style: var(--tw-border-style);
color: currentColor;
background-color: #0000;
border-width: 0;
flex-shrink: 0;
justify-content: center;
align-items: center;
padding: 0;
display: inline-flex;
}
#fed-stakes.rfe-controls.combobox [data-clear]:disabled {
pointer-events: none;
opacity: 0.5;
}
#fed-stakes.rfe-controls.combobox [data-clear][hidden],
#fed-stakes
.rfe-controls
.combobox
.input-group:has([data-clear]:not([hidden]))
button[aria-haspopup='listbox'] {
display: none;
}
#fed-stakes.rfe-controls.combobox [data-popover] {
isolation: isolate;
min-width: max(100%, 9rem);
overflow: hidden;
}
#fed-stakes.rfe-controls.combobox [role='listbox'] {
--tw-outline-style: none;
outline-style: none;
overflow: hidden auto;
}
@media (forced-colors: active) {
#fed-stakes.rfe-controls.combobox [role='listbox'] {
outline-offset: 2px;
outline: 2px solid #0000;
}
}
#fed-stakes
.rfe-controls
.combobox
[role='listbox']
[role='group']:not(:has([role='option']:not([aria-hidden='true']))) {
display: none;
}
#fed-stakes
.rfe-controls
.combobox
[role='listbox']:not(
:has([data-value]:not([aria-hidden='true']))
):before {
text-overflow: ellipsis;
white-space: nowrap;
justify-content: center;
align-items: center;
display: flex;
overflow: hidden;
}
#fed-stakes
.rfe-controls
.combobox
[role='listbox'][data-empty]:not(
:has([data-value]:not([aria-hidden='true']))
):before {
--tw-content: attr(data-empty);
content: var(--tw-content);
}
#fed-stakes
.rfe-controls
.combobox
[role='listbox']:not([data-empty]):not(
:has([data-value]:not([aria-hidden='true']))
):before {
--tw-content: 'No results found';
content: var(--tw-content);
}
#fed-stakes.rfe-controls.combobox [role='option'] {
cursor: default;
text-overflow: ellipsis;
white-space: nowrap;
--tw-outline-style: none;
outline-style: none;
align-items: center;
width: 100%;
display: flex;
position: relative;
overflow: hidden;
}
@media (forced-colors: active) {
#fed-stakes.rfe-controls.combobox [role='option'] {
outline-offset: 2px;
outline: 2px solid #0000;
}
}
#fed-stakes.rfe-controls.combobox [role='option'] {
-webkit-user-select: none;
user-select: none;
}
#fed-stakes.rfe-controls.combobox [role='option']:disabled,
#fed-stakes
.rfe-controls
.combobox
[role='option'][aria-disabled='true'] {
pointer-events: none;
opacity: 0.5;
}
#fed-stakes
.rfe-controls
.combobox
[role='option'][aria-hidden='true'] {
display: none;
}
#fed-stakes.rfe-controls.combobox [role='option'] svg {
pointer-events: none;
flex-shrink: 0;
}
#fed-stakes
.rfe-controls
.combobox
[role='option'][aria-selected='true'] {
background-image: var(--check-icon);
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 0.875rem;
}
#fed-stakes
.rfe-controls
.combobox
[role='option'][aria-selected='true']:dir(rtl) {
background-position: 0.5rem;
}
#fed-stakes.rfe-controls.combobox [role='separator'] {
border-style: var(--tw-border-style);
border-width: 0;
}
#fed-stakes.rfe-controls.combobox [role='option'].active,
#fed-stakes.rfe-controls.combobox [role='option']:focus-visible,
#fed-stakes
.rfe-controls
.combobox:not([data-combobox-initialized])
[role='option']:not([aria-disabled='true']):not(:disabled):hover {
background-color: var(--color-accent);
color: var(--color-accent-foreground);
}
#fed-stakes.rfe-controls.combobox-chips {
flex-wrap: wrap;
align-items: center;
width: 100%;
min-width: 0;
display: flex;
}
#fed-stakes.rfe-controls.combobox-chips > input[role='combobox'] {
min-width: calc(var(--spacing) * 16);
--tw-outline-style: none;
background-color: #0000;
outline-style: none;
flex: 1;
}
@media (forced-colors: active) {
#fed-stakes.rfe-controls.combobox-chips > input[role='combobox'] {
outline-offset: 2px;
outline: 2px solid #0000;
}
}
#fed-stakes
.rfe-controls
.combobox-chips
> input[role='combobox']::placeholder {
color: var(--color-muted-foreground);
}
#fed-stakes
.rfe-controls
.combobox-chips
> input[role='combobox']:disabled {
pointer-events: none;
cursor: not-allowed;
opacity: 0.5;
}
#fed-stakes.rfe-controls.combobox-chip {
white-space: nowrap;
flex-shrink: 0;
align-items: center;
width: fit-content;
display: inline-flex;
}
#fed-stakes.rfe-controls.combobox-chip-remove {
flex-shrink: 0;
justify-content: center;
align-items: center;
display: inline-flex;
}
#fed-stakes.rfe-controls.dropdown-menu > [data-popover] {
min-width: anchor-size(width);
}
#fed-stakes.rfe-controls [data-popover] {
visibility: visible;
z-index: 50;
--tw-scale-x: 100%;
--tw-scale-y: 100%;
--tw-scale-z: 100%;
width: max-content;
min-width: 100%;
scale: var(--tw-scale-x) var(--tw-scale-y);
opacity: 1;
--tw-outline-style: none;
outline-style: none;
position: absolute;
overflow: hidden auto;
}
@media (forced-colors: active) {
#fed-stakes.rfe-controls [data-popover] {
outline-offset: 2px;
outline: 2px solid #0000;
}
}
#fed-stakes.rfe-controls [data-popover] {
transition-property: all;
transition-timing-function: var(
--tw-ease,
var(--default-transition-timing-function)
);
transition-duration: var(
--tw-duration,
var(--default-transition-duration)
);
}
#fed-stakes.rfe-controls [data-popover][aria-hidden='true'] {
visibility: hidden;
--tw-scale-x: 95%;
--tw-scale-y: 95%;
--tw-scale-z: 95%;
scale: var(--tw-scale-x) var(--tw-scale-y);
opacity: 0;
}
#fed-stakes
.rfe-controls
[data-popover][aria-hidden='true']:not([data-side]),
#fed-stakes
.rfe-controls
[data-popover][aria-hidden='true'][data-side='bottom'] {
--tw-translate-y: calc(var(--spacing) * -2);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
#fed-stakes
.rfe-controls
[data-popover][aria-hidden='true'][data-side='top'] {
--tw-translate-y: calc(var(--spacing) * 2);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
#fed-stakes
.rfe-controls
[data-popover][aria-hidden='true'][data-side='left'] {
--tw-translate-x: calc(var(--spacing) * 2);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
#fed-stakes
.rfe-controls
[data-popover][aria-hidden='true'][data-side='right'] {
--tw-translate-x: calc(var(--spacing) * -2);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
#fed-stakes
.rfe-controls
[data-popover][aria-hidden='true'][data-side='inline-start'] {
--tw-translate-x: calc(var(--spacing) * 2);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
#fed-stakes
.rfe-controls
[data-popover][aria-hidden='true'][data-side='inline-start']:dir(
rtl
) {
--tw-translate-x: calc(var(--spacing) * -2);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
#fed-stakes
.rfe-controls
[data-popover][aria-hidden='true'][data-side='inline-end'] {
--tw-translate-x: calc(var(--spacing) * -2);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
#fed-stakes
.rfe-controls
[data-popover][aria-hidden='true'][data-side='inline-end']:dir(rtl) {
--tw-translate-x: calc(var(--spacing) * 2);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
#fed-stakes.rfe-controls [data-popover]:not([data-side]),
#fed-stakes.rfe-controls [data-popover][data-side='bottom'] {
margin-top: var(--spacing);
top: 100%;
}
#fed-stakes.rfe-controls [data-popover][data-side='top'] {
margin-bottom: var(--spacing);
bottom: 100%;
}
#fed-stakes.rfe-controls [data-popover][data-side='left'] {
margin-inline-end: var(--spacing);
inset-inline-end: 100%;
}
#fed-stakes.rfe-controls [data-popover][data-side='right'] {
margin-inline-start: var(--spacing);
inset-inline-start: 100%;
}
#fed-stakes.rfe-controls [data-popover][data-side='inline-start'] {
margin-inline-end: var(--spacing);
inset-inline-end: 100%;
}
#fed-stakes.rfe-controls [data-popover][data-side='inline-end'] {
margin-inline-start: var(--spacing);
inset-inline-start: 100%;
}
#fed-stakes
.rfe-controls
:is(
[data-popover]:not([data-side]),
[data-popover][data-side='bottom'],
[data-popover][data-side='top']
):not([data-align]),
#fed-stakes
.rfe-controls
:is(
[data-popover]:not([data-side]),
[data-popover][data-side='bottom'],
[data-popover][data-side='top']
)[data-align='start'] {
inset-inline-start: calc(var(--spacing) * 0);
}
#fed-stakes
.rfe-controls
:is(
[data-popover]:not([data-side]),
[data-popover][data-side='bottom'],
[data-popover][data-side='top']
)[data-align='end'] {
inset-inline-end: calc(var(--spacing) * 0);
}
#fed-stakes
.rfe-controls
:is(
[data-popover]:not([data-side]),
[data-popover][data-side='bottom'],
[data-popover][data-side='top']
)[data-align='center'] {
--tw-translate-x: calc(calc(1 / 2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
inset-inline-start: 50%;
}
#fed-stakes
.rfe-controls
:is(
[data-popover][data-side='left'],
[data-popover][data-side='right'],
[data-popover][data-side='inline-start'],
[data-popover][data-side='inline-end']
):not([data-align]),
#fed-stakes
.rfe-controls
:is(
[data-popover][data-side='left'],
[data-popover][data-side='right'],
[data-popover][data-side='inline-start'],
[data-popover][data-side='inline-end']
)[data-align='start'] {
top: 0;
}
#fed-stakes
.rfe-controls
:is(
[data-popover][data-side='left'],
[data-popover][data-side='right'],
[data-popover][data-side='inline-start'],
[data-popover][data-side='inline-end']
)[data-align='end'] {
bottom: 0;
}
#fed-stakes
.rfe-controls
:is(
[data-popover][data-side='left'],
[data-popover][data-side='right'],
[data-popover][data-side='inline-start'],
[data-popover][data-side='inline-end']
)[data-align='center'] {
--tw-translate-y: calc(calc(1 / 2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
top: 50%;
}
#fed-stakes.rfe-controls.select:not(select) [data-popover] {
isolation: isolate;
min-width: max(100%, 9rem);
overflow-x: hidden;
}
#fed-stakes.rfe-controls.combobox > input[role='combobox'] {
height: calc(var(--spacing) * 9);
border-radius: var(--radius-md);
border-style: var(--tw-border-style);
border-width: 1px;
border-color: var(--color-input);
padding-block: calc(var(--spacing) * 2);
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, #0000000d);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
transition-property: color, box-shadow;
transition-timing-function: var(
--tw-ease,
var(--default-transition-timing-function)
);
transition-duration: var(
--tw-duration,
var(--default-transition-duration)
);
background-color: #0000;
padding-inline-start: calc(var(--spacing) * 2.5);
padding-inline-end: calc(var(--spacing) * 8);
}
#fed-stakes
.rfe-controls
.combobox
> input[role='combobox']:focus-visible {
border-color: var(--color-ring);
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0
calc(3px + var(--tw-ring-offset-width))
var(--tw-ring-color, currentcolor);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
--tw-ring-color: var(--color-ring);
}
@supports (color: color-mix(in lab, red, red)) {
#fed-stakes
.rfe-controls
.combobox
> input[role='combobox']:focus-visible {
--tw-ring-color: color-mix(
in oklab,
var(--color-ring) 50%,
transparent
);
}
}
#fed-stakes
.rfe-controls
.combobox
> input[role='combobox'][aria-invalid='true'] {
border-color: var(--color-destructive);
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0
calc(3px + var(--tw-ring-offset-width))
var(--tw-ring-color, currentcolor);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
--tw-ring-color: var(--color-destructive);
}
@supports (color: color-mix(in lab, red, red)) {
#fed-stakes
.rfe-controls
.combobox
> input[role='combobox'][aria-invalid='true'] {
--tw-ring-color: color-mix(
in oklab,
var(--color-destructive) 20%,
transparent
);
}
}
#fed-stakes.rfe-controls.combobox > svg,
#fed-stakes.rfe-controls.combobox-trigger-icon {
width: calc(var(--spacing) * 4);
height: calc(var(--spacing) * 4);
--tw-translate-y: calc(calc(1 / 2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
color: var(--color-muted-foreground);
inset-inline-end: calc(var(--spacing) * 2.5);
top: 50%;
}
#fed-stakes.rfe-controls.combobox [data-popover] {
max-height: calc(var(--spacing) * 72);
border-radius: var(--radius-md);
background-color: var(--color-popover);
color: var(--color-popover-foreground);
--tw-shadow:
0 4px 6px -1px var(--tw-shadow-color, #0000001a),
0 2px 4px -2px var(--tw-shadow-color, #0000001a);
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0
calc(1px + var(--tw-ring-offset-width))
var(--tw-ring-color, currentcolor);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
--tw-ring-color: color-mix(
in oklab,
var(--color-foreground) 10%,
transparent
);
--tw-duration: 0.1s;
gap: 0;
padding: 0;
transition-duration: 0.1s;
}
#fed-stakes.rfe-controls.combobox [role='listbox'] {
max-height: min(18rem, var(--available-height, 18rem));
scroll-padding-block: var(--spacing);
padding: var(--spacing);
overflow-y: auto;
}
#fed-stakes
.rfe-controls
.combobox
[role='listbox']:not(:has([data-value]:not([aria-hidden='true']))) {
padding: 0;
}
#fed-stakes
.rfe-controls
.combobox
[role='listbox']:not(
:has([data-value]:not([aria-hidden='true']))
):before {
padding-inline: calc(var(--spacing) * 3);
padding-block: calc(var(--spacing) * 2);
text-align: center;
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
color: var(--color-muted-foreground);
}
#fed-stakes.rfe-controls.combobox [role='heading'] {
padding-inline: calc(var(--spacing) * 2);
padding-block: calc(var(--spacing) * 1.5);
font-size: var(--text-xs);
line-height: var(--tw-leading, var(--text-xs--line-height));
color: var(--color-muted-foreground);
}
#fed-stakes.rfe-controls.combobox [role='option'] {
gap: calc(var(--spacing) * 2);
border-radius: var(--radius-sm);
padding-block: calc(var(--spacing) * 1.5);
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
padding-inline-start: calc(var(--spacing) * 2);
padding-inline-end: calc(var(--spacing) * 8);
}
#fed-stakes
.rfe-controls
.combobox
[role='option']
svg:not([class*='size-']) {
width: calc(var(--spacing) * 4);
height: calc(var(--spacing) * 4);
}
#fed-stakes.rfe-controls.combobox [role='option'].active,
#fed-stakes.rfe-controls.combobox [role='option']:focus-visible {
background-color: var(--color-accent);
color: var(--color-accent-foreground);
}
#fed-stakes
.rfe-controls
:is(
.combobox [role='option'].active,
.combobox [role='option']:focus-visible
)
svg {
color: var(--color-accent-foreground);
}
#fed-stakes.rfe-controls.combobox [role='group'] {
scroll-margin-block: var(--spacing);
padding: var(--spacing);
}
#fed-stakes.rfe-controls.combobox [role='separator'] {
margin-inline: calc(var(--spacing) * -1);
margin-block: var(--spacing);
background-color: var(--color-border);
height: 1px;
}
#fed-stakes.rfe-controls.combobox-chips {
min-height: calc(var(--spacing) * 9);
gap: calc(var(--spacing) * 1.5);
border-radius: var(--radius-md);
border-style: var(--tw-border-style);
border-width: 1px;
border-color: var(--color-input);
padding-inline: calc(var(--spacing) * 1.5);
padding-block: calc(var(--spacing) * 1.5);
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, #0000000d);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
transition-property: color, box-shadow;
transition-timing-function: var(
--tw-ease,
var(--default-transition-timing-function)
);
transition-duration: var(
--tw-duration,
var(--default-transition-duration)
);
background-color: #0000;
background-clip: padding-box;
}
#fed-stakes.rfe-controls.combobox-chips:focus-within {
border-color: var(--color-ring);
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0
calc(3px + var(--tw-ring-offset-width))
var(--tw-ring-color, currentcolor);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
--tw-ring-color: var(--color-ring);
}
@supports (color: color-mix(in lab, red, red)) {
#fed-stakes.rfe-controls.combobox-chips:focus-within {
--tw-ring-color: color-mix(
in oklab,
var(--color-ring) 50%,
transparent
);
}
}
#fed-stakes.rfe-controls.combobox-chips:has([aria-invalid='true']) {
border-color: var(--color-destructive);
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0
calc(3px + var(--tw-ring-offset-width))
var(--tw-ring-color, currentcolor);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
--tw-ring-color: var(--color-destructive);
}
@supports (color: color-mix(in lab, red, red)) {
#fed-stakes.rfe-controls.combobox-chips:has([aria-invalid='true']) {
--tw-ring-color: color-mix(
in oklab,
var(--color-destructive) 20%,
transparent
);
}
}
#fed-stakes.rfe-controls.combobox-chip {
height: calc(var(--spacing) * 5.5);
gap: var(--spacing);
border-radius: var(--radius-sm);
background-color: var(--color-muted);
padding-inline: calc(var(--spacing) * 1.5);
font-size: var(--text-xs);
line-height: var(--tw-leading, var(--text-xs--line-height));
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
color: var(--color-foreground);
}
#fed-stakes.rfe-controls.combobox-chip:has(>.combobox-chip-remove) {
padding-inline-end: 0;
}
#fed-stakes.rfe-controls.combobox-chip-remove {
opacity: 0.5;
margin-inline-start: calc(var(--spacing) * -1);
}
@media (hover: hover) {
#fed-stakes.rfe-controls.combobox-chip-remove:hover {
opacity: 1;
}
}
#fed-stakes.rfe-controls.combobox-chip-remove svg {
width: calc(var(--spacing) * 3.5);
height: calc(var(--spacing) * 3.5);
}
#fed-stakes.rfe-controls.combobox [data-popover] >.input-group {
margin: var(--spacing);
height: calc(var(--spacing) * 8);
border-color: var(--color-input);
width: auto;
margin-bottom: 0;
}
@supports (color: color-mix(in lab, red, red)) {
#fed-stakes.rfe-controls.combobox [data-popover] >.input-group {
border-color: color-mix(
in oklab,
var(--color-input) 30%,
transparent
);
}
}
#fed-stakes.rfe-controls.combobox [data-popover] >.input-group {
background-color: var(--color-input);
}
@supports (color: color-mix(in lab, red, red)) {
#fed-stakes.rfe-controls.combobox [data-popover] >.input-group {
background-color: color-mix(
in oklab,
var(--color-input) 30%,
transparent
);
}
}
#fed-stakes.rfe-controls.combobox [data-popover] >.input-group {
--tw-shadow: 0 0 #0000;
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
}
#fed-stakes
.rfe-controls
.combobox
[data-popover]
>.input-group:focus-within {
border-color: inherit;
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0
calc(0px + var(--tw-ring-offset-width))
var(--tw-ring-color, currentcolor);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
}
#fed-stakes
.rfe-controls
.combobox
[data-popover]
> input[role='combobox'] {
margin-bottom: var(--spacing);
width: 100%;
}
#fed-stakes.rfe-controls.select:not(select) [data-popover] {
border-radius: var(--radius-md);
background-color: var(--color-popover);
padding: var(--spacing);
color: var(--color-popover-foreground);
--tw-shadow:
0 4px 6px -1px var(--tw-shadow-color, #0000001a),
0 2px 4px -2px var(--tw-shadow-color, #0000001a);
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0
calc(1px + var(--tw-ring-offset-width))
var(--tw-ring-color, currentcolor);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
--tw-ring-color: color-mix(
in oklab,
var(--color-foreground) 10%,
transparent
);
--tw-duration: 0.1s;
transition-duration: 0.1s;
}
#fed-stakes.rfe-controls.dropdown-menu > [data-popover] {
border-radius: var(--radius-md);
background-color: var(--color-popover);
padding: var(--spacing);
color: var(--color-popover-foreground);
--tw-shadow:
0 4px 6px -1px var(--tw-shadow-color, #0000001a),
0 2px 4px -2px var(--tw-shadow-color, #0000001a);
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0
calc(1px + var(--tw-ring-offset-width))
var(--tw-ring-color, currentcolor);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
--tw-ring-color: color-mix(
in oklab,
var(--color-foreground) 10%,
transparent
);
--tw-duration: 0.1s;
gap: 0;
transition-duration: 0.1s;
overflow: hidden auto;
}
#fed-stakes.rfe-controls [data-popover] {
gap: calc(var(--spacing) * 4);
border-radius: var(--radius-md);
background-color: var(--color-popover);
padding: calc(var(--spacing) * 4);
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
color: var(--color-popover-foreground);
--tw-shadow:
0 4px 6px -1px var(--tw-shadow-color, #0000001a),
0 2px 4px -2px var(--tw-shadow-color, #0000001a);
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0
calc(1px + var(--tw-ring-offset-width))
var(--tw-ring-color, currentcolor);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
var(--tw-shadow);
--tw-ring-color: color-mix(
in oklab,
var(--color-foreground) 10%,
transparent
);
--tw-duration: 0.1s;
flex-direction: column;
transition-duration: 0.1s;
display: flex;
}
#fed-stakes.rfe-controls.popover > [data-popover] > header,
#fed-stakes.rfe-controls.popover > [data-popover] > [data-header] {
gap: var(--spacing);
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
flex-direction: column;
display: flex;
}
#fed-stakes
.rfe-controls
.popover
> [data-popover]
> header
> :is(h1, h2, h3, h4, h5, h6),
#fed-stakes
.rfe-controls
.popover
> [data-popover]
> :is(h1, h2, h3, h4, h5, h6, [data-title]) {
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
}
#fed-stakes.rfe-controls.popover > [data-popover] > header > p,
#fed-stakes
.rfe-controls
.popover
> [data-popover]
> [data-description] {
color: var(--color-muted-foreground);
}
@property --tw-border-style {
syntax: '*';
inherits: false;
initial-value: solid;
}
@property --tw-content {
syntax: '*';
inherits: false;
initial-value: '';
}
@property --tw-shadow {
syntax: '*';
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-shadow-color {
syntax: '*';
inherits: false;
}
@property --tw-shadow-alpha {
syntax: '<percentage>';
inherits: false;
initial-value: 100%;
}
@property --tw-inset-shadow {
syntax: '*';
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-inset-shadow-color {
syntax: '*';
inherits: false;
}
@property --tw-inset-shadow-alpha {
syntax: '<percentage>';
inherits: false;
initial-value: 100%;
}
@property --tw-ring-color {
syntax: '*';
inherits: false;
}
@property --tw-ring-shadow {
syntax: '*';
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-inset-ring-color {
syntax: '*';
inherits: false;
}
@property --tw-inset-ring-shadow {
syntax: '*';
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-ring-inset {
syntax: '*';
inherits: false;
}
@property --tw-ring-offset-width {
syntax: '<length>';
inherits: false;
initial-value: 0;
}
@property --tw-ring-offset-color {
syntax: '*';
inherits: false;
initial-value: #fff;
}
@property --tw-ring-offset-shadow {
syntax: '*';
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-space-x-reverse {
syntax: '*';
inherits: false;
initial-value: 0;
}
@property --tw-leading {
syntax: '*';
inherits: false;
}
@property --tw-translate-x {
syntax: '*';
inherits: false;
initial-value: 0;
}
@property --tw-translate-y {
syntax: '*';
inherits: false;
initial-value: 0;
}
@property --tw-translate-z {
syntax: '*';
inherits: false;
initial-value: 0;
}
@property --tw-duration {
syntax: '*';
inherits: false;
}
@property --tw-ease {
syntax: '*';
inherits: false;
}
@property --tw-font-weight {
syntax: '*';
inherits: false;
}
@property --tw-ordinal {
syntax: '*';
inherits: false;
}
@property --tw-slashed-zero {
syntax: '*';
inherits: false;
}
@property --tw-numeric-figure {
syntax: '*';
inherits: false;
}
@property --tw-numeric-spacing {
syntax: '*';
inherits: false;
}
@property --tw-numeric-fraction {
syntax: '*';
inherits: false;
}
@property --tw-scale-x {
syntax: '*';
inherits: false;
initial-value: 1;
}
@property --tw-scale-y {
syntax: '*';
inherits: false;
initial-value: 1;
}
@property --tw-scale-z {
syntax: '*';
inherits: false;
initial-value: 1;
}
@property --tw-tracking {
syntax: '*';
inherits: false;
}
@property --tw-outline-style {
syntax: '*';
inherits: false;
initial-value: solid;
}
@property --tw-backdrop-blur {
syntax: '*';
inherits: false;
}
@property --tw-backdrop-brightness {
syntax: '*';
inherits: false;
}
@property --tw-backdrop-contrast {
syntax: '*';
inherits: false;
}
@property --tw-backdrop-grayscale {
syntax: '*';
inherits: false;
}
@property --tw-backdrop-hue-rotate {
syntax: '*';
inherits: false;
}
@property --tw-backdrop-invert {
syntax: '*';
inherits: false;
}
@property --tw-backdrop-opacity {
syntax: '*';
inherits: false;
}
@property --tw-backdrop-saturate {
syntax: '*';
inherits: false;
}
@property --tw-backdrop-sepia {
syntax: '*';
inherits: false;
}

/* Project sizing; Basecoat owns the controls and option states. */
#fed-stakes.rfe-controls.combobox {
width: 100%;
min-width: 0;
--radius: var(--interface-radius);
--radius-md: var(--interface-radius);
--radius-sm: var(--interface-radius);
--input: var(--bs-border-color, rgb(26 23 20 / 0.3));
--tw-border-style: solid;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-inset: initial;
--tw-shadow: 0 0 #0000;
--tw-inset-shadow: 0 0 #0000;
--tw-inset-ring-shadow: 0 0 #0000;
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
}
#fed-stakes.rfe-controls.combobox > input[role='combobox'] {
height: 36px;
padding-inline: 11px 64px;
font: inherit;
font-size: var(--type-label);
}
/* iOS Safari zooms into any field under 16px on focus and never zooms back. */
@media (pointer: coarse) {
#fed-stakes.rfe-controls.combobox > input[role='combobox'],
#fed-stakes.rfe-controls.combobox-chips > input[role='combobox'] {
font-size: 16px;
}
}
#fed-stakes.rfe-controls.combobox >.combobox-trigger-icon {
width: 16px;
height: 16px;
right: 11px;
top: 50%;
translate: 0 -50%;
}
#fed-stakes.rfe-controls.combobox > [data-clear] {
position: absolute;
right: 30px;
top: 4px;
width: 28px;
height: 28px;
padding: 0;
border: 0;
border-radius: var(--interface-radius);
background: transparent;
color: var(--color-muted-foreground);
}
#fed-stakes.rfe-controls.combobox > [data-clear]:hover {
background: var(--color-accent);
}
#fed-stakes.rfe-controls.combobox > [data-clear] svg {
width: 14px;
height: 14px;
}
#fed-stakes.rfe-controls.combobox [role='listbox'] {
max-height: 260px;
}
#fed-stakes.rfe-controls.combobox [role='option'] {
font-size: var(--type-label);
gap: 8px;
}
#fed-stakes.rfe-controls.combobox [data-popover] {
max-width: min(360px, 90vw);
/* The dropdown's outline uses the shared border color too. */
--tw-ring-color: var(--input);
}
#fed-stakes.rfe-controls.combobox.sector-option-count {
color: var(--color-muted-foreground);
}
#fed-stakes.rfe-controls.combobox [role='status'] {
padding: 6px 8px;
margin: 0;
font-size: var(--type-note);
color: var(--color-muted-foreground);
}

/* Download menu: the last action in the filter toolbar, after the pager. */
#fed-stakes.download-menu {
position: relative;
flex: none;
}
/* The pager and Download share the toolbar's right-hand group. */
#fed-stakes.toolbar-actions {
display: flex;
align-items: center;
gap: 20px;
}
#fed-stakes #download-toggle {
display: inline-flex;
align-items: center;
gap: 6px;
height: 36px;
padding: 0 12px;
border: 1px solid var(--border);
border-radius: var(--interface-radius);
background: #fff;
/* Same ink as the toolbar's other buttons (arrows, Show grid). */
color: var(--foreground);
font-size: var(--type-label);
}
#fed-stakes #download-toggle:hover,
#fed-stakes #download-toggle[aria-expanded='true'] {
background: #f4f4f5;
}
#fed-stakes #download-options {
position: absolute;
right: 0;
top: calc(100% + 4px);
z-index: 20;
display: flex;
flex-direction: column;
min-width: 180px;
padding: 4px;
border-radius: var(--interface-radius);
background: #fff;
box-shadow:
0 0 0 1px var(--border),
0 4px 6px -1px #0000001a,
0 2px 4px -2px #0000001a;
}
#fed-stakes #download-options button {
height: auto;
padding: 6px 8px;
border: 0;
border-radius: var(--interface-radius);
background: transparent;
color: var(--foreground);
font-size: var(--type-label);
text-align: left;
white-space: nowrap;
}
#fed-stakes #download-options button:hover,
#fed-stakes #download-options button:focus-visible {
background: #f4f4f5;
outline: none;
}
/* Narrow columns: an icon-only square, so the toolbar doesn't wrap again. */
@container (max-width: 640px) {
#fed-stakes #download-toggle {
width: 36px;
padding: 0;
justify-content: center;
}
#fed-stakes #download-toggle.download-label {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
}
/* The dashboard's Download wraps to its own row: a labelled button under
the filters, with the menu opening from its left edge. */
#fed-stakes.is-dashboard #download-toggle {
width: auto;
padding: 0 12px;
}
#fed-stakes.is-dashboard #download-toggle.download-label {
position: static;
width: auto;
height: auto;
overflow: visible;
clip: auto;
}
#fed-stakes.is-dashboard #download-options {
left: 0;
right: auto;
}
}
#fed-stakes #download-options button:disabled {
color: var(--muted);
cursor: progress;
}
/* Narrow screens open on the total; a soft left fade and a short cue say
earlier months are off to the left. Both go once the reader reaches the start. */
#fed-stakes #chart.has-earlier {
-webkit-mask-image: linear-gradient(to right, #0006, #000 28px);
mask-image: linear-gradient(to right, #0006, #000 28px);
}
/* The cue sits in the empty space under the staircase, just above the axis,
pinned to this frame so it stays put while the months scroll. */
#fed-stakes.chart-frame {
position: relative;
}
#fed-stakes.scroll-cue {
position: absolute;
left: 0;
top: 352px;
padding: 2px 4px 2px 0;
border: 0;
background: none;
color: var(--muted);
font-size: var(--type-note);
transition: opacity 200ms;
}
#fed-stakes.scroll-cue.is-done {
opacity: 0;
visibility: hidden;
}
@media (prefers-reduced-motion: reduce) {
#fed-stakes.scroll-cue {
transition: none;
}
}
# The US Government’s Equity Portfolio

Federal equity positions announced Jun 2025–Sep 2026 Each square is one company

← Swipe for earlier months 

## Company details

Sector

Company 

‹ › 

Show grid 

Download Chart image (PNG) Data (CSV) 

No companies match these filters. Clear the search or choose another sector.

(() => {
const componentRegistry = {};
let observer = null;

const registerComponent = (name, selectorOrOptions, initFunction) => {
const options =
typeof selectorOrOptions === 'object'
? selectorOrOptions
: { selector: selectorOrOptions, init: initFunction };

componentRegistry[name] = {
selector: options.selector,
init: options.init,
refresh: options.refresh,
};
};

const initComponent = (element, componentName) => {
const component = componentRegistry[componentName];
if (!component) return;

try {
component.init(element);
if (element.hasAttribute(`data-${componentName}-initialized`)) {
element.dataset.basecoatComponent = componentName;
}
} catch (error) {
console.error(`Failed to initialize ${componentName}:`, error);
if (typeof element._destroy === 'function') {
try {
element._destroy();
} catch (destroyError) {
console.error(
`Failed to clean up ${componentName} after initialization error:`,
destroyError,
);
}
}
delete element._destroy;
element.removeAttribute(`data-${componentName}-initialized`);
delete element.dataset.basecoatComponent;
}
};

const destroyComponent = element => {
if (!element || element.nodeType !== Node.ELEMENT_NODE) return;
const componentName = element.dataset?.basecoatComponent;

if (typeof element._destroy === 'function') {
try {
element._destroy();
} catch (error) {
console.error('Failed to destroy Basecoat component:', error);
}
}

delete element._destroy;
if (componentName)
element.removeAttribute(`data-${componentName}-initialized`);
delete element.dataset.basecoatComponent;
};

const destroyRemovedComponents = node => {
if (node.nodeType !== Node.ELEMENT_NODE) return;
if (node.isConnected) return;

if (node.dataset?.basecoatComponent) destroyComponent(node);
node
.querySelectorAll('[data-basecoat-component]')
.forEach(destroyComponent);
};

const uniqueElements = elements => Array.from(new Set(elements));

const getComponentElements = (
componentName,
selector,
force = false,
) => {
const elements = Array.from(document.querySelectorAll(selector));
if (force) {
elements.push(
...document.querySelectorAll(
`[data-basecoat-component="${componentName}"]`,
),
);
}
return uniqueElements(elements);
};

const initAllComponents = (options = {}) => {
const force = options.force === true;
Object.entries(componentRegistry).forEach(
([name, { selector }]) => {
getComponentElements(name, selector, force).forEach(element => {
const wasComponent =
element.dataset?.basecoatComponent === name;
if (force) destroyComponent(element);
if (wasComponent || element.matches(selector))
initComponent(element, name);
});
},
);
};

const initNewComponents = node => {
if (node.nodeType !== Node.ELEMENT_NODE) return;

Object.entries(componentRegistry).forEach(
([name, { selector }]) => {
if (node.matches(selector)) initComponent(node, name);
node
.querySelectorAll(selector)
.forEach(element => initComponent(element, name));
},
);
};

const refreshComponent = element => {
if (!element) return;
if (typeof element.refresh === 'function') {
element.refresh();
return;
}

const componentName = element.dataset?.basecoatComponent;
const component = componentName
? componentRegistry[componentName]
: null;
if (component?.refresh) {
component.refresh(element);
}
};

const startObserver = () => {
if (observer) return;

observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(initNewComponents);
mutation.removedNodes.forEach(destroyRemovedComponents);
});
});

observer.observe(document.body, { childList: true, subtree: true });
};

const stopObserver = () => {
if (!observer) return;
observer.disconnect();
observer = null;
};

const initRegisteredComponent = (componentName, options = {}) => {
const component = componentRegistry[componentName];
if (!component) {
console.warn(
`Component '${componentName}' not found in registry`,
);
return;
}

const force = options.force === true;
getComponentElements(
componentName,
component.selector,
force,
).forEach(element => {
const wasComponent =
element.dataset?.basecoatComponent === componentName;
if (force) destroyComponent(element);
if (wasComponent || element.matches(component.selector))
initComponent(element, componentName);
});
};

const initAllRegisteredComponents = (options = {}) => {
initAllComponents(options);
};
window.basecoat = {
register: registerComponent,
init: initRegisteredComponent,
initAll: initAllRegisteredComponents,
refresh: refreshComponent,
start: startObserver,
stop: stopObserver,
};

document.addEventListener('DOMContentLoaded', () => {
initAllComponents();
startObserver();
});
})();

(() => {
const states = new WeakMap();

const getElements = root => {
const popover = root.querySelector(':scope > [data-popover]');
const input =
root.querySelector(
':scope > input[role="combobox"], :scope >.input-group input[role="combobox"], :scope >.combobox-chips input[role="combobox"]',
) || popover?.querySelector('input[role="combobox"]');
const chips = root.querySelector(':scope >.combobox-chips');
const popupTrigger = root.querySelector(
':scope > button[aria-haspopup="listbox"]',
);
const trigger =
popupTrigger ||
root.querySelector(
':scope >.input-group button[aria-haspopup="listbox"]',
);
const clearButton = root.querySelector('[data-clear]');
const valueTarget =
popupTrigger?.querySelector('[data-value]') ||
(popupTrigger?.matches('[data-value]') ? popupTrigger : null);
const listbox = popover
? popover.querySelector('[role="listbox"]')
: null;
const hiddenInput = root.querySelector(
':scope > input[type="hidden"]',
);
return {
input,
chips,
trigger,
clearButton,
valueTarget,
popover,
listbox,
hiddenInput,
};
};

const ensureMultipleInputSurface = (root, elements) => {
if (
elements.listbox?.getAttribute('aria-multiselectable') !==
'true' ||
elements.chips ||
!elements.input
)
return elements;
if (elements.input.parentElement !== root) return elements;

const chips = document.createElement('div');
chips.className = 'combobox-chips';
root.insertBefore(chips, elements.input);
chips.appendChild(elements.input);

return getElements(root);
};

const getValue = option =>
option.dataset.value ?? option.textContent.trim();
const getLabel = option =>
option.dataset.label || option.textContent.trim();
const getFormat = root =>
root.dataset.format === 'object' ? 'object' : 'value';
const isDisabled = option =>
option.getAttribute('aria-disabled') === 'true';
const toSelected = option => ({
value: getValue(option),
label: getLabel(option),
});
const normalizeEntry = entry => {
if (entry && typeof entry === 'object') {
const value = entry.value == null ? '' : String(entry.value);
return value
? { value, label: String(entry.label ?? entry.value) }
: null;
}
const value = entry == null ? '' : String(entry);
return value ? { value, label: value } : null;
};

const getOptions = listbox => {
const allOptions = Array.from(
listbox.querySelectorAll('[role="option"]'),
);
return {
allOptions,
options: allOptions.filter(option => !isDisabled(option)),
};
};

const getSelection = state => Array.from(state.selected.values());
const getCanonicalValue = state =>
state.isMultiple
? getSelection(state).map(item => item.value)
: getSelection(state)[0]?.value || '';
const getSelectedDetail = state =>
state.isMultiple
? getSelection(state)
: getSelection(state)[0] || null;

const serializeSelection = state => {
const selected = getSelection(state);
if (state.format === 'object') {
return JSON.stringify(
state.isMultiple ? selected : selected[0] || null,
);
}
const value = selected.map(item => item.value);
return state.isMultiple ? JSON.stringify(value) : value[0] || '';
};

const parseStoredSelection = (storedValue, inputValue, state) => {
if (state.isMultiple) {
let parsed = [];
try {
parsed = JSON.parse(storedValue || '[]');
} catch (_) {
parsed = [];
}
if (!Array.isArray(parsed)) return [];
return parsed
.map(item => {
const entry = normalizeEntry(
state.format === 'object'
? item
: {
value: item,
label:
state.selected.get(String(item))?.label ?? item,
},
);
if (!entry) return null;
const option = state.options.find(
opt => getValue(opt) === entry.value,
);
return option ? toSelected(option) : entry;
})
.filter(Boolean);
}

if (state.format === 'object') {
try {
const entry = normalizeEntry(JSON.parse(storedValue || 'null'));
if (!entry) return [];
const option = state.options.find(
opt => getValue(opt) === entry.value,
);
return [option ? toSelected(option) : entry];
} catch (_) {
return [];
}
}

const value = storedValue || '';
if (!value) return [];
const option = state.options.find(opt => getValue(opt) === value);
return [
option
? toSelected(option)
: {
value,
label:
state.selected.get(value)?.label || inputValue || value,
},
];
};

const scrollOptionIntoListbox = (state, option) => {
const optionRect = option.getBoundingClientRect();
const listboxRect = state.listbox.getBoundingClientRect();

if (optionRect.top < listboxRect.top) {
state.listbox.scrollTop -= listboxRect.top - optionRect.top;
} else if (optionRect.bottom > listboxRect.bottom) {
state.listbox.scrollTop += optionRect.bottom - listboxRect.bottom;
}
};

const setActiveOption = (state, index) => {
if (state.activeIndex > -1 && state.options[state.activeIndex]) {
state.options[state.activeIndex].classList.remove('active');
}

state.activeIndex = index;

if (state.activeIndex > -1) {
const activeOption = state.options[state.activeIndex];
activeOption.classList.add('active');
if (!activeOption.id)
activeOption.id = `${state.listbox.id || state.root.id || 'combobox'}-option-${state.activeIndex}`;
state.input.setAttribute(
'aria-activedescendant',
activeOption.id,
);
} else {
state.input.removeAttribute('aria-activedescendant');
}
};

const syncEmptyState = state => {
state.popover.dataset.empty = String(
state.visibleOptions.length === 0,
);
};

const filterOptions = (
state,
{ preserveActive = false, search: forcedSearch } = {},
) => {
const previousActive =
state.activeIndex > -1 ? state.options[state.activeIndex] : null;
state.visibleOptions = [];

if (state.manualFilter) {
state.visibleOptions = state.options.filter(
option => option.getAttribute('aria-hidden') !== 'true',
);

if (
preserveActive &&
previousActive &&
state.visibleOptions.includes(previousActive)
) {
setActiveOption(state, state.options.indexOf(previousActive));
} else {
setActiveOption(
state,
state.autoHighlight && state.visibleOptions.length > 0
? state.options.indexOf(state.visibleOptions[0])
: -1,
);
}
syncEmptyState(state);
return;
}

const search = (forcedSearch ?? state.input.value)
.trim()
.toLowerCase();

state.allOptions.forEach(option => {
if (option.hasAttribute('data-force')) {
option.setAttribute('aria-hidden', 'false');
if (state.options.includes(option))
state.visibleOptions.push(option);
return;
}

const optionText = (
option.dataset.filter ||
option.dataset.label ||
option.textContent
)
.trim()
.toLowerCase();
const keywords = (option.dataset.keywords || '')
.toLowerCase()
.split(/[\s,]+/)
.filter(Boolean);
const matches =
!search ||
optionText.includes(search) ||
keywords.some(keyword => keyword.includes(search));
option.setAttribute('aria-hidden', String(!matches));
if (matches && state.options.includes(option))
state.visibleOptions.push(option);
});

if (
preserveActive &&
previousActive &&
state.visibleOptions.includes(previousActive)
) {
setActiveOption(state, state.options.indexOf(previousActive));
} else {
setActiveOption(
state,
state.autoHighlight && state.visibleOptions.length > 0
? state.options.indexOf(state.visibleOptions[0])
: -1,
);
}
syncEmptyState(state);
};

const renderChips = root => {
const state = states.get(root);
if (!state.chips) return;

state.chips
.querySelectorAll('.combobox-chip')
.forEach(chip => chip.remove());

getSelection(state).forEach(entry => {
const chip = document.createElement('span');
chip.className = 'combobox-chip';
chip.dataset.value = entry.value;

const label = document.createElement('span');
label.textContent = entry.label;

const remove = document.createElement('button');
remove.type = 'button';
remove.className = 'combobox-chip-remove btn';
remove.dataset.variant = 'ghost';
remove.dataset.size = 'icon-xs';
remove.setAttribute('aria-label', `Remove ${entry.label}`);
remove.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/>';
remove.addEventListener('click', event => {
event.stopPropagation();
root.deselect(entry.value);
state.input.focus();
});

chip.appendChild(label);
chip.appendChild(remove);
state.chips.insertBefore(chip, state.input);
});
};

const syncTriggerValue = state => {
if (!state.valueTarget) return;
const selected = getSelection(state);
state.valueTarget.textContent = state.isMultiple
? selected.map(entry => entry.label).join(', ')
: selected[0]?.label ||
state.valueTarget.dataset.placeholder ||
'';
};

const syncClearButton = state => {
if (!state.clearButton) return;
state.clearButton.hidden =
getSelection(state).length === 0 && state.input.value === '';
};

const syncSelectedOptions = state => {
state.options.forEach(option => {
if (state.selected.has(getValue(option))) {
option.setAttribute('aria-selected', 'true');
} else {
option.removeAttribute('aria-selected');
}
});
};

const setSelected = (root, entries, triggerEvent = true) => {
const state = states.get(root);
const normalized = (Array.isArray(entries) ? entries : [entries])
.map(normalizeEntry)
.filter(Boolean);

state.selected.clear();
if (state.isMultiple) {
normalized.forEach(entry =>
state.selected.set(entry.value, entry),
);
state.input.value = '';
} else if (normalized[0]) {
state.selected.set(normalized[0].value, normalized[0]);
state.input.value = state.valueTarget ? '' : normalized[0].label;
} else {
state.input.value = '';
}

state.hiddenInput.value = serializeSelection(state);
syncSelectedOptions(state);
if (state.isMultiple) {
renderChips(root);
filterOptions(state, { preserveActive: true });
}
syncTriggerValue(state);
syncClearButton(state);

if (triggerEvent) {
root.dispatchEvent(
new CustomEvent('change', {
detail: {
value: getCanonicalValue(state),
selected: getSelectedDetail(state),
},
bubbles: true,
}),
);
}
};

const closePopover = (state, focusInput = false) => {
if (state.popover.getAttribute('aria-hidden') === 'true') return;
state.popover.setAttribute('aria-hidden', 'true');
state.input.setAttribute('aria-expanded', 'false');
state.trigger?.setAttribute('aria-expanded', 'false');
setActiveOption(state, -1);
if (focusInput) {
state.skipOpenOnFocus = true;
state.input.focus();
requestAnimationFrame(() => {
state.skipOpenOnFocus = false;
});
}
};

const openPopover = state => {
const { root } = state;
if (state.popover.getAttribute('aria-hidden') === 'false') return;
document.dispatchEvent(
new CustomEvent('basecoat:popover', { detail: { source: root } }),
);
root.refresh();
const selected = getSelection(state)[0];
if (state.valueTarget) state.input.value = '';
const search =
!state.isMultiple &&
selected &&
state.input.value === selected.label
? ''
: undefined;
filterOptions(state, { search });
state.popover.setAttribute('aria-hidden', 'false');
state.input.setAttribute('aria-expanded', 'true');
state.trigger?.setAttribute('aria-expanded', 'true');
if (state.trigger) state.input.focus();
};

const refreshCombobox = root => {
const state = states.get(root);
if (!state) return;

let elements = getElements(root);
if (
!elements.input ||
!elements.popover ||
!elements.listbox ||
!elements.hiddenInput
) {
const missing = [];
if (!elements.input) missing.push('input');
if (!elements.popover) missing.push('popover');
if (!elements.listbox) missing.push('listbox');
if (!elements.hiddenInput) missing.push('hidden input');
console.error(
`Combobox refresh failed. Missing element(s): ${missing.join(', ')}`,
root,
);
return;
}

elements = ensureMultipleInputSurface(root, elements);
const previousValue = elements.hiddenInput.value;
const previousInputValue = elements.input.value;
Object.assign(state, elements, getOptions(elements.listbox));
state.isMultiple =
state.listbox.getAttribute('aria-multiselectable') === 'true';
state.closeOnSelect = root.dataset.closeOnSelect === 'true';
state.autoHighlight = root.dataset.autoHighlight === 'true';
state.manualFilter = root.dataset.filter === 'manual';
state.format = getFormat(root);

const stored = parseStoredSelection(
previousValue,
previousInputValue,
state,
);
if (stored.length > 0) {
setSelected(root, stored, false);
} else {
const selectedOptions = state.options
.filter(
option => option.getAttribute('aria-selected') === 'true',
)
.map(toSelected);
if (selectedOptions.length > 0) {
setSelected(
root,
state.isMultiple ? selectedOptions : selectedOptions[0],
false,
);
} else if (state.isMultiple) {
setSelected(root, [], false);
} else {
state.input.value = previousInputValue;
state.selected.clear();
state.hiddenInput.value = '';
syncSelectedOptions(state);
filterOptions(state, { preserveActive: true });
}
}
syncTriggerValue(state);
syncClearButton(state);
};

const resolveEntry = (state, value) => {
if (value && typeof value === 'object') {
const entry = normalizeEntry(value);
if (!entry) return null;
const option = state.options.find(
opt => getValue(opt) === entry.value,
);
return option ? toSelected(option) : entry;
}

const option = state.options.find(opt => getValue(opt) === value);
if (option) return toSelected(option);
const existing = state.selected.get(String(value));
return existing || null;
};

const selectValue = (root, value) => {
const state = states.get(root);
const entry = resolveEntry(state, value);
if (!entry) return;

if (state.isMultiple) {
setSelected(root, [
...getSelection(state).filter(
item => item.value !== entry.value,
),
entry,
]);
if (state.closeOnSelect) root.close(true);
} else {
setSelected(root, entry);
root.close(state.trigger ? false : true);
state.trigger?.focus();
}
};

const deselectValue = (root, value) => {
const state = states.get(root);
if (!state.isMultiple) return;
const normalized = String(value);
if (!state.selected.has(normalized)) return;
setSelected(
root,
getSelection(state).filter(item => item.value !== normalized),
);
};

const clearValue = root => {
const state = states.get(root);
setSelected(root, [], true);
state.input.value = '';
filterOptions(state);
syncClearButton(state);
if (state.popover.getAttribute('aria-hidden') === 'false')
state.input.focus();
};

const handleKeydown = (event, root) => {
const state = states.get(root);
if (
![
'ArrowDown',
'ArrowUp',
'Enter',
'Home',
'End',
'Escape',
'Backspace',
].includes(event.key)
)
return;

const isOpen =
state.popover.getAttribute('aria-hidden') === 'false';

if (
event.key === 'Backspace' &&
state.isMultiple &&
state.input.value === ''
) {
const selected = getSelection(state);
const last = selected[selected.length - 1];
if (last) root.deselect(last.value);
return;
}

if (event.key === 'Escape') {
root.close(true);
return;
}

if (
!isOpen &&
['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)
) {
event.preventDefault();
openPopover(state);
}

if (state.popover.getAttribute('aria-hidden') === 'true') return;

if (event.key === 'Enter') {
if (state.activeIndex > -1) {
event.preventDefault();
const option = state.options[state.activeIndex];
state.isMultiple
? root.toggle(getValue(option))
: root.select(getValue(option));
}
return;
}

if (
!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key) ||
state.visibleOptions.length === 0
)
return;
event.preventDefault();

const currentVisibleIndex =
state.activeIndex > -1
? state.visibleOptions.indexOf(state.options[state.activeIndex])
: -1;
let nextVisibleIndex = currentVisibleIndex;

if (event.key === 'ArrowDown')
nextVisibleIndex = Math.min(
currentVisibleIndex + 1,
state.visibleOptions.length - 1,
);
if (event.key === 'ArrowUp')
nextVisibleIndex =
currentVisibleIndex <= 0 ? 0 : currentVisibleIndex - 1;
if (event.key === 'Home') nextVisibleIndex = 0;
if (event.key === 'End')
nextVisibleIndex = state.visibleOptions.length - 1;

const nextOption = state.visibleOptions[nextVisibleIndex];
setActiveOption(state, state.options.indexOf(nextOption));
scrollOptionIntoListbox(state, nextOption);
};

const initCombobox = root => {
if (root.dataset.comboboxInitialized) return;

const state = {
root,
activeIndex: -1,
allOptions: [],
options: [],
visibleOptions: [],
selected: new Map(),
format: 'value',
manualFilter: false,
skipOpenOnFocus: false,
};
states.set(root, state);
root.refresh = () => refreshCombobox(root);

refreshCombobox(root);
if (
!state.input ||
!state.popover ||
!state.listbox ||
!state.hiddenInput
) {
states.delete(root);
delete root.refresh;
return;
}

root.close = (focusInput = false) =>
closePopover(state, focusInput);

root.select = value => selectValue(root, value);
root.selectByValue = root.select;
root.setValue = value => {
const entries = state.isMultiple
? Array.isArray(value)
? value
: value == null
? []
: [value]
: [value];
const resolved = entries
.map(
entry => resolveEntry(state, entry) || normalizeEntry(entry),
)
.filter(Boolean);
setSelected(root, state.isMultiple ? resolved : resolved[0]);
};
if (state.isMultiple) {
root.deselect = value => deselectValue(root, value);
root.toggle = value => {
const entry = resolveEntry(state, value);
if (!entry) return;
state.selected.has(entry.value)
? root.deselect(entry.value)
: root.select(entry);
};
root.selectAll = () =>
setSelected(root, state.options.map(toSelected));
root.selectNone = () => setSelected(root, []);
}

const handleInputFocus = () => {
if (state.skipOpenOnFocus) return;
openPopover(state);
};
const handleInputClick = () => openPopover(state);
const handleInput = () => {
openPopover(state);
filterOptions(state);
if (!state.isMultiple) {
state.hiddenInput.value = '';
state.selected.clear();
syncSelectedOptions(state);
syncTriggerValue(state);
syncClearButton(state);
}
};
const handleTriggerClick = () => {
if (state.popover.getAttribute('aria-hidden') === 'false') {
root.close(false);
} else {
openPopover(state);
}
};
const handleClearClick = event => {
event.preventDefault();
event.stopPropagation();
clearValue(root);
};
const handleInputKeydown = event => handleKeydown(event, root);
const handleListboxMousedown = event => {
// Options use aria-activedescendant: focus stays on the input. Otherwise
// focusout can dismiss the popup between mouse press and click.
if (
event.button === 0 &&
event.target.closest('[role="option"]')
) {
event.preventDefault();
}
};
const handleListboxMousemove = event => {
const option = event.target.closest('[role="option"]');
if (option && state.visibleOptions.includes(option))
setActiveOption(state, state.options.indexOf(option));
};
const handleListboxClick = event => {
const option = event.target.closest('[role="option"]');
if (!option || !state.options.includes(option)) return;
state.isMultiple
? root.toggle(getValue(option))
: root.select(getValue(option));
if (state.isMultiple && !state.closeOnSelect) state.input.focus();
};
const handleDocumentClick = event => {
if (!root.contains(event.target)) root.close(false);
};
const handleDocumentPopover = event => {
if (event.detail.source !== root) root.close(false);
};

state.input.addEventListener('focus', handleInputFocus);
state.input.addEventListener('click', handleInputClick);
state.input.addEventListener('input', handleInput);
state.input.addEventListener('keydown', handleInputKeydown);
state.trigger?.addEventListener('click', handleTriggerClick);
state.clearButton?.addEventListener('click', handleClearClick);
state.listbox.addEventListener('mousedown', handleListboxMousedown);
state.listbox.addEventListener('mousemove', handleListboxMousemove);
state.listbox.addEventListener('click', handleListboxClick);
document.addEventListener('click', handleDocumentClick);
document.addEventListener(
'basecoat:popover',
handleDocumentPopover,
);

root._destroy = () => {
state.input.removeEventListener('focus', handleInputFocus);
state.input.removeEventListener('click', handleInputClick);
state.input.removeEventListener('input', handleInput);
state.input.removeEventListener('keydown', handleInputKeydown);
state.trigger?.removeEventListener('click', handleTriggerClick);
state.clearButton?.removeEventListener('click', handleClearClick);
state.listbox.removeEventListener(
'mousedown',
handleListboxMousedown,
);
state.listbox.removeEventListener(
'mousemove',
handleListboxMousemove,
);
state.listbox.removeEventListener('click', handleListboxClick);
document.removeEventListener('click', handleDocumentClick);
document.removeEventListener(
'basecoat:popover',
handleDocumentPopover,
);
state.chips
?.querySelectorAll('.combobox-chip')
.forEach(chip => chip.remove());
states.delete(root);
delete root.refresh;
delete root.close;
delete root.select;
delete root.selectByValue;
delete root.setValue;
delete root.clear;
delete root.deselect;
delete root.toggle;
delete root.selectAll;
delete root.selectNone;
};

state.popover.setAttribute('aria-hidden', 'true');
state.input.setAttribute('aria-expanded', 'false');
state.trigger?.setAttribute('aria-expanded', 'false');
root.clear = () => clearValue(root);

Object.defineProperty(root, 'value', {
configurable: true,
get: () => getCanonicalValue(state),
set: value => root.setValue(value),
});

Object.defineProperty(root, 'selected', {
configurable: true,
get: () => getSelectedDetail(state),
});

root.dataset.comboboxInitialized = 'true';
root.dispatchEvent(new CustomEvent('basecoat:initialized'));
};

if (window.basecoat) {
window.basecoat.register('combobox', {
selector: '.combobox:not([data-combobox-initialized])',
init: initCombobox,
refresh: refreshCombobox,
});
}
})();

/* ANIMATION STORYBOARD
* 0ms Hide plateau lines, total column, and selection annotations; move company marks.
* 600ms After the marks settle, reveal cumulative totals and restore annotations.
* Interrupted transitions never reveal stale totals. Reduced motion settles immediately.
*/
/* CAROUSEL STORYBOARD
* On selection: keep All active or activate the company's sector, then center its card among muted neighbors, clamped at the list edges.
* Hover never navigates. Reduced motion aligns immediately.
*/
// On the Government Ownership Dashboard page, Drupal's page title replaces ours.
// It also opens the company cards as a grid instead of a carousel.
// Any path containing "dashboard" counts, so a migrated URL still matches.
// Add ?view=dashboard to any URL (e.g. the dev environment) to preview it.
const isDashboard =
window.location.pathname.toLowerCase().includes('dashboard') ||
new URLSearchParams(window.location.search).get('view') ===
'dashboard';
if (isDashboard)
document.querySelector('#fed-stakes').classList.add('is-dashboard');
// COMPANY EXPORT SWITCH
// When on, selecting a company adds "Chart image, highlighting <company>" to
// the Download menu: an image with the title and that company's detail card
// in the upper left. To hide it, change true to false.
const COMPANY_EXPORT = true;
// Show the component once it's ready, or once there's an error to show.
const revealComponent = () => {
const root = document.querySelector('#fed-stakes');
root.classList.remove('is-loading');
root.removeAttribute('aria-busy');
};
// D3 loads from jsDelivr; if it's blocked or down, say so instead of leaving a blank chart.
if (typeof d3 === 'undefined') {
revealComponent();
const chart = document.querySelector('#fed-stakes #chart');
chart.setAttribute('role', 'alert');
chart.textContent =
'Unable to load the chart. Check your connection and refresh the page.';
document.querySelector('#fed-stakes.company-directory').hidden =
true;
// The title normally overlaps the chart's empty corner; keep it clear of the message.
document.querySelector('#fed-stakes.hero').style.marginBottom =
'16px';
throw new Error('D3 failed to load from jsDelivr.');
}
let activeSector = 'All';
let carouselNavigated = false;
let carouselCentered = false;
let expandedDirectory = isDashboard;
if (expandedDirectory) {
const carousel = document.querySelector('#company-cards');
carousel.classList.add('is-expanded');
carousel.parentElement.classList.add('is-expanded');
carousel.setAttribute('aria-roledescription', 'company grid');
document
.querySelector('#directory-layout')
.setAttribute('aria-pressed', 'true');
}
let sectorGroups = [];
const TIMING = { settleMarks: 600 }; // Reveal totals only after this movement completes.
const CHART = {
// Use the available desktop width; retain the original mobile minimum.
minWidth: 659,
height: 436,
margin: { top: 14, right: 0, bottom: 48, left: 0 },
edgeInset: 28,
totalExtraGap: 18,
};
const TOTAL_LABELS = {
fallbackGutter: 150,
edgePadding: 8,
gap: 6, // Clear space between neighboring labels.
offset: 15,
lineHeight: 13,
countGap: 3, // Extra space between a sector name and its count.
};
const SELECTION = {
mutedMark: 0.25,
mutedConnector: 0.25,
mutedLabel: 0.35,
mutedText: '#a1a1aa', // Faded sector names in the Total column.
};
// Fixed colors by sector. Table order breaks ties when stacking.
const SECTOR_COLORS = {
Steel: '#64748b',
Minerals: '#d97706',
'Energy (nuclear)': '#34b887',
'Quantum computing': '#9c27b0',
'Rocket motors': '#b42318',
Semiconductors: '#2563eb',
'Energy (oil)': '#164e63',
};
// New sectors take these in order; all are color-blind checked against the fixed set.
const SPARE_SECTOR_COLORS = ['#facc15', '#06b6d4'];
const UNASSIGNED_SECTOR = 'Unassigned';
const UNASSIGNED_COLOR = '#d4d4d8';
// Quieter text hues preserve category matching without competing with totals.
const categoryTextColors = {
'#64748b': '#475569',
'#d97706': '#92400e',
'#4f46e5': '#3730a3',
'#059669': '#065f46',
'#34b887': '#065f46',
'#b42318': '#8a1c13',
'#9333ea': '#6b21a8',
'#0e7490': '#155e75',
'#2563eb': '#1e40af',
'#9c27b0': '#6a1b7a',
'#facc15': '#a16207',
'#06b6d4': '#155e75',
'#d4d4d8': '#52525b',
};
const chart = document.querySelector('#chart');
const searchInput = document.querySelector('#company-search');
const number = d3.format(',');
const billions = value => `$${number(value)}B`;
const formatDate = value =>
d3.timeFormat('%b %Y')(d3.timeParse('%Y-%m')(value.slice(0, 7)));
const amountLabel = unit =>
unit.value === null
? 'Not specified'
: unit.value === 0
? '$0 federal cash'
: unit.value < 1
? `$${d3.format(',.3~f')(unit.value * 1000)}M`
: billions(unit.value);
const reducedMotion = () =>
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
let categories = [];
let mode = 'cumulative';
let directLabels = true;
const arithmeticLabels = true;
let companyQuery = '';
let selectedId = null;
let urlReady = false; // Hold URL writes until a shared link has been read.
let previewId = null;
let data;
let scene;
let lastChartWidth = 0;
let detailId = null;
const activeId = () => previewId || selectedId;
// URL-safe names: "Energy (nuclear)" -> "energy-nuclear", "Intel" -> "intel".
const slug = text =>
text
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '');

function parse(source) {
const parsed = d3.csvParse(source.replace(/^\uFEFF/, ''));
// Skip blank rows an editor may leave in the sheet.
const rows = parsed.filter(row => row.company?.trim());
rows.columns = parsed.columns;
const required = [
'company',
'sector',
'agency',
'announced',
'summary',
'source',
];
if (required.some(column => !rows.columns.includes(column)))
throw new Error(`CSV must include: ${required.join(', ')}.`);
if (!rows.length) throw new Error('The CSV contains no companies.');
const ids = new Set();
const parseDay = d3.timeParse('%Y-%m-%d');
const parseMonth = d3.timeParse('%Y-%m');
const units = rows.map((row, index) => {
const announced = row.announced.trim();
const exact = parseDay(announced) || parseMonth(announced);
// Chart squares stack by month; the full date only orders and labels them.
const date = exact && d3.timeMonth.floor(exact);
const id = slug(row.company);
if (!id || ids.has(id) || !date)
throw new Error(
`Check CSV row ${index + 2}: unique company and valid announcement date (YYYY-MM-DD or YYYY-MM) required.`,
);
ids.add(id);
return {
id,
company: row.company.trim(),
category: row.sector.trim() || UNASSIGNED_SECTOR,
date,
announced: d3.timeFormat(
announced.length > 7 ? '%Y-%m-%d' : '%Y-%m',
)(exact),
value: null,
summary: row.summary.trim(),
agency: row.agency.trim(),
source: row.source.trim(),
};
});
// Order companies within a month by day, then by name.
units.sort(
(a, b) =>
a.announced.localeCompare(b.announced) ||
a.company.localeCompare(b.company),
);
// Alternate single-company sectors with larger ones so the singles
// separate the blocks and every label has room; Unassigned goes last.
const knownSectors = Object.keys(SECTOR_COLORS);
const rank = name =>
knownSectors.includes(name)
? knownSectors.indexOf(name)
: knownSectors.length;
const sectorCounts = d3.rollup(
units,
v => v.length,
unit => unit.category,
);
const sectors = [...sectorCounts.keys()]
.filter(name => name !== UNASSIGNED_SECTOR)
.sort((a, b) => rank(a) - rank(b));
const singles = sectors.filter(name => sectorCounts.get(name) === 1);
const blocks = sectors.filter(name => sectorCounts.get(name) > 1);
const stackOrder = [];
while (singles.length || blocks.length) {
if (singles.length) stackOrder.push(singles.shift());
if (blocks.length) stackOrder.push(blocks.shift());
}
if (sectorCounts.has(UNASSIGNED_SECTOR))
stackOrder.push(UNASSIGNED_SECTOR);
let spareIndex = 0;
categories = stackOrder.map(name => {
if (name === UNASSIGNED_SECTOR)
return { name, color: UNASSIGNED_COLOR };
if (SECTOR_COLORS[name])
return { name, color: SECTOR_COLORS[name] };
if (spareIndex >= SPARE_SECTOR_COLORS.length)
console.warn(
`No spare color left for sector "${name}"; reusing a color. Merge sectors or add a color.`,
);
const palette = [
...SPARE_SECTOR_COLORS,
...Object.values(SECTOR_COLORS),
];
return { name, color: palette[spareIndex++ % palette.length] };
});
const byMonth = d3.group(units, unit => +unit.date);
const months = d3.timeMonth.range(
d3.min(units, unit => unit.date),
d3.timeMonth.offset(
d3.max(units, unit => unit.date),
1,
),
);
let total = 0;
let count = 0;
return months.map(date => {
const values = byMonth.get(+date) || [];
const addition = d3.sum(values, unit => unit.value);
const start = total;
total += addition;
count += values.length;
return { date, values, addition, start, total, count };
});
}

function prepare() {
const units = data.flatMap(event => {
let cumulative = event.start;
let local = 0;
const ordered = [...event.values].sort(
(a, b) =>
categories.findIndex(item => item.name === a.category) -
categories.findIndex(item => item.name === b.category),
);
return ordered.map((unit, index) => {
const { value, category } = unit;
const start = cumulative;
const localStart = local;
cumulative += value;
local += value;
const countStart = event.count - event.values.length + index;
return {
...unit,
date: event.date,
start,
end: cumulative,
localStart,
localEnd: local,
countStart,
countEnd: countStart + 1,
localCountStart: index,
localCountEnd: index + 1,
value,
category,
index: index + 1,
};
});
});
const totalDate = d3.timeMonth.offset(data.at(-1).date, 1);
const sectorRank = name =>
categories.findIndex(category => category.name === name);
let level = 0;
const totalUnits = [...units]
.sort(
(a, b) =>
sectorRank(a.category) - sectorRank(b.category) ||
b.value - a.value,
)
.map((unit, index) => {
const start = level;
level += unit.value;
return {
...unit,
sourceDate: unit.date,
date: totalDate,
start,
end: level,
countStart: index,
countEnd: index + 1,
};
});
return {
units,
totalDate,
totalUnits,
lineData: [...data, {...data.at(-1), date: totalDate }],
};
}

// The page chart fills #chart. The PNG export passes its own container and
// height to draw an offscreen copy without touching the page or its state.
function buildChart(
width,
{ height = CHART.height, target = chart } = {},
) {
const offscreen = target !== chart;
const { margin } = CHART;
const { units, totalDate, totalUnits } = prepare();
const color = d3.scaleOrdinal(
categories.map(d => d.name),
categories.map(d => d.color),
);
const x = d3
.scaleTime()
.range([CHART.edgeInset, width - CHART.edgeInset]);
const y = d3
.scaleLinear()
.range([height - margin.bottom, margin.top]);
const svg = d3
.create('svg')
.attr('viewBox', [0, 0, width, height])
.attr('width', width)
.attr('height', height)
.attr('role', 'group')
.attr(
'aria-label',
'Company marks. Use arrow keys to move between companies. Use enter to select.',
);
const xAxis = svg
.append('g')
.attr('transform', `translate(0,${y(0)})`)
.attr('aria-hidden', 'true');
const connector = svg
.append('path')
.attr('class', 'plateau-lines')
.attr('fill', 'none')
.attr('stroke', '#18181b')
.attr('stroke-width', 1.25)
.attr('pointer-events', 'none');
const bars = svg
.append('g')
.selectAll('rect')
.data(units, d => d.id)
.join('rect')
.attr('class', 'company-mark')
.attr('data-company-id', d => d.id)
.attr('fill', d => color(d.category));
const separators = svg
.append('g')
.attr('pointer-events', 'none')
.selectAll('line')
.data(
units.filter(
d =>
d.index <
data.find(event => +event.date === +d.date).values.length,
),
)
.join('line')
.attr('stroke', '#fff')
.attr('stroke-width', 0.6);
const totalGroup = svg.append('g').attr('class', 'total-column');
const totalBars = totalGroup
.selectAll('rect.company-mark')
.data(totalUnits, d => d.id)
.join('rect')
.attr('class', 'company-mark')
.attr('data-company-id', d => d.id)
.attr('fill', d => color(d.category));
const totalSeparators = totalGroup
.append('g')
.attr('pointer-events', 'none')
.selectAll('line')
.data(totalUnits.slice(0, -1))
.join('line')
.attr('stroke', '#fff')
.attr('stroke-width', 0.6);
const topLabels = svg
.append('g')
.attr('class', 'top-labels')
.selectAll('text')
.data(data)
.join('text');
const additions = svg
.append('g')
.attr('class', 'addition-labels')
.selectAll('text')
.data(data)
.join('text');
for (const labels of [topLabels, additions])
labels.attr('text-anchor', 'middle');
const finalLabel = totalGroup
.append('text')
.attr('class', 'final-label')
.attr('text-anchor', 'middle');
finalLabel
.append('tspan')
.attr('class', 'count')
.text(number(units.length));
const equalsLabel = totalGroup
.append('text')
.attr('class', 'equals-label')
.attr('text-anchor', 'middle')
.text('=');
const sectorLabels = totalGroup
.append('g')
.attr('class', 'total-sector-labels')
.attr('pointer-events', 'none')
.selectAll('g')
.data(
categories.map(category => {
const members = totalUnits.filter(
unit => unit.category === category.name,
);
const nameLines = category.name === 'Quantum computing' ? 2 : 1;
return {
...category,
count: members.length,
// Rough block height, used to space neighboring labels.
labelHeight:
(nameLines + (members.length > 1 ? 1 : 0)) *
TOTAL_LABELS.lineHeight,
midpoint:
(members[0].countStart + members.at(-1).countEnd) / 2,
};
}),
)
.join('g');
sectorLabels
.append('text')
.attr('fill', d => categoryTextColors[d.color] || d.color)
.attr(
'aria-label',
d =>
`${d.name}: ${d.count} ${d.count === 1 ? 'company' : 'companies'}`,
)
.each(function (d) {
const label = d3.select(this);
const nameLines =
d.name === 'Quantum computing'
? ['Quantum', 'computing']
: [d.name];
// Center the whole block, including the extra gap above the count.
const countStep = TOTAL_LABELS.lineHeight + TOTAL_LABELS.countGap;
// In the export, a wrapped name takes the same line step as its count.
const nameStep =
target === chart ? TOTAL_LABELS.lineHeight : countStep;
const blockHeight =
(nameLines.length - 1) * nameStep +
(d.count > 1 ? countStep : 0);
nameLines.forEach((line, index) => {
label
.append('tspan')
.attr('class', 'sector-name')
.attr(
'dy',
index === 0 ? -blockHeight / 2 : nameStep,
)
.text(line);
});
if (d.count > 1)
label
.append('tspan')
.attr('class', 'sector-count')
.attr('dy', countStep)
.text(`${number(d.count)} companies`);
});
const selectionLabels = svg
.append('g')
.attr('class', 'selection-labels')
.attr('pointer-events', 'none');
let squareSize;
let layoutVersion = 0;
let stage = 0; // 0: marks moving; 1: marks settled and totals may be shown.
const cumulative = () => mode === 'cumulative';
const start = unit =>
cumulative() ? unit.countStart : unit.localCountStart;
const end = unit =>
cumulative() ? unit.countEnd : unit.localCountEnd;

function reveal(id) {
const unit = units.find(d => d.id === id);
if (!unit || chart.scrollWidth <= chart.clientWidth) return;
const center = x(unit.date);
if (
center < chart.scrollLeft + 44 ||
center > chart.scrollLeft + chart.clientWidth - 44
) {
chart.scrollTo({
left: center - chart.clientWidth / 2,
behavior: reducedMotion() ? 'instant' : 'smooth',
});
}
}

function applySelection() {
const id = activeId();
const needle = companyQuery.toLowerCase();
const matches = new Set(
units.filter(matchesFilters).map(d => d.id),
);
const filtering = Boolean(needle) || activeSector !== 'All';
const dim = d => (id ? d.id !== id : !matches.has(d.id));
// While focus is outside the chart, Tab enters on the selected
// company, or the first one when nothing is selected.
if (!document.activeElement?.classList.contains('company-mark')) {
const stopId = selectedId ?? units[0].id;
bars.attr('tabindex', unit => (unit.id === stopId ? 0 : -1));
}
for (const marks of [bars, totalBars]) {
marks
.attr('opacity', d => (dim(d) ? SELECTION.mutedMark : 1))
.attr('aria-pressed', d => String(d.id === selectedId));
}
connector.attr(
'opacity',
id || filtering ? SELECTION.mutedConnector : 1,
);
for (const marks of [topLabels, additions, finalLabel, equalsLabel])
marks.attr('opacity', id || filtering ? SELECTION.mutedLabel : 1);
const source = units.find(d => d.id === id);
// Faded sector names turn one neutral gray rather than a pastel of their
// color, so the active sector is the only colored text in the column.
const sectorMuted = d =>
source
? source.category !== d.name
: filtering &&
!units.some(
unit => unit.category === d.name && matches.has(unit.id),
);
sectorLabels
.select('text')
.attr('fill', d =>
sectorMuted(d)
? SELECTION.mutedText
: categoryTextColors[d.color] || d.color,
);
sectorLabels
.selectAll('.sector-count')
.style('fill', d =>
sectorMuted(d) ? SELECTION.mutedText : null,
);
const labels = source ? [source] : [];
selectionLabels
.attr('visibility', stage >= 1 ? null : 'hidden')
.selectAll('text')
.data(labels)
.join('text')
.attr(
'x',
d =>
x(d.date) +
(x(d.date) > width / 2 ? -1 : 1) * (squareSize / 2 + 8),
)
.attr('y', d => y((start(d) + end(d)) / 2))
.attr('dominant-baseline', 'central')
.attr('text-anchor', d =>
x(d.date) > width / 2 ? 'end' : 'start',
)
.attr('stroke', '#fff')
.attr('stroke-width', 4)
.attr('paint-order', 'stroke')
.text(d => d.company);
if (!offscreen) showDetails(source);
}

// Keyboard focus gets a dark ring drawn outside the square, on top
// of the white separators, so it never reads as another gap.
const focusRing = svg
.append('rect')
.attr('class', 'mark-focus-ring')
.attr('aria-hidden', 'true')
.attr('display', 'none');
function showFocusRing(mark) {
if (!mark.matches(':focus-visible')) return;
const box = mark.getBBox();
const m = svg
.node()
.getScreenCTM()
.inverse()
.multiply(mark.getScreenCTM());
const gap = 2;
focusRing
.attr('x', m.e + box.x * m.a - gap)
.attr('y', m.f + box.y * m.d - gap)
.attr('width', box.width * m.a + gap * 2)
.attr('height', box.height * m.d + gap * 2)
.attr('display', null)
.raise();
if (!hintDismissed) showKeyboardHint(mark);
}
// Sighted keyboard users don't hear the aria-label, so the first
// keyboard visit shows how to move; it goes once an arrow is used.
let hintDismissed = false;
const keyboardHint = svg
.append('g')
.attr('class', 'keyboard-hint')
.attr('aria-hidden', 'true')
.attr('pointer-events', 'none')
.attr('display', 'none');
const hintBox = keyboardHint.append('rect');
const hintText = keyboardHint
.append('text')
.attr('dominant-baseline', 'central')
.attr('text-anchor', 'middle')
.text('← → to move. Use enter to select');
function showKeyboardHint(mark) {
keyboardHint.attr('display', null).raise();
const ring = focusRing.node().getBBox();
const text = hintText.node().getBBox();
const w = text.width + 16;
const h = 24;
const cx = Math.max(
w / 2 + 4,
Math.min(width - w / 2 - 4, ring.x + ring.width / 2),
);
const top =
ring.y - h - 6 >= 0 ? ring.y - h - 6 : ring.y + ring.height + 6;
hintBox
.attr('x', cx - w / 2)
.attr('y', top)
.attr('width', w)
.attr('height', h);
hintText.attr('x', cx).attr('y', top + h / 2);
}
function hideKeyboardHint(dismiss = false) {
if (dismiss) hintDismissed = true;
keyboardHint.attr('display', 'none');
}
for (const marks of [bars, totalBars]) {
marks
.attr('role', 'button')
.attr('aria-controls', 'company-cards')
.attr(
'aria-label',
d =>
`${d.company}, ${d.category}, announced ${formatDate(d.announced)}, ${d.agency}`,
)
.attr('tabindex', (d, i) => (marks === bars && i === 0 ? 0 : -1))
.on('pointerenter', (event, d) => {
if (event.pointerType === 'touch') return;
previewId = d.id;
applySelection();
})
.on('pointerleave', () => {
previewId = null;
applySelection();
})
.on('focus', (event, d) => {
showFocusRing(event.currentTarget);
bars.attr('tabindex', unit => (unit.id === d.id ? 0 : -1));
previewId = d.id;
reveal(d.id);
applySelection();
})
.on('blur', () => {
focusRing.attr('display', 'none');
hideKeyboardHint();
previewId = null;
applySelection();
})
.on('click', (event, d) => toggleCompany(d.id))
.on('keydown', (event, d) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
toggleCompany(d.id);
} else if (
[
'ArrowRight',
'ArrowLeft',
'ArrowUp',
'ArrowDown',
'Home',
'End',
].includes(event.key)
) {
event.preventDefault();
hideKeyboardHint(true);
const index = units.findIndex(unit => unit.id === d.id);
const next =
event.key === 'Home'
? 0
: event.key === 'End'
? units.length - 1
: (index +
(['ArrowRight', 'ArrowDown'].includes(event.key)
? 1
: -1) +
units.length) %
units.length;
bars
.filter(unit => unit.id === units[next].id)
.node()
.focus({ preventScroll: true });
}
});
}

function setMode(next, animate = true) {
mode = next;
svg.attr('data-mode', mode);
previewId = null;
const version = ++layoutVersion;
stage = 0;
svg.interrupt('layout');
svg.selectAll('*').interrupt('layout');
connector.attr('visibility', 'hidden');
totalGroup.attr('visibility', 'hidden').attr('aria-hidden', 'true');
selectionLabels.attr('visibility', 'hidden');
const duration =
animate && !reducedMotion() ? TIMING.settleMarks : 0;
if (!offscreen)
document.querySelector('.chart-meta').hidden =
cumulative() && directLabels;
sectorLabels.attr('display', directLabels ? null : 'none');
equalsLabel.attr('display', 'none').attr('y', y(0) + 40);
svg.classed('arithmetic-labels', false);
// Size the gutter to the longest rendered line, including wrapped names and counts.
const labelLineWidth =
cumulative() && directLabels
? d3.max(sectorLabels.selectAll('tspan').nodes(), node =>
node.getComputedTextLength(),
)
: 0;
const labelGutter =
labelLineWidth > 0
? Math.ceil(
labelLineWidth +
TOTAL_LABELS.offset +
TOTAL_LABELS.edgePadding,
)
: TOTAL_LABELS.fallbackGutter;
x.range([
CHART.edgeInset,
width -
(cumulative() && directLabels ? labelGutter : CHART.edgeInset),
]);
x.domain([
data[0].date,
cumulative() ? totalDate : data.at(-1).date,
]);
if (cumulative() && data.length > 1) {
// Reserve extra space for the summary while preserving the monthly timeline.
const [left, right] = x.range();
const lastMonthX = x(data.at(-1).date) - CHART.totalExtraGap;
x.domain([data[0].date, data.at(-1).date, totalDate]).range([
left,
lastMonthX,
right,
]);
}
y.domain([
0,
cumulative()
? units.length * 1.04
: d3.max(data, d => d.values.length) + 2,
]);
squareSize = y(0) - y(1);
const t = svg
.transition('layout')
.duration(duration)
.ease(d3.easeCubicInOut);
const plotWidth = cumulative()
? x(totalDate) + squareSize / 2
: width - margin.left - margin.right;
xAxis.attr('transform', `translate(0,${y(0)})`).call(
d3
.axisBottom(x)
.tickValues([
...data.map(d => d.date),
...(cumulative() ? [totalDate] : []),
])
.tickFormat(d => {
if (+d === +totalDate)
return arithmeticLabels ? 'Total' : 'All companies';
return d3.timeFormat('%b')(d);
})
.tickSize(0)
.tickPadding(14),
);
xAxis
.selectAll('.tick')
.classed('summary-tick', d => cumulative() && +d === +totalDate);
xAxis.selectAll('.tick text').each(function (d) {
if (+d === +totalDate) return;
if (+d === +data[0].date || d.getMonth() === 0) {
d3.select(this)
.append('tspan')
.attr('x', 0)
.attr('dy', '1.2em')
.text(d3.timeFormat('%Y')(d));
}
});
xAxis
.select('.domain')
.attr('stroke', '#a1a1aa')
.attr('d', `M${margin.left},0.5H${margin.left + plotWidth}`);
bars
.transition(t)
.attr('x', d => x(d.date) - squareSize / 2)
.attr('width', squareSize)
.attr('height', squareSize)
.attr('y', d => y(end(d)));
separators
.transition(t)
.attr('x1', d => x(d.date) - squareSize / 2)
.attr('x2', d => x(d.date) + squareSize / 2)
.attr('y1', d => y(end(d)))
.attr('y2', d => y(end(d)));
topLabels
.text(d =>
cumulative() && d.values.length && d.count < units.length
? number(d.count)
: '',
)
.transition(t)
.attr('x', d => x(d.date))
.attr('y', d => y(cumulative() ? d.count : d.values.length) - 8);
// Final column edges and label tops, for placeIntro() to test against.
if (!offscreen)
columnTops = data.map(d => ({
left: x(d.date) - squareSize / 2,
top:
y(cumulative() ? d.count : d.values.length) -
8 -
TOTAL_LABELS.lineHeight -
4,
}));
// Label larger monthly additions beside the center of their stacks.
additions
.text(d =>
d.values.length > 1 ? `+${number(d.values.length)}` : '',
)
.attr('text-anchor', 'start')
.attr('dominant-baseline', 'central')
.transition(t)
.attr('x', d => x(d.date) + squareSize / 2 + 6)
.attr('y', d =>
y(
cumulative()
? d.count - d.values.length / 2
: d.values.length / 2,
),
);
connector.attr(
'd',
data
.map(
(d, i) =>
`M${x(d.date) - squareSize / 2},${y(d.count)}H${x(i === data.length - 1 ? totalDate : data[i + 1].date) + squareSize / 2}`,
)
.join(''),
);
totalBars
.attr('x', x(totalDate) - squareSize / 2)
.attr('width', squareSize)
.attr('height', squareSize)
.attr('y', d => y(d.countEnd));
totalSeparators
.attr('x1', x(totalDate) - squareSize / 2)
.attr('x2', x(totalDate) + squareSize / 2)
.attr('y1', d => y(d.countEnd))
.attr('y2', d => y(d.countEnd));
// Place sector names beside their segment, separating neighboring one-company sectors.
// Labels that would collide form a run spaced one gap apart, centered on
// their segments so no label drifts far from its own squares.
const runs = [];
[...sectorLabels.data()]
.sort((a, b) => b.midpoint - a.midpoint)
.forEach(d => {
let run = {
items: [
{ name: d.name, targetY: y(d.midpoint), height: d.labelHeight },
],
};
for (;;) {
// Each label's distance below the run's first label.
let offset = 0;
run.items.forEach((item, i) => {
if (i)
offset +=
(run.items[i - 1].height + item.height) / 2 +
TOTAL_LABELS.gap;
item.offset = offset;
});
run.startY = Math.max(
TOTAL_LABELS.edgePadding,
d3.mean(run.items, item => item.targetY - item.offset),
);
const previous = runs.at(-1);
const last = previous?.items.at(-1);
if (
!previous ||
previous.startY +
last.offset +
(last.height + run.items[0].height) / 2 +
TOTAL_LABELS.gap <=
run.startY
)
break;
runs.pop();
run = { items: [...previous.items,...run.items] };
}
runs.push(run);
});
const positions = new Map(
runs.flatMap(run =>
run.items.map(item => [
item.name,
{
targetY: item.targetY,
labelY: run.startY + item.offset,
},
]),
),
);
const labelX = x(totalDate) + TOTAL_LABELS.offset;
sectorLabels
.select('text')
.attr('x', labelX)
.attr('y', d => positions.get(d.name).labelY)
.attr('dominant-baseline', 'central');
sectorLabels.selectAll('tspan').attr('x', labelX);

// Keep the centered total label inside the SVG even at its right edge.
const labelHalfWidth =
d3.max(finalLabel.selectAll('tspan').nodes(), node =>
node.getComputedTextLength(),
) / 2;
equalsLabel.attr('x', (x(data.at(-1).date) + x(totalDate)) / 2);
finalLabel
.attr('y', y(units.length) - 8)
.selectAll('tspan')
.attr('x', Math.min(x(totalDate), width - labelHalfWidth - 2));
if (!offscreen)
chart.setAttribute(
'aria-label',
`${cumulative() ? 'Cumulative' : 'Monthly'} company announcements. One mark per company.`,
);
applySelection();
if (selectedId && !offscreen) reveal(selectedId);
const settle = () => {
if (version !== layoutVersion || !svg.node().isConnected) return;
stage = 1;
connector.attr('visibility', cumulative() ? null : 'hidden');
totalGroup
.attr('visibility', cumulative() ? null : 'hidden')
.attr('aria-hidden', String(!cumulative()));
selectionLabels.attr('visibility', null);
applySelection();
};
if (duration) {
// Await the actual mark transition, rather than racing a separate timeout.
bars
.transition(t)
.end()
.then(settle, () => {});
} else settle();
}
target.replaceChildren(svg.node());
if (offscreen) {
setMode(mode, false);
return svg.node();
}
scene = { setMode, applySelection, reveal, units };
setMode(mode, false);
}

// Below the wide layout, the subtitle and key sit inside the chart when they
// clear every month's stack and label; otherwise they stay under the title.
// Once the chart scrolls sideways, they always take their own row.
let columnTops = [];
function placeIntro() {
const root = document.querySelector('#fed-stakes');
const layout = root.querySelector('.fed-stakes-layout');
const style = getComputedStyle(layout);
const layoutWidth =
layout.clientWidth -
parseFloat(style.paddingLeft) -
parseFloat(style.paddingRight);
root.classList.remove('intro-inset', 'intro-inline');
if (isDashboard || layoutWidth >= 750) return;
if (chart.scrollWidth > chart.clientWidth) {
root.classList.add('intro-inline');
return;
}
root.classList.add('intro-inset');
const frame = chart.getBoundingClientRect();
const intro = root.querySelector('.intro').getBoundingClientRect();
const right = intro.right - frame.left + 16;
const bottom = intro.bottom - frame.top + 8;
const fits = columnTops.every(
column => column.left > right || column.top > bottom,
);
if (!fits) root.classList.replace('intro-inset', 'intro-inline');
}

function createAnnouncementLink(unit) {
let url;
try {
url = new URL(unit.source);
} catch {
return null;
}
if (!['https:', 'http:'].includes(url.protocol)) return null;
const link = document.createElement('a');
link.className = 'company-announcement';
link.href = url.href;
link.target = '_blank';
link.rel = 'noopener noreferrer';
const label = `Read the announcement for ${unit.company} (opens in a new tab)`;
link.setAttribute('aria-label', label);
link.title = label;
// Lucide-style external-link icon; inline SVG avoids another runtime dependency.
link.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M21 14v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5"/>';
return link;
}

function buildCompanyCards() {
const container = document.querySelector('#company-cards');
if (container.children.length) return;
for (const unit of [...scene.units].sort(
(a, b) =>
a.announced.localeCompare(b.announced) ||
a.company.localeCompare(b.company),
)) {
const card = document.createElement('article');
card.className = 'company-card';
card.id = `company-${unit.id}`;
card.dataset.companyId = unit.id;
card.dataset.category = unit.category;
const sectorColor = categories.find(
c => c.name === unit.category,
).color;
const heading = document.createElement('h3');
const selectButton = document.createElement('button');
selectButton.type = 'button';
selectButton.className = 'company-select';
selectButton.textContent = unit.company;
selectButton.setAttribute(
'aria-label',
`Highlight ${unit.company} on the chart`,
);
selectButton.setAttribute('aria-controls', 'chart');
selectButton.setAttribute('aria-pressed', 'false');
heading.append(selectButton);
const announcementLink = createAnnouncementLink(unit);
card.addEventListener('click', event => {
// Keep other controls independent of selection.
const control = event.target.closest(
'button, a, input, select, textarea',
);
if (control && control !== selectButton) return;
const textSelection = window.getSelection();
if (
!control &&
!textSelection.isCollapsed &&
card.contains(textSelection.anchorNode)
)
return;
toggleCompany(unit.id);
});
const header = document.createElement('div');
header.className = 'company-card-header';
header.append(heading);
// One layout at every width: the name, then "■ Sector | Month Year", then agencies.
// Terms stay for screen readers; the values speak for themselves on screen.
const facts = document.createElement('dl');
facts.className = 'detail-facts';
const agencies = unit.agency
.trim()
.split(/\s{2,}|\s*[,;]\s*/)
.filter(Boolean);
const fact = (className, label, value) => {
const pair = document.createElement('div');
const term = document.createElement('dt');
const description = document.createElement('dd');
pair.className = className;
term.className = 'sr-only';
term.textContent = label;
description.append(...value);
pair.append(term, description);
return pair;
};
const sectorSquare = document.createElement('i');
sectorSquare.className = 'company-sector-square';
sectorSquare.style.background = sectorColor;
sectorSquare.setAttribute('aria-hidden', 'true');
const date = document.createElement('time');
date.dateTime = unit.announced;
date.textContent = d3.timeFormat('%B %Y')(
d3.timeParse('%Y-%m')(unit.announced.slice(0, 7)),
);
const metaLine = document.createElement('div');
metaLine.className = 'metadata-line';
metaLine.append(
fact('metadata-sector', 'Sector:', [sectorSquare, unit.category]),
fact('metadata-date', 'Announced:', [date]),
);
facts.append(
metaLine,
fact(
'metadata-agency',
agencies.length > 1 ? 'Agencies:' : 'Agency:',
[agencies.join(', ')],
),
);
const summary = document.createElement('p');
summary.className = 'detail-summary';
summary.id = `summary-${unit.id}`;
summary.textContent = unit.summary;
if (announcementLink) {
// Keep the source icon with the final word rather than on its own line.
const ending = document.createElement('span');
ending.className = 'company-announcement-ending';
const parts = unit.summary.match(/^(.*\s)?(\S+)\s*$/s);
summary.textContent = parts ? parts[1] || '' : '';
ending.append(parts ? parts[2] : unit.summary, announcementLink);
summary.append(ending);
}
card.append(header, facts, summary);
container.append(card);
}
buildSectorTabs();
setSector(activeSector || sectorGroups[0].name);
let carouselWidth = container.clientWidth;
new ResizeObserver(() => {
if (container.clientWidth === carouselWidth) return;
carouselWidth = container.clientWidth;
sizeCarousel();
const selected = visibleCards().find(
card => card.dataset.companyId === selectedId,
);
if (carouselNavigated)
alignCard(selected || visibleCards()[0], false);
else updateCarouselControls();
}).observe(container);
let rangeUpdateTimer;
container.addEventListener(
'scroll',
() => {
updateCarouselControls({ updateRange: false });
// Fallback for browsers without scrollend support.
if (!('onscrollend' in container)) {
clearTimeout(rangeUpdateTimer);
rangeUpdateTimer = setTimeout(updateCarouselControls, 150);
}
},
{ passive: true },
);
container.addEventListener('scrollend', () =>
updateCarouselControls(),
);
}

function showDetails(unit) {
document.querySelectorAll('.company-card').forEach(card => {
card.classList.toggle(
'is-active',
card.dataset.companyId === unit?.id,
);
card.classList.toggle(
'is-muted',
Boolean(selectedId) && card.dataset.companyId !== selectedId,
);
card.classList.toggle(
'is-selected',
card.dataset.companyId === selectedId,
);
card
.querySelector('.company-select')
.setAttribute(
'aria-pressed',
String(card.dataset.companyId === selectedId),
);
});
const selected = scene?.units.find(item => item.id === selectedId);
document.querySelector('#selection-context').textContent =
previewId && previewId !== selectedId
? `Preview: ${unit?.company || ''} (${unit?.category || ''})${selected ? ` · Selected: ${selected.company} (${selected.category})` : ''}`
: selected
? `Selected: ${selected.company} (${selected.category})`
: 'Hover or focus to preview. Click to select.';
if ((unit?.id || null) === detailId) return;
detailId = unit?.id || null;
document.querySelector('#selection-announcement').textContent = unit
? `${unit.company}. ${unit.category}. ${unit.agency}. ${unit.summary}`
: '';
}

const matchesFilters = unit =>
(activeSector === 'All' || unit.category === activeSector) &&
unit.company.toLowerCase().includes(companyQuery.toLowerCase());

function filterCompanyCards({ resetScroll = true } = {}) {
const matches = new Set(
(scene?.units || [])
.filter(
unit =>
(activeSector === 'All' || unit.category === activeSector) &&
// The carousel keeps neighbors in view around a selection; the grid
// narrows to the selected card so it isn't buried down the page.
(selectedId
? !expandedDirectory || unit.id === selectedId
: matchesFilters(unit)),
)
.map(unit => unit.id),
);
document
.querySelectorAll('#company-cards.company-card')
.forEach(card => {
card.hidden = !matches.has(card.dataset.companyId);
});
sizeCarousel();
document.querySelector('#directory-empty').hidden = matches.size > 0;
if (selectedId && !matches.has(selectedId)) {
selectedId = null;
syncUrl();
}
if (resetScroll) {
document
.querySelector('#company-cards')
.scrollTo({ left: 0, behavior: 'instant' });
}
updateCarouselControls();
}

const visibleCards = () =>
[...document.querySelectorAll('#company-cards.company-card')].filter(
card => !card.hidden,
);

function buildSectorTabs() {
const tabs = document.querySelector('#sector-tabs');

sectorGroups = [...new Set(categories.map(category => category.name))]
.map(name => ({
name,
color: categories.find(category => category.name === name).color,
count: scene.units.filter(unit => unit.category === name).length,
}))
.sort((a, b) =>
a.name.localeCompare(b.name, 'en', { sensitivity: 'base' }),
);
tabs.replaceChildren();
const all = document.createElement('button');
all.type = 'button';
all.id = 'sector-all';
all.textContent = 'All';
all.setAttribute('aria-controls', 'company-cards');
all.style.setProperty('--sector-color', '#3f3f46');
all.addEventListener('click', () => setSector('All'));
const divider = document.createElement('span');
divider.className = 'sector-tab-divider';
divider.setAttribute('aria-hidden', 'true');
const select = document.createElement('select');
select.id = 'sector-select';
select.setAttribute('aria-labelledby', 'sector-field-label');
select.setAttribute('aria-controls', 'company-cards');
select.add(new Option('Choose a sector…', 'All'));
sectorGroups.forEach(category => {
select.add(
new Option(`${category.name} · ${category.count}`, category.name),
);
});
select.addEventListener('change', () => setSector(select.value));
const classic = document.createElement('div');
classic.id = 'classic-sector-controls';
classic.className = 'sector-tabs';
classic.style.flex = '1';
classic.append(all, divider, select);
const template = document.createElement('template');
template.innerHTML = `<div id="sector-combobox" class="combobox" data-auto-highlight="true">
<input id="sector-combo-input" type="text" role="combobox" aria-labelledby="sector-field-label" aria-autocomplete="list" aria-expanded="false" aria-controls="sector-options" placeholder="Find a sector…" autocomplete="off" autocorrect="off" spellcheck="false" />
<svg class="combobox-trigger-icon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" />
<button id="clear-sector" type="button" data-clear aria-label="Clear sector filter" hidden><svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12" />
<div id="sector-popover" data-popover data-align="end" aria-hidden="true">
<div id="sector-options" role="listbox" aria-label="Sectors" aria-orientation="vertical" data-empty="No sectors found.">

`; const combo = template.content.firstElementChild; const list = combo.querySelector('[role="listbox"]'); sectorGroups.forEach(group =&gt; { const option = document.createElement('div'); option.setAttribute('role', 'option'); option.dataset.value = group.name; option.dataset.label = group.name; const swatch = document.createElement('span'); swatch.className = 'sector-option-swatch'; swatch.style.background = group.color; swatch.setAttribute('aria-hidden', 'true'); const count = document.createElement('span'); count.className = 'sector-option-count'; count.textContent = group.count; option.append(swatch, document.createTextNode(group.name), count); list.append(option); }); tabs.append(classic, combo); window.basecoat.initAll(); combo.addEventListener('change', event =&gt; { if (event.target !== combo || syncingComboboxes || !event.detail) return; setSector(event.detail.value || 'All'); }); connectComboboxFocus(combo, () =&gt; activeSector === 'All' ? '' : activeSector, ); syncSectorControlStyle(); } function syncSectorControlStyle() { const classic = false; document.querySelector('#classic-sector-controls').hidden = !classic; document.querySelector('#sector-combobox').hidden = classic; } function sizeCarousel() { const carousel = document.querySelector('#company-cards'); const width = carousel.clientWidth; const columns = width &gt;= 700 ? 3 : width &gt;= 500 ? 2 : 1; const cardWidth = columns === 1 ? Math.min(320, width - 32) : (width - (columns - 1) * 16) / columns; carousel.style.setProperty('--carousel-card-width', `${cardWidth}px`); carousel.classList.toggle('is-centered', carouselCentered); // Selected cards center where possible; the list edges stay flush with the viewport. const edgeSpace = Math.max(0, width - cardWidth); carousel.style.setProperty('--carousel-start-space', '0px'); carousel.style.setProperty( '--carousel-end-space', `${carouselCentered ? 0 : edgeSpace}px`, ); } function setSector(name) { const selectedCompany = scene?.units.find( unit =&gt; unit.id === selectedId, ); if ( selectedCompany &amp;&amp; name !== 'All' &amp;&amp; selectedCompany.category !== name ) { selectedId = null; companyQuery = ''; searchInput.value = ''; closeCompanyOptions(); syncUrl(); } activeSector = name; carouselNavigated = false; carouselCentered = Boolean(selectedId); previewId = null; const carousel = document.querySelector('#company-cards'); document.querySelector('#sector-all').setAttribute('aria-pressed', String(name === 'All')); document.querySelector('#sector-select').value = name; syncComboboxValue( document.querySelector('#sector-combobox'), name === 'All' ? '' : name, ); carousel.setAttribute( 'aria-label', name === 'All' ? 'Companies in all sectors' : `Companies in ${name}`, ); filterCompanyCards(); document.querySelector('#directory-layout').textContent = expandedDirectory ? 'Show carousel' : 'Show grid'; sizeCarousel(); carousel.scrollTo({ left: 0, behavior: 'instant' }); if (selectedId) alignCard( visibleCards().find( card =&gt; card.dataset.companyId === selectedId, ), false, ); updateCarouselControls(); scene?.applySelection(); refreshCompanyOptions(); syncUrl(); } function alignCard(card, animate = true) { if (!card || expandedDirectory) return; carouselNavigated = true; sizeCarousel(); const carousel = document.querySelector('#company-cards'); const box = card.getBoundingClientRect(); const frame = carousel.getBoundingClientRect(); carousel.scrollTo({ left: carousel.scrollLeft + box.left - frame.left - (carouselCentered ? (carousel.clientWidth - box.width) / 2 : 0), behavior: animate &amp;&amp; !reducedMotion() ? 'smooth' : 'instant', }); updateCarouselControls(); } function matchVisibleCardHeights(cards) { document.querySelectorAll('#company-cards.company-card').forEach(card =&gt; { card.style.removeProperty('min-height'); }); const carousel = document.querySelector('#company-cards'); carousel.style.removeProperty('height'); if (expandedDirectory || !cards.length) return; const height = Math.ceil( Math.max(...cards.map(card =&gt; card.getBoundingClientRect().height)), ); cards.forEach(card =&gt; { card.style.minHeight = `${height}px`; }); // Size the row to the cards in view, not the tallest card off-screen. const style = getComputedStyle(carousel); const padding = style.boxSizing === 'border-box' ? parseFloat(style.paddingTop) + parseFloat(style.paddingBottom) : 0; carousel.style.height = `${height + padding}px`; } function updateCarouselControls({ updateRange = true } = {}) { const carousel = document.querySelector('#company-cards'); const cards = visibleCards(); const frame = carousel.getBoundingClientRect(); const shown = cards.map((card, index) =&gt; ({ box: card.getBoundingClientRect(), index, })).filter( ({ box }) =&gt; box.right &gt; frame.left + 2 &amp;&amp; box.left &lt; frame.right - 2, ); // Count only cards that are mostly in view, so a peeking card isn't "shown". const counted = shown.filter( ({ box }) =&gt; Math.min(box.right, frame.right) - Math.max(box.left, frame.left) &gt;= box.width * 0.6, ); const range = counted.length ? counted : shown; matchVisibleCardHeights(shown.map(({ index }) =&gt; cards[index])); document.querySelector('#directory-layout').hidden = cards.length &lt;= 1; if (updateRange) document.querySelector('#carousel-position').textContent = expandedDirectory ? `${cards.length} ${cards.length === 1 ? 'company' : 'companies'}` : range.length &gt; 1 ? `${range[0].index + 1}–${range.at(-1).index + 1} of ${cards.length}` : range.length ? `${range[0].index + 1} of ${cards.length}` : `0 of ${cards.length}`; for (const [id, unavailable] of [['carousel-previous', carousel.scrollLeft &lt; 2], [ 'carousel-next', carousel.scrollLeft &gt;= carousel.scrollWidth - carousel.clientWidth - 2, ], ]) { const button = document.getElementById(id); button.hidden = expandedDirectory || cards.length &lt;= 1; button.disabled = unavailable; } } document.querySelector('#directory-layout').addEventListener('click', event =&gt; { expandedDirectory = !expandedDirectory; const carousel = document.querySelector('#company-cards'); carousel.classList.toggle('is-expanded', expandedDirectory); carousel.parentElement.classList.toggle( 'is-expanded', expandedDirectory, ); carousel.setAttribute( 'aria-roledescription', expandedDirectory ? 'company grid' : 'carousel', ); event.currentTarget.setAttribute( 'aria-pressed', String(expandedDirectory), ); setSector(activeSector); }); function stepCarousel(direction) { const carousel = document.querySelector('#company-cards'); const frame = carousel.getBoundingClientRect(); const cards = visibleCards(); const current = cards.reduce( (best, card, index) =&gt; { const box = card.getBoundingClientRect(); const distance = Math.abs( box.left - frame.left - (carouselCentered ? (carousel.clientWidth - box.width) / 2 : 0), ); return distance &lt; best.distance ? { index, distance } : best; }, { index: 0, distance: Infinity }, ); alignCard( cards[ Math.max(0, Math.min(cards.length - 1, current.index + direction)) ], ); } document.querySelector('#carousel-previous').addEventListener('click', () =&gt; stepCarousel(-1)); document.querySelector('#carousel-next').addEventListener('click', () =&gt; stepCarousel(1)); document.querySelector('#company-cards').addEventListener('keydown', event =&gt; { if ( expandedDirectory || event.target !== event.currentTarget || !['ArrowLeft', 'ArrowRight'].includes(event.key) ) return; event.preventDefault(); stepCarousel(event.key === 'ArrowRight' ? 1 : -1); }); function toggleCompany(id) { if (selectedId === id) clearSelection(); else selectCompany(id, true); } function selectCompany(id, reveal = false, syncInput = true) { selectedId = id; carouselCentered = true; previewId = null; if (syncInput) { const selectedCompany = scene?.units.find(unit =&gt; unit.id === id); if (selectedCompany) { searchInput.value = selectedCompany.company; companyQuery = selectedCompany.company; closeCompanyOptions(); } } const unit = scene?.units.find(unit =&gt; unit.id === id); if (unit) { if (activeSector !== 'All' &amp;&amp; activeSector !== unit.category) setSector(unit.category); // Keep the current position so nearby selections only travel the distance between cards. filterCompanyCards({ resetScroll: false }); alignCard( visibleCards().find(card =&gt; card.dataset.companyId === id), ); } scene?.applySelection(); if (reveal) scene?.reveal(id); syncUrl(); } // Keep ?sector= and ?company= in the address bar so a copied URL opens on the same view. // replaceState swaps the URL in place, so Back still leaves the page in one step. // Other parameters (e.g. ?view=dashboard, Drupal's own) are left untouched. function syncUrl() { if (!urlReady) return; const url = new URL(window.location.href); if (activeSector !== 'All') url.searchParams.set('sector', slug(activeSector)); else url.searchParams.delete('sector'); if (selectedId) url.searchParams.set('company', selectedId); else url.searchParams.delete('company'); if (url.href !== window.location.href) history.replaceState(history.state, '', url); } function clearSelection() { const carousel = document.querySelector('#company-cards'); const scrollLeft = carousel.scrollLeft; selectedId = previewId = null; companyQuery = ''; searchInput.value = ''; filterCompanyCards({ resetScroll: false }); closeCompanyOptions(); scene?.applySelection(); syncUrl(); // Preserve the centered view and snap geometry when clearing selection. carousel.scrollTo({ left: scrollLeft, behavior: 'instant' }); updateCarouselControls(); } // Basecoat owns option filtering, popup visibility, and keyboard navigation. // Guard its change event when chart/card interactions update the control. let syncingComboboxes = false; function syncComboboxValue(combo, value) { if (!combo?.setValue) return; syncingComboboxes = true; try { combo.setValue(value); } finally { syncingComboboxes = false; } syncComboboxSwatch(combo); } // Show the committed sector's color; typing a new search hides it. // The company field skips it: its highlighted card already shows the color. function syncComboboxSwatch(combo) { if (combo.id !== 'sector-combobox') return; const sector = activeSector !== 'All' &amp;&amp; activeSector; const color = categories.find( category =&gt; category.name === sector, )?.color; combo.classList.toggle('has-swatch', Boolean(color)); if (color) combo.style.setProperty('--swatch-color', color); } document.addEventListener('input', event =&gt; { const combo = event.target.closest?.('.rfe-controls.combobox'); if (combo &amp;&amp; event.target.matches('[role=combobox]')) combo.classList.remove('has-swatch'); }); function closeCompanyOptions() { const combo = document.querySelector('#company-combobox'); const selected = scene?.units.find(unit =&gt; unit.id === selectedId); syncComboboxValue( combo, selected ? { value: selected.id, label: selected.company } : '', ); combo.close?.(); } function refreshCompanyOptions() { if (!scene) return; const combo = document.querySelector('#company-combobox'); const list = document.querySelector('#search-matches'); const choices = scene.units.filter( unit =&gt; activeSector === 'All' || unit.category === activeSector, ).sort((a, b) =&gt; a.company.localeCompare(b.company, 'en', { sensitivity: 'base' }), ); list.replaceChildren(...choices.map(unit =&gt; { const option = document.createElement('div'); option.setAttribute('role', 'option'); option.dataset.value = unit.id; option.dataset.label = unit.company; option.textContent = unit.company; return option; }), ); combo.refresh?.(); const selected = scene.units.find(unit =&gt; unit.id === selectedId); syncComboboxValue( combo, selected ? { value: selected.id, label: selected.company } : '', ); } function connectComboboxFocus(combo, committedValue) { // A dismissed search restores the committed filter. Tab also closes the popup. combo.addEventListener('focusout', () =&gt; { requestAnimationFrame(() =&gt; { if (combo.contains(document.activeElement)) return; combo.close(); syncComboboxValue(combo, committedValue()); }); }); combo.addEventListener( 'keydown', event =&gt; { if ( event.key === 'Escape' &amp;&amp; combo.querySelector('[role=combobox]').getAttribute('aria-expanded') === 'true' ) { // Dismiss the search first; a second Escape can clear chart selection. event.preventDefault(); event.stopPropagation(); combo.close(true); syncComboboxValue(combo, committedValue()); } }, true, ); } let chartOpenedAtEnd = false; // Leave 2px for embedded-page rounding; preserve scrolling below 659px. function render() { const width = Math.max(CHART.minWidth, chart.clientWidth - 2); lastChartWidth = width; // Stay pinned to the end unless the reader has scrolled back in time. const pinnedToEnd = !chartOpenedAtEnd || chart.scrollLeft &gt;= chart.scrollWidth - chart.clientWidth - 4; buildChart(width); buildCompanyCards(); scene.applySelection(); // On narrow screens, open on the running total; readers scroll back in time. if (pinnedToEnd &amp;&amp; chart.scrollWidth &gt; chart.clientWidth) { chart.scrollLeft = chart.scrollWidth; chartOpenedAtEnd = true; } const legend = document.querySelector('#legend'); legend.replaceChildren(); for (const { name, color } of categories) { const key = document.createElement('span'); key.className = 'key'; const swatch = document.createElement('i'); swatch.style.background = color; swatch.setAttribute('aria-hidden', 'true'); key.append(swatch, document.createTextNode(name)); legend.append(key); } updateScrollCue(); placeIntro(); } // Shown only when the chart overflows. It fades out for good as soon as the // reader swipes, since the earlier months then slide in underneath it. // The left-edge fade stays until they reach the first month. const scrollCue = document.querySelector('#scroll-cue'); let cueDismissed = false; function updateScrollCue() { const earlier = chart.scrollLeft &gt; 4; if ( chartOpenedAtEnd &amp;&amp; chart.scrollLeft &lt; chart.scrollWidth - chart.clientWidth - 4 ) cueDismissed = true; scrollCue.hidden = chart.scrollWidth &lt;= chart.clientWidth; scrollCue.classList.toggle('is-done', !earlier || cueDismissed); chart.classList.toggle('has-earlier', earlier); } chart.addEventListener('scroll', updateScrollCue, { passive: true }); // A tap or click steps back most of a screen. scrollCue.addEventListener('click', () =&gt; chart.scrollBy({ left: -chart.clientWidth * 0.8, behavior: reducedMotion() ? 'instant' : 'smooth', }), ); window.basecoat.initAll(); const companyCombo = document.querySelector('#company-combobox'); companyCombo.addEventListener('change', event =&gt; { if ( event.target !== companyCombo || syncingComboboxes || !event.detail ) return; if (event.detail.value) selectCompany(event.detail.value, true); else clearSelection(); }); connectComboboxFocus(companyCombo, () =&gt; { const unit = scene?.units.find(unit =&gt; unit.id === selectedId); return unit ? { value: unit.id, label: unit.company } : ''; }); // One Escape action clears pinned selections, hover previews, and company searches. document.addEventListener('keydown', event =&gt; { if ( event.key !== 'Escape' || event.defaultPrevented || event.isComposing ) return; const results = document.querySelector('#search-results'); if ( !selectedId &amp;&amp; !previewId &amp;&amp; !companyQuery &amp;&amp; results.getAttribute('aria-hidden') === 'true' ) return; event.preventDefault(); const restoreSearchFocus = document.activeElement?.closest( '#search-results, #detail-content', ); clearSelection(); if (restoreSearchFocus) searchInput.focus({ preventScroll: true }); companyCombo.close(); document.querySelector('#selection-announcement').textContent = 'Selection cleared.'; }); new ResizeObserver(() =&gt; { if (!data) return; const width = Math.max(CHART.minWidth, chart.clientWidth - 2); if (width !== lastChartWidth) render(); else { scene.setMode(mode, false); placeIntro(); } }).observe(chart); // Published Google Sheet (companies tab). The embedded copy below is the fallback. const SHEET_CSV_URL = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQasIorR3ELmmDuONGc45OTTSdAS8NRVOdl0U4y4VJdZxVjr-uW_K7ac0X_L99tXeG3elpn_ZCMuf8N/pub?gid=570003253&amp;single=true&amp;output=csv'; const embeddedCsv = 'company,sector,agency,announced,summary,source\nU.S. Steel/Nippon Steel,Steel,Committee on Foreign Investment in the United States,2025-06,"Golden share under the CFIUS National Security Agreement, with veto rights over major corporate decisions",https://www.nipponsteel.com/common/secure/en/news/20250618_100.pdf\nMP Materials,Minerals,Defense,2025-07,"$400 million DoD preferred-stock purchase plus a warrant, 15% if converted/exercised, with a separate $150 million OSC direct loan",https://investors.mpmaterials.com/investor-news/news-details/2025/MP-Materials-Announces-Transformational-Public-Private-Partnership-with-the-Department-of-Defense-to-Accelerate-U-S--Rare-Earth-Magnet-Independence/default.aspx\nIntel,Semiconductors,Commerce,2025-08,"$8.9 billion in CHIPS Incentives and Secure Enclave funding for a 9.9% stake, plus a 5% warrant if foundry ownership falls below 51%",https://www.intc.com/news-events/press-releases/detail/1748/intel-and-trump-administration-reach-historic-agreement-to\nTrilogy Metals,Minerals,Defense,2025-10,"$35.6 million executed IBAS investment for 10% direct ownership, plus 7.5% in penny warrants (17.5% total economic position)",https://www.sec.gov/Archives/edgar/data/1543418/000127956925001094/ex991.pdf\nLithium Americas,Minerals,Energy,2025-10,"$184 million DOE ATVM debt-service deferral, with 5% company warrants and a 5% Thacker Pass JV investment",https://www.sec.gov/Archives/edgar/data/1966983/000119312525233937/d10878d8k.htm\nWestinghouse,Energy (nuclear),Commerce,2025-10,"20% Commerce upside participation, convertible to an equity warrant if IPO conditions are met",https://www.sec.gov/Archives/edgar/data/1009001/000119312525260490/d23402dex992.htm\nVulcan Elements,Minerals,"Commerce, Defense",2025-11,"Proposed $50 million CHIPS equity investment, plus a conditional $620 million OSC loan with warrants",https://www.war.gov/News/Releases/Release/Article/4339788/office-of-strategic-capital-agrees-to-joint-700m-conditional-loan-commitment-wi/\nReElement Technologies,Minerals,Defense,2025-11,"Original conditional $80 million OSC loan with warrants withdrawn and replaced by a $25 million EDU investment financed through the Industrial Base Fund (IBF), form not disclosed",https://www.sec.gov/Archives/edgar/data/1590715/000165495425012526/arec_ex991.htm\n"Korea Zinc/Crucible Metals, LLC",Minerals,"Commerce, Defense",2025-12,"$210 million CHIPS award to Crucible Metals, with equity provided to Commerce by an affiliate under undisclosed terms. Defense contributed $150 million in IBAS equity to Crucible JV, which acquired 10.59% of Korea Zinc, and conditionally committed $1.25 billion in OSC loans",https://finance.yahoo.com/news/factbox-trump-administration-pivots-buying-132228071.html\nxLight,Semiconductors,Commerce,2025-12,"$150 million in CHIPS R&amp;D funding, with a Commerce equity stake",https://www.nist.gov/news-events/news/2025/12/department-commerce-and-nist-announce-chips-research-and-development-letter\nL3Harris/Missile Solutions,Rocket motors,Defense,2026-01,"$1 billion IBAS convertible preferred security in Missile Solutions, plus warrants, convertible to common stock at IPO",https://www.war.gov/News/Releases/Release/Article/4376463/department-of-war-announces-1-billion-direct-to-supplier-investment-to-secure-t/\n"Atlantic Alumina Company, LLC",Minerals,Defense,2026-01,"$400 million in IBAS preferred equity, including a $100 million follow-on announced Aug. 28, 2026, through Class A Preferred Units",https://www.reuters.com/business/energy/usa-rare-earth-shares-surge-report-trump-administration-investment-2026-01-26/\nUSA Rare Earth,Minerals,Commerce,2026-01,"Up to $277 million in CHIPS direct funding and up to $1.3 billion in a CHIPS senior secured loan, with shares and warrants",https://www.usare.com/article?i=161514\nGlobalFoundries,Quantum computing,Commerce,2026-05,Proposed up to $675 million in CHIPS R&amp;D funding across two LOIs: $375 million for a quantum foundry and $300 million for silicon photonics. A separate agreement gives Commerce an approximately 1% equity stake.,https://www.nist.gov/news-events/news/2026/05/department-commerce-announces-letters-intent-9-companies-2-billion\nIBM/Anderson,Quantum computing,Commerce,2026-05,Proposed $1 billion in CHIPS R&amp;D funding for a minority stake in Anderon. IBM will contribute $1 billion plus assets and IP,https://www.nist.gov/news-events/news/2026/05/department-commerce-announces-letters-intent-9-companies-2-billion\nAtom Computing,Quantum computing,Commerce,2026-05,"$100 million in planned CHIPS R&amp;D funding, with a minority equity stake",https://www.nist.gov/news-events/news/2026/05/department-commerce-announces-letters-intent-9-companies-2-billion\nDiraq,Quantum computing,Commerce,2026-05,"Up to $38 million in planned CHIPS R&amp;D funding, with a minority equity stake",https://www.nist.gov/news-events/news/2026/05/department-commerce-announces-letters-intent-9-companies-2-billion\nD-Wave,Quantum computing,Commerce,2026-05,Proposed $100 million in CHIPS R&amp;D funding for a $100 million equity stake at final award,https://www.nist.gov/news-events/news/2026/05/department-commerce-announces-letters-intent-9-companies-2-billion\nInfleqtion,Quantum computing,Commerce,2026-05,Proposed $100 million in CHIPS R&amp;D funding for common stock priced 15% below the lower of the LOI-date or final-agreement market price,https://www.nist.gov/news-events/news/2026/05/department-commerce-announces-letters-intent-9-companies-2-billion\nPsiQuantum,Quantum computing,Commerce,2026-05,"$100 million in planned CHIPS R&amp;D funding, with a minority equity stake",https://www.nist.gov/news-events/news/2026/05/department-commerce-announces-letters-intent-9-companies-2-billion\nQuantinuum,Quantum computing,Commerce,2026-05,Proposed $100 million in CHIPS R&amp;D funding for common stock priced at the lower of 20% below a $60 IPO price or 15% below the award-date closing price,https://www.nist.gov/news-events/news/2026/05/department-commerce-announces-letters-intent-9-companies-2-billion\nRigetti,Quantum computing,Commerce,2026-05,Proposed $100 million in CHIPS R&amp;D funding over three years for common stock priced 15% below the lower of the May 20 or award-date closing price,https://www.nist.gov/news-events/news/2026/05/department-commerce-announces-letters-intent-9-companies-2-billion\nSandboxAQ,Semiconductors,Commerce,2026-06,"$500 million in CHIPS R&amp;D funding, with a minority equity stake",https://www.nist.gov/news-events/news/2026/06/department-commerce-announces-definitive-agreement-sandboxaq-500-million\nI-Pulse,Semiconductors,Commerce,2026-06,"$250 million in CHIPS R&amp;D funding, with a minority equity stake",https://www.nist.gov/news-events/news/2026/06/department-commerce-announces-definitive-agreement-i-pulse-250-million\nKepler Computing,Semiconductors,Commerce,2026-07,"Proposed up to $245 million in CHIPS R&amp;D funding, with a minority equity stake",https://www.nist.gov/news-events/news/2026/07/department-commerce-announces-letters-intent-7-companies-874-million\nMultibeam Corporation,Semiconductors,Commerce,2026-07,"Proposed up to $140 million in CHIPS R&amp;D funding, with a minority equity stake",https://www.nist.gov/news-events/news/2026/07/department-commerce-announces-letters-intent-7-companies-874-million\nExtropic,Semiconductors,Commerce,2026-07,"Proposed up to $75 million in CHIPS R&amp;D funding, with a minority equity stake",https://www.nist.gov/news-events/news/2026/07/department-commerce-announces-letters-intent-7-companies-874-million\nThintronics,Semiconductors,Commerce,2026-07,"Proposed up to $50 million in CHIPS R&amp;D funding, with a minority equity stake",https://www.nist.gov/news-events/news/2026/07/department-commerce-announces-letters-intent-7-companies-874-million\nOBSIDIA Semiconductors,Semiconductors,Commerce,2026-07,"Proposed up to $34 million in CHIPS R&amp;D funding, with a minority equity stake",https://www.nist.gov/news-events/news/2026/07/department-commerce-announces-letters-intent-7-companies-874-million\nAeluma,Semiconductors,Commerce,2026-07,"Proposed up to $30 million in CHIPS R&amp;D funding, with a minority equity stake equal to the award amount",https://www.nist.gov/news-events/news/2026/07/department-commerce-announces-letters-intent-7-companies-874-million\n"Strategic Bauxite USA, LLC",Minerals,Defense,2026-08,"$85.5 million IBAS equity investment agreement alongside $64.5 million in private co-investment, with the federal ownership percentage undisclosed",https://www.war.gov/News/Releases/Release/Article/4566604/department-of-war-announces-an-855-million-agreement-with-strategic-bauxite-usa/\nNorth American Blue Energy Partners (NABEP),Energy (oil),"Defense, State",2026-09,"NABEP granted OSC a 35% equity stake in its corporate parent, reportedly through penny warrants. State receives the right to buy 20% of production at cost and first refusal on the remaining 80%. U.S. government has veto power over board appointments",https://www.whitehouse.gov/fact-sheets/2026/08/fact-sheet-president-donald-j-trump-announces-historic-oil-agreement-to-secure-american-energy-dominance-and-drive-venezuelas-economic-recovery/\nElmet Group,Minerals,Defense,2026-09,"$450 million DoD commitment (EDU; funding source undisclosed) for redeemable preferred equity and warrants for up to 19.9% of post-transaction common stock if exercised ($200 million at closing, remainder through subsequent drawdowns)",https://www.war.gov/News/Releases/Release/Article/4599882/department-of-war-announces-a-450-million-investment-in-the-elmet-group-to-secu/\nHertha Metals,Minerals,Defense,2026-09,"$65 million DoD equity investment through IBAS, in partnership with EDU, included in a closed $133.65 million Series A financing round; ownership percentage and share terms undisclosed",https://www.businesswire.com/news/home/20260929199684/en/Hertha-Metals-Secures-%24133.65M-Series-A-to-Produce-High-Purity-Iron-for-Domestic-Rare-Earth-Magnets\n'; async function fetchSheet() { try { const response = await fetch(SHEET_CSV_URL, { cache: 'no-cache' }); if (!response.ok) throw new Error(`HTTP ${response.status}`); const source = await response.text(); parse(source); return source; } catch (cause) { console.warn('Using embedded data; sheet fetch failed:', cause); return embeddedCsv; } } async function loadData() { try { csvSource = await fetchSheet(); data = parse(csvSource); const first = data[0]; const last = data.at(-1); const range = document.createElement('span'); range.className = 'subtitle-range'; range.textContent = `${d3.timeFormat('%b %Y')(first.date)}–${d3.timeFormat('%b %Y')(last.date)}`; document.querySelector('#chart-subtitle').replaceChildren('Federal equity positions announced ', range); render(); refreshCompanyOptions(); // Open a shared link. Unknown or renamed names are skipped, never an error, // and a company outside the shared sector is dropped so the sector holds. const params = new URLSearchParams(window.location.search); const sharedSector = sectorGroups.find( group =&gt; slug(group.name) === params.get('sector'), )?.name; const sharedCompany = scene.units.find( unit =&gt; unit.id === params.get('company') &amp;&amp; (!sharedSector || unit.category === sharedSector), ); urlReady = true; if (sharedSector) setSector(sharedSector); if (sharedCompany) selectCompany(sharedCompany.id, true); syncUrl(); } catch (cause) { chart.setAttribute('role', 'alert'); chart.removeAttribute('aria-label'); chart.textContent = `Unable to load the chart: ${cause.message} Refresh the page to retry.`; } finally { revealComponent(); } } // Downloads: a golden-ratio PNG of the full chart with title, source, and logo, and the raw CSV. // The PNG is drawn from an offscreen copy at a fixed size, so every reader // gets the same clean, unselected image whatever their screen. let csvSource = ''; const CATO_LOGO_SVG = ``; const EXPORT = { companyHighlight: COMPANY_EXPORT, // See COMPANY EXPORT SWITCH near the top. // Layout size in CSS pixels; scale sets the PNG's pixel density. // Golden ratio (1.618:1) at 2.16x gives 2160 x 1335, which social feeds show // uncropped and which drops into slides. For a square, try 800 x 800 at 2.7x, // though the months get crowded at that width. width: 1000, height: 618, scale: 2.16, padding: 24, // Larger type than the page, since the image is usually seen shrunk in a feed. chartTextSize: 15, titleSize: 26, cardNameSize: 22, // Company name on a card, clearly above its sector line. cardDetailSize: 12, // Sector, date and agency lines; matches the summary. cardSummarySize: 12, cardWidth: 430, // Company card text width; the chart's upper-left is empty here. textSize: 16, sourceSize: 14, logoHeight: 36, source: 'Source: Company and government announcements compiled by the Cato Institute.', }; const fileStamp = () =&gt; d3.timeFormat('%Y-%m-%d')(new Date()); function saveBlob(blob, filename) { const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = filename; document.body.append(link); link.click(); link.remove(); setTimeout(() =&gt; URL.revokeObjectURL(link.href), 1000); } const loadImage = src =&gt; new Promise((resolve, reject) =&gt; { const image = new Image(); image.onload = () =&gt; resolve(image); image.onerror = () =&gt; reject(new Error('Image failed to load.')); image.src = src; }); const toDataUrl = blob =&gt; new Promise(resolve =&gt; { const reader = new FileReader(); reader.onload = () =&gt; resolve(reader.result); reader.readAsDataURL(blob); }); // An SVG drawn as an image can't see the page's webfonts, so embed Inter's // Latin subset. If that fails, the chart text falls back to a system sans. let interFontCss; function embeddedInterCss() { interFontCss ??= (async () =&gt; { const css = await ( await fetch( 'https://fonts.googleapis.com/css2?family=Inter:wght@400..600&amp;display=swap', ) ).text(); const latin = css.split('/* latin */')[1] || css; const url = latin.match(/url\((https:[^)]+\.woff2)\)/)[1]; const font = await toDataUrl(await (await fetch(url)).blob()); return `@font-face{font-family:'Inter';font-weight:400 600;src:url(${font}) format('woff2');}`; })().catch(error =&gt; { console.warn('Exporting without embedded Inter:', error); interFontCss = undefined; return ''; }); return interFontCss; } // The committed selection the export can highlight: a picked company, else a // chosen sector. Hover previews and half-typed searches don't count. function exportHighlight() { const unit = EXPORT.companyHighlight &amp;&amp; scene?.units.find(item =&gt; item.id === selectedId); if (unit) return { name: unit.company, line: `Highlighted: ${unit.company} · ${unit.category} · ${d3.timeFormat('%B %Y')(d3.timeParse('%Y-%m')(unit.announced.slice(0, 7)))}`, file: unit.id, unit, }; if (activeSector === 'All') return null; const count = scene.units.filter( item =&gt; item.category === activeSector, ).length; return { name: activeSector, line: `Highlighted: ${activeSector} (${count} ${count === 1 ? 'company' : 'companies'})`, file: slug(activeSector), }; } async function chartImage(width, height, highlight) { const holder = document.createElement('div'); holder.id = 'chart-export'; holder.setAttribute('aria-hidden', 'true'); // Offscreen but laid out, so text can be measured for the label gutter. Object.assign(holder.style, { position: 'absolute', left: '-10000px', top: '0', visibility: 'hidden', }); for (const [name, value] of [ ['--type-note', `${EXPORT.chartTextSize}px`], ['--type-label', `${EXPORT.chartTextSize}px`], ]) holder.style.setProperty(name, value); document.querySelector('#fed-stakes').append(holder); let copy; let obstacles = []; // The offscreen chart reads the page's selection state; set it to what // this image should show, then put the reader's state back. const saved = { selectedId, previewId, companyQuery, activeSector }; previewId = null; companyQuery = ''; if (!highlight) { selectedId = null; activeSector = 'All'; } else if (!highlight.unit) selectedId = null; try { const live = buildChart(width, { height, target: holder }); // Even instant D3 transitions apply on the next frame; run them now. d3.timerFlush(); if (highlight?.unit) { // A company's image drops the monthly "+n" counts, which the // running totals already imply, and the on-chart name label: // the card names the company and its full-color square marks it. live.querySelectorAll('.addition-labels,.selection-labels').forEach(node =&gt; node.remove()); } copy = live.cloneNode(true); // Boxes of the marks and their labels, so a company card's text can // flow around the staircase instead of running into it. obstacles = [...live.querySelectorAll( 'rect.company-mark,.top-labels text,.addition-labels text,.final-label', ), ].map(node =&gt; node.getBBox()).filter(box =&gt; box.width &amp;&amp; box.height); // Classes carry the chart's type styles; copy the computed values inline. const liveText = live.querySelectorAll('text, tspan'); copy.querySelectorAll('text, tspan').forEach((node, index) =&gt; { const style = getComputedStyle(liveText[index]); for (const property of ['fill', 'font-size', 'font-weight']) node.style.setProperty( property, style.getPropertyValue(property), ); }); } finally { holder.remove(); ({ selectedId, previewId, companyQuery, activeSector } = saved); } copy.querySelectorAll('.mark-focus-ring,.keyboard-hint').forEach(node =&gt; node.remove()); if (highlight?.unit) { // Outline the company's squares so the card and chart point at each other. copy.querySelectorAll( `rect.company-mark[data-company-id="${CSS.escape(highlight.unit.id)}"]`, ).forEach(node =&gt; { // Drawn inside the square, so the white gaps between squares // and the square's size stay as they are. const ring = node.cloneNode(false); const inset = 0.8; for (const [key, shift] of [ ['x', inset], ['y', inset], ['width', -2 * inset], ['height', -2 * inset], ]) ring.setAttribute(key, +node.getAttribute(key) + shift); ring.removeAttribute('data-company-id'); ring.setAttribute('fill', 'none'); ring.setAttribute('stroke', '#18181b'); ring.setAttribute('stroke-width', 1); node.after(ring); }); // Muted bars stay pale, but the totals stay readable. copy.querySelectorAll( '.top-labels,.addition-labels,.final-label,.equals-label', ).forEach(node =&gt; node.setAttribute('opacity', 0.75)); // Faded sector names drop to regular weight, so the company's sector // is the only bold label in the Total column. copy.querySelectorAll('.total-sector-labels text').forEach(node =&gt; { if (node.getAttribute('fill') === SELECTION.mutedText) node.querySelectorAll('.sector-name').forEach(name =&gt; name.style.setProperty('font-weight', '400')); }); } // Sector counts don't align with other numbers, so tabular 1s only look loose. copy.querySelectorAll('.sector-count').forEach(node =&gt; node.style.setProperty('font-variant-numeric', 'proportional-nums'), ); copy.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); copy.setAttribute( 'style', "font-family:'Inter',system-ui,sans-serif;font-variant-numeric:tabular-nums", ); const style = document.createElementNS( 'http://www.w3.org/2000/svg', 'style', ); style.textContent = await embeddedInterCss(); copy.prepend(style); const markup = new XMLSerializer().serializeToString(copy); const image = await loadImage( `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`, ); return { image, obstacles }; } // Wraps text to a width; returns the lines. function wrapLines(context, text, maxWidth) { const lines = []; let line = ''; for (const word of text.split(/\s+/)) { const next = line ? `${line} ${word}` : word; if (line &amp;&amp; context.measureText(next).width &gt; maxWidth) { lines.push(line); line = word; } else line = next; } if (line) lines.push(line); return lines; } // A single company's export adds its card below the title block, laid out // like the chart-side card design: sector in its color, the company name, // labeled Announced and Agency columns, then the full summary. // The summary is never cut, since the image travels without the page. function drawCompanyCard(context, unit, top, obstacles = []) { const { padding, cardWidth, cardNameSize, cardDetailSize, cardSummarySize, } = EXPORT; const textSize = cardDetailSize; const font = (weight, size) =&gt; `${weight} ${size}px Inter, system-ui, sans-serif`; const color = categories.find(c =&gt; c.name === unit.category).color; let y = top; // Sector above the name, in its text color. y += textSize; context.fillStyle = categoryTextColors[color] || color; context.font = font(600, textSize); context.fillText(unit.category, padding, y); // Company name, close under its sector; long names wrap. y += cardNameSize * 0.2; context.fillStyle = '#18181b'; context.font = font(600, cardNameSize); for (const line of wrapLines(context, unit.company, cardWidth)) { y += cardNameSize * 1.15; context.fillText(line, padding, y); } // Announced and Agency columns: quiet labels over secondary-gray values, // so the details stay below the summary in weight. const announced = d3.timeFormat('%B %Y')( d3.timeParse('%Y-%m')(unit.announced.slice(0, 7)), ); const agencies = unit.agency.trim().split(/\s{2,}|\s*[,;]\s*/).filter(Boolean); context.font = font(400, textSize); const agencyX = padding + Math.max( context.measureText('Announced').width, context.measureText(announced).width, ) + textSize * 3; y += textSize * 2.2; context.fillStyle = '#71717a'; context.fillText('Announced', padding, y); context.fillText('Agency', agencyX, y); y += textSize * 1.45; context.fillStyle = '#52525b'; context.fillText(announced, padding, y); // Agencies share a line when they fit; otherwise one per line, so a // name never breaks partway ("Department of / State"). const agencyWidth = cardWidth - (agencyX - padding); const joined = agencies.join(', '); (context.measureText(joined).width &lt;= agencyWidth ? [joined] : agencies.flatMap(agency =&gt; wrapLines(context, agency, agencyWidth)) ).forEach((line, index) =&gt; { if (index) y += textSize * 1.45; context.fillText(line, agencyX, y); }); // The deal summary, in full, in the cards' dark gray. Each line's width // stops short of any chart mark or label at that height, so long // summaries wrap around the staircase. If lines would get too narrow, // the summary steps down in size until it fits. const summaryTop = y + cardSummarySize * 0.6; const gap = 12; const widthAt = (baseline, size) =&gt; obstacles.reduce((width, box) =&gt; { const overlaps = box.y &lt; baseline + size * 0.35 &amp;&amp; box.y + box.height &gt; baseline - size; return overlaps ? Math.min(width, box.x - padding - gap) : width; }, cardWidth); const flow = size =&gt; { context.font = font(400, size); const words = unit.summary.split(/\s+/); const lines = []; let baseline = summaryTop; let narrowest = cardWidth; while (words.length) { baseline += size * 1.5; const width = widthAt(baseline, size); narrowest = Math.min(narrowest, width); if (width &lt; size * 6) { // Nothing fits beside the chart here; the size is too large. if (baseline &gt; EXPORT.height) break; continue; } let line = words.shift(); while ( words.length &amp;&amp; context.measureText(`${line} ${words[0]}`).width &lt;= width ) line += ` ${words.shift()}`; lines.push({ line, baseline }); } return { lines, narrowest, fits: !words.length }; }; let layout; for (let size = cardSummarySize; size &gt;= 10.5; size -= 0.5) { layout = flow(size); layout.size = size; if (layout.fits &amp;&amp; layout.narrowest &gt;= cardWidth * 0.6) break; } context.fillStyle = '#3f3f46'; context.font = font(400, layout.size); for (const { line, baseline } of layout.lines) context.fillText(line, padding, baseline); } async function downloadPng(highlight = null) { await document.fonts.ready; const { width, height, padding, scale, logoHeight } = EXPORT; const footerHeight = logoHeight; const { titleSize, textSize } = EXPORT; const chartWidth = width - padding * 2; // The overview's title sits in the chart's empty upper-left corner, as on // the page. A company's image moves the title above the chart instead, so // the card gets the whole corner. const titleBand = highlight?.unit ? Math.round(titleSize * 1.6) : 0; const chartTop = padding + titleBand; const chartHeight = height - padding * 2 - footerHeight - titleBand; const { image: chartPicture, obstacles } = await chartImage( chartWidth, chartHeight, highlight, ); const canvas = document.createElement('canvas'); canvas.width = Math.round(width * scale); canvas.height = Math.round(height * scale); const context = canvas.getContext('2d'); context.imageSmoothingQuality = 'high'; context.scale(scale, scale); context.fillStyle = '#fff'; context.fillRect(0, 0, width, height); context.textBaseline = 'alphabetic'; const font = (weight, size) =&gt; `${weight} ${size}px Inter, system-ui, sans-serif`; context.drawImage( chartPicture, padding, chartTop, chartWidth, chartHeight, ); // Header: title, date range, and how to read the marks. A company's // image keeps only the title, with the card in the chart's corner. context.fillStyle = '#18181b'; const pageTitle = document.querySelector('#page-title').textContent.trim(); // The card names the company, so the title stays the chart's own. context.font = font(600, titleSize); context.fillText( pageTitle, padding - titleSize * 0.04, // optical pull-back for the large title padding + titleSize * 0.9, ); if (highlight?.unit) drawCompanyCard( context, highlight.unit, chartTop, // Chart coordinates, shifted to where the chart sits on the canvas. obstacles.map(box =&gt; ({ x: box.x + padding, y: box.y + chartTop, width: box.width, height: box.height, })), ); else { context.fillStyle = '#52525b'; context.font = font(400, textSize); const subtitleY = padding + titleSize * 0.9 + textSize * 1.9; context.fillText( document.querySelector('#chart-subtitle').textContent.trim(), padding, subtitleY, ); const keyY = subtitleY + textSize * 1.6; const square = Math.round(textSize * 0.75); context.fillText('Each', padding, keyY); const squareX = padding + context.measureText('Each').width + 7; context.fillRect(squareX, keyY - square + 1, square, square); context.fillText('is one company', squareX + square + 7, keyY); if (highlight) { context.fillStyle = '#18181b'; context.font = font(600, textSize); context.fillText(highlight.line, padding, keyY + textSize * 1.6); } } // Footer: source on the left, logo on the right. const footerTop = height - padding - logoHeight; context.fillStyle = '#71717a'; context.font = font(400, EXPORT.sourceSize); context.fillText(EXPORT.source, padding, footerTop + logoHeight - 3); // Fill the logo's vector paths directly so it stays crisp at any scale. const logoScale = logoHeight / 278; context.save(); context.translate(width - padding - 551 * logoScale, footerTop); context.scale(logoScale, logoScale); context.fillStyle = '#3043B4'; for (const [, path] of CATO_LOGO_SVG.matchAll(/ d="([^"]+)"/g)) context.fill(new Path2D(path)); context.restore(); const blob = await new Promise(resolve =&gt; canvas.toBlob(resolve, 'image/png'), ); saveBlob( blob, `government-equity-portfolio-${highlight ? `${highlight.file}-` : ''}${fileStamp()}.png`, ); } function downloadCsv() { // The BOM lets Excel read dashes and accents as UTF-8. const text = `﻿${csvSource.replace(/^﻿/, '')}`; saveBlob( new Blob([text], { type: 'text/csv;charset=utf-8' }), `government-equity-portfolio-${fileStamp()}.csv`, ); } // Menu button pattern: arrows move between items; Escape and Tab close. const downloadToggle = document.querySelector('#download-toggle'); const downloadOptions = document.querySelector('#download-options'); const highlightItem = downloadOptions.querySelector( '[data-download="png-highlight"]', ); let downloadItems = []; function setDownloadMenu(open, focusItem = 0) { if (open) { // Offer a highlighted image only while a company or sector is chosen. const highlight = exportHighlight(); highlightItem.hidden = !highlight; if (highlight) highlightItem.textContent = `Chart image, highlighting ${highlight.name}`; downloadItems = [...downloadOptions.querySelectorAll('button:not([hidden])'), ]; if (focusItem &lt; 0) focusItem = downloadItems.length - 1; } downloadOptions.hidden = !open; downloadToggle.setAttribute('aria-expanded', String(open)); if (open) downloadItems[focusItem].focus(); } downloadToggle.addEventListener('click', () =&gt; setDownloadMenu(downloadOptions.hidden), ); downloadToggle.addEventListener('keydown', event =&gt; { if (!['ArrowDown', 'ArrowUp'].includes(event.key)) return; event.preventDefault(); setDownloadMenu(true, event.key === 'ArrowUp' ? -1 : 0); }); downloadOptions.addEventListener('keydown', event =&gt; { const index = downloadItems.indexOf(document.activeElement); if (event.key === 'Escape') { // Close the menu only; don't also clear the chart selection. event.preventDefault(); event.stopPropagation(); setDownloadMenu(false); downloadToggle.focus(); } else if (['ArrowDown', 'ArrowUp'].includes(event.key)) { event.preventDefault(); const step = event.key === 'ArrowDown' ? 1 : -1; downloadItems[ (index + step + downloadItems.length) % downloadItems.length].focus(); } else if (event.key === 'Tab') setDownloadMenu(false); }); document.addEventListener('click', event =&gt; { if (!event.target.closest('.download-menu')) setDownloadMenu(false); }); downloadOptions.addEventListener('click', async event =&gt; { const item = event.target.closest('[data-download]'); if (!item || !data) return; setDownloadMenu(false); downloadToggle.focus(); if (item.dataset.download === 'csv') return downloadCsv(); item.disabled = true; try { await downloadPng( item === highlightItem ? exportHighlight() : null, ); } catch (error) { console.error('PNG export failed:', error); document.querySelector('#selection-announcement').textContent = 'Sorry, the chart image could not be created.'; } finally { item.disabled = false; } }); loadData(); 

#### Methodology 

The Government Ownership Dashboard tracks the second Trump administration’s expansion of federal ownership and ownership rights in private businesses. It covers arrangements announced or completed since January 20, 2025, within the scope described below. The dashboard counts companies covered by those arrangements, including pending deals.

**What we include**

We include common and preferred stock, as well as warrants, options, and convertible securities that give the federal government a right to acquire an ownership interest. These rights may depend on future events. An unexercised warrant or option is identified as such and does not mean the government already owns the underlying shares.

We also include special ownership instruments that confer company-specific control. The US Steel golden share, for example, gives the federal government veto rights over certain business decisions despite carrying no conventional economic interest.

**How we count companies**

Each company is counted once, even when it receives multiple awards, has arrangements with several federal agencies, or issues more than one type of ownership instrument. Both publicly traded and privately held companies, including companies headquartered outside the United States, are eligible.

Company-level interests can be held directly or through a transaction-specific intermediary that owns shares in an identified operating company. We distinguish these arrangements from stakes confined to an individual project, joint venture, investment fund, or financing vehicle that convey no interest in the sponsoring company. Those standalone arrangements are outside the main company count.

When one package includes both company and project interests, we describe both in the company’s entry without counting the project as another company. Entries identify the business in which the ownership rights actually arise.

**Development Finance Corporation investments**

Investments under the US International Development Finance Corporation’s [standing statutory equity authority](https://www.law.cornell.edu/uscode/text/22/9621) are excluded from the dashboard because Congress expressly authorized DFC minority equity investments in the 2018 BUILD Act. The dashboard focuses on the administration’s acquisition of ownership interests and rights in companies outside the established DFC equity program. This exclusion does not imply that DFC ownership raises no economic, governance, or legal concerns. It reflects a fundamental difference between DFC’s established, congressionally authorized equity investment program and the administration’s use of company ownership as a regular economic policy tool outside a broader financial crisis.

That said, DFC’s participation does not prevent a company or transaction from being included in the dashboard where another agency acquires a separate qualifying ownership interest. Arrangements involving DFC but executed on behalf of another agency or under other authorities are assessed based on their actual structure and the authority being exercised.