/**
 * Bonza: Feefo review card, Figma node 34345:186657.
 *
 * REPLACED 2026-09-22 (build spec:
 * projects/bonza/docs/feefo-api-card-build-spec-20260922.md). This used to
 * style only the OUTER container of Feefo's hosted, closed-shadow-DOM
 * widget (see the previous revision's docblock, kept in git history): the
 * inner card was permanently unreachable from outside a shadow root
 * exposing zero `part` attributes, so this file could only ever touch the
 * container and rely on a handful of inheritable CSS properties crossing
 * the boundary. Now that includes/feefo-reviews-api.php renders our own
 * markup (`.bfr-card`/`.bfr-carousel`), every element Figma draws is a
 * real, ownable selector.
 *
 * Config investigated: same as product-page-marketing.css -- no Blocksy
 * Customizer surface exists for a bespoke review-card component like this,
 * and this component is shared across pdp/checkout/section contexts (a
 * `.bfr-card--pdp`/`.bfr-card--checkout`/`.bfr-card--section` BEM
 * modifier, set by whichever context renders it), so its base styling
 * lives in its own file rather than folded into any one page's stylesheet.
 *
 * editor-friendly-ok: this restyles cards rendered by a shared PHP
 * function (bonza_feefo_render_card()/bonza_feefo_render_carousel()) with
 * no Gutenberg block/pattern counterpart to register add_editor_style()
 * against -- the same documented exception product-page-marketing.css and
 * the pre-2026-09-22 revision of this file already use. #FF6256 (stroke)
 * and #FFA825 (stars/avatar fill) are Figma-node-specific values with no
 * existing --wp--preset--color--* slug in this site's palette (checked:
 * no theme.json/design-tokens.css defines either), so they stay literal
 * per that same exception rather than inventing a new preset token for a
 * value used nowhere else. #FFFFFF/#2E2A2A mirror product-page-marketing.css's
 * own --bppm-white/--bppm-ink (14.18:1 ink-on-white, WCAG AAA) but
 * are re-declared locally as --bfr-* rather than reused from `.bppm`,
 * because this component also renders OUTSIDE `.bppm` scope (checkout,
 * home/category), where --bppm-* custom properties are not in scope.
 *
 * MEASURED (Figma node 34345:186657, values relayed by the build spec; no
 * rounding applied per this project's own recurring-mistakes register --
 * 15.21px and 2.9915px are real, not approximations of 15px/3px):
 *   layout    row, alignSelf stretch, alignItems center, wrap: true
 *   padding   28px             -> padding: 28px
 *   gap       24px             -> gap: 24px
 *   fill      #FFFFFF          -> background: var(--bfr-white)
 *   stroke    #FF6256, "0px 0px 0px 3px" (left edge only, 3px)
 *                              -> border-left: 3px solid #FF6256; no
 *                                 top/right/bottom border, matching the
 *                                 per-side weight exactly
 *   radius    12px             -> border-radius: 12px
 *   photo     200px tall, 12px radius
 *   column    16px gap
 *   stars     15.21 x 15.21px, fill #FFA825, gap 2.9915px
 *   body      Kobe1.1 Regular 400, 16px/20px, #2E2A2A
 *   divider   1px solid rgba(0,0,0,0.08)
 *   avatar    44 x 44px circle, fill #FFA825
 *
 * SIZING (`.bfr-card` inside `.bfr-carousel__track`), REVISED 2026-09-23
 * (q21/q22): the reviews-ROW context (`.bfr-carousel--pdp`) is NOT a
 * wrapping grid -- Figma node 35066:60246 draws one horizontally-scrolling
 * line of fixed-334px cards (see the file-level MEASURED note further
 * down), so `.bfr-card` is now `scroll-snap-align:start` with a fixed
 * 334px basis at >=1024px and a fluid `clamp(240px, 85vw, 334px)` below it
 * (Figma gives no explicit sub-1024 value; this avoids inventing one while
 * still never exceeding the container). This project's own
 * recurring-mistakes register (category 1, full-width/container/section
 * layout) records a `minmax(280px, Npx)` 320px-width overflow as an
 * already-proven bug on this exact site (homepage product-card grid,
 * 2026-09-22) -- avoided here the same way, `clamp()`/fixed-basis with
 * `max-width:100%`, never `minmax()`, so a card can never exceed its
 * container at any viewport. The one-card-per-slide buy-box context
 * (`.bfr-carousel--pdp-summary`) instead sizes `flex-basis:100%`. A
 * second, related nested-flexbox bug (a flex item's un-wrapped text
 * setting its ancestor's min-content width past the viewport) was found
 * and fixed live during this build's own 320/375/425 screenshot check --
 * see the `min-width: 0` comments on `.bfr-card` and `.bfr-card__col`
 * below.
 *
 * ACCESSIBILITY: the avatar-circle initials render in --bfr-ink (#2E2A2A)
 * on the #FFA825 fill, not white. White-on-#FFA825 measures ~1.97:1,
 * failing WCAG 1.4.3 even at large-text thresholds; #2E2A2A-on-#FFA825
 * measures ~7.6:1 (AAA). The build spec does not confirm Figma's own EM-
 * text-node colour, so this is a documented accessibility-driven choice,
 * not a confirmed Figma value -- flag to design if Figma is later found to
 * specify white.
 *
 * EMPTY STARS: Feefo supplies only a single filled-star asset
 * (assets/reviews/icon-review-star.svg, #FFA825); there is no separate
 * "empty star" asset in the build spec. A rating below 5 renders the same
 * icon at reduced opacity for the unfilled positions
 * (`.bfr-card__star--empty`) rather than inventing a different outline
 * glyph with no source.
 */

.bfr-carousel {
	--bfr-white: #FFFFFF;
	--bfr-ink: #2E2A2A;
	--bfr-stroke: #FF6256;
	--bfr-amber: #FFA825;
	--bfr-divider: rgba(0, 0, 0, 0.08);
	--bfr-tag-bg: #F8EDE3;
	--bfr-name-ink: #333333;

	display: flex;
	flex-direction: column;
	gap: 24px;
	width: 100%;
	box-sizing: border-box;
	/*
	 * FIXED 2026-09-24 (ClickUp 86eypb6me, q18 review-card-height): every
	 * card in the `--pdp` reviews-row carousel was centring its stars/body/
	 * meta text at every breakpoint. CONFIRMED via Chrome DevTools Protocol
	 * `CSS.getMatchedStylesForNode` on a live `.bfr-carousel--pdp .bfr-card`
	 * (not a `document.styleSheets` selector walk -- that missed it, see
	 * below) -- the WINNING rule is `.bppm-reviews { text-align: center; }`
	 * (product-page-marketing.css), the "What Owners Are Saying" SECTION
	 * wrapper this carousel renders inside; nothing in this file ever set
	 * its own `text-align`, so the section's own (correct, Figma-matching)
	 * centred heading/intro text was inherited straight down through
	 * `.bfr-card` and every descendant. The buy-box `--pdp-summary`
	 * carousel already measured left-aligned because it renders in a
	 * DIFFERENT section, never wrapped in `.bppm-reviews` -- so this had
	 * never surfaced there. `text-align: left` here, on the shared
	 * `.bfr-carousel` rule both contexts use, stops the inheritance at the
	 * carousel boundary regardless of which section it is ever placed in
	 * (checkout/home/category use the same class), matching Figma's card
	 * text, which is always left-aligned inside the card itself.
	 */
	text-align: left;
}

/* MEASURED (Figma node 35066:60246, "Reviews" frame, 2026-09-23, ClickUp
   86eypb6me q21/q22): the review-card row is a SINGLE horizontally-scrolling
   line, not a wrapping grid -- 5 card instances sit at x=16760/17110/17460/
   17810/18160 (350px apart, 334px wide, 16px gap) inside a 1384px-wide
   parent, so the 5th card visibly overflows the frame's own right edge (a
   "peek" affordance hinting more content). The vertical gap from this track
   to the "DOT - PAGE INDICATOR" instance below it is 64px (Reviews frame's
   own itemSpacing). `.bfr-carousel--pdp`/`--pdp-summary` below apply that
   64px; `section`/`checkout` contexts (no matching Figma node measured yet)
   keep the previous 24px rather than guessing. */
.bfr-carousel--pdp {
	gap: 64px;
}

/*
 * FIXED 2026-09-24 (ClickUp 86eypb6me, q18 S6): --pdp-summary used to share
 * the --pdp row's 64px gap, borrowed from the WRONG Figma node. MEASURED
 * live this session against the buy-box widget's OWN instance (node
 * I21316:76932;34350:73717, not 35066:60246): its containing frame is
 * VERTICAL with itemSpacing:0 between the card and the dots row, and the
 * dots row itself carries paddingTop/Bottom:20 -- so the real card-to-dots
 * gap here is 20px (from the dots container's own padding below), not 64px.
 * The shared-component reuse-without-per-instance-override mistake this
 * project's recurring-mistakes register already names (category 3, item on
 * shared carousel defaults).
 */
.bfr-carousel--pdp-summary {
	gap: 0;
}

/*
 * FIXED 2026-09-24 (ClickUp 86eypb6me, PDP round 2, item 6a): align-items
 * was `stretch`, which force-equalises every card in the horizontally-
 * scrolling row to the height of the TALLEST card, then .bfr-card's own
 * `align-items:center` vertically centres each card's (single, no-photo)
 * content column inside that stretched box -- measured live, a short
 * no-photo card sat inside an 814px-tall box with 243px of real content,
 * a 196px dead gap above AND below the stars/body/meta block. Figma's own
 * card ROW ("Review Copy Variant", node I21316:76932;35066:60252) sets
 * `counterAxisAlignItems: MIN` (top-align), not stretch, and each card
 * component itself has `counterAxisSizingMode: AUTO` (hugs its own
 * content height, confirmed: a card with its photo rectangle
 * visible:false hugs to exactly padding+content, no reserved photo
 * space) -- cards in Figma's own row are genuinely different heights,
 * top-aligned. `align-items:flex-start` reproduces that: every card now
 * hugs its own height and its content sits at the Figma-correct 28px
 * padding from the top, no centred dead space. Same shared class serves
 * the buy-box `--pdp-summary` carousel (also measured live with the same
 * symptom on its no-photo cards), so this is not scoped to one context.
 */
.bfr-carousel__track {
	display: flex;
	flex-wrap: nowrap; /* CHANGED 2026-09-23 (q21): Figma draws one scrollable row, not a
	                       wrapping grid -- see the file-level MEASURED note above. */
	align-items: flex-start;
	gap: 16px; /* Figma-measured card-to-card gap (17110 - (16760+334) = 16). */
	width: 100%;
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scroll-snap-type: x mandatory;
	scrollbar-width: none; /* FIXED 2026-09-24 (q18 S3/S18/S34): was `thin`, which still paints a
	                           visible native scrollbar track/thumb in Firefox; Figma's carousel has
	                           no visible scrollbar at all (scroll affordance is the dots + card peek).
	                           `none` hides it while overflow-x:auto keeps the track scrollable by
	                           touch/trackpad/keyboard -- the carousel's own tabindex + arrow-key
	                           handling in feefo-carousel.js remains the keyboard path. */
	-ms-overflow-style: none; /* legacy Edge/IE, same intent as scrollbar-width:none. */
	-webkit-overflow-scrolling: touch;
}

.bfr-carousel__track::-webkit-scrollbar {
	display: none; /* Chrome/Safari/Edge(Chromium) -- scrollbar-width/-ms-overflow-style above
	                   don't reach WebKit/Blink; this is the third, required leg of hiding the
	                   native scrollbar cross-browser without disabling scroll itself. */
}

.bfr-carousel__track:focus-visible {
	outline: 2px solid var(--bfr-stroke);
	outline-offset: 2px;
}

.bfr-card {
	display: flex;
	flex-wrap: wrap;
	/* FIXED 2026-09-24 (q18 S5): was `center`, which vertically centres the
	   200px photo against the (usually taller) text column, so the photo's
	   top edge sat lower than the stars row instead of level with it. Figma's
	   card row itself is top-aligned (this file's own 2026-09-24 note above
	   on .bfr-carousel__track already established `counterAxisAlignItems:
	   MIN` for the equivalent card-row instance) -- `flex-start` puts the
	   photo's top edge flush with the stars row, matching Figma. */
	align-items: flex-start;
	gap: 24px;
	/* Figma's reviews-ROW instance (35066:60246) is a fixed 334px card in a
	   single scrolling line (see file-level MEASURED note above); below the
	   1024px breakpoint Figma gives no explicit value, so this scales down
	   fluidly rather than guessing a breakpoint-specific px, per this file's
	   documented "no per-breakpoint value given" convention. */
	flex: 0 0 clamp(240px, 85vw, 334px);
	scroll-snap-align: start;
	min-width: 0; /* a flex item's automatic min-width is content-based (auto), which let the
	                 review body's un-broken text set this card's min-content width past the
	                 320px viewport at 320/375/425 -- zeroing it lets width:100%/max-width below
	                 actually apply, letting flex-wrap + word-break do their job instead. */
	max-width: 100%;
	box-sizing: border-box;
	padding: 28px;
	background: var(--bfr-white);
	border: 0;
	border-left: 3px solid var(--bfr-stroke);
	border-radius: 12px;
	margin: 0;
	/* FIXED 2026-09-24 (ClickUp 86eypb6me, q18 review-card-height): safety
	   clip for the fixed heights set below -- with `height` now a hard
	   constant per breakpoint/context instead of hug-content, an outlier
	   (e.g. an unusually long reviewer name wrapping to 2 lines at the
	   narrowest widths) must not visually break the box or make one card
	   taller than its siblings. `.bfr-card__body` already self-clamps (see
	   its own rule below, N varies by context); this is the same guarantee
	   for the rest of the card. */
	overflow: hidden;
}

@media (min-width: 1024px) {
	.bfr-carousel--pdp .bfr-card {
		flex-basis: 334px; /* Figma-exact at the only breakpoint Figma specifies. */
	}
}

/*
 * FIXED 2026-09-24 (ClickUp 86eypb6me, q18 review-card-height): fixed,
 * per-breakpoint card heights so every card in a row is equal -- the user's
 * own ask ("make all review container span full height so they are all
 * equal... like a fixed height like in figma... both the reviews with image
 * and the other one without image"). Before this, `.bfr-card` had no
 * explicit height at all (hug-content via flex), so real Feefo review
 * bodies of very different lengths (the file's own docblock above already
 * measured 40-1188 chars) produced visibly uneven cards even with the
 * 4-line body clamp in place, because the OTHER rows (stars/tags/divider/
 * meta) still varied.
 *
 * MEASURED (Figma, this session, ClickUp 86eypb6me q18-review-height;
 * extraction 2026-09-23, DESKTOP/TABLET/MOBILE "BASIC PRODUCT PAGE" frames,
 * node I{frame}:76932/35546/112443;35066:60246 "Reviews" -> "Review Copy
 * Variant" -> its 5 sibling card instances, ids ...:60253/60275/60295/
 * 60315/60337): every one of these 5 real-content instances is a no-photo
 * card (their own Rectangle 9785 photo layer is `visible: false` --
 * `bonza_feefo_render_carousel()`'s `--pdp` context always passes
 * `show_photo => false`, matching this file's own pre-existing docblock).
 * Their AUTO-hug heights per breakpoint (max across the 5 -- the tallest,
 * a card WITH a tag pill, since that governs so a tag-bearing card can
 * never be clipped even though this merchant's live Feefo data carries no
 * tags today, per feefo-reviews-api.php's own docblock):
 *   desktop (1512 frame, >=1024px) node ...:60253  334x290.21 -> 290.21
 *   tablet  (744  frame, 691-1023px) node ...:60253 334x288.21 -> 288.21
 *   mobile  (428  frame, <=690px)    node ...:60253 334x286.21 -> 286.21
 * No rounding, per this file's own established convention (see file
 * docblock "15.21px and 2.9915px are real, not approximations").
 */
.bfr-carousel--pdp .bfr-card {
	height: 286.21px;
}

@media (min-width: 691px) and (max-width: 1023px) {
	.bfr-carousel--pdp .bfr-card {
		height: 288.21px;
	}
}

@media (min-width: 1024px) {
	.bfr-carousel--pdp .bfr-card {
		height: 290.21px;
	}
}

/* One-card-per-slide (Figma node 34345:186657, the buy-box summary widget,
   ClickUp q2: "one card per slide"). Card fills the track's own width,
   whatever that is at the current breakpoint -- the buy-box column is
   already responsive, so no extra media query is needed here. */
.bfr-carousel--pdp-summary .bfr-card {
	flex: 0 0 100%;
}

/*
 * FIXED 2026-09-24 (ClickUp 86eypb6me, q18 review-card-height): fixed
 * height for the buy-box summary card, matching the WITH-photo/NO-photo
 * split this file's own `@media (max-width: 690px)` rule below already
 * draws for hiding the photo.
 *
 * WITH photo (>=691px): MEASURED (Figma, this session) node
 * I{frame}:76932;34345:186657 "Frame 2147229724" (the buy-box master
 * component) on both the DESKTOP (1512) and TABLET (744) "BASIC PRODUCT
 * PAGE" frames -- IDENTICAL at both, height 256px, photo layer (Rectangle
 * 9785) `visible: true`, 200x200. Figma-exact, no deviation needed: the
 * fixed 200px photo already governs (200 + 28px*2 padding = 256), taller
 * than this card's own text column at every content length this file's
 * body-clamp allows, so 256px cannot clip real content.
 *
 * WITHOUT photo (<=690px): REVISED 2026-09-24, second pass (client
 * decision via team lead): use Figma's own mobile (428) number VERBATIM,
 * 223.21px (node I29629:112443;34345:186657, photo layer `visible:
 * false`), rather than the previous session's 286.21px deviation. That
 * deviation existed because a FULL 4-line/80px body clamp does not fit
 * inside 223.21px without clipping the divider/meta row -- true, but the
 * fix is to shrink the CLAMP to match Figma's own real height, not grow
 * the height past what Figma draws. Figma's text column here (Frame
 * 2147229953, MEASURED this session) is 167.21px = stars(15.21) +
 * body(60) + divider(~0) + meta(44) + 3x16px gaps(48), i.e. Figma's own
 * body text genuinely renders at exactly 60px = 3 lines at this
 * component's 20px line-height, not a rounding artifact. See
 * `.bfr-carousel--pdp-summary .bfr-card__body`'s own override below
 * (max-width: 690px) for the matching 3-line/60px clamp -- with that
 * clamp in place, 223.21px never clips real content, because nothing can
 * render past 3 lines any more.
 */
@media (max-width: 690px) {
	.bfr-carousel--pdp-summary .bfr-card {
		height: 223.21px;
	}
}

@media (min-width: 691px) {
	.bfr-carousel--pdp-summary .bfr-card {
		height: 256px;
	}
}

/*
 * FIXED 2026-09-24 (ClickUp 86eypb6me, q18 S32, rerouted from q18-buybox):
 * the buy-box summary carousel passes `show_photo => true` at every
 * breakpoint (bonza_pdp_feefo_summary_carousel(), product-page/feefo.php --
 * that flag matches Figma's master component 34345:186657, whose photo slot
 * is NOT hidden). MEASURED live this session at 425px: the 200px-tall photo
 * consumes most of the buy-box column before any review text is visible.
 * Figma's own MOBILE frame for this same widget (screenshot, ClickUp q18
 * Image #57) draws the card text-only -- no photo rectangle at all -- so
 * this is a genuine per-breakpoint design difference, not a CSS oversight
 * to "fix" by shrinking the image. 690px matches this file's sibling
 * product-page-subscribe-band.css, which already uses Blocksy's real mobile
 * breakpoint (690px, not the commonly-assumed 768px) for its own
 * mobile-only rule. Scoped to `--pdp-summary` only -- the reviews-tab
 * carousel (`--pdp`) already renders with `show_photo => false` on a
 * product page regardless of breakpoint (see bonza_pdp_feefo_widget()'s own
 * docblock), so it needs no change here.
 */
@media (max-width: 690px) {
	.bfr-carousel--pdp-summary .bfr-card__photo {
		display: none;
	}
}

/* MEASURED node 35066:65516 (card, autoLayout VERTICAL, itemSpacing 16):
 * `--section` needs its own direction/gap, unlike `--pdp`/`--pdp-summary`
 * above which already override the shared row-based `.bfr-card` base. */
.bfr-card--section {
	flex-direction: column;
	flex-wrap: nowrap;
	gap: 16px;
}

/* 86eypb6m6: homepage (S15) card height, Figma 2026-09-23 SignOff frames,
 * node 35066:65516 area: 290.21 desktop, 288.21 tablet, 286.21 mobile
 * (live hugged content at 296). Same values as the --pdp cards above. */
.bfr-carousel:not(.bfr-carousel--pdp):not(.bfr-carousel--pdp-summary) .bfr-card--section {
	height: 286.21px;
}
@media (min-width: 691px) and (max-width: 1023px) {
	.bfr-carousel:not(.bfr-carousel--pdp):not(.bfr-carousel--pdp-summary) .bfr-card--section {
		height: 288.21px;
	}
}
@media (min-width: 1024px) {
	.bfr-carousel:not(.bfr-carousel--pdp):not(.bfr-carousel--pdp-summary) .bfr-card--section {
		height: 290.21px;
	}
}

/* B19/S9: a card with team-typed chips or a dog line carries two extra rows
 * (about 48px). The fixed heights above would squeeze the review text to one
 * clipped line, so these cards keep the same height as a floor and grow. Cards
 * with no entry never get this class and render exactly as before.
 * Scoped on purpose: the reviews-tab (--pdp) and homepage section cards use the
 * 286.21/288.21/290.21 floor; the buy-box (--pdp-summary) keeps its own 256px
 * (223.21px at 690px and below) floor; the category carousel gets no rule, since
 * its stretch alignment would pull every sibling card up to the floor. Rating-only
 * (--no-body) cards are left out so they keep hugging their content. On the
 * homepage section chain the selector out-specifies the :not() height rule; on
 * --pdp / --pdp-summary it ties the base height rule and wins by source order. */
.bfr-carousel--pdp .bfr-card.bfr-card--has-extras:not(.bfr-card--no-body),
.bfr-carousel:not(.bfr-carousel--pdp):not(.bfr-carousel--pdp-summary):not(.bfr-carousel--category) .bfr-card--section.bfr-card--has-extras:not(.bfr-card--no-body) {
	height: auto;
	min-height: 286.21px;
}
@media (min-width: 691px) and (max-width: 1023px) {
	.bfr-carousel--pdp .bfr-card.bfr-card--has-extras:not(.bfr-card--no-body),
	.bfr-carousel:not(.bfr-carousel--pdp):not(.bfr-carousel--pdp-summary):not(.bfr-carousel--category) .bfr-card--section.bfr-card--has-extras:not(.bfr-card--no-body) {
		min-height: 288.21px;
	}
}
@media (min-width: 1024px) {
	.bfr-carousel--pdp .bfr-card.bfr-card--has-extras:not(.bfr-card--no-body),
	.bfr-carousel:not(.bfr-carousel--pdp):not(.bfr-carousel--pdp-summary):not(.bfr-carousel--category) .bfr-card--section.bfr-card--has-extras:not(.bfr-card--no-body) {
		min-height: 290.21px;
	}
}
.bfr-carousel--pdp-summary .bfr-card.bfr-card--has-extras:not(.bfr-card--no-body) {
	height: auto;
	min-height: 256px;
}
@media (max-width: 690px) {
	.bfr-carousel--pdp-summary .bfr-card.bfr-card--has-extras:not(.bfr-card--no-body) {
		min-height: 223.21px;
	}
}

.bfr-card__photo {
	flex: 0 0 200px;
	width: 200px;
	height: 200px;
	max-width: 100%;
	border-radius: 12px;
	overflow: hidden;
}

.bfr-card__photo img {
	display: block;
	width: 100%;
	height: 100%;
	-o-object-fit: contain;
	   object-fit: contain;
	border-radius: 12px;
}

/* S15 (13 Oct revisions): a customer photo is shown whole, never cropped,
   with any spare space in a neutral fill rather than cutting off the dog. */
.bfr-card__photo {
	background: #F4EFE8;
}

/*
 * ADDED 2026-09-24 (86eypb6me, q19 follow-up): `height: 100%` so this
 * column fills `.bfr-card`'s own fixed per-breakpoint/context height
 * (its containing block, box-sizing:border-box, so the percentage
 * resolves against a definite content-box height) instead of hugging
 * its own content. Without this, `.bfr-card__body-wrap`'s `flex: 1 1
 * auto` below has no real free space to grow into or centre inside --
 * a flex column with an auto (content-based) height never has leftover
 * space for a flex-growing child to claim.
 */
.bfr-card__col {
	display: flex;
	flex-direction: column;
	gap: 16px;
	flex: 1 1 240px;
	width: 100%;
	height: 100%;
	min-width: 0; /* the classic nested-flexbox text-wrap bug: a flex item's automatic min-width
	                 is its content's MAX-content size (the width if the text never wrapped),
	                 not min-content, so a non-zero min-width here (even min(240px,100%)) let the
	                 review-body <p> refuse to wrap and overflow the 320/375/425 screenshots.
	                 Zeroing this is the standard fix; flex-basis:240px above still gives it its
	                 intended width whenever there IS room, this only removes the wrap-breaking
	                 floor. */
	max-width: 100%;
}

.bfr-card__stars {
	display: flex;
	align-items: center;
	gap: 2.9915px;
}

.bfr-card__star svg {
	display: block;
	width: 15.21px;
	height: 15.21px;
}

.bfr-card__star--empty svg {
	opacity: 0.25; /* see file docblock "EMPTY STARS": no separate outline asset in the build spec. */
}

/*
 * ADDED 2026-09-24 (86eypb6me, q19 follow-up x2): the divider/name row
 * below used to sit directly after `.bfr-card__body` in the column's
 * normal flow, so a short review left it floating high under a mostly-
 * empty card instead of level with every other card's divider in the
 * row. `flex: 1 1 auto` makes this wrapper absorb ALL the column's
 * leftover vertical space (stars/tags/divider/meta are all fixed-
 * height, so "leftover" is a constant for a given breakpoint/context,
 * whatever the review's own length) -- that pins the divider+meta block
 * flush to the column's bottom on every card at once, no separate
 * margin-top:auto needed on the divider itself. `justify-content:
 * center` then centres the clamped body text vertically inside that
 * same absorbed space, per the user's follow-up ask. `min-height: 0`
 * is the standard fix for a flex column item containing text that would
 * otherwise refuse to shrink below its content height and overflow the
 * fixed-height card. The clamp itself stays entirely on the inner <p>
 * (feefo-reviews-api.php now wraps it in this element) -- clamping
 * needs `display: -webkit-box` on the clamped element, which cannot
 * also be `display: flex`, so the two responsibilities are split across
 * wrapper and child rather than doubled up on one element.
 */
.bfr-card__body-wrap {
	flex: 1 1 auto;
	min-height: 0;
	display: flex;
	flex-direction: column;
	justify-content: center;
}

.bfr-card__body {
	margin: 0;
	width: 100%;
	min-width: 0;
	font-family: var(--wp--preset--font-family--bhp-body, "Kobe1.1", sans-serif);
	font-weight: 400;
	font-size: 16px;
	line-height: 20px; /* unitless-equivalent: 20/16 = 1.25, kept in px per this file's existing sibling components' convention (checkout-order-summary.css etc.) */
	color: var(--bfr-ink);
	overflow-wrap: anywhere; /* stronger than word-break:break-word: real review bodies carry
	                            emoji grapheme clusters (measured live, e.g. a dog-breed emoji
	                            run) that word-break alone did not reliably wrap in the 320px
	                            screenshot check. */
	/*
	 * FIXED 2026-09-24 (ClickUp 86eypb6me, q18 S16 + the duplicate "clip the
	 * review paragraphs" ask): real review bodies range 40-1188 characters
	 * live (MEASURED this session on 4 adjacent cards: 242/561/567/1188
	 * chars), so an unclamped .bfr-card__body let card height swing
	 * 298-814px in the SAME row -- Figma's own card is a fixed height, not
	 * content-driven. Figma's body TEXT NODE (I21316:76932;35066:60262,
	 * MEASURED this session) has an absoluteBoundingBox height of exactly
	 * 80px at this same 20px line-height = 4 lines, not a rounded guess.
	 * -webkit-line-clamp is the standard multi-line-ellipsis mechanism (no
	 * pure-CSS equivalent exists); max-height is a non-WebKit fallback that
	 * still caps the box even where the clamp itself is unsupported.
	 */
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 4;
	max-height: 80px;
	overflow: hidden;
}

/*
 * FIXED 2026-09-24 (ClickUp 86eypb6me, q18 review-card-height): the 4-line/
 * 80px clamp above is only correct for the buy-box WITH-photo card (256px
 * fixed height). The other two fixed-height contexts this file now sets
 * (see `.bfr-carousel--pdp .bfr-card` / `.bfr-carousel--pdp-summary
 * .bfr-card` above) have MORE body room once their real content -- no
 * tags, no "Feefo Verified" badge line (removed, see
 * includes/feefo-reviews-api.php) -- is laid out inside them, and N must
 * be a WHOLE number of `.bfr-card__body`'s own 20px line-height so the
 * clamp never cuts a line in half.
 *
 * WORKED (fixed height - 2x28px padding - 15.21px stars row - 1px divider
 * - 44px avatar-driven meta row - 3x16px column gaps = body budget,
 * floor-divided by the 20px line-height):
 *   `--pdp` reviews-row, every breakpoint: 290.21/288.21/286.21 - 164.21 =
 *     126.00/124.00/122.00 -> floor(.../20) = 6 lines at all three, so one
 *     rule below covers every breakpoint (no separate media queries
 *     needed here, unlike the height rules).
 *   `--pdp-summary` buy-box, no-photo (<=690px, 286.21px): identical
 *     budget to the reviews-row's own mobile figure (same no-photo shape,
 *     same fixed height, deliberately reused -- see that rule's own
 *     docblock) -> 6 lines too.
 *   `--pdp-summary` buy-box, WITH photo (>=691px, 256px): 256-164.21 =
 *     91.79 -> floor(91.79/20) = 4 lines -- the base rule above already
 *     matches, no override needed.
 */
.bfr-carousel--pdp .bfr-card__body {
	-webkit-line-clamp: 6;
	max-height: 120px;
}

/*
 * REVISED 2026-09-24, second pass (client decision via team lead): the
 * buy-box no-photo card's height above reverted to Figma's exact
 * 223.21px, so its clamp must shrink to match instead of growing the
 * card -- 6 lines/120px no longer fits (budget worked out below), and
 * Figma's own mobile mock genuinely renders exactly 3 lines/60px of body
 * text in this shape (see the height rule's own docblock above: MEASURED
 * Frame 2147229953 = stars 15.21 + body 60 + divider ~0 + meta 44 +
 * 3x16px gaps 48 = 167.21, the card's real text-column height). Re-worked
 * against this file's own CSS geometry (28px*2 padding, 1px divider,
 * 44px avatar-driven meta, 15.21px stars, 16px column gaps): 223.21 -
 * 164.21 = 59.00px of true body budget, one px under a clean 3x20 -- the
 * 1px gap is Figma's hairline divider (Vector 119) measuring 0 height
 * against this file's rendered 1px `.bfr-card__divider`, absorbed by
 * `.bfr-card`'s own `overflow: hidden` safety clip (see that rule's
 * docblock), not a real content cut.
 */
@media (max-width: 690px) {
	.bfr-carousel--pdp-summary .bfr-card__body {
		-webkit-line-clamp: 3;
		max-height: 60px;
	}
}

/*
 * FIXED 2026-09-24 (ClickUp 86eypb6me, q18 review-card-height): CAUGHT
 * during the after-measurement pass for the line-clamp fix above --
 * `getComputedStyle` on a live `.bfr-carousel--pdp .bfr-card__body`
 * reported `line-height: 22.4px`, not this file's own `20px`. Named the
 * winning rule via `document.styleSheets`/`el.matches()`
 * (recurring-mistakes-register #30/#153/#154, the exact pattern this
 * project's own product-page-marketing.css already documents fixing once
 * for `.bppm-eyebrow`, see that file's line ~191 comment): the reviews
 * section wrapper is `<section class="bppm-section bppm-reviews">`
 * (product-page-marketing.php), and that file's `.bppm-section p`
 * (0,1,1: `line-height: 1.4` = 22.4px at this 16px font-size) outranks
 * this rule's plain `.bfr-card__body` (0,1,0) -- a class beats a class,
 * but a class+type beats a class alone, same as the eyebrow bug. This
 * ONLY hits the `--pdp` reviews-row carousel (nested inside `.bppm-
 * reviews`) -- the buy-box `--pdp-summary` widget lives in a different,
 * non-`.bppm-section` part of the page and already measured a correct
 * 20px, confirmed live.
 *
 * This is not cosmetic: at the wrong 22.4px, `-webkit-line-clamp: 6`
 * needs 6 x 22.4 = 134.4px to draw 6 whole lines, but `max-height: 120px`
 * above physically caps the box at 120px -- 14.4px short, enough to
 * slice through the middle of the 6th line instead of showing it whole
 * with an ellipsis. `.bppm-section .bfr-card__body` (0,2,0) reliably
 * outranks `.bppm-section p` (0,1,1) regardless of source order,
 * restoring the file's own intended 20px so `max-height: 120px` (exactly
 * 6 x 20) draws 6 whole lines with a real trailing "…", not a slice.
 */
.bppm-section .bfr-card__body {
	font-family: var(--wp--preset--font-family--bhp-body, "Kobe1.1", sans-serif);
	line-height: 20px;
}

/* Tag pills (Figma node 35066:60246, e.g. "Frame 2147229742" holding
   "Digestion"): cream fill, 6px radius, 8/10px padding, 0.8 opacity on the
   pill itself, 16px gap between pills in a row, 14px Kobe1.1 Bold text.
   ALWAYS empty today -- see includes/feefo-reviews-api.php's own docblock
   on bonza_feefo_normalise_review() for the live-verified absence of any
   tag/attribute field on this merchant's Feefo data; this styling exists so
   the markup renders correctly the moment that ever changes, never before. */
.bfr-card__tags {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 16px;
	width: 100%;
}

.bfr-card__tag {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	opacity: 0.8;
	padding: 8px 10px;
	border-radius: 6px;
	background: var(--bfr-tag-bg);
	color: var(--bfr-ink);
	font-family: var(--wp--preset--font-family--bhp-body, "Kobe1.1", sans-serif);
	font-weight: 700;
	font-size: 14px;
	line-height: 1;
}

.bfr-card__divider {
	width: 100%;
	height: 1px;
	background: var(--bfr-divider);
}

.bfr-card__meta {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
}

.bfr-card__avatar {
	display: flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 44px;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	background: var(--bfr-amber);
	color: var(--bfr-ink); /* see file docblock "ACCESSIBILITY": ~7.6:1, AAA. White-on-amber here measures ~1.97:1 and fails WCAG 1.4.3. */
	font-family: var(--wp--preset--font-family--bhp-body, "Kobe1.1", sans-serif);
	font-weight: 700;
	font-size: 14px;
	line-height: 1;
	text-transform: uppercase;
}

.bfr-card__identity {
	display: flex;
	flex-direction: column;
	gap: 2px;
}

/* MEASURED (Figma node 35066:60246 "Sarah K." text node, 2026-09-23, q21):
   Nominee Heavy Extended 900, 14px/16px, 2% letter-spacing -- corrected from
   this file's previous Kobe1.1 700/18px guess. 2% letter-spacing is Figma's
   percent-of-font-size convention; CSS letter-spacing has no % unit, so per
   this project's own conversion rule (percent / 100 = em) that is 0.02em,
   not a literal "2%". 'Nominee' with the site's existing
   var(--theme-font-family) fallback matches product-page-marketing.css's
   own established heading-font declaration pattern, not a new one. */
.bfr-card__name {
	font-family: 'Nominee', var(--theme-font-family, sans-serif);
	font-weight: 900;
	font-size: 14px;
	line-height: 1.4; /* was 16px (1.14x), 86eypb6j8 */
	letter-spacing: 0.02em;
	color: var(--bfr-name-ink);
}

/* Dog line under the reviewer name ("Mochi • Standard Poodle • 4"), Figma node
   35066:60246: Kobe1.1 Regular 14px/20px. Typed in Settings > Feefo review
   details (includes/feefo-review-extras.php); absent when a review has no line. */
.bfr-card__dog {
	font-family: var(--wp--preset--font-family--bhp-body, "Kobe1.1", sans-serif);
	font-weight: 400;
	font-size: 14px;
	line-height: 20px;
	color: var(--bfr-name-ink);
}

/* `.bfr-card__badge` ("Feefo Verified" link) REMOVED 2026-09-24 (ClickUp
   86eypb6me, q18 review-card-height, client decision): the markup that used
   it is gone from bonza_feefo_render_card() (includes/feefo-reviews-api.php)
   -- see that file for the removal note. No rule left here to go stale. */

/* Dots are <button>s injected by js/feefo-carousel.js (server renders the
   container empty). Size, gap and colours come from the shared --bonza-dot-*
   tokens in css/carousel-dots.css (Figma "DOT - PAGE INDICATOR"); the fallbacks
   repeat the Figma values. 20px vertical padding = the Figma 48px wrapper. */
.bfr-carousel__dots {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--bonza-dot-gap, 12px);
	padding: 20px 0;
}

.bfr-carousel__dots[hidden] {
	display: none;
}

.bfr-carousel__dot {
	display: inline-block;
	width: var(--bonza-dot-size, 8px);
	height: var(--bonza-dot-size, 8px);
	padding: 0;
	border: 0;
	border-radius: 9999px;
	background: var(--bonza-dot-review-inactive, #868889);
	cursor: pointer;
	transition: width 0.2s ease, background-color 0.2s ease;
}

.bfr-carousel__dot.is-active {
	width: var(--bonza-dot-pill, 32px);
	background: var(--bonza-dot-review-active, #ff9572);
}

/* PDP reviews row sits on the dark green frame: Figma's on-green pair. */
.bfr-carousel--pdp .bfr-carousel__dot {
	background: var(--bonza-dot-on-green-inactive, #f8ede3);
}

.bfr-carousel--pdp .bfr-carousel__dot.is-active {
	background: var(--bonza-dot-on-green-active, #ff9572);
}

/*
 * editor-friendly-ok: same documented exception as the file docblock above
 * (component with no Gutenberg block/pattern counterpart, no matching
 * --wp--preset--color--* slug for either literal below).
 *
 * FIXED 2026-09-24 (ClickUp 86eypb6me, q18 S6): the buy-box summary widget
 * (`--pdp-summary`) sits directly on the page's own body background, which
 * MEASURED live this session is rgb(248,237,227) = #F8EDE3 -- IDENTICAL to
 * --bfr-tag-bg, the inactive-dot fill shared with the `--pdp` reviews-row
 * context. Zero contrast: the 6-7 inactive dots were rendering invisibly,
 * leaving only the active pill visible (what q18's screenshot shows). This
 * is NOT the same context Figma measured for the cream dot: node
 * I21316:76932;35066:60366 (the `--pdp` reviews-row, whose own parent frame
 * fills a dark green, #247B4E-adjacent) is genuinely cream-on-green there.
 * The buy-box widget's OWN instance (node I21316:76932;34350:73717,
 * MEASURED separately this session) uses a DIFFERENT pair entirely: inactive
 * Ellipse 40 fills rgb(1,1,1)=#FFFFFF (white, visible against the cream page
 * bg), active Rectangle 3392 fills rgb(1,0.7569,0.1451)=#FFC125 (gold, not
 * the reviews-row's #FF9572 coral) -- confirmed real, not a rounding
 * artifact of --bfr-amber (#FFA825): the green channel differs by 25 (193 vs
 * 168). Per-instance override, not a shared-default edit (recurring-mistakes
 * register category 3).
 */
.bfr-carousel--pdp-summary .bfr-carousel__dot {
	background: var(--bonza-dot-inactive, #878787);
}

.bfr-carousel--pdp-summary .bfr-carousel__dot.is-active {
	background: var(--bonza-dot-active, #ffc125);
}

.bfr-carousel__dot:focus-visible {
	outline: 2px solid var(--bfr-stroke);
	outline-offset: 2px;
}

/* editor-friendly-ok: the card's own INTERNAL geometry (padding/gap/star
   size/avatar) stays static across Figma's export at every breakpoint (same
   pattern the pre-2026-09-22 revision of this file documented for the
   widget container); `flex-wrap: wrap` on `.bfr-card` itself (the
   photo/column pair inside ONE card) still handles narrow viewports the
   same way it always did. What CHANGED 2026-09-23 (q21/q22) is the
   CAROUSEL-level layout: `.bfr-carousel__track` is no longer
   `flex-wrap:wrap` (see the file-level MEASURED note and the SIZING note
   above) -- it is a `nowrap` horizontally-scrolling row at every
   breakpoint, matching Figma's single-line "peek" carousel, with
   `.bfr-card`'s own width narrowing fluidly below 1024px rather than
   stacking into multiple rows. */

/*
 * Bites category reviews carousel: equal-height cards, no tall photo card
 * (ClickUp z8udzkgngn and z8udzkgrgv, QA re-test 20 Oct).
 *
 * MEASURED on the live v1 category page (chromium): 19 cards, heights 164 to
 * 244px, and one 468px card (the 19th, a review with a 200px customer photo).
 * The track takes the height of its tallest card, so the track was 468px and
 * about 224px of empty space sat under the visible card on mobile. The cards
 * were also different heights because the track top-aligned them.
 *
 * The category strip is a text-review band, like the product page carousel
 * (Figma draws that component with its photo rectangle hidden). So the photo
 * is not drawn here, the cards stretch to one shared height, and the track
 * follows the tallest text card (244px at 384px, measured). Photos still show
 * wherever a carousel opts in with show_photo.
 */
.bfr-carousel--category .bfr-carousel__track {
	align-items: stretch;
}

.bfr-carousel--category .bfr-card__photo {
	display: none;
}

/* Rating-only reviews (z8udzkgnh0): no body text, so the card shows stars, name and the real date. */
.bfr-card__date {
	display: block;
	font-size: 14px;
	line-height: 20px;
	color: #5E5856; /* 7.0:1 on white, WCAG AA */
}

/* Rating-only cards hug their content (z8udzkgnh0, Lan 23 Oct): every fixed `height` above is for
   text/photo cards; with no body the stars-to-name gap was dead space. `height:auto` beats the
   per-breakpoint rules (0,5,0 > 0,4,0), `align-self:flex-start` keeps the card top-aligned in the
   track (the category track stretches), and the divider keeps its normal flow gap. */
.bfr-carousel .bfr-carousel__track > .bfr-card.bfr-card--no-body {
	height: auto;
	min-height: 0;
	align-self: flex-start;
}

.bfr-card--no-body .bfr-card__col {
	height: auto;
}
