/**
 * Librarian for Recencio — Frontend Styles
 *
 * All visual decisions use CSS custom properties with inherit defaults
 * for theme compatibility. All selectors scoped under .lbr-wrap.
 * Visual design matches the prototype: Lora + Open Sans typography,
 * dark gradient header, card grid, filter pills.
 *
 * @package Librarian
 * @since   1.0.0
 */

/* --------------------------------------------------------
   CSS Custom Properties
   -------------------------------------------------------- */

:root {
	/* Typography */
	--lbr-font-family: inherit;
	--lbr-font-heading: 'Lora', Georgia, serif;
	--lbr-font-body: 'Open Sans', Arial, sans-serif;
	--lbr-font-size-base: 16px;
	--lbr-font-size-heading: 1.5em;
	--lbr-font-size-small: 0.875em;
	--lbr-line-height: inherit;

	/* Colours — matches prototype palette */
	--lbr-primary-color: #283B91;
	--lbr-primary-hover: #131D48;
	--lbr-heading-color: #131D48;
	--lbr-text-color: #5A5A5A;
	--lbr-text-muted: #9B9C9F;
	--lbr-background: #FAFBFD;
	--lbr-card-background: #FFFFFF;
	--lbr-alt-background: #F2F4F5;
	--lbr-border-color: #D6D9DD;

	/* Cards */
	--lbr-card-radius: 10px;
	--lbr-card-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.04);
	--lbr-card-shadow-hover: 0 8px 24px rgba(40, 59, 145, 0.12), 0 2px 6px rgba(0, 0, 0, 0.06);
	--lbr-card-padding: 1rem;

	/* Layout */
	--lbr-grid-gap: 28px 20px;
	--lbr-section-spacing: 2rem;
	--lbr-max-width: 1200px;
	--lbr-btn-radius: 100px;

	/* Search */
	--lbr-input-background: rgba(255, 255, 255, 0.08);
	--lbr-input-border: rgba(255, 255, 255, 0.15);
	--lbr-input-border-focus: rgba(255, 255, 255, 0.3);
	--lbr-input-radius: 10px;

	/* Chat */
	--lbr-chat-background: #f9fafb;
	--lbr-chat-user-bubble: var(--lbr-primary-color);
	--lbr-chat-assistant-bubble: #f3f4f6;
}

/* --------------------------------------------------------
   Animations
   -------------------------------------------------------- */

@keyframes lbr-chipIn {
	from {
		opacity: 0;
		transform: translateY(6px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

@keyframes lbr-fadeIn {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}

@keyframes lbr-gridIn {
	from {
		opacity: 0;
		transform: translateY(12px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

/* --------------------------------------------------------
   Base Wrapper
   -------------------------------------------------------- */

.lbr-wrap {
	--lbr-hero-base: #283B91;
	font-family: var(--lbr-font-body);
	font-size: var(--lbr-font-size-base);
	line-height: var(--lbr-line-height);
	/* Match text rendering across themes: BuddyBoss antialiases
	   globally, default themes don't — pin it so type looks the same. */
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	/* Break out of WordPress block theme constrained layout.
	   The alignfull class on the mount div tells the block theme
	   to remove the max-width constraint. */
	color: var(--lbr-text-color);
	background: var(--lbr-background);
	box-sizing: border-box;
}

.lbr-wrap *,
.lbr-wrap *::before,
.lbr-wrap *::after {
	box-sizing: border-box;
}

/* --------------------------------------------------------
   Header Section (brand gradient, driven by --lbr-hero-base)
   -------------------------------------------------------- */

.lbr-wrap .lbr-header-section {
	/* Solid fallback first for browsers without color-mix(). */
	background: var(--lbr-hero-base, #283B91);
	background: linear-gradient(135deg, var(--lbr-hero-base, #283B91) 0%, color-mix(in srgb, var(--lbr-hero-base, #283B91) 78%, #ffffff) 100%);
	position: relative;
	overflow: hidden;
}

/* The suggestion list is the one thing here entitled to leave the hero:
   it belongs to the input, not to the banner, and five rows is taller
   than the space below the box. The clip is only lifted while the list
   is open, so nothing else gains permission to bleed. */
/* While the author list is open the hero must stop clipping AND out-rank
   what follows it. Letting it out of the box is not enough on its own:
   the header and the content below are both position:relative siblings,
   so without a z-index the later one wins on document order and the
   list is covered the moment it crosses the fold. */
.lbr-wrap .lbr-header-section--suggesting {
	overflow: visible;
	z-index: 40;
}

.lbr-wrap .lbr-header-section::before {
	content: '';
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: radial-gradient(ellipse at 30% 50%, rgba(255, 255, 255, 0.05) 0%, transparent 70%);
	pointer-events: none;
}

/* --------------------------------------------------------
   Hero / Search Area
   -------------------------------------------------------- */

.lbr-wrap .lbr-hero {
	padding: 48px 48px 20px;
	text-align: center;
	position: relative;
}

.lbr-wrap .lbr-hero-title {
	color: var(--lbr-hero-text, rgba(255, 255, 255, 0.92)) !important;
	font-family: var(--lbr-font-heading);
	font-size: 48px;
	font-weight: 400;
	line-height: 1.2;
	margin: 0 0 10px;
}

.lbr-wrap .lbr-hero-subtitle {
	color: var(--lbr-hero-text-muted, rgba(255, 255, 255, 0.45)) !important;
	font-family: var(--lbr-font-body);
	font-size: 15px;
	margin: 0 0 36px;
}

.lbr-wrap .lbr-search-wrap {
	max-width: 640px;
	margin: 0 auto;
	position: relative;
}

.lbr-wrap .lbr-search-input {
	width: 100%;
	/* Height comes from padding, not a fixed box: themes (BuddyBoss)
	   pin input heights globally, which crowds the 18px serif text. */
	height: auto;
	padding: 16px 96px 16px 24px;
	border: 1px solid var(--lbr-input-border);
	border-radius: var(--lbr-input-radius);
	background: var(--lbr-input-background);
	-webkit-backdrop-filter: blur(10px);
	backdrop-filter: blur(10px);
	color: white;
	font-family: var(--lbr-font-heading);
	font-size: 18px;
	font-style: italic;
	outline: none;
	transition: all 0.3s ease;
}

.lbr-wrap .lbr-search-input::placeholder {
	color: rgba(255, 255, 255, 0.35);
	font-style: italic;
	/* Placeholder must match typed text: themes (BuddyBoss) style
	   ::placeholder globally with a smaller size and a -5px nudge. */
	font-size: inherit;
	font-family: inherit;
	position: static;
	top: auto;
}

.lbr-wrap .lbr-search-input:focus {
	border-color: var(--lbr-input-border-focus);
	background: rgba(255, 255, 255, 0.12);
	box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}

/* Search buttons */

.lbr-wrap .lbr-search-btns {
	position: absolute;
	right: 8px;
	top: 50%;
	transform: translateY(-50%);
	display: flex;
	gap: 4px;
}

.lbr-wrap .lbr-search-btn,
.lbr-wrap .lbr-clear-btn {
	width: 40px;
	height: 40px;
	border-radius: 8px;
	border: none;
	background: rgba(255, 255, 255, 0.12);
	color: rgba(255, 255, 255, 0.7);
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: all 0.2s;
	font-size: 18px;
}

.lbr-wrap .lbr-search-btn:hover,
.lbr-wrap .lbr-clear-btn:hover {
	background: rgba(255, 255, 255, 0.2);
	color: white;
}

.lbr-wrap .lbr-clear-btn {
	font-size: 14px;
	display: none;
}

.lbr-wrap .lbr-clear-btn--visible {
	display: flex;
}

/* Author suggestions
   ------------------------------------------------------------------
   Anchored to .lbr-search-wrap (already position:relative) and taken
   out of flow: the hint and chips below must not move as the list
   opens and closes under the reader's hands. */

.lbr-wrap .lbr-suggest {
	position: absolute;
	top: calc(100% + 6px);
	left: 0;
	right: 0;
	z-index: 30;
	/* Five rows show, the rest are a scroll away. The cap itself is set in
	   JS from the height of a row that actually rendered (sizeSuggestions)
	   — CSS guessing that number is what sliced the fifth name in half.
	   overscroll-behavior keeps a flick inside the list instead of
	   handing the page the leftovers. */
	overflow-y: auto;
	overscroll-behavior: contain;
	/* Momentum scrolling on touch, where this list is most likely to be
	   flicked rather than wheeled. */
	-webkit-overflow-scrolling: touch;
	border: 1px solid var(--lbr-input-border);
	border-radius: var(--lbr-input-radius);
	background: rgba(20, 28, 68, 0.92);
	-webkit-backdrop-filter: blur(10px);
	backdrop-filter: blur(10px);
	box-shadow: 0 16px 40px rgba(0, 0, 0, 0.28);
	text-align: left;
}

.lbr-wrap .lbr-suggest[hidden] {
	display: none;
}

/* "There is more below": the last visible row fades out at the bottom
   edge. A list of five that looks complete gives the reader no reason to
   try scrolling — and the sixth name may be the one they want. The mask
   is applied to the scroll container's own box, so it stays pinned to
   the bottom edge as the content moves under it, and the class comes off
   the moment the end is reached. */
.lbr-wrap .lbr-suggest--more {
	-webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 14px), rgba(0, 0, 0, 0.35) 100%);
	mask-image: linear-gradient(to bottom, #000 calc(100% - 14px), rgba(0, 0, 0, 0.35) 100%);
}

/* Slim scrollbar: on the platforms that show one, the default is a grey
   slab against a dark translucent panel. */
.lbr-wrap .lbr-suggest {
	scrollbar-width: thin;
	scrollbar-color: rgba(255, 255, 255, 0.25) transparent;
}

.lbr-wrap .lbr-suggest::-webkit-scrollbar {
	width: 8px;
}

.lbr-wrap .lbr-suggest::-webkit-scrollbar-thumb {
	background: rgba(255, 255, 255, 0.22);
	border-radius: 4px;
}

.lbr-wrap.lbr-hero--on-light .lbr-suggest {
	scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
}

.lbr-wrap.lbr-hero--on-light .lbr-suggest::-webkit-scrollbar-thumb {
	background: rgba(0, 0, 0, 0.22);
}

.lbr-wrap .lbr-suggest-item {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
	padding: 9px 16px;
	cursor: pointer;
	font-family: var(--lbr-font-body);
	color: rgba(255, 255, 255, 0.82);
}

/* Hover and keyboard highlight are the SAME state — with the focus ring
   still on the input, two different "here" markers would be one too
   many. */
.lbr-wrap .lbr-suggest-item--active {
	background: rgba(255, 255, 255, 0.12);
	color: #fff;
}

.lbr-wrap .lbr-suggest-name {
	font-size: 14px;
}

.lbr-wrap .lbr-suggest-count {
	flex: 0 0 auto;
	font-size: 11px;
	color: rgba(255, 255, 255, 0.45);
	white-space: nowrap;
}

.lbr-wrap.lbr-hero--on-light .lbr-suggest {
	background: rgba(255, 255, 255, 0.97);
	border-color: rgba(0, 0, 0, 0.14);
}

.lbr-wrap.lbr-hero--on-light .lbr-suggest-item {
	color: rgba(19, 29, 72, 0.85);
}

.lbr-wrap.lbr-hero--on-light .lbr-suggest-item--active {
	background: rgba(19, 29, 72, 0.08);
	color: var(--lbr-heading-color);
}

.lbr-wrap.lbr-hero--on-light .lbr-suggest-count {
	color: rgba(19, 29, 72, 0.5);
}

/* Tab hint */

.lbr-wrap .lbr-tab-hint {
	text-align: center;
	margin-top: 10px;
	font-family: var(--lbr-font-body);
	font-size: 12px;
	color: rgba(255, 255, 255, 0.25);
	transition: opacity 0.3s;
}

.lbr-wrap .lbr-tab-hint kbd {
	display: inline-block;
	padding: 1px 6px;
	border: 1px solid rgba(255, 255, 255, 0.2);
	border-radius: 4px;
	font-family: var(--lbr-font-body);
	font-size: 11px;
	background: rgba(255, 255, 255, 0.08);
	color: rgba(255, 255, 255, 0.4);
	margin: 0 2px;
}

/* Light hero colours flip the text cluster to dark (auto-contrast —
   class set server-side from the hero colour's luminance) and re-skin
   the translucent interactive elements that assumed a dark ground. */
.lbr-wrap.lbr-hero--on-light {
	--lbr-hero-text: rgba(19, 29, 72, 0.92);
	--lbr-hero-text-muted: rgba(19, 29, 72, 0.55);
}

.lbr-wrap.lbr-hero--on-light .lbr-search-input {
	background: rgba(0, 0, 0, 0.05);
	border-color: rgba(0, 0, 0, 0.15);
	color: var(--lbr-heading-color);
}

.lbr-wrap.lbr-hero--on-light .lbr-search-input::placeholder {
	color: rgba(19, 29, 72, 0.4);
}

.lbr-wrap.lbr-hero--on-light .lbr-search-btn,
.lbr-wrap.lbr-hero--on-light .lbr-clear-btn {
	background: rgba(0, 0, 0, 0.08);
	color: rgba(19, 29, 72, 0.7);
}

.lbr-wrap.lbr-hero--on-light .lbr-hero .lbr-chips .lbr-chip {
	border-color: rgba(0, 0, 0, 0.18);
	background: rgba(0, 0, 0, 0.04);
	color: rgba(19, 29, 72, 0.75);
}

.lbr-wrap.lbr-hero--on-light .lbr-tab-hint {
	color: rgba(19, 29, 72, 0.45);
}

.lbr-wrap.lbr-hero--on-light .lbr-tab-hint kbd {
	border-color: rgba(0, 0, 0, 0.2);
	background: rgba(0, 0, 0, 0.05);
	color: rgba(19, 29, 72, 0.55);
}

/* --------------------------------------------------------
   Question Chips
   -------------------------------------------------------- */

.lbr-wrap .lbr-chips {
	display: flex;
	gap: 8px;
	justify-content: center;
	flex-wrap: wrap;
	margin-top: 16px;
	min-height: 36px;
}

/* During a search the hero row is empty (follow-ups live in the results
   header instead) — release its space rather than leaving a gap. */
.lbr-wrap .lbr-chips:empty {
	display: none;
}

/* The hero chips are the one row with no container of their own, so on a
   wide screen they wrap out to the viewport edge while everything below
   stops at the catalogue. Match .lbr-main's content box — its max-width
   less its 48px gutters — and the block ends where the covers do. Below
   1200px both sides are gutter-driven and already agree. */
.lbr-wrap .lbr-hero .lbr-chips {
	max-width: calc(var(--lbr-max-width) - 96px);
	margin-left: auto;
	margin-right: auto;
}

.lbr-wrap .lbr-chip {
	padding: 6px 14px;
	border-radius: var(--lbr-btn-radius);
	border: 1px solid rgba(255, 255, 255, 0.15);
	background: rgba(255, 255, 255, 0.06);
	color: rgba(255, 255, 255, 0.6);
	font-family: var(--lbr-font-body);
	font-size: 12px;
	cursor: pointer;
	transition: all 0.2s;
	opacity: 0;
	animation: lbr-chipIn 0.3s ease forwards;
}

.lbr-wrap .lbr-chip:hover {
	background: rgba(255, 255, 255, 0.14);
	color: rgba(255, 255, 255, 0.9);
	border-color: rgba(255, 255, 255, 0.25);
}

/* --------------------------------------------------------
   Stats Section
   -------------------------------------------------------- */

.lbr-wrap .lbr-stats {
	display: flex;
	gap: 48px;
	justify-content: center;
	padding: 28px 48px 32px;
	position: relative;
}

.lbr-wrap .lbr-stat {
	text-align: center;
}

.lbr-wrap .lbr-stat-num {
	color: var(--lbr-hero-text, rgba(255, 255, 255, 0.85));
	font-family: var(--lbr-font-heading);
	font-size: 28px;
	font-weight: 600;
}

.lbr-wrap .lbr-stat-label {
	color: var(--lbr-hero-text-muted, rgba(255, 255, 255, 0.35));
	font-family: var(--lbr-font-body);
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: 1.5px;
	margin-top: 2px;
}

/* Masthead form: the classic stat blocks, ABOVE the search box —
   orientation happens before asking. Same serif numerals and small-caps
   labels, tightened so the row sits comfortably over the input. */
.lbr-wrap .lbr-stats--masthead {
	gap: 40px;
	padding: 0 24px 22px;
}

.lbr-wrap .lbr-stats--masthead .lbr-stat-num {
	font-size: 24px;
}

/* --------------------------------------------------------
   Main Content Area
   -------------------------------------------------------- */

.lbr-wrap .lbr-main {
	max-width: var(--lbr-max-width);
	margin: 0 auto;
	padding: 32px 48px 80px;
}

/* --------------------------------------------------------
   Context Line
   -------------------------------------------------------- */

.lbr-wrap .lbr-context-line-wrap {
	min-height: 0;
}

.lbr-wrap .lbr-context-line {
	text-align: center;
	color: var(--lbr-text-color);
	font-family: var(--lbr-font-heading);
	font-size: 18px;
	font-style: italic;
	margin-bottom: 24px;
	opacity: 0;
	animation: lbr-fadeIn 0.5s ease forwards;
	padding: 12px 20px;
}

/* --------------------------------------------------------
   Loading
   -------------------------------------------------------- */

.lbr-wrap .lbr-loading {
	text-align: center;
	color: var(--lbr-text-muted);
	font-family: var(--lbr-font-heading);
	font-style: italic;
	font-size: 18px;
	padding: 48px 0;
	opacity: 0;
	animation: lbr-fadeIn 0.3s ease forwards;
}

/* --------------------------------------------------------
   Category Filter Pills
   -------------------------------------------------------- */

.lbr-wrap .lbr-categories {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
	justify-content: center;
	margin-bottom: 28px;
}

.lbr-wrap .lbr-cat-pill {
	padding: 8px 16px;
	border-radius: var(--lbr-btn-radius);
	border: 1px solid var(--lbr-border-color);
	background: var(--lbr-card-background);
	color: var(--lbr-text-color);
	font-family: var(--lbr-font-body);
	font-size: 13px;
	font-weight: 500;
	cursor: pointer;
	transition: all 0.2s;
	white-space: nowrap;
}

.lbr-wrap .lbr-cat-pill:hover {
	border-color: var(--lbr-primary-color);
	color: var(--lbr-primary-color);
}

.lbr-wrap .lbr-cat-pill--active {
	background: var(--lbr-primary-color);
	color: white;
	border-color: var(--lbr-primary-color);
}

/* Active pill on hover: the generic :hover sets color to the primary colour,
   which on the primary background made the label INVISIBLE. Keep white text
   and dim the background slightly instead. */
.lbr-wrap .lbr-cat-pill--active:hover {
	color: white;
	filter: brightness( 0.88 );
}

/* Explicit remove affordance on active facet pills. */
.lbr-wrap .lbr-pill-x {
	margin-left: 8px;
	font-size: 11px;
	opacity: 0.75;
}

.lbr-wrap .lbr-cat-pill--active:hover .lbr-pill-x {
	opacity: 1;
}

.lbr-wrap .lbr-cat-count {
	font-weight: 400;
	opacity: 0.6;
	margin-left: 4px;
}

/* --------------------------------------------------------
   Divider
   -------------------------------------------------------- */

.lbr-wrap .lbr-divider {
	display: flex;
	align-items: center;
	gap: 16px;
	/* The divider is the results header's CLOSING rule: air above it
	   separates it from the summary/chips (or the sort row in browse),
	   air below hands over to the books. */
	margin: 26px 0 28px;
}

.lbr-wrap .lbr-divider-line {
	flex: 1;
	height: 1px;
	background: var(--lbr-border-color);
}

.lbr-wrap .lbr-divider-text {
	color: var(--lbr-text-muted);
	font-family: var(--lbr-font-body);
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: 2px;
	white-space: nowrap;
}

/* --------------------------------------------------------
   Book Card Grid
   -------------------------------------------------------- */

.lbr-wrap .lbr-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
	gap: var(--lbr-grid-gap);
}

/* — The run of editions —
   The grid is a CSS grid of covers; the run is a stack of editions, and
   each edition lays its own grid out inside itself. Collapsed, that
   grid is clipped to exactly one row — the peek IS the edition, not a
   label standing in for it. */

.lbr-wrap .lbr-grid--issues {
	display: block;
}

.lbr-wrap .lbr-issue-run-controls {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
	padding: 0 2px 10px;
	border-bottom: 1px solid var(--lbr-border-color, rgba(19, 29, 72, 0.12));
	margin-bottom: 4px;
}

.lbr-wrap .lbr-issue-run-count {
	font-family: var(--lbr-font-body);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--lbr-text-muted);
}

.lbr-wrap .lbr-issue-run-toggle,
.lbr-wrap .lbr-issue-run-toggle:hover,
.lbr-wrap .lbr-issue-run-toggle:focus {
	background: none;
	border: 0;
	box-shadow: none;
	padding: 2px 4px;
	font-family: var(--lbr-font-body);
	font-size: 12px;
	font-weight: 600;
	color: var(--lbr-primary-color, #283B91);
	cursor: pointer;
}

.lbr-wrap .lbr-issue-group {
	padding-bottom: 18px;
	margin-bottom: 18px;
	border-bottom: 1px solid var(--lbr-border-color, rgba(19, 29, 72, 0.10));
	/* Closing an edition scrolls back to its heading; themes here run a
	   sticky header, so leave it room rather than landing underneath it.
	   Kept in step with ISSUE_ANCHOR_MARGIN in lbr-grid.js. */
	scroll-margin-top: 96px;
}

/* The edition's name, and how much is in it. Not a button: the row of
   covers below is the thing you look at, and the control that opens the
   rest sits under it where the covers run out. */
/* Pinned while its edition is on screen, exactly as the year separator
   is in the flat list — scrolling a long edition without knowing which
   one you are inside is the thing the heading is for. z-index sits
   below the list view's column bar (6) so the two stack in the order
   they read: columns above, edition beneath. */
.lbr-wrap .lbr-issue-head {
	display: flex;
	align-items: baseline;
	gap: 12px;
	padding: 8px 2px 12px;
	position: sticky;
	top: var(--lbr-sticky-top, 0px);
	z-index: 5;
	background: var(--lbr-background, #fafafa);
	/* Side skirts, as the year header uses: page-colour paint past each
	   edge so a sliding cover and its shadow pass behind, not beside. */
	box-shadow: 24px 0 0 var(--lbr-background, #fafafa), -24px 0 0 var(--lbr-background, #fafafa);
}

/* In list view the edition heading IS the label row, so it needs no
   offset to clear one — the rule under the label row is also the pin
   line beneath the month and year. */
.lbr-wrap .lbr-issue-head--list {
	padding-top: 14px;
	padding-bottom: 6px;
	pointer-events: none;
}

/* The heading sharing the label row: full weight, so the month and year
   still read as the heading they are and not as a fourth column label. */
.lbr-wrap .lbr-row-heading {
	font-family: var(--lbr-font-heading);
	font-size: 19px;
	line-height: 1.2;
	color: var(--lbr-heading-color);
	text-transform: none;
	letter-spacing: normal;
	opacity: 1;
}

.lbr-wrap .lbr-row-heading-sub {
	margin-left: 10px;
	font-family: var(--lbr-font-body);
	font-size: 12px;
	color: var(--lbr-text-muted);
	text-transform: none;
	letter-spacing: normal;
}

.lbr-wrap .lbr-issue-name {
	flex: 0 0 auto;
	font-family: var(--lbr-font-heading);
	font-size: 20px;
	line-height: 1.2;
	color: var(--lbr-heading-color);
}

.lbr-wrap .lbr-issue-count {
	flex: 1 1 auto;
	font-family: var(--lbr-font-body);
	font-size: 12px;
	color: var(--lbr-text-muted);
}

.lbr-wrap .lbr-issue-body {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
	gap: var(--lbr-grid-gap);
	/* Clipped to one row until opened — the height is measured, because
	   how many covers make a row depends on the window. */
	overflow: hidden;
	transition: max-height 0.25s ease;
}

/* List view: rows, not a cover grid — and the peek is the first three,
   which stands about as tall as one row of covers. */
.lbr-wrap .lbr-issue-body.lbr-grid--list {
	display: block;
}

.lbr-wrap .lbr-issue-more,
.lbr-wrap .lbr-issue-more:hover,
.lbr-wrap .lbr-issue-more:focus {
	display: inline-block;
	margin-top: 12px;
	padding: 5px 14px;
	background: none;
	border: 1px solid var(--lbr-border-color, rgba(19, 29, 72, 0.22));
	border-radius: var(--lbr-btn-radius, 999px);
	box-shadow: none;
	font-family: var(--lbr-font-body);
	font-size: 12px;
	font-weight: 600;
	color: var(--lbr-primary-color, #283B91);
	cursor: pointer;
}

.lbr-wrap .lbr-issue-more:hover {
	background: rgba(40, 59, 145, 0.06);
	border-color: var(--lbr-primary-color, #283B91);
}

@media (prefers-reduced-motion: reduce) {
	.lbr-wrap .lbr-issue-body {
		transition: none;
	}
}

.lbr-wrap .lbr-book {
	text-decoration: none;
	color: inherit;
	display: block;
	transition: transform 0.2s ease;
}

.lbr-wrap .lbr-book:hover {
	transform: translateY(-4px);
}

/* Animation */

.lbr-wrap .lbr-book-anim {
	opacity: 0;
	animation: lbr-gridIn 0.4s ease forwards;
}

/* Cover wrapper */

.lbr-wrap .lbr-cover-wrap {
	position: relative;
	border-radius: var(--lbr-card-radius);
	overflow: hidden;
	background: var(--lbr-alt-background);
	aspect-ratio: 2 / 3;
	box-shadow: var(--lbr-card-shadow);
	transition: box-shadow 0.18s ease-out, transform 0.18s ease-out;
}

/* The pop: a whisper of lift-and-grow on the cover only — the caption
   stays planted, which is what makes the motion read as physical. */
.lbr-wrap .lbr-book:hover .lbr-cover-wrap {
	box-shadow: var(--lbr-card-shadow-hover);
	transform: translateY(-2px) scale(1.02);
}

@media (prefers-reduced-motion: reduce) {
	.lbr-wrap .lbr-book:hover .lbr-cover-wrap {
		transform: none;
	}
}

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

/* Coverless final state: calm light grey, no pulse. */

.lbr-wrap .lbr-cover--none {
	background: #E9EBEE;
}

/* Cover fallback title (image failed / no cover recorded). */

.lbr-wrap .lbr-cover-fallback {
	color: rgba(90, 90, 90, 0.85);
	font-family: var(--lbr-font-heading);
	font-size: 14px;
	padding: 20px;
	text-align: center;
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 80%;
}

/* Category badge on cover */

.lbr-wrap .lbr-cover-badge {
	position: absolute;
	bottom: 0;
	left: 0;
	right: 0;
	padding: 6px 10px;
	background: linear-gradient(transparent, rgba(19, 29, 72, 0.75));
	color: rgba(255, 255, 255, 0.9);
	font-family: var(--lbr-font-body);
	font-size: 10px;
	letter-spacing: 0.3px;
	text-transform: uppercase;
}

/* Book metadata */

.lbr-wrap .lbr-book-title {
	margin-top: 10px;
	font-family: var(--lbr-font-heading);
	font-size: 15px;
	font-weight: 600;
	color: var(--lbr-heading-color);
	line-height: 1.35;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.lbr-wrap .lbr-book-author {
	margin-top: 3px;
	font-family: var(--lbr-font-body);
	font-size: 12px;
	color: var(--lbr-text-color);
	line-height: 1.3;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.lbr-wrap .lbr-book-reviewer {
	margin-top: 1px;
	font-family: var(--lbr-font-body);
	font-size: 11px;
	color: var(--lbr-text-muted);
	line-height: 1.3;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* --------------------------------------------------------
   No Results
   -------------------------------------------------------- */

.lbr-wrap .lbr-no-results {
	text-align: center;
	padding: 64px 24px;
}

.lbr-wrap .lbr-no-results-icon {
	font-size: 32px;
	color: var(--lbr-border-color);
	margin-bottom: 16px;
}

.lbr-wrap .lbr-no-results-text {
	color: var(--lbr-text-muted);
	font-family: var(--lbr-font-body);
	font-size: 14px;
	max-width: 400px;
	margin: 0 auto;
	line-height: 1.6;
}

/* --------------------------------------------------------
   Load More
   -------------------------------------------------------- */

.lbr-wrap .lbr-load-more-wrap {
	text-align: center;
	margin-top: 40px;
}

.lbr-wrap .lbr-load-more-btn {
	padding: 12px 36px;
	border-radius: var(--lbr-btn-radius);
	border: 1px solid var(--lbr-border-color);
	background: transparent;
	color: var(--lbr-text-color);
	font-family: var(--lbr-font-body);
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.5px;
	cursor: pointer;
	transition: all 0.2s;
}

.lbr-wrap .lbr-load-more-btn:hover {
	background: var(--lbr-primary-color);
	color: white;
	border-color: var(--lbr-primary-color);
}

.lbr-wrap .lbr-load-more-btn:disabled {
	opacity: 0.5;
	cursor: not-allowed;
}

/* --------------------------------------------------------
   Responsive — Mobile First
   -------------------------------------------------------- */

@media (max-width: 768px) {
	.lbr-wrap .lbr-hero {
		padding: 32px 20px 16px;
	}

	.lbr-wrap .lbr-hero-title {
		font-size: 32px;
	}

	.lbr-wrap .lbr-hero-subtitle {
		margin-bottom: 24px;
	}

	.lbr-wrap .lbr-search-input {
		font-size: 16px;
		padding: 14px 88px 14px 18px;
	}

	.lbr-wrap .lbr-stats {
		gap: 24px;
		padding: 24px 20px 28px;
	}

	.lbr-wrap .lbr-stat-num {
		font-size: 22px;
	}

	.lbr-wrap .lbr-main {
		padding: 24px 20px 80px;
	}

	.lbr-wrap .lbr-grid {
		grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
		gap: 20px 14px;
	}

	.lbr-wrap .lbr-book-title {
		font-size: 13px;
	}

	.lbr-wrap .lbr-categories {
		gap: 6px;
	}

	.lbr-wrap .lbr-cat-pill {
		font-size: 12px;
		padding: 6px 12px;
	}

	.lbr-wrap .lbr-context-line {
		font-size: 16px;
		padding: 8px 12px;
	}
}

@media (max-width: 480px) {
	.lbr-wrap .lbr-grid {
		grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
		gap: 16px 10px;
	}

	.lbr-wrap .lbr-book-title {
		font-size: 12px;
	}

	.lbr-wrap .lbr-book-author {
		font-size: 11px;
	}

	.lbr-wrap .lbr-cover-badge {
		font-size: 9px;
		padding: 4px 8px;
	}
}

/* --------------------------------------------------------
   Chat Widget
   -------------------------------------------------------- */

/* Chat bubble (floating button) */

.lbr-wrap .lbr-chat-bubble {
	position: fixed;
	bottom: 24px;
	right: 24px;
	z-index: 9999;
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 12px 20px;
	border-radius: var(--lbr-btn-radius);
	border: none;
	background: var(--lbr-primary-color);
	color: white;
	font-family: var(--lbr-font-body);
	font-size: 14px;
	font-weight: 500;
	cursor: pointer;
	box-shadow: 0 4px 16px rgba(40, 59, 145, 0.3), 0 2px 6px rgba(0, 0, 0, 0.1);
	transition: all 0.3s ease;
}

.lbr-wrap .lbr-chat-bubble:hover {
	background: var(--lbr-primary-hover);
	box-shadow: 0 6px 24px rgba(40, 59, 145, 0.4), 0 3px 8px rgba(0, 0, 0, 0.12);
	transform: translateY(-2px);
}

.lbr-wrap .lbr-chat-bubble--open {
	opacity: 0.5;
	transform: scale(0.9);
}

.lbr-wrap .lbr-chat-bubble-icon {
	font-size: 18px;
	line-height: 1;
}

.lbr-wrap .lbr-chat-bubble-text {
	line-height: 1;
}

/* Chat panel */

.lbr-wrap .lbr-chat-panel {
	position: fixed;
	bottom: 80px;
	right: 24px;
	z-index: 9998;
	width: 380px;
	max-width: calc(100vw - 48px);
	height: 500px;
	max-height: calc(100vh - 120px);
	display: flex;
	flex-direction: column;
	background: var(--lbr-chat-background);
	border-radius: 16px;
	box-shadow: 0 12px 48px rgba(0, 0, 0, 0.15), 0 4px 16px rgba(0, 0, 0, 0.08);
	overflow: hidden;
	animation: lbr-chatPanelIn 0.3s ease forwards;
}

@keyframes lbr-chatPanelIn {
	from {
		opacity: 0;
		transform: translateY(16px) scale(0.95);
	}
	to {
		opacity: 1;
		transform: translateY(0) scale(1);
	}
}

/* Panel header */

.lbr-wrap .lbr-chat-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 16px 20px;
	background: var(--lbr-primary-color);
	color: white;
	flex-shrink: 0;
}

.lbr-wrap .lbr-chat-header-title {
	font-family: var(--lbr-font-heading);
	font-size: 15px;
	font-weight: 600;
}

.lbr-wrap .lbr-chat-close {
	width: 28px;
	height: 28px;
	border-radius: 50%;
	border: none;
	background: rgba(255, 255, 255, 0.15);
	color: white;
	font-size: 14px;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: background 0.2s;
}

.lbr-wrap .lbr-chat-close:hover {
	background: rgba(255, 255, 255, 0.25);
}

/* "+ New question" header button */

.lbr-wrap .lbr-chat-new-question {
	margin-left: auto;
	margin-right: 8px;
	padding: 4px 10px;
	border-radius: 12px;
	border: 1px solid rgba(255, 255, 255, 0.35);
	background: transparent;
	color: white;
	font-size: 12px;
	cursor: pointer;
	transition: background 0.2s;
}

.lbr-wrap .lbr-chat-new-question:hover {
	background: rgba(255, 255, 255, 0.15);
}

/* Disabled chat bubble */

.lbr-wrap .lbr-chat-bubble--disabled {
	opacity: 0.5;
	cursor: not-allowed;
}

.lbr-wrap .lbr-chat-bubble--disabled:hover {
	transform: none;
}

/* "Search changed" scope banner */

.lbr-wrap .lbr-chat-scope-banner {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 8px 14px;
	background: #fff7ed;
	border-top: 1px solid #fed7aa;
	font-size: 12px;
	color: #7c2d12;
	flex-wrap: wrap;
}

.lbr-wrap .lbr-chat-scope-banner-text {
	flex: 1 1 auto;
	min-width: 0;
}

.lbr-wrap .lbr-chat-scope-banner-actions {
	display: inline-flex;
	gap: 6px;
	flex-shrink: 0;
}

.lbr-wrap .lbr-chat-scope-rescope,
.lbr-wrap .lbr-chat-scope-dismiss {
	padding: 3px 8px;
	border-radius: 4px;
	border: 1px solid #fdba74;
	background: white;
	color: #7c2d12;
	font-size: 11px;
	cursor: pointer;
}

.lbr-wrap .lbr-chat-scope-rescope:hover {
	background: #fed7aa;
}

.lbr-wrap .lbr-chat-scope-dismiss:hover {
	background: #fef3c7;
}

/* Messages area */

.lbr-wrap .lbr-chat-messages {
	flex: 1;
	overflow-y: auto;
	padding: 16px;
	display: flex;
	flex-direction: column;
	gap: 12px;
}

/* Message bubbles */

.lbr-wrap .lbr-chat-msg {
	display: flex;
	flex-direction: column;
	max-width: 85%;
	animation: lbr-fadeIn 0.3s ease;
}

.lbr-wrap .lbr-chat-msg--user {
	align-self: flex-end;
}

.lbr-wrap .lbr-chat-msg--assistant {
	align-self: flex-start;
}

.lbr-wrap .lbr-chat-msg-bubble {
	/* The server keeps the model's paragraph breaks; show them. */
	white-space: pre-line;
	padding: 10px 14px;
	border-radius: 12px;
	font-family: var(--lbr-font-body);
	font-size: 14px;
	line-height: 1.5;
	word-wrap: break-word;
}

.lbr-wrap .lbr-chat-msg--user .lbr-chat-msg-bubble {
	background: var(--lbr-chat-user-bubble);
	color: white;
	border-bottom-right-radius: 4px;
}

.lbr-wrap .lbr-chat-msg--assistant .lbr-chat-msg-bubble {
	background: var(--lbr-chat-assistant-bubble);
	color: var(--lbr-heading-color);
	border-bottom-left-radius: 4px;
}

/* Thinking animation */

.lbr-wrap .lbr-chat-msg--thinking .lbr-chat-msg-bubble {
	opacity: 0.7;
	font-style: italic;
}

/* Citations */

.lbr-wrap .lbr-chat-citations {
	display: flex;
	flex-direction: column;
	gap: 4px;
	margin-top: 6px;
	padding-left: 4px;
}

.lbr-wrap .lbr-chat-citation-link {
	font-family: var(--lbr-font-body);
	font-size: 12px;
	color: var(--lbr-primary-color);
	text-decoration: none;
	transition: color 0.2s;
}

.lbr-wrap .lbr-chat-citation-link:hover {
	color: var(--lbr-primary-hover);
	text-decoration: underline;
}

/* Chat input area */

.lbr-wrap .lbr-chat-input-wrap {
	display: flex;
	gap: 8px;
	padding: 12px 16px;
	border-top: 1px solid var(--lbr-border-color);
	background: white;
	flex-shrink: 0;
}

.lbr-wrap .lbr-chat-input {
	flex: 1;
	padding: 10px 14px;
	border: 1px solid var(--lbr-border-color);
	border-radius: 8px;
	font-family: var(--lbr-font-body);
	font-size: 14px;
	outline: none;
	transition: border-color 0.2s;
	color: var(--lbr-heading-color);
	background: var(--lbr-chat-background);
}

.lbr-wrap .lbr-chat-input::placeholder {
	color: var(--lbr-text-muted);
}

.lbr-wrap .lbr-chat-input:focus {
	border-color: var(--lbr-primary-color);
}

.lbr-wrap .lbr-chat-send {
	padding: 10px 16px;
	border-radius: 8px;
	border: none;
	background: var(--lbr-primary-color);
	color: white;
	font-family: var(--lbr-font-body);
	font-size: 13px;
	font-weight: 600;
	cursor: pointer;
	transition: background 0.2s;
	white-space: nowrap;
}

.lbr-wrap .lbr-chat-send:hover {
	background: var(--lbr-primary-hover);
}

/* Responsive — Chat widget */

@media (max-width: 768px) {
	.lbr-wrap .lbr-chat-bubble-text {
		display: none;
	}

	.lbr-wrap .lbr-chat-bubble {
		padding: 14px;
		border-radius: 50%;
	}

	.lbr-wrap .lbr-chat-panel {
		right: 12px;
		bottom: 72px;
		width: calc(100vw - 24px);
		height: calc(100vh - 100px);
		/* iOS: 100vh is the LARGEST viewport, so the header sat above the
		   screen with the URL bar showing. dvh follows the real one; the
		   line above stays for browsers without it. */
		height: calc(100dvh - 100px);
		border-radius: 12px;
	}
}

@media (max-width: 480px) {
	.lbr-wrap .lbr-chat-bubble {
		bottom: 16px;
		right: 16px;
	}

	.lbr-wrap .lbr-chat-panel {
		right: 8px;
		bottom: 64px;
		width: calc(100vw - 16px);
	}
}


/* ── Touch-device refinements (mobile audit) ─────────────────────────── */

/* The Tab-to-complete hint refers to a key touch devices don't have. */
@media (hover: none) {
	.lbr-wrap .lbr-tab-hint {
		display: none;
	}
}

/* Comfortable tap targets: chips and pills measured 31px tall on phones,
   against the ~44px touch guideline. Coarse pointers get taller targets
   without changing the desktop density. */
@media (pointer: coarse) {
	.lbr-wrap .lbr-chip,
	.lbr-wrap .lbr-cat-pill {
		padding-top: 11px;
		padding-bottom: 11px;
	}

	.lbr-wrap .lbr-clear-btn,
	.lbr-wrap .lbr-search-btn {
		min-width: 44px;
		min-height: 44px;
	}
}


/* ── AI-regeneration state & motion pass ─────────────────────────────── */

/* While a chip refresh is in flight the old chips LEAVE — they answer
   the previous question, and a dimmed-but-present row read as broken.
   The ghost chip alone carries the signal until the new set lands. */
.lbr-wrap .lbr-chips--regenerating .lbr-chip:not(.lbr-chip--ghost) {
	display: none;
}

.lbr-wrap .lbr-sparkle {
	vertical-align: -4px;
	margin-right: 3px;
}

/* Ghost chip: a shimmering placeholder announcing the regeneration. */
.lbr-wrap .lbr-chip--ghost {
	cursor: default;
	border-style: dashed;
	color: rgba(255, 255, 255, 0.85);
	background: linear-gradient(
		100deg,
		rgba(255, 255, 255, 0.06) 20%,
		rgba(255, 255, 255, 0.22) 50%,
		rgba(255, 255, 255, 0.06) 80%
	);
	background-size: 200% 100%;
	animation: lbr-chipIn 0.4s ease forwards, lbr-shimmer 3s linear infinite;
}

@keyframes lbr-shimmer {
	from { background-position: 200% 0; }
	to   { background-position: -200% 0; }
}

/* Context line: ease in each time it changes (the element is recreated
   per change, so the entrance runs on every new description). */
.lbr-wrap .lbr-context-line {
	animation: lbr-gridIn 0.45s ease both;
}

/* Sparkle sits inline with the summary text. */
.lbr-wrap .lbr-context-line .lbr-sparkle {
	vertical-align: -3px;
	margin-right: 2px;
}

/* Beat two of the search rhythm: the summary slot visibly thinks while
   the AI reads the results — same shimmer language as the ghost chip. */
.lbr-wrap .lbr-context-line--ghost {
	opacity: 0.7;
	background: linear-gradient(100deg, currentColor 30%, rgba(128, 128, 128, 0.35) 50%, currentColor 70%);
	background-size: 200% 100%;
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: transparent;
	animation: lbr-gridIn 0.45s ease both, lbr-shimmer 3.2s linear infinite;
}

.lbr-wrap .lbr-context-line--ghost .lbr-sparkle {
	-webkit-text-fill-color: initial;
}

/* Follow-up chips in the results header: centred under the summary,
   with clear air before the books begin. min-height keeps the column
   steady while chips swap for the ghost and back. */
.lbr-wrap .lbr-chips--results {
	justify-content: center;
	margin: 2px 0 32px;
	min-height: 34px;
}

/* The summary slot likewise holds at least one line while its content
   swaps (summary → ghost → new summary) so the grid never bounces. */
.lbr-wrap .lbr-context-line-wrap:not(:empty) {
	min-height: 34px;
}

.lbr-wrap .lbr-chips--results:empty {
	display: none;
}


/* Chip skin is hero-native (white text, translucent-white fill) — on the
   light main-content surface that rendered WHITE-ON-WHITE. Follow-ups
   wear the primary blue with white text: unmissable, and visually
   distinct from the white filter pills above them. */
.lbr-wrap .lbr-chips--results .lbr-chip {
	border: 1px solid var(--lbr-primary-color);
	background: var(--lbr-primary-color);
	color: #fff;
}

.lbr-wrap .lbr-chips--results .lbr-chip:hover {
	filter: brightness( 0.86 );
	color: #fff;
}

/* The ghost is a status, not a button: quiet grey shimmer on the light
   surface (the hero ghost's white-based gradient was invisible here —
   it "popped in" with no visible animation). */
.lbr-wrap .lbr-chips--results .lbr-chip--ghost {
	color: var(--lbr-text-color);
	border: 1px dashed var(--lbr-border-color);
	background: linear-gradient(
		100deg,
		rgba(0, 0, 0, 0.03) 30%,
		rgba(0, 0, 0, 0.1) 50%,
		rgba(0, 0, 0, 0.03) 70%
	);
	background-size: 200% 100%;
}

/* Facet/category pills: settle in rather than pop. */
.lbr-wrap .lbr-cat-pill {
	animation: lbr-fadeIn 0.35s ease both;
}

/* Divider count: gentle emphasis when it re-renders. */
.lbr-wrap .lbr-divider-text {
	transition: opacity 0.3s ease;
}

/* ── Small screens: chips become one swipeable row ───────────────────── */

/* Stacked full-width chips pushed the results two screens down on a
   phone. A horizontally scrollable strip keeps every chip reachable in
   one thumb-height row; the end-fade signals there is more to swipe. */
@media (max-width: 640px) {
	/* Keep typed text clear of the overlaid ✕/→ buttons. */
	.lbr-wrap .lbr-search-input {
		padding-right: 112px;
	}

	.lbr-wrap .lbr-chips {
		flex-wrap: nowrap;
		justify-content: flex-start;
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
		scroll-snap-type: x proximity;
		scrollbar-width: none;
		padding: 2px 16px;
		-webkit-mask-image: linear-gradient(to right, black 92%, transparent);
		mask-image: linear-gradient(to right, black 92%, transparent);
	}

	.lbr-wrap .lbr-chips::-webkit-scrollbar {
		display: none;
	}

	.lbr-wrap .lbr-chip {
		flex: 0 0 auto;
		scroll-snap-align: start;
		white-space: nowrap;
	}
}

/* ── Respect reduced-motion preferences ──────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
	.lbr-wrap .lbr-skel-pill,
	.lbr-wrap .lbr-skel-stat-num,
	.lbr-wrap .lbr-skel-stat-lbl,
	.lbr-wrap .lbr-chip--skel {
		animation: none;
	}

	.lbr-wrap .lbr-chip,
	.lbr-wrap .lbr-chip--ghost,
	.lbr-wrap .lbr-cat-pill,
	.lbr-wrap .lbr-context-line,
	.lbr-wrap .lbr-book-anim {
		animation: none !important;
		opacity: 1 !important;
		transform: none !important;
	}
}


/* ── Toolbar: sort + view toggle ─────────────────────────────────────── */

.lbr-wrap .lbr-temporal-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	margin: 14px 0 6px;
	padding-top: 14px;
	border-top: 1px solid var(--lbr-border-color);
}

.lbr-wrap .lbr-toolbar {
	display: flex;
	justify-content: flex-end;
	align-items: center;
	gap: 12px;
	flex: 0 0 auto;
}


/* Open strip state: TWO stacked rows inside the year column. Row one is
   back pill + the pool strip; row two (only when years are selected) is
   the selection basket — its own scroller, so any number of picks reads
   at a glance and NOTHING here can widen the column and squeeze the
   toolbar beside it (every sub-row pins min-width: 0). */
.lbr-wrap .lbr-year-pills--open {
	flex-direction: column;
	flex-wrap: nowrap;
	align-items: stretch;
	gap: 10px;
	min-width: 0;
}

.lbr-wrap .lbr-year-row {
	display: flex;
	align-items: center;
	gap: 8px;
	min-width: 0;
}

.lbr-wrap .lbr-year-selected {
	display: flex;
	align-items: center;
	gap: 8px;
	min-width: 0;
}

.lbr-wrap .lbr-year-selected-scroll {
	display: flex;
	align-items: center;
	gap: 8px;
	flex: 1 1 0;
	min-width: 0;
	overflow-x: auto;
	scrollbar-width: none;
	cursor: grab;
	padding: 2px;
	-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
	mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
}

.lbr-wrap .lbr-year-selected-scroll::-webkit-scrollbar {
	display: none;
}

.lbr-wrap .lbr-year-scroller {
	position: relative;
	/* Fill the row's remaining width; without the width cap a flex item
	   sizes to its CONTENT and the strip never overflows — ancestors
	   clip it and nothing scrolls. */
	flex: 1 1 0;
	min-width: 100px;
	max-width: 100%;
}

.lbr-wrap .lbr-year-token {
	flex: 0 0 auto;
}

.lbr-wrap .lbr-year-clear {
	flex: 0 0 auto;
	width: 26px;
	height: 26px;
	border-radius: 50%;
	border: 1px solid var(--lbr-border-color);
	background: var(--lbr-card-background);
	color: var(--lbr-text-color);
	font-size: 12px;
	line-height: 1;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	opacity: 0.75;
}

.lbr-wrap .lbr-year-clear:hover {
	opacity: 1;
	border-color: var(--lbr-primary-color);
}

.lbr-wrap .lbr-year-strip {
	display: flex;
	align-items: center;
	gap: 8px;
	overflow-x: auto;
	scrollbar-width: none;
	cursor: grab;
	padding: 2px 28px 2px 2px;
	-webkit-mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
	mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
}

.lbr-wrap .lbr-year-strip::-webkit-scrollbar {
	display: none;
}

.lbr-wrap .lbr-grab--dragging {
	cursor: grabbing;
	user-select: none;
	scroll-behavior: auto;
}

.lbr-wrap .lbr-grab--dragging .lbr-year-pill {
	pointer-events: none;
}

.lbr-wrap .lbr-strip--at-start .lbr-year-strip {
	-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
	mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
}

.lbr-wrap .lbr-strip--at-end .lbr-year-strip {
	-webkit-mask-image: linear-gradient(to right, transparent 0, #000 28px);
	mask-image: linear-gradient(to right, transparent 0, #000 28px);
}

.lbr-wrap .lbr-year-strip > * {
	flex: 0 0 auto;
}



.lbr-wrap .lbr-strip-arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	width: 26px;
	height: 26px;
	border-radius: 50%;
	border: 1px solid var(--lbr-border-color);
	background: var(--lbr-card-background);
	color: var(--lbr-text-color);
	font-size: 16px;
	line-height: 1;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0 0 2px;
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
	transition: opacity 0.2s ease;
	z-index: 1;
}

.lbr-wrap .lbr-strip-arrow--left {
	left: -6px;
}

.lbr-wrap .lbr-strip-arrow--right {
	right: -2px;
}

.lbr-wrap .lbr-strip--at-start .lbr-strip-arrow--left,
.lbr-wrap .lbr-strip--at-end .lbr-strip-arrow--right {
	opacity: 0;
	pointer-events: none;
}

@media (pointer: coarse) {
	.lbr-wrap .lbr-strip-arrow {
		display: none;
	}
}

.lbr-wrap .lbr-sort {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	font-family: var(--lbr-font-body);
	font-size: 13px;
	color: var(--lbr-text-color);
	/* The theme gives every <label> `margin-bottom: 4px` for its form
	   layouts, and Sort/Reviewer ARE labels. In a flex row that margin
	   is part of the item's box: it stretched the toolbar to 38px and,
	   under align-items:center, centred a 38px box — leaving the two
	   dropdowns sitting 2px ABOVE the view toggle, the bookshelf pill
	   and the year chips. Every control on the row is 34px; they must
	   all be 34px of the same 34px. */
	margin: 0;
}

.lbr-wrap .lbr-sort-label {
	opacity: 0.6;
}

.lbr-wrap .lbr-sort-select {
	/* Own the chevron: themes (BuddyBoss) set appearance:none on selects
	   and paint their own arrow, which our background overrides erase. */
	-webkit-appearance: none;
	appearance: none;
	padding: 7px 28px 7px 10px;
	border-radius: var(--lbr-btn-radius);
	border: 1px solid var(--lbr-border-color);
	background-color: var(--lbr-card-background);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23667085' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 10px center;
	color: var(--lbr-text-color);
	font-family: var(--lbr-font-body);
	font-size: 13px;
	cursor: pointer;
}

.lbr-wrap .lbr-view-toggle {
	display: inline-flex;
	border: 1px solid var(--lbr-border-color);
	border-radius: var(--lbr-btn-radius);
	overflow: hidden;
}

.lbr-wrap .lbr-view-btn {
	padding: 8px 12px;
	border: none;
	background: var(--lbr-card-background);
	color: var(--lbr-text-color);
	font-size: 14px;
	cursor: pointer;
	opacity: 0.55;
}

/* Icon buttons: kill the inline-SVG baseline gap. */
.lbr-wrap .lbr-view-btn svg {
	display: block;
}

.lbr-wrap .lbr-view-btn--active {
	background: var(--lbr-primary-color);
	color: white;
	opacity: 1;
}

/* List view is a desktop/tablet affordance; phones keep the grid. */
@media (max-width: 640px) {
	.lbr-wrap .lbr-view-toggle {
		display: none;
	}
}

/* ── Year pills (decade → year Russian doll) ─────────────────────────── */

.lbr-wrap .lbr-year-pills {
	margin-top: 0;
	/* .lbr-categories brings margin-bottom: 28px, which inside the
	   vertically-centred temporal row pushed the pills up relative to
	   the sort control. Zero it: the row's own margins do the spacing. */
	margin-bottom: 0;
	flex: 1 1 auto;
	justify-content: flex-start;
	min-height: 0;
	/* Flex min-width defaults to CONTENT size — with the all-years strip
	   inside, the row would refuse to shrink and nothing would scroll. */
	min-width: 0;
}

.lbr-wrap .lbr-year-back {
	font-weight: 600;
}

/* ── Sticky year headers ─────────────────────────────────────────────── */

.lbr-wrap .lbr-year-header {
	grid-column: 1 / -1;
	position: sticky;
	top: var(--lbr-sticky-top, 0px);
	z-index: 5;
	padding: 8px 4px;
	margin-top: 10px;
	background: var(--lbr-background, #fafafa);
	/* Invisible side skirts: page-colour paint extending 24px past each
	   edge — wide enough to swallow a sliding cover AND its hover
	   shadow — while the pin line below stays exactly grid width. */
	box-shadow: 24px 0 0 var(--lbr-background, #fafafa), -24px 0 0 var(--lbr-background, #fafafa);
	font-family: var(--lbr-font-heading);
	font-size: 22px;
	color: var(--lbr-text-color);
	/* Bottom stays a hard pin line — the right edge for book covers to
	   be cut against. */
	border-bottom: 1px solid var(--lbr-border-color);
	animation: lbr-fadeIn 0.3s ease both;
}

/* The LEADING edge is soft: during the sticky handoff the next year
   slides over the pinned one, and a hard edge crossing the numerals
   looked like a guillotine. 10px of fade lives in the header's own
   top margin, so it never touches the cards in normal flow. */
.lbr-wrap .lbr-year-header::before {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	bottom: 100%;
	height: 10px;
	background: linear-gradient(to top, var(--lbr-background, #fafafa), transparent);
	pointer-events: none;
}

/* ── List view rows ──────────────────────────────────────────────────── */

.lbr-wrap .lbr-grid--list {
	display: flex;
	flex-direction: column;
	gap: 0;
}

.lbr-wrap .lbr-row {
	display: flex;
	align-items: center;
	gap: 16px;
	padding: 10px 8px;
	border-bottom: 1px solid var(--lbr-border-color);
	text-decoration: none;
	color: var(--lbr-text-color);
	animation: lbr-fadeIn 0.25s ease both;
}

.lbr-wrap .lbr-row:hover {
	background: var(--lbr-card-background);
}

.lbr-wrap .lbr-row-thumb {
	flex: 0 0 48px;
	width: 48px;
	height: 72px;
	border-radius: 4px;
	overflow: hidden;
	background: linear-gradient(135deg, #283B91, #131D48);
}

.lbr-wrap .lbr-row-thumb img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.lbr-wrap .lbr-row-main {
	flex: 1;
	min-width: 0;
}

.lbr-wrap .lbr-row-title {
	font-family: var(--lbr-font-heading);
	font-size: 16px;
	font-weight: 600;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* Subtitle tier: between title and author, quieter than the title but
   less muted than the author's byline. */
.lbr-wrap .lbr-row-sub {
	font-size: 13px;
	opacity: 0.8;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.lbr-wrap .lbr-row-author {
	font-size: 13px;
	opacity: 0.65;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.lbr-wrap .lbr-row-col {
	flex: 0 0 84px;
	text-align: right;
	font-variant-numeric: tabular-nums;
	font-size: 13px;
	opacity: 0.7;
}

/* Reviewer column: names, not numbers — wider, ellipsised. Folds away
   below tablet width where the two year columns already earn their keep. */
.lbr-wrap .lbr-row-col--rv {
	flex: 0 0 140px;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	font-variant-numeric: normal;
}

@media (max-width: 900px) {
	.lbr-wrap .lbr-row-col--rv {
		display: none;
	}
}

/* Column-header row for the list view: ONE sticky labels bar — year
   headers stick just beneath it, so labels appear once but stay visible
   at any scroll depth. */
.lbr-wrap .lbr-row--header {
	position: sticky;
	top: var(--lbr-sticky-top, 0px);
	z-index: 6;
	background: var(--lbr-background, #fafafa);
	border-bottom: 2px solid var(--lbr-border-color);
	padding-top: 2px;
	padding-bottom: 6px;
	pointer-events: none;
}

.lbr-wrap .lbr-row--header .lbr-row-col {
	font-size: 11px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	opacity: 0.55;
}

.lbr-wrap .lbr-row-thumb--header {
	height: auto;
	background: none;
}

/* Small screens: the list stays usable rather than cramped — the
   Published column folds away (Reviewed matches the default sort and the
   year headers), the thumb slims down, and rows tighten. */
@media (max-width: 640px) {
	.lbr-wrap .lbr-row-col--pub {
		display: none;
	}

	.lbr-wrap .lbr-row {
		gap: 10px;
		padding: 8px 4px;
	}

	.lbr-wrap .lbr-row-thumb {
		flex-basis: 40px;
		width: 40px;
		height: 60px;
	}

	.lbr-wrap .lbr-row-col {
		flex-basis: 56px;
	}

	/* The temporal row stacks: years strip above, sort below. */
	.lbr-wrap .lbr-temporal-row {
		flex-direction: column;
		align-items: stretch;
		gap: 10px;
	}

	.lbr-wrap .lbr-toolbar {
		justify-content: flex-end;
	}
}

/* Small screens: rows that clip are rows that fail. The toolbar was a
   nowrap flex-end row, so overflow pushed the Sort control off the LEFT
   edge — controls stack full-width instead. Chips wrap onto new lines
   rather than scrolling off-screen with no affordance. */
@media (max-width: 640px) {
	.lbr-wrap .lbr-toolbar {
		flex-wrap: wrap;
		justify-content: flex-start;
		row-gap: 10px;
	}

	.lbr-wrap .lbr-sort {
		flex: 1 1 100%;
	}

	/* On a phone the row is one control per line, so the shrink-to-fit
	   wrapper gives way and the select spans the width again — back in
	   flow, with the ghost gone. */
	.lbr-wrap .lbr-sort .lbr-select-fit {
		flex: 1 1 auto;
		min-width: 0;
		display: flex;
	}

	.lbr-wrap .lbr-sort .lbr-select-ghost {
		display: none;
	}

	/* Back in flow only — the 34px height comes from the toolbar
	   unification rule further down and must not be released here, or
	   the mobile padding below re-inflates the control to 54px. */
	.lbr-wrap .lbr-sort .lbr-select-fit > .lbr-sort-select {
		position: static;
	}

	.lbr-wrap .lbr-sort .lbr-sort-select {
		flex: 1 1 auto;
		min-width: 0;
		/* Comfortable thumb target (~42px). */
		padding-top: 10px;
		padding-bottom: 10px;
	}

	.lbr-wrap .lbr-view-toggle {
		margin-left: auto;
	}

	/* NOT re-wrapping the chips here: the ≤640 block above deliberately
	   turns them into one swipeable line, and this rule — same
	   specificity, further down the file — silently undid it. Eleven
	   chips then stacked eleven rows deep and pushed the app off the
	   screen. The strip stands. */

	/* Categories: eleven pills wrapped over seven rows ate half the
	   screen — two swipeable rows instead. The year strip inside the
	   temporal row already swipes by design. */
	/* One swipeable line, each pill as wide as its own words. The two-row
	   grid this replaces sized every COLUMN to its widest member, so
	   "All" and "General" were padded out to match whatever long
	   category happened to sit above or below them — and the padding
	   changed whenever the set did, which is why the pills resized on
	   entering the bookshelf. */
	.lbr-wrap .lbr-categories:not(.lbr-year-pills) {
		display: flex;
		flex-wrap: nowrap;
		justify-content: flex-start;
		gap: 8px;
		overflow-x: auto;
		scrollbar-width: none;
		padding-bottom: 4px;
		-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
		mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
	}

	.lbr-wrap .lbr-categories:not(.lbr-year-pills) > * {
		flex: 0 0 auto;
	}

	.lbr-wrap .lbr-categories:not(.lbr-year-pills)::-webkit-scrollbar {
		display: none;
	}
}



/* Snapshot restores land instantly: no entrance choreography on a page
   the reader has already seen. The ghost chip is excluded — its infinite
   shimmer is a progress indicator, not entrance choreography. Ending this
   mode is beginInstant()'s job in lbr-search.js: it stamps inline
   `animation: none` on rendered elements BEFORE removing the class,
   because a bare removal would create these animations at frame 0 and
   visibly replay the whole entrance. */
.lbr-wrap.lbr-instant .lbr-book-anim,
.lbr-wrap.lbr-instant .lbr-chip:not(.lbr-chip--ghost),
.lbr-wrap.lbr-instant .lbr-cat-pill,
.lbr-wrap.lbr-instant .lbr-row,
.lbr-wrap.lbr-instant .lbr-year-header,
.lbr-wrap.lbr-instant .lbr-context-line {
	animation: none !important;
	opacity: 1 !important;
	transform: none !important;
}


/* While a search is in flight the previous results stay put, dimmed —
   wiping them collapsed the page and snapped the viewport to the top. */
.lbr-wrap .lbr-grid--stale {
	opacity: 0.4;
	pointer-events: none;
	transition: opacity 0.15s ease;
}


/* Scoped-dead-end escape hatch inside the no-results block. */
.lbr-wrap .lbr-clear-scope-btn {
	margin-top: 14px;
	padding: 9px 18px;
	border: 1px solid var(--lbr-primary-color);
	border-radius: var(--lbr-btn-radius);
	background: var(--lbr-primary-color);
	color: #fff;
	font-family: var(--lbr-font-body);
	font-size: 13px;
	cursor: pointer;
	transition: filter 0.2s;
}

.lbr-wrap .lbr-clear-scope-btn:hover {
	filter: brightness( 0.88 );
}


/* Cover placeholder: cards render instantly but cover images stream in
   lazily — the cover area holds a gently pulsing ground until its image
   paints, then the image fades in. */
.lbr-wrap .lbr-cover-wrap:not(.lbr-cover-loaded) {
	background: #E2E5E8;
	animation: lbr-skelPulse 1.8s ease-in-out infinite;
}

.lbr-wrap .lbr-cover-wrap img {
	opacity: 0;
	transition: opacity 0.25s ease;
}

.lbr-wrap .lbr-cover-loaded img {
	opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
	.lbr-wrap .lbr-cover-wrap:not(.lbr-cover-loaded) {
		animation: none;
	}
	.lbr-wrap .lbr-cover-wrap img {
		transition: none;
	}
}

/* Skeleton placeholders for infinite-scroll appends: same footprint as
   the incoming card/row, pulsing slowly. */
/* Skeleton pills, stat blocks and hero chips: the page's shape exists
   before its data — pills for the category/year rows, translucent
   blocks in the blue masthead. */

.lbr-wrap .lbr-skel-pill {
	display: inline-block;
	height: 37px;
	border-radius: 999px;
	background: #E9EBEE;
	animation: lbr-skelPulse 1.8s ease-in-out infinite;
}

.lbr-wrap .lbr-skel-pill--year {
	width: 92px;
	height: 33px;
}

.lbr-wrap .lbr-skel-stat-num {
	display: block;
	width: 64px;
	height: 30px;
	margin: 0 auto 8px;
	border-radius: 6px;
	background: rgba(255, 255, 255, 0.25);
	animation: lbr-skelPulse 1.8s ease-in-out infinite;
}

.lbr-wrap .lbr-skel-stat-lbl {
	display: block;
	width: 78px;
	height: 10px;
	margin: 0 auto;
	border-radius: 5px;
	background: rgba(255, 255, 255, 0.16);
	animation: lbr-skelPulse 1.8s ease-in-out infinite;
}

.lbr-wrap .lbr-hero .lbr-chip--skel {
	height: 40px;
	background: rgba(255, 255, 255, 0.1);
	border-color: rgba(255, 255, 255, 0.14);
	pointer-events: none;
	animation: lbr-skelPulse 1.8s ease-in-out infinite;
}

/* Zero results: the AI voice's register — sparkle, serif, soft. */
.lbr-wrap .lbr-no-results-icon {
	color: var(--lbr-primary-color, #283B91);
	opacity: 0.55;
	font-size: 22px;
}

.lbr-wrap .lbr-no-results-text {
	font-family: var(--lbr-font-heading);
	font-style: italic;
	font-size: 17px;
	color: var(--lbr-text-color);
	opacity: 0.75;
	max-width: 540px;
	margin: 0 auto;
}

@keyframes lbr-skelPulse {
	0%, 100% { opacity: 0.45; }
	50%      { opacity: 0.9; }
}

.lbr-wrap .lbr-skel {
	animation: lbr-skelPulse 1.8s ease-in-out infinite;
}

.lbr-wrap .lbr-skel-cover {
	aspect-ratio: 2 / 3;
	border-radius: var(--lbr-card-radius);
	background: var(--lbr-alt-background);
	margin-bottom: 10px;
}

.lbr-wrap .lbr-skel-bar {
	height: 12px;
	border-radius: 6px;
	background: var(--lbr-alt-background);
	margin: 6px 0;
}

.lbr-wrap .lbr-skel--row {
	display: flex;
	align-items: center;
	gap: 16px;
	padding: 10px 8px;
	border-bottom: 1px solid var(--lbr-border-color);
}

.lbr-wrap .lbr-skel-thumb {
	flex: 0 0 48px;
	width: 48px;
	height: 72px;
	border-radius: 4px;
	background: var(--lbr-alt-background);
}

.lbr-wrap .lbr-skel-lines {
	flex: 1;
}

@media (prefers-reduced-motion: reduce) {
	.lbr-wrap .lbr-skel {
		animation: none;
		opacity: 0.6;
	}
}


/* ── Back-to-top ─────────────────────────────────────────────────────── */

.lbr-top-btn {
	position: fixed;
	bottom: 24px;
	left: 50%;
	z-index: 9998;
	width: 48px;
	height: 48px;
	border-radius: 50%;
	border: none;
	background: var(--lbr-primary-color, #283B91);
	color: white;
	display: flex;
	align-items: center;
	justify-content: center;
	/* The theme's generic button padding (10px 20px) would squeeze the
	   flexed arrow to a sliver inside the 48px circle. */
	padding: 0;
	cursor: pointer;
	box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
	opacity: 0;
	transform: translate(-50%, 12px);
	pointer-events: none;
	transition: opacity 0.25s ease, transform 0.25s ease;
}

/* Centred and translucent at rest so it floats over the covers without
   shouting; solid on hover/focus when it becomes the thing you're using. */
.lbr-top-btn--visible {
	opacity: 0.55;
	transform: translate(-50%, 0);
	pointer-events: auto;
}

.lbr-top-btn--visible:hover,
.lbr-top-btn--visible:focus-visible {
	opacity: 1;
}

.lbr-top-btn svg {
	width: 22px;
	height: 22px;
	flex: 0 0 auto;
	display: block;
}

@media (max-width: 640px) {
	.lbr-top-btn {
		bottom: 16px;
		width: 44px;
		height: 44px;
	}
}

@media (prefers-reduced-motion: reduce) {
	.lbr-top-btn {
		transition: none;
	}
}

@media print {
	/* !important: the panel carries an inline display from the script and
	   the base rules are more specific than a bare class. */
	.lbr-top-btn,
	.lbr-wrap .lbr-chat-bubble,
	.lbr-wrap .lbr-chat-panel,
	.lbr-toast {
		display: none !important;
	}
}


/* ── Top-three elevation (relevance-ranked search only) ──────────────── */

.lbr-wrap .lbr-book.lbr-top-match {
	position: relative;
}

/* Headroom for the floating podium chips above the first row. */
.lbr-wrap .lbr-grid--podium {
	margin-top: 18px;
}

.lbr-wrap .lbr-top-match-badge {
	/* Fully ABOVE the card: book covers keep their title at the top, so
	   there is no safe overlay zone on the art itself. */
	position: absolute;
	top: -36px;
	left: -6px;
	z-index: 3;
	padding: 5px 12px;
	border-radius: 999px;
	background: var(--lbr-primary-color, #283B91);
	color: #fff;
	border: 2px solid #fff;
	font-family: var(--lbr-font-body);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.03em;
	box-shadow: 0 3px 10px rgba(0, 0, 0, 0.25);
}

.lbr-wrap .lbr-book.lbr-top-match .lbr-cover-wrap {
	position: relative;
}

/* The closest match gets the ring of honour; 2nd and 3rd a lighter one. */
.lbr-wrap .lbr-book.lbr-top-match--1 .lbr-cover-wrap {
	outline: 3px solid var(--lbr-primary-color, #283B91);
	outline-offset: 2px;
	border-radius: 6px;
}

.lbr-wrap .lbr-book.lbr-top-match--2 .lbr-cover-wrap,
.lbr-wrap .lbr-book.lbr-top-match--3 .lbr-cover-wrap {
	outline: 2px solid color-mix(in srgb, var(--lbr-primary-color, #283B91) 40%, transparent);
	outline-offset: 2px;
	border-radius: 6px;
}

/* List rows: the badge sits inline above the title. */
.lbr-wrap .lbr-row.lbr-top-match .lbr-top-match-badge {
	position: static;
	transform: none;
	display: inline-block;
	margin-bottom: 4px;
	background: var(--lbr-primary-color, #283B91);
	color: white;
	box-shadow: none;
}


/* List-mode year headers stick BENEATH the one sticky labels bar. */
.lbr-wrap .lbr-year-header--list {
	top: calc(var(--lbr-sticky-top, 0px) + 27px);
}

/* ————— Author shelf (query-understanding rail) ————— */

.lbr-wrap .lbr-author-shelf {
	margin: 20px 0 4px;
}

.lbr-wrap .lbr-author-shelf-head {
	display: flex;
	align-items: baseline;
	gap: 8px;
	margin-bottom: 12px;
}

.lbr-wrap .lbr-author-shelf-title {
	font-size: 16px;
	font-weight: 700;
	color: var(--lbr-heading-color);
	letter-spacing: 0.01em;
}

.lbr-wrap .lbr-author-shelf-count {
	font-size: 12px;
	font-weight: 600;
	color: var(--lbr-primary-color);
	background: rgba(40, 59, 145, 0.08);
	border-radius: 999px;
	padding: 1px 8px;
	line-height: 1.6;
}

/* One row of standard cards; overflow scrolls. The vertical padding
   keeps hover lift and card shadows from clipping at the scroller
   edges. */
.lbr-wrap .lbr-author-shelf-scroll {
	display: flex;
	gap: 16px;
	overflow-x: auto;
	padding: 4px 4px 12px;
	scroll-snap-type: x proximity;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: thin;
	cursor: grab;
}

/* The card that closes a capped rail. Sized to the covers beside it so
   the strip keeps one rhythm, but deliberately not dressed as a book —
   it is a door, not another title. */
.lbr-wrap .lbr-rail-seeall {
	flex: 0 0 auto;
	align-self: stretch;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-width: 132px;
	padding: 16px;
	border: 1px dashed var(--lbr-border-color, rgba(19, 29, 72, 0.22));
	border-radius: 8px;
	background: transparent;
	color: var(--lbr-primary-color, #283B91);
	font-family: var(--lbr-font-body);
	font-size: 13px;
	font-weight: 600;
	line-height: 1.35;
	text-align: center;
	cursor: pointer;
	transition: background 0.2s, border-color 0.2s;
}

.lbr-wrap .lbr-rail-seeall:hover {
	background: rgba(40, 59, 145, 0.06);
	border-color: var(--lbr-primary-color, #283B91);
}

.lbr-wrap .lbr-rail-seeall-arrow {
	font-size: 17px;
	line-height: 1;
}

.lbr-wrap .lbr-author-shelf-scroll.lbr-grab--dragging {
	cursor: grabbing;
}

.lbr-wrap .lbr-author-shelf-scroll .lbr-book {
	flex: 0 0 138px;
	width: 138px;
	scroll-snap-align: start;
}

/* The disambiguation line: quiet prose, names as understated links. */
.lbr-wrap .lbr-author-shelf-also {
	margin-top: 2px;
	font-size: 13px;
	color: var(--lbr-text-muted);
}

.lbr-wrap .lbr-author-shelf-also .lbr-author-shelf-alt {
	background: none;
	border: 0;
	padding: 0;
	margin: 0;
	font: inherit;
	color: var(--lbr-primary-color);
	text-decoration: underline;
	text-underline-offset: 2px;
	text-decoration-color: rgba(40, 59, 145, 0.35);
	cursor: pointer;
}

.lbr-wrap .lbr-author-shelf-also .lbr-author-shelf-alt:hover {
	color: var(--lbr-primary-hover);
	text-decoration-color: currentColor;
}

@media (max-width: 700px) {
	.lbr-wrap .lbr-author-shelf-scroll .lbr-book {
		flex-basis: 118px;
		width: 118px;
	}
}

/* Labelled section divider under the author shelf: the demarcation
   between "the author's books" and everything explorable below. */
.lbr-wrap .lbr-explore-divider {
	margin: 26px 0 6px;
}

/* — "Related searches" affordance on the AI follow-up chips —
   These sit above the refine pills while doing the opposite thing
   (replace the search); the row label + magnifier announce that. */
.lbr-wrap .lbr-chips--results .lbr-chips-rowlabel {
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--lbr-text-muted);
	align-self: center;
	margin-right: 4px;
	white-space: nowrap;
}

.lbr-wrap .lbr-chip .lbr-chip-mag {
	width: 12px;
	height: 12px;
	margin-right: 7px;
	vertical-align: -1px;
	opacity: 0.75;
}

/* The filter row's counterpart label — the pills row is centred flex,
   so the label rides inline at the row's start. */
.lbr-wrap .lbr-categories .lbr-filter-rowlabel {
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--lbr-text-muted);
	align-self: center;
	margin-right: 4px;
	white-space: nowrap;
}

/* Picks fold toggle — a quiet disclosure chevron in the rail's header. */
.lbr-wrap .lbr-picks-toggle,
.lbr-wrap .lbr-picks-toggle:hover,
.lbr-wrap .lbr-picks-toggle:focus,
.lbr-wrap .lbr-picks-toggle:active {
	background: none !important;
	border: 0 !important;
	box-shadow: none !important;
	border-radius: 0 !important;
	padding: 2px 6px;
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.02em;
	line-height: 1.4;
	color: var(--lbr-text-muted);
	cursor: pointer;
	align-self: center;
	white-space: nowrap;
}

/* Folded: the head is JUST the tiny "Show collections" item, sitting
   quietly on the left with no reserved height — and the whole table's
   spacing collapses with it. */
.lbr-wrap .lbr-author-shelf-head--folded {
	margin-bottom: 0;
}

.lbr-wrap .lbr-picks--folded {
	margin: 4px 0 0;
}

.lbr-wrap .lbr-picks--folded + .lbr-explore-divider {
	margin-top: 6px;
}

.lbr-wrap .lbr-author-shelf-head--folded .lbr-picks-toggle {
	padding-left: 0;
}

.lbr-wrap .lbr-picks-toggle:hover {
	color: var(--lbr-primary-color);
}

/* — Searching state — */

/* The bar breathes while a committed search is in flight. */
@keyframes lbr-search-busy {
	0%, 100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.35); }
	50%      { box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.12); }
}

.lbr-wrap .lbr-search-wrap--busy {
	animation: lbr-search-busy 1.1s ease-in-out infinite;
	border-radius: 14px;
}

@media (prefers-reduced-motion: reduce) {
	.lbr-wrap .lbr-search-wrap--busy {
		animation: none;
	}
}

/* Buttons do NOT inherit cursor:pointer — every interactive chip says
   so explicitly (skeletons and the ghost shimmer excluded: not
   clickable, must not lie about it). */
.lbr-wrap .lbr-chip:not(.lbr-chip--skel):not(.lbr-chip--ghost) {
	cursor: pointer;
}

/* — Front-table tabs — */
.lbr-wrap .lbr-rails-tabs {
	display: flex;
	gap: 20px;
	align-items: baseline;
	flex-wrap: wrap;
}

/* Hardened against theme button chrome (BuddyBoss pills, focus
   rings): these are underline TABS — flat text, a 2px rule under the
   active one, nothing rounded. */
.lbr-wrap .lbr-rail-tab,
.lbr-wrap .lbr-rail-tab:hover,
.lbr-wrap .lbr-rail-tab:focus {
	appearance: none;
	-webkit-appearance: none;
	background: none !important;
	border: 0 !important;
	border-radius: 0 !important;
	box-shadow: none !important;
	outline: none;
	margin: 0;
	padding: 0 0 6px;
	font-family: inherit;
	font-size: 16px;
	font-weight: 700;
	letter-spacing: 0.01em;
	line-height: 1.3;
	color: var(--lbr-text-muted);
	cursor: pointer;
	border-bottom: 2px solid transparent !important;
	transition: color 0.15s ease, border-color 0.15s ease;
}

.lbr-wrap .lbr-rail-tab:hover {
	color: var(--lbr-heading-color);
}

.lbr-wrap .lbr-rail-tab--active,
.lbr-wrap .lbr-rail-tab--active:hover,
.lbr-wrap .lbr-rail-tab--active:focus {
	color: var(--lbr-heading-color);
	border-bottom-color: var(--lbr-primary-color) !important;
}

/* Keyboard focus keeps an affordance without the theme's ring. */
/* Per-tab count pill: quiet on inactive tabs, brand-tinted on the
   active one — the number always belongs to the label it sits inside. */
.lbr-wrap .lbr-rail-tab .lbr-rail-tab-count {
	display: inline-block;
	margin-left: 7px;
	font-size: 11.5px;
	font-weight: 600;
	line-height: 1.6;
	padding: 0 7px;
	border-radius: 999px;
	background: rgba(60, 64, 72, 0.08);
	color: inherit;
	opacity: 0.75;
}

.lbr-wrap .lbr-rail-tab--active .lbr-rail-tab-count {
	background: rgba(40, 59, 145, 0.1);
	opacity: 1;
}

.lbr-wrap .lbr-rail-tab:focus-visible {
	border-bottom-color: var(--lbr-text-muted) !important;
}

/* One tab renders as a plain heading — no tab affordance to nowhere. */
.lbr-wrap .lbr-rails-tabs--single .lbr-rail-tab {
	color: var(--lbr-heading-color);
	border-bottom-color: transparent;
	cursor: default;
}

.lbr-wrap .lbr-rail-empty {
	padding: 22px 4px 26px;
	color: var(--lbr-text-muted);
	font-size: 14px;
}

/* — Bookshelf — */

/* Gold is the bookshelf's colour — the friendly kind of gold star. */
.lbr-wrap {
	--lbr-gold: #d9a406;
	--lbr-gold-dark: #a87d00;
}

/* Card star: our own rounded SVG (no OS glyph, no theme button chrome
   — hardened resets kill the ghost disc). White outline over artwork;
   solid gold once saved, and then always visible. */
.lbr-wrap .lbr-card-star,
.lbr-wrap .lbr-card-star:hover,
.lbr-wrap .lbr-card-star:focus,
.lbr-wrap .lbr-card-star:active {
	position: absolute;
	top: 5px;
	right: 7px;
	z-index: 3;
	appearance: none;
	-webkit-appearance: none;
	border: 0 !important;
	background: none !important;
	box-shadow: none !important;
	border-radius: 0 !important;
	outline: none;
	padding: 3px;
	line-height: 0;
	cursor: pointer;
	opacity: 0;
	transition: opacity 0.15s ease, transform 0.15s ease;
}

.lbr-wrap .lbr-book:hover .lbr-card-star,
.lbr-wrap .lbr-card-star:focus-visible,
.lbr-wrap .lbr-card-star--saved {
	opacity: 1 !important;
}

.lbr-wrap .lbr-card-star:hover {
	transform: scale(1.12);
}

.lbr-wrap .lbr-card-star svg {
	width: 24px;
	height: 24px;
	display: block;
	filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.55));
}

.lbr-wrap .lbr-card-star svg path {
	fill: rgba(255, 255, 255, 0.2);
	stroke: #fff;
	stroke-width: 2;
	stroke-linejoin: round;
	stroke-linecap: round;
}

.lbr-wrap .lbr-card-star--saved svg {
	filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3));
}

.lbr-wrap .lbr-card-star--saved svg path {
	fill: var(--lbr-gold);
	stroke: var(--lbr-gold);
}

/* Same Save/Remove rollover tip as the list rows, sitting left of the
   star so it stays on the cover. */
.lbr-wrap .lbr-card-star::after {
	content: attr(data-tip);
	position: absolute;
	right: calc(100% + 6px);
	top: 50%;
	transform: translateY(-50%);
	background: #2c3140;
	color: #fff;
	font-family: 'Open Sans', Arial, sans-serif;
	font-size: 11px;
	font-weight: 600;
	line-height: 1;
	padding: 5px 9px;
	border-radius: 4px;
	white-space: nowrap;
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.12s ease;
}

@media (hover: hover) {
	.lbr-wrap .lbr-card-star:hover::after {
		opacity: 1;
	}
}

/* After a click the tip goes quiet until the pointer leaves and comes
   back — it must never flip Save→Remove live under the cursor. */
.lbr-wrap .lbr-card-star.lbr-star-quiet::after,
.lbr-wrap .lbr-row-star.lbr-star-quiet::after {
	opacity: 0 !important;
	/* Instantly — a fade-out would flash the freshly flipped word. */
	transition: none;
}

/* ── Save confirmation toast ──────────────────────────────────────────
   Bottom centre, out of the way of both the covers and the back-to-top
   button, gone in two seconds. It is placed on <body>, not inside the
   app, so it is unaffected by the grid's transforms and stacking. */
.lbr-toast {
	position: fixed;
	left: 50%;
	bottom: 28px;
	z-index: 100000;
	display: flex;
	align-items: center;
	gap: 9px;
	max-width: calc(100vw - 32px);
	padding: 11px 18px;
	border-radius: 999px;
	background: #2c3140;
	color: #fff;
	font-family: 'Open Sans', Arial, sans-serif;
	font-size: 14px;
	font-weight: 600;
	line-height: 1;
	box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);
	/* Both transforms in one property — a separate `left` animation would
	   fight the centring. */
	transform: translate(-50%, 14px);
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.2s ease, transform 0.2s ease;
}

.lbr-toast--on {
	opacity: 1;
	transform: translate(-50%, 0);
}

.lbr-toast-tick {
	width: 17px;
	height: 17px;
	flex: 0 0 auto;
	color: var(--lbr-gold, #e0a800);
}

.lbr-toast-text {
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

@media (prefers-reduced-motion: reduce) {
	.lbr-toast {
		transition: opacity 0.2s ease;
		transform: translate(-50%, 0);
	}
}

/* Toolbar anchor: the fixed doorknob to the place — its star is gold
   at all times; INSIDE the shelf the whole pill inverts to gold. */
.lbr-wrap .lbr-shelf-anchor {
	background: none;
	border: 1px solid var(--lbr-border-color);
	border-radius: 999px;
	padding: 8px 16px;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--lbr-text-color);
	cursor: pointer;
	margin-left: 12px;
	white-space: nowrap;
}

.lbr-wrap .lbr-shelf-anchor-star {
	display: inline-block;
	line-height: 0;
	vertical-align: -3px;
	margin-right: 4px;
}

.lbr-wrap .lbr-shelf-anchor-star svg {
	width: 15px;
	height: 15px;
}

.lbr-wrap .lbr-shelf-anchor-star svg path {
	fill: var(--lbr-gold);
	stroke: var(--lbr-gold);
	stroke-width: 2;
	stroke-linejoin: round;
	stroke-linecap: round;
}

.lbr-wrap .lbr-shelf-anchor:hover {
	border-color: var(--lbr-gold);
	color: var(--lbr-gold-dark);
}

.lbr-wrap .lbr-shelf-anchor--active {
	background: var(--lbr-gold);
	border-color: var(--lbr-gold);
	color: #fff;
}

.lbr-wrap .lbr-shelf-anchor--active .lbr-shelf-anchor-star svg path {
	fill: #fff;
	stroke: #fff;
}

/* Removal confirm — small, centred, unambiguous. */
.lbr-confirm-overlay {
	position: fixed;
	inset: 0;
	z-index: 100000;
	background: rgba(19, 29, 72, 0.45);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 20px;
}

.lbr-confirm-box {
	background: #fff;
	border-radius: 12px;
	padding: 26px 28px 22px;
	max-width: 420px;
	box-shadow: 0 18px 50px rgba(0, 0, 0, 0.28);
}

.lbr-confirm-msg {
	font-size: 16px;
	line-height: 1.5;
	margin: 0 0 18px;
	color: #131D48;
}

.lbr-confirm-row {
	display: flex;
	gap: 12px;
	justify-content: flex-end;
}

.lbr-confirm-cancel {
	background: none;
	border: 1px solid #c9cdd4;
	border-radius: 999px;
	padding: 8px 18px;
	font-size: 14px;
	cursor: pointer;
	color: #3c4048;
}

.lbr-confirm-remove {
	background: #b3261e;
	border: 0;
	border-radius: 999px;
	padding: 8px 18px;
	font-size: 14px;
	cursor: pointer;
	color: #fff;
}

/* — Toolbar unification: one control height, one hover language —
   Pills, both dropdowns, the view toggle and the bookshelf anchor all
   stand 34px tall and answer hover the same way the category pills do
   (primary keyline + primary text); select focus loses the theme's
   sticky grey halo in favour of the same keyline. */
.lbr-wrap .lbr-sort-select {
	height: 34px;
	padding-top: 0;
	padding-bottom: 0;
	line-height: 32px;
	border-radius: 999px;
	transition: border-color 0.15s ease, color 0.15s ease;
}

.lbr-wrap .lbr-sort-select:hover {
	border-color: var(--lbr-primary-color);
	color: var(--lbr-primary-color);
}

/* Shrink-to-fit dropdowns: a hidden twin carrying the SELECTED label
   sets the width, the select lies on top of it. Without this the box is
   as wide as the longest option ("A Longish Reviewer Name (3498)"), so "All" sat
   in 167px of empty pill next to 104px year chips.

   The select is taken OUT of flow: sharing a grid cell was not enough,
   because a grid track sizes to the largest max-content among its items
   and the select's max-content IS the longest option — the ghost got
   stretched to the very width it was supposed to replace. Absolutely
   positioned, the select contributes nothing and the ghost alone
   decides. */
.lbr-wrap .lbr-select-fit {
	position: relative;
	display: inline-block;
	max-width: 100%;
}

.lbr-wrap .lbr-select-fit > .lbr-sort-select {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	min-width: 0;
}

/* Must match the select's box and type metrics exactly — it IS the
   measurement. `visibility: hidden` rather than display:none: the twin
   has to be laid out to have a width. */
.lbr-wrap .lbr-select-ghost {
	display: block;
	visibility: hidden;
	pointer-events: none;
	white-space: nowrap;
	height: 34px;
	box-sizing: border-box;
	padding: 0 28px 0 10px;
	border: 1px solid transparent;
	font-family: var(--lbr-font-body);
	font-size: 13px;
	font-weight: 400;
	line-height: 32px;
}

.lbr-wrap .lbr-sort-select:focus,
.lbr-wrap .lbr-sort-select:focus-visible {
	outline: none;
	box-shadow: none;
	border-color: var(--lbr-primary-color);
}

.lbr-wrap .lbr-view-toggle {
	height: 34px;
	border-radius: 999px;
	transition: border-color 0.15s ease;
}

.lbr-wrap .lbr-view-toggle:hover {
	border-color: var(--lbr-primary-color);
}

/* The two halves must be exactly that — halves. The theme puts
   `border-radius: 100px` on every <button>, so the active segment drew
   itself as a floating stadium with a bulging round right edge inside
   the toggle's own pill, and each icon sat off-centre in the shape it
   was supposed to be centred in. Square the buttons off and let the
   container's `overflow: hidden` cut the outer corners; centre the icon
   in each half explicitly rather than relying on button text metrics. */
.lbr-wrap .lbr-view-btn {
	padding: 0 12px;
	height: 32px;
	border-radius: 0 !important;
	display: flex;
	align-items: center;
	justify-content: center;
}

.lbr-wrap .lbr-shelf-anchor {
	height: 34px;
	padding-top: 0;
	padding-bottom: 0;
	line-height: 32px;
	transition: border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}

/* The gold pill's hover: text STAYS white; the affordance is a white
   pin line inside the pill — the same "keyline lights up" language as
   the category pills, translated to an inverted surface. */
.lbr-wrap .lbr-shelf-anchor--active:hover {
	color: #fff;
	border-color: var(--lbr-gold);
	box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.65);
}

.lbr-wrap .lbr-shelf-anchor--active:hover .lbr-shelf-anchor-star svg path {
	fill: #fff;
	stroke: #fff;
}

/* List-row star: rides the row's right edge; outline until saved. */
.lbr-wrap .lbr-row-star,
.lbr-wrap .lbr-row-star:hover,
.lbr-wrap .lbr-row-star:focus,
.lbr-wrap .lbr-row-star:active {
	position: relative;
	display: inline-block;
	vertical-align: -3px;
	margin-left: 8px;
	appearance: none;
	-webkit-appearance: none;
	border: 0 !important;
	background: none !important;
	box-shadow: none !important;
	border-radius: 0 !important;
	outline: none;
	padding: 4px;
	line-height: 0;
	cursor: pointer;
	flex: 0 0 auto;
	transition: transform 0.15s ease;
}

.lbr-wrap .lbr-row-star:hover {
	transform: scale(1.12);
}

.lbr-wrap .lbr-row-star svg {
	width: 18px;
	height: 18px;
	display: block;
}

.lbr-wrap .lbr-row-star svg path {
	fill: none;
	stroke: #b9bec6;
	stroke-width: 2;
	stroke-linejoin: round;
	stroke-linecap: round;
}

.lbr-wrap .lbr-row-star:hover svg path {
	stroke: var(--lbr-gold);
}

.lbr-wrap .lbr-row-star--saved svg path {
	fill: var(--lbr-gold);
	stroke: var(--lbr-gold);
}

/* Desktop rollover tip: says what the click will do (Save / Remove).
   Sits to the left of the star so it never clips at the list's edge;
   hover-capable pointers only, so touch never sees a stuck tip. */
.lbr-wrap .lbr-row-star::after {
	content: attr(data-tip);
	position: absolute;
	left: calc(100% + 8px);
	top: 50%;
	transform: translateY(-50%);
	background: #2c3140;
	color: #fff;
	/* The star sits inside the serif title; the tip is UI, not prose. */
	font-family: 'Open Sans', Arial, sans-serif;
	font-size: 11px;
	font-weight: 600;
	line-height: 1;
	padding: 5px 9px;
	border-radius: 4px;
	white-space: nowrap;
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.12s ease;
}

@media (hover: hover) {
	.lbr-wrap .lbr-row-star:hover::after {
		opacity: 1;
	}
}

/* ──────────────────────────────────────────────────────────────────────
   Small screens
   Measured on a 375px iPhone SE and a 412px Pixel: at 375 the covers
   came out 105px across, the sort control took a full-width row, and
   the metadata clipped mid-word on every card.
   ────────────────────────────────────────────────────────────────── */

@media (max-width: 640px) {
	/* Wider edges. It costs the grid ~8px, which is why it arrives with
	   the two-column rule below rather than fighting it. */
	.lbr-wrap .lbr-hero,
	.lbr-wrap .lbr-main {
		padding-left: 18px;
		padding-right: 18px;
	}

	/* Three lines under a 105px cover is two more than the reader needs
	   on a phone; the reviewer is the one that was clipping mid-word. */
	.lbr-wrap .lbr-book-reviewer {
		display: none;
	}

	/* The sort control was the heaviest thing on the page — a 335px pill
	   above content nobody has scrolled to yet. */
	.lbr-wrap .lbr-sort-label {
		display: none;
	}

	.lbr-wrap .lbr-sort .lbr-sort-select {
		font-size: 13px;
		padding-top: 6px;
		padding-bottom: 6px;
	}

	/* "Filter by" orphaned the All pill onto a second row beside a long
	   category. The pills say what they are. */
	.lbr-wrap .lbr-filter-rowlabel,
	.lbr-wrap .lbr-chips-rowlabel {
		display: none;
	}

	/* The collections tabs stack at this width and read as a list of
	   headings rather than a tab row. One swipeable line instead — the
	   same idiom the chips use. */
	.lbr-wrap .lbr-rails-tabs {
		display: flex;
		flex-wrap: nowrap;
		overflow-x: auto;
		scrollbar-width: none;
		-webkit-overflow-scrolling: touch;
	}

	.lbr-wrap .lbr-rails-tabs::-webkit-scrollbar {
		display: none;
	}

	.lbr-wrap .lbr-rail-tab {
		flex: 0 0 auto;
		white-space: nowrap;
	}
}

/* Two columns on the smallest screens. Three fits, but at 105px a cover
   is a thumbnail rather than a book — and these covers are the only
   thing distinguishing four thousand rows from each other. The Pixel
   keeps three: 117px reads fine, and the reader sees more of the shelf. */
@media (max-width: 400px) {
	.lbr-wrap .lbr-grid,
	.lbr-wrap .lbr-issue-body {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/* The collections tabs, on a phone, as a menu rather than a row.
   Swiping a tab strip hides that the other collections exist at all —
   the second and third titles are off-screen and the first is clipped
   mid-word. A select says how many there are and names them in full. */
.lbr-wrap .lbr-rails-select {
	display: none;
}

@media (max-width: 640px) {
	.lbr-wrap .lbr-rails-tabs {
		display: none;
	}

	.lbr-wrap .lbr-rails-select {
		display: block;
		max-width: 100%;
		margin: 0 0 4px;
		padding: 7px 30px 7px 12px;
		border: 1px solid var(--lbr-border-color, rgba(19, 29, 72, 0.18));
		border-radius: var(--lbr-btn-radius, 999px);
		/* Own the chevron, as the sort control does: BuddyBoss sets
		   appearance:none on selects and paints its own arrow, which any
		   background of ours erases — leaving a menu with nothing to say
		   it opens. */
		-webkit-appearance: none;
		appearance: none;
		background-color: transparent;
		background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23667085' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
		background-repeat: no-repeat;
		background-position: right 12px center;
		font-family: var(--lbr-font-body);
		font-size: 13px;
		font-weight: 600;
		color: var(--lbr-heading-color);
	}
}

/* The labelled rule above the related books carries an author's name,
   and the name can be long. `white-space: nowrap` on the label meant a
   long one simply ran off the right edge instead of wrapping — the
   label is a sentence at that point, not a caption, so let it behave
   like one on a narrow screen. */
@media (max-width: 640px) {
	.lbr-wrap .lbr-divider {
		gap: 10px;
		margin: 20px 0 22px;
	}

	.lbr-wrap .lbr-divider-text {
		white-space: normal;
		text-align: center;
		letter-spacing: 1px;
		min-width: 0;
	}

	/* The podium badge floats 36px above its card, which works where a
	   card is 160px+ and there is room overhead. At two columns it
	   collides with the row above and its own words outrun the cover.
	   The ring already says which three these are — that survives. */
	.lbr-wrap .lbr-top-match-badge {
		display: none;
	}

	.lbr-wrap .lbr-grid--podium {
		margin-top: 0;
	}
}

/* NOT centring #lbr-shelf-slot. It was tried, to chase a report of the
   AI summary sitting off-centre, and it over-reached: the slot is an
   ancestor of the whole author shelf, so it centred the "Also reviewed"
   disambiguation line too, which belongs on the left with the shelf it
   qualifies. The summary carries its own text-align, and did all along
   — the rule bought nothing and cost that. */

/* The suggestion list keeps its shape whatever the name inside it does.
   A long author plus a nowrap "14 reviews" made the row wider than the
   box; with overflow-y:auto the box then scrolled sideways and its
   right-hand corners went with it. The name truncates instead — it is
   the part the reader is scanning, and they can see enough of it. */
.lbr-wrap .lbr-suggest {
	overflow-x: hidden;
}

.lbr-wrap .lbr-suggest-item {
	min-width: 0;
}

.lbr-wrap .lbr-suggest-name {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Focusing the box on a phone raises the keyboard over the very list
   the box just opened. Leave the search room to climb. */
.lbr-wrap .lbr-search-wrap {
	scroll-margin-top: 12px;
}

/* The save star is revealed by hover, and a touch screen has none — so
   on a phone the whole feature was invisible until you happened to
   land on a review page and find the button there. Show it always
   where there is no hover, at a weight that reads as available rather
   than as decoration; a saved star is full strength as it is
   everywhere else. */
@media (hover: none) {
	.lbr-wrap .lbr-card-star,
	.lbr-wrap .lbr-row-star {
		opacity: 0.45;
	}

	.lbr-wrap .lbr-card-star--saved,
	.lbr-wrap .lbr-row-star--saved {
		opacity: 1 !important;
	}
}

/* The bookshelf pill on a phone. It sits in a row that wraps, so it
   inherited the row's left alignment and read as an orphan rather than
   as the destination it is — and at 13.5px in a 8px/16px pill it was
   the smallest tap target on a screen made of tap targets. */
@media (max-width: 640px) {
	.lbr-wrap .lbr-shelf-anchor {
		display: flex;
		align-items: center;
		justify-content: center;
		margin-left: 0;
		padding: 10px 20px;
		font-size: 14.5px;
	}

	.lbr-wrap .lbr-shelf-anchor-star svg {
		width: 17px;
		height: 17px;
	}
}

/* Horizontal scrollers must not hand their swipe to the page.
   Swiping a rail that has reached its end chains the gesture outward by
   default, and the document then rubber-bands sideways — which is the
   white strip appearing down the right on Android. Contained, the rail
   simply stops. */
.lbr-wrap .lbr-chips,
.lbr-wrap .lbr-categories,
.lbr-wrap .lbr-year-strip,
.lbr-wrap .lbr-rails-tabs,
.lbr-wrap .lbr-author-shelf-scroll {
	overscroll-behavior-x: contain;
}

/* Book suggestions, beneath the authors. A cover and the author's name
   under the title: the same two facts a card carries, so a suggestion
   and the thing it leads to look like each other. */
.lbr-wrap .lbr-suggest-head {
	padding: 8px 16px 4px;
	font-family: var(--lbr-font-body);
	font-size: 10px;
	font-weight: 700;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, 0.4);
}

.lbr-wrap.lbr-hero--on-light .lbr-suggest-head {
	color: rgba(19, 29, 72, 0.45);
}

.lbr-wrap .lbr-suggest-item--title {
	/* The author row is name-left / count-right, so the item is
	   space-between. A book row is cover-then-title and must not be
	   pushed apart by it. */
	justify-content: flex-start;
	gap: 10px;
	align-items: center;
}

.lbr-wrap .lbr-suggest-cover {
	flex: 0 0 auto;
	width: 26px;
	height: 38px;
	object-fit: cover;
	border-radius: 3px;
}

.lbr-wrap .lbr-suggest-text {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.lbr-wrap .lbr-suggest-by {
	font-size: 11px;
	color: rgba(255, 255, 255, 0.45);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.lbr-wrap.lbr-hero--on-light .lbr-suggest-by {
	color: rgba(19, 29, 72, 0.5);
}

/* ---------------------------------------------------------------------
   Members-only front door (1.5.199). Server-rendered, inert: the hero
   and chips keep the app's look; the gate panel carries the way in and
   the skeleton shelf behind it keeps the page's shape.
   ------------------------------------------------------------------ */
.lbr-wrap--gate .lbr-search-input:disabled {
	opacity: 1;
	cursor: default;
	-webkit-text-fill-color: inherit;
}

.lbr-wrap--gate .lbr-search-btn:disabled,
.lbr-wrap--gate .lbr-chip:disabled {
	cursor: default;
	opacity: 0.85;
}

.lbr-wrap--gate .lbr-main {
	position: relative;
}

.lbr-wrap .lbr-gate {
	position: relative;
	z-index: 2;
	max-width: 520px;
	margin: 32px auto 0;
	padding: 36px 32px;
	text-align: center;
	background: var(--lbr-card-background, #fff);
	border: 1px solid var(--lbr-border-color);
	border-radius: var(--lbr-card-radius, 12px);
	box-shadow: 0 18px 48px rgba(0, 0, 0, 0.12);
}

.lbr-wrap .lbr-gate-lock {
	width: 36px;
	height: 36px;
	color: var(--lbr-hero-base, #283B91);
	margin-bottom: 12px;
}

.lbr-wrap .lbr-gate-text {
	font-family: var(--lbr-font-heading);
	font-size: 20px;
	line-height: 1.4;
	margin: 0 0 22px;
	color: var(--lbr-text-color, #1a1a1a);
}

.lbr-wrap .lbr-gate-actions {
	display: flex;
	gap: 12px;
	justify-content: center;
	flex-wrap: wrap;
}

.lbr-wrap .lbr-gate-btn {
	display: inline-block;
	padding: 12px 26px;
	border-radius: 999px;
	font-size: 15px;
	font-weight: 600;
	text-decoration: none !important;
	border: 2px solid var(--lbr-hero-base, #283B91);
	color: var(--lbr-hero-base, #283B91) !important;
	background: transparent;
	transition: background 0.15s, color 0.15s;
}

.lbr-wrap .lbr-gate-btn--join {
	background: var(--lbr-hero-base, #283B91);
	color: #fff !important;
}

.lbr-wrap .lbr-gate-btn:hover {
	background: var(--lbr-hero-base, #283B91);
	color: #fff !important;
}

.lbr-wrap .lbr-gate-shelf {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
	gap: 24px;
	margin-top: -120px;
	padding: 140px 8px 24px;
	opacity: 0.55;
	pointer-events: none;
	-webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 160px, rgba(0, 0, 0, 1) 45%, rgba(0, 0, 0, 0) 85%);
	mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 160px, rgba(0, 0, 0, 1) 45%, rgba(0, 0, 0, 0) 85%);
}

@media (max-width: 600px) {
	.lbr-wrap .lbr-gate {
		margin: 20px 16px 0;
		padding: 28px 20px;
	}
	.lbr-wrap .lbr-gate-shelf {
		grid-template-columns: repeat(3, 1fr);
		gap: 14px;
	}
}
