/**
 * PFG Toplist — the firm row.
 *
 * Measurements are transcribed from the resolved design dumps (`.sk-spot`,
 * `.sk-rowtop`, `.sk-rowmeta`, `.sk-rowid`, `.sk-rowmetrics`, `.sk-rowprice`,
 * `.sk-gbar`). Every token reference carries a literal fallback so a theme
 * switch degrades to less pretty, never to unreadable.
 *
 * The row RESTRUCTURES on phones rather than merely shrinking: the rank leaves
 * the flow for the card's top-left corner, the meta and the metrics wrap into a
 * two-column grid that fits inside the card, and the price column becomes a
 * full-width footer with the CTA as a block. Nothing inside the card scrolls
 * sideways. That is why this is a reflow and not a media-query font-size tweak.
 *
 * Both rails used to be horizontal scrollers instead. See the phone block at the
 * foot of this file for what that cost and why it went.
 */

/* While the Interactivity Router swaps the region, the list stops taking
   clicks: a second click mid-navigation is a race between two swaps of the
   same region. The class comes and goes with state.navigating; with scripting
   off it never appears.

   The rows themselves are replaced by placeholders (see the skeleton section
   at the foot of this file), so only what is left of the OUTGOING page dims —
   the more-link, the disclosure, and the empty state on a list with no rows to
   stand in for. Dimming the skeleton too would make the thing we just put
   there to be looked at the hardest thing on screen to see.

   NONE OF THIS APPLIES TO "SHOW MORE", which is why both rules exclude it.
   That navigation replaces nothing: the rows on screen come back identical, so
   dimming them, hiding them or taking their links away is a reload announcing
   itself for no reason. `.pfg-skel--more` is the placeholder that goes BELOW
   the rows rather than over them, and its presence is what tells this
   stylesheet which of the two navigations is in flight. */
.pfg-toplist.is-loading:has(.pfg-skel--replace) > :not(.pfg-skel) {
	opacity: 0.55;
	transition: opacity 150ms ease;
}

.pfg-toplist.is-loading:has(.pfg-skel--replace) {
	pointer-events: none;
}

/* Extending: the only thing that steps back is the control that was pressed,
   and it steps back so it cannot be pressed twice. Everything the reader is
   actually looking at is left exactly as it was. */
.pfg-toplist.is-loading:has(.pfg-skel--more) .pfg-toplist__more {
	opacity: 0.55;
	pointer-events: none;
	transition: opacity 150ms ease;
}

/* ================= The table =================

   The markup is a native <table>: one <tbody> per firm holding the data row
   and the grade strip. The design draws cards, so every table box is taken out
   of table layout and laid out with grid or flex instead.

   NO ARIA ROLES, deliberately. Native table semantics survive a `display`
   change in every engine we support (Chromium since 2020, Firefox since 2022,
   Safari since 17), confirmed by reading the accessibility tree against this
   stylesheet. Restating them would break ARIA rule 1, and a role set that is
   complete today silently becomes a partial one — which drops cells from the
   tree — the first time a cell is added without its role.

   TABLE LAYOUT NEVER RUNS here, so `table-layout: fixed` would be inert. The
   guarantee it buys is bought instead by every grid track being explicit: the
   rank and price tracks are fixed px and the firm track is the remainder, so
   no column width is derived from content and a webfont swap cannot reflow the
   table.

   THE ROW IS A GRID, and its right-hand visual column is a PAIR of cells
   stacked vertically — price over coupon. Flexbox used to nest that pair in a
   wrapper div and centre it independently of the identity column, with the row
   height the taller of the two. Cells cannot nest, so it is rebuilt as:

     - two grid rows, sized by the identity cell and the metrics cell;
     - the price cell SPANS both rows and reserves the coupon's height as
       padding, so its box is the full height of the price pair. Spanning is
       what makes the row the right height, and grid shares the surplus equally
       between the two spanned tracks — which is exactly the offset that
       centring a shorter identity pair produces;
     - the identity cell takes `align-self: end` and the metrics cell
       `align-self: start`, so the pair stays glued together across that shared
       surplus and lands where the centred wrapper used to.

   Verified against the pre-refactor geometry at 1440/1101/768/390, comfortable
   and compact: identical to the hundredth of a pixel. */

.pfg-toplist__rows {
	display: flex;
	flex-direction: column;
	width: 100%;
	margin: 0;
	padding: 0;
	border-spacing: 0;
	list-style: none;
}

/* The 10px between cards is a margin, not the flex container's `gap`. The
   header row is a real in-flow child, and a `gap` would put 10px above the
   first card to clear a row that is zero pixels tall. */
.pfg-row + .pfg-row {
	margin-top: 10px;
}

/* The header row stays IN FLOW and native. What is hidden is the TEXT inside
   each <th>, by a .pfg-sr span in the markup; the cell itself is only zeroed.

   `display: none` here would be a bug, not a shortcut: it removes the header
   cells from the accessibility tree, and a body row whose headers do not exist
   pairs every value with nothing. Measured — with `display: none` the tree
   reports 0 columnheaders; zeroed like this it reports all seven. */
.pfg-toplist__col {
	padding: 0;
	border: 0;
	block-size: 0;
}

/* Hiding a COLUMN is the one case where `display: none` on a <th> is right,
   because the matching <td> goes with it and the column stops existing for
   everyone. Customize switching one metric off, and compact density dropping
   all four, are both that case. Header and cells must always move together. */
.pfg-toplist__col.is-off {
	display: none;
}

/* ---- Card ---- */

.pfg-row {
	display: flex;
	flex-direction: column;
	min-width: 0;
	background: var(--surface-4, #1A1F2C);
	border: 1px solid var(--border-2, rgba(255, 255, 255, 0.07));
	border-radius: 18px;
}

.pfg-row__top {
	/* The gutter between the row's three visual columns. It was the flex
	   `gap`; as cell margins it can differ per breakpoint and density from one
	   declaration. */
	--pfg-row-gutter: 22px;
	/* The gap that stacks identity over metric rail. Was `.pfg-row__main`'s
	   flex gap, and it still is — the metric cells only have to match it. */
	--pfg-row-stack: 13px;
	--pfg-price-stack: 10px;
	/* The rank's own outer width: 30px plus its 1px borders. */
	--pfg-rank-w: 32px;
	/* The phone layout subtracts the row's own padding from the card width, so
	   a padding that drifts from that arithmetic is a silent layout bug. */
	--pfg-row-pad-y: 18px;
	--pfg-row-pad-x: 20px;

	display: grid;
	/* Rank, four content-sized metric tracks, a spacer that absorbs the rest of
	   the identity's width, then the price column. The gutters ride in the rank
	   and price tracks because they are cell margins, not a grid `gap`: the row
	   needs different gaps on its two axes and inside the identity stack, which
	   one `gap` cannot express.

	   Only the metric tracks are content-derived, and each is bounded by the
	   metric's own max-width, so a webfont swap cannot reflow the row's three
	   structural columns. (`table-layout: fixed` would be inert here — the
	   table is laid out as flex and grid, so table layout never runs.) */
	grid-template-columns:
		calc(var(--pfg-rank-w) + var(--pfg-row-gutter))
		repeat(4, auto)
		minmax(0, 1fr)
		calc(340px + var(--pfg-row-gutter));
	align-items: start;
	padding: var(--pfg-row-pad-y) var(--pfg-row-pad-x);
	min-width: 0;
}

/* ---- Cell reset ----
   Three things arrive uninvited once these boxes are real table cells: the UA
   gives every td/th 1px of padding, it centres and emboldens th, and the
   theme's `table td, table th` rule puts tabular figures on all of them. The
   last one is not cosmetic — firm names here contain digits, so tabular figures
   would change the width of the name.

   Elements that genuinely want tabular figures ask for them by class
   (.pfg-num, .pfg-metric__value, .pfg-years__n) or keep their own rule
   (.pfg-row__rank), and every one of those out-specifies the theme's element
   selector. This block stays ahead of the layout rules below so they can add
   padding back. */
.pfg-row__rank,
.pfg-row__main,
.pfg-metric,
.pfg-row__price,
.pfg-row__gradecell {
	padding: 0;
}

.pfg-row__main,
.pfg-metric,
.pfg-row__price,
.pfg-row__gradecell {
	font-variant-numeric: normal;
}

.pfg-row__rank {
	grid-column: 1;
	grid-row: 1 / 3;
	margin-right: var(--pfg-row-gutter);
}

/* The firm cell — the old `.pfg-row__main` wrapper, now the <th scope="row">.
   It still stacks the meta rail over the logo/name line. It spans the metric
   tracks and the spacer, so its width is the old main column exactly. */
.pfg-row__main {
	grid-column: 2 / 7;
	grid-row: 1;
	align-self: end;
	text-align: left;
	font-weight: inherit;
}

/* The metric cells auto-place along row 2. Auto-placement is what makes CSS
   `order` work — Customize sets it inline — and the rank already occupies
   column 1 of that row, so they start under the identity where the rail did.
   Reordering only ever moves a cell WITHIN its own row. */
.pfg-metric {
	grid-row: 2;
	align-self: start;
	margin-top: var(--pfg-row-stack);
	margin-right: clamp(18px, 2.2vw, 32px);
}

/* A metric with no datum renders an empty cell so the columns stay aligned
   down the table. It must not also leave the rail's gutter behind, or the rail
   grows a hole exactly where the old markup rendered nothing at all. */
.pfg-metric:empty {
	margin-right: 0;
}

/* The price column carries its call to action inside it, exactly as the old
   wrapper div did, so this cell is ONE box and `align-self: center` reproduces
   the flex centring directly — no reserved padding, no second cell to align
   against it, and no residual when a firm's price block is short. */
.pfg-row__price {
	grid-column: 7;
	grid-row: 1 / 3;
	align-self: center;
	margin-left: var(--pfg-row-gutter);
}

/* ---- Grade strip ---- */

/* The strip's own <tr> spans every column. The cell is a pass-through: the
   .pfg-gbar inside it is unchanged, so the standalone grade-row block on a firm
   page renders from the same GradeRow::render() output. */
.pfg-row__graderow {
	display: block;
}

.pfg-row__gradecell {
	display: block;
	padding: 0;
}

/* ---- Rank, with medal treatment for the podium ---- */

.pfg-row__rank {
	display: grid;
	place-items: center;
	width: 30px;
	height: 30px;
	flex: 0 0 30px;
	align-self: flex-start;
	border-radius: 999px;
	font-family: var(--wp--preset--font-family--display, "Outfit", sans-serif);
	font-weight: 600;
	font-size: clamp(13px, 0.75vw, 15.5px);
	font-variant-numeric: tabular-nums;
	background: var(--surface-chip, #262C3B);
	color: var(--text-3, #979CAD);
	border: 1px solid var(--border-2, rgba(255, 255, 255, 0.08));
}

.pfg-row__rank--1 {
	background: rgba(240, 185, 11, 0.16);
	color: var(--warning, #F0B90B);
	border-color: rgba(240, 185, 11, 0.4);
}

.pfg-row__rank--2 {
	background: rgba(198, 203, 221, 0.14);
	color: var(--text-2, #C6CBDD);
	border-color: rgba(198, 203, 221, 0.34);
}

.pfg-row__rank--3 {
	background: rgba(205, 127, 50, 0.16);
	color: #E0995B;
	border-color: rgba(205, 127, 50, 0.4);
}

/* ---- Main column ---- */

/* The firm cell stacks the meta rail over the logo/name line. Its gap and the
   metric cells' margin-top are the same token, so the rail below can never
   drift away from the lines above it. */
.pfg-row__main {
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: var(--pfg-row-stack);
}

.pfg-row__meta {
	display: flex;
	align-items: center;
	gap: 9px;
	flex-wrap: wrap;
	font-size: clamp(12.5px, 0.72vw, 14.5px);
	color: var(--text-3, #979CAD);
}

.pfg-row__fav {
	display: grid;
	place-items: center;
	width: 28px;
	height: 28px;
	flex: 0 0 28px;
	padding: 0;
	border: 0;
	border-radius: 999px;
	background: rgba(255, 255, 255, 0.05);
	color: var(--text-4, #838A9D);
	transition: color 200ms ease, background 200ms ease;
}

/*
 * The logo's link to the review. It wraps the tile and must not change the
 * row's geometry: same flex item size as the bare tile, no underline, no
 * focus ring (it is out of the tab order; the name link carries focus).
 */
.pfg-row__tilelink {
	display: inline-flex;
	flex: 0 0 auto;
	line-height: 0;
	text-decoration: none;
	color: inherit;
	border-radius: inherit;
}

.pfg-row__id {
	display: flex;
	align-items: center;
	gap: 12px;
	flex-wrap: wrap;
	min-width: 0;
}

.pfg-row__name {
	font-family: var(--wp--preset--font-family--display, "Outfit", sans-serif);
	font-size: clamp(20px, 1.5vw, 29px);
	font-weight: 400;
	line-height: 1;
	color: var(--text-1, #F2F3F8);
	text-decoration: none;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	transition: color 150ms ease;
}

.pfg-row__name:hover,
.pfg-row__name:focus-visible {
	color: var(--acc-bright, #C1BDFF);
}

/* ---- Metric cells ----
   Each metric is its own column now, so there is no rail element left: the
   cells sit in row 2 of the row's grid and the gutter between them is a margin
   rather than a container's `gap`. */

/* A column the visitor switched off in Customize. The stylesheet hides the
   header cell on the same class, so the column disappears from the header row
   and the body rows together. With scripting off it never appears. */
.pfg-metric.is-off {
	display: none;
}

.pfg-metric {
	display: flex;
	flex-direction: column;
	gap: 5px;
	min-width: 0;
}

.pfg-metric__label {
	font-size: clamp(10.5px, 0.6vw, 12.5px);
	letter-spacing: var(--wp--custom--label--tracking, 0.08em);
	text-transform: uppercase;
	color: var(--text-4, #838A9D);
	white-space: nowrap;
}

.pfg-metric__value {
	font-size: clamp(13.5px, 0.78vw, 16px);
	font-weight: 600;
	color: var(--text-1, #F2F3F8);
	font-variant-numeric: tabular-nums;
}

/* The cap that used to live on the platform cell's TEXT now lives on the cell.
   It was a one-line ellipsis over a comma-joined string: at 200px a firm with
   seven platforms showed two and a half of them and the rest was a `title`
   attribute, which is not readable by touch and not readable by keyboard. The
   value is a wrapping row of badges now, so the cell bounds it and the badge
   list decides what fits — see FirmRow::badge_list().

   520px, NOT the 200px this replaced and not the 280px an early draft used.
   Five badges plus the "+N" chip on ONE line is what the design asks for, and
   measured across the catalogue that line wants 396-502px depending on the
   firm and the viewport (the pill font is a `clamp`, so the same list is
   narrower at 1280 than at 1920). The widest real case is Blue Guardian at
   1920: 502px.

   THE CAP IS A SAFETY RAIL, NOT THE LAYOUT. The metric tracks are `auto`, so a
   cell only ever takes what its own content needs — a firm with three
   platforms still measures 224px and the spacer keeps the rest. What the cap
   buys is a floor under the OTHER cells: the row's spacer is `minmax(0, 1fr)`
   and collapses first, and once it is gone an over-long platforms cell would
   start shrinking `assets` instead of itself. Bounding platforms means the
   squeeze lands on platforms, which wraps to a second line and is designed to.
   Verified: the assets cell measures 280px at every width, before and after.

   Where the line genuinely does not fit, it wraps rather than truncating. At
   1280 that is exactly one row in the catalogue — see the phone block and
   BADGES_VISIBLE for the numbers. */
.pfg-metric--platforms {
	flex: 0 1 auto;
	min-width: 0;
	max-width: 520px;
}

/* Content-sized like every other cell. It used to carry flex-grow so the
   chips could stretch into the rail's leftover space — safe only while
   Assets was always the LAST cell. Customize lets a visitor order it
   anywhere, and a growing cell ordered first swallowed half the rail and
   shoved the other columns to the far edge.

   340px, up from 280px, and the 60px is bought for ONE case. This cell is a
   badge list now, capped at three plus a chip — 267px, comfortably inside the
   old 280. But `badge_list()` shows a fourth value rather than a "+1" chip
   that would cost the same room and say less, and a firm with exactly four
   instrument types therefore renders four badges wanting 311px. Atmos Funded
   is that firm, and against a 280px cap its cell was the only one on
   /forex/ standing at two lines while every other row stood at one.

   Bounded, not removed, for the reason the platforms cell is bounded: the
   row's spacer collapses first, and past that an unbounded list cell shrinks
   its neighbour instead of itself. */
.pfg-metric--assets {
	flex: 0 1 auto;
	min-width: 0;
	max-width: 340px;
}

   A panel anchored `left: 0` grows to the RIGHT of its chip, which is correct
   for platforms — it sits second of four, with alloc and assets to its right.
   Assets is last, so its chip sits at the right-hand end of the metric run and
   a rightward panel ran straight out of the card. Measured on /forex/ at 768:
   four of the six panels crossed the card edge and all four crossed the
   VIEWPORT edge too, the widest by 137px. At 1100 E8 Markets' panel ended 44px
   past the card. This is new with assets adopting the component — platforms
   alone never sat far enough right to show it.

   Flipping the anchor puts the panel's right edge on the chip's right edge, so
   it grows back across the cell it belongs to. That is safe wherever the cell
   is, because only a cell with a chip has a panel and such a cell is at least
   244px wide — wider than the panel's 240px cap — so a panel can never reach
   past the far side of its own cell, and the cell is inside the card by
   construction. It therefore survives Customize reordering the column. */
.pfg-metric--assets .pfg-badges__panel {
	left: auto;
	right: 0;
}


/* Max-alloc level bars: ascending columns, filled by absolute size tier —
   the signal-strength idiom. Reads as a size CLASS; the old track-and-fill
   under-bar read as progress toward a goal that does not exist. */
.pfg-metric__row {
	display: inline-flex;
	align-items: center;
	gap: 7px;
}

.pfg-metric__steps {
	display: inline-flex;
	align-items: flex-end;
	gap: 2px;
	height: 13px;
}

.pfg-metric__steps > span {
	width: 3px;
	border-radius: 1.5px;
	background: rgba(255, 255, 255, 0.12);
}

.pfg-metric__steps > span:nth-child(1) { height: 4px; }
.pfg-metric__steps > span:nth-child(2) { height: 7px; }
.pfg-metric__steps > span:nth-child(3) { height: 10px; }
.pfg-metric__steps > span:nth-child(4) { height: 13px; }

.pfg-metric__steps > .is-on {
	background: var(--acc, #A79FFF);
}

/* ---- Price column ---- */

.pfg-row__price {
	display: flex;
	flex-direction: column;
	gap: var(--pfg-price-stack);
	align-items: flex-end;
	max-width: 100%;
}

.pfg-row__pricelabel {
	font-size: clamp(12.5px, 0.72vw, 14.5px);
	color: var(--text-3, #979CAD);
}

.pfg-row__pricerow {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	flex-wrap: wrap;
	justify-content: flex-end;
}

.pfg-row__pay {
	font-family: var(--wp--preset--font-family--display, "Outfit", sans-serif);
	font-weight: 500;
	font-size: clamp(26px, 2.2vw, 32px);
	line-height: 1;
	color: var(--acc, #A79FFF);
}

.pfg-row__was {
	font-size: clamp(15px, 0.87vw, 18px);
	color: var(--wp--preset--color--text-label, #828BA6);
}

.pfg-row__off {
	display: inline-flex;
	align-items: center;
	height: 26px;
	padding: 0 9px;
	border-radius: 8px;
	background: rgba(var(--acc-rgb, 167, 159, 255), 0.14);
	color: var(--acc, #A79FFF);
	font-size: clamp(13px, 0.75vw, 15.5px);
	font-weight: 800;
	white-space: nowrap;
}

.pfg-row__pricenote {
	font-size: clamp(11px, 0.63vw, 12.5px);
	color: var(--text-4, #838A9D);
}

/* ---- Grade row ---- */

.pfg-gbar {
	display: flex;
	align-items: center;
	gap: clamp(11px, 1vw, 18px);
	flex-wrap: wrap;
	padding: 8px 20px 9px;
	min-width: 0;
	border-top: 1px solid var(--border-1, rgba(255, 255, 255, 0.06));
	background: rgba(0, 0, 0, 0.14);
	border-radius: 0 0 17px 17px;
}

/* The ratified grade-status word beside the mark ("Preliminary") — muted UI
   text, never accent. Rendered by GradeMark::status_html(). */
.pfg-gbar__status {
	font-style: normal;
	font-size: 10.5px;
	font-weight: 700;
	letter-spacing: 0.04em;
	line-height: 1;
	color: var(--text-3, #979CAD);
	white-space: nowrap;
}

.pfg-gbar__rule {
	width: 1px;
	height: 19px;
	flex: 0 0 1px;
	background: var(--border-2, rgba(255, 255, 255, 0.08));
}

.pfg-gev {
	display: flex;
	align-items: center;
	gap: clamp(10px, 1.2vw, 20px);
	flex-wrap: wrap;
	flex: 1 1 auto;
	min-width: 0;
}

.pfg-gitem {
	display: flex;
	align-items: center;
	gap: 7px;
	font-size: clamp(11.5px, 0.66vw, 13px);
	line-height: 1.42;
	white-space: nowrap;
	color: var(--text-3, #979CAD);
}

.pfg-gitem b {
	font-weight: 800;
	color: var(--text-1, #F2F3F8);
}

.pfg-gbar__cta {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	min-height: 36px;
	margin-left: auto;
	padding: 0 2px;
	color: var(--acc-bright, #C1BDFF);
	font-size: clamp(12.5px, 0.72vw, 14px);
	font-weight: 800;
	text-decoration: none;
	flex: 0 0 auto;
	white-space: nowrap;
}

.pfg-gbar__cta:hover {
	text-decoration: underline;
}

/* ---- Empty state ---- */

.pfg-toplist__empty {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 10px;
	padding: 44px 20px;
	text-align: center;
	color: var(--text-3, #979CAD);
}

.pfg-toplist__clear {
	display: inline-flex;
	align-items: center;
	height: 38px;
	padding: 0 16px;
	border-radius: 999px;
	border: 1px solid var(--acc-border, rgba(167, 159, 255, 0.4));
	color: var(--acc, #A79FFF);
	font-weight: 700;
	text-decoration: none;
}

/* ================= Tablet ================= */

@media (max-width: 1100px) {
	.pfg-row__top {
		--pfg-row-gutter: 16px;
		--pfg-price-stack: 12px;
		--pfg-row-pad-y: 16px;
		--pfg-row-pad-x: 16px;

		/* The price track is gone: the price column becomes a full-width footer
		   row under the identity and the metric cells. */
		grid-template-columns:
			calc(var(--pfg-rank-w) + var(--pfg-row-gutter))
			repeat(4, auto)
			minmax(0, 1fr);
		padding: var(--pfg-row-pad-y) var(--pfg-row-pad-x);
	}

	.pfg-row__main {
		grid-column: 2 / -1;
	}

	/* The 340px price column stops earning its width once the main column is
	   under ~360px; below that it becomes a full-width footer with a hairline.

	   The coupon is INSIDE this cell, so it is still the last item of the same
	   wrapping flex line — it shares the line with the price and its note where
	   there is room and drops below where there is not, exactly as before. That
	   is the whole reason price and offer share a column. */
	.pfg-row__price {
		grid-column: 1 / -1;
		grid-row: 3;
		align-self: auto;
		flex-direction: row;
		flex-wrap: wrap;
		align-items: center;
		justify-content: space-between;
		gap: var(--pfg-price-stack);
		margin-left: 0;
		margin-top: var(--pfg-row-gutter);
		padding-top: 12px;
		border-top: 1px solid var(--border-1, rgba(255, 255, 255, 0.06));
	}

	.pfg-row__pricelabel {
		order: -1;
		flex-basis: 100%;
	}

	/* THE CAP COMES BACK DOWN HERE, and it is protecting the ASSETS cell rather
	   than this one.

	   The desktop cap is 520px so five badges and the chip get one line. Below
	   1100 the row has less width to give: measured at 768 the metric tracks
	   share 623px, and a platforms cell taking its full 396px demand leaves
	   208px where `assets` wants 280. The grid does not know which of two `auto`
	   tracks matters more, so it shrinks BOTH — assets came out at 244px, a
	   column this change has no business touching.

	   So the cap tracks the viewport instead of being one number, because one
	   number cannot serve both ends of this range: 280px protects assets at
	   1100 but not at 768, and the 208px that protects it at 768 costs a third
	   badge line everywhere above.

	   `100vw - 580px` is the row's own arithmetic — page gutter, card padding,
	   rank, alloc, and the 280px assets wants — with 20px of slack. It holds
	   one line down to about 1000px and yields below that. The slack is there
	   because `100vw` counts the scrollbar and the content box does not; if it
	   is ever wrong it is wrong by a scrollbar's width, in the direction of
	   giving assets slightly less, which is the same worst case a fixed 280px
	   has at 768 anyway. */
	.pfg-metric--platforms {
		max-width: min(520px, calc(100vw - 580px));
	}
}

/* ================= Phone ================= */

@media (max-width: 760px) {
	/* NOTHING INSIDE THE CARD SCROLLS SIDEWAYS HERE.

	   What this replaces: the four metrics used to sit on one horizontally
	   scrolling rail. The row itself was the scroll container, every non-metric
	   cell was `position: sticky` so the rail slid underneath it, and a pinned
	   gradient in the rank's grid area faded the tail. It was a working
	   mechanism and still the wrong shape — at 390px a populated row overflowed
	   by up to 131px, so PLATFORMS, MAX ALLOC and ASSETS were cut off at the
	   card's right edge behind a swipe, inside a page that already scrolls
	   vertically. Meta a reader compares firms on cannot live behind a gesture.

	   So the metrics WRAP: two equal columns, auto-placed, every value on
	   screen. The width that makes them fit comes from the rank, which no
	   longer holds a column open. It is absolutely positioned into the card's
	   top-left corner — where a rank badge belongs on a card anyway — which
	   hands its 44px (the 32px disc plus the 12px gutter) back to the logo, the
	   firm name and the meta on every line of the card.

	   `container-type: inline-size` went with the rail. It existed so the pinned
	   cells could be sized in `cqw` against the card while their grid areas were
	   as wide as the overflow; no cell is pinned now and no rule reads `cqw`. */
	.pfg-row__top {
		--pfg-row-gutter: 12px;
		--pfg-row-pad-y: 14px;
		--pfg-row-pad-x: 14px;
		/* The rank's inset from the card's corner. Equal to the row padding, so
		   the badge lines up with the first line of everything below it. */
		--pfg-rank-inset: var(--pfg-row-pad-x);

		/* Containing block for the rank, which is now out of flow. */
		position: relative;

		/* Two equal columns for the metrics to wrap through. The identity and
		   the price both span the pair, so this is only ever the metric grid.
		   `minmax(0, 1fr)` rather than `1fr`: a long platform list has a
		   min-content contribution wider than half the card, and a bare `1fr`
		   floors at min-content and overflows again — the same bug in a new
		   shape. Bounded tracks make the value wrap instead. */
		grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: var(--pfg-row-gutter);
		padding: var(--pfg-row-pad-y) var(--pfg-row-pad-x);
	}

	/* The rank badge, pinned to the corner. `grid-column`/`grid-row` are reset
	   to `auto` so the offsets resolve against the row's padding box instead of
	   against the grid area the wider templates place it in. Out of flow, it
	   reserves no track — which is the entire point of moving it. */
	.pfg-row__rank {
		position: absolute;
		grid-column: auto;
		grid-row: auto;
		top: var(--pfg-rank-inset);
		left: var(--pfg-rank-inset);
		margin-right: 0;
	}

	.pfg-row__main {
		grid-column: 1 / -1;
		grid-row: 1;
		min-width: 0;
	}

	/* The meta line is the one line that still has to clear the badge; every
	   line under it starts at the card's own padding, which is the room the rank
	   gave back.

	   `min-height` is not decoration. In production the favourite control is not
	   rendered, and a firm with no HQ country and no challenge models leaves this
	   line empty — the badge would then hang over the logo. Reserving the disc's
	   own height keeps the corner honest whatever the row carries, and it is
	   inert the moment there is a single chip on the line. */
	.pfg-row__meta {
		flex-wrap: wrap;
		padding-left: calc(var(--pfg-rank-w) + 10px);
		min-height: 30px;
	}

	.pfg-row__meta > * {
		flex: 0 0 auto;
	}

	/* Decision-critical meta leads. */
	.pfg-chip--step { order: -2; }
	.pfg-row__fav   { order: -1; }

	/* Auto-placed across the two columns rather than pinned to one rail row.
	   The right margin WAS the rail's gutter; `column-gap` is that now, and
	   keeping both would push the second column off the card. */
	.pfg-metric {
		grid-row: auto;
		margin-right: 0;
	}

	/* An empty metric holds its column open on the wider templates so the
	   table's columns line up down the page. There is no column to line up with
	   on a phone — each card is read on its own — so an empty cell would only
	   be a hole in the pair. */
	.pfg-metric:empty {
		display: none;
	}

	/* `grid-row: auto`, not the tablet template's hard `3`: the metrics now
	   occupy rows 2 AND 3, so a pinned row 3 would land the price on top of the
	   second pair. Auto-placement puts it on the first row that fits a two-column
	   span, which is whatever row the metrics stopped on. */
	.pfg-row__price {
		grid-column: 1 / -1;
		grid-row: auto;
		flex-direction: column;
		align-items: stretch;
		padding-top: 12px;
	}

	.pfg-row__name {
		font-size: 19px;
	}

	/* The desktop 200px cap comes off: the metric grid's `minmax(0, 1fr)` track
	   is the bound here, and it is 128px at 375px wide.

	   THE RULE THIS REPLACES WAS RIGHT AND IS NOT BEING REVERSED. It un-did the
	   desktop one-line ellipsis so a firm's platforms could not end up cut off
	   with no way to read the rest — meta a reader compares firms on cannot live
	   behind a gesture. What it cost was height: measured at 375px, the wrapped
	   comma string ran to 183px for a firm with seven platforms and the row to
	   478px, and the catalogue's deepest firm carries fourteen. The badge list
	   keeps the rule's intent and drops that cost, because a "+N" chip is an
	   affordance where an ellipsis was not: the rest is one tap away and the
	   names are announced with the badges either way. */
	.pfg-metric--platforms {
		max-width: none;
	}

	/* A BADGE CELL WITH MORE THAN ONE BADGE TAKES THE WHOLE CARD WIDTH.

	   The other metrics are a number and a year count; they pair two-up happily
	   in half a card. These are LISTS, and at 128px — half of a 375px card — an
	   85px pill fills a line on its own. So TWO badges already stack into a
	   column of one-pill lines, which is the shape this rule exists to prevent.
	   Spanning gives the cell 267px, where three pills fit a line.

	   TWO, NOT FOUR. Owner's call, 2026-08-27: "tidier is better". Spanning
	   costs a grid row — the metric that used to sit beside the cell has to go
	   somewhere — so a higher threshold is very slightly shorter overall.
	   Measured across ten cards at 375, with the loaded selector asserted each
	   time rather than inferred:

	       threshold  max card   ten-card total   cells still stacking
	       >= 4          614          5490                 5
	       >= 3          614          5531 (+41)           3
	       >= 2          648          5632 (+142)          0

	   The owner took the +2.6% to get the zero. Height was the thing they were
	   looking at and they still chose composition, so do not "optimise" this
	   back up without asking them.

	   A single badge still does not span: one badge is one child, the rule needs
	   two, and a lone pill already fits its half-column — spanning it would buy
	   a wasted grid row. Without `:has()` support nothing spans and every cell
	   behaves as it did before: more lines, never broken.

	   ANY BADGE CELL, not just platforms. Assets is a badge list too, and it
	   failed identically for the identical reason. Keying the rule on
	   `.pfg-metric` rather than on one column's class means the next cell to
	   adopt `badge_list()` inherits the fix instead of rediscovering it. */
	.pfg-metric:has(.pfg-badges > :nth-child(2)) {
		grid-column: 1 / -1;
	}

	/* THE PANEL ANCHORS TO THE CELL HERE, NOT TO THE CHIP.

	   An earlier version made it a full-width line INSIDE `.pfg-badges__more`,
	   which meant the wrapper had to be `flex: 1 0 100%` to give that line its
	   width. That worked, and it cost a whole line PER CELL: the "+N" chip was
	   pushed onto a row of its own even where it would have sat beside the last
	   badge with room to spare. Measured at 375, E8 Markets' assets cell needed
	   262px for three badges and the chip in a 267px cell — one line — and was
	   rendering two.

	   The reason the panel could not float before was that a chip in the metric
	   grid's second column would open a 240px panel off the side of the phone.
	   That reason is gone: a cell long enough to carry a chip now SPANS both
	   columns (see the rule above), so the cell is the full card width and a
	   panel that fills the cell cannot leave it. So the cell becomes the
	   containing block and the panel fills it.

	   DOWNWARD here, unlike the desktop flyout. The badges sit directly under
	   the firm name on a phone, and an upward panel would cover the one thing
	   identifying the card. `visibility` again rather than `display`, because
	   out of flow it holds no height while closed. */
	.pfg-metric:has(.pfg-badges) {
		position: relative;
	}

	.pfg-badges__more {
		position: static;
	}

	/* 4px, one less than everywhere else, and it is not taste — it is a
	   rounding buffer.

	   Measured at 375: three asset badges plus the "+N" chip need 267.1px in a
	   267.0px cell. Short by a TENTH OF A PIXEL, so the cell wrapped to two
	   lines to hide one chip. A miss that small is not stable either — it is
	   inside the noise of font rasterisation, so the same cell could fit on one
	   machine and wrap on the next, which is worse than either outcome.

	   Three gaps at 1px less buys 3px and settles it deterministically. Applied
	   on phones only, where the cell width is fixed by the card rather than by
	   the row's spacer and there is nowhere else for the 3px to come from. */
	.pfg-badges {
		gap: 4px;
	}

	/* Two classes deep to match `.pfg-metric--assets .pfg-badges__panel`, which
	   right-anchors the last column on desktop. Equal specificity, and this
	   block is further down the file, so the phone rule wins on order. A bare
	   `.pfg-badges__panel` here would lose and the assets panel would stay
	   right-anchored with `left: auto`, which is wrong once it fills the cell. */
	.pfg-metric .pfg-badges__panel {
		top: calc(100% + var(--pfg-badges-gap));
		bottom: auto;
		left: 0;
		right: auto;
		width: 100%;
		max-width: none;
	}

	/* The bridge flips with the panel: the gap to cross is now ABOVE it. */
	.pfg-badges__panel::after {
		top: auto;
		bottom: 100%;
	}

	/* The label is redundant once the price is the only thing on its own row. */
	.pfg-row__pricelabel {
		display: none;
	}

	.pfg-row__pricerow {
		justify-content: flex-start;
	}

	/* Both coupon states go full width here: at this size the CTA is the row's
	   primary action and a shrink-wrapped button reads as an afterthought. On
	   desktop the no-code CTA keeps its intrinsic width — the price column
	   right-aligns it, so there is no gap to fill. */
	.pfg-coupon,
	.pfg-coupon__cta--solo {
		width: 100%;
	}

	.pfg-gbar {
		padding: 10px 14px 11px;
	}

	.pfg-gbar__rule {
		display: none;
	}

	.pfg-gev {
		order: 3;
		flex: 1 1 100%;
		flex-wrap: nowrap;
		overflow-x: auto;
		scrollbar-width: none;
	}

	.pfg-gev::-webkit-scrollbar {
		display: none;
	}

	.pfg-gbar__cta {
		order: 2;
		width: 100%;
		margin-left: 0;
		justify-content: space-between;
		font-size: 12.5px;
	}
}

/* ================= Compact density =================
   Driven by the header's view toggle through ?pfg_view=compact. The toggle
   changes a real layout: the metric rail and the evidence rail are the two
   things a scanner skips when comparing many firms quickly, so those are what
   compact drops. A toggle that changed nothing would be worse than no toggle. */

.pfg-toplist[data-density="compact"] .pfg-row__top {
	--pfg-row-gutter: 14px;
	--pfg-row-pad-y: 10px;
	--pfg-row-pad-x: 16px;

	padding: var(--pfg-row-pad-y) var(--pfg-row-pad-x);
}

.pfg-toplist[data-density="compact"] .pfg-row {
	border-radius: 14px;
}

/* With the rail gone the identity has nothing to stay glued to, so it stops
   being the top half of a pair and becomes a single box that centres on its
   own. This is not just tidier — the `align-self: end` half of the pair trick
   needs the rail row to carry content. With row 2 empty, the surplus from the
   spanning price cell lands entirely in the empty row instead of being shared,
   and the identity sits at the top of the card instead of centred. */
.pfg-toplist[data-density="compact"] .pfg-row__main {
	grid-row: 1 / 3;
	align-self: center;
}

/* Compact drops the metrics COLUMN, so its <th> has to go with its <td>. A
   hidden cell whose header is still counted desynchronises the row from the
   header row, and every value after it is announced against the wrong column.
   (Customize's own `is-off` needs no such pairing — it hides pairs INSIDE the
   cell, and a pair is not a column.)

   Because the identity-to-rail gap is a margin on the metrics cell rather than
   a `row-gap`, the emptied row costs nothing and the card collapses to exactly
   the height it had before. */
.pfg-toplist[data-density="compact"] .pfg-metric,
.pfg-toplist[data-density="compact"] .pfg-toplist__col--metric,
.pfg-toplist[data-density="compact"] .pfg-gev,
.pfg-toplist[data-density="compact"] .pfg-gbar__rule,
.pfg-toplist[data-density="compact"] .pfg-row__pricenote {
	display: none;
}

.pfg-toplist[data-density="compact"] .pfg-row__main {
	gap: 7px;
}

.pfg-toplist[data-density="compact"] .pfg-row__name {
	font-size: clamp(17px, 1.1vw, 21px);
}

.pfg-toplist[data-density="compact"] .pfg-row__pay {
	font-size: clamp(20px, 1.5vw, 25px);
}

.pfg-toplist[data-density="compact"] .pfg-gbar {
	padding: 6px 16px 7px;
}

.pfg-toplist[data-density="compact"] .pfg-coupon {
	--pfg-coupon-h: 38px;
}

/* ---- Compact on desktop: ONE LINE per firm ----
   Above the tablet breakpoint the price track exists, so the whole row can
   sit on a single baseline: rank, logo, name and its chips on the left; the
   price, the strike-through and the saving beside the coupon on the right;
   the grade strip underneath thinned to a ribbon. Nothing is removed that
   comfortable shows — the "Price after code" label is kept for screen
   readers, where it still disambiguates the number — it is only laid out
   horizontally. Measured: 185px -> ~88px per row (owner, 2026-08-20). */
@media (min-width: 1101px) {
	.pfg-toplist[data-density="compact"] .pfg-row__top {
		--pfg-row-pad-y: 8px;
		align-items: center;
		/* The price track widens: it now holds the number AND the coupon
		   side by side, where comfortable stacks them. */
		grid-template-columns:
			calc(var(--pfg-rank-w) + var(--pfg-row-gutter))
			repeat(4, auto)
			minmax(0, 1fr)
			calc(460px + var(--pfg-row-gutter));
	}

	.pfg-toplist[data-density="compact"] .pfg-row__rank {
		align-self: center;
	}

	/* Identity: name first, chips trailing on the same line. */
	.pfg-toplist[data-density="compact"] .pfg-row__main {
		flex-direction: row;
		align-items: center;
		flex-wrap: wrap;
		gap: 6px 14px;
	}

	.pfg-toplist[data-density="compact"] .pfg-row__id {
		order: 1;
		flex-wrap: nowrap;
		gap: 10px;
	}

	.pfg-toplist[data-density="compact"] .pfg-row__meta {
		order: 2;
		gap: 6px;
	}

	.pfg-toplist[data-density="compact"] .pfg-row__name {
		font-size: clamp(16px, 1vw, 19px);
	}

	/* Price cell: number, was, saving, then the coupon — one line. */
	.pfg-toplist[data-density="compact"] .pfg-row__price {
		flex-direction: row;
		align-items: center;
		justify-content: flex-end;
		gap: 14px;
	}

	.pfg-toplist[data-density="compact"] .pfg-row__pricelabel {
		position: absolute !important;
		width: 1px;
		height: 1px;
		padding: 0;
		margin: -1px;
		overflow: hidden;
		clip: rect(0, 0, 0, 0);
		clip-path: inset(50%);
		white-space: nowrap;
		border: 0;
	}

	.pfg-toplist[data-density="compact"] .pfg-row__pricerow {
		flex-wrap: nowrap;
		gap: 8px;
	}

	.pfg-toplist[data-density="compact"] .pfg-row__pay {
		font-size: clamp(18px, 1.25vw, 22px);
	}

	.pfg-toplist[data-density="compact"] .pfg-row__was {
		font-size: clamp(13px, 0.8vw, 15px);
	}

	/* The coupon sizes to its code and button rather than to a share of the
	   cell: `min(380px, 100%)` against a cell it now splits with the price
	   shrank it to the button alone and ellipsised the code to nothing. */
	.pfg-toplist[data-density="compact"] .pfg-coupon {
		--pfg-coupon-h: 34px;
		width: max-content;
		max-width: 100%;
		flex: 0 0 auto;
	}

	/* The grade strip as a ribbon: the evidence items are already gone in
	   compact, so what remains is the wordmark, the grade and the review
	   link, which need no more than a 28px line. */
	.pfg-toplist[data-density="compact"] .pfg-gbar {
		padding: 3px 16px 4px;
		gap: 10px;
	}

	.pfg-toplist[data-density="compact"] .pfg-gbar .pfg-gring {
		--pfg-gring: 26px;
	}

	.pfg-toplist[data-density="compact"] .pfg-gbar__cta {
		min-height: 28px;
	}
}

/* Show more — a link that goes to a real, fully server-rendered longer list.
   Styled as a wide affordance rather than a small text link because it is the
   primary way deeper rows are reached, and it must be obviously actionable
   with no script running.

   NEUTRAL, NOT ACCENT (2026-08-26). This was an accent outline: a 1px
   var(--acc) ring, accent label, an 8% accent wash, and a hover that filled it
   solid accent. On /futures/ that put a #E6FF00 pill ~75px above "Browse all
   reviewed firms" — the link that actually leaves the page — which was a dull
   2px grey outline. The emphasis was exactly inverted: the loudest control on
   the screen expanded a list in place, and the quietest one navigated.

   The design agrees and always did. Its own view-more (design HTML line 1891)
   is `background:#1D2230; border:1px solid var(--border-3); color:var(--text-1)`
   with a trailing chevron and a hover that only strengthens the hairline —
   zero accent, and DESIGN-INVENTORY.md line 1427 files it on the same raised
   neutral surface as "Read full review". The inventory's list of surfaces that
   may carry an accent fill (switcher pill, coupon and affiliate CTAs, applied
   filter chips, grade-tooltip rung, consent Accept) does not include it. The
   accent-outline treatment in the design is reserved for state resets —
   "Clear filters", "Show all verticals" — which is a different job again.

   So: a raised neutral pill, one hairline, hover brightens the hairline and
   nothing else. It is a control, not a destination. */
.pfg-toplist__more {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 8px;
	margin-top: 18px;
}

/* --wp--preset--color--surface-tile is #1E2330. The design's literal here is
   #1D2230, which differs by one step in two channels and has no token of its
   own; DESIGN-INVENTORY.md line 1427 asks for a `--surface-raised` to hold it.
   Minting a token a hair away from one that already exists would be noise, so
   this reads the existing raised surface and the delta is recorded rather than
   pretended away. */
.pfg-toplist__morelink {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 9px;
	min-height: 46px;
	padding: 0 26px;
	border: 1px solid var(--border-3, rgba(255, 255, 255, 0.10));
	border-radius: 999px;
	color: var(--wp--preset--color--text-1, #F2F3F8);
	font-size: clamp(13.5px, 0.78vw, 16px);
	font-weight: 700;
	letter-spacing: 0.01em;
	text-decoration: none;
	background: var(--wp--preset--color--surface-tile, #1E2330);
	transition: border-color 0.15s ease;
}

/* Hover is a hairline brightening, and only that. No lift, no scale, no
   shadow, no fill change — the design's own hover on this control moves the
   border and nothing else. */
.pfg-toplist__morelink:hover,
.pfg-toplist__morelink:focus-visible {
	border-color: var(--border-strong, rgba(255, 255, 255, 0.14));
}

/* The chevron the design carries, drawn rather than declared so it inherits
   currentColor and cannot drift from the label. Decorative: the link's own
   text already says what it does. */
.pfg-toplist__morelink .pfg-toplist__morechev {
	flex: 0 0 auto;
}

/* The theme's global :focus-visible squares every focused control to
   `border-radius: 4px` — a theme-wide defect logged on PR #61 and NOT fixed
   here. This link is a solid pill now, so the squaring would be more visible
   than it was against the old 8% wash. Radius restated locally so the change
   does not make the existing defect worse; the ring is left alone. */
.pfg-toplist__morelink:focus-visible {
	border-radius: 999px;
}

.pfg-toplist__morenote {
	color: var(--text-4, #838A9D);
	font-size: 13px;
}

/* ---- Years-in-operation (identity.operatingSince, contract 1.3.0).
   The Google-Calendar idiom, minimal (owner decision 2026-08-03): the number
   IS the icon, centred on a compact rounded tile whose one calendar
   signifier is the thin lighter header strip — drawn by the tile's own
   background gradient, no SVG at all. Neutral gray, deliberately NOT the
   accent (a quiet fact, not a signal) and not a ring (rings mean grades). ---- */

.pfg-metric--years .pfg-years {
	display: inline-grid;
	place-items: center;
	width: 24px;
	height: 24px;
	border-radius: 7px;
	background:
		linear-gradient(to bottom, rgba(242, 243, 248, 0.22) 0 26%, transparent 26%),
		var(--surface-chip, #262C3B);
}

.pfg-years__n {
	padding-top: 5px;
	line-height: 1;
	font-size: 11.5px;
	font-weight: 800;
	font-variant-numeric: tabular-nums;
	color: var(--text-1, #F2F3F8);
}

/* HQ-country chip (identity.hqCountry). */
.pfg-chip--country {
	letter-spacing: 0.06em;
}

/* ================= Loading skeleton =================

   Placeholder rows shown while a sort or filter navigation is in flight. The
   markup is built by list-nav.js at the moment of the click and never exists
   in a server response: with scripting off there is no router navigation and
   no `state.navigating`, so a filter is a plain link that loads a new page and
   nothing below this line is ever reached. That is the whole no-JS contract —
   rendering any of this server-side would hand a scriptless visitor a screen
   of grey blocks instead of the rows they asked for.

   IT REUSES THE ROW'S OWN CLASSES. A skeleton row is `.pfg-row` >
   `.pfg-row__top` > `.pfg-row__rank`/`.pfg-row__main`/`.pfg-metric`/
   `.pfg-row__price`, on divs rather than table boxes, so every grid track,
   padding, breakpoint reflow and compact-density rule above applies to it
   unchanged. A second set of geometry declarations here is a second set to
   keep in sync, and it would be out of sync by the next layout change.

   ITS HEIGHT IS THE REAL ROW'S HEIGHT. --pfg-skel-h is measured off the row
   each placeholder stands in for, so the swap cannot resize the page whatever
   the rows contain, at any viewport, in either density. The grade strip is
   pushed to the bottom of that box and the surplus lands inside the card,
   where it reads as padding. */

.pfg-skel {
	display: flex;
	flex-direction: column;
}

/* The real rows give up their place while a skeleton stands in for them.
   Keyed on the skeleton's presence rather than on `.is-loading`, so the table
   is hidden exactly when something is there to replace it — an empty list has
   no skeleton and keeps its empty state.

   `.pfg-skel--more` is excluded because it stands in for nothing. It is the
   "Show more" placeholder, appended AFTER the table in the space the new rows
   are about to fill, and the rows already on screen are not going anywhere. */
.pfg-toplist:has(.pfg-skel:not(.pfg-skel--more)) .pfg-toplist__rows {
	display: none;
}

/* --pfg-skel-h is a BORDER box, because it was measured as one — a row's
   getBoundingClientRect() includes the card's 1px border. Left as content-box
   the skeleton stood 2px taller per row, which measured as a 12px page shift
   on a six-row list: small enough to look like nothing and large enough to be
   exactly the thing this was built to avoid. */
.pfg-skel__row {
	box-sizing: border-box;
	height: var(--pfg-skel-h);
	overflow: hidden;
}

/* A bar is slimmer than the line box the text it replaces occupies, so the
   placeholders come up short of the pinned height by a dozen-odd pixels. The
   surplus goes to the row's grid, never to a gap above the grade strip: the
   grid's two auto rows share it, and because the identity is `align-self: end`
   and the metrics `align-self: start` the pair stays glued in the middle while
   the spanning price cell stays centred — the same distribution the real row
   makes. Growing, but never shrinking: a placeholder that overran the pin
   would be clipped rather than squashed out of shape. */
.pfg-skel__row .pfg-row__top {
	flex-grow: 1;
	flex-shrink: 0;
}

/* The rank keeps its 30px disc — it is the one placeholder that is already the
   right shape — and only trades its chip fill for the recess every other bar
   uses, so it reads as empty rather than as a rank of nothing. */
.pfg-skel__rank {
	background: var(--recess, rgba(13, 15, 22, 0.6));
	border-color: var(--border-1, rgba(255, 255, 255, 0.06));
}

.pfg-skel__bar {
	display: block;
	flex: 0 0 auto;
	border-radius: var(--wp--custom--radius--xs, 7px);
	background: var(--recess, rgba(13, 15, 22, 0.6));
}

.pfg-skel__bar--chip {
	width: 44px;
	height: 24px;
	border-radius: var(--wp--custom--radius--pill, 999px);
}

.pfg-skel__bar--tile {
	width: 34px;
	height: 34px;
	border-radius: var(--wp--custom--radius--sm, 11px);
}

.pfg-skel__bar--name {
	width: 190px;
	height: 22px;
}

.pfg-skel__bar--label {
	height: 12px;
}

.pfg-skel__bar--value {
	height: 18px;
}

.pfg-skel__bar--pricelabel {
	width: 92px;
	height: 13px;
}

.pfg-skel__bar--pay {
	width: 130px;
	height: 30px;
}

.pfg-skel__bar--pricenote {
	width: 120px;
	height: 12px;
}

.pfg-skel__bar--cta {
	width: 168px;
	height: 46px;
	border-radius: var(--wp--custom--radius--pill, 999px);
}

/* The grade mark is a ring, so its placeholder is a disc. */
.pfg-skel__bar--grade {
	width: 34px;
	height: 34px;
	border-radius: var(--wp--custom--radius--pill, 999px);
}

.pfg-skel__bar--gitem {
	width: 96px;
	height: 13px;
}

.pfg-skel__bar--gcta {
	width: 150px;
	height: 22px;
	margin-left: auto;
}

.pfg-skel__bar--wide {
	width: 130px;
}

/* The sweep is DECLARED ONLY under `no-preference`, not disabled under
   `reduce`. This codebase has already found that a blanket
   `@media (prefers-reduced-motion: reduce) * { animation: none }` does not
   reach every animated box — pseudo-elements are missed by it — so an
   opt-in is the only form that cannot be silently escaped: under `reduce`
   there is no animation to have to switch off. Same shape as the filter bar's
   flame and the grade mark's draw-in. */
@keyframes pfgSkelSweep {
	from {
		background-position: -60% 0;
	}

	to {
		background-position: 160% 0;
	}
}

@media (prefers-reduced-motion: no-preference) {
	.pfg-skel__bar,
	.pfg-skel__rank {
		background-image: linear-gradient(
			90deg,
			transparent 0%,
			var(--surface-chip, #262C3B) 50%,
			transparent 100%
		);
		background-repeat: no-repeat;
		background-size: 60% 100%;
		animation: pfgSkelSweep 1.4s linear infinite;
	}
}

/* ================= Hand-picked mode =================
   `mode: "selected"` (render.php). Both differences are opt-in by attribute,
   so a ranked list matches none of this. */

/* The optional intro above the rows. Absent by default: no sentence is
   invented when the writer has not written one. Same measure and colour as the
   similar-operators intro, so the two read as one family. */
.pfg-toplist__intro {
	margin: 0 0 16px;
	max-width: 68ch;
	color: var(--wp--preset--color--text-2, var(--text-2, #C6CBDD));
}

/* RANK NUMBERS OFF. The rank cell and its <th> are not rendered at all, so the
   header and body rows still agree. What the grid loses is the item that held
   column 1 open across both rows: without it the first metric auto-places into
   that column and the rail shifts a track left. An empty pseudo-element takes
   the rank's place, and the first track collapses to nothing by cancelling the
   gutter it carries. Nothing is drawn. */
.pfg-toplist[data-rank="off"] .pfg-row__top {
	--pfg-rank-w: calc(-1 * var(--pfg-row-gutter));
}

.pfg-toplist[data-rank="off"] .pfg-row__top::before {
	content: "";
	grid-column: 1;
	grid-row: 1 / 3;
}

@media (max-width: 760px) {
	/* On a phone the rank sat out of flow in the card's corner and the meta
	   line was indented to clear it. With no rank there is nothing to clear and
	   no column to hold open. */
	.pfg-toplist[data-rank="off"] .pfg-row__top::before {
		content: none;
	}

	.pfg-toplist[data-rank="off"] .pfg-row__meta {
		padding-left: 0;
		min-height: 0;
	}
}
