/* perennial — listing
   Category and tag archives, product search results, and the shop once it is
   paged or filtered. Everything here is type, 1px hairlines and whitespace:
   no badges, no ratings, no sold counters, no sale banners, no carousels, no
   shadows except the filter sheet, and no radius but 2px on the inputs and the
   full round on pills and dots. Measurements come from the drawn frames. */

/* The design's breakpoints are 600 / 900 / 1200, half a step from the theme's
   768 / 1024. The gutters are the same three values, so we move the steps
   rather than the numbers. */
.pn-listing {
	--pn-gutter: var(--page-gutter);
	/* The design's meta size. The token scale steps 12 → 14 and this page leans
	   on 13 for the breadcrumb, the attribute line, the per-unit price and every
	   count. Declared here rather than added to the tokens, which are verbatim
	   from the design system. */
	--pn-meta: 13px;
}

@media (min-width: 600px) {
	.pn-listing { --pn-gutter: 40px; }
}

@media (min-width: 900px) {
	.pn-listing { --pn-gutter: var(--page-gutter-lg); }
}

.pn-listing__body {
	padding-top: var(--space-7);
	padding-bottom: var(--space-8);
}

/* WooCommerce's own notices, printed by the template because this page does not
   run the archive hooks. Empty on every load but the one after a no-JavaScript
   add to cart, so it must take no room when it has nothing to say. */
.pn-listing__notices:not(:empty) { margin-bottom: var(--space-5); }

/* ── 1 · Category header ───────────────────────────────────────────────── */
.pn-listing__head {
	display: flex;
	flex-direction: column;
	gap: 20px;
}

.pn-crumbs__list {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
	font-size: var(--pn-meta);
	color: var(--text-secondary);
}
.pn-crumbs__item { display: inline-flex; align-items: center; gap: var(--space-2); }
.pn-crumbs__sep { color: var(--stone-400); }
.pn-crumbs__link { transition: opacity var(--dur-fast) var(--ease-out); }
.pn-crumbs__link:hover { opacity: var(--hover-opacity); }
.pn-crumbs__current { color: var(--text-primary); }

.pn-listing__title {
	font-family: var(--font-serif);
	font-weight: var(--weight-regular);
	font-size: var(--pn-text-h2);
	line-height: 1.3;
	letter-spacing: 0.02em;
}

/* Three lines on a phone, whatever the category description says, so the first
   card's image is on screen at 390×844. The CMS cannot push the grid down. */
.pn-listing .pn-listing__lead {
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
	max-width: 34em;
	margin-top: var(--space-1);
	font-size: var(--text-small);
	line-height: 1.85;
	color: var(--text-secondary);
}

@media (min-width: 600px) {
	.pn-listing .pn-listing__lead { font-size: 15px; }
}

@media (min-width: 900px) {
	.pn-listing .pn-listing__lead { font-size: var(--text-body); }
}

/* ── 2 · Sub-category chips ────────────────────────────────────────────── */
/* Under 600 the row scrolls sideways and bleeds into the gutter, so a half-cut
   chip says there is more to the right. Momentum only — no arrows, no snap. */
.pn-listing__subnav {
	margin-top: 40px;
	padding-bottom: 28px;
}

.pn-listing .pn-subnav {
	display: flex;
	gap: var(--space-3);
	overflow-x: auto;
	margin-inline: calc(-1 * var(--pn-gutter));
	padding-inline: var(--pn-gutter);
	scrollbar-width: none;
	-webkit-overflow-scrolling: touch;
}
.pn-listing .pn-subnav::-webkit-scrollbar { display: none; }
.pn-subnav__item { flex: none; }

.pn-listing .pn-chip {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	min-height: 40px;
	padding: var(--space-2) 14px;
	border: var(--line-weight) solid var(--line-hair);
	border-radius: var(--radius-full);
	font-size: var(--text-small);
	line-height: 1.4;
	white-space: nowrap;
	color: var(--text-primary);
	transition:
		background var(--dur-fast) var(--ease-out),
		border-color var(--dur-fast) var(--ease-out),
		color var(--dur-fast) var(--ease-out);
}
.pn-listing .pn-chip:hover { background: var(--bg-subtle); }
.pn-listing .pn-chip.is-current {
	background: var(--green-950);
	border-color: var(--green-950);
	color: var(--paper-000);
}
.pn-chip__count { color: var(--text-tertiary); }

@media (min-width: 600px) {
	.pn-listing .pn-subnav {
		flex-wrap: wrap;
		overflow: visible;
		margin-inline: 0;
		padding-inline: 0;
	}
	.pn-listing .pn-chip { padding: var(--space-2) var(--space-4); }
}

/* ── 3 · Toolbar ───────────────────────────────────────────────────────── */
.pn-listing__toolbar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	padding-block: var(--space-4);
	border-top: var(--border-hair);
	border-bottom: var(--border-hair);
}

/* With no chips above it, the band still needs air under the header block. */
.pn-listing__head + .pn-listing__toolbar { margin-top: 40px; }

.pn-listing__count {
	font-size: var(--text-small);
	color: var(--text-secondary);
	white-space: nowrap;
}

/* The five dropdowns. Hidden until 900, where the sheet does the same job. */
.pn-listing__facets { display: none; }

.pn-facet { position: relative; }
.pn-facet__trigger {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	min-height: 44px;
	padding: 0 14px;
	font-size: var(--text-small);
	color: var(--text-secondary);
	white-space: nowrap;
	cursor: pointer;
	list-style: none;
	transition: color var(--dur-fast) var(--ease-out);
}
.pn-facet__trigger::-webkit-details-marker { display: none; }
.pn-facet__trigger:hover,
.pn-facet__trigger.is-on { color: var(--text-primary); }
.pn-facet__caret { font-size: 10px; color: var(--stone-500); }

.pn-facet__panel {
	position: absolute;
	top: 46px;
	left: 0;
	z-index: 5;
	min-width: 232px;
	padding-block: var(--space-2);
	background: var(--paper-000);
	border: var(--border-strong);
	border-radius: var(--radius-none);
}

.pn-listing .pn-facet__option {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	min-height: 44px;
	padding: 0 var(--space-4);
	font-size: var(--text-small);
	color: var(--text-primary);
	transition: background var(--dur-fast) var(--ease-out);
}
.pn-listing .pn-facet__option:hover { background: var(--bg-subtle); }
/* Zero-result options stay in the list rather than vanishing — a facet that
   reflows under her thumb is worse than one with a nought in it. */
.pn-listing .pn-facet__option.is-empty { opacity: 0.4; pointer-events: none; }
.pn-facet__label { flex: 1; }
.pn-facet__num { font-size: var(--pn-meta); color: var(--text-tertiary); }

.pn-facet__box,
.pn-fsheet__box {
	display: flex;
	align-items: center;
	justify-content: center;
	flex: none;
	width: 16px;
	height: 16px;
	border: var(--border-strong);
	border-radius: var(--radius-sm);
	font-size: 11px;
	line-height: 1;
	color: var(--paper-000);
}
.pn-facet__option.is-selected .pn-facet__box,
.pn-fsheet__option.is-selected .pn-fsheet__box { background: var(--green-950); }

/* 篩選 — the sheet's trigger. Reads 更多篩選 between 900 and 1200, where three
   dropdowns are inline and the rest are in the sheet. */
.pn-listing .pn-listing__filter {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	min-height: 44px;
	padding: 0 var(--space-1);
	font-size: var(--text-small);
	color: var(--text-primary);
	white-space: nowrap;
	transition: opacity var(--dur-fast) var(--ease-out);
}
.pn-listing .pn-listing__filter:hover { opacity: var(--hover-opacity); }
.pn-listing__filter-more { display: none; }
.pn-listing__filter-count { color: var(--text-tertiary); }

.pn-listing__sort {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	white-space: nowrap;
}
/* The visible 排序 label is gone — the chosen option names the control — but the
   <label> is still there, off screen, so the select keeps an accessible name. */
.pn-listing .pn-listing__sort-select {
	min-height: 44px;
	padding: 10px var(--space-3);
	line-height: 1.5;
	background: var(--paper-000);
	border: var(--border-hair);
	border-radius: var(--radius-sm);
	font-family: var(--font-sans);
	font-size: var(--text-small);
	color: var(--text-primary);
	cursor: pointer;
}
/* The submit button is the control until the script arrives. */
.pn-listing.is-live .pn-listing__sort-go { display: none; }
.pn-listing .pn-listing__sort-go {
	min-height: 44px;
	padding: 0 var(--space-3);
	font-size: var(--pn-meta);
	color: var(--text-link);
	cursor: pointer;
}

/* Below 600 the band is 篩選 / count / 排序, and the sort label goes: the
   select says what it is. */
@media (max-width: 599px) {
	.pn-listing__toolbar { justify-content: space-between; padding-block: var(--space-2); }
	.pn-listing__count { order: 2; font-size: var(--pn-meta); }
	.pn-listing .pn-listing__filter { order: 1; }
	.pn-listing__sort { order: 3; }
	.pn-listing .pn-listing__sort-select {
		padding: 10px var(--space-2);
		border-color: transparent;
	}
}

/* Between 600 and 900 the dropdowns are gone and only three things are left.
   The count belongs alone on the left; 篩選 belongs beside the sort it works
   with, not floating in the middle of the band. */
@media (min-width: 600px) and (max-width: 899px) {
	.pn-listing .pn-listing__count { margin-right: auto; }
}

@media (min-width: 900px) {
	.pn-listing__facets { display: flex; align-items: center; flex: 1; gap: var(--space-1); }
	.pn-listing__filter-all { display: none; }
	.pn-listing__filter-more { display: inline; }
	/* Three inline, the rest in the sheet, until there is room for all five. */
	.pn-facet:nth-child(n + 4) { display: none; }
}

@media (min-width: 1200px) {
	.pn-facet:nth-child(n + 4) { display: flex; }
	.pn-listing .pn-listing__filter { display: none; }
}

/* ── 4 · Applied filters ───────────────────────────────────────────────── */
.pn-listing__applied:not(.is-empty) { padding-top: 20px; }

.pn-applied {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-3);
}
.pn-listing .pn-applied__chip {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	min-height: 34px;
	padding: 7px 12px 7px 14px;
	background: var(--green-950);
	border: var(--line-weight) solid var(--green-950);
	border-radius: var(--radius-full);
	font-size: var(--pn-meta);
	color: var(--paper-000);
	transition: opacity var(--dur-fast) var(--ease-out);
}
.pn-listing .pn-applied__chip:hover { opacity: var(--hover-opacity); }
.pn-applied__x { opacity: 0.7; }
.pn-listing .pn-applied__clear { margin-left: var(--space-2); font-size: var(--pn-meta); }

/* ── 5 · The grid ──────────────────────────────────────────────────────── */
.pn-listing__results {
	margin-top: var(--space-7);
	transition: opacity var(--dur-fast) var(--ease-out);
}
/* While a filter is being applied: dim, and hold the height. No skeletons —
   forty product skeletons is a light show, and dimming keeps her scroll still. */
.pn-listing__results.is-busy { opacity: 0.5; pointer-events: none; }

/* The row gap is the only thing separating one card from the next now that the
   hairline is gone, so it has to stay clearly wider than anything inside a card
   — the widest of those is the 15px between the image and the name. It does not
   have to be as wide as it was: 56px was sized to give a rule air on both sides,
   and there is no rule. */
.pn-listing__grid {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	column-gap: var(--space-3);
	row-gap: var(--space-5);
}

/* The buy row has a hard floor: two 44px stepper keys, a field, 6px and a 44px
   button need 154px of column, which two-up stops giving at about 375. Below
   that the grid goes to one column rather than shrink a touch target — the card
   stays exactly the card, it just gets a row to itself. The handoff dropped the
   stepper on a phone for this reason; one column keeps it and keeps the card
   identical at every width. */
@media (max-width: 374px) {
	.pn-listing__grid { grid-template-columns: minmax(0, 1fr); }
}

@media (min-width: 600px) {
	.pn-listing__grid { column-gap: var(--space-4); row-gap: 28px; }
}

@media (min-width: 900px) {
	.pn-listing__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); row-gap: var(--space-6); }
}

@media (min-width: 1200px) {
	.pn-listing__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ── 6 · The card ──────────────────────────────────────────────────────── */
/* No rule under the card and no box around it. What separates one card from the
   next is the row gap, and what holds the grid together is that every image
   starts at the same line and every buy row ends at one.

   The <li> stretches to the row's height; the price block takes `margin-top:
   auto`, so the price and the buy row settle at the bottom of the tallest card
   in the row without the name having to reserve space it may not need. */
.pn-listing__item { display: flex; }

.pn-lcard {
	flex: 1;
	display: flex;
	flex-direction: column;
	gap: 10px;
	height: 100%;
}

.pn-lcard__media { display: block; }
/* 3:4 rather than 4:5. A narrower column gap and a taller frame together are
   what make the photograph the card, which is the whole point of a shop grid —
   the source crops are 4:5, and `object-fit: cover` takes ~9px off each side
   until they are re-cut. The home page and the 商店 landing are still 4:5. */
.pn-lcard .pn-media { aspect-ratio: 3 / 4; background: var(--paper-200); }
.pn-lcard .pn-media__img { transition: opacity var(--dur-fast) var(--ease-out); }
.pn-lcard:hover .pn-media__img { opacity: var(--hover-opacity); }
/* Out of stock keeps its place in the grid and keeps its price — she still
   wants to know. The image steps back; the card is never greyed out whole. */
.pn-lcard.is-out .pn-media { opacity: 0.4; }

.pn-lcard__body {
	display: flex;
	flex-direction: column;
	flex: 1;
	gap: var(--space-1);
}

/* Two lines then an ellipsis — but only as many as the name needs. Reserving
   both lines in every card left 31px of empty paper under every one-line name,
   which is most of them, and that hole is what made the grid read as loose.
   The buy row still lines up across the row; the price block's `margin-top:
   auto` does that job, and it does it without spending space on names that are
   not there. */
.pn-lcard__name {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	font-family: var(--font-serif);
	font-weight: var(--weight-regular);
	font-size: var(--text-body);
	/* The leading does more for this than the size does. goop sets a 20px serif
	   on a 24px line — 1.2 — and that tightness is most of what reads as
	   editorial. 1.2 is too tight for Chinese, whose glyphs fill the em box;
	   1.4 is the floor that stays comfortable. */
	line-height: 1.4;
	letter-spacing: 0.02em;
}
.pn-lcard__link { transition: color var(--dur-fast) var(--ease-out); }
.pn-lcard:hover .pn-lcard__link { color: var(--text-link); }

.pn-lcard__attrs {
	font-size: var(--text-caption);
	line-height: 1.7;
	letter-spacing: 0.02em;
	color: var(--text-secondary);
}

/* Price and buy row together, so a phone can put them on one line. */
.pn-lcard__foot {
	display: flex;
	flex-direction: column;
	margin-top: auto;
	padding-top: var(--space-3);
}

.pn-listing .pn-lcard__price {
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.pn-lcard__amount {
	font-size: var(--text-small);
	font-weight: var(--weight-medium);
	color: var(--text-primary);
}
.pn-lcard__amount del { color: var(--text-tertiary); }
.pn-lcard__unit { font-size: var(--text-caption); color: var(--text-tertiary); }

.pn-lcard__buy {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	min-height: 44px;
	margin-top: var(--space-1);
}
.pn-lcard__out { font-size: var(--pn-meta); color: var(--text-tertiary); }
.pn-listing .pn-lcard__notify { font-size: var(--pn-meta); }

.pn-lcard__form {
	display: flex;
	align-items: center;
	gap: 4px;
	width: 100%;
	min-width: 0;
}

@media (min-width: 600px) {
	.pn-lcard__form { gap: var(--space-3); }
}

.pn-listing .pn-stepper {
	display: flex;
	align-items: center;
	height: 44px;
	border: var(--border-hair);
	border-radius: var(--radius-sm);
}
.pn-listing .pn-stepper__btn {
	display: none;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 42px;
	font-size: var(--text-small);
	color: var(--text-secondary);
	cursor: pointer;
	transition: color var(--dur-fast) var(--ease-out);
}
.pn-listing .pn-stepper__btn:hover { color: var(--text-primary); }
.pn-listing.is-live .pn-stepper__btn { display: flex; }
.pn-listing .pn-stepper__value {
	width: 3ch;
	min-width: 22px;
	padding: 0;
	background: none;
	border: 0;
	border-radius: 0;
	font-family: var(--font-sans);
	font-size: var(--text-small);
	text-align: center;
	color: var(--text-primary);
	-moz-appearance: textfield;
	appearance: textfield;
}
.pn-listing .pn-stepper__value::-webkit-outer-spin-button,
.pn-listing .pn-stepper__value::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* Without the script the field is the control, so it has to look typeable. */
.pn-listing:not(.is-live) .pn-stepper { flex: 1; justify-content: center; }
.pn-listing:not(.is-live) .pn-stepper__value { width: 100%; min-height: 42px; }

.pn-listing .pn-lcard__add {
	display: flex;
	align-items: center;
	justify-content: center;
	flex: none;
	width: 44px;
	height: 44px;
	color: var(--text-primary);
	cursor: pointer;
	transition:
		color var(--dur-fast) var(--ease-out),
		transform var(--dur-fast) var(--ease-out);
}
.pn-listing .pn-lcard__add:hover { color: var(--text-link); }
.pn-listing .pn-lcard__add:active { transform: scale(var(--press-scale)); }
.pn-lcard__add .pn-icon { color: currentColor; }

/* Added: the 44px row is replaced where it stands, at the same height, so
   nothing moves and her scroll is untouched. Four seconds, then back. */
.pn-lcard__added {
	display: none;
	align-items: center;
	gap: var(--space-2);
	font-size: var(--text-small);
	transition: opacity var(--dur-base) var(--ease-out);
}
.pn-lcard__added-cart { color: var(--text-secondary); }
.pn-listing.is-live .pn-lcard.is-added .pn-lcard__form { display: none; }
.pn-listing.is-live .pn-lcard.is-added .pn-lcard__added { display: flex; }

/* An add that did not happen says so in the same row, at the same height, and
   then gets out of the way. No dot, because there is nothing to mark. */
.pn-lcard.has-error .pn-lcard__added { color: var(--error); }
.pn-lcard.has-error .pn-lcard__added .pn-dot { display: none; }

/* One buy row at every width: the stepper and the cart button, side by side,
   left-aligned with everything above them. It does fit two-up on a phone —
   44 + value + 44 for the stepper, 8 for the gap and 44 for the button is 160
   of the 165 a 390 column gives — so the quantity field is what flexes as the
   screen narrows, and the four touch targets stay 44px wide everywhere. */
.pn-listing .pn-lcard__stepper { flex: 1; min-width: 0; }
.pn-listing .pn-stepper__value { flex: 1; width: 0; min-width: 14px; }

/* The stepper only flexes in the two-up phone band, where the column is tight.
   One column (under 375) and 600-and-up both have room, so there it keeps the
   natural size it has everywhere else. Both cases in one query because a media
   query adds no specificity — only source order settles this against the rule
   above, and this has to come after it. */
@media (max-width: 374px), (min-width: 600px) {
	.pn-listing .pn-lcard__stepper { flex: none; }
	.pn-listing .pn-stepper__value { flex: none; width: 3ch; }
}

@media (min-width: 600px) {
	.pn-lcard { gap: 12px; }
	.pn-lcard__body { gap: 4px; }
	.pn-lcard__name { font-size: 16px; }
	.pn-lcard__attrs { font-size: var(--pn-meta); }
	.pn-listing .pn-lcard__price { flex-direction: row; align-items: baseline; gap: 10px; }
	/* A step above the meta text and a step below the name: the price is the
	   number she is comparing, and on a phone 15 would crowd a 16px name. */
	.pn-lcard__amount { font-size: 15px; }
	.pn-lcard__unit { font-size: var(--pn-meta); }
}

@media (min-width: 1200px) {
	.pn-lcard { gap: 14px; }
	.pn-lcard__body { gap: 5px; }
	/* 17, not 20: the category name is the voice on this page, and a 20px serif
	   in every one of sixteen cards was arguing with it. */
	.pn-lcard__name { font-size: 17px; }
	.pn-lcard__amount { font-size: 15px; }
}

/* ── 7 · 載入更多 ──────────────────────────────────────────────────────── */
.pn-listing__more {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-3);
	padding-top: var(--space-7);
}
.pn-listing__shown { font-size: var(--text-caption); color: var(--text-tertiary); }

@media (min-width: 900px) {
	.pn-listing__more { gap: var(--space-4); padding-top: 72px; }
	.pn-listing__shown { font-size: var(--pn-meta); }
}

/* ── Nothing found ─────────────────────────────────────────────────────── */
.pn-empty {
	display: flex;
	flex-direction: column;
	gap: 14px;
	max-width: 34em;
	padding: var(--space-6) 0 var(--space-7);
}
.pn-empty__title {
	font-family: var(--font-serif);
	font-weight: var(--weight-regular);
	font-size: 22px;
	line-height: 1.4;
}
.pn-empty__lead {
	font-size: var(--text-small);
	line-height: 1.85;
	color: var(--text-secondary);
}
.pn-empty__actions { display: flex; flex-wrap: wrap; gap: var(--space-5); margin-top: var(--space-1); }
.pn-listing .pn-empty__actions .pn-link { min-height: 44px; display: inline-flex; align-items: center; }
.pn-empty__nearby {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	margin-top: var(--space-5);
	padding-top: var(--space-5);
	border-top: var(--border-hair);
}
.pn-listing .pn-empty__chips { flex-wrap: wrap; overflow: visible; margin-inline: 0; padding-inline: 0; }

@media (min-width: 900px) {
	.pn-empty { gap: var(--space-4); padding: 72px 0; }
	.pn-empty__title { font-size: var(--text-h3); }
	.pn-empty__lead { font-size: var(--text-body); }
}

/* ── 8 · One editorial link ────────────────────────────────────────────── */
.pn-listing__mag {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--space-4);
	margin-top: var(--space-9);
	padding-top: 28px;
	border-top: var(--border-hair);
}
.pn-listing .pn-listing__mag-link { font-size: 15px; }

/* ── The filter sheet ──────────────────────────────────────────────────── */
/* Without the script this is an ordinary block of filters at the foot of the
   page and 篩選 is an anchor to it. Every option is a link either way. */
.pn-fsheet {
	margin-top: var(--space-8);
	padding-top: var(--space-5);
	border-top: var(--border-hair);
}
.pn-fsheet__scrim,
.pn-fsheet__close { display: none; }

.pn-fsheet__head { margin-bottom: var(--space-4); }
.pn-fsheet__title {
	font-family: var(--font-serif);
	font-weight: var(--weight-regular);
	font-size: var(--pn-text-h3);
	line-height: 1.3;
}

.pn-fsheet__group {
	padding-block: 20px;
	border-bottom: var(--border-hair);
}
.pn-fsheet__group .pn-eyebrow { margin-bottom: 10px; }
.pn-fsheet__options { display: flex; flex-direction: column; }

.pn-listing .pn-fsheet__option {
	display: flex;
	align-items: center;
	gap: 14px;
	min-height: 44px;
	font-size: 15px;
	color: var(--text-primary);
	transition: opacity var(--dur-fast) var(--ease-out);
}
.pn-listing .pn-fsheet__option:hover { opacity: var(--hover-opacity); }
.pn-listing .pn-fsheet__option.is-empty { opacity: 0.4; pointer-events: none; }
.pn-fsheet__box { width: 18px; height: 18px; font-size: 12px; }
.pn-fsheet__label { flex: 1; }
.pn-fsheet__num { font-size: var(--pn-meta); color: var(--text-tertiary); }

.pn-fsheet__foot {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	padding-top: var(--space-4);
}
.pn-listing .pn-fsheet__clear { min-height: 44px; display: inline-flex; align-items: center; font-size: var(--text-small); }
.pn-listing .pn-fsheet__done { flex: 1; min-height: 48px; border-radius: var(--radius-full); }

/* With the script, and only up to 1200, it is a sheet: full height from under
   the header, over a forest scrim. The one shadow in the whole design. */
.pn-listing.is-live .pn-fsheet {
	position: fixed;
	inset: 0;
	z-index: 90;
	display: none;
	margin: 0;
	padding: 0;
	border: 0;
}
.pn-listing.is-live .pn-fsheet.is-open { display: block; }
.pn-listing.is-live .pn-fsheet__scrim {
	display: block;
	position: absolute;
	inset: 0;
	background: var(--surface-overlay);
}
.pn-listing.is-live .pn-fsheet__panel {
	position: absolute;
	inset: 64px 0 0;
	display: flex;
	flex-direction: column;
	background: var(--paper-000);
	box-shadow: var(--shadow-overlay);
}
.pn-listing.is-live .pn-fsheet__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex: none;
	height: 60px;
	margin: 0;
	padding-inline: var(--pn-gutter);
	border-bottom: var(--border-hair);
}
.pn-listing.is-live .pn-fsheet__close {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	margin-right: calc(-1 * var(--space-2));
	cursor: pointer;
}
.pn-listing.is-live .pn-fsheet__body {
	flex: 1;
	overflow-y: auto;
	padding: var(--space-2) var(--pn-gutter) var(--space-5);
	-webkit-overflow-scrolling: touch;
}
.pn-listing.is-live .pn-fsheet__foot {
	flex: none;
	padding: var(--space-4) var(--pn-gutter) var(--space-5);
	border-top: var(--border-hair);
}

/* Above 1200 every facet is inline, so there is no sheet — and .is-open must
   lose here too, or a sheet opened at 900 survives the resize. */
@media (min-width: 1200px) {
	.pn-listing.is-live .pn-fsheet,
	.pn-listing.is-live .pn-fsheet.is-open { display: none; }
}

/* ── Reduced motion ────────────────────────────────────────────────────── */
/* One deliberate moment on this page — the 已加入 cross-fade — and it goes.
   The state still changes, instantly. Hover opacity stays: nothing here needs
   motion to be understood. */
@media (prefers-reduced-motion: reduce) {
	.pn-lcard__added,
	.pn-listing__results,
	.pn-lcard .pn-media__img { transition: none; }
}
