/*
 * The result card.
 *
 * The most reused thing on the site: the browse page today, and every archive
 * that follows. So it is a stylesheet and a set of classes rather than fifty
 * element settings, for the reason controls.css exists - an element setting
 * emits as `#brxe-{id}

` and cannot be overridden by anything, including by the
 * next person editing the same card in the builder.
 *
 * ⚠️ ONE link in the markup, not two.
 *
 * The title's anchor is stretched over the whole card with a pseudo-element,
 * so clicking the image, the padding or the notes all follow the title. The
 * naive version - wrapping the image in its own <a> to the same URL - gives a
 * screen reader two adjacent links to the same place with different names, and
 * doubles the tab stops on a page of twelve cards from twelve to twenty-four.
 *
 * Anything inside the card that is separately clickable, the brand link, has to
 * lift itself back above that overlay. That is the one cost of the pattern and
 * it is why .oview-card__parent carries a z-index it otherwise would not need.
 */

/*
 * ⚠️ NEVER WRAPS. A CARD IS A STACK, AND BRICKS DISAGREES BY DEFAULT.
 *
 * `.brxe-block` ships `display: flex; flex-direction: column; flex-wrap: wrap`,
 * and a COLUMN flex that wraps puts its overflow in a second COLUMN beside the
 * first. So the moment a card's content is taller than the grid row it was
 * given, the body leaves the card sideways.
 *
 * On a phone that is not hypothetical: the media renders square, so at a 295px
 * card it is 257px tall, while the grid row was sized at 183. The body wrapped
 * out to x=330 - outside a card spanning 40 to 335 - and `check-mobile.js`
 * reported it as `right=494 vs 375`.
 *
 * ⚠️ It cost a wrong diagnosis first. No stylesheet sets `flex-wrap` on the
 * card, so the rule looked absent; it comes from the framework. And it only
 * showed up under the denser grid, which made the card narrow enough for the
 * square media to exceed the row - so it read as a grid bug for an hour.
 */
.oview-card {
	flex-wrap: nowrap;
}

/*
 * ⚠️ A NAME LONGER THAN THE CARD BREAKS, IT DOES NOT ESCAPE.
 *
 * `Dolce&Gabbana` and `Coco Mademoiselle` are single words wider than a 140px
 * card, and the default `overflow-wrap: normal` refuses to break inside a word.
 * The card sets `overflow: hidden`, so the page never scrolled sideways and the
 * mobile gate stayed green: the name was simply sliced off mid-letter.
 *
 * ⚠️ `min-width: 0` is the load-bearing half. A flex and grid child's automatic
 * minimum size is its content, so the body would not shrink below the longest
 * word no matter what the wrapping said.
 *
 * `break-word` rather than `anywhere`: it only breaks a word that cannot fit,
 * so nothing on a wider screen changes.
 */
.oview-card__body,
.oview-card__meta {
	min-width: 0;
}

.oview-card__title,
.oview-card__parent,
.oview-card__fact,
.oview-card__marker {
	overflow-wrap: break-word;
}

.oview-card {
	/*
	 * The card's accent, off by default.
	 *
	 * A custom property rather than a colour, because WHICH colour is the
	 * dataset's business and this file must not know: the fragrance plugin sets
	 * it per gender, and a dataset that sets nothing gets a line it cannot see
	 * rather than a rule it has to override.
	 */
	--card-accent: transparent;
	position: relative;
	display: flex;
	flex-direction: column;
	/*
	 * ⚠️ Bricks gives a block `align-items: flex-start`, which shrinks every
	 * child to its own content width. It is the trap design.md already records
	 * for containers, and it hid here until the notes came off the card: the
	 * body had been as wide as its longest line of text, so it LOOKED full
	 * width. With the notes gone it collapsed to 114px inside a 167px card and
	 * the gender mark stopped reaching the right edge.
	 */
	align-items: stretch;
	overflow: hidden;
	border: 1px solid var(--color-border);
	border-radius: var(--radius-l-2);
	background: var(--color-surface);
	transition: border-color 150ms ease, box-shadow 150ms ease;
}

/*
 * Border only, no shadow. Nothing else in this build casts one - not the facet
 * cards, not the controls - and one card that lifts is a different design
 * language on the same page. If shadows arrive they arrive everywhere at once.
 */
.oview-card:hover,
.oview-card:focus-within {
	border-color: var(--color-border-strong);
}

/*
 * The bottle grows 5% under the pointer. Small on purpose: enough that the card
 * feels live, not enough to be an animation someone has to wait for.
 *
 * It works because the card is `overflow: hidden` - the image is already inset
 * by its own padding, so at 1.05 it fills a little more of the same box rather
 * than spilling over the border.
 */
.oview-card:hover .oview-card__media,
.oview-card:focus-within .oview-card__media {
	transform: scale(1.05);
}

/*
 * Anyone who has asked their system for less movement gets none. This is the
 * only thing on the site that moves, so honouring it costs one rule.
 */
@media (prefers-reduced-motion: reduce) {
	.oview-card__media {
		transition: none;
	}

	.oview-card:hover .oview-card__media,
	.oview-card:focus-within .oview-card__media {
		transform: none;
	}
}

/*
 * A hairline along the bottom edge, fading out at both ends.
 *
 * Two pixels, and the fade is what keeps it from reading as a border: a solid
 * line stops somewhere, and the eye notices where. Fading to transparent at
 * both ends means it has no ends.
 *
 * `pointer-events: none` because it sits over the stretched title link and
 * would otherwise take the click along the bottom edge of every card.
 */
.oview-card::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	height: 2px;
	background: linear-gradient(to right, transparent, var(--card-accent), transparent);
	pointer-events: none;
}

/* --- the grid ------------------------------------------------------------ */

/*
 * Five to a row at full width, and fewer as it narrows.
 *
 * `auto-fill` with a floor rather than `repeat(5, 1fr)`: five is the default,
 * not a rule, and a fixed five would be five 67px slivers on a phone rather
 * than two readable cards.
 *
 * Measured, not guessed. The results column is 922px beside a 260px sidebar
 * with a 13.8px gap, so the bands are: 150-165 gives five, 166-210 gives four,
 * 211-290 gives three.
 *
 * ⚠️ In a stylesheet and not on the element, because the list view has to be
 * able to override it. A Bricks element setting emits as `#brxe-{id}

` and no
 * class rule can beat an ID.
 */
.oview-card-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(155px, 1fr));
	gap: var(--space-s);
	margin-top: var(--space-m);
}

/*
 * A denser grid, for a card that carries less.
 *
 * A brand card is a wordmark, a name and a sentence, and it wants the room. A
 * fragrance card is a bottle, a name and a house: the same column width leaves
 * it airy and fits three where four read better.
 *
 * ⚠️ TWO CLASSES, because a promoted frame states `.oview-card-grid` at one
 * class and loads after this file. A modifier at the same weight would lose on
 * source order, which is a rule that is present, correct and inert.
 *
 * `auto-fill` and a minimum, not a fixed four: the number is a consequence of
 * the width, so a narrower column gets three and a phone gets one without
 * anything here counting.
 */
.oview-card-grid.oview-card-grid--dense {
	grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
}

/* --- media --------------------------------------------------------------- */

/*
 * A fixed aspect box, not a fixed height.
 *
 * ⚠️ These rules are ON THE <img>, not on a wrapper. A Bricks image element
 * with no caption and no link renders a bare `<img>` and nothing around it, so
 * a `.oview-card__media img` descendant selector matches nothing at all - the
 * class is on the image itself. Padding still works: it shrinks the content
 * box, and object-fit resolves inside that.
 *
 * Bottles are photographed at wildly different ratios, and the old fixed
 * `height: 180px` made a wide one look squat and a tall one look cropped. A
 * ratio plus `contain` gives every card the same footprint and no bottle its
 * own idea of the layout.
 */
.oview-card__media {
	display: block;
	width: 100%;
	/*
	 * ⚠️ A DEFAULT, NOT A CONSTANT. 4/3 is right for an archive card, where the
	 * shot shares a small tile with a name and three facts. A dashboard listing
	 * what somebody owns is mostly pictures, and a bottle is taller than it is
	 * wide, so that grid asks for a taller box with `--media-ratio` rather than
	 * every grid on the site changing shape.
	 */
	aspect-ratio: var(--media-ratio, 4 / 3);
	padding: var(--space-s);
	object-fit: contain;
	/* Grows on hover. The transition lives here rather than on :hover so the
	   image eases back down on the way out as well as up on the way in. */
	transition: transform 250ms ease;
	/*
	 * White, like the rest of the card. The bottles are photographed on white,
	 * so a sunk panel behind them drew a hard rectangle around every one and no
	 * blend mode hides that edge honestly - multiply lightened the dark bottles
	 * to pay for it. One surface, no seam.
	 */
	background: var(--color-surface);
}

/*
 * The media box before there is a picture in it.
 *
 * Notes and accords have no images yet. This is the reserved box, not a
 * decoration: it inherits every rule above, so it is the same ratio, the same
 * padding and the same 8.4rem square in the list view that a real image will
 * be. Nothing about the layout changes on the day the pictures arrive.
 *
 * Deliberately plain. It is meant to read as "not yet" rather than as a
 * designed empty state, because a convincing empty state is the thing nobody
 * ever gets round to replacing.
 */
.oview-card__media--empty {
	background: var(--color-surface-sunk);
	border-radius: var(--radius-m-2);
	/* Inset so the sunk panel does not touch the card's own edge. The grid
	   view's media sits flush to the top of the card, and a full-bleed grey
	   rectangle there reads as a broken image rather than as a placeholder. */
	margin: var(--space-s) var(--space-s) 0;
	width: auto;
	padding: 0;
}

/*
 * ⚠️ `mix-blend-mode: multiply` is inherited from the list-view rule and has to
 * go. It exists for bottles photographed on white; on a flat grey panel it
 * multiplies the panel into the row and the placeholder disappears on hover,
 * which looks like a rendering bug rather than an empty box.
 */
.ov-view-list .oview-card__media--empty {
	mix-blend-mode: normal;
	margin: 0;
}

/* --- body ---------------------------------------------------------------- */

/*
 * The body fills whatever height the card was given, so the row underneath can
 * be pushed to the bottom of it.
 *
 * ⚠️ This is half the fix. `flex: 1` here makes the body as tall as the card;
 * `margin-top: auto` on the meta row is what drops the row to the foot of that
 * space. Without BOTH, a two-line title pushes the year and the gender mark
 * down and no two cards in a row line up along the bottom - which is exactly
 * how it looked before.
 *
 * The cards themselves are already equal height: they are grid items and a
 * grid stretches its items to the tallest in the row by default.
 */
.oview-card__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: var(--space-2xs);
	/*
	 * Less room at the foot than at the head. The padding was uniform, which is
	 * why the bottom row sat so far off the edge - it was matching the space
	 * above the title, and nothing needs that much air under a four-digit year.
	 */
	padding: var(--space-s) var(--space-s) var(--space-xs);
}

/*
 * ⚠️ Two lines, then an ellipsis, and this is not cosmetic.
 *
 * Cards are grid items and a grid stretches every item in a row to the tallest,
 * so ONE forty-character name made four other cards grow to match it. The
 * clamp is what stops a single outlier resizing its whole row.
 *
 * The full name is still in the DOM - clipped, not removed - so it is still
 * read out and still what the link is called. Only the pixels are missing.
 *
 * ⚠️ No min-height. There was one, reserving two lines so that "a one-word name
 * does not pull its meta row up past everyone else's" - which the pin at the
 * bottom of the body already does on its own. All it actually achieved was a
 * gap of blank line under every short title, so a one-line name floated well
 * above its brand while a two-line one sat tight against it.
 */
.oview-card__title {
	margin: 0;
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	overflow: hidden;
	text-align: center;
	font-size: var(--text-m);
	font-weight: 600;
	/* 1.2, not 1.3. The leading under a title is space between it and the brand
	   that belongs to it, and at this size 1.3 was reading as a paragraph gap. */
	line-height: 1.2;
	color: var(--color-text);
}

.oview-card__title a {
	color: inherit;
	text-decoration: none;
}

/* The whole card, from one anchor. See the note at the top of this file. */
/*
 * ⚠️ BOTH SHAPES OF TITLE, because a card can be built either way and only one
 * of them worked. An archive card is a heading with a link INSIDE it; a card
 * built from a Bricks text element with a link setting IS the anchor, carrying
 * `.oview-card__title` on the `<a>` itself. The second matched nothing here, so
 * on the account page the bottle and the whole card body were dead and only the
 * five characters of the name could be clicked.
 */
.oview-card__title a::after,
a.oview-card__title::after {
	content: "";
	position: absolute;
	inset: 0;
	/* Above the card's own content, below anything that lifts itself out. */
	z-index: 0;
}

/*
 * ⚠️ No colour change on the title.
 *
 * The card already says it is hovered twice - the border lifts and the image
 * grows - and a third signal in the accent blue made the title look like a
 * link that had been styled by accident. Hover is a state, not an event: one
 * or two quiet signals, not every element announcing itself.
 */

/* --- meta ---------------------------------------------------------------- */

/*
 * Brand, year, rating - and the separators are the CSS's job, not the text's.
 *
 * ⚠️ This is the fix for `Creed &middot; &middot; 4.18`. The three parts used
 * to be one string with the dots typed between them, so a fragrance with no
 * year printed the gap AND both of its separators. Now each part is its own
 * element, an empty one is removed by :empty, and the separator hangs off
 * whichever parts survive.
 *
 * :empty is exact - it matches an element with no child nodes at all, which is
 * precisely what a Bricks text element renders when its tag resolves to
 * nothing. One space of stray whitespace in the template would defeat it, so
 * the meta parts must carry a tag and nothing else.
 */
.oview-card__meta {
	display: flex;
	/* The other half of the pin: everything above takes its natural height and
	   this row gets whatever is left, which puts it on the card's bottom edge. */
	margin-top: auto;
	padding-top: var(--space-2xs);
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0 var(--space-2xs);
	font-size: var(--text-xs);
	color: var(--color-text-muted);
}

/*
 * A slot with nothing in it draws no gap and no separator.
 *
 * ⚠️ AN `<img>` IS ALWAYS `:empty`, AND THAT IS NOT WHAT THIS MEANS.
 *
 * `:empty` is about child nodes, and a void element has none by definition. So
 * this hid every country flag on the brands archive: the picture loaded, the
 * `src` was right, the element was in the DOM at the correct place, and it was
 * `display: none` with no rule anywhere that mentions flags. Two separate
 * sessions went looking for the thing hiding `.ov-flag`.
 *
 * The exclusion is by KIND rather than by class, because the next replaced
 * element somebody puts in this row will have the same problem and a different
 * name.
 */
.oview-card__meta > *:empty:not(img):not(svg):not(picture):not(video):not(canvas) {
	display: none;
}

/*
 * A separator on any part that has a visible part before it.
 *
 * `:not(:empty) ~ :not(:empty)` and not `:not(:first-child)`, because a hidden
 * element is still a child: with `:first-child` a card whose brand was missing
 * would open with a leading dot. This says what is actually meant - draw a
 * separator when there is something on both sides of it.
 */
/*
 * ⚠️ The gender mark is excluded. It is in this row so it shares a baseline
 * with the year, but it is not part of the sentence the row makes - a
 * separator in front of it would read as another fact rather than as the
 * card's own label, and it is pushed to the far edge anyway.
 */
.oview-card__meta > *:not(:empty) ~ *:not(:empty):not(.oview-card__marker)::before {
	content: "\00b7";
	margin-inline-end: var(--space-2xs);
	color: var(--color-border);
}

/*
 * The year carries the bottom row, so it is the one thing there with weight.
 * It sits opposite a coloured mark and has to hold its own against it.
 */
/*
 * 500, the same as a facet count. They are the same kind of thing - a small
 * number stated plainly - and they were 600 and 400, which read as two
 * different pieces of typography doing one job.
 */
.oview-card__fact--0 {
	font-weight: 500;
	color: var(--color-text-body);
}

/*
 * Its own line, and its own link - so it has to climb back over the title's
 * stretched overlay to stay clickable.
 *
 * `align-self: flex-start` so the hit area is the width of the NAME, not the
 * width of the card: a full-width invisible link over a card is a click that
 * goes somewhere the reader did not point at.
 *
 * ⚠️ The vertical padding is a tap target, not decoration. At 12px the link
 * measured 19.2px tall and check-mobile.js failed it against WCAG 2.2 SC 2.5.8,
 * correctly - it is a thumb-sized miss on a phone.
 */
.oview-card__parent {
	position: relative;
	z-index: 1;
	/*
	 * Centred with the title, and `align-self` rather than `text-align`, so the
	 * hit area is the width of the NAME. A full-width invisible link across a
	 * card is a click that goes somewhere the reader did not point at.
	 */
	align-self: center;
	text-align: center;
	max-width: 100%;
	/*
	 * Pulled back up under the title, by the body's gap AND by its own top
	 * padding.
	 *
	 * That padding is a TAP TARGET, not spacing, but the flow cannot tell the
	 * difference - it stacked on the gap and left the brand floating under a
	 * name it belongs to. Both are cancelled here, so the brand sits where it
	 * looks like it should while the hit area stays exactly as large.
	 */
	margin-top: calc((var(--space-2xs) + 0.4rem) * -1);
	/*
	 * ⚠️ WCAG 2.2 SC 2.5.8 is 24 by 24, and the second number bit after the
	 * first was fixed. Once the link was centred rather than full width its
	 * box became the width of the NAME, and a short one - "Zara" at 12px -
	 * measured 22.9px across. The padding below covers height; this covers a
	 * name too short to be a target on its own.
	 */
	min-width: 2.4rem;
	padding-block: 0.4rem;
	font-size: var(--text-xs);
	line-height: 1.4;
	color: var(--color-text-muted);
	text-decoration: none;
}

/*
 * Underline only, no colour. The card already carries a coloured border on
 * hover and the accent blue was a second, unrelated hue arriving on top of it.
 * An underline is what says "link" anyway; the colour was decoration.
 */
.oview-card__parent:hover,
.oview-card__parent:focus-visible {
	text-decoration: underline;
}

/*
 * ⚠️ A dataset decorates its own facts, and the star is the example.
 *
 * It used to be here, on the second fact - which meant this file knew that a
 * fragrance's second fact is a rating. It lives in the fragrance plugin now,
 * keyed on the per-position class ov_card() emits.
 */

/* --- gender -------------------------------------------------------------- */

/*
 * An icon slot, and the icon is the dataset's business.
 *
 * The word used to be printed here and took a whole line of a small card to
 * say something a shape says instantly. It is still in the DOM - pushed out of
 * sight with text-indent rather than display:none, so it is still read aloud
 * and still the thing that carries the meaning. Colour and shape are the
 * glance; the text is the information (WCAG 1.4.1).
 *
 * Which mask goes in it, and in which colour, is in the fragrance plugin's own
 * stylesheet. Nothing here knows that this dataset has a gender column, let
 * alone what its values are.
 */
.oview-card__marker {
	/*
	 * The right end of the bottom row.
	 *
	 * `margin-inline-start: auto` rather than `justify-content: space-between`
	 * on the row: with space-between, a card that had no year would push the
	 * mark to the LEFT edge, because it would be the only thing there.
	 *
	 * It never took a line of its own - it was absolute in a corner before this,
	 * and in the body flow before that, where an 18px mark cost a whole row of a
	 * 167px card.
	 */
	position: relative;
	margin-inline-start: auto;
	flex: 0 0 auto;
	/*
	 * ⚠️ A short LABEL, because that is what the slot is documented to be.
	 *
	 * This rule used to be `width: 1.6rem; height: 1.6rem; overflow: hidden;
	 * text-indent: -100em` - an icon box with its text pushed off-screen. That
	 * is not a generic marker, it is a gender marker, and it only looked
	 * generic because fragrances were the only dataset using it. The first
	 * archive to put a word in the slot (note groups) rendered a blank grey
	 * square, which is D45 failing in the one file it is not allowed to.
	 *
	 * The icon box now lives in fragrances.css, keyed on the three values that
	 * have icons. A dataset whose marker is a word gets a word.
	 */
	max-width: 50%;
	overflow: hidden;
	color: var(--color-text-muted);
	font-size: var(--text-xs);
	line-height: 1.4;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/*
 * ⚠️ NO `text-align: right` here, and it is not an oversight.
 *
 * It was added with the rest of this block, did nothing, and broke the icon
 * markers: `text-indent: -100em` shifts where a line STARTS, and right
 * alignment then pushes the line back so its END sits at the right edge of the
 * box. The last 16px of "unisex" landed inside a 16px icon box and every
 * fragrance card read **"sex"** under its gender symbol.
 *
 * Measured: box at x=473 width 16, glyph run at x=456 width 33 - ending at
 * exactly 489, the box's right edge.
 *
 * It did nothing because the box is already at the right end of the row
 * (`margin-inline-start: auto`) and shrinks to its own content
 * (`white-space: nowrap`), so there is no spare width for text to align within.
 * A speculative property that broke a working one.
 */

.oview-card__marker:empty {
	display: none;
}

/* --- notes --------------------------------------------------------------- */

/*
 * Two lines, then an ellipsis. A note list runs from two names to twenty, and
 * without a clamp one card in a row of three decided the height of all of them.
 */
.oview-card__summary {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	overflow: hidden;
	font-size: var(--text-xs);
	line-height: 1.5;
	color: var(--color-text-body);
}

.oview-card__summary:empty {
	display: none;
}

/* --- list view ----------------------------------------------------------- */

/*
 * The same markup, read as rows.
 *
 * ⚠️ Not a second card. Every rule below re-lays the elements the grid view
 * already builds - there is no list template, no second $show map and no second
 * set of BEM names, so a change to what a card CONTAINS lands in both views by
 * definition. Two templates would be two places to forget.
 *
 * Switched on a body class, which the view switcher writes from `?ov_view=`
 * (Oview\Bricks\Views). The loop and the grid are siblings, so the class has
 * to hang off a common ancestor and `body` is the only guaranteed one.
 */

/* One continuous surface, and the rows sit inside it. */
.ov-view-list .oview-card-grid {
	display: block;
	border: 1px solid var(--color-border);
	border-radius: var(--radius-l-2);
	background: var(--color-surface);
	overflow: hidden;
}

.ov-view-list .oview-card {
	flex-direction: row;
	align-items: center;
	gap: var(--space-s);
	padding-inline: var(--space-s);
	border-width: 0 0 1px;
	border-radius: 0;
}

/* The last row would otherwise draw a line along the container's own border. */
.ov-view-list .oview-card:last-child {
	border-bottom-width: 0;
}

/*
 * A row lifts its background rather than its border: it has no border left to
 * lift, and a coloured line between rows would read as a divider that had
 * changed its mind.
 */
.ov-view-list .oview-card:hover,
.ov-view-list .oview-card:focus-within {
	border-color: var(--color-border);
	background-color: var(--color-surface-sunk);
}

/*
 * Small and square. A 4:3 box at row height leaves the bottle floating.
 *
 * 8.4rem rather than 6.4: a bottle is a tall object photographed with air above
 * and below it, so at 64px the glass itself was about 40px and the row read as
 * a list of thumbnails rather than of fragrances.
 */
.ov-view-list .oview-card__media {
	flex: 0 0 auto;
	width: 8.4rem;
	height: 8.4rem;
	aspect-ratio: auto;
	padding: var(--space-2xs);
	background: transparent;
	/*
	 * ⚠️ Multiply, which the grid view deliberately does NOT use.
	 *
	 * The bottles are photographed on white. On a white row that is invisible;
	 * the moment a row greys on hover, each one draws a white rectangle around
	 * its bottle. Multiply drops the white into whatever is behind it.
	 *
	 * It was rejected in the grid view for lightening dark bottles - but there
	 * the panel was permanently grey, so the cost was permanent too. Here the
	 * resting row is white, where multiply is a no-op, and it only does any
	 * work on the one row being pointed at.
	 */
	mix-blend-mode: multiply;
}

/*
 * ⚠️ Grid, not flex, and this is what lets one set of markup serve both views.
 *
 * The body holds title, brand and the meta row in that order. A flex row would
 * put all three side by side; a two-column grid with named areas stacks the
 * first two on the left and spans the third down the right, without moving a
 * single element in the DOM.
 */
.ov-view-list .oview-card__body {
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto;
	grid-template-areas:
		"title meta"
		"brand meta";
	align-items: center;
	column-gap: var(--space-m);
	row-gap: 0;
	/*
	 * ⚠️ NO VERTICAL PADDING, BECAUSE THE ROW ALREADY HAS SOME. This carried
	 * `var(--space-m)` top and bottom inside a row padded `var(--space-l)`, so
	 * every list row was 26px of air above and below two lines of text and
	 * stood 99px tall for a 44px bottle. The hover tint made it obvious: it
	 * filled a band half of which had nothing in it.
	 */
	padding: 0;
}

.ov-view-list .oview-card__title {
	grid-area: title;
	align-self: end;
	text-align: start;
	-webkit-line-clamp: 1;
	line-clamp: 1;
}

.ov-view-list .oview-card__parent {
	grid-area: brand;
	align-self: start;
	justify-self: start;
	margin-top: 0;
	text-align: start;
}

.ov-view-list .oview-card__meta {
	grid-area: meta;
	margin-top: 0;
	padding-top: 0;
	gap: var(--space-m);
}

/* In a row there is width to spare, so the mark sits beside the year rather
   than being pushed to the far edge of a 167px card. */
.ov-view-list .oview-card__marker {
	margin-inline-start: 0;
}

/*
 * The accent turns on its side.
 *
 * The same pseudo-element, re-laid: a 2px line fading along the bottom of a
 * 167px card is a hairline; the same line across a 900px row is a stripe. As a
 * bar down the leading edge it is the same idea at the same weight, and it is
 * where a list row expects its status marker.
 */
.ov-view-list .oview-card::after {
	inset-inline: 0 auto;
	top: 0;
	bottom: 0;
	width: 2px;
	height: auto;
	background: linear-gradient(to bottom, transparent, var(--card-accent), transparent);
	/*
	 * ⚠️ Hidden until the row is pointed at, which the grid view does not do.
	 *
	 * Thirty cards each with a 2px mark is texture; thirty ROWS each with one is
	 * a column of coloured ticks running down the left of the page, and the eye
	 * reads a column as a list of its own. A list earns its clarity by being
	 * plain, so the colour arrives only when a row is singled out.
	 *
	 * Opacity and not `display`, so it can fade rather than appear.
	 */
	opacity: 0;
	transition: opacity 150ms ease;
}

.ov-view-list .oview-card:hover::after,
.ov-view-list .oview-card:focus-within::after {
	opacity: 1;
}

/* --------------------------------------------------------------------------
 * The profile module on /account/.
 *
 * A row: photo and who you are. It borrows the card language
 * the rest of the dashboard uses rather than introducing a panel of its own.
 * -------------------------------------------------------------------------- */

.oview-profile {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-m);
	padding: var(--space-m);
	background: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-l-2);
}

.oview-profile__avatar img {
	inline-size: 6.4rem;
	block-size: 6.4rem;
	border-radius: var(--radius-full);
	object-fit: cover;
	display: block;
}

.oview-profile__who {
	display: flex;
	flex-direction: column;
	gap: var(--space-2xs);
	/* Takes the slack so the action sits at the far end on a wide screen and
	 * wraps underneath on a narrow one, with no breakpoint to maintain. */
	flex: 1 1 20rem;
}

/*
 * ⚠️ IT IS AN `h1` FOR STRUCTURE, NOT FOR SIZE. Nothing here said how big a
 * name is, so it inherited the theme's hero heading: an account called "admin"
 * arrived at display size in the serif face, shouting a login at the top of a
 * dashboard. The tag is right - it is the page's subject - and the size is a
 * separate decision.
 */
.oview-profile__name {
	font-size: var(--text-xl);
	font-weight: 600;
	line-height: 1.2;
	margin: 0;
}

.oview-profile__name span {
	color: var(--color-text-muted);
	font-size: var(--text-xs);
	font-weight: 400;
}

.oview-profile__meta {
	color: var(--color-text-muted);
	font-size: var(--text-xs);
}

/* --------------------------------------------------------------------------
 * Flags.
 *
 * ⚠️ THE HAIRLINE IS NOT DECORATION. Japan, Poland and a dozen others are
 * mostly white, and a white rectangle on a white card is an invisible flag -
 * the same failure as the emoji it replaced, arrived at a different way. The
 * border is the palest one in the palette and only shows where the flag does
 * not.
 *
 * ⚠️ Sized in `ch` rather than a space token: this sits inline in a line of
 * text, so it should scale with the text it interrupts rather than with the
 * layout around it.
 * -------------------------------------------------------------------------- */

.ov-flag {
	display: inline-block;
	inline-size: 2.4ch;
	block-size: auto;
	aspect-ratio: 4 / 3;
	vertical-align: -0.15em;
	border: 1px solid var(--color-border);
	border-radius: 1px;
	object-fit: cover;
}

/* ==========================================================================
 * ADOPTED FROM THE BRANDS DRAWING, 2026-08-30.
 *
 * ⚠️ THESE RULES LIVED IN `assets/parts/site-frame-brands.css`, WHICH WAS A
 * DESIGN SYSTEM NOBODY COULD MAINTAIN.
 *
 * `mockup-decompose.php frame` derived that file from one drawing's stylesheet
 * and loaded it on EVERY page. It carried the white card background, the
 * unstriped media and the facet label treatment for the whole site, and three
 * things were wrong with where it lived: nothing in the repo owned it, a hand
 * edit was undone by the next import, and it was only rewritten when somebody
 * re-imported that one drawing. Then a build shrank it by 6,875 bytes and
 * deleted the card design site-wide while reporting nine steps and zero
 * failures.
 *
 * ⚠️ MERGED PROPERTY BY PROPERTY, NOT COPIED. The frame stated some selectors
 * three times: the drawing's rule, then the map's `corrections` restating it
 * where the drawing had our class name wrong. `.oview-facet__name` arrived as
 * mono uppercase, which is what the drawing thought a panel title was, then
 * twice more as sans sentence case, which is what it actually renders as.
 * Copied across, this file would carry a mistake and its own fix side by side.
 *
 * ⚠️ NAMES NO DATASET, which is why it may live in the platform at all. Every
 * value here is a token; what a brand or a fragrance IS appears nowhere.
 *
 * `node scripts/build/adopt-frame.js <mockup>` does the classification for the
 * next drawing. It writes nothing.
 * ========================================================================== */

.oview-card-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(23.2rem, 1fr));
	gap: var(--space-l);
}

.oview-card {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: var(--space-l);
	border: var(--border-hair) solid var(--color-border);
	border-radius: var(--radius-l-2);
	/*
	 * ⚠️ THE PADDING IS A VARIABLE BECAUSE ONE PAGE WANTED LESS OF IT AND
	 * EVERY ARCHIVE WANTED THE SAME.
	 *
	 * A dashboard card is small and dense; an archive card is the page's main
	 * subject. The old answer to that is a `--tight` modifier, which is a second
	 * class carrying one declaration. `--card-pad` is set on an ancestor
	 * instead, so the value travels down to whatever cards are inside without
	 * any of them being told which page they are on (D126).
	 *
	 * The fallback is what this rule said before, so nothing that does not set
	 * it moves.
	 */
	padding: var(--card-pad, var(--space-xl));
	box-sizing: border-box;
	/*
	 * ⚠️ NO `container-type`, AND REMOVING IT CLOSED A 7,839px HOLE.
	 *
	 * `container-type: inline-size` made the card a query container, and a
	 * container's contribution to its ancestors' INTRINSIC height is computed
	 * differently from how it finally lays out. On the notes archive at 375px
	 * that gave `.ov-archive-cols` a row of 16,017px around a grid of 8,288 -
	 * the height of the same cards stacked one per line - so the page ended with
	 * seven and a half thousand pixels of white between the last card and the
	 * footer.
	 *
	 * It bought nothing. The two `@container` queries in this file ask about
	 * `.oview-card-grid` and `.ov-view-list .oview-card`, and both of those are
	 * the container itself or its ancestor rather than its descendants, so
	 * neither could ever match. Nothing anywhere queries a `.oview-card__*`
	 * child. The declaration was pure cost.
	 *
	 * ⚠️ If a card internal ever does need a container query, put
	 * `container-type` on a wrapper INSIDE the card, not on the card: on the
	 * card it is also a grid item, and that is where the intrinsic-sizing
	 * mismatch comes from.
	 */
}

.oview-card:hover {
	border-color: var(--color-accent-soft);
	color: var(--color-text);
}

.oview-card__media {
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: center;
	/*
	 * ⚠️ THIS BEATS THE `aspect-ratio` DECLARED ON THE SAME CLASS ABOVE, which
	 * has therefore never applied to a card image - a rule present, correct and
	 * inert, the same shape as the `@container` note further down. A height and
	 * a ratio on one element is a fight the height wins, so the height is the
	 * one that takes a variable.
	 *
	 * ⚠️ A DEFAULT, NOT A CONSTANT. An archive tile shares its width with a
	 * name and three facts; a dashboard of what somebody owns is mostly
	 * pictures, and a bottle is taller than it is wide.
	 */
	height: var(--media-h, 10.4rem);
	border-radius: var(--radius-m-2);
	border: var(--border-hair) solid var(--color-border);
	background: var(--hatch-l);
	font-family: var(--font-mono);
	font-size: var(--text-xs);
	letter-spacing: 0.08rem;
	color: var(--color-text-muted);
	box-sizing: border-box;
}

.oview-card__body {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-3xs);
	text-align: center;
}

.oview-card__title {
	font-size: var(--text-xl);
	font-weight: 700;
	line-height: 1.25;
	margin: 0;
}

.oview-card__parent {
	font-size: var(--text-l);
	color: var(--color-text-muted);
	margin: 0;
}

.oview-card__meta {
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-s);
	padding-top: var(--space-m);
	border-top: var(--border-hair) solid var(--color-hairline);
}

/*
 * ⚠️ ONE SIZE FOR BOTH FACTS, SO THE ROW HAS ONE BASELINE.
 *
 * This was `--text-l` and the year overrode it to `--text-xs`, so the two facts
 * on a card sat at 18px and 12px beside each other in the same row. They are
 * the same KIND of thing - a small number stated plainly - and the size
 * difference read as one of them being an afterthought rather than as emphasis.
 *
 * `--text-m` for both. The rating still leads: it keeps the accent colour and
 * the heavier weight, which is emphasis without a second type size.
 */
.oview-card__fact {
	font-size: var(--text-m);
	font-weight: 700;
	color: var(--color-text);
	margin: 0;
}

.oview-card__marker {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	font-size: var(--text-l);
	line-height: 1.5;
	color: var(--color-text-quiet);
	margin: 0;
}

.ov-view-list .oview-card-grid {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	border: var(--border-hair) solid var(--color-border);
	border-radius: var(--radius-l-2);
	overflow: hidden;
	/*
	 * ⚠️ `gap: 0`, WHICH THIS NEVER SAID. The base grid carries
	 * `gap: var(--space-l)` for cards standing apart, and switching to a flex
	 * column inherited it - so every list row had 12px of page showing above
	 * its own hairline. The divider looked detached and the hover tint stopped
	 * short of it, which is the gap between rows nobody could place.
	 */
	gap: 0;
}

.ov-view-list .oview-card {
	display: flex;
	flex-direction: row;
	align-items: center;
	gap: var(--space-l);
	border: 0;
	border-top: var(--border-hair) solid var(--color-hairline);
	border-radius: 0;
	/*
	 * ⚠️ `--space-m`, NOT `--space-l`. A list row is two lines of text beside a
	 * 44px bottle; it does not need an archive card's air, and with the body's
	 * own padding removed above, this is the only vertical space in the row.
	 * The horizontal inset stays: that is the gutter the whole list aligns on.
	 */
	padding: var(--space-m) var(--space-xl);
}

.ov-view-list .oview-card:first-child {
	border-top: 0;
}

.ov-view-list .oview-card:hover {
	background: var(--color-surface-sunk);
	border-color: var(--color-hairline);
	color: var(--color-text);
}

.ov-view-list .oview-card__media {
	width: 4.4rem;
	height: 4.4rem;
	flex-shrink: 0;
}

.ov-view-list .oview-card__body {
	align-items: flex-start;
	text-align: left;
	flex: 2 1 18rem;
	min-width: 0;
}

.ov-view-list .oview-card__meta {
	border-top: 0;
	padding-top: 0;
	flex-shrink: 0;
	gap: var(--space-l);
}

.oview-card__media img {
	object-fit: contain;
	width: 100%;
	height: 100%;
	padding: var(--space-s);
	box-sizing: border-box;
}

.oview-card__media:not(.oview-card__media--empty) {
	border: 0;
	background: var(--color-surface);
}

.oview-card__media--empty {
	background: var(--color-surface-sunk);
	border: 0;
}

/*
 * ⚠️ THIS @container RULE HAS NEVER APPLIED, and it is the only thing that was
 * supposed to collapse a card grid on a phone.
 *
 * `@container` needs an ancestor declaring `container-type`, and nothing above
 * `.oview-card-grid` declares one - every ancestor up to `<body>` reports
 * `container-type: normal`. So the rule has been inert for as long as it has
 * existed. Nobody saw it because the default 23.2rem minimum happens to give
 * one column at phone widths anyway.
 *
 * It stops being harmless the moment a grid asks for a smaller minimum: the
 * dense variant is 20rem, and on the entity single pages that laid out two
 * 212px columns inside a 375px screen. `check-mobile.js` caught it at
 * `right=494.1 vs 375`.
 *
 * Kept, because it is correct for a card grid inside a narrow COLUMN rather
 * than a narrow screen, and a real media query added beneath it for the screen.
 */
@container (max-width: 34rem) {
	.oview-card-grid {
		grid-template-columns: 1fr;
	}
}

/*
 * TWO ACROSS ON A PHONE, not one.
 *
 * A card is a bottle, a name and a line of facts, and one per line means a
 * screenful is two results. Two puts six on the same screen and the shot is
 * still large enough to tell one bottle from another.
 *
 * ⚠️ Stated as `repeat(2, 1fr)` rather than a smaller `auto-fill` minimum,
 * because auto-fill answers "how many fit" and at 375px the honest answer for a
 * 20rem minimum is one. This is a decision about what a phone should show, so
 * it is written as the number.
 *
 * 47.9rem is the breakpoint the site's own stylesheets already use. Both
 * selectors, because the dense variant is two classes and would otherwise win.
 */
@media (max-width: 47.9rem) {
	.oview-card-grid,
	.oview-card-grid.oview-card-grid--dense {
		grid-template-columns: repeat(2, 1fr);
	}
}

@container (max-width: 26rem) {
	.ov-view-list .oview-card {
		flex-wrap: wrap;
	}
}

/* ==========================================================================
 * A COLLECTION CARD: the result card, saying what somebody said about a thing.
 *
 * ⚠️ FOUR MODIFIERS, NOT A SECOND CARD. The dashboard lists the same things the
 * archives list, so it is `oview-card` with the parts a personal row needs: a
 * hatch where the archives have a photo, a name that starts at the left because
 * a house sits under it, a state, and a foot.
 * ========================================================================== */

/*
 * The hatch every other shotless image on this site draws.
 *
 * ⚠️ IT CARRIES `--empty` AS WELL, and that is not decoration.
 * `.oview-card__media:not(.oview-card__media--empty)` repaints the media
 * `--color-surface`, at two classes of specificity, so a media that was only
 * `--hatch` came out flat white: the element was there, the right size, and
 * blank. Keeping both classes is what stops that rule matching.
 */
/*
 * ⚠️ THE RESERVED BOX STANDS DOWN WHEN A REAL PICTURE ARRIVES. A card can carry
 * both an image element and the hatch, because a dataset with no pictures
 * renders no `<img>` at all and an image element on its own would collapse the
 * media box to nothing on that day. So the hatch is always there and hides
 * itself when it is not needed, rather than the template guessing in advance
 * which datasets have pictures.
 */
.oview-card:has( > img.oview-card__media ) > .oview-card__media--hatch {
	display: none;
}

.oview-card__media--hatch {
	aspect-ratio: var(--media-ratio, 1 / 1);
	height: auto;
	padding: 0;
	margin: var(--space-s) var(--space-s) 0;
	border-radius: var(--radius-m-2);
	background: repeating-linear-gradient(135deg, var(--color-surface-hover) 0 0.6rem, var(--color-surface) 0.6rem 1.2rem);
}

/* What the space is for, in the mono face the site uses for small facts. */
.oview-card__media-label {
	font-family: var(--font-mono);
	font-size: var(--text-xs);
	color: var(--color-text-muted);
	margin: 0;
}

/*
 * ⚠️ THE CARD BODY CENTRES ITS CHILDREN and a dashboard card cannot. The
 * archives show a name over a house and centring reads well; here the name, the
 * house, the state and the score are four left-read lines, and centring each
 * one makes a column of things that look misplaced.
 */
.oview-card__body--start {
	align-items: flex-start;
	text-align: left;
}

/* A name with a house under it reads from the left. */
.oview-card__title--start {
	text-align: left;
	font-weight: 700;
}

/*
 * ⚠️ `__parent` IS ALREADY THE SLOT FOR WHAT A THING BELONGS TO, and it is the
 * word the card contract uses for a brand, a promotion, a publisher. I added
 * `__house` beside it, which is (a) a duplicate and (b) FRAGRANCE VOCABULARY in
 * a platform stylesheet that must not know what a fragrance is (D45).
 *
 * The slot centres itself for an archive card; a dashboard card reads from the
 * left, and that is the body's business rather than a second slot's.
 */
.oview-card__body--start .oview-card__parent {
	align-self: flex-start;
	text-align: left;
	margin-top: 0;
}

/*
 * Which of have, want and had this is.
 *
 * ⚠️ `--color-success-ink` RATHER THAN `--color-success`. White on the lighter
 * green is about 2.3:1 and `check-mobile.js` fails it; the darker one is the
 * same hue and passes, which is the palette floor D117 exists for.
 */
.ov-state {
	display: inline-flex;
	align-items: center;
	padding: var(--space-2xs) var(--space-s);
	border-radius: var(--radius-full);
	background: var(--color-success-ink);
	color: var(--color-on-accent);
	font-size: var(--text-xs);
	font-weight: 600;
}

/*
 * The foot: what you scored it, and the control that changes your mind. A rule
 * above it, because the two belong to the thing rather than to the vote.
 */
.oview-card__foot {
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-s);
	margin-top: var(--space-xs);
	padding-top: var(--space-s);
	border-top: var(--border-hair) solid var(--color-border);
}

.oview-card__fact--score {
	color: var(--color-warning-ink);
}

/*
 * ⚠️ ONE GRID, ONE KNOB, NO BREAKPOINTS.
 *
 * This was two modifiers and three media queries: a `--collection` that named
 * four columns and stepped down to three, two and one, and a `--performance`
 * that did its own thing. Both were saying the same sentence in different
 * words - "cards about this wide, as many as fit" - and a media query was
 * re-deciding at three widths what the grid can decide continuously.
 *
 * `auto-fit` already scales: it fits as many tracks as the row allows and
 * stretches them to share it, so four at one width IS three at a narrower one
 * with nothing to declare. The only thing that differs between two grids is how
 * wide a card wants to be, so that is the only thing left to say - `--col-min`,
 * set where the grid is used.
 *
 * ⚠️ `min(..., 100%)` because `minmax(20rem, 1fr)` OVERFLOWS below 20rem: the
 * minimum is a floor the track cannot go under, so on a narrow phone the row
 * gets wider than the screen instead of dropping to one column.
 */
/*
 * ⚠️ `auto-fill`, NOT `auto-fit`, AND THAT IS THE WHOLE DIFFERENCE. `auto-fit`
 * COLLAPSES the tracks nothing landed in and shares the row between what is
 * left, so a grid holding one card stretches it edge to edge - a single
 * fragrance in a collection filled the account page, 1200px wide, while five
 * of them sat in a correct four-column row. `auto-fill` keeps the empty tracks,
 * so one card is one card and the count still follows the width.
 */
.oview-card-grid--fit {
	grid-template-columns: repeat(auto-fill, minmax(min(var(--col-min, 22rem), 100%), 1fr));
	justify-content: start;
}


/*
 * A drawn card's picture slot (D223): `<div class="oview-media">` in a mockup,
 * with the bound `<img>` inside when the dataset has pictures. The same hatch
 * the platform's own cards draw, so a site with no pictures still shows where
 * one goes, and a picture that arrives covers it. The linter warns about a
 * card that links to a page and has no slot (`NO_MEDIA`).
 *
 * ⚠️ PLATFORM, NOT SITE. A drawing uses the class without defining it; the
 * shape is the site's to change with a modifier or `--media-ratio`.
 */
.oview-media {
	position: relative;
	overflow: hidden;
	aspect-ratio: var(--media-ratio, 4 / 3);
	/*
	 * ⚠️ A CORNER IS A PROPORTION OF WHAT IT IS ON, and this was one number for
	 * every picture on the site. `--radius-m-2` is the CONTROL corner, right on
	 * a card's own image and far too round on a sidebar thumbnail: at 80px wide
	 * the same 19px radius reads as a pill, and the same shape that looks
	 * considered at 300px looks like a mistake at 80.
	 *
	 * A knob, like `--media-ratio` beside it, so a small thumb asks for the
	 * inset step and nothing else has to change. The four corners are still the
	 * only four (D224); this picks between them rather than inventing a fifth.
	 */
	--media-radius: var(--radius-m-2);
	border-radius: var(--media-radius);
	background: repeating-linear-gradient(135deg, var(--color-surface-hover) 0 0.6rem, var(--color-surface) 0.6rem 1.2rem);
}

.oview-media > img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.oview-media--portrait { --media-ratio: 3 / 4; }
.oview-media--square { --media-ratio: 1 / 1; }
.oview-media--wide { --media-ratio: 16 / 9; }

/* ==========================================================================
 * A LISTING'S RESULTS, IN EITHER VIEW, FOR A CARD THIS FILE DID NOT DRAW
 *
 * ⚠️ THE LIST VIEW WAS BUILT FOR .oview-card AND NOTHING ELSE, so a site whose
 * drawing brings its own card got the switch and no second view: the body class
 * changed and the page did not (D224). These rules key off two names instead:
 * `oview-results` on the container a drawing puts its cards in, and ACSS's
 * `card` on the cards themselves.
 *
 * The grid view stays the drawing's: its own class sets the columns and wins on
 * load order. Only the list view is ours, and it wins because a body class puts
 * it a step higher.
 * ========================================================================== */

/*
 * ⚠️ `body` IS LOAD-BEARING HERE. Bricks exports a drawing's rule as
 * `.celeb-grid.brxe-div`, two classes, and it loads after this file, so
 * `.ov-view-list .oview-results` ties and loses. The element selector breaks
 * the tie without !important.
 */
/*
 * ⚠️ THE CONTAINER IS THE PANEL, NOT THE ROWS. Left the other way round, the
 * rows carried the fill and the container's hairline sat directly against it,
 * so a list read as a borderless slab beside facet panels that plainly had an
 * edge (D224). One filled surface with a hairline, exactly like a facet panel,
 * and the rows are separated rather than drawn.
 */
body.ov-view-list .oview-results {
	grid-template-columns: minmax(0, 1fr);
	gap: 0;
	border: var(--border-hair) solid var(--color-border);
	/* A surface's corner, the same one the facet panels and the cards draw. */
	border-radius: var(--radius-l-2);
	background: var(--color-surface);
	overflow: hidden;
}

/*
 * ⚠️ A CARD IN A LISTING IS A PICTURE AND ONE TEXT BLOCK, which is what makes
 * one rule able to re-lay a card this file never drew: two children, so the row
 * is two columns. The platform's own oview-card has had that shape all along
 * (__media and __body); a drawing that hangs four siblings off its card lands
 * them in the grid one after another and the row reads as rubble. Checked by
 * the linter (CARD_SHAPE).
 */
body.ov-view-list .oview-results > .card {
	--card-display: grid;
	--card-radius: 0;
	--card-gap: var(--space-m);
	--card-background: transparent;
	/* A row is separated, not boxed: the panel around it carries the edge. */
	--card-border-width: 0;
	grid-template-columns: var(--list-thumb, 7rem) minmax(0, 1fr);
	align-items: center;
	/* The same hair the facet panels draw, so a listing's two views agree. */
	border-bottom: var(--border-hair) solid var(--color-border);
}

/*
 * ⚠️ A LIST OF ROWS ALL ONE COLOUR IS A WALL OF TEXT. The hairline between two
 * rows is the right weight for a pair and the wrong one for forty: a reader
 * scanning a long list loses which line belongs to which row. Every other row
 * takes the sunk surface, which is one step off the panel and reads as a band
 * rather than as a highlight (D224).
 */
body.ov-view-list .oview-results > .card:nth-child(even) {
	--card-background: var(--color-surface-sunk);
}

body.ov-view-list .oview-results > .card:last-child {
	border-bottom: 0;
}

/* A row's picture is a thumbnail, whatever shape the grid view gave it. */
body.ov-view-list .oview-results > .card .oview-media {
	aspect-ratio: 1;
}

/*
 * ⚠️ A CARD'S HOVER LIGHTS ITS NAME, NOT ONLY ITS GROUND. A row whose ground
 * tinted and whose text sat there unchanged read as a highlight rather than as
 * a link, and every drawing was left to remember the second half on its own
 * (D224). The name is matched the way ACSS's own framework matches a card's
 * parts, by the BEM tail rather than by a class this file cannot know.
 */
.card:hover :is([class*="__name"], [class*="__title"], h1, h2, h3, h4, h5, h6),
.card:focus-within :is([class*="__name"], [class*="__title"], h1, h2, h3, h4, h5, h6) {
	color: var(--hover-ink);
}
