.page-content {
    margin: 0 auto;
}

.exp-container{
    margin: var(--space-40) 0px;
    display: grid;
    grid-template-columns: 280px minmax(0, 1fr) minmax(0, 1fr);
    /* Row 1: logo (col 1, spans rows 1-2) | name/meta | progression
       Row 2: logo (continued)             | pricing   | search

       Logo fills column 1 across both rows; pricing sits directly
       under name/meta in col 2; search keeps its (2,3) corner under
       progression. */
    grid-template-rows: auto auto;
    grid-template-areas:
        "logo  name     progression"
        "logo  pricing  search";
    column-gap: var(--space-16);
    row-gap: var(--space-8);
    align-items: start;
}

a.exp-page-logo  { grid-area: 1 / 1 / 3 / 2; }
.exp-name        { grid-area: name; }
.exp-progression { grid-area: progression; }
.exp-search      { grid-area: search; }

/* .exp-info is a stub for a future checklist; hide it so it doesn't
   reserve grid space or perturb autoflow. */
.exp-info { display: none; }

a.exp-page-logo{
    text-decoration: none;
    color: inherit;
    width: 100%;
    /* Span both grid rows (set via grid-area 1/1/3/2 above) AND fill
       that spanned height — align-items:start on .exp-container would
       otherwise let the logo shrink to its image's natural size and
       sit at the top of column 1. align-self: stretch overrides that
       so the logo is the full (row1 + row-gap + row2) height. */
    height: 100%;
    align-self: stretch;
    display: grid;
    place-items: center;
    background-color: var(--colour-popup-bg);
    border-radius: 10px;
    border: 1px solid rgba(255, 255, 255, 0.06);
    box-shadow:
        0 20px 30px rgba(0,0,0,0.15),
        0 8px 10px rgba(0,0,0,0.10);
    padding: var(--space-16);
    box-sizing: border-box;
    position: relative;
    overflow: hidden;
}

[data-theme="light"] a.exp-page-logo,
:root:not([data-theme="dark"]) a.exp-page-logo {
    border-color: rgba(0, 0, 0, 0.06);
}

/* Banner background behind logo */
.exp-logo-banner {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1;
}

.exp-logo-banner img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0.8;
    filter: blur(1.75px);
}

/* Ensure logo stays on top of banner */
.exp-page-logo > picture:not(.exp-logo-banner) {
    position: relative;
    z-index: 2;
}

/* Logo image styling (not the banner). Scales up to the container
   while preserving aspect ratio. width:100% means landscape logos
   fill the card width; max-height keeps tall logos from running
   past the bottom of the card. */
.exp-page-logo > picture:not(.exp-logo-banner) img {
    display: block;
    width: 100%;
    max-width: 100%;
    max-height: 100%;
    height: auto;
    object-fit: contain;
}

.exp-logo img{
    
	width: 100%;
}

.exp-checklist{
    justify-content: baseline;
}

.exp-checklist-title{
    font-size: 20px;
    margin: 5px 0;
}

.item-name {
    font-size: var(--text-sm);
    margin: 0;
}

/* ── Set page progression panel (One Piece, Palworld, collections) ──
   Unified card-style container (rgba bg + 1px border + 10px radius)
   so it matches .exp-name in the same header. The Pokemon expansion
   page moved to the set header below on 2026-09-30. */
.exp-progression {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  padding: var(--space-16) var(--space-32);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 10px;
  box-sizing: border-box;
  box-shadow:
    0 2px 1px -1px var(--shadow-key-umbra),
    0 1px 1px 0    var(--shadow-key-penumbra),
    0 1px 3px 0    var(--shadow-key-ambient);
  width: 100%;
  min-width: 240px;
  max-width: 480px;
  box-sizing: border-box;
  justify-self: end;
}

.exp-progression-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.exp-progression-label {
  font-family: var(--font-header);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-md);
  color: var(--colour-text);
  display: flex;
  justify-content: space-between;
}

.exp-progression-label span {
  color: var(--colour-text-muted);
  font-weight: var(--font-weight-sm);
}

.exp-progression-track {
  position: relative;
  width: 100%;
  height: 18px;
  background: var(--bar-track);
  border-radius: 9px;
  overflow: hidden;
}

.exp-progression-fill {
  height: 100%;
  border-radius: 6px;
  transition: width 0.6s ease;
}

.exp-progression-fill--base   { background: var(--colour-chart-1); }
.exp-progression-fill--master { background: var(--colour-chart-2); }
.exp-progression-fill--grand  { background: var(--colour-chart-3); }

.exp-progression-track:has(.exp-progression-fill--base) {
  background: color-mix(in srgb, var(--colour-chart-1) 50%, transparent);
}
.exp-progression-track:has(.exp-progression-fill--master) {
  background: color-mix(in srgb, var(--colour-chart-2) 50%, transparent);
}
.exp-progression-track:has(.exp-progression-fill--grand) {
  background: color-mix(in srgb, var(--colour-chart-3) 50%, transparent);
}

.exp-progression-legend {
  display: flex;
  gap: var(--space-16);
  flex-wrap: wrap;
  margin-top: var(--space-4);
}

.exp-progression-legend-item {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-family: var(--font-paragraph);
  font-size: 11px;
  color: var(--colour-text-muted);
}

.exp-progression-legend-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
}


@media (max-width: 900px) {
  .exp-progression {
    min-width: 0;
    max-width: none;
    width: 100%;
    padding: var(--space-12) var(--space-16);
  }
}

/* === Mobile stack: collapse the 3-col grid to a single-column flow.
   Below 768px the third column gets crushed to ~30px and the progression
   labels truncate ("Bas…", "Mas…", "Gra…"). Switch to a flex column so
   each block gets full width. === */
@media (max-width: 767px) {
  .exp-container {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-12);
    grid-template-columns: none;
    grid-template-rows: none;
    margin: var(--space-16) 0;
  }

  /* Banner/logo: keep its branded look but fill the row width. */
  a.exp-page-logo {
    width: 100%;
    max-width: 100%;
    height: clamp(140px, 38vw, 200px);
    padding: var(--space-12);
  }
  .exp-page-logo > picture:not(.exp-logo-banner) img {
    max-width: 220px;
    max-height: 120px;
  }

  /* Source-order matters when grid-area is overridden — make the search
     sit AFTER the progression on mobile rather than in some auto slot. */
  .exp-search {
    grid-area: auto;
    align-items: stretch;
    width: 100%;
  }
  .exp-search-label {
    text-align: left;
  }
  .exp-search-field {
    width: 100%;
  }

}

/* Progression labels must never truncate, regardless of viewport. */
.exp-progression-label {
  white-space: nowrap;
  overflow: visible;
  text-overflow: clip;
  min-width: 0;
}
.exp-progression-label span {
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .exp-progression-fill {
    transition: none;
  }
}

.exp-name{
    /* Styled-card container matching the pricing strip — rgba bg,
       1px hairline border, 10px radius, breathing-room padding.
       Capped to 480px and centred horizontally in column 2 so the
       middle column visually aligns with the progression/search
       column-3 blocks (which are also 480px max). */
    display: flex;
    justify-content: flex-start;
    flex-direction: column;
    gap: var(--space-8);
    padding: 12px 16px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 10px;
    box-sizing: border-box;
    width: 100%;
    max-width: 480px;
    justify-self: center;
}

[data-theme="light"] .exp-name,
:root:not([data-theme="dark"]) .exp-name {
    background: rgba(0, 0, 0, 0.03);
    border-color: rgba(0, 0, 0, 0.06);
}

.exp-name-header{
    display: flex;
    align-items: center;
}

.exp-name-header img{
    height: 36px;
    margin-left: 10px;
}

.exp-name-header p{
    line-height: 30px;
    margin: 0;
}

.exp-rd p{
    font-size: var(--text-xs);
    color: var(--colour-text);
    margin: 0;
}

.exp-nocards p{
    font-size: var(--text-xs);
    margin: 0;
    color: var(--colour-text);
}

/* The one-sentence set summary under the header facts (all three TCG set pages). */
.exp-summary{
    font-size: var(--text-xs);
    line-height: 1.5;
    color: var(--colour-text-muted);
    margin: var(--space-8) 0 0;
    max-width: 62ch;
}

.exp-search{
    /* grid-area set via grid-template-areas on .exp-container.
       .exp-progression has a hard max-width: 480px + justify-self: end
       (line ~181) — without matching constraints here, the search
       expands to the full grid cell while progression stays capped at
       480px, breaking width parity. Mirror those constraints exactly. */
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
    gap: var(--space-8);
    width: 100%;
    max-width: 480px;
    justify-self: end;
    box-sizing: border-box;
}

.exp-search-label{
    font-size: var(--text-xs);
    font-weight: var(--font-weight-md);
    color: var(--colour-text);
}

.exp-search-field{
    position: relative;
    /* Fill the column so the input matches the progression panel
       width above it (was previously capped at 360px). */
    width: 100%;
}

.exp-search-input{
    width: 100%;
    padding: 12px 40px 12px 12px;
    border: 1px solid var(--colour-border);
    border-radius: var(--radius-xs);
    background: var(--colour-paper);
    color: var(--colour-text);
    font-family: var(--font-paragraph);
    font-size: var(--text-sm);
    box-sizing: border-box;
    transition: border-color 0.2s ease, background-color 0.2s ease;
}

.exp-search-input:hover{
    border-color: var(--colour-secondary);
}

.exp-search-input:focus-visible{
    background-color: var(--colour-popup-paper);
    border-color: var(--colour-secondary);
    outline: 3px solid var(--colour-secondary);
    outline-offset: 0;
}

.exp-search-icon{
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--colour-secondary);
    pointer-events: none;
}

.exp-search-input::placeholder{
    color: var(--colour-text-muted);
}

@media (max-width: 480px) {
    .exp-search{
        align-items: stretch;
    }

    .exp-search-label{
        text-align: left;
    }

    .exp-search-field{
        width: 100%;
    }
}

.card-info{
	    margin-top: var(--space-12);
		height: 100px;
	    background: var(--colour-paper);
	    border-radius: var(--radius-sm);
	    padding: 4px;
	    position: relative;
	    z-index: 2;
	    box-shadow:
	    0 20px 30px rgba(0,0,0,0.15),
	    0 8px 10px rgba(0,0,0,0.10);
}

.card-tracking{
	position: relative;
	z-index: 1;
}

.card-tracking.dropdown-open{
	z-index: 20;
}

.card-grid {
	display: grid;
	width: 100%;
	grid-template-columns: repeat(auto-fill, var(--card-width-sm));
	gap: var(--space-32);
	justify-content: center;
}

.card {
		width: var(--card-width-sm);
		height: var(--card-hight-sm);
		overflow: hidden;
		border-radius: var(--radius-sm);
		cursor: pointer;
		position: relative;
		z-index: 1;
		box-shadow:
	    0 20px 30px rgba(0,0,0,0.15),
	    0 8px 10px rgba(0,0,0,0.10);
		transition: transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease;
}

.card::after {
	content: '';
	position: absolute;
	inset: 0;
	background: rgba(150, 150, 150, 0.40);
	pointer-events: none;
	z-index: 1;
	opacity: 0;
	transition: opacity 0.3s ease;
}

.card-unowned .card {
	filter: grayscale(20%);
}

.card-unowned .card::after {
	opacity: 1;
}

.card-tracking--ptcgl.card-unowned .card::after,
.card-unowned .card:has(.ptcgl-card-render--grid)::after {
	background: rgba(0, 0, 0, 0.22);
}

.card-tracking--ptcgl.card-unowned .card:has(.ptcgl-card-render--grid:not(.ptcgl-card-render--image-ready)) {
	filter: none;
}

.card-tracking--ptcgl.card-unowned .card:has(.ptcgl-card-render--grid:not(.ptcgl-card-render--image-ready))::after {
	opacity: 0;
}

/* User opt-out: when <html> carries .no-dim-unowned the dim treatment is
   suppressed (toggled in settings → Appearance, mirrored to the
   dim_unowned_pref cookie by the front controller, applied pre-paint
   by /js/shared/dimUnowned.js). */
html.no-dim-unowned .card-unowned .card {
	filter: none;
}

html.no-dim-unowned .card-unowned .card::after {
	opacity: 0;
}

/* Under the Base goal a One Piece or Palworld tile stands for its card, and
   the card is owned when any of its prints is (a parallel, a reprint): the
   bar and the Owned chip count it, so its tile is not dimmed either, though
   its own counter still shows its own print. set-toolbar.js sets the class
   (set-header Phase 6 review). */
.card-unowned.card-slot-owned .card {
	filter: none;
}

.card-unowned.card-slot-owned .card::after {
	opacity: 0;
}

.card:hover {
	transform: translateY(-4px);
	box-shadow:
		0 24px 38px rgba(0, 0, 0, 0.20),
		0 12px 16px rgba(0, 0, 0, 0.14);
}
.card img{
	width: calc(var(--card-width-sm) + 2px);
	height: auto;
	position: relative;
	top: -1px;
	left: -1px;
}

.card img.is-placeholder-image {
	filter: grayscale(0.72) saturate(0.72) brightness(0.84) contrast(0.95);
	opacity: 0.9;
}

.card-tracking-info{
	padding: 0px 0px var(--space-4) 0px;
	height: 45px;
    display: flex;
    flex-direction: row;
	position: relative;
}
.card-title{
    display: flex;
    flex-direction: column;
    justify-content: center;
	min-width: 0;
	flex: 1 1 auto;
	width: 100%;
}

.card-name {
	display: inline-flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 0.18em;
	width: 100%;
	max-width: 100%;
	min-width: 0;
	font-weight: var(--font-weight-bold);
	line-height: 1.18;
	padding-bottom: 3px;
	overflow: visible;
}

.card-name > a {
	display: contents;
	color: inherit;
	text-decoration: none;
}

.card-name:has(.card-name-part--annotation) {
	padding-right: 34px;
	box-sizing: border-box;
}

.card-name-part {
	display: inline-block;
	min-width: 0;
	line-height: inherit;
	vertical-align: baseline;
}

.card-name-part--normal {
	flex: 0 0 auto;
	white-space: nowrap;
}

.card-name-part.name-symbol--ex-era {
	color: inherit;
	font-family: Arial, Helvetica, var(--font-main, sans-serif);
	font-size: inherit;
	font-weight: 900;
	font-style: italic;
	line-height: 1;
	letter-spacing: 0;
	padding-left: 0.08em;
	transform: skewX(-7deg) scaleX(0.9);
	transform-origin: left center;
	text-shadow: 0.018em 0 currentColor;
}

.card-name-part--small {
	font-size: 0.72em;
	font-weight: var(--font-weight-bold);
	line-height: inherit;
	align-self: baseline;
}

.card-name-part--small:has(+ .name-symbol--lv-x) {
	font-size: 0.52em;
}

.card-name-part--annotation {
	font-size: 0.72em;
	font-style: italic;
	font-weight: var(--font-weight-bold);
	line-height: inherit;
	align-self: baseline;
	flex: 0 1 auto;
	margin-left: auto;
	padding-left: 0.35em;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.card-name-part--symbol {
	display: inline-flex;
	align-items: center;
	align-self: center;
	flex: 0 0 auto;
	max-width: 32px;
	overflow: hidden;
}

.card-name-part--symbol img {
	display: block;
	width: auto;
	height: 0.96em;
	max-width: 100%;
	max-height: 17px;
	object-fit: contain;
}

.card-name-part--symbol.name-symbol--mega img,
.card-name-part--symbol.name-symbol--primal img {
	height: 1.06em;
}

.card-name-part--symbol.name-symbol--lv-x {
	max-width: none;
	overflow: visible;
	color: #b88916;
	font-family: "Arial Black", Arial, var(--font-main, sans-serif);
	font-size: 1.16em;
	font-weight: 900;
	font-style: normal;
	line-height: 1;
	padding: 0 0.03em 0.05em;
	text-shadow: none;
	transform: skewX(-5deg) scaleX(1.04);
	transform-origin: center;
	-webkit-text-stroke: 0;
}

.card-number{
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
}

/* Rarity icon now lives INSIDE .card-number (right after the number
   text) — populated at runtime by cardPopup.js / updateCardGridRarityIcon,
   which writes a composite of <span class="rarity-symbol"> + image into
   the .card-rarety wrapper. Override the absolute-positioned defaults
   (kept lower in this stylesheet) so it sits inline at a card-number-
   appropriate size. */
.card-number .card-rarety,
.card-number .card-rarity{
    position: static;
    top: auto;
    right: auto;
    min-width: 0;
    flex: 0 0 auto;
    align-self: center;
    margin-left: 0;
}
.card-number .card-rarety img,
.card-number .card-rarity img{
    width: 18px;
    height: 18px;
}
.card-number .card-rarety .rarity-composite,
.card-number .card-rarity .rarity-composite{
    --rarity-icon-size: 18px;
}
/* Empty wrapper (JS hasn't rendered yet, or this card has no rarity)
   collapses to zero so the row doesn't have a phantom gap. */
.card-number .card-rarety:empty,
.card-number .card-rarity:empty{
    display: none;
}

.card-expansion-image{
    width: 30px;
}

/* Phase 4 — tile price + 7d delta. Positioned absolutely at the
   bottom-right of .card-tracking-info so it sits directly above the
   variant-icons fan in the .card-tracking-container row below. The
   number/rarity row keeps its natural left-aligned flow; price
   floats clear of it. */
.card-price-group{
    position: absolute;
    right: 8px;
    bottom: 4px;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-family: inherit;
    font-weight: var(--font-weight-md);
    line-height: 1;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.card-price{
    /* Inherit font-size from .card-price-group → .card-number so the
       price reads at exactly the same scale as the "001/088" number. */
    color: var(--colour-text);
}

.card-price-delta{
    /* One step smaller than the price so the delta reads as a
       secondary indicator. */
    font-size: 0.85em;
    font-weight: var(--font-weight-md);
}

.card-price-delta.is-up{
    color: var(--colour-trend-positive);
}

.card-price-delta.is-down{
    color: var(--colour-trend-negative);
}

/* Sparkline is off by default (pricing_tile_sparklines preference).
   When enabled, paints in trend colour via currentColor on the path. */
.card-price-sparkline{
    width: 48px;
    height: 16px;
    overflow: visible;
}

.card-price-sparkline.is-up{
    color: var(--colour-trend-positive);
}

.card-price-sparkline.is-down{
    color: var(--colour-trend-negative);
}

.card-rarety,
.card-rarity{
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 13px;
    right: 0;
    margin-left: 0;
    min-width: 34px;
    flex: 0 0 34px;
}

.card-rarety img,
.card-rarity img{
    width: 32px;
    height: 32px;
    object-fit: contain;
    display: block;
}

[data-theme="dark"] .card-rarety img:not(.rarity-symbol-image),
[data-theme="dark"] .card-rarity img:not(.rarity-symbol-image){
    filter:
        drop-shadow(0 0 1px rgba(255, 255, 255, 0.55))
        drop-shadow(0 0 2px rgba(255, 255, 255, 0.28))
        drop-shadow(0 1px 1px rgba(0, 0, 0, 0.55));
}

.card-rarety .rarity-composite,
.card-rarity .rarity-composite{
    --rarity-icon-size: 32px;
}

.card-rarety .rarity-composite--long,
.card-rarity .rarity-composite--long{
    --rarity-suffix-size: calc(var(--rarity-icon-size) * 0.48);
}

.card-tracking-container{
    display: grid;
    grid-template-columns: 69.5px 139px 69.5px;
	height: 50px;
	align-items: center;
}

.card-tracking-container > .popup-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    justify-self: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--colour-secondary);
    cursor: pointer;
}

.card-tracking-container > .popup-icon:focus-visible {
    outline: 2px solid var(--colour-focus-ring);
    outline-offset: 2px;
}

.popup-icon img{
	height: 30px;
}

.card-counter{
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    justify-content: center;
    align-items: center;
}

.card-counter button{
	height: auto;
	width: auto;
	padding: 0;
	border: none;
	background: none;
	color: var(--colour-secondary); /*Colour of the -/+*/
    cursor: pointer;
	transition: color 0.3s ease, opacity 0.3s ease;
	/* Expand touch target without affecting layout */
	position: relative;
}

.card-counter button .fa-solid {
	-webkit-text-stroke: 1.5px currentColor;
}

/* Expanded touch target via pseudo-element */
.card-counter button::before {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 44px;
	height: 44px;
	border-radius: 8px;
}

.card-counter button:focus-visible {
	outline: 2px solid var(--colour-focus-ring);
	outline-offset: 4px;
	border-radius: 4px;
}

/* Disabled state styles */
.card-counter button:disabled,
.card-counter button.disabled {
  color: var(--colour-text-muted);       /* Gray out the icons/text */
  cursor: not-allowed;  /* Show disabled cursor */
  opacity: 0.5;         /* Slightly transparent */
  pointer-events: none; /* Prevent clicks */
}

.card-count{
    text-align: center;
    line-height: 14px;
    font-size: 30px;
}

.card-type-container{
	right: 0px;
	position: relative;
	align-items: right;
}

/* ========================================
   VARIANT FAN BUTTON
   ======================================== */

/* Dropdown container */
.dropdown {
    position: relative;
    display: inline-block;
}

/* Dropdown Button — must fit within the 69.5px grid column */
.dropbtn {
    border: 2px solid transparent;
    cursor: pointer;
    background: transparent;
    color: var(--colour-text);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: auto;
    min-height: 44px;
    width: 69.5px;
    border-radius: 6px;
    transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

.dropbtn:focus-visible {
    outline: 2px solid var(--colour-focus-ring);
    outline-offset: 2px;
}

/* Completion state: gold stroke on each card (not the button box), in
   --fan-complete-glow (gold by the owner's ruling, 1 Oct 2026) */
.dropbtn.variant-complete .icon.fan-owned .cls-1 {
    stroke: var(--fan-complete-glow);
    stroke-width: 14px;
}

/* Fan container — constrained to button width */
.variant-icons {
    display: flex;
    align-items: center;
    justify-content: center;
    height: var(--icon-size);
    gap: 0;
    width: 69.5px;
}

/* Individual fan card */
.variant-icons .icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--icon-size);
    height: 34px;
    margin-left: var(--overlap);
    position: relative;
    z-index: 1;
    transition: transform 0.18s cubic-bezier(0.2, 0.9, 0.2, 1);
    will-change: transform;
    pointer-events: auto;
}

/* First fan card has no negative margin */
.variant-icons .icon:first-child {
    margin-left: 0;
}

/* Middle card gets highest z-index */
.variant-icons .icon.middle {
    z-index: 5;
}
.variant-icons .icon.left,
.variant-icons .icon.right {
    z-index: 3;
}
.variant-icons .icon.far-left,
.variant-icons .icon.far-right {
    z-index: 1;
}

/* SVG scaling */
.variant-icons svg {
    display: block;
    height: 100%;
    width: auto;
    pointer-events: none;
}

/* --- Fan card ownership states --- */

/* Unowned: gray + muted */
.icon.fan-unowned .cls-1 {
    fill: var(--fan-gray);
    stroke: var(--fan-gray-stroke);
    opacity: 0.6;
    transition: fill 0.3s ease, stroke 0.3s ease, opacity 0.3s ease;
}

/* Owned: uses the variant's dropdown colour via --fan-color */
.icon.fan-owned .cls-1 {
    fill: var(--fan-color, var(--fan-owned-fill));
    stroke: var(--fan-color, var(--fan-owned-stroke));
    stroke-width: 14px;
    opacity: 1;
    transition: fill 0.3s ease, stroke 0.3s ease, opacity 0.3s ease;
}

/* --- Completion magnifying glass overlay (centered on each card) --- */
.icon .fan-complete-badge {
    display: none;
}

.variant-complete .icon .fan-complete-badge {
    display: block;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 16px;
    height: 16px;
    pointer-events: none;
}

.variant-complete .icon .fan-complete-badge svg {
    width: 100%;
    height: 100%;
    filter: drop-shadow(0 1px 2px rgba(0,0,0,0.4));
}

/* --- Hover effects --- */

/* Each fan card is individually hoverable */
.variant-icons .icon:hover {
    transform: translateY(-3px) scale(1.1);
    z-index: 10;
}

/* Owned cards get a brighter highlight on hover */
.variant-icons .icon.fan-owned:hover .cls-1 {
    filter: brightness(1.2);
}

/* Light theme, a completed card's gold ring under hover. The brightness
   above lands on the stroke too (Chrome multiplies the sRGB values by 1.2),
   which lifts the ring's #9E7400 (--fan-complete-glow, style.css) to
   #BE8B00, 2.80:1 on the #F5F5F5 tile. The hovered stroke is #846100
   instead, which renders as #9E7400 (3.89:1); change both together. The
   filter transitions with the stroke, so the ring holds its colour through
   the hover while the fill brightens. Dark keeps the accent (9.62:1
   brightened), and the magnifier badge is not a .cls-1, so neither rule
   reaches it. */
:root:not([data-theme="dark"]) .dropbtn.variant-complete .icon.fan-owned .cls-1 {
    transition: fill 0.3s ease, stroke 0.3s ease, opacity 0.3s ease, filter 0.3s ease;
}

:root:not([data-theme="dark"]) .dropbtn.variant-complete .icon.fan-owned:hover .cls-1 {
    stroke: #846100;
}

/* Unowned cards become slightly less muted on hover */
.variant-icons .icon.fan-unowned:hover .cls-1 {
    opacity: 0.8;
}

/* --- Reduced motion --- */
@media (prefers-reduced-motion: reduce) {
    .card,
    .dropbtn,
    .variant-icons .icon,
    .icon.fan-unowned .cls-1,
    .icon.fan-owned .cls-1 {
        transition: none !important;
    }
}

/* --- Small screen adjustments --- */
@media (max-width: 480px) {
    :root {
        --icon-size: 36px;
        --overlap: -12px;
    }

    .dropbtn {
        height: calc(var(--icon-size) + 6px);
        padding: 4px 8px;
    }
}

/* ========================================
   VARIANT DROPDOWN CONTENT
   ======================================== */

/* Dropdown Content (Hidden by Default) */
.dropdown-content {
    display: none;
    position: absolute;
    background-color: var(--colour-paper);
    min-width: 160px;
    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
    z-index: var(--z-dropdown);
    top: 30px;
    left: 21.35px;
}

/* Links inside the dropdown */
.dropdown-content a {
    color: var(--colour-text);
    padding: 12px 16px;
    text-decoration: none;
    display: block;
}

/* Hover state for dropdown options */
.dropdown-option:hover {
    background-color: var(--dropdown-color, var(--colour-bg));
}

/* Divider between variants and report link */
.dropdown-divider {
    height: 1px;
    background-color: var(--colour-border, #e0e0e0);
    margin: 4px 0;
}

/* "Missing variant?" report link */
.dropdown-report-link {
    color: var(--colour-text-muted, #888);
    padding: 8px 16px;
    text-decoration: none;
    display: block;
    font-size: 0.8rem;
    text-align: center;
}

.dropdown-report-link:hover {
    color: var(--colour-primary);
    background-color: var(--colour-bg);
}

/* Show the dropdown menu */
.dropdown .dropdown-content.show {
    display: block;
}

/* ========================================
   SET FAQ (shared accordion, below the card grid)
   ======================================== */

/* Only the container lives here. The question/answer styling is the site's
   shared FAQ component in /css/shared/faq.css, which premium.php uses too. An
   earlier version of this block hand-rolled a flat list; that was a third FAQ
   look on a site that already had one, so it was removed rather than kept. */

.set-faq {
  max-width: 900px;
  margin-inline: auto;
  margin-block: var(--space-48) 0;
  padding-inline: var(--space-16);
}

@media (max-width: 600px) {
  .set-faq {
    margin-block-start: var(--space-32);
  }
}

/* The set's one-sentence summary opens the FAQ (set-header decision 6: it
   left the header and must stay visible, not only in the meta description). */
.set-faq__summary {
  margin: 0 0 var(--space-16);
  font-family: var(--font-paragraph);
  font-size: var(--text-sm);
  font-weight: 400;
  line-height: 1.55;
  color: var(--colour-text-muted);
}

/* ========================================
   SET HEADER (set-header handoff Phase 2, 2026-09-30)
   templates/shared/tcg/set-header.php + public/js/shared/tcg/set-header.js.
   Pokemon expansion page only so far. Mock ids: 12a (>=1420px here; the
   mock says 1366px, see the 12b query), 12b (1024-1419px), 8d (<1024px),
   8b (goal complete), 8c (light theme). The
   hairline and hover tint are mixed from the text colour so both themes
   work; nothing here may hard-code rgba(255,255,255,...).
   ======================================== */

.set-header {
  --sh-hairline: color-mix(in srgb, var(--colour-text) 8%, transparent);
  --sh-hover: color-mix(in srgb, var(--colour-text) 12%, transparent);
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr) 440px;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  grid-template-areas:
    "art head    panel"
    "art meta    panel"
    "art actions panel"
    "art price   panel";
  column-gap: 32px;
  row-gap: 20px;
  align-items: start;
  max-width: 1600px;
  margin: var(--space-40) auto 24px;
}

/* A class with a display value beats the UA's [hidden] rule. */
.set-header [hidden] {
  display: none !important;
}

.set-header--no-panel {
  grid-template-columns: 200px minmax(0, 1fr);
  grid-template-areas:
    "art head"
    "art meta"
    "art actions"
    "art price";
}

/* Pages without art (All Cards, the index pages; README 6e): the identity
   column takes the art's place. */
.set-header--no-art {
  grid-template-columns: minmax(0, 1fr) 440px;
  grid-template-areas:
    "head    panel"
    "meta    panel"
    "actions panel"
    "price   panel";
}

.set-header--no-art.set-header--no-panel {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "head"
    "meta"
    "actions"
    "price";
}

.set-header__head {
  grid-area: head;
  min-width: 0;
}

/* Mobile only: "Released 17 Jul 2026 · 84 cards + 36 secret". */
.set-header__factsline {
  display: none;
  grid-area: facts;
  margin: 0;
  font-family: var(--font-paragraph);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--colour-text-muted);
}

/* ── Art: card-shaped (5:7), holo shimmer until the logo loads ── */
.set-header__art {
  grid-area: art;
  position: relative;
  width: 200px;
  height: 280px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  box-shadow: var(--shadow-floating);
  background: var(--holo-gradient), var(--colour-paper);
  background-size: 200% 200%;
  animation: ttcg-shimmer 3s ease-in-out infinite;
}

.set-header__art.is-loaded {
  animation: none;
}

/* One card's art in the frame (a collection's featured card, an event's
   cover card): the picture fills the 5:7 box. */
.set-header__card {
  position: absolute;
  inset: 0;
}

.set-header__card img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.set-header__banner,
.set-header__logo {
  position: absolute;
  inset: 0;
  display: block;
}

.set-header__banner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.8;
  filter: blur(1.75px);
}

.set-header__logo img {
  width: 100%;
  height: 100%;
  padding: var(--space-16);
  box-sizing: border-box;
  object-fit: contain;
}

/* A card-mode frame may carry a logo for its fallback (set-header Phase 6,
   Palworld: the booster pack alone and sharp, which prints its own logo; a
   set with no booster image yet shows the placeholder under the logo, the
   banner mode's look). The logo appears only once the card image has
   fallen back (set-header.php marks the frame .is-fallback). */
.set-header__art--card .set-header__logo {
  display: none;
}

.set-header__art--card.is-fallback .set-header__logo {
  display: block;
}

.set-header__art--card.is-fallback .set-header__card img {
  opacity: 0.8;
  filter: blur(1.75px);
}

/* ── Premium milestone strip (set-header Phase 6, README section 7) ──
   Between the header and the toolbar on the owner's own Pokémon set page,
   once per user (templates/shared/tcg/milestone-strip.php). Gold here is
   Premium: the border, the tint, the pill and the button text
   (--colour-premium-text); the line is the text colour. */
.milestone-strip {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 28px;
  max-width: 1600px;
  margin: 0 auto 24px;
  padding: 16px 20px;
  border: 2px solid var(--colour-premium);
  border-radius: var(--radius-sm);
  background: var(--colour-premium-bg);
  box-sizing: border-box;
}

/* The author display beats the UA's [hidden] rule. */
.milestone-strip[hidden] {
  display: none;
}

.milestone-strip__text {
  min-width: 0;
}

.milestone-strip__kicker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 4px;
}

.milestone-strip__line {
  margin: 0;
  font-family: var(--font-header);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--colour-text);
}

.milestone-strip .milestone-strip__cta {
  white-space: nowrap;
}

.milestone-strip__dismiss {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--colour-text);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.milestone-strip__dismiss:hover {
  background: color-mix(in srgb, var(--colour-text) 12%, transparent);
}

.milestone-strip__dismiss:focus-visible {
  outline: 3px solid var(--colour-focus-ring);
  outline-offset: 2px;
}

/* Opacity only when it opens live: no height animation to shift the grid. */
.milestone-strip.is-entering {
  animation: milestone-strip-in 0.2s ease;
}

@keyframes milestone-strip-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* The strip's own button rules carry the strip's class: style.css (which
   imports nav-dropdowns.css, the .nav-premium-btn rules) loads after this
   file on the expansion page, so a lone class here loses the tie. */
@media (prefers-reduced-motion: reduce) {
  .milestone-strip.is-entering {
    animation: none;
  }

  .milestone-strip .milestone-strip__cta:hover {
    transform: none;
  }
}

/* Phones (not mocked): the text and the dismiss button share the first row,
   the button takes the second at full width; every target is 44px. */
@media (max-width: 1023px) {
  .milestone-strip {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "text dismiss"
      "cta  cta";
    gap: 12px 16px;
    padding: 16px;
  }

  .milestone-strip__text {
    grid-area: text;
  }

  .milestone-strip__dismiss {
    grid-area: dismiss;
    align-self: start;
    width: 44px;
    height: 44px;
  }

  .milestone-strip .milestone-strip__cta {
    grid-area: cta;
    justify-content: center;
    min-height: 44px;
    font-size: var(--text-sm);
    white-space: normal;
    text-align: center;
  }
}

/* ── Facts and money ── */
/* Labels align along the top of their row and values along its bottom: a
   label that wraps ("Left to Complete · Grandmaster" always can, and more
   labels do near 1024px) must not lift its value out of line with the
   others, and a value that wraps (the sets index's Cards cell, "18,875 +
   2,555 secret + 55 Classic Collection") must not lift its label. The cells
   stretch to the row and space their two lines apart, so in an uneven row
   the shorter cells open a gap between label and value instead. Cells sat on
   the bottom of the row (align-items: end) until 2026-09-30, which held the
   first case and broke the second. */
.set-meta {
  grid-area: meta;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px 24px;
  margin: 0;
  min-width: 0;
}

.set-meta__cell {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 4px;
  min-width: 0;
}

.set-meta dd {
  margin: 0;
}

.set-label {
  margin: 0;
  font-family: var(--font-header);
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--colour-text-muted);
}

/* The label may break only before the goal: "Left to Complete" / "· Master". */
.set-label__goal {
  white-space: nowrap;
}

.set-meta__fact {
  font-family: var(--font-paragraph);
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.35;
  color: var(--colour-text);
}

.set-meta__muted {
  font-weight: 400;
  color: var(--colour-text-muted);
}

.set-meta__link {
  color: var(--colour-secondary-text);
  text-decoration: none;
}

.set-meta__link:hover {
  color: var(--colour-secondary-text);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.set-meta__money {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 8px;
}

/* Money is data, so it sits in the text colour: gold is Premium only. */
.set-meta__value {
  font-family: var(--font-header);
  font-size: var(--text-md);
  font-weight: 700;
  line-height: 1.1;
  color: var(--colour-text);
  white-space: nowrap;
}

.set-meta__value.is-unavailable,
.set-meta__fact.is-unavailable {
  font-family: var(--font-paragraph);
  font-size: var(--text-sm);
  font-weight: 400;
  line-height: 1.35;
  color: var(--colour-text-muted);
}

.set-delta {
  font-family: var(--font-paragraph);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
}

.set-delta--up   { color: var(--colour-trend-positive-text); }
.set-delta--down { color: var(--colour-trend-negative); }
.set-delta--flat { color: var(--colour-trend-neutral); }

.set-header__actions {
  grid-area: actions;
  align-self: end;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
}

/* The one primary action a page may carry (7b: New Collection). */
.set-header__actions .set-header__primary {
  margin: 0;
  max-width: none;
}

.set-header__price {
  grid-area: price;
  justify-self: end;
  align-self: end;
  margin: 0;
  font-family: var(--font-paragraph);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--colour-text-muted);
  text-align: right;
}

/* ── Progress panel ── */
.set-panel {
  grid-area: panel;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
  min-width: 0;
  padding: 20px 24px;
  border-radius: var(--radius-sm);
  background: var(--colour-paper);
  box-shadow: var(--shadow-floating);
}

.set-panel__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-12);
}

.set-panel__count {
  margin: 0;
  font-family: var(--font-header);
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: 1;
  color: var(--colour-text);
  white-space: nowrap;
}

.set-panel__total {
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--colour-text-muted);
}

.set-panel__equal {
  margin: 0;
}

/* Segmented control. The toolbar's filter chips (Phase 3) reuse the track. */
.goal-switch {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border-radius: 999px;
  background: var(--sh-hairline);
}

.goal-switch__option {
  padding: 6px 14px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  font-family: var(--font-header);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--colour-text);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.goal-switch__option:hover {
  background: var(--sh-hover);
}

.goal-switch__option[aria-checked="true"] {
  background: var(--colour-primary);
  color: #FAFAFA;
}

.set-panel__status {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  margin: 0;
  font-family: var(--font-header);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--colour-text);
}

.set-panel__status--hint {
  font-family: var(--font-paragraph);
  font-weight: 400;
  color: var(--colour-text-muted);
}

.set-panel__switch {
  font-size: 14px;
}

.set-panel__tiers {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 14px;
  border-top: 1px solid var(--sh-hairline);
}

.tier-row {
  display: grid;
  grid-template-columns: 110px minmax(0, 1fr) 64px;
  align-items: center;
  gap: var(--space-12);
}

.tier-row--base   { --sh-tier: var(--colour-chart-1); }
.tier-row--master { --sh-tier: var(--colour-chart-2); }
.tier-row--grand  { --sh-tier: var(--colour-chart-3); }

.tier-row__label {
  font-family: var(--font-header);
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1.2;
  color: var(--colour-text);
}

.mini-bar {
  display: block;
  height: 6px;
  overflow: hidden;
  border-radius: 3px;
  background: color-mix(in srgb, var(--sh-tier) 35%, transparent);
}

.mini-bar__fill {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: var(--sh-tier);
  transition: width 0.6s ease;
}

.tier-row__count {
  font-family: var(--font-paragraph);
  font-size: 14px;
  font-weight: 600;
  color: var(--colour-text-muted);
  text-align: right;
  white-space: nowrap;
}

.set-panel__rarity {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  padding-top: var(--space-12);
  border-top: 1px solid var(--sh-hairline);
}

.set-panel__rarity-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.set-panel__rarity-count {
  font-family: var(--font-paragraph);
  font-size: 14px;
  font-weight: 600;
  color: var(--colour-text-muted);
  white-space: nowrap;
}

.rarity-bar {
  display: flex;
  gap: 2px;
  height: 6px;
  overflow: hidden;
  border-radius: 3px;
  transition: opacity 0.3s ease;
}

.rarity-bar.is-complete {
  opacity: 0.25;
}

/* Without JS the segments share the bar by flex-grow, 6px at the least.
   set-header.js then gives each a whole-pixel width (snapRarityBar), so a
   1-of-204 segment is a crisp 6px block instead of a 2px sliver and every
   2px gap paints the same; the transition covers count changes only, never
   the first layout or a window resize. */
.rarity-bar__seg {
  flex-basis: 0;
  min-width: 6px;
}

.rarity-bar.is-snapped .rarity-bar__seg {
  transition: flex-basis 0.6s ease;
}

/* One segment per rarity, coloured by its position in the set's own ladder
   (App\Pokemon\RarityOrder::colourSlot): the six chart colours cycle as
   1, 2, 3, 4, 6, 5, so the two near-identical teals (chart-2, chart-6) and
   the pair colour-blind viewers cannot tell apart (chart-1, chart-4) never
   sit next to each other. A set can carry up to 15 rarities, so a colour
   cannot mean one rarity; the hover title and the screen-reader list name
   each segment. */
.rarity-bar__seg--c1 { background: var(--colour-chart-1); }
.rarity-bar__seg--c2 { background: var(--colour-chart-2); }
.rarity-bar__seg--c3 { background: var(--colour-chart-3); }
.rarity-bar__seg--c4 { background: var(--colour-chart-4); }
.rarity-bar__seg--c5 { background: var(--colour-chart-5); }
.rarity-bar__seg--c6 { background: var(--colour-chart-6); }

/* Premium only, text-safe gold (rule 39). Free viewers on their own page. */
.set-panel__premium {
  margin-top: auto;
  align-self: flex-start;
  font-family: var(--font-header);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--colour-premium-text);
  text-decoration: none;
}

.set-panel__premium:hover {
  color: var(--colour-premium-text);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* The sign-up action block (SIGNED_OUT_PANEL.md, 2026-09-30): it closes the
   signed-out panel under the tier preview, and sits under the owner's
   progress when a logged-out visitor is on someone else's page (owner
   decision, 2026-09-30). Pinned to the panel's bottom: a hairline, 16px, then
   the line and the links 14px apart. */
.set-panel__signup {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: auto;
  padding-top: var(--space-16);
  border-top: 1px solid var(--sh-hairline);
}

/* The locked Premium panel (decisions 11 and 12) and the plan meter (7b):
   a label row with a pill, a line or a count and a bar, the Premium link. */
.set-panel__labelrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.set-panel__labelrow .set-panel__label {
  margin: 0;
}

.set-panel__meterline {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.set-panel__unit {
  font-family: var(--font-paragraph);
  font-size: 16px;
  font-weight: 400;
  color: var(--colour-text-muted);
}

.set-panel__copy {
  margin: 0;
  font-family: var(--font-paragraph);
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.5;
  color: var(--colour-text);
}

/* Progress by source set (6b): the tier-row pattern with room for a set name. */
.set-panel__sources .tier-row {
  grid-template-columns: minmax(96px, 160px) minmax(0, 1fr) 64px;
}

.set-panel__sources .tier-row__label {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.set-panel__sources .tier-row__label a {
  color: inherit;
  text-decoration: none;
}

.set-panel__sources .tier-row__label a:hover {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.set-panel__more {
  justify-self: start;
  margin-top: 4px;
}

/* The collection page's Include cameos switch, under the main bar (6a). */
.set-panel__toggle {
  display: flex;
  align-items: center;
  gap: 12px;
}

.set-panel__toggle-meta {
  font-family: var(--font-paragraph);
  font-size: 14px;
  font-weight: 600;
  color: var(--colour-text-muted);
}

/* Logged out: the same box asks for an account (decision 17). */
.set-panel__line {
  margin: 0;
  font-family: var(--font-paragraph);
  font-size: var(--text-sm);
  font-weight: 400;
  line-height: 1.5;
  color: var(--colour-text);
}

/* Sign Up! then Log In, 24px apart and centred on each other; the row wraps
   under about 335px. */
.set-panel__cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-12) 24px;
}

/* Sign Up! is the standard .submit (not the landing hero's .signup-button),
   re-flowed for a row as .ab-cta .submit is (about.css): .submit is written
   for a form (width 50%, block, margin 0 auto). Exactly 48px tall (its own
   15px padding gives 49px), so the identity column's 48px link row centres
   on it. Two classes, so it wins over style.css's .submit whichever sheet
   loads last. */
.set-panel__cta .submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: auto;
  min-height: 48px;
  margin: 0;
  padding-block: 0;
  text-decoration: none;
}

/* The signed-out tier preview (set-header.php, tcg_signup_preview()): the
   tiers the signed-in panel would show here, totals only. The name reuses
   .tier-row__label; the bar reuses .exp-progression-track with its fill at
   0% (the fill is what tints the track), without the % text. Tiers 12px
   apart, the bar 6px under its line. */
.set-panel__preview {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.set-preview__tier {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.set-preview__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

/* Hind text sets its weight: the body is Montserrat 700. */
.set-preview__total {
  font-family: var(--font-paragraph);
  font-size: 14px;
  font-weight: 400;
  color: var(--colour-text-muted);
  text-align: right;
  white-space: nowrap;
}

/* Signed out, desktop (SIGNED_OUT_PANEL.md, Alignment): the identity
   column's link row is 48px tall and its links centre on Sign Up!. The empty
   price track's 20px row-gap already holds the row 20px above the column's
   bottom, the panel's bottom padding. */
@media (min-width: 1024px) {
  .set-header--signup .set-header__actions {
    min-height: 48px;
    align-items: center;
  }

  /* A signed-out header with a price line under its link row (the
     generation and event collection pages): the price line moves above the
     link row, which becomes the last track, 20px above the bottom
     (implementer's call, 2026-09-30; the row otherwise sat 38px up, 18px off
     Sign Up!). Scoped to headers with both, so no other page moves. */
  .set-header.set-header--signup:has(> .set-header__price):has(> .set-header__actions) {
    grid-template-areas:
      "art head    panel"
      "art meta    panel"
      "art price   panel"
      "art actions panel";
  }

  .set-header.set-header--signup.set-header--no-art:has(> .set-header__price):has(> .set-header__actions) {
    grid-template-areas:
      "head    panel"
      "meta    panel"
      "price   panel"
      "actions panel";
  }

  .set-header--signup:has(> .set-header__price) > .set-header__actions {
    margin-bottom: 20px;
  }
}

/* ── Toolbar (set-header handoff Phase 3, 2026-09-30) ──
   templates/shared/tcg/set-toolbar.php + public/js/shared/tcg/set-toolbar.js.
   One row under the header: search · All / Owned / Missing / Dupes · Sort ·
   Grid / List / Binder (README, "Toolbar"). The chips and the view switch
   reuse the goal switch's track as .seg-switch; the Sort menu, and the View
   menu that replaces the switch under 1024px, reuse the navbar's dropdown
   surface. The hairline tokens repeat the header's, because the toolbar sits
   outside .set-header. */
.set-toolbar {
  --sh-hairline: color-mix(in srgb, var(--colour-text) 8%, transparent);
  --sh-hover: color-mix(in srgb, var(--colour-text) 12%, transparent);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  max-width: 1600px;
  margin: 0 auto var(--space-32);
  padding-top: 24px;
  border-top: 1px solid var(--sh-hairline);
}

.set-toolbar__left,
.set-toolbar__right {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
  min-width: 0;
}

.set-toolbar__search {
  position: relative;
  width: 320px;
  max-width: 100%;
}

/* One row from 1024px up. The left group's basis is the laptop layout's
   search + chips (240 + 16 + 376 for three-digit counts), so the row breaks
   only when even that will not fit; the right group then takes its own row,
   right-aligned, 16px below. Above 1419px the search flexes from 320px down
   to 240px before the row breaks: the page's 5% gutters leave 1278px at
   1420, and the full toolbar (320 search, the Premium pill) needs 1284. */
@media (min-width: 1024px) {
  .set-toolbar__left {
    flex: 1 1 auto;
    /* No inner wrap: a wrapping row breaks before it shrinks, so the search
       could never give up its 80px. The chips keep their width; the search
       absorbs the shortfall down to 240px. */
    flex-wrap: nowrap;
  }

  /* With chips (a signed-in viewer) the basis is the laptop layout's
     search + chips; a guest's group is the search alone and fits anywhere. */
  .set-toolbar__left--chips {
    flex-basis: 632px;
  }

  .set-toolbar__left .filter-chips {
    flex-shrink: 0;
  }

  /* Tiles mode (the index pages, Phase 5) carries a caller's chip set: up to
     six product-type chips ("Premium Booster 2" among them), so the left
     group's basis is its content and it wraps; the right group takes its
     own row when the chips need the width. The set page keeps the four-chip
     sizing above. */
  .set-toolbar[data-tb-mode="tiles"] .set-toolbar__left--chips {
    flex-basis: auto;
    flex-wrap: wrap;
  }

  .set-toolbar__right {
    flex: 0 0 auto;
    flex-wrap: nowrap;
    margin-left: auto;
  }

  .set-toolbar__search {
    flex: 0 1 320px;
    min-width: 240px;
  }
}

.set-toolbar__input,
.set-sticky-bar__input {
  width: 100%;
  height: 44px;
  padding: 0 40px 0 12px;
  border: 1px solid var(--colour-border);
  border-radius: var(--radius-xs);
  background: var(--colour-paper);
  color: var(--colour-text);
  font-family: var(--font-paragraph);
  font-size: var(--text-sm);
  box-sizing: border-box;
  /* A placeholder longer than the box ("Search Pokémon expansions" at the
     laptop width) ends in an ellipsis instead of running under the icon. */
  text-overflow: ellipsis;
  transition: border-color 0.2s ease;
}

.set-toolbar__input:hover,
.set-sticky-bar__input:hover {
  border-color: var(--colour-secondary);
}

.set-toolbar__input:focus-visible,
.set-sticky-bar__input:focus-visible {
  border-color: var(--colour-secondary);
  outline: 3px solid var(--colour-focus-ring);
  outline-offset: 0;
}

.set-toolbar__input::placeholder,
.set-sticky-bar__input::placeholder {
  color: var(--colour-text-muted);
  text-overflow: ellipsis;
}

.set-toolbar__search-icon,
.set-sticky-bar__search-icon {
  position: absolute;
  right: 12px;
  top: 50%;
  width: 18px;
  height: 18px;
  transform: translateY(-50%);
  color: var(--colour-secondary-text);
  pointer-events: none;
}

/* A multi-select listbox (Rarity, Type on All Cards) marks its chosen
   options with a check, so "2 selected" can be read in the list. */
.tb-menu__list[aria-multiselectable="true"] [role="option"]::before {
  content: "";
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  border: 1px solid var(--colour-border);
  border-radius: 4px;
}

.tb-menu__list[aria-multiselectable="true"] [role="option"][aria-selected="true"]::before {
  border-color: var(--colour-primary);
  background: var(--colour-primary) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23FAFAFA' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center / 10px no-repeat;
}

/* Segmented track, the goal switch's twin (README: "same track as the goal switch"). */
.seg-switch {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border-radius: 999px;
  background: var(--sh-hairline);
}

.seg-switch__option {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  font-family: var(--font-header);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--colour-text);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.seg-switch__option:hover {
  background: var(--sh-hover);
}

.seg-switch__option[aria-checked="true"] {
  background: var(--colour-primary);
  color: #FAFAFA;
}

.seg-switch__option:focus-visible {
  outline: 3px solid var(--colour-focus-ring);
  outline-offset: 2px;
}

/* List and Binder: marked Soon, kept in the markup, not clickable. */
.seg-switch__option[aria-disabled="true"] {
  cursor: default;
}

.seg-switch__option[aria-disabled="true"]:hover {
  background: transparent;
}

.filter-chips__count {
  font-weight: 500;
  color: var(--colour-text-muted);
}

.seg-switch__option[aria-checked="true"] .filter-chips__count {
  color: inherit;
  opacity: 0.8;
}

.view-switch__option .coming-soon-badge,
.view-switch__option .premium-pill {
  margin-left: 2px;
}

/* The Premium pill collapses to the dot at 12b (README, laptop). Two
   classes, because style.css's .premium-dot sets display too. */
.view-switch .view-switch__premium-dot {
  display: none;
}

/* Sort menu (and the mobile View menu): the navbar's dropdown surface. */
.tb-menu {
  position: relative;
}

.tb-menu__button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 14px;
  border: 1px solid var(--colour-border);
  border-radius: var(--radius-xs);
  background: var(--colour-paper);
  font-family: var(--font-paragraph);
  font-size: var(--text-sm);
  color: var(--colour-text);
  cursor: pointer;
  transition: border-color 0.2s ease;
}

.tb-menu__button:hover {
  border-color: var(--colour-secondary);
}

.tb-menu__button:focus-visible {
  outline: 3px solid var(--colour-focus-ring);
  outline-offset: 2px;
}

.tb-menu__lead {
  color: var(--colour-text-muted);
}

.tb-menu__value {
  font-weight: 600;
}

.tb-menu__chevron {
  flex-shrink: 0;
  transition: transform var(--dropdown-open-duration) ease-out;
}

.tb-menu__button[aria-expanded="true"] .tb-menu__chevron {
  transform: rotate(180deg);
}

.tb-menu__list {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 30;
  min-width: 100%;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--nav-dropdown-bg);
  border: 1px solid var(--colour-border);
  border-radius: var(--radius-xs);
  box-shadow: var(--nav-dropdown-shadow);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
  transition:
    opacity var(--dropdown-close-duration) ease-in,
    transform var(--dropdown-close-duration) ease-in,
    visibility var(--dropdown-close-duration);
}

.tb-menu__list--right {
  left: auto;
  right: 0;
}

.tb-menu__list.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  /* Visibility is not transitioned on open: a visibility transition reads
     "hidden" on its first frame, so the synchronous focus() that a keyboard
     open puts on the selected option would fail. Opacity and transform keep
     the navbar dropdown's timing. */
  transition:
    opacity var(--dropdown-open-duration) ease-out,
    transform var(--dropdown-open-duration) ease-out;
}

.tb-menu__list [role="option"] {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-radius: 6px;
  font-family: var(--font-paragraph);
  font-size: var(--text-sm);
  /* Hind sets its weight (the body is Montserrat 700, which made every
     unselected option bolder than the selected one's 600). */
  font-weight: 400;
  color: var(--colour-text);
  white-space: nowrap;
  cursor: pointer;
}

.tb-menu__list [role="option"]:hover {
  background: var(--sh-hover);
}

.tb-menu__list [role="option"]:focus-visible {
  background: var(--sh-hover);
  outline: 2px solid var(--colour-focus-ring);
  outline-offset: -2px;
}

.tb-menu__list [role="option"][aria-selected="true"] {
  font-weight: 600;
  color: var(--colour-secondary-text);
}

.tb-menu__list [role="option"][aria-disabled="true"] {
  color: var(--colour-text-muted);
  cursor: default;
}

/* The View menu exists for the mobile stack only. */
.tb-menu--view {
  display: none;
}

/* Nothing left after the search, the chip and the goal. */
.set-toolbar__empty {
  max-width: 1600px;
  margin: 0 auto var(--space-32);
  font-family: var(--font-paragraph);
  font-size: var(--text-sm);
  font-weight: 400;
  color: var(--colour-text-muted);
}

@media (prefers-reduced-motion: reduce) {
  .tb-menu__list,
  .tb-menu__chevron,
  .tb-menu__button,
  .seg-switch__option,
  .set-toolbar__input {
    transition: none;
  }
}

/* ── Compact sticky bar (set-header handoff Phase 4, mock 8e, 2026-09-30) ──
   Fixed in the navbar's slot once the toolbar has scrolled away (shown and
   hidden by set-sticky-bar.js): top = the navbar's height while it shows, 0
   once nav.js has hidden it. Its edges are the page's (the body's gutter,
   clamp(16px, 5vw, 140px), and 1600px at most, like the header and the
   toolbar). z-index 97 keeps it under the navbar (100), its mobile menu (99),
   its scrim (98) and its floating utility buttons (101), which is decision
   26; --z-sticky (101) would paint it over the navbar. The card popup
   overlay is --z-modal (1000) and covers it either way. The `top` change
   takes the navbar's 0.3 s so the two edges move together (README says
   0.2 s); the entrance keeps 0.2 s. */
/* It follows the toolbar, whose bottom margin is --space-32: the negative
   top margin collapses with it to -1px, so the sentinel overlaps the
   toolbar's last pixel; the bottom margin gives the grid its gap back. */
.set-sticky-sentinel {
  height: 1px;
  margin-top: calc(-1px - var(--space-32));
  margin-bottom: var(--space-32);
  visibility: hidden;
  pointer-events: none;
}

.set-sticky-bar {
  --sh-hairline: color-mix(in srgb, var(--colour-text) 8%, transparent);
  --sh-hover: color-mix(in srgb, var(--colour-text) 12%, transparent);
  position: fixed;
  top: 64px;
  right: clamp(16px, 5vw, 140px);
  left: clamp(16px, 5vw, 140px);
  z-index: 97;
  display: flex;
  align-items: center;
  gap: 16px; /* the mock's 24px: the mock had 1360px of content at 1440, this page has 1296 */
  box-sizing: border-box;
  height: 64px;
  max-width: 1600px;
  margin: 0 auto;
  padding: 0 20px;
  border-bottom: 1px solid var(--sh-hairline);
  border-radius: 12px;
  background: color-mix(in srgb, var(--colour-paper) 94%, transparent);
  box-shadow: var(--shadow-resting);
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity 0.2s ease, transform 0.2s ease, top 0.3s ease;
}

/* The author display above would beat the UA's [hidden] rule (about.css and
   premium.css carry the same note): the attribute must really remove it. */
.set-sticky-bar[hidden] {
  display: none;
}

/* The fade-out must not catch a click meant for what is under it. */
.set-sticky-bar:not(.is-visible) {
  pointer-events: none;
}

@media (min-width: 1024px) {
  .set-sticky-bar {
    top: 80px;
  }
}

.set-sticky-bar.is-visible {
  opacity: 1;
  transform: none;
}

/* Keyboard and screen-reader focus scrolls land under the bar, not behind it
   (navbar 80 + bar 64 + 16; under 1024px the bar is two rows). */
html.has-sticky-bar {
  scroll-padding-top: 160px;
}

@media (max-width: 1023px) {
  html.has-sticky-bar {
    scroll-padding-top: 200px;
  }
}

/* nav.js has hidden the navbar: the bar takes its slot. */
.set-sticky-bar--top {
  top: 0;
}

.set-sticky-bar__thumb {
  flex: none;
  width: 28px;
  height: 39px;
  overflow: hidden;
  border-radius: 4px;
}

.set-sticky-bar__thumb picture,
.set-sticky-bar__thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* When the row is short of room (a long set name, three-digit counts, four
   chips) the name, the progress bar and the search give way, in that order
   of weight, before anything wraps or overflows. */
.set-sticky-bar__name {
  flex: 0 1 auto;
  min-width: 96px;
  overflow: hidden;
  font-family: var(--font-header);
  font-size: 16px;
  font-weight: 700;
  color: var(--colour-text);
  white-space: nowrap;
  text-overflow: ellipsis;
}

.set-sticky-bar__code {
  flex: none;
  padding: 2px 8px;
}

.set-sticky-bar__goal {
  flex: none;
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--font-header);
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
}

.set-sticky-bar__goal-label {
  color: var(--colour-text);
}

/* Text colour, not muted: the surface is 94% paper over whatever card art
   scrolls under it, and muted text measured 4.15:1 there in dark mode. */
.set-sticky-bar__goal-count {
  color: var(--colour-text);
}

.set-sticky-bar__bar {
  flex: 0 2 160px;
  min-width: 96px;
}

.set-sticky-bar__search {
  position: relative;
  flex: 0 1 280px;
  min-width: 200px;
}

.set-sticky-bar__chips {
  flex: none;
}

.set-sticky-bar__chips .seg-switch__option {
  padding: 6px 12px;
}

/* Ghost button; 44px tall for the target size, the bar is 64. */
.set-sticky-bar__top {
  flex: none;
  min-height: 44px;
  margin-left: auto;
  padding: 0 14px;
  border: 0;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--colour-text);
  font-family: var(--font-header);
  font-size: 16px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.set-sticky-bar__top:hover {
  background: var(--sh-hover);
}

.set-sticky-bar__top:focus-visible {
  outline: 3px solid var(--colour-focus-ring);
  outline-offset: 0;
}

/* The navbar's floating buttons (nav.css, shown by nav.js while the navbar
   is hidden) would sit on the bar's two ends at top 16px, above it at z 101.
   display: none also drops them from the tab order and the accessibility
   tree whatever aria-hidden nav.js writes. The compound with .is-visible is
   what wins on specificity. While the bar shows there is no theme toggle on
   screen; the navbar (and its toggle) is one upward scroll away. */
html.has-sticky-bar .sticky-back-btn.is-visible,
html.has-sticky-bar .sticky-theme-toggle.is-visible {
  display: none;
}

/* Laptop, 1024 to 1419px: the row must still fit the content width (922px
   at 1024), so the thumbnail and the progress bar go and the search shrinks. */
@media (max-width: 1419px) {
  .set-sticky-bar {
    gap: 16px;
  }

  .set-sticky-bar__thumb,
  .set-sticky-bar__bar {
    display: none;
  }

  .set-sticky-bar__search {
    flex-basis: 220px;
    min-width: 140px;
  }

  /* The name gives way first (Phase 4's order): at 1024 a One Piece code
     pill (OP-07) and a three-digit goal count pushed Back to Top past the
     bar's edge while the name sat at 96px. */
  .set-sticky-bar__name {
    min-width: 60px;
  }
}

/* Under 1024px (not mocked; README 8e): name, goal count and the chips at
   44px, in two rows. */
@media (max-width: 1023px) {
  .set-sticky-bar {
    flex-wrap: wrap;
    gap: 8px 12px;
    height: auto;
    padding: 8px 12px;
  }

  .set-sticky-bar__code,
  .set-sticky-bar__search,
  .set-sticky-bar__top {
    display: none;
  }

  /* A bar with neither goal nor chips (a guest, or an owner sharing no
     completion stats) would be a landmark holding only the name: it keeps
     Back to Top instead. */
  .set-sticky-bar[data-ssb-minimal] .set-sticky-bar__top {
    display: inline-flex;
    align-items: center;
    margin-left: auto;
  }

  .set-sticky-bar__name {
    /* A zero basis, so a long name (OP14-EB04, ETD01) shrinks and ends in an
       ellipsis beside the goal count or Back to Top instead of taking a row
       of its own; its min-width keeps 96px of it. */
    flex: 1 1 0;
    min-height: 44px;
    line-height: 44px;
  }

  .set-sticky-bar__goal {
    align-items: center;
    min-height: 44px;
  }

  .set-sticky-bar__chips {
    flex: 1 0 100%;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 4px;
    padding: 4px;
  }

  .set-sticky-bar__chips .seg-switch__option {
    justify-content: center;
    min-height: 44px;
    padding: 0 6px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .set-sticky-bar {
    transition: none;
  }
}

/* ── 12b: laptop, 1024-1419px ── */
/* The mock switches at 1365px, but in three columns "Most Expensive Card"
   wraps from 1366 to about 1420px, so the two-column layout takes over
   below 1420 (owner decision, 2026-09-30). */
@media (max-width: 1419px) {
  .set-header {
    grid-template-columns: 200px minmax(0, 1fr) 380px;
  }

  .set-header--no-art {
    grid-template-columns: minmax(0, 1fr) 380px;
  }

  /* Toolbar at 12b: search 240px, the Premium pill becomes the dot. */
  .set-toolbar__search {
    flex-basis: 240px;
    width: 240px;
  }

  .view-switch .view-switch__premium-pill {
    display: none;
  }

  .view-switch .view-switch__premium-dot {
    display: inline-block;
  }

  .set-header--no-panel {
    grid-template-columns: 200px minmax(0, 1fr);
  }

  .set-meta {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px 24px;
  }
}

/* A header with no money row and exactly three facts keeps them on one row
   on a laptop (decision 15); two facts (the One Piece and Palworld set pages:
   Released and Cards) already sit on one row in the two columns above, at
   about 127px each at 1024 (three columns gave them 77px and wrapped both
   values). Four facts or more keep two columns. Its own query, because the
   :has() selector outranks the phone rule's plain .set-meta; under 1024 the
   cells stack beside the art like every header's. */
@media (min-width: 1024px) and (max-width: 1419px) {
  .set-header--no-money .set-meta:has(> :nth-child(3)):not(:has(> :nth-child(4))) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* The narrow end of 12b: a cell is about 127px at 1024, where a 25px
   "€14,767.10" would run into the next column. The mobile money size fits. */
@media (min-width: 1024px) and (max-width: 1130px) {
  .set-meta__value {
    font-size: 20px;
  }
}

/* ── 8d: mobile, under 1024px ── */
@media (max-width: 1023px) {
  .set-header,
  .set-header--no-panel {
    grid-template-columns: 96px minmax(0, 1fr);
    grid-template-rows: none;
    grid-template-areas:
      "head    head"
      "facts   facts"
      "art     meta"
      "price   price"
      "actions actions"
      "panel   panel";
    gap: var(--space-16);
  }

  .set-header--no-art,
  .set-header--no-art.set-header--no-panel {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "head"
      "facts"
      "meta"
      "price"
      "actions"
      "panel";
  }

  .set-header__factsline {
    display: block;
  }

  .set-header__art {
    width: 96px;
    height: 134px;
    border-radius: 12px;
  }

  .set-header__logo img {
    padding: var(--space-8);
  }

  .set-meta {
    grid-template-columns: minmax(0, 1fr);
    align-self: center;
    gap: var(--space-8);
  }

  /* The facts line carries these on mobile; Most Expensive Card is dropped. */
  .set-meta__cell--fact {
    display: none;
  }

  .set-meta__cell {
    gap: 2px;
  }

  .set-meta__value {
    font-size: 20px;
  }

  .set-delta__period {
    display: none;
  }

  /* No money row: the facts sit beside the art instead of the line. */
  .set-header--no-money .set-header__factsline {
    display: none;
  }

  .set-header--no-money .set-meta__cell--fact {
    display: flex;
  }

  .set-header__price {
    justify-self: start;
    text-align: left;
  }

  .set-panel {
    padding: var(--space-16);
    gap: var(--space-12);
  }

  .set-panel__top {
    flex-direction: column;
    align-items: stretch;
  }

  /* Full-width switch first, one column per tier. */
  .goal-switch {
    order: -1;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
  }

  .goal-switch__option {
    min-height: 44px;
    padding: 0 4px;
  }

  .tier-row {
    grid-template-columns: 100px minmax(0, 1fr) 64px;
    gap: 10px;
  }

  .set-header .ttcg-textlink,
  .set-panel__premium {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* ── Toolbar under 1024px (8d): search, the chips in a four-column grid,
   then Sort beside the View menu. Nothing scrolls sideways. ── */
@media (max-width: 1023px) {
  .set-toolbar {
    flex-direction: column;
    align-items: stretch;
    gap: 16px;
    padding-top: 16px;
  }

  .set-toolbar__left {
    flex-direction: column;
    align-items: stretch;
    gap: 16px;
  }

  .set-toolbar__search {
    /* The laptop rule's flex-basis would be a HEIGHT in this column stack. */
    flex-basis: auto;
    width: 100%;
  }

  .filter-chips {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 4px;
    padding: 4px;
  }

  .filter-chips__chip {
    justify-content: center;
    gap: 4px;
    min-height: 44px;
    padding: 0 6px;
  }

  /* A caller's chip set (tiles mode: "Not Started 79", "Starter Deck 36")
     does not fit a quarter column; those chips fill a grid of 120px-or-wider
     columns (two at 390, so four chips sit two by two), any chip count, each
     a 44px target. A wrapping flex row left a lone fourth chip on its own
     full-width row. */
  .set-toolbar[data-tb-mode="tiles"] .filter-chips {
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  }

  .set-toolbar[data-tb-mode="tiles"] .filter-chips__chip {
    padding: 0 8px;
  }

  /* A header without a money row shows its fact cells on a phone (the facts
     line stands in only when the money row hides them), and a linked fact
     (Newest Set, Most Valuable Collection) is a 44px target there. */
  .set-meta__link {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 4px;
    min-height: 44px;
  }

  .set-toolbar__right {
    flex-wrap: nowrap;
    justify-content: space-between;
  }

  .set-toolbar__right .tb-menu[data-tb-menu="sort"] {
    flex: 1 1 auto;
    min-width: 0;
  }

  .set-toolbar__right .tb-menu[data-tb-menu="sort"] .tb-menu__button {
    width: 100%;
    justify-content: space-between;
  }

  .set-toolbar__right .tb-menu[data-tb-menu="sort"] .tb-menu__value {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .view-switch {
    display: none;
  }

  .tb-menu--view {
    display: block;
  }
}
